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 | |
23 | using namespace Baloo; |
24 | |
25 | // Pseudo plugin class to embed meta data |
26 | class KIOPluginForMetaData : public QObject |
27 | { |
28 | Q_OBJECT |
29 | Q_PLUGIN_METADATA(IID "org.kde.kio.worker.timeline" FILE "timeline.json" ) |
30 | }; |
31 | |
32 | namespace |
33 | { |
34 | KIO::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 | |
50 | KIO::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 | |
70 | KIO::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 | |
83 | KIO::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 | |
94 | TimelineProtocol::TimelineProtocol(const QByteArray& poolSocket, const QByteArray& appSocket) |
95 | : KIO::WorkerBase("timeline" , poolSocket, appSocket) |
96 | { |
97 | } |
98 | |
99 | TimelineProtocol::~TimelineProtocol() |
100 | { |
101 | } |
102 | |
103 | KIO::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 | |
154 | KIO::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 | |
177 | KIO::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 | |
212 | void 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 | |
224 | bool 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 | |
235 | void 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 | |
252 | extern "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 | |