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.exists()) {
121 initializationStatus = InitializationStatus::FirstUpdate;
122 }
123 else {
124 fi.open(QFile::ReadOnly);
125 QVersionNumber resource_version = QVersionNumber::fromString(QString::fromUtf8(fi.readAll()));
126 QVersionNumber krita_version = QVersionNumber::fromString(KritaVersionWrapper::versionString());
127 if (krita_version > resource_version) {
128 initializationStatus = InitializationStatus::Updating;
129 }
130 else {
131 initializationStatus = InitializationStatus::Initialized;
132 }
133 }
134 }
135
136 if (initializationStatus != InitializationStatus::Initialized) {
137 KisResourceLocator::LocatorError res = firstTimeInstallation(initializationStatus, installationResourcesLocation);
138 if (res != LocatorError::Ok) {
139 return res;
140 }
141 initializationStatus = InitializationStatus::Initialized;
142 }
143
144 if (!synchronizeDb()) {
146 }
147
148 return LocatorError::Ok;
149}
150
152{
153 return d->errorMessages;
154}
155
157{
158 return d->resourceLocation;
159}
160
161bool KisResourceLocator::resourceCached(QString storageLocation, const QString &resourceType, const QString &filename) const
162{
163 storageLocation = makeStorageLocationAbsolute(storageLocation);
164 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
165
166 return d->resourceCache.contains(key);
167}
168
170{
171 auto loadResourcesGroup =
172 [this, parentResource = resource] (QList<KoResourceLoadResult> resources,
173 const QString &resourceGroup) {
174
175 Q_FOREACH (KoResourceLoadResult res, resources) {
176 switch (res.type())
177 {
179 KIS_SAFE_ASSERT_RECOVER_NOOP(res.resource()->resourceId() >= 0);
180 break;
183 QByteArray data = res.embeddedResource().data();
184 QBuffer buffer(&data);
185 buffer.open(QBuffer::ReadOnly);
186 importResourceDeduplicateFileName(sig.type, sig.filename, &buffer, "memory");
187 break;
188 }
190 qWarning() << "Failed to load" << resourceGroup << "resource:" << res.signature();
191 break;
192 }
193 }
194 };
195
200 loadResourcesGroup(resource->takeSideLoadedResources(KisGlobalResourcesInterface::instance()), "side-loaded");
201
205 loadResourcesGroup(resource->requiredResources(KisGlobalResourcesInterface::instance()), "linked");
206}
207
208KisTagSP KisResourceLocator::tagForUrl(const QString &tagUrl, const QString resourceType)
209{
210 if (d->tagCache.contains(QPair<QString, QString>(resourceType, tagUrl))) {
211 return d->tagCache[QPair<QString, QString>(resourceType, tagUrl)];
212 }
213
214 KisTagSP tag = tagForUrlNoCache(tagUrl, resourceType);
215
216 if (tag && tag->valid()) {
217 d->tagCache[QPair<QString, QString>(resourceType, tagUrl)] = tag;
218 }
219
220 return tag;
221}
222
223KisTagSP KisResourceLocator::tagForUrlNoCache(const QString &tagUrl, const QString resourceType)
224{
225 QSqlQuery query;
226 bool r = query.prepare("SELECT tags.id\n"
227 ", tags.url\n"
228 ", tags.active\n"
229 ", tags.name\n"
230 ", tags.comment\n"
231 ", tags.filename\n"
232 ", resource_types.name as resource_type\n"
233 ", resource_types.id\n"
234 "FROM tags\n"
235 ", resource_types\n"
236 "WHERE tags.resource_type_id = resource_types.id\n"
237 "AND resource_types.name = :resource_type\n"
238 "AND tags.url = :tag_url\n");
239
240 if (!r) {
241 qWarning() << "Could not prepare KisResourceLocator::tagForUrl query" << query.lastError();
242 return KisTagSP();
243 }
244
245 query.bindValue(":resource_type", resourceType);
246 query.bindValue(":tag_url", tagUrl);
247
248 r = query.exec();
249 if (!r) {
250 qWarning() << "Could not execute KisResourceLocator::tagForUrl query" << query.lastError() << query.boundValues();
251 return KisTagSP();
252 }
253
254 r = query.first();
255 if (!r) {
256 return KisTagSP();
257 }
258
259 KisTagSP tag(new KisTag());
260
261 int tagId = query.value("tags.id").toInt();
262 int resourceTypeId = query.value("resource_types.id").toInt();
263
264 tag->setUrl(query.value("url").toString());
265 tag->setResourceType(resourceType);
266 tag->setId(query.value("id").toInt());
267 tag->setActive(query.value("active").toBool());
268 tag->setName(query.value("name").toString());
269 tag->setComment(query.value("comment").toString());
270 tag->setFilename(query.value("filename").toString());
271 tag->setValid(true);
272
273
274 QMap<QString, QString> names;
275 QMap<QString, QString> comments;
276
277 r = query.prepare("SELECT language\n"
278 ", name\n"
279 ", comment\n"
280 "FROM tag_translations\n"
281 "WHERE tag_id = :id");
282
283 if (!r) {
284 qWarning() << "Could not prepare KisResourceLocator::tagForUrl translation query" << query.lastError();
285 }
286
287 query.bindValue(":id", tag->id());
288
289 if (!query.exec()) {
290 qWarning() << "Could not execute KisResourceLocator::tagForUrl translation query" << query.lastError();
291 }
292
293 while (query.next()) {
294 names[query.value(0).toString()] = query.value(1).toString();
295 comments[query.value(0).toString()] = query.value(2).toString();
296 }
297
298 tag->setNames(names);
299 tag->setComments(comments);
300
301 QSqlQuery defaultResourcesQuery;
302
303 if (!defaultResourcesQuery.prepare("SELECT resources.filename\n"
304 "FROM resources\n"
305 ", resource_tags\n"
306 "WHERE resource_tags.tag_id = :tag_id\n"
307 "AND resources.resource_type_id = :type_id\n"
308 "AND resource_tags.resource_id = resources.id\n"
309 "AND resource_tags.active = 1\n")) {
310 qWarning() << "Could not prepare resource/tag query" << defaultResourcesQuery.lastError();
311 }
312
313 defaultResourcesQuery.bindValue(":tag_id", tagId);
314 defaultResourcesQuery.bindValue(":type_id", resourceTypeId);
315
316 if (!defaultResourcesQuery.exec()) {
317 qWarning() << "Could not execute resource/tag query" << defaultResourcesQuery.lastError();
318 }
319
320 QStringList resourceFileNames;
321
322 while (defaultResourcesQuery.next()) {
323 resourceFileNames << defaultResourcesQuery.value("resources.filename").toString();
324 }
325
326 tag->setDefaultResources(resourceFileNames);
327
328 return tag;
329}
330
331
332KoResourceSP KisResourceLocator::resource(QString storageLocation, const QString &resourceType, const QString &filename)
333{
334 storageLocation = makeStorageLocationAbsolute(storageLocation);
335
336 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + filename);
337
339 if (d->resourceCache.contains(key)) {
340 resource = d->resourceCache[key];
341 }
342 else {
343 KisResourceStorageSP storage = d->safeGetStorage(storageLocation);
344 if (!storage) {
345 return 0;
346 }
347
348 resource = storage->resource(resourceType + "/" + filename);
349
350 if (resource) {
351 d->resourceCache[key] = resource;
352 // load all the embedded resources into temporary "memory" storage
354 }
355 }
356
357 if (!resource) {
358 qWarning() << "KoResourceSP KisResourceLocator::resource" << storageLocation << resourceType << filename << "was not found";
359 return 0;
360 }
361
362 resource->setStorageLocation(storageLocation);
363 Q_ASSERT(!resource->storageLocation().isEmpty());
364
365 if (resource->resourceId() < 0 || resource->version() < 0) {
366 QSqlQuery q;
367 if (!q.prepare("SELECT resources.id\n"
368 ", versioned_resources.version as version\n"
369 ", versioned_resources.md5sum as md5sum\n"
370 ", resources.name\n"
371 ", resources.status\n"
372 "FROM resources\n"
373 ", storages\n"
374 ", resource_types\n"
375 ", versioned_resources\n"
376 "WHERE storages.id = resources.storage_id\n"
377 "AND storages.location = :storage_location\n"
378 "AND resource_types.id = resources.resource_type_id\n"
379 "AND resource_types.name = :resource_type\n"
380 "AND resources.filename = :filename\n"
381 "AND versioned_resources.resource_id = resources.id\n"
382 "AND versioned_resources.version = (SELECT MAX(version) FROM versioned_resources WHERE versioned_resources.resource_id = resources.id)")) {
383 qWarning() << "Could not prepare id/version query" << q.lastError();
384
385 }
386
387 q.bindValue(":storage_location", makeStorageLocationRelative(storageLocation));
388 q.bindValue(":resource_type", resourceType);
389 q.bindValue(":filename", filename);
390
391 if (!q.exec()) {
392 qWarning() << "Could not execute id/version query" << q.lastError() << q.boundValues();
393 }
394
395 if (!q.first()) {
396 qWarning() << "Could not find the resource in the database" << storageLocation << resourceType << filename;
397 }
398
399 resource->setResourceId(q.value(0).toInt());
400 Q_ASSERT(resource->resourceId() >= 0);
401
402 resource->setVersion(q.value(1).toInt());
403 Q_ASSERT(resource->version() >= 0);
404
405 resource->setMD5Sum(q.value(2).toString());
406 Q_ASSERT(!resource->md5Sum().isEmpty());
407
408 resource->setActive(q.value(4).toBool());
409
410 // To override resources that use the filename for the name, which is versioned, and we don't want the version number in the name
411 resource->setName(q.value(3).toString());;
412 }
413
414 if (!resource) {
415 qWarning() << "Could not find resource" << resourceType + "/" + filename;
416 return 0;
417 }
418
419 return resource;
420}
421
428
429bool KisResourceLocator::setResourceActive(int resourceId, bool active)
430{
431 // First remove the resource from the cache
432 ResourceStorage rs = getResourceStorage(resourceId);
433 QPair<QString, QString> key = QPair<QString, QString> (rs.storageLocation, rs.resourceType + "/" + rs.resourceFileName);
434
435 d->resourceCache.remove(key);
436 if (!active) {
438 }
439
440 bool result = KisResourceCacheDb::setResourceActive(resourceId, active);
441
442 Q_EMIT resourceActiveStateChanged(rs.resourceType, resourceId);
443
444 return result;
445}
446
447KoResourceSP KisResourceLocator::importResourceFromFile(const QString &resourceType, const QString &fileName, const bool allowOverwrite, const QString &storageLocation)
448{
449 QFile f(fileName);
450 if (!f.open(QFile::ReadOnly)) {
451 qWarning() << "Could not open" << fileName << "for loading";
452 return nullptr;
453 }
454
455 return importResource(resourceType, fileName, &f, allowOverwrite, storageLocation);
456}
457
458KoResourceSP KisResourceLocator::importResource(const QString &resourceType, const QString &fileName, QIODevice *device, const bool allowOverwrite, const QString &storageLocation)
459{
460 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
461 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, nullptr);
462
463 QByteArray resourceData = device->readAll();
465
466 {
467 QBuffer buf(&resourceData);
468 buf.open(QBuffer::ReadOnly);
469
471
472 if (!loader) {
473 qWarning() << "Could not import" << fileName << ": resource doesn't load.";
474 return nullptr;
475 }
476
477 resource = loader->load(QFileInfo(fileName).fileName(), buf, KisGlobalResourcesInterface::instance());
478 }
479
480 if (!resource || !resource->valid()) {
481 qWarning() << "Could not import" << fileName << ": resource doesn't load.";
482 return nullptr;
483 }
484
485 const QString md5 = KoMD5Generator::generateHash(resourceData);
486 const QString resourceUrl = resourceType + "/" + resource->filename();
487
488 const KoResourceSP existingResource = storage->resource(resourceUrl);
489
490 if (existingResource) {
491 const QString existingResourceMd5Sum = storage->resourceMd5(resourceUrl);
492
493 if (!allowOverwrite) {
494 return nullptr;
495 }
496
497 if (existingResourceMd5Sum == md5 &&
498 existingResource->filename() == resource->filename()) {
499
507 int existingResourceId = -1;
508 bool r = KisResourceCacheDb::getResourceIdFromFilename(existingResource->filename(), resourceType, storageLocation, existingResourceId);
509
510 if (r && existingResourceId > 0) {
511 return resourceForId(existingResourceId);
512 }
513 }
514
515 qWarning() << "A resource with the same filename but a different MD5 already exists in the storage" << resourceType << fileName << storageLocation;
516 if (storageLocation == "") {
517 qWarning() << "Proceeding with overwriting the existing resource...";
518 // remove all versions of the resource from the resource folder
519 QStringList versionsLocations;
520
521 // this resource has id -1, we need correct id
522 int existingResourceId = -1;
523 bool r = KisResourceCacheDb::getResourceIdFromVersionedFilename(existingResource->filename(), resourceType, storageLocation, existingResourceId);
524
525 if (r && existingResourceId >= 0) {
526 if (KisResourceCacheDb::getAllVersionsLocations(existingResourceId, versionsLocations)) {
527
528 for (int i = 0; i < versionsLocations.size(); i++) {
529 QFileInfo fi(this->resourceLocationBase() + "/" + resourceType + "/" + versionsLocations[i]);
530 if (fi.exists()) {
531 r = QFile::remove(fi.filePath());
532 if (!r) {
533 qWarning() << "KisResourceLocator::importResourceFromFile: Removal of " << fi.filePath()
534 << "was requested, but it wasn't possible, something went wrong.";
535 }
536 } else {
537 qWarning() << "KisResourceLocator::importResourceFromFile: Removal of " << fi.filePath()
538 << "was requested, but it doesn't exist.";
539 }
540 }
541 } else {
542 qWarning() << "KisResourceLocator::importResourceFromFile: Finding all locations for " << existingResourceId << "was requested, but it failed.";
543 return nullptr;
544 }
545 } else {
546 qWarning() << "KisResourceLocator::importResourceFromFile: there is no resource file found in the location of " << storageLocation << resource->filename() << resourceType;
547 return nullptr;
548 }
549
550 Q_EMIT beginExternalResourceRemove(resourceType, {existingResourceId});
551
552 // remove everything related to this resource from the database (remember about tags and versions!!!)
553 r = KisResourceCacheDb::removeResourceCompletely(existingResourceId);
554
555 {
556 const QString absoluteStorageLocation = makeStorageLocationAbsolute(resource->storageLocation());
557 KisResourceThumbnailCache::instance()->remove(absoluteStorageLocation, resourceType, existingResource->filename());
558 }
559
560 Q_EMIT endExternalResourceRemove(resourceType);
561
562 if (!r) {
563 qWarning() << "KisResourceLocator::importResourceFromFile: Removing resource with id " << existingResourceId << "completely from the database failed.";
564 return nullptr;
565 }
566
567 } else {
568 qWarning() << "KisResourceLocator::importResourceFromFile: Overwriting of the resource was denied, aborting import.";
569 return nullptr;
570 }
571 }
572
573 QBuffer buf(&resourceData);
574 buf.open(QBuffer::ReadOnly);
575
576 if (storage->importResource(resourceUrl, &buf)) {
577 resource = storage->resource(resourceUrl);
578
579 if (!resource) {
580 qWarning() << "Could not retrieve imported resource from the storage" << resourceType << fileName << storageLocation;
581 return nullptr;
582 }
583
584 resource->setStorageLocation(storageLocation);
585 resource->setMD5Sum(storage->resourceMd5(resourceUrl));
586 resource->setVersion(0);
587 resource->setDirty(false);
589
590 Q_EMIT beginExternalResourceImport(resourceType, 1);
591
592 // Insert into the database
593 const bool result = KisResourceCacheDb::addResource(storage,
594 storage->timeStampForResource(resourceType, resource->filename()),
595 resource,
596 resourceType);
597
598 Q_EMIT endExternalResourceImport(resourceType);
599
600 if (!result) {
601 return nullptr;
602 }
603
604 // resourceCaches use absolute locations
605 const QString absoluteStorageLocation = makeStorageLocationAbsolute(resource->storageLocation());
606 const QPair<QString, QString> key = {absoluteStorageLocation, resourceType + "/" + resource->filename()};
607 // Add to the cache
608 d->resourceCache[key] = resource;
610
611 return resource;
612 }
613
614 return nullptr;
615}
616
617namespace {
618QString findDeduplicatedFileName(const QString &resourceType, const QString &proposedFileName, KisResourceStorageSP storage)
619{
620
621 auto fileAllowedCallback = [resourceType, storage] (const QString &fileName) {
622 QString resourceUrl = resourceType + "/" + fileName;
623 return !storage->resource(resourceUrl);
624 };
625
626 return KritaUtils::deduplicateFileName(proposedFileName, "_embedded_", fileAllowedCallback);
627}
628}
629
630KoResourceSP KisResourceLocator::importResourceDeduplicateFileName(const QString &resourceType, const QString &proposedFileName, QIODevice *device, const QString &storageLocation)
631{
632 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
633 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, nullptr);
634
635 const QString fileName = findDeduplicatedFileName(resourceType, proposedFileName, storage);
636 return importResource(resourceType, fileName, device, false, storageLocation);
637}
638
639bool KisResourceLocator::addResourceDeduplicateFileName(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
640{
641 // fix filename is missing
642 if (resource->filename().isEmpty()) {
643 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
644 }
645
646 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!resource->filename().isEmpty(), false);
647
648 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
650
651 const QString fileName = findDeduplicatedFileName(resourceType, resource->filename(), storage);
652 resource->setFilename(fileName);
653 return addResource(resourceType, resource, storageLocation);
654}
655
656bool KisResourceLocator::importWillOverwriteResource(const QString &resourceType, const QString &fileName, const QString &storageLocation) const
657{
658 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
660
661 const QString resourceUrl = resourceType + "/" + QFileInfo(fileName).fileName();
662
663 const KoResourceSP existingResource = storage->resource(resourceUrl);
664
665 return !existingResource.isNull();
666}
667
668bool KisResourceLocator::exportResource(KoResourceSP resource, QIODevice *device)
669{
670 if (!resource || !resource->valid() || resource->resourceId() < 0) return false;
671
672 const QString resourceUrl = resource->resourceType().first + "/" + resource->filename();
673 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(resource->storageLocation()));
675 return storage->exportResource(resourceUrl, device);
676}
677
678bool KisResourceLocator::addResource(const QString &resourceType, const KoResourceSP resource, const QString &storageLocation)
679{
680 if (!resource || !resource->valid()) return false;
681
682 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
684
685 //If we have gotten this far and the resource still doesn't have a filename to save to, we should generate one.
686 if (resource->filename().isEmpty()) {
687 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
688 }
689
690 if (resource->version() != 0) { // Can happen with cloned resources
691 resource->setVersion(0);
692 }
693
694 // Save the resource to the storage storage
695 if (!storage->addResource(resource)) {
696 qWarning() << "Could not add resource" << resource->filename() << "to the storage" << storageLocation;
697 return false;
698 }
699
700 resource->setStorageLocation(storageLocation);
701 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
702 resource->setDirty(false);
704
705 d->resourceCache[QPair<QString, QString>(storageLocation, resourceType + "/" + resource->filename())] = resource;
706
712 const bool result = KisResourceCacheDb::addResource(storage,
713 storage->timeStampForResource(resourceType, resource->filename()),
714 resource,
715 resourceType);
716 return result;
717}
718
719bool KisResourceLocator::updateResource(const QString &resourceType, const KoResourceSP resource)
720{
721 QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation());
722
723 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
725
726 if (resource->resourceId() < 0) {
727 return addResource(resourceType, resource);
728 }
729
730 if (!storage->supportsVersioning()) return false;
731
732 // remove older version
733 KisResourceThumbnailCache::instance()->remove(storageLocation, resourceType, resource->filename());
734
735 resource->updateThumbnail();
736 resource->setVersion(resource->version() + 1);
737 resource->setActive(true);
738
739 if (!storage->saveAsNewVersion(resource)) {
740 qWarning() << "Failed to save the new version of " << resource->name() << "to storage" << storageLocation;
741 return false;
742 }
743
744 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
745 resource->setDirty(false);
747
748 // The version needs already to have been incremented
749 if (!KisResourceCacheDb::addResourceVersion(resource->resourceId(), QDateTime::currentDateTime(), storage, resource)) {
750 qWarning() << "Failed to add a new version of the resource to the database" << resource->name();
751 return false;
752 }
753
754 if (!setMetaDataForResource(resource->resourceId(), resource->metadata())) {
755 qWarning() << "Failed to update resource metadata" << resource;
756 return false;
757 }
758
759 // Update the resource in the cache
760 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + resource->filename());
761 d->resourceCache[key] = resource;
763
764 return true;
765}
766
767bool KisResourceLocator::reloadResource(const QString &resourceType, const KoResourceSP resource)
768{
769 // This resource isn't in the database yet, so we cannot reload it
770 if (resource->resourceId() < 0) return false;
771
772 QString storageLocation = makeStorageLocationAbsolute(resource->storageLocation());
773 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
775
776 if (!storage->loadVersionedResource(resource)) {
777 qWarning() << "Failed to reload the resource" << resource->name() << "from storage" << storageLocation;
778 return false;
779 }
780
781 resource->setMD5Sum(storage->resourceMd5(resourceType + "/" + resource->filename()));
782 resource->setDirty(false);
784
785 // We haven't changed the version of the resource, so the cache must be still valid
786 QPair<QString, QString> key = QPair<QString, QString> (storageLocation, resourceType + "/" + resource->filename());
787 Q_ASSERT(d->resourceCache[key] == resource);
788
789 return true;
790}
791
792QMap<QString, QVariant> KisResourceLocator::metaDataForResource(int id) const
793{
794 return KisResourceCacheDb::metaDataForId(id, "resources");
795}
796
797bool KisResourceLocator::setMetaDataForResource(int id, QMap<QString, QVariant> map) const
798{
799 return KisResourceCacheDb::updateMetaDataForId(map, id, "resources");
800}
801
802QMap<QString, QVariant> KisResourceLocator::metaDataForStorage(const QString &storageLocation) const
803{
804 QMap<QString, QVariant> metadata;
805
806 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
807 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, metadata);
808
809 Q_FOREACH(const QString key, storage->metaDataKeys()) {
810 metadata[key] = storage->metaData(key);
811 }
812 return metadata;
813}
814
815void KisResourceLocator::setMetaDataForStorage(const QString &storageLocation, QMap<QString, QVariant> map) const
816{
817 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(storageLocation));
819
820 Q_FOREACH(const QString &key, map.keys()) {
821 storage->setMetaData(key, map[key]);
822 }
823}
824
825void KisResourceLocator::purge(const QString &storageLocation)
826{
827 Q_FOREACH(const auto key, d->resourceCache.keys()) {
828 if (key.first == storageLocation) {
829 d->resourceCache.remove(key);
831 }
832 }
833}
834
835bool KisResourceLocator::addStorage(const QString &storageLocation, KisResourceStorageSP storage)
836{
837 if (d->storages.contains(storageLocation)) {
838 if (!removeStorage(storageLocation)) {
839 qWarning() << "could not remove" << storageLocation;
840 return false;
841 }
842 }
843
844 QVector<std::pair<QString, int>> addedResources;
845 Q_FOREACH(const QString &type, KisResourceLoaderRegistry::instance()->resourceTypes()) {
846 int numAddedResources = 0;
847
848 QSharedPointer<KisResourceStorage::ResourceIterator> it = storage->resources(type);
849 while (it->hasNext()) {
850 it->next();
851 numAddedResources++;
852 }
853
854 if (numAddedResources > 0) {
855 addedResources << std::make_pair(type, numAddedResources);
856 }
857 }
858
859 Q_FOREACH (const auto &typedResources, addedResources) {
860 Q_EMIT beginExternalResourceImport(typedResources.first, typedResources.second);
861 }
862
863 d->storages[storageLocation] = storage;
864 if (!KisResourceCacheDb::addStorage(storage, false)) {
865 d->errorMessages.append(i18n("Could not add %1 to the database", storage->location()));
866 qWarning() << d->errorMessages;
867 return false;
868 }
869
871 d->errorMessages.append(QString("Could not add tags for storage %1 to the cache database").arg(storage->location()));
872 qWarning() << d->errorMessages;
873 return false;
874 }
875
876 Q_FOREACH (const auto &typedResources, addedResources) {
877 Q_EMIT endExternalResourceImport(typedResources.first);
878 }
879
880 Q_EMIT storageAdded(makeStorageLocationRelative(storage->location()));
881 return true;
882}
883
884bool KisResourceLocator::removeStorage(const QString &storageLocation)
885{
886 // Cloned documents have a document storage, but that isn't in the locator.
887 if (!d->storages.contains(storageLocation)) {
888 return true;
889 }
890
892
893 Q_FOREACH(const QString &type, KisResourceLoaderRegistry::instance()->resourceTypes()) {
894 const QVector<int> resources = KisResourceCacheDb::resourcesForStorage(type, storageLocation);
895 if (!resources.isEmpty()) {
896 removedResources << std::make_pair(type, resources);
897 }
898 }
899
900 Q_FOREACH (const auto &typedResources, removedResources) {
901 Q_EMIT beginExternalResourceRemove(typedResources.first, typedResources.second);
902 }
903
904 purge(storageLocation);
905
906 KisResourceStorageSP storage = d->storages.take(storageLocation);
907
908 if (!KisResourceCacheDb::deleteStorage(storage)) {
909 d->errorMessages.append(i18n("Could not remove storage %1 from the database", storage->location()));
910 qWarning() << d->errorMessages;
911 return false;
912 }
913
914 Q_FOREACH (const auto &typedResources, removedResources) {
915 Q_EMIT endExternalResourceRemove(typedResources.first);
916 }
917
918 Q_EMIT storageRemoved(makeStorageLocationRelative(storage->location()));
919
920 return true;
921}
922
923bool KisResourceLocator::hasStorage(const QString &document)
924{
925 return d->storages.contains(document);
926}
927
929{
930 QSqlQuery query;
931
932 if (!query.prepare("SELECT tags.url \n"
933 ", resource_types.name \n"
934 "FROM tags\n"
935 ", resource_types\n"
936 "WHERE tags.resource_type_id = resource_types.id\n"))
937 {
938 qWarning() << "Could not prepare save tags query" << query.lastError();
939 return;
940 }
941
942 if (!query.exec()) {
943 qWarning() << "Could not execute save tags query" << query.lastError();
944 return;
945 }
946
947 // this needs to use ResourcePaths because it is sometimes called during initialization
948 // (when the database versions don't match up and tags need to be saved)
949 QString resourceLocation = KoResourcePaths::getAppDataLocation() + "/";
950
951 while (query.next()) {
952 // Save tag...
953 KisTagSP tag = tagForUrlNoCache(query.value("tags.url").toString(),
954 query.value("resource_types.name").toString());
955
956 if (!tag || !tag->valid()) {
957 continue;
958 }
959
960
961 QString filename = tag->filename();
962 if (filename.isEmpty() || QFileInfo(filename).suffix().isEmpty()) {
963 filename = tag->url() + ".tag";
964 }
965
966
967 if (QFileInfo(filename).suffix() != "tag" && QFileInfo(filename).suffix() != "TAG") {
968 // it's either .abr file, or maybe a .bundle
969 // or something else, but not a tag file
970 dbgResources << "Skipping saving tag " << tag->name(false) << filename << tag->resourceType();
971 continue;
972 }
973
974 filename.remove(resourceLocation);
975
976 QFile f(resourceLocation + "/" + tag->resourceType() + '/' + filename);
977
978 if (!f.open(QFile::WriteOnly)) {
979 qWarning () << "Could not open tag file for writing" << f.fileName();
980 continue;
981 }
982
983 QBuffer buf;
984 buf.open(QIODevice::WriteOnly);;
985
986 if (!tag->save(buf)) {
987 qWarning() << "Could not save tag to" << f.fileName();
988 buf.close();
989 f.close();
990 continue;
991 }
992
993 f.write(buf.data());
994 f.flush();
995
996 f.close();
997 }
998}
999
1000void KisResourceLocator::purgeTag(const QString tagUrl, const QString resourceType)
1001{
1002 d->tagCache.remove(QPair<QString, QString>(resourceType, tagUrl));
1003}
1004
1006{
1007 KisResourceStorageSP storage = d->safeGetStorage(makeStorageLocationAbsolute(resource->storageLocation()));
1008 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(storage, QString());
1009
1010 const QString resourceUrl = resource->resourceType().first + "/" + resource->filename();
1011
1012 return storage->resourceFilePath(resourceUrl);
1013}
1014
1016{
1018 qWarning() << i18n("Could not synchronize updated font registry with the database");
1019 } else {
1020 Q_EMIT storageResynchronized(fontStorage()->location(), false);
1021 }
1022}
1023
1024KisResourceLocator::LocatorError KisResourceLocator::firstTimeInstallation(InitializationStatus initializationStatus, const QString &installationResourcesLocation)
1025{
1026 Q_EMIT progressMessage(i18n("Krita is running for the first time. Initialization will take some time."));
1027 Q_UNUSED(initializationStatus);
1028
1029 Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1030 QDir dir(d->resourceLocation + '/' + folder + '/');
1031 if (!dir.exists()) {
1032 if (!QDir().mkpath(d->resourceLocation + '/' + folder + '/')) {
1033 d->errorMessages << i18n("3. Could not create the resource location at %1.", dir.path());
1035 }
1036 }
1037 }
1038
1039 Q_FOREACH(const QString &folder, KisResourceLoaderRegistry::instance()->resourceTypes()) {
1040 QDir dir(installationResourcesLocation + '/' + folder + '/');
1041 if (dir.exists()) {
1042 Q_FOREACH(const QString &entry, dir.entryList(QDir::Files | QDir::Readable)) {
1043 QFile f(dir.canonicalPath() + '/'+ entry);
1044 if (!QFileInfo(d->resourceLocation + '/' + folder + '/' + entry).exists()) {
1045 if (!f.copy(d->resourceLocation + '/' + folder + '/' + entry)) {
1046 d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation + '/' + folder + '/' + entry);
1047 }
1048 }
1049 }
1050 }
1051 }
1052
1053 // And add bundles and adobe libraries
1054 QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
1055 QDirIterator iter(installationResourcesLocation, filters, QDir::Files, QDirIterator::Subdirectories);
1056 while (iter.hasNext()) {
1057 iter.next();
1058 Q_EMIT progressMessage(i18n("Installing the resources from bundle %1.", iter.filePath()));
1059 QFile f(iter.filePath());
1060 Q_ASSERT(f.exists());
1061 if (!f.copy(d->resourceLocation + '/' + iter.fileName())) {
1062 d->errorMessages << i18n("Could not copy resource %1 to %2", f.fileName(), d->resourceLocation);
1063 }
1064 }
1065
1066 QFile f(d->resourceLocation + '/' + "KRITA_RESOURCE_VERSION");
1067 f.open(QFile::WriteOnly);
1068 f.write(KritaVersionWrapper::versionString().toUtf8());
1069 f.close();
1070
1071 return LocatorError::Ok;
1072}
1073
1075{
1076 d->storages.clear();
1077 d->resourceCache.clear();
1078
1079 // Add the folder
1081 Q_ASSERT(storage->location() == d->resourceLocation);
1082 d->storages[d->resourceLocation] = storage;
1083
1084 // Add the memory storage
1085 d->storages["memory"] = QSharedPointer<KisResourceStorage>::create("memory");
1086 d->storages["memory"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Temporary Resources"));
1087
1088 // Add font storage
1090 if (fontStorage && fontStorage->valid()) {
1091 d->storages["fontregistry"] = fontStorage;
1092 d->storages["fontregistry"]->setMetaData(KisResourceStorage::s_meta_name, i18n("Font Storage"));
1093 }
1094
1095 // And add bundles and adobe libraries
1096 QStringList filters = QStringList() << "*.bundle" << "*.abr" << "*.asl";
1097 QDirIterator iter(d->resourceLocation, filters, QDir::Files, QDirIterator::Subdirectories);
1098 while (iter.hasNext()) {
1099 iter.next();
1101 if (!storage->valid()) {
1102 // we still add the storage to the list and try to read whatever possible
1103 qWarning() << "KisResourceLocator::findStorages: the storage is invalid" << storage->location();
1104 }
1105 d->storages[storage->location()] = storage;
1106 }
1107
1108 // Add any missing storage types to the resource cache database.
1109 Q_FOREACH(const KisResourceStorage::StorageType &type, KisStoragePluginRegistry::instance()->storageTypes()) {
1111 }
1112}
1113
1115{
1116 return d->storages.values();
1117}
1118
1120{
1121 KisResourceStorageSP storage = d->safeGetStorage(location);
1122 if (!storage || !storage->valid()) {
1123 qWarning() << "Could not retrieve the" << location << "storage object or the object is not valid";
1124 return 0;
1125 }
1126
1127 return storage;
1128}
1129
1131{
1132 return storageByLocation(d->resourceLocation);
1133}
1134
1139
1141{
1142 return storageByLocation("fontregistry");
1143}
1144
1146{
1147 ResourceStorage rs;
1148
1149 QSqlQuery q;
1150 bool r = q.prepare("SELECT storages.location\n"
1151 ", resource_types.name as resource_type\n"
1152 ", resources.filename\n"
1153 "FROM resources\n"
1154 ", storages\n"
1155 ", resource_types\n"
1156 "WHERE resources.id = :resource_id\n"
1157 "AND resources.storage_id = storages.id\n"
1158 "AND resource_types.id = resources.resource_type_id");
1159 if (!r) {
1160 qWarning() << "KisResourceLocator::removeResource: could not prepare query." << q.lastError();
1161 return rs;
1162 }
1163
1164
1165 q.bindValue(":resource_id", resourceId);
1166
1167 r = q.exec();
1168 if (!r) {
1169 qWarning() << "KisResourceLocator::removeResource: could not execute query." << q.lastError();
1170 return rs;
1171 }
1172
1173 q.first();
1174
1175 QString storageLocation = q.value("location").toString();
1176 QString resourceType= q.value("resource_type").toString();
1177 QString resourceFilename = q.value("filename").toString();
1178
1179 rs.storageLocation = makeStorageLocationAbsolute(storageLocation);
1180 rs.resourceType = resourceType;
1181 rs.resourceFileName = resourceFilename;
1182
1183 return rs;
1184}
1185
1186QString KisResourceLocator::makeStorageLocationAbsolute(QString storageLocation) const
1187{
1188// debugResource << "makeStorageLocationAbsolute" << storageLocation;
1189
1190 if (storageLocation.isEmpty()) {
1191 return resourceLocationBase();
1192 }
1193
1194 if (QFileInfo(storageLocation).isRelative() && (storageLocation.endsWith(".bundle", Qt::CaseInsensitive)
1195 || storageLocation.endsWith(".asl", Qt::CaseInsensitive)
1196 || storageLocation.endsWith(".abr", Qt::CaseInsensitive))) {
1197 if (resourceLocationBase().endsWith('/') || resourceLocationBase().endsWith("\\")) {
1198 storageLocation = resourceLocationBase() + storageLocation;
1199 }
1200 else {
1201 storageLocation = resourceLocationBase() + '/' + storageLocation;
1202 }
1203 }
1204
1205// debugResource << "\t" << storageLocation;
1206 return storageLocation;
1207}
1208
1210{
1211 Q_EMIT progressMessage(i18n("Synchronizing the resources."));
1212
1213 d->errorMessages.clear();
1214
1215 // Add resource types that have been added since first-time installation.
1216 Q_FOREACH(auto loader, KisResourceLoaderRegistry::instance()->values()) {
1217 KisResourceCacheDb::registerResourceType(loader->resourceType());
1218 }
1219
1220
1221 findStorages();
1222 Q_FOREACH(const KisResourceStorageSP storage, d->storages) {
1224 d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location()));
1225 } else {
1226 Q_EMIT storageResynchronized(storage->location(), true);
1227 }
1228 }
1229
1230 Q_FOREACH(const KisResourceStorageSP storage, d->storages) {
1231 if (!KisResourceCacheDb::addStorageTags(storage)) {
1232 d->errorMessages.append(i18n("Could not synchronize %1 with the database", storage->location()));
1233 }
1234 }
1235
1237
1247
1248 // now remove the storages that no longer exists
1249 KisStorageModel model;
1250
1251 QList<QString> storagesToRemove;
1252 for (int i = 0; i < model.rowCount(); i++) {
1253 QModelIndex idx = model.index(i, 0);
1254 QString location = model.data(idx, Qt::UserRole + KisStorageModel::Location).toString();
1255 storagesToRemove << location;
1256 }
1257
1258 for (int i = 0; i < storagesToRemove.size(); i++) {
1259 QString location = storagesToRemove[i];
1260 if (!d->storages.contains(this->makeStorageLocationAbsolute(location))) {
1261 if (!KisResourceCacheDb::deleteStorage(location)) {
1262 d->errorMessages.append(i18n("Could not remove storage %1 from the database", this->makeStorageLocationAbsolute(location)));
1263 qWarning() << d->errorMessages;
1264 return false;
1265 }
1266 Q_EMIT storageRemoved(this->makeStorageLocationAbsolute(location));
1267 }
1268 }
1269
1270
1271 d->errorMessages <<
1273
1274 d->resourceCache.clear();
1275 return d->errorMessages.isEmpty();
1276}
1277
1278
1280{
1281// debugResource << "makeStorageLocationRelative" << location << "locationbase" << resourceLocationBase();
1282 return location.remove(resourceLocationBase());
1283}
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 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
void purge(const QString &storageLocation)
purge purges the local resource cache
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 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)