1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2015-07-27
7 * Description : Special digiKam trash implementation
8 *
9 * Copyright (C) 2015 by Mohamed_Anwer <m_dot_anwer at gmx dot com>
10 *
11 * This program is free software; you can redistribute it
12 * and/or modify it under the terms of the GNU General
13 * Public License as published by the Free Software Foundation;
14 * either version 2, or (at your option)
15 * any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * ============================================================ */
23
24 #include "dtrash.h"
25
26 // Qt includes
27
28 #include <QDir>
29 #include <QFile>
30 #include <QUuid>
31 #include <QJsonObject>
32 #include <QJsonDocument>
33 #include <QJsonValue>
34 #include <QDateTime>
35
36 // Local includes
37
38 #include "digikam_debug.h"
39 #include "collectionmanager.h"
40 #include "albummanager.h"
41
42 namespace Digikam
43 {
44
45 const QString DTrash::TRASH_FOLDER = QLatin1String(".dtrash");
46 const QString DTrash::FILES_FOLDER = QLatin1String("files");
47 const QString DTrash::INFO_FOLDER = QLatin1String("info");
48 const QString DTrash::INFO_FILE_EXTENSION = QLatin1String(".dtrashinfo");
49 const QString DTrash::PATH_JSON_KEY = QLatin1String("path");
50 const QString DTrash::DELETIONTIMESTAMP_JSON_KEY = QLatin1String("deletiontimestamp");
51 const QString DTrash::IMAGEID_JSON_KEY = QLatin1String("imageid");
52
53 // ----------------------------------------------
54
DTrash()55 DTrash::DTrash()
56 {
57 }
58
deleteImage(const QString & imagePath,const QDateTime & deleteTime)59 bool DTrash::deleteImage(const QString& imagePath, const QDateTime& deleteTime)
60 {
61 QString collection = CollectionManager::instance()->albumRootPath(imagePath);
62
63 qCDebug(DIGIKAM_IOJOB_LOG) << "DTrash: Image album root path:"
64 << collection;
65
66 if (!prepareCollectionTrash(collection))
67 {
68 return false;
69 }
70
71 QFileInfo imageFileInfo(imagePath);
72 QString fileName = imageFileInfo.fileName();
73
74 // Get the album path, i.e. collection + album. For this,
75 // get the n leftmost characters where n is the complete path without the size of the filename
76
77 QString completePath = imageFileInfo.path();
78
79 qlonglong imageId = -1;
80
81 // Get the album and with this the image id of the image to trash.
82
83 PAlbum* const pAlbum = AlbumManager::instance()->findPAlbum(QUrl::fromLocalFile(completePath));
84
85 if (pAlbum)
86 {
87 imageId = AlbumManager::instance()->getItemFromAlbum(pAlbum, fileName);
88 }
89
90 QString baseNameForMovingIntoTrash = createJsonRecordForFile(imageId,
91 imagePath,
92 deleteTime,
93 collection);
94
95 QString destinationInTrash = collection + QLatin1Char('/') + TRASH_FOLDER +
96 QLatin1Char('/') + FILES_FOLDER + QLatin1Char('/') +
97 baseNameForMovingIntoTrash + QLatin1Char('.') +
98 imageFileInfo.completeSuffix();
99
100 if (!QFile::rename(imagePath, destinationInTrash))
101 {
102 return false;
103 }
104
105 return true;
106 }
107
deleteDirRecursivley(const QString & dirToDelete,const QDateTime & deleteTime)108 bool DTrash::deleteDirRecursivley(const QString& dirToDelete, const QDateTime& deleteTime)
109 {
110 QDir srcDir(dirToDelete);
111
112 foreach (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Files))
113 {
114 if (!deleteImage(fileInfo.filePath(), deleteTime))
115 {
116 return false;
117 }
118 }
119
120 foreach (const QFileInfo& fileInfo, srcDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot))
121 {
122 if (!deleteDirRecursivley(fileInfo.filePath(), deleteTime))
123 {
124 return false;
125 }
126 }
127
128 return srcDir.removeRecursively();
129 }
130
extractJsonForItem(const QString & collPath,const QString & baseName,DTrashItemInfo & itemInfo)131 void DTrash::extractJsonForItem(const QString& collPath, const QString& baseName, DTrashItemInfo& itemInfo)
132 {
133 QString jsonFilePath = collPath + QLatin1Char('/') + TRASH_FOLDER +
134 QLatin1Char('/') + INFO_FOLDER + QLatin1Char('/') +
135 baseName + INFO_FILE_EXTENSION;
136
137 QFile jsonFile(jsonFilePath);
138
139 if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text))
140 {
141 return;
142 }
143
144 QJsonDocument doc = QJsonDocument::fromJson(jsonFile.readAll());
145 jsonFile.close();
146
147 QJsonObject fileInfoObj = doc.object();
148
149 itemInfo.jsonFilePath = jsonFilePath;
150
151 itemInfo.collectionPath = fileInfoObj.value(PATH_JSON_KEY).toString();
152
153 itemInfo.collectionRelativePath = fileInfoObj.value(PATH_JSON_KEY).toString()
154 .replace(collPath, QLatin1String(""));
155
156 itemInfo.deletionTimestamp = QDateTime::fromString(
157 fileInfoObj.value(DELETIONTIMESTAMP_JSON_KEY).toString());
158
159 QJsonValue imageIdValue = fileInfoObj.value(IMAGEID_JSON_KEY);
160
161 if (!imageIdValue.isUndefined())
162 {
163 itemInfo.imageId = imageIdValue.toString().toLongLong();
164 }
165 else
166 {
167 itemInfo.imageId = -1;
168 }
169 }
170
prepareCollectionTrash(const QString & collectionPath)171 bool DTrash::prepareCollectionTrash(const QString& collectionPath)
172 {
173 QString trashFolder = collectionPath + QLatin1Char('/') + TRASH_FOLDER;
174 QDir trashDir(trashFolder);
175
176 if (!trashDir.exists())
177 {
178 bool isCreated = true;
179
180 isCreated &= trashDir.mkpath(trashFolder);
181 isCreated &= trashDir.mkpath(trashFolder + QLatin1Char('/') + FILES_FOLDER);
182 isCreated &= trashDir.mkpath(trashFolder + QLatin1Char('/') + INFO_FOLDER);
183
184 if (!isCreated)
185 {
186 qCDebug(DIGIKAM_IOJOB_LOG) << "DTrash: could not create trash folder for collection";
187 return false;
188 }
189 }
190
191 qCDebug(DIGIKAM_IOJOB_LOG) << "Trash folder for collection: " << trashFolder;
192
193 return true;
194 }
195
createJsonRecordForFile(qlonglong imageId,const QString & imagePath,const QDateTime & deleteTime,const QString & collectionPath)196 QString DTrash::createJsonRecordForFile(qlonglong imageId,
197 const QString& imagePath,
198 const QDateTime& deleteTime,
199 const QString& collectionPath)
200 {
201 QJsonObject jsonObjForImg;
202
203 QJsonValue pathJsonVal(imagePath);
204 QJsonValue timestampJsonVal(deleteTime.toString());
205 QJsonValue imageIdJsonVal(QString::number(imageId));
206
207 jsonObjForImg.insert(PATH_JSON_KEY, pathJsonVal);
208 jsonObjForImg.insert(DELETIONTIMESTAMP_JSON_KEY, timestampJsonVal);
209 jsonObjForImg.insert(IMAGEID_JSON_KEY, imageIdJsonVal);
210
211 QJsonDocument jsonDocForImg(jsonObjForImg);
212
213 QFileInfo imgFileInfo(imagePath);
214
215 QString jsonFileName = getAvialableJsonFilePathInTrash(collectionPath,
216 imgFileInfo.baseName());
217
218 QFile jsonFileForImg(jsonFileName);
219
220 QFileInfo jsonFileInfo(jsonFileName);
221
222 if (!jsonFileForImg.open(QFile::WriteOnly))
223 {
224 return jsonFileInfo.baseName();
225 }
226
227 jsonFileForImg.write(jsonDocForImg.toJson());
228 jsonFileForImg.close();
229
230 return jsonFileInfo.baseName();
231 }
232
getAvialableJsonFilePathInTrash(const QString & collectionPath,const QString & baseName,int version)233 QString DTrash::getAvialableJsonFilePathInTrash(const QString& collectionPath,
234 const QString& baseName, int version)
235 {
236 QString pathToCreateJsonFile = collectionPath + QLatin1Char('/') +
237 TRASH_FOLDER + QLatin1Char('/') +
238 INFO_FOLDER + QLatin1Char('/') +
239 baseName + QLatin1Char('-') +
240 QUuid::createUuid().toString().mid(1, 8) +
241 (version ? QString::number(version) : QLatin1String("")) +
242 INFO_FILE_EXTENSION;
243
244 QFileInfo jsonFileInfo(pathToCreateJsonFile);
245
246 if (jsonFileInfo.exists())
247 {
248 return getAvialableJsonFilePathInTrash(collectionPath, baseName, ++version);
249 }
250 else
251 {
252 return pathToCreateJsonFile;
253 }
254 }
255
256 } // namespace Digikam
257