File indexing completed on 2024-05-12 15:59:48

0001 /*
0002  * SPDX-FileCopyrightText: 2018 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "KisFolderStorage.h"
0008 
0009 #include <QDirIterator>
0010 #include <KisMimeDatabase.h>
0011 #include <kis_debug.h>
0012 #include <KisTag.h>
0013 #include <KisResourceLoaderRegistry.h>
0014 #include <KisGlobalResourcesInterface.h>
0015 #include <kis_pointer_utils.h>
0016 #include <KoMD5Generator.h>
0017 
0018 
0019 class FolderTagIterator : public KisResourceStorage::TagIterator
0020 {
0021 public:
0022 
0023     FolderTagIterator(const QString &location, const QString &resourceType)
0024         : m_location(location)
0025         , m_resourceType(resourceType)
0026     {
0027         m_dirIterator.reset(new QDirIterator(location + '/' + resourceType,
0028                                              QStringList() << "*.tag",
0029                                              QDir::Files | QDir::Readable,
0030                                              QDirIterator::Subdirectories));
0031     }
0032 
0033     bool hasNext() const override
0034     {
0035         return m_dirIterator->hasNext();
0036     }
0037 
0038     void next() override
0039     {
0040         m_dirIterator->next();
0041         const_cast<FolderTagIterator*>(this)->m_tag.reset(new KisTag);
0042         if (!load(m_tag)) {
0043             qWarning() << "Could not load tag" << m_dirIterator->filePath();
0044         }
0045     }
0046 
0047     KisTagSP tag() const override
0048     {
0049         return m_tag;
0050     }
0051 
0052 private:
0053 
0054     bool load(KisTagSP tag) const
0055     {
0056         QFile f(m_dirIterator->filePath());
0057         tag->setFilename(m_dirIterator->fileName());
0058         if (f.exists()) {
0059             f.open(QFile::ReadOnly);
0060             if (!tag->load(f)) {
0061                 qWarning() << m_dirIterator->filePath() << "is not a valid tag desktop file";
0062                 return false;
0063             }
0064 
0065         }
0066         return true;
0067     }
0068 
0069     QScopedPointer<QDirIterator> m_dirIterator;
0070     QString m_location;
0071     QString m_resourceType;
0072     KisTagSP m_tag;
0073 };
0074 
0075 
0076 class FolderItem : public KisResourceStorage::ResourceItem
0077 {
0078 public:
0079     ~FolderItem() override {}
0080 };
0081 
0082 
0083 KisFolderStorage::KisFolderStorage(const QString &location)
0084     : KisStoragePlugin(location)
0085 {
0086 }
0087 
0088 KisFolderStorage::~KisFolderStorage()
0089 {
0090 }
0091 
0092 bool KisFolderStorage::saveAsNewVersion(const QString &resourceType, KoResourceSP _resource)
0093 {
0094     return KisStorageVersioningHelper::addVersionedResource(location() + "/" + resourceType, _resource, 0);
0095 }
0096 
0097 KisResourceStorage::ResourceItem KisFolderStorage::resourceItem(const QString &url)
0098 {
0099     QFileInfo fi(url);
0100     FolderItem item;
0101     item.url = url;
0102     item.folder = fi.path().split("/").last();
0103     item.lastModified = fi.lastModified();
0104     return item;
0105 }
0106 
0107 bool KisFolderStorage::loadVersionedResource(KoResourceSP resource)
0108 {
0109     QFileInfo fi(location() + '/' + resource->resourceType().first + '/' + resource->filename());
0110 
0111     QFile f(fi.absoluteFilePath());
0112     if (!f.open(QFile::ReadOnly)) {
0113         qWarning() << "Could not open" << fi.absoluteFilePath() << "for reading";
0114         return false;
0115     }
0116 
0117     bool r = resource->loadFromDevice(&f, KisGlobalResourcesInterface::instance());
0118 
0119     // Check for the thumbnail
0120     if (r) {
0121         sanitizeResourceFileNameCase(resource, fi.dir());
0122 
0123         if ((resource->image().isNull() || resource->thumbnail().isNull()) && !resource->thumbnailPath().isNull()) {
0124             QImage img(location() + '/' + resource->resourceType().first + '/' + resource->thumbnailPath());
0125             resource->setImage(img);
0126             resource->updateThumbnail();
0127         }
0128     }
0129 
0130     return r;
0131 }
0132 
0133 QString KisFolderStorage::resourceMd5(const QString &url)
0134 {
0135     QString result;
0136 
0137     QFile file(location() + "/" + url);
0138     if (file.exists() && file.open(QIODevice::ReadOnly)) {
0139         result = KoMD5Generator::generateHash(file.readAll());
0140     }
0141 
0142     return result;
0143 }
0144 
0145 QString KisFolderStorage::resourceFilePath(const QString &url)
0146 {
0147     QFileInfo file(location() + "/" + url);
0148     return file.exists() ? file.absoluteFilePath() : QString();
0149 }
0150 
0151 QSharedPointer<KisResourceStorage::ResourceIterator> KisFolderStorage::resources(const QString &resourceType)
0152 {
0153     QVector<VersionedResourceEntry> entries;
0154 
0155     const QString resourcesSaveLocation = location() + "/" + resourceType;
0156 
0157     QDirIterator it(resourcesSaveLocation,
0158                     KisResourceLoaderRegistry::instance()->filters(resourceType),
0159                     QDir::Files | QDir::Readable,
0160                     QDirIterator::Subdirectories);;
0161 
0162     while (it.hasNext()) {
0163         it.next();
0164         QFileInfo info(it.fileInfo());
0165 
0166         VersionedResourceEntry entry;
0167         entry.filename = it.filePath().mid(resourcesSaveLocation.size() + 1);
0168 
0169         // Don't load 4.x backup resources
0170         if (entry.filename.contains("backup")) {
0171             continue;
0172         }
0173 
0174         entry.lastModified = info.lastModified();
0175         entry.tagList = {}; // TODO
0176         entry.resourceType = resourceType;
0177         entries.append(entry);
0178     }
0179 
0180     KisStorageVersioningHelper::detectFileVersions(entries);
0181 
0182     return toQShared(new KisVersionedStorageIterator(entries, this));
0183 }
0184 
0185 QSharedPointer<KisResourceStorage::TagIterator> KisFolderStorage::tags(const QString &resourceType)
0186 {
0187     return QSharedPointer<KisResourceStorage::TagIterator>(new FolderTagIterator(location(), resourceType));
0188 }
0189 
0190 bool KisFolderStorage::importResource(const QString &url, QIODevice *device)
0191 {
0192     bool result = false;
0193 
0194     const QString resourcesLocation = location() + "/" + url;
0195 
0196     QFile f(resourcesLocation);
0197 
0198     if (f.exists()) return result;
0199 
0200     if (f.open(QFile::WriteOnly)) {
0201         qint64 writtenBytes = f.write(device->readAll());
0202         f.close();
0203         result = (writtenBytes == device->size());
0204     } else {
0205         qWarning() << "Cannot open" << resourcesLocation << "for writing";
0206     }
0207 
0208     KoResourceSP resourceAfterLoading = resource(url);
0209 
0210     if (resourceAfterLoading.isNull()) {
0211         f.remove();
0212         return false;
0213     }
0214 
0215     return result;
0216 }
0217 
0218 bool KisFolderStorage::exportResource(const QString &url, QIODevice *device)
0219 {
0220     bool result = false;
0221 
0222     const QString resourcesLocation = location() + "/" + url;
0223 
0224     QFile f(resourcesLocation);
0225 
0226     if (!f.exists()) return result;
0227 
0228     if (f.open(QFile::ReadOnly)) {
0229         device->write(f.readAll());
0230         f.close();
0231         result = true;
0232     } else {
0233         qWarning() << "Cannot open" << resourcesLocation << "for reading";
0234     }
0235 
0236     return result;
0237 }
0238 
0239 bool KisFolderStorage::addResource(const QString &resourceType, KoResourceSP resource)
0240 {
0241     if (!resource || !resource->valid()) return false;
0242 
0243     const QString resourcesSaveLocation = location() + "/" + resourceType;
0244 
0245     QFileInfo fi(resourcesSaveLocation + "/" + resource->filename());
0246     if (fi.exists()) {
0247         qWarning() << "Resource" << resourceType << resource->filename() << "already exists in" << resourcesSaveLocation;
0248         return false;
0249     }
0250 
0251     QFile resourceFile(fi.absoluteFilePath());
0252     if (!resourceFile.open(QFile::WriteOnly)) {
0253         qWarning() << "Could not open" << fi.absoluteFilePath() << "for writing.";
0254         return false;
0255     }
0256 
0257     if (!resource->saveToDevice(&resourceFile)) {
0258         qWarning() << "Could not save resource to" << fi.absoluteFilePath();
0259         resourceFile.close();
0260         return false;
0261     }
0262     resourceFile.close();
0263 
0264 
0265 
0266 
0267     return true;
0268 }
0269 
0270 QStringList KisFolderStorage::metaDataKeys() const
0271 {
0272     return QStringList() << KisResourceStorage::s_meta_name;
0273 }
0274 
0275 QVariant KisFolderStorage::metaData(const QString &key) const
0276 {
0277     if (key == KisResourceStorage::s_meta_name) {
0278         return i18n("Local Resources");
0279     }
0280     return QVariant();
0281 
0282 }