Krita Source Code Documentation
Loading...
Searching...
No Matches
KisResourceLocator.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
8
9#include <QApplication>
10#include <QDebug>
11#include <QList>
12#include <QDir>
13#include <QDirIterator>
14#include <QFileInfo>
15#include <QMessageBox>
16#include <QVersionNumber>
17#include <QSqlQuery>
18#include <QSqlError>
19#include <QBuffer>
20
21#include <kconfig.h>
22#include <kconfiggroup.h>
23#include <ksharedconfig.h>
24#include <klocalizedstring.h>
25
26#include <KritaVersionWrapper.h>
27#include <KisMimeDatabase.h>
28#include <kis_assert.h>
29#include <kis_debug.h>
30#include <KisUsageLogger.h>
31#include <KisFileUtils.h>
32
33#include "KoResourcePaths.h"
34#include "KisResourceStorage.h"
35#include "KisResourceCacheDb.h"
37#include "KisMemoryStorage.h"
40#include <KisStorageModel.h>
41#include <KoMD5Generator.h>
44
45#include "ResourceDebug.h"
46
47const QString KisResourceLocator::resourceLocationKey {"ResourceDirectory"};
48
50public:
52 QMap<QString, KisResourceStorageSP> storages;
53 QHash<QPair<QString, QString>, KoResourceSP> resourceCache;
54 QMap<QPair<QString, QString>, KisTagSP> tagCache;
56
57 KisResourceStorageSP safeGetStorage(const QString &storageLocation) {
64 if (!storages.contains(storageLocation)) {
65 qWarning() << "WARNING: KisResourceLocator: failed to find a storage with location" << storageLocation;
66 return nullptr;
67 }
68
69 return storages[storageLocation];
70 }
71};
72
74 : QObject(parent)
75 , d(new Private())
76{
77}
78
80{
81 // Not a regular Q_GLOBAL_STATIC, because we want this deleted as
82 // part of the app destructor.
83 KisResourceLocator *locator = qApp->findChild<KisResourceLocator *>(QString());
84 if (!locator) {
85 locator = new KisResourceLocator(qApp);
86 }
87 return locator;
88}
89
93
94KisResourceLocator::LocatorError KisResourceLocator::initialize(const QString &installationResourcesLocation)
95{
97
98 d->resourceLocation = KoResourcePaths::getAppDataLocation();
99
100 if (!d->resourceLocation.endsWith('/')) d->resourceLocation += '/';
101
102 QFileInfo fi(d->resourceLocation);
103
104 if (!fi.exists()) {
105 if (!QDir().mkpath(d->resourceLocation)) {
106 d->errorMessages << i18n("1. Could not create the resource location at %1.", d->resourceLocation);
108 }
109 initializationStatus = InitializationStatus::FirstRun;
110 }
111
112 if (!fi.isWritable()) {
113 d->errorMessages << i18n("2. The resource location at %1 is not writable.", d->resourceLocation);
115 }
116
117 // Check whether we're updating from an older version
118 if (initializationStatus != InitializationStatus::FirstRun) {
119 QFile fi(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION");
120 if (!fi.open(QFile::ReadOnly)) {
121 initializationStatus = InitializationStatus::FirstUpdate;
122 }
123 else {
124 QVersionNumber resource_version = QVersionNumber::fromString(QString::fromUtf8(fi.readAll()));
125 QVersionNumber krita_version = QVersionNumber::fromString(KritaVersionWrapper::versionString());
126 if (krita_version > resource_version) {
127 initializationStatus = InitializationStatus::Updating;
128 }
129 else {
130 initializationStatus = InitializationStatus::Initialized;
131 }
132 }
133 }
134
135 if (initializationStatus != InitializationStatus::Initialized) {
136 KisResourceLocator::LocatorError res = firstTimeInstallation(initializationStatus, installationResourcesLocation);
137 if (res != LocatorError::Ok) {
138 return res;
139 }
140 initializationStatus = InitializationStatus::Initialized;
141 }
142
143 if (!synchronizeDb()) {
145 }
146
147 return LocatorError::Ok;
148}
149
151{
152 return d->errorMessages;
153}
154
156{
157 return d->resourceLocation;
158}
159
160bool KisResourceLocator::resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const
161{
162 storageLocation = makeStorageLocationAbsolute(storageLocation);
163 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
164
165 return d->resourceCache.contains(key);
166}
167
169{
170 auto loadResourcesGroup =
171 [this, parentResource = resource] (QList<KoResourceLoadResult> resources,
172 const QString &resourceGroup) {
173
174 Q_FOREACH (KoResourceLoadResult res, resources) {
175 switch (res.type())
176 {
178 KIS_SAFE_ASSERT_RECOVER_NOOP(res.resource()->resourceId() >= 0);
179 break;
182 QByteArray data = res.embeddedResource().data();
183 QBuffer buffer(&data);
184 buffer.open(QBuffer::ReadOnly);
185 importResourceDeduplicateFileName(sig.type, sig.filename, &buffer, "memory");
186 break;
187 }
189 qWarning() << "Failed to load" << resourceGroup << "resource:" << res.signature();
190 break;
191 }
192 }
193 };
194
199 loadResourcesGroup(resource->takeSideLoadedResources(KisGlobalResourcesInterface::instance()), "side-loaded");
200
204 loadResourcesGroup(resource->requiredResources(KisGlobalResourcesInterface::instance()), "linked");
205}
206
207KisTagSP KisResourceLocator::tagForUrl(const QString &tagUrl, const QString resourceType)
208{
209 if (d->tagCache.contains(QPair<QString, QString>(resourceType, tagUrl))) {
210 return d->tagCache[QPair<QString, QString>(resourceType, tagUrl)];
211 }
212
213 KisTagSP tag = tagForUrlNoCache(tagUrl, resourceType);
214
215 if (tag && tag->valid()) {
216 d->tagCache[QPair<QString, QString>(resourceType, tagUrl)] = tag;
217 }
218
219 return tag;
220}
221
222KisTagSP KisResourceLocator::tagForUrlNoCache(const QString &tagUrl, const QString resourceType)
223{
224 QSqlQuery query;
225 bool r = query.prepare("SELECT tags.id\n"
226 ", tags.url\n"
227 ", tags.active\n"
228 ", tags.name\n"
229 ", tags.comment\n"
230 ", tags.filename\n"
231 ", resource_types.name as resource_type\n"
232 ", resource_types.id\n"
233 "FROM tags\n"
234 ", resource_types\n"
235 "WHERE tags.resource_type_id = resource_types.id\n"
236 "AND resource_types.name = :resource_type\n"
237 "AND tags.url = :tag_url\n");
238
239 if (!r) {
240 qWarning() << "Could not prepare KisResourceLocator::tagForUrl query" << query.lastError();
241 return KisTagSP();
242 }
243
244 query.bindValue(":resource_type", resourceType);
245 query.bindValue(":tag_url", tagUrl);
246
247 r = query.exec();
248 if (!r) {
249 qWarning() << "Could not execute KisResourceLocator::tagForUrl query" << query.lastError() << query.boundValues();
250 return KisTagSP();
251 }
252
253 r = query.first();
254 if (!r) {
255 return KisTagSP();
256 }
257
258 KisTagSP tag(new KisTag());
259
260 int tagId = query.value("tags.id").toInt();
261 int resourceTypeId = query.value("resource_types.id").toInt();
262
263 tag->setUrl(query.value("url").toString());
264 tag->setResourceType(resourceType);
265 tag->setId(query.value("id").toInt());
266 tag->setActive(query.value("active").toBool());
267 tag->setName(query.value("name").toString());
268 tag->setComment(query.value("comment").toString());
269 tag->setFilename(query.value("filename").toString());
270 tag->setValid(true);
271
272
273 QMap<QString, QString> names;
274 QMap<QString, QString> comments;
275
276 r = query.prepare("SELECT language\n"
277 ", name\n"
278 ", comment\n"
279 "FROM tag_translations\n"
280 "WHERE tag_id = :id");
281
282 if (!r) {
283 qWarning() << "Could not prepare KisResourceLocator::tagForUrl translation query" << query.lastError();
284 }
285
286 query.bindValue(":id", tag->id());
287
288 if (!query.exec()) {
289 qWarning() << "Could not execute KisResourceLocator::tagForUrl translation query" << query.lastError();
290 }
291
292 while (query.next()) {
293 names[query.value(0).toString()] = query.value(1).toString();
294 comments[query.value(0).toString()] = query.value(2).toString();
295 }
296
297 tag->setNames(names);
298 tag->setComments(comments);
299
300 QSqlQuery defaultResourcesQuery;
301
302 if (!defaultResourcesQuery.prepare("SELECT resources.filename\n"
303 "FROM resources\n"
304 ", resource_tags\n"
305 "WHERE resource_tags.tag_id = :tag_id\n"
306 "AND resources.resource_type_id = :type_id\n"
307 "AND resource_tags.resource_id = resources.id\n"
308 "AND resource_tags.active = 1\n")) {
309 qWarning() << "Could not prepare resource/tag query" << defaultResourcesQuery.lastError();
310 }
311
312 defaultResourcesQuery.bindValue(":tag_id", tagId);
313 defaultResourcesQuery.bindValue(":type_id", resourceTypeId);
314
315 if (!defaultResourcesQuery.exec()) {
316 qWarning() << "Could not execute resource/tag query" << defaultResourcesQuery.lastError();
317 }
318
319 QStringList resourceFileNames;
320
321 while (defaultResourcesQuery.next()) {
322 resourceFileNames << defaultResourcesQuery.value("resources.filename").toString();
323 }
324
325 tag->setDefaultResources(resourceFileNames);
326
327 return tag;
328}
329
330
331KoResourceSP KisResourceLocator::resource(QString storageLocation, const QString &resourceType, const QString &filename)
332{
333 storageLocation = makeStorageLocationAbsolute(storageLocation);
334
335 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
336
338 if (d->resourceCache.contains(key)) {
339 resource = d->resourceCache[key];
340 }
341 else {
342 KisResourceStorageSP storage = d->safeGetStorage(storageLocation);
343 if (!storage) {
344 return 0;
345 }
346
347 resource = storage->resource(resourceType + "/" + filename);
348
349 if (resource) {
350 d->resourceCache[key] = resource;
351 // load all the embedded resources into temporary "memory" storage
353 }
354 }
355
356 if (!resource) {
357 qWarning() << "KoResourceSP KisResourceLocator::resource" << storageLocation << resourceType << filename << "was not found";
358 return 0;
359 }
360
361 resource->setStorageLocation(storageLocation);
362 Q_ASSERT(!resource->storageLocation().isEmpty());
363
364 if (resource->resourceId() < 0 || resource->version() < 0) {
365 QSqlQuery q;
366 if (!q.prepare("SELECT resources.id\n"
367 ", versioned_resources.version as version\n"
368 ", versioned_resources.md5sum as md5sum\n"
369 ", resources.name\n"
370 ", resources.status\n"
371 "FROM resources\n"
372 ", storages\n"
373 ", resource_types\n"
374 ", versioned_resources\n"
375 "WHERE storages.id = resources.storage_id\n"
376 "AND storages.location = :storage_location\n"
377 "AND resource_types.id = resources.resource_type_id\n"
378 "AND resource_types.name = :resource_type\n"
379 "AND resources.filename = :filename\n"
380 "AND versioned_resources.resource_id = resources.id\n"
381 "AND versioned_resources.version = (SELECT MAX(version) FROM versioned_resources WHERE versioned_resources.resource_id = resources.id)")) {
382 qWarning() << "Could not prepare id/version query" << q.lastError();
383
384 }
385
386 q.bindValue(":storage_location", makeStorageLocationRelative(storageLocation));
387 q.bindValue(":resource_type", resourceType);
388 q.bindValue(":filename", filename);
389
390 if (!q.exec()) {
391 qWarning() << "Could not execute id/version query" << q.lastError() << q.boundValues();
392 }
393
394 if (!q.first()) {
395 qWarning() << "Could not find the resource in the database" << storageLocation << resourceType << filename;
396 }
397
398 resource->setResourceId(q.value(0).toInt());
399 Q_ASSERT(resource->resourceId() >= 0);
400
401 resource->setVersion(q.value(1).toInt());
402 Q_ASSERT(resource->version() >= 0);
403
404 resource->setMD5Sum(q.value(2).toString());
405 Q_ASSERT(!resource->md5Sum().isEmpty());
406
407 resource->setActive(q.value(4).toBool());
408
409 // To override resources that use the filename for the name, which is versioned, and we don't want the version number in the name
410 resource->setName(q.value(3).toString());;
411 }
412
413 if (!resource) {
414 qWarning() << "Could not find resource" << resourceType + "/" + filename;
415 return 0;
416 }
417
418 return resource;
419}
420
427
428bool KisResourceLocator::setResourceActive(int resourceId, bool active)
429{
430 // First remove the resource from the cache
431 ResourceStorage rs = getResourceStorage(resourceId);
432 QPair<QString, QString> key = QPair<QString, QString> (rs.storageLocation, rs.resourceType + "/" + rs.resourceFileName);
433
434 d->resourceCache.remove(key);
435 if (!active) {
437 }
438
439 bool result = KisResourceCacheDb::setResourceActive(resourceId, active);
440
441 Q_EMIT resourceActiveStateChanged(rs.resourceType, resourceId);
442
443 return result;
444}
445
446KoResourceSP KisResourceLocator::importResourceFromFile(const QString &resourceType, const QString &fileName, const bool allowOverwrite, const QString &storageLocation)
447{
448 QFile f(fileName);
449 if (!f.open(QFile::ReadOnly)) {
450 qWarning() << "Could not open" << fileName << "for loading";
451 return nullptr;
452 }
453
454 return importResource(resourceType, fileName, &f, allowOverwrite, storageLocation);
455}
456
457KoResourceSP KisResourceLocator::importResource(const QString &resourceType, const QString &fileName, QIODevice *device, const bool allowOverwrite, const QString &storageLocation)
458{
459 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
460 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, nullptr);
461
462 QByteArray resourceData = device->readAll();
464
465 {
466 QBuffer buf(&resourceData);
467 buf.open(QBuffer::ReadOnly);
468
470
471 if (!loader) {
472 qWarning() << "Could not import" << fileName << ": resource doesn't load.";
473 return nullptr;
474 }
475
476 resource = loader->load(QFileInfo(fileName).fileName(), buf, KisGlobalResourcesInterface::instance());
477 }
478
479 if (!resource || !resource->valid()) {
480 qWarning() << "Could not import" << fileName << ": resource doesn't load.";
481 return nullptr;
482 }
483
484 const QString md5 = KoMD5Generator::generateHash(resourceData);
485 const QString resourceUrl = resourceType + "/" + resource->filename();
486
487 KoResourceSP existingResource = storage->resource(resourceUrl);
488
489 if (existingResource) {
490 const QString existingResourceMd5Sum = storage->resourceMd5(resourceUrl);
491
492 if (!allowOverwrite) {
493 return nullptr;
494 }
495
496 if (existingResourceMd5Sum == md5 &&
497 existingResource->filename() == resource->filename()) {
498
506 int existingResourceId = -1;
507 bool r = KisResourceCacheDb::getResourceIdFromFilename(existingResource->filename(), resourceType, storageLocation, existingResourceId);
508
509 if (r && existingResourceId > 0) {
510 return resourceForId(existingResourceId);
511 }
512 }
513
514 qWarning() << "A resource with the same filename but a different MD5 already exists in the storage" << resourceType << fileName << storageLocation;
515 if (storageLocation == "") {
516 qWarning() << "Proceeding with overwriting the existing resource...";
517 // remove all versions of the resource from the resource folder
518 QStringList versionsLocations;
519
520 // this resource has id -1, we need correct id
521 int existingResourceId = -1;
522 bool r = KisResourceCacheDb::getResourceIdFromVersionedFilename(existingResource->filename(), resourceType, storageLocation, existingResourceId);
523
524 if (r && existingResourceId >= 0) {
525 if (KisResourceCacheDb::getAllVersionsLocations(existingResourceId, versionsLocations)) {
526
527 for (int i = 0; i < versionsLocations.size(); i++) {
528 QFileInfo fi(this->resourceLocationBase() + "/" + resourceType + "/" + versionsLocations[i]);
529 if (fi.exists()) {
530 r = QFile::remove(fi.filePath());
531 if (!r) {
532 qWarning() << "KisResourceLocator::importResourceFromFile: Removal of " << fi.filePath()
533 << "was requested, but it wasn't possible, something went wrong.";
534 }
535 } else {
536 qWarning() << "KisResourceLocator::importResourceFromFile: Removal of " << fi.filePath()
537 << "was requested, but it doesn't exist.";
538 }
539 }
540 } else {
541 qWarning() << "KisResourceLocator::importResourceFromFile: Finding all locations for " << existingResourceId << "was requested, but it failed.";
542 return nullptr;
543 }
544 } else {
545 qWarning() << "KisResourceLocator::importResourceFromFile: there is no resource file found in the location of " << storageLocation << resource->filename() << resourceType;
546 return nullptr;
547 }
548
549 Q_EMIT beginExternalResourceRemove(resourceType, {existingResourceId});
550
551 // remove everything related to this resource from the database (remember about tags and versions!!!)
552 r = KisResourceCacheDb::removeResourceCompletely(existingResourceId);
553
554 {
555 const QString absoluteStorageLocation = makeStorageLocationAbsolute(resource->storageLocation());
556 KisResourceThumbnailCache::instance()->remove(absoluteStorageLocation, resourceType, existingResource->filename());
557 }
558
559 Q_EMIT endExternalResourceRemove(resourceType);
560
561 if (!r) {
562 qWarning() << "KisResourceLocator::importResourceFromFile: Removing resource with id " << existingResourceId << "completely from the database failed.";
563 return nullptr;
564 }
565
566 } else {
567 qWarning() << "KisResourceLocator::importResourceFromFile: Overwriting of the resource was denied, aborting import.";
568 return nullptr;
569 }
570 }
571
572 QBuffer buf(&resourceData);
573 buf.open(QBuffer::ReadOnly);
574
575 if (storage->importResource(resourceUrl, &buf)) {
576 resource = storage->resource(resourceUrl);
577
578 if (!resource) {
579 qWarning() << "Could not retrieve imported resource from the storage" << resourceType << fileName << storageLocation;
580 return nullptr;
581 }
582
583 resource->setStorageLocation(storageLocation);
584 resource->setMD5Sum(storage->resourceMd5(resourceUrl));
585 resource->setVersion(0);
586 resource->setDirty(false);
588
589 Q_EMIT beginExternalResourceImport(resourceType, 1);
590
591 // Insert into the database
592 const bool result = KisResourceCacheDb::addResource(storage,
593 storage->timeStampForResource(resourceType, resource->filename()),
594 resource,
595 resourceType);
596
597 Q_EMIT endExternalResourceImport(resourceType);
598
599 if (!result) {
600 return nullptr;
601 }
602
603 // resourceCaches use absolute locations
604 const QString absoluteStorageLocation = makeStorageLocationAbsolute(resource->storageLocation());
605 const QPair<QString, QString> key = {absoluteStorageLocation, resourceType + "/" + resource->filename()};
606 // Add to the cache
607 d->resourceCache[key] = resource;
609
610 return resource;
611 }
612
613 return nullptr;
614}
615
616namespace {
617QString findDeduplicatedFileName(const QString &resourceType, const QString &proposedFileName, KisResourceStorageSP storage)
618{
619
620 auto fileAllowedCallback = [resourceType, storage] (const QString &fileName) {
621 QString resourceUrl = resourceType + "/" + fileName;
622 return !storage->resource(resourceUrl);
623 };
624
625 return KritaUtils::deduplicateFileName(proposedFileName, "_embedded_", fileAllowedCallback);
626}
627}
628
629KoResourceSP KisResourceLocator::importResourceDeduplicateFileName(const QString &resourceType, const QString &proposedFileName, QIODevice *device, const QString &storageLocation)
630{
631 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
632 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, nullptr);
633
634 const QString fileName = findDeduplicatedFileName(resourceType, proposedFileName, storage);
635 return importResource(resourceType, fileName, device, false, storageLocation);
636}
637
638bool KisResourceLocator::addResourceDeduplicateFileName(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
639{
640 // fix filename is missing
641 if (resource->filename().isEmpty()) {
642 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
643 }
644
645 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!resource->filename().isEmpty(), false);
646
647 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
649
650 const QString fileName = findDeduplicatedFileName(resourceType, resource->filename(), storage);
651 resource->setFilename(fileName);
652 return addResource(resourceType, resource, storageLocation);
653}
654
655bool KisResourceLocator::importWillOverwriteResource(const QString &resourceType, const QString &fileName, const QString &storageLocation) const
656{
657 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
659
660 const QString resourceUrl = resourceType + "/" + QFileInfo(fileName).fileName();
661
662 KoResourceSP existingResource = storage->resource(resourceUrl);
663
664 return !existingResource.isNull();
665}
666
667bool KisResourceLocator::exportResource(KoResourceSP resource, QIODevice *device)
668{
669 if (!resource || !resource->valid() || resource->resourceId() < 0) return false;
670
671 const QString resourceUrl = resource->resourceType().first + "/" + resource->filename();
672 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(resource->storageLocation()));
674 return storage->exportResource(resourceUrl, device);
675}
676
677bool KisResourceLocator::addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
678{
679 if (!resource || !resource->valid()) return false;
680
681 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
683
684 //If we have gotten this far and the resource still doesn't have a filename to save to, we should generate one.
685 if (resource->filename().isEmpty()) {
686 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
687 }
688
689 if (resource->version() != 0) { // Can happen with cloned resources
690 resource->setVersion(0);
691 }
692
693 // Save the resource to the storage storage
694 if (!storage->addResource(resource)) {
695 qWarning() << "Could not add resource" << resource->filename() << "to the storage" << storageLocation;
696 return false;
697 }
698
699 resource->setStorageLocation(storageLocation);
700 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
701 resource->setDirty(false);
703
704 d->resourceCache[QPair<QString, QString>(storageLocation, resourceType + "/" + resource->filename())] = resource;
705
711 const bool result = KisResourceCacheDb::addResource(storage,
712 storage->timeStampForResource(resourceType, resource->filename()),
713 resource,
714 resourceType);
715 return result;
716}
717
718bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource)
719{
720 QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation());
721
722 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
724
725 if (resource->resourceId() < 0) {
726 return addResource(resourceType, resource);
727 }
728
729 if (!storage->supportsVersioning()) return false;
730
731 // remove older version
732 KisResourceThumbnailCache::instance()->remove(storageLocation, resourceType, resource->filename());
733
734 resource->updateThumbnail();
735 resource->setVersion(resource->version() + 1);
736 resource->setActive(true);
737
738 if (!storage->saveAsNewVersion(resource)) {
739 qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation;
740 return false;
741 }
742
743 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
744 resource->setDirty(false);
746
747 // The version needs already to have been incremented
748 if (!KisResourceCacheDb::addResourceVersion(resource->resourceId(), QDateTime::currentDateTime(), storage, resource)) {
749 qWarning() << "Failed to add a new version of the resource to the database" << resource->name();
750 return false;
751 }
752
753 if (!setMetaDataForResource(resource->resourceId(), resource->metadata())) {
754 qWarning() << "Failed to update resource metadata" << resource;
755 return false;
756 }
757
758 // Update the resource in the cache
759 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + resource->filename());
760 d->resourceCache[key] = resource;
762
763 return true;
764}
765
766bool KisResourceLocator::reloadResource(const QString &resourceType, const KoResourceSP resource)
767{
768 // This resource isn't in the database yet, so we cannot reload it
769 if (resource->resourceId() < 0) return false;
770
771 QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation());
772 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
774
775 if (!storage->loadVersionedResource(resource)) {
776 qWarning() << "Failed to reload the resource" << resource->name() << "from storage" << storageLocation;
777 return false;
778 }
779
780 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
781 resource->setDirty(false);
783
784 // We haven't changed the version of the resource, so the cache must be still valid
785 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + resource->filename());
786 Q_ASSERT(d->resourceCache[key] == resource);
787
788 return true;
789}
790
791QMap<QString, QVariant> KisResourceLocator::metaDataForResource(int id) const
792{
793 return KisResourceCacheDb::metaDataForId(id, "resources");
794}
795
796bool KisResourceLocator::setMetaDataForResource(int id, QMap<QString, QVariant> map) const
797{
798 return KisResourceCacheDb::updateMetaDataForId(map, id, "resources");
799}
800
801QMap<QString, QVariant> KisResourceLocator::metaDataForStorage(const QString &storageLocation) const
802{
803 QMap<QString, QVariant> metadata;
804
805 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
806 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, metadata);
807
808 Q_FOREACH(const QString key, storage->metaDataKeys()) {
809 metadata[key] = storage->metaData(key);
810 }
811 return metadata;
812}
813
814void KisResourceLocator::setMetaDataForStorage(const QString &storageLocation, QMap<QString, QVariant> map) const
815{
816 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
818
819 Q_FOREACH(const QString &key, map.keys()) {
820 storage->setMetaData(key, map[key]);
821 }
822}
823
824void KisResourceLocator::purge(const QString &storageLocation, const QVector<int> &removedTagIds)
825{
826 Q_FOREACH(const auto key, d->resourceCache.keys()) {
827 if (key.first == storageLocation) {
828 d->resourceCache.remove(key);
830 }
831 }
832
833 for (auto it = d->tagCache.begin(); it != d->tagCache.end();) {
834 if (removedTagIds.contains(it.value()->id())) {
835 it = d->tagCache.erase(it);
836 } else {
837 ++it;
838 }
839 }
840}
841
842bool KisResourceLocator::addStorage(const QString &storageLocation, KisResourceStorageSP storage)
843{
844 if (d->storages.contains(storageLocation)) {
845 if (!removeStorage(storageLocation)) {
846 qWarning() << "could not remove" << storageLocation;
847 return false;
848 }
849 }
850
851 QVector<std::pair<QString, int>> addedResources;
852 Q_FOREACH(const QString &type, KisResourceLoaderRegistry::instance()->resourceTypes()) {
853 int numAddedResources = 0;
854
855 QSharedPointer<KisResourceStorage::ResourceIterator> it = storage->resources(type);
856 while (it->hasNext()) {
857 it->next();
858 numAddedResources++;
859 }
860
861 if (numAddedResources > 0) {
862 addedResources << std::make_pair(type, numAddedResources);
863 }
864 }
865
866 Q_FOREACH (const auto &typedResources, addedResources) {
867 Q_EMIT beginExternalResourceImport(typedResources.first, typedResources.second);
868 }
869
870 d->storages[storageLocation] = storage;
871 if (!KisResourceCacheDb::addStorage(storage, false)) {
872 d->errorMessages.append(i18n("Could not add %1 to the database", storage->location()));
873 qWarning() << d->errorMessages;
874 return false;
875 }
876
878 d->errorMessages.append(QString("Could not add tags for storage %1 to the cache database").arg(storage->location()));
879 qWarning() << d->errorMessages;
880 return false;
881 }
882
883 Q_FOREACH (const auto &typedResources, addedResources) {
884 Q_EMIT endExternalResourceImport(typedResources.first);
885 }
886
887 Q_EMIT storageAdded(makeStorageLocationRelative(storage->location()));
888 return true;
889}
890
891bool KisResourceLocator::removeStorage(const QString &storageLocation)
892{
893 // Cloned documents have a document storage, but that isn't in the locator.
894 if (!d->storages.contains(storageLocation)) {
895 return true;
896 }
897
899
900 Q_FOREACH(const QString &type, KisResourceLoaderRegistry::instance()->resourceTypes()) {
901 const QVector<int> resources = KisResourceCacheDb::resourcesForStorage(type, storageLocation);
902 if (!resources.isEmpty()) {
903 removedResources << std::make_pair(type, resources);
904 }
905 }
906
908 QVector<int> allRemovedTagIds;
909 Q_FOREACH(const QString &type, KisResourceLoaderRegistry::instance()->resourceTypes()) {
910 auto [uniqueTags, sharedTags] = KisResourceCacheDb::tagsForStorage(type, storageLocation);
911 removedTags << std::make_pair(type, uniqueTags);
912 allRemovedTagIds << uniqueTags;
913 }
914
915 Q_FOREACH (const auto &typedResources, removedResources) {
916 Q_EMIT beginExternalResourceRemove(typedResources.first, typedResources.second);
917 }
918
919 // TODO: add model notification about removed tags
920 Q_UNUSED(removedTags);
921
922 purge(storageLocation, allRemovedTagIds);
923
924 KisResourceStorageSP storage = d->storages.take(storageLocation);
925
926 if (!KisResourceCacheDb::deleteStorage(storage)) {
927 d->errorMessages.append(i18n("Could not remove storage %1 from the database", storage->location()));
928 qWarning() << d->errorMessages;
929 return false;
930 }
931
932 Q_FOREACH (const auto &typedResources, removedResources) {
933 Q_EMIT endExternalResourceRemove(typedResources.first);
934 }
935
936 Q_EMIT storageRemoved(makeStorageLocationRelative(storage->location()));
937
938 return true;
939}
940
941bool KisResourceLocator::hasStorage(const QString &document)
942{
943 return d->storages.contains(document);
944}
945
947{
948 QSqlQuery query;
949
950 if (!query.prepare("SELECT tags.url \n"
951 ", resource_types.name \n"
952 "FROM tags\n"
953 ", resource_types\n"
954 "WHERE tags.resource_type_id = resource_types.id\n"))
955 {
956 qWarning() << "Could not prepare save tags query" << query.lastError();
957 return;
958 }
959
960 if (!query.exec()) {
961 qWarning() << "Could not execute save tags query" << query.lastError();
962 return;
963 }
964
965 // this needs to use ResourcePaths because it is sometimes called during initialization
966 // (when the database versions don't match up and tags need to be saved)
967 QString resourceLocation = KoResourcePaths::getAppDataLocation() + "/";
968
969 while (query.next()) {
970 // Save tag...
971 KisTagSP tag = tagForUrlNoCache(query.value("tags.url").toString(),
972 query.value("resource_types.name").toString());
973
974 if (!tag || !tag->valid()) {
975 continue;
976 }
977
978
979 QString filename = tag->filename();
980 if (filename.isEmpty() || QFileInfo(filename).suffix().isEmpty()) {
981 filename = tag->url() + ".tag";
982 }
983
984
985 if (QFileInfo(filename).suffix() != "tag" && QFileInfo(filename).suffix() != "TAG") {
986 // it's either .abr file, or maybe a .bundle
987 // or something else, but not a tag file
988 dbgResources << "Skipping saving tag " << tag->name(false) << filename << tag->resourceType();
989 continue;
990 }
991
992 filename.remove(resourceLocation);
993
994 QFile f(resourceLocation + "/" + tag->resourceType() + '/' + filename);
995
996 if (!f.open(QFile::WriteOnly)) {
997 qWarning () << "Could not open tag file for writing" << f.fileName();
998 continue;
999 }
1000
1001 QBuffer buf;
1002 buf.open(QIODevice::WriteOnly);;
1003
1004 if (!tag->save(buf)) {
1005 qWarning() << "Could not save tag to" << f.fileName();
1006 buf.close();
1007 f.close();
1008 continue;
1009 }
1010
1011 f.write(buf.data());
1012 f.flush();
1013
1014 f.close();
1015 }
1016}
1017
1018void KisResourceLocator::purgeTag(const QString tagUrl, const QString resourceType)
1019{
1020 d->tagCache.remove(QPair<QString, QString>(resourceType, tagUrl));
1021}
1022
1024{
1025 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(resource->storageLocation()));
1026 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, QString());
1027
1028 const QString resourceUrl = resource->resourceType().first + "/" + resource->filename();
1029
1030 return storage->resourceFilePath(resourceUrl);
1031}
1032
1034{
1036 qWarning() << i18n("Could not synchronize updated font registry with the database");
1037 } else {
1038 Q_EMIT storageResynchronized(fontStorage()->location(), false);
1039 }
1040}
1041
1042KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitializationStatus initializationStatus, const QString &installationResourcesLocation)
1043{
1044 Q_EMIT progressMessage(i18n("Krita is running for the first time. Initialization will take some time."));
1045 Q_UNUSED(initializationStatus);
1046
1047 Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1048 QDir dir(d->resourceLocation + '/' + folder + '/');
1049 if (!dir.exists()) {
1050 if (!QDir().mkpath(d->resourceLocation + '/' + folder + '/')) {
1051 d->errorMessages << i18n("3. Could not create the resource location at %1.", dir.path());
1053 }
1054 }
1055 }
1056
1057 Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1058 QDir dir(installationResourcesLocation + '/' + folder + '/');
1059 if (dir.exists()) {
1060 Q_FOREACH(const QString &entry, dir.entryList(QDir::Files | QDir::Readable)) {
1061 QFile f(dir.canonicalPath() + '/'+ entry);
1062 if (!QFileInfo(d->resourceLocation + '/' + folder + '/' + entry).exists()) {
1063 if (!f.copy(d->resourceLocation + '/' + folder + '/' + entry)) {
1064 d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation + '/' + folder + '/' + entry);
1065 }
1066 }
1067 }
1068 }
1069 }
1070
1071 // And add bundles and adobe libraries
1072 QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
1073 QDirIterator iter(installationResourcesLocation, filters, QDir::Files, QDirIterator::Subdirectories);
1074 while (iter.hasNext()) {
1075 iter.next();
1076 Q_EMIT progressMessage(i18n("Installing the resources from bundle %1.", iter.filePath()));
1077 QFile f(iter.filePath());
1078 Q_ASSERT(f.exists());
1079 if (!f.copy(d->resourceLocation + '/' + iter.fileName())) {
1080 d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation);
1081 }
1082 }
1083
1084 QFile f(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION");
1085 if (f.open(QFile::WriteOnly)) {
1086 f.write(KritaVersionWrapper::versionString().toUtf8());
1087 f.close();
1088 } else {
1089 qWarning() << "Could not open" << f.fileName() << "for writing:" << f.errorString();
1090 }
1091
1092 return LocatorError::Ok;
1093}
1094
1096{
1097 d->storages.clear();
1098 d->resourceCache.clear();
1099
1100 // Add the folder
1102 Q_ASSERT(storage->location() == d->resourceLocation);
1103 d->storages[d->resourceLocation] = storage;
1104
1105 // Add the memory storage
1106 d->storages["memory"] = QSharedPointer<KisResourceStorage>::create("memory");
1107 d->storages["memory"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Temporary Resources"));
1108
1109 // Add font storage
1111 if (fontStorage && fontStorage->valid()) {
1112 d->storages["fontregistry"] = fontStorage;
1113 d->storages["fontregistry"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Font Storage"));
1114 }
1115
1116 // And add bundles and adobe libraries
1117 QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
1118 QDirIterator iter(d->resourceLocation, filters, QDir::Files, QDirIterator::Subdirectories);
1119 while (iter.hasNext()) {
1120 iter.next();
1122 if (!storage->valid()) {
1123 // we still add the storage to the list and try to read whatever possible
1124 qWarning() << "KisResourceLocator::findStorages: the storage is invalid" << storage->location();
1125 }
1126 d->storages[storage->location()] = storage;
1127 }
1128
1129 // Add any missing storage types to the resource cache database.
1130 Q_FOREACH(const KisResourceStorage::StorageType &type, KisStoragePluginRegistry::instance()->storageTypes()) {
1132 }
1133}
1134
1136{
1137 return d->storages.values();
1138}
1139
1141{
1142 KisResourceStorageSP storage = d->safeGetStorage(location);
1143 if (!storage || !storage->valid()) {
1144 qWarning() << "Could not retrieve the" << location << "storage object or the object is not valid";
1145 return 0;
1146 }
1147
1148 return storage;
1149}
1150
1152{
1153 return storageByLocation(d->resourceLocation);
1154}
1155
1160
1162{
1163 return storageByLocation("fontregistry");
1164}
1165
1167{
1168 ResourceStorage rs;
1169
1170 QSqlQuery q;
1171 bool r = q.prepare("SELECT storages.location\n"
1172 ", resource_types.name as resource_type\n"
1173 ", resources.filename\n"
1174 "FROM resources\n"
1175 ", storages\n"
1176 ", resource_types\n"
1177 "WHERE resources.id = :resource_id\n"
1178 "AND resources.storage_id = storages.id\n"
1179 "AND resource_types.id = resources.resource_type_id");
1180 if (!r) {
1181 qWarning() << "KisResourceLocator::removeResource: could not prepare query." << q.lastError();
1182 return rs;
1183 }
1184
1185
1186 q.bindValue(":resource_id", resourceId);
1187
1188 r = q.exec();
1189 if (!r) {
1190 qWarning() << "KisResourceLocator::removeResource: could not execute query." << q.lastError();
1191 return rs;
1192 }
1193
1194 q.first();
1195
1196 QString storageLocation = q.value("location").toString();
1197 QString resourceType= q.value("resource_type").toString();
1198 QString resourceFilename = q.value("filename").toString();
1199
1200 rs.storageLocation = makeStorageLocationAbsolute(storageLocation);
1201 rs.resourceType = resourceType;
1202 rs.resourceFileName = resourceFilename;
1203
1204 return rs;
1205}
1206
1207QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const
1208{
1209// debugResource << "makeStorageLocationAbsolute" << storageLocation;
1210
1211 if (storageLocation.isEmpty()) {
1212 return resourceLocationBase();
1213 }
1214
1215 if (QFileInfo(storageLocation).isRelative() && (storageLocation.endsWith(".bundle", Qt::CaseInsensitive)
1216 || storageLocation.endsWith(".asl", Qt::CaseInsensitive)
1217 || storageLocation.endsWith(".abr", Qt::CaseInsensitive))) {
1218 if (resourceLocationBase().endsWith('/') || resourceLocationBase().endsWith("\\")) {
1219 storageLocation = resourceLocationBase() + storageLocation;
1220 }
1221 else {
1222 storageLocation = resourceLocationBase() + '/' + storageLocation;
1223 }
1224 }
1225
1226// debugResource << "\t" << storageLocation;
1227 return storageLocation;
1228}
1229
1231{
1232 Q_EMIT progressMessage(i18n("Synchronizing the resources."));
1233
1234 d->errorMessages.clear();
1235
1236 // Add resource types that have been added since first-time installation.
1237 Q_FOREACH(auto loader, KisResourceLoaderRegistry::instance()->values()) {
1238 KisResourceCacheDb::registerResourceType(loader->resourceType());
1239 }
1240
1241
1242 findStorages();
1243 Q_FOREACH(const KisResourceStorageSP storage, d->storages) {
1245 d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location()));
1246 } else {
1247 Q_EMIT storageResynchronized(storage->location(), true);
1248 }
1249 }
1250
1251 Q_FOREACH(const KisResourceStorageSP storage, d->storages) {
1252 if (!KisResourceCacheDb::addStorageTags(storage)) {
1253 d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location()));
1254 }
1255 }
1256
1258
1268
1269 // now remove the storages that no longer exists
1270 KisStorageModel model;
1271
1272 QList<QString> storagesToRemove;
1273 for (int i = 0; i < model.rowCount(); i++) {
1274 QModelIndex idx = model.index(i, 0);
1275 QString location = model.data(idx, Qt::UserRole + KisStorageModel::Location).toString();
1276 storagesToRemove << location;
1277 }
1278
1279 for (int i = 0; i < storagesToRemove.size(); i++) {
1280 QString location = storagesToRemove[i];
1281 if (!d->storages.contains(this->makeStorageLocationAbsolute(location))) {
1282 if (!KisResourceCacheDb::deleteStorage(location)) {
1283 d->errorMessages.append(i18n("Could not remove storage %1 from the database", this->makeStorageLocationAbsolute(location)));
1284 qWarning() << d->errorMessages;
1285 return false;
1286 }
1287 Q_EMIT storageRemoved(this->makeStorageLocationAbsolute(location));
1288 }
1289 }
1290
1291
1292 d->errorMessages <<
1294
1295 d->resourceCache.clear();
1296 return d->errorMessages.isEmpty();
1297}
1298
1299
1301{
1302// debugResource << "makeStorageLocationRelative" << location << "locationbase" << resourceLocationBase();
1303 return location.remove(resourceLocationBase());
1304}
QList< QString > QStringList
QSharedPointer< KisTag > KisTagSP
Definition KisTag.h:20
static KisResourcesInterfaceSP instance()
static QString mimeTypeForFile(const QString &file, bool checkExistingFiles=true)
Find the mimetype for the given filename. The filename must include a suffix.
static bool addStorage(KisResourceStorageSP storage, bool preinstalled)
static bool addResource(KisResourceStorageSP storage, QDateTime timestamp, KoResourceSP resource, const QString &resourceType)
static bool getAllVersionsLocations(int resourceId, QStringList &outVersionsLocationsList)
static bool addResourceVersion(int resourceId, QDateTime timestamp, KisResourceStorageSP storage, KoResourceSP resource)
addResourceVersion adds a new version of the resource to the database. The resource itself already sh...
static bool removeOrphanedMetaData()
removeOrphanedMetaData Previous versions of Krita never removed metadata, so this function doublechec...
static bool updateMetaDataForId(const QMap< QString, QVariant > map, int id, const QString &tableName)
setMetaDataForId removes all metadata for the given id and table name, and inserts the metadata in th...
static QMap< QString, QVariant > metaDataForId(int id, const QString &tableName)
metaDataForId
static QVector< int > resourcesForStorage(const QString &resourceType, const QString &storageLocation)
static bool getResourceIdFromFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId)
The function will find the resource only if it is the latest version.
static bool deleteStorage(KisResourceStorageSP storage)
Actually delete the storage and all its resources from the database (i.e., nothing is set to inactive...
static bool setResourceActive(int resourceId, bool active=false)
Make this resource active or inactive; this does not remove the resource from disk or from the databa...
static bool removeResourceCompletely(int resourceId)
static bool addStorageTags(KisResourceStorageSP storage)
static std::pair< QVector< int >, QVector< int > > tagsForStorage(const QString &resourceType, const QString &storageLocation)
static bool getResourceIdFromVersionedFilename(QString filename, QString resourceType, QString storageLocation, int &outResourceId)
Note that here you can put even the original filename - any filename from the versioned_resources - a...
static bool registerResourceType(const QString &resourceType)
registerResourceType registers this resource type in the database
static bool synchronizeStorage(KisResourceStorageSP storage)
static bool registerStorageType(const KisResourceStorage::StorageType storageType)
registerStorageType registers this storage type in the database
The KisResourceLoader class is an abstract interface class that must be implemented by actual resourc...
bool load(KoResourceSP resource, QIODevice &dev, KisResourcesInterfaceSP resourcesInterface)
static KisResourceLoaderRegistry * instance()
KisResourceLoaderBase * loader(const QString &resourceType, const QString &mimetype) const
QHash< QPair< QString, QString >, KoResourceSP > resourceCache
QMap< QString, KisResourceStorageSP > storages
QMap< QPair< QString, QString >, KisTagSP > tagCache
KisResourceStorageSP safeGetStorage(const QString &storageLocation)
KisResourceStorageSP folderStorage() const
QMap< QString, QVariant > metaDataForStorage(const QString &storageLocation) const
metaDataForStorage
void progressMessage(const QString &)
LocatorError initialize(const QString &installationResourcesLocation)
initialize Setup the resource locator for use.
QScopedPointer< Private > d
KisTagSP tagForUrl(const QString &tagUrl, const QString resourceType)
tagForUrl create a tag from the database
bool resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const
KoResourceSP resource(QString storageLocation, const QString &resourceType, const QString &filename)
resource finds a physical resource in one of the storages
KoResourceSP importResource(const QString &resourceType, const QString &fileName, QIODevice *device, const bool allowOverwrite, const QString &storageLocation=QString())
importResource
bool addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation=QString())
addResource adds the given resource to the database and potentially a storage
static const QString resourceLocationKey
void updateFontStorage()
This updates the "fontregistry" storage. Called when the font directories change;.
KisResourceStorageSP fontStorage() const
void endExternalResourceRemove(const QString &resourceType)
Emitted when the locator finished importing the embedded resource.
KisResourceStorageSP storageByLocation(const QString &location) const
void storageRemoved(const QString &location)
Emitted whenever a storage is removed.
bool exportResource(KoResourceSP resource, QIODevice *device)
exportResource
QStringList errorMessages() const
errorMessages
QList< KisResourceStorageSP > storages() const
ResourceStorage getResourceStorage(int resourceId) const
bool updateResource(const QString &resourceType, const KoResourceSP resource)
updateResource
void storageAdded(const QString &location)
Emitted whenever a storage is added.
KoResourceSP importResourceDeduplicateFileName(const QString &resourceType, const QString &proposedFileName, QIODevice *device, const QString &storageLocation=QString())
importResourceDeduplicateFileName imports the resource fith file name deduplication
QString resourceLocationBase() const
resourceLocationBase is the place where all resource storages (folder, bundles etc....
bool removeStorage(const QString &storageLocation)
removeStorage removes the temporary storage from the database
KisResourceStorageSP memoryStorage() const
void storagesBulkSynchronizationFinished()
void purge(const QString &storageLocation, const QVector< int > &removedTagIds)
purge purges the local resource cache
void beginExternalResourceImport(const QString &resourceType, int numResources)
Emitted when the locator needs to add an embedded resource.
bool hasStorage(const QString &storageLocation)
hasStorage can be used to check whether the given storage already exists
void beginExternalResourceRemove(const QString &resourceType, const QVector< int > resourceIds)
Emitted when the locator needs to add an embedded resource.
void loadRequiredResources(KoResourceSP resource)
QString makeStorageLocationRelative(QString location) const
bool reloadResource(const QString &resourceType, const KoResourceSP resource)
Reloads the resource from its persistent storage.
QString makeStorageLocationAbsolute(QString storageLocation) const
bool addStorage(const QString &storageLocation, KisResourceStorageSP storage)
addStorage Adds a new resource storage to the database. The storage is will be marked as not pre-inst...
static void saveTags()
saveTags saves all tags to .tag files in the resource folder
QMap< QString, QVariant > metaDataForResource(int id) const
metaDataForResource
QString filePathForResource(KoResourceSP resource)
bool importWillOverwriteResource(const QString &resourceType, const QString &fileName, const QString &storageLocation=QString()) const
return whether importing will overwrite some existing resource
void setMetaDataForStorage(const QString &storageLocation, QMap< QString, QVariant > map) const
setMetaDataForStorage
bool setMetaDataForResource(int id, QMap< QString, QVariant > map) const
setMetaDataForResource
void endExternalResourceImport(const QString &resourceType)
Emitted when the locator finished importing the embedded resource.
bool setResourceActive(int resourceId, bool active)
setResourceActive
KisResourceLocator(QObject *parent)
static KisTagSP tagForUrlNoCache(const QString &tagUrl, const QString resourceType)
tagForUrlNoCache create a tag from the database, don't use cache
void purgeTag(const QString tagUrl, const QString resourceType)
void storageResynchronized(const QString &storage, bool isBulkResynchronization)
LocatorError firstTimeInstallation(InitializationStatus initializationStatus, const QString &installationResourcesLocation)
bool addResourceDeduplicateFileName(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
addResourceDeduplicateFileName imports the resource fith file name deduplication
void resourceActiveStateChanged(const QString &resourceType, int resourceId)
Emitted when a resource changes its active state.
KoResourceSP resourceForId(int resourceId)
resourceForId returns the resource with the given id, or 0 if no such resource exists....
static KisResourceLocator * instance()
KoResourceSP importResourceFromFile(const QString &resourceType, const QString &fileName, const bool allowOverwrite, const QString &storageLocation=QString())
importResourceFromFile
static const QString s_meta_name
void insert(const QString &storageLocation, const QString &resourceType, const QString &filename, const QImage &image)
static KisResourceThumbnailCache * instance()
void remove(const QString &storageLocation, const QString &resourceType, const QString &filename)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
QVariant data(const QModelIndex &index, int role) const override
static KisStoragePluginRegistry * instance()
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
const KoResourceSignature & signature() const
QByteArray data() const
static QString generateHash(const QString &filename)
generateHash reads the given file and generates a hex-encoded md5sum for the file.
KoResourceSP resource() const noexcept
KoEmbeddedResource embeddedResource() const noexcept
KoResourceSignature signature() const
static QString getAppDataLocation()
A simple wrapper object for the main information about the resource.
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define dbgResources
Definition kis_debug.h:43
QString deduplicateFileName(const QString &fileName, const QString &separator, std::function< bool(QString)> fileAllowedCallback)
KRITAVERSION_EXPORT QString versionString(bool checkGit=false)