File indexing completed on 2025-01-05 03:56:11
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2015-07-27 0007 * Description : Special digiKam trash implementation 0008 * 0009 * SPDX-FileCopyrightText: 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "dtrash.h" 0016 0017 // Qt includes 0018 0019 #include <QDir> 0020 #include <QFile> 0021 #include <QUuid> 0022 #include <QJsonObject> 0023 #include <QJsonDocument> 0024 #include <QJsonValue> 0025 #include <QDateTime> 0026 0027 // Local includes 0028 0029 #include "digikam_debug.h" 0030 #include "collectionmanager.h" 0031 #include "albummanager.h" 0032 0033 namespace Digikam 0034 { 0035 0036 const QString DTrash::TRASH_FOLDER = QLatin1String(".dtrash"); 0037 const QString DTrash::FILES_FOLDER = QLatin1String("files"); 0038 const QString DTrash::INFO_FOLDER = QLatin1String("info"); 0039 const QString DTrash::INFO_FILE_EXTENSION = QLatin1String(".dtrashinfo"); 0040 const QString DTrash::PATH_JSON_KEY = QLatin1String("path"); 0041 const QString DTrash::DELETIONTIMESTAMP_JSON_KEY = QLatin1String("deletiontimestamp"); 0042 const QString DTrash::IMAGEID_JSON_KEY = QLatin1String("imageid"); 0043 0044 // ---------------------------------------------- 0045 0046 DTrash::DTrash() 0047 { 0048 } 0049 0050 bool DTrash::deleteImage(const QString& imagePath, const QDateTime& deleteTime) 0051 { 0052 QString collection = CollectionManager::instance()->albumRootPath(imagePath); 0053 0054 qCDebug(DIGIKAM_IOJOB_LOG) << "DTrash: Image album root path:" 0055 << collection; 0056 0057 if (!prepareCollectionTrash(collection)) 0058 { 0059 return false; 0060 } 0061 0062 QFileInfo imageFileInfo(imagePath); 0063 QString fileName = imageFileInfo.fileName(); 0064 0065 // Get the album path, i.e. collection + album. For this, 0066 // get the n leftmost characters where n is the complete path without the size of the filename 0067 0068 QString completePath = imageFileInfo.path(); 0069 0070 qlonglong imageId = -1; 0071 0072 // Get the album and with this the image id of the image to trash. 0073 0074 PAlbum* const pAlbum = AlbumManager::instance()->findPAlbum(QUrl::fromLocalFile(completePath)); 0075 0076 if (pAlbum) 0077 { 0078 imageId = AlbumManager::instance()->getItemFromAlbum(pAlbum, fileName); 0079 } 0080 0081 QString baseNameForMovingIntoTrash = createJsonRecordForFile(imageId, 0082 imagePath, 0083 deleteTime, 0084 collection); 0085 0086 QString destinationInTrash = collection + QLatin1Char('/') + TRASH_FOLDER + 0087 QLatin1Char('/') + FILES_FOLDER + QLatin1Char('/') + 0088 baseNameForMovingIntoTrash + QLatin1Char('.') + 0089 imageFileInfo.completeSuffix(); 0090 0091 if (!QFile::rename(imagePath, destinationInTrash)) 0092 { 0093 return false; 0094 } 0095 0096 return true; 0097 } 0098 0099 bool DTrash::deleteDirRecursivley(const QString& dirToDelete, const QDateTime& deleteTime) 0100 { 0101 QDir srcDir(dirToDelete); 0102 0103 Q_FOREACH (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Files)) 0104 { 0105 if (!deleteImage(fileInfo.filePath(), deleteTime)) 0106 { // cppcheck-suppress useStlAlgorithm 0107 return false; 0108 } 0109 } 0110 0111 Q_FOREACH (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) 0112 { 0113 if (!deleteDirRecursivley(fileInfo.filePath(), deleteTime)) 0114 { // cppcheck-suppress useStlAlgorithm 0115 return false; 0116 } 0117 } 0118 0119 return srcDir.removeRecursively(); 0120 } 0121 0122 void DTrash::extractJsonForItem(const QString& collPath, const QString& baseName, DTrashItemInfo& itemInfo) 0123 { 0124 QString jsonFilePath = collPath + QLatin1Char('/') + TRASH_FOLDER + 0125 QLatin1Char('/') + INFO_FOLDER + QLatin1Char('/') + 0126 baseName + INFO_FILE_EXTENSION; 0127 0128 QFile jsonFile(jsonFilePath); 0129 0130 if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) 0131 { 0132 return; 0133 } 0134 0135 QJsonDocument doc = QJsonDocument::fromJson(jsonFile.readAll()); 0136 jsonFile.close(); 0137 0138 QJsonObject fileInfoObj = doc.object(); 0139 0140 itemInfo.jsonFilePath = jsonFilePath; 0141 0142 itemInfo.collectionPath = fileInfoObj.value(PATH_JSON_KEY).toString(); 0143 0144 itemInfo.collectionRelativePath = fileInfoObj.value(PATH_JSON_KEY).toString() 0145 .replace(collPath, QLatin1String("")); 0146 0147 itemInfo.deletionTimestamp = QDateTime::fromString( 0148 fileInfoObj.value(DELETIONTIMESTAMP_JSON_KEY).toString(), Qt::ISODate); 0149 0150 if (!itemInfo.deletionTimestamp.isValid()) 0151 { 0152 // Failback to date encoded as string using locale. 0153 // This is an older way to store date in JSOn, which do not support change in locale. 0154 // This is why ISO format is now used. 0155 0156 itemInfo.deletionTimestamp = QDateTime::fromString( 0157 fileInfoObj.value(DELETIONTIMESTAMP_JSON_KEY).toString()); 0158 } 0159 0160 QJsonValue imageIdValue = fileInfoObj.value(IMAGEID_JSON_KEY); 0161 0162 if (!imageIdValue.isUndefined()) 0163 { 0164 itemInfo.imageId = imageIdValue.toString().toLongLong(); 0165 } 0166 else 0167 { 0168 itemInfo.imageId = -1; 0169 } 0170 } 0171 0172 bool DTrash::prepareCollectionTrash(const QString& collectionPath) 0173 { 0174 QString trashFolder = collectionPath + QLatin1Char('/') + TRASH_FOLDER; 0175 QString trashFiles = trashFolder + QLatin1Char('/') + FILES_FOLDER; 0176 QString trashInfo = trashFolder + QLatin1Char('/') + INFO_FOLDER; 0177 bool isCreated = !collectionPath.isEmpty(); 0178 0179 if (isCreated && !QFileInfo::exists(trashFolder)) 0180 { 0181 isCreated &= QDir().mkpath(trashFolder); 0182 } 0183 0184 if (isCreated && !QFileInfo::exists(trashFiles)) 0185 { 0186 isCreated &= QDir().mkpath(trashFiles); 0187 } 0188 0189 if (isCreated && !QFileInfo::exists(trashInfo)) 0190 { 0191 isCreated &= QDir().mkpath(trashInfo); 0192 } 0193 0194 if (!isCreated) 0195 { 0196 qCDebug(DIGIKAM_IOJOB_LOG) << "DTrash: could not create trash folder for collection"; 0197 0198 return false; 0199 } 0200 0201 qCDebug(DIGIKAM_IOJOB_LOG) << "Trash folder for collection:" << trashFolder; 0202 0203 return true; 0204 } 0205 0206 QString DTrash::createJsonRecordForFile(qlonglong imageId, 0207 const QString& imagePath, 0208 const QDateTime& deleteTime, 0209 const QString& collectionPath) 0210 { 0211 QJsonObject jsonObjForImg; 0212 0213 QJsonValue pathJsonVal(imagePath); 0214 QJsonValue timestampJsonVal(deleteTime.toString(Qt::ISODate)); 0215 QJsonValue imageIdJsonVal(QString::number(imageId)); 0216 0217 jsonObjForImg.insert(PATH_JSON_KEY, pathJsonVal); 0218 jsonObjForImg.insert(DELETIONTIMESTAMP_JSON_KEY, timestampJsonVal); 0219 jsonObjForImg.insert(IMAGEID_JSON_KEY, imageIdJsonVal); 0220 0221 QJsonDocument jsonDocForImg(jsonObjForImg); 0222 0223 QFileInfo imgFileInfo(imagePath); 0224 0225 QString jsonFileName = getAvialableJsonFilePathInTrash(collectionPath, 0226 imgFileInfo.baseName()); 0227 0228 QFile jsonFileForImg(jsonFileName); 0229 0230 QFileInfo jsonFileInfo(jsonFileName); 0231 0232 if (!jsonFileForImg.open(QFile::WriteOnly)) 0233 { 0234 return jsonFileInfo.baseName(); 0235 } 0236 0237 jsonFileForImg.write(jsonDocForImg.toJson()); 0238 jsonFileForImg.close(); 0239 jsonFileForImg.setPermissions(QFileDevice::ReadOwner | 0240 QFileDevice::ReadGroup | 0241 QFileDevice::ReadOther | 0242 QFileDevice::WriteOwner | 0243 QFileDevice::WriteGroup); 0244 0245 return jsonFileInfo.baseName(); 0246 } 0247 0248 QString DTrash::getAvialableJsonFilePathInTrash(const QString& collectionPath, 0249 const QString& baseName, 0250 int version) 0251 { 0252 QString pathToCreateJsonFile = collectionPath + QLatin1Char('/') + 0253 TRASH_FOLDER + QLatin1Char('/') + 0254 INFO_FOLDER + QLatin1Char('/') + 0255 baseName + QLatin1Char('-') + 0256 QUuid::createUuid().toString().mid(1, 8) + 0257 (version ? QString::number(version) : QLatin1String("")) + 0258 INFO_FILE_EXTENSION; 0259 0260 QFileInfo jsonFileInfo(pathToCreateJsonFile); 0261 0262 if (jsonFileInfo.exists()) 0263 { 0264 return getAvialableJsonFilePathInTrash(collectionPath, baseName, ++version); 0265 } 0266 else 0267 { 0268 return pathToCreateJsonFile; 0269 } 0270 } 0271 0272 } // namespace Digikam