Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBundleStorage.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7#include "KisBundleStorage.h"
8
9#include <QDebug>
10#include <QFileInfo>
11#include <QDir>
12#include <QDirIterator>
13
14#include <KisTag.h>
15#include "KisResourceStorage.h"
16#include <KoMD5Generator.h>
17#include "KoResourceBundle.h"
20
22#include <kis_pointer_utils.h>
23#include <kis_debug.h>
24
26public:
28
30 QScopedPointer<KoResourceBundle> bundle;
31};
32
33
35{
36public:
37
38 BundleTagIterator(KoResourceBundle *bundle, const QString &resourceType)
39 : m_bundle(bundle)
40 , m_resourceType(resourceType)
41 {
43 Q_FOREACH(const KoResourceBundleManifest::ResourceReference &resourceReference, resources) {
44 Q_FOREACH(const QString &tagname, resourceReference.tagList) {
45 if (!m_tags.contains(tagname)){
47 tag->setName(tagname);
48 tag->setComment(tagname);
49 tag->setUrl(tagname);
50 tag->setResourceType(resourceType);
51 tag->setValid(true);
52 m_tags[tagname] = tag;
53 }
54
55 m_tags[tagname]->setDefaultResources(m_tags[tagname]->defaultResources()
56 << QFileInfo(resourceReference.resourcePath).fileName());
57 }
58 }
59 m_tagIterator.reset(new QListIterator<KisTagSP>(m_tags.values()));
60 }
61
62 bool hasNext() const override
63 {
64 return m_tagIterator->hasNext();
65 }
66
67 void next() override
68 {
69 const_cast<BundleTagIterator*>(this)->m_tag = m_tagIterator->next();
70 }
71 KisTagSP tag() const override { return m_tag; }
72
73private:
74 QHash<QString, KisTagSP> m_tags;
77 QScopedPointer<QListIterator<KisTagSP> > m_tagIterator;
79};
80
81
82KisBundleStorage::KisBundleStorage(const QString &location)
83 : KisStoragePlugin(location)
84 , d(new Private(this))
85{
86 d->bundle.reset(new KoResourceBundle(location));
87 if (!d->bundle->load()) {
88 qWarning() << "Could not load bundle" << location;
89 }
90}
91
95
97{
99 item.url = url;
100 QStringList parts = url.split('/', Qt::SkipEmptyParts);
101 Q_ASSERT(parts.size() == 2);
102 item.folder = parts[0];
103 item.resourceType = parts[0];
104 item.lastModified = QFileInfo(d->bundle->filename()).lastModified();
105 return item;
106}
107
109{
110 bool foundVersionedFile = false;
111
112 const QString resourceType = resource->resourceType().first;
113 const QString resourceUrl = resourceType + "/" + resource->filename();
114
115 const QString bundleSaveLocation = location() + "_modified" + "/" + resourceType;
116
117 if (QDir(bundleSaveLocation).exists()) {
118 const QString fn = bundleSaveLocation + "/" + resource->filename();
119 const QFileInfo fi(fn);
120 if (fi.exists()) {
121 foundVersionedFile = true;
122
123 QFile f(fn);
124 if (!f.open(QFile::ReadOnly)) {
125 qWarning() << "Could not open resource file for reading" << fn;
126 return false;
127 }
128 if (!resource->loadFromDevice(&f, KisGlobalResourcesInterface::instance())) {
129 qWarning() << "Could not reload resource file" << fn;
130 return false;
131 }
132
134
135 // Check for the thumbnail
136 if ((resource->image().isNull() || resource->thumbnail().isNull()) && !resource->thumbnailPath().isNull()) {
137 QImage img(bundleSaveLocation + "/" + '/' + resource->thumbnailPath());
138 resource->setImage(img);
139 resource->updateThumbnail();
140 }
141 f.close();
142 }
143 }
144
145 if (!foundVersionedFile) {
146 d->bundle->loadResource(resource);
147 }
148
149 return true;
150}
151
152QString KisBundleStorage::resourceMd5(const QString &url)
153{
154 QString result;
155
156 QFile modifiedFile(location() + "_modified" + "/" + url);
157 if (modifiedFile.exists() && modifiedFile.open(QIODevice::ReadOnly)) {
158 result = KoMD5Generator::generateHash(modifiedFile.readAll());
159 } else {
160 result = d->bundle->resourceMd5(url);
161 }
162
163 return result;
164}
165
167{
169
171 d->bundle->manifest().files(resourceType);
172
173 for (auto it = references.begin(); it != references.end(); ++it) {
175 // it->resourcePath() contains paths like "brushes/ink.png" or "brushes/subfolder/splash.png".
176 // we need to cut off the first part and get "ink.png" in the first case,
177 // but "subfolder/splash.png" in the second case in order for subfolders to work
178 // so it cannot just use QFileInfo(verIt->url()).fileName() here.
179 QString path = QDir::fromNativeSeparators(it->resourcePath); // make sure it uses Unix separators
180 int folderEndIdx = path.indexOf("/");
181 QString properFilenameWithSubfolders = path.right(path.length() - folderEndIdx - 1);
182
183 entry.filename = properFilenameWithSubfolders;
184 entry.lastModified = QFileInfo(location()).lastModified();
185 entry.tagList = it->tagList;
186 entry.resourceType = resourceType;
187 entries.append(entry);
188 }
189
190 const QString bundleSaveLocation = location() + "_modified" + "/" + resourceType;
191
192 QDirIterator it(bundleSaveLocation,
193 KisResourceLoaderRegistry::instance()->filters(resourceType),
194 QDir::Files | QDir::Readable,
195 QDirIterator::Subdirectories);;
196
197 while (it.hasNext()) {
198 it.next();
199 QFileInfo info(it.fileInfo());
200
202 entry.filename = info.fileName();
203 entry.lastModified = info.lastModified();
204 entry.tagList = {}; // TODO
205 entry.resourceType = resourceType;
206 entries.append(entry);
207 }
208
210
211 return toQShared(new KisVersionedStorageIterator(entries, this));
212}
213
218
220{
221 return d->bundle->image();
222}
223
241
242QVariant KisBundleStorage::metaData(const QString &key) const
243{
244 return d->bundle->metaData(key);
245}
246
247bool KisBundleStorage::saveAsNewVersion(const QString &resourceType, KoResourceSP resource)
248{
249 QString bundleSaveLocation = location() + "_modified" + "/" + resourceType;
250
251 if (!QDir(bundleSaveLocation).exists()) {
252 QDir().mkpath(bundleSaveLocation);
253 }
254
255 return KisStorageVersioningHelper::addVersionedResource(bundleSaveLocation, resource, 1);
256}
257
258bool KisBundleStorage::exportResource(const QString &url, QIODevice *device)
259{
260 QStringList parts = url.split('/', Qt::SkipEmptyParts);
261 Q_ASSERT(parts.size() == 2);
262
263 const QString resourceType = parts[0];
264 const QString resourceFileName = parts[1];
265
266 bool foundVersionedFile = false;
267
268 const QString bundleSaveLocation = location() + "_modified" + "/" + resourceType;
269
270 if (QDir(bundleSaveLocation).exists()) {
271 const QString fn = bundleSaveLocation + "/" + resourceFileName;
272 if (QFileInfo(fn).exists()) {
273 foundVersionedFile = true;
274
275 QFile f(fn);
276 if (!f.open(QFile::ReadOnly)) {
277 qWarning() << "Could not open resource file for reading" << fn;
278 return false;
279 }
280
281 device->write(f.readAll());
282 }
283 }
284
285 if (!foundVersionedFile) {
286 d->bundle->exportResource(resourceType, resourceFileName, device);
287 }
288
289 return true;
290}
QList< QString > QStringList
QScopedPointer< QListIterator< KisTagSP > > m_tagIterator
KisTagSP tag() const override
A tag object on which we can set properties and which we can save.
QHash< QString, KisTagSP > m_tags
bool hasNext() const override
void next() override
The iterator is only valid if next() has been called at least once.
BundleTagIterator(KoResourceBundle *bundle, const QString &resourceType)
KoResourceBundle * m_bundle
QScopedPointer< KoResourceBundle > bundle
Private(KisBundleStorage *_q)
KisBundleStorage(const QString &location)
QVariant metaData(const QString &key) const override
QStringList metaDataKeys() const override
bool loadVersionedResource(KoResourceSP resource) override
Note: this should find resources in a folder that override a resource in the bundle first.
bool exportResource(const QString &url, QIODevice *device) override
QImage thumbnail() const override
QScopedPointer< Private > d
QSharedPointer< KisResourceStorage::ResourceIterator > resources(const QString &resourceType) override
QString resourceMd5(const QString &url) override
bool saveAsNewVersion(const QString &resourceType, KoResourceSP resource) override
KisResourceStorage::ResourceItem resourceItem(const QString &url) override
QSharedPointer< KisResourceStorage::TagIterator > tags(const QString &resourceType) override
static KisResourcesInterfaceSP instance()
static KisResourceLoaderRegistry * instance()
static const QString s_meta_author
static const QString s_meta_title
static const QString s_meta_user_defined
static const QString s_meta_creation_date
static const QString s_meta_value
static const QString s_meta_description
static const QString s_meta_dc_date
static const QString s_meta_generator
static const QString s_meta_version
static const QString s_meta_name
static const QString s_meta_initial_creator
static const QString s_meta_creator
QString location() const
virtual KoResourceSP resource(const QString &url)
void sanitizeResourceFileNameCase(KoResourceSP resource, const QDir &parentDir)
static void detectFileVersions(QVector< VersionedResourceEntry > &allFiles)
static bool addVersionedResource(const QString &saveLocation, KoResourceSP resource, int minVersion)
The KisTag loads a tag from a .tag file. A .tag file is a .desktop file. The following fields are imp...
Definition KisTag.h:34
static QString generateHash(const QString &filename)
generateHash reads the given file and generates a hex-encoded md5sum for the file.
QList< ResourceReference > files(const QString &type=QString()) const
A KoResourceBundle is a zip file that contains resources, some metadata about the creator of the bund...
KoResourceBundleManifest & manifest()
QSharedPointer< T > toQShared(T *ptr)
A resource item is simply an entry in the storage,.