Commit eb98b1d7 authored by Robby Stephenson's avatar Robby Stephenson
Browse files

Fix image paths when importing and exporting GCstar collections.

Be sure to have directory delimiters where appropriate. Add a unit test
case with a wine collection.

BUG: 453075
FIXED-IN: 3.4.5
parent 88a59bf0
Pipeline #169163 passed with stage
in 4 minutes and 6 seconds
2022-04-27 Robby Stephenson <robby@periapsis.org>
* Fixed image path for exporting GCstar files (Bug 453075).
2022-04-08 Robby Stephenson <robby@periapsis.org>
* Updated CSV importer to remember column and row delimiter.
......
<?xml version="1.0" encoding="UTF-8"?>
<collection type="GCwines" items="1" version="1.6.0">
<collection type="GCwines" items="1" version="1.7.2">
<information>
<maxId>2</maxId>
</information>
......@@ -19,18 +19,13 @@
volume="750"
alcohol="12"
medal="distinction"
location=""
shelfindex=""
quantity="1"
purchasedate="28/08/2010"
purchaseprice="12.99"
gift="yes"
bottlelabel=""
website=""
bottlelabel="fb23724a101bd7100cd0360b8ad368bc.jpeg"
tasted="1"
rating="6"
favourite="0"
tags=""
>
<comments>comments</comments>
<serving></serving>
......
......@@ -44,6 +44,9 @@ QTEST_GUILESS_MAIN( GCstarTest )
void GCstarTest::initTestCase() {
QStandardPaths::setTestModeEnabled(true);
// remove the test image directory
QDir gcstarImageDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/gcstar/"));
gcstarImageDir.removeRecursively();
Tellico::ImageFactory::init();
Tellico::DataFileRegistry::self()->addDataLocation(QFINDTESTDATA("../../xslt/gcstar2tellico.xsl"));
// need to register the collection types
......@@ -374,6 +377,7 @@ void GCstarTest::testBoardGame() {
void GCstarTest::testWine() {
QUrl url = QUrl::fromLocalFile(QFINDTESTDATA("data/test-wine.gcs"));
Tellico::Import::GCstarImporter importer(url);
importer.setHasRelativeImageLinks(true);
Tellico::Data::CollPtr coll = importer.collection();
QVERIFY(coll);
......@@ -401,9 +405,12 @@ void GCstarTest::testWine() {
QCOMPARE(entry->field("tasted"), QStringLiteral("true"));
QVERIFY(!entry->field("description").isEmpty());
QVERIFY(!entry->field("comments").isEmpty());
QVERIFY(!entry->field("label").isEmpty());
Tellico::Export::GCstarExporter exporter(coll);
exporter.setOptions(exporter.options() | Tellico::Export::ExportImages);
exporter.setEntries(coll->entries());
Tellico::Import::GCstarImporter importer2(exporter.text());
Tellico::Data::CollPtr coll2 = importer2.collection();
......@@ -416,10 +423,7 @@ void GCstarTest::testWine() {
Tellico::Data::EntryPtr e2 = coll2->entryById(e1->id());
QVERIFY(e2);
foreach(Tellico::Data::FieldPtr f, coll->fields()) {
// skip images
if(f->type() != Tellico::Data::Field::Image) {
QCOMPARE(f->name() + e2->field(f), f->name() + e1->field(f));
}
QCOMPARE(f->name() + e2->field(f), f->name() + e1->field(f));
}
}
}
......
/***************************************************************************
Copyright (C) 2009 Robby Stephenson <robby@periapsis.org>
Copyright (C) 2009-2022 Robby Stephenson <robby@periapsis.org>
***************************************************************************/
/***************************************************************************
......@@ -37,6 +37,7 @@
#include <QDomDocument>
#include <QFile>
#include <QDir>
#include <QTextStream>
#include <QStandardPaths>
#include <QApplication>
......@@ -66,37 +67,7 @@ bool GCstarExporter::exec() {
bool success = true;
if(options() & ExportImages) {
const QString imgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("gcstar/images/");
ProgressItem& item = ProgressManager::self()->newProgressItem(this, QString(), false);
item.setTotalSteps(entries().count());
ProgressItem::Done done(this);
const uint stepSize = qMax(1, entries().count()/100);
const bool showProgress = options() & ExportProgress;
uint j = 0;
foreach(const Data::EntryPtr& entry, entries()) {
foreach(Data::FieldPtr field, entry->collection()->imageFields()) {
if(entry->field(field).isEmpty()) {
break;
}
const Data::Image& img = ImageFactory::self()->imageById(entry->field(field));
if(img.isNull()) {
break;
}
QUrl target = QUrl::fromLocalFile(imgDir);
target = target.adjusted(QUrl::RemoveFilename);
target.setPath(target.path() + img.id());
// myDebug() << "Writing" << target.url();
success &= FileHandler::writeDataURL(target, img.byteArray(), true /* force */);
}
if(showProgress && j%stepSize == 0) {
item.setProgress(j);
qApp->processEvents();
}
++j;
}
writeImages();
}
return !text.isEmpty() &&
FileHandler::writeTextURL(url(), text, options() & ExportUTF8, options() & Export::ExportForce) &&
......@@ -139,12 +110,12 @@ QString GCstarExporter::text() {
delete m_handler;
m_handler = new XSLTHandler(dom, QFile::encodeName(xsltFile));
if(!m_handler || !m_handler->isValid()) {
myDebug() << "bad handler";
return QString();
}
if(options() & ExportImages) {
m_handler->addStringParam("imageDir", QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).toLocal8Bit() + "gcstar/images/");
m_handler->addStringParam("imageDir", QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).toLocal8Bit() + "/gcstar/images/");
writeImages();
}
// now grab the XML
......@@ -161,3 +132,40 @@ QWidget* GCstarExporter::widget(QWidget* parent_) {
Q_UNUSED(parent_);
return nullptr;
}
bool GCstarExporter::writeImages() {
const QString imgDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/gcstar/images/");
QDir dir(imgDir);
if(!dir.exists() && !dir.mkpath(QLatin1String("."))) return false;
ProgressItem& item = ProgressManager::self()->newProgressItem(this, QString(), false);
item.setTotalSteps(entries().count());
ProgressItem::Done done(this);
const uint stepSize = qMax(1, entries().count()/100);
const bool showProgress = options() & ExportProgress;
bool success = true;
uint j = 0;
foreach(const Data::EntryPtr& entry, entries()) {
foreach(Data::FieldPtr field, entry->collection()->imageFields()) {
if(entry->field(field).isEmpty()) {
break;
}
const Data::Image& img = ImageFactory::self()->imageById(entry->field(field));
if(img.isNull()) {
break;
}
QUrl target = QUrl::fromLocalFile(imgDir);
target.setPath(target.path() + img.id());
success &= FileHandler::writeDataURL(target, img.byteArray(), true /* force */);
}
if(showProgress && j%stepSize == 0) {
item.setProgress(j);
qApp->processEvents();
}
++j;
}
return success;
}
......@@ -53,6 +53,8 @@ public:
QString text();
private:
bool writeImages();
XSLTHandler* m_handler;
QString m_xsltFile;
};
......
/***************************************************************************
Copyright (C) 2005-2009 Robby Stephenson <robby@periapsis.org>
Copyright (C) 2005-2022 Robby Stephenson <robby@periapsis.org>
***************************************************************************/
/***************************************************************************
......@@ -266,7 +266,7 @@ void GCstarImporter::readGCstar(const QString& text_) {
}
if(m_relativeImageLinks) {
handler.addStringParam("baseDir", url().adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path().toLocal8Bit());
handler.addStringParam("baseDir", url().adjusted(QUrl::RemoveFilename).path().toLocal8Bit());
}
const QString str = handler.applyStylesheet(text_);
......
......@@ -437,21 +437,18 @@
<tc:location><xsl:value-of select="."/></tc:location>
</xsl:template>
<xsl:template match="image|boxpic">
<tc:cover>
<xsl:choose>
<!-- no.png means no image -->
<xsl:when test=". = 'images/no.png'">
</xsl:when>
<!-- is the image location relative or not? -->
<xsl:when test="starts-with(., 'file://') or starts-with(., 'http') or starts-with(., '/')">
<xsl:value-of select="."/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($baseDir,.)"/>
</xsl:otherwise>
</xsl:choose>
</tc:cover>
<xsl:template match="image|boxpic|cover">
<xsl:call-template name="image-element">
<xsl:with-param name="elem" select="'tc:cover'"/>
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="bottlelabel">
<xsl:call-template name="image-element">
<xsl:with-param name="elem" select="'tc:label'"/>
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="web|webPage">
......@@ -670,12 +667,18 @@
<tc:pur_price><xsl:value-of select="."/></tc:pur_price>
</xsl:template>
<xsl:template match="front">
<tc:obverse><xsl:value-of select="."/></tc:obverse>
<xsl:template match="front|picture">
<xsl:call-template name="image-element">
<xsl:with-param name="elem" select="'tc:obverse'"/>
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="back">
<tc:reverse><xsl:value-of select="."/></tc:reverse>
<xsl:call-template name="image-element">
<xsl:with-param name="elem" select="'tc:reverse'"/>
<xsl:with-param name="value" select="."/>
</xsl:call-template>
</xsl:template>
<xsl:template match="designedby">
......@@ -787,10 +790,6 @@
<tc:distinction><xsl:value-of select="."/></tc:distinction>
</xsl:template>
<xsl:template match="bottlelabel">
<tc:label><xsl:value-of select="."/></tc:label>
</xsl:template>
<xsl:template match="gift">
<xsl:if test="string-length(.) &gt; 0 and . != 'false' and . != '0'">
<tc:gift>true</tc:gift>
......@@ -809,14 +808,6 @@
</xsl:if>
</xsl:template>
<xsl:template match="front">
<tc:obverse><xsl:value-of select="."/></tc:obverse>
</xsl:template>
<xsl:template match="back">
<tc:reverse><xsl:value-of select="."/></tc:reverse>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'gcsfield')]">
<xsl:choose>
<xsl:when test="key('fieldsByName', local-name())/@type = 'single list'">
......@@ -836,6 +827,27 @@
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="image-element">
<xsl:param name="elem"/>
<xsl:param name="value"/>
<xsl:element name="{$elem}">
<xsl:choose>
<!-- no.png means no image -->
<xsl:when test="$value = 'images/no.png'">
</xsl:when>
<!-- is the image location relative or not? -->
<xsl:when test="starts-with($value, 'file://') or
starts-with($value, 'http') or
starts-with($value, '/')">
<xsl:value-of select="$value"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($baseDir,$value)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:template>
<xsl:template name="substring-before-last">
......
......@@ -52,7 +52,7 @@
<a:attribute name="read" format="bool" type="GCbooks">read</a:attribute>
<a:attribute name="seen" format="bool" type="GCfilms">seen</a:attribute>
<a:attribute name="favourite" format="bool">favorite</a:attribute>
<a:attribute name="label">label</a:attribute>
<a:attribute name="label" skip="GCwines">label</a:attribute>
<a:attribute name="release" type="GCfilms">year</a:attribute>
<a:attribute name="composer">composer</a:attribute>
<a:attribute name="producer">producer</a:attribute>
......@@ -84,10 +84,13 @@
<a:attribute name="currency">currency</a:attribute>
<a:attribute name="diameter">diameter</a:attribute>
<a:attribute name="value">denomination</a:attribute>
<a:attribute name="cover" format="image" type="GCbooks, GCmusics">cover</a:attribute>
<a:attribute name="image" format="image" type="GCfilms, GCcomics">cover</a:attribute>
<a:attribute name="boxpic" format="image" type="GCgames, GCboardgames">cover</a:attribute>
<a:attribute name="cover" format="image" type="GCbooks, GCmusics">cover</a:attribute>
<a:attribute name="image" format="image" type="GCfilms, GCcomics">cover</a:attribute>
<a:attribute name="boxpic" format="image" type="GCgames, GCboardgames">cover</a:attribute>
<a:attribute name="picture" format="image" type="GCcoins">obverse</a:attribute>
<a:attribute name="front" format="image" type="GCcoins">obverse</a:attribute>
<a:attribute name="back" format="image" type="GCcoins">reverse</a:attribute>
<a:attribute name="bottlelabel" format="image" type="GCwines">label</a:attribute>
</a:attributes>
<xsl:variable name="collType">
<xsl:choose>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment