1/*
2 SPDX-FileCopyrightText: 2009-2010 Sebastian Trueg <trueg@kde.org>
3 SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in>
4 SPDX-FileCopyrightText: 2018-2020 Stefan BrĂ¼ns <bruns@kde.org>
5
6 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
7*/
8
9#include "kio_timeline.h"
10#include "timelinetools.h"
11#include "query.h"
12#include "resultiterator.h"
13#include "../common/udstools.h"
14#include <sys/stat.h>
15
16#include <QCoreApplication>
17
18#include <KUser>
19#include <KFormat>
20
21#include <KLocalizedString>
22
23using namespace Baloo;
24
25// Pseudo plugin class to embed meta data
26class KIOPluginForMetaData : public QObject
27{
28 Q_OBJECT
29 Q_PLUGIN_METADATA(IID "org.kde.kio.worker.timeline" FILE "timeline.json")
30};
31
32namespace
33{
34KIO::UDSEntry createFolderUDSEntry(const QString& name)
35{
36 KIO::UDSEntry uds;
37 uds.reserve(size: 5);
38 uds.fastInsert(field: KIO::UDSEntry::UDS_NAME, value: name);
39 uds.fastInsert(field: KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
40 uds.fastInsert(field: KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory"));
41#ifdef Q_OS_WIN
42 uds.fastInsert(KIO::UDSEntry::UDS_ACCESS, _S_IREAD );
43#else
44 uds.fastInsert(field: KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR);
45#endif
46 uds.fastInsert(field: KIO::UDSEntry::UDS_USER, value: KUser().loginName());
47 return uds;
48}
49
50KIO::UDSEntry createDateFolderUDSEntry(const QString& name, const QString& displayName, const QDate& date)
51{
52 KIO::UDSEntry uds;
53 uds.reserve(size: 8);
54 QDateTime dt(date, QTime(0, 0, 0));
55 uds.fastInsert(field: KIO::UDSEntry::UDS_NAME, value: name);
56 uds.fastInsert(field: KIO::UDSEntry::UDS_DISPLAY_NAME, value: displayName);
57 uds.fastInsert(field: KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR);
58 uds.fastInsert(field: KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory"));
59 uds.fastInsert(field: KIO::UDSEntry::UDS_MODIFICATION_TIME, l: dt.toSecsSinceEpoch());
60 uds.fastInsert(field: KIO::UDSEntry::UDS_CREATION_TIME, l: dt.toSecsSinceEpoch());
61#ifdef Q_OS_WIN
62 uds.fastInsert(KIO::UDSEntry::UDS_ACCESS, _S_IREAD );
63#else
64 uds.fastInsert(field: KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR);
65#endif
66 uds.fastInsert(field: KIO::UDSEntry::UDS_USER, value: KUser().loginName());
67 return uds;
68}
69
70KIO::UDSEntry createMonthUDSEntry(int month, int year)
71{
72 QString dateString = QDate(year, month, 1).toString(
73 i18nc("Month and year used in a tree above the actual days. "
74 "Have a look at https://doc.qt.io/qt-5/qdate.html#toString "
75 "to see which variables you can use and ask kde-i18n-doc@kde.org if you have "
76 "problems understanding how to translate this",
77 "MMMM yyyy"));
78 return createDateFolderUDSEntry(name: QDate(year, month, 1).toString(QStringLiteral("yyyy-MM")),
79 displayName: dateString,
80 date: QDate(year, month, 1));
81}
82
83KIO::UDSEntry createDayUDSEntry(const QDate& date)
84{
85 KIO::UDSEntry uds = createDateFolderUDSEntry(name: date.toString(QStringLiteral("yyyy-MM-dd")),
86 displayName: KFormat().formatRelativeDate(date, format: QLocale::LongFormat),
87 date);
88
89 return uds;
90}
91
92}
93
94TimelineProtocol::TimelineProtocol(const QByteArray& poolSocket, const QByteArray& appSocket)
95 : KIO::WorkerBase("timeline", poolSocket, appSocket)
96{
97}
98
99TimelineProtocol::~TimelineProtocol()
100{
101}
102
103KIO::WorkerResult TimelineProtocol::listDir(const QUrl& url)
104{
105 QUrl canonicalUrl = canonicalizeTimelineUrl(url);
106 if (url != canonicalUrl) {
107 redirection(url: canonicalUrl);
108 return KIO::WorkerResult::pass();
109 }
110
111 switch (parseTimelineUrl(url, date: &m_date, filename: &m_filename)) {
112 case RootFolder:
113 listEntry(entry: createFolderUDSEntry(QStringLiteral(".")));
114 listEntry(entry: createDateFolderUDSEntry(QStringLiteral("today"), i18n("Today"), date: QDate::currentDate()));
115 listEntry(entry: createDateFolderUDSEntry(QStringLiteral("calendar"), i18n("Calendar"), date: QDate::currentDate()));
116 break;
117
118 case CalendarFolder:
119 listEntry(entry: createFolderUDSEntry(QStringLiteral(".")));
120 listThisYearsMonths();
121 // TODO: add entry for previous years
122 break;
123
124 case MonthFolder:
125 listEntry(entry: createFolderUDSEntry(QStringLiteral(".")));
126 listDays(month: m_date.month(), year: m_date.year());
127 break;
128
129 case DayFolder: {
130 listEntry(entry: createFolderUDSEntry(QStringLiteral(".")));
131 Query query;
132 query.setDateFilter(year: m_date.year(), month: m_date.month(), day: m_date.day());
133 query.setSortingOption(Query::SortNone);
134
135 UdsFactory udsf;
136
137 ResultIterator it = query.exec();
138 while (it.next()) {
139 KIO::UDSEntry uds = udsf.createUdsEntry(filePath: it.filePath());
140 if (uds.count()) {
141 listEntry(entry: uds);
142 }
143 }
144 break;
145 }
146
147 case NoFolder:
148 return KIO::WorkerResult::fail(error: KIO::ERR_DOES_NOT_EXIST, errorString: url.toString());
149 }
150
151 return KIO::WorkerResult::pass();
152}
153
154KIO::WorkerResult TimelineProtocol::mimetype(const QUrl& url)
155{
156 QUrl canonicalUrl = canonicalizeTimelineUrl(url);
157 if (url != canonicalUrl) {
158 redirection(url: canonicalUrl);
159 return KIO::WorkerResult::pass();
160 }
161
162 switch (parseTimelineUrl(url, date: &m_date, filename: &m_filename)) {
163 case RootFolder:
164 case CalendarFolder:
165 case MonthFolder:
166 case DayFolder:
167 mimetype(url: QUrl(QLatin1String("inode/directory")));
168 break;
169
170 case NoFolder:
171 return KIO::WorkerResult::fail(error: KIO::ERR_DOES_NOT_EXIST, errorString: url.toString());
172 }
173
174 return KIO::WorkerResult::pass();
175}
176
177KIO::WorkerResult TimelineProtocol::stat(const QUrl& url)
178{
179 QUrl canonicalUrl = canonicalizeTimelineUrl(url);
180 if (url != canonicalUrl) {
181 redirection(url: canonicalUrl);
182 return KIO::WorkerResult::pass();
183 }
184
185 switch (parseTimelineUrl(url, date: &m_date, filename: &m_filename)) {
186 case RootFolder: {
187 statEntry(entry: createFolderUDSEntry(QStringLiteral("/")));
188 break;
189 }
190
191 case CalendarFolder:
192 statEntry(entry: createDateFolderUDSEntry(QStringLiteral("calendar"), i18n("Calendar"), date: QDate::currentDate()));
193 break;
194
195 case MonthFolder:
196 statEntry(entry: createMonthUDSEntry(month: m_date.month(), year: m_date.year()));
197 break;
198
199 case DayFolder:
200 if (m_filename.isEmpty()) {
201 statEntry(entry: createDayUDSEntry(date: m_date));
202 }
203 break;
204
205 case NoFolder:
206 return KIO::WorkerResult::fail(error: KIO::ERR_DOES_NOT_EXIST, errorString: url.toString());
207 }
208
209 return KIO::WorkerResult::pass();
210}
211
212void TimelineProtocol::listDays(int month, int year)
213{
214 const int days = QDate(year, month, 1).daysInMonth();
215 for (int day = 1; day <= days; ++day) {
216 QDate date(year, month, day);
217
218 if (date <= QDate::currentDate() && filesInDate(date)) {
219 listEntry(entry: createDayUDSEntry(date));
220 }
221 }
222}
223
224bool TimelineProtocol::filesInDate(const QDate& date)
225{
226 Query query;
227 query.setLimit(1);
228 query.setDateFilter(year: date.year(), month: date.month(), day: date.day());
229 query.setSortingOption(Query::SortNone);
230
231 ResultIterator it = query.exec();
232 return it.next();
233}
234
235void TimelineProtocol::listThisYearsMonths()
236{
237 Query query;
238 query.setLimit(1);
239 query.setSortingOption(Query::SortNone);
240
241 int year = QDate::currentDate().year();
242 int currentMonth = QDate::currentDate().month();
243 for (int month = 1; month <= currentMonth; ++month) {
244 query.setDateFilter(year, month);
245 ResultIterator it = query.exec();
246 if (it.next()) {
247 listEntry(entry: createMonthUDSEntry(month, year));
248 }
249 }
250}
251
252extern "C"
253{
254 Q_DECL_EXPORT int kdemain(int argc, char** argv)
255 {
256 QCoreApplication app(argc, argv);
257 app.setApplicationName(QStringLiteral("kio_timeline"));
258 Baloo::TimelineProtocol worker(argv[2], argv[3]);
259 worker.dispatchLoop();
260 return 0;
261 }
262}
263
264#include "kio_timeline.moc"
265

source code of baloo/src/kioworkers/timeline/kio_timeline.cpp