Incidenceeditor

incidencedefaults.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Bertjan Broeksema <broeksema@kde.org>
3 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include <config-enterprise.h>
9
10#include "alarmpresets.h"
11#include "incidencedefaults.h"
12#include "incidenceeditor_debug.h"
13
14#include <CalendarSupport/KCalPrefs>
15#include <akonadi/calendarsettings.h> //krazy:exclude=camelcase this is a generated file
16
17#include <KContacts/Addressee>
18
19#include <KCalendarCore/Alarm>
20#include <KCalendarCore/Event>
21#include <KCalendarCore/Journal>
22#include <KCalendarCore/Todo>
23
24#include <KEmailAddress>
25
26#include <KIO/StoredTransferJob>
27#include <KLocalizedString>
28
29#include <QFile>
30#include <QUrl>
31
32using namespace CalendarSupport;
33using namespace IncidenceEditorNG;
34using namespace KCalendarCore;
35
36namespace IncidenceEditorNG
37{
38enum {
39 UNSPECIFED_PRIORITY = 0
40};
41
42class IncidenceDefaultsPrivate
43{
44public:
45 /// Members
48 QStringList mEmails;
49 QString mGroupWareDomain;
50 KCalendarCore::Incidence::Ptr mRelatedIncidence;
51 QDateTime mStartDt;
52 QDateTime mEndDt;
53 bool mCleanupTemporaryFiles;
54
55 /// Methods
56 [[nodiscard]] KCalendarCore::Person organizerAsPerson() const;
57 [[nodiscard]] KCalendarCore::Attendee organizerAsAttendee(const KCalendarCore::Person &organizer) const;
58
59 void todoDefaults(const KCalendarCore::Todo::Ptr &todo) const;
60 void eventDefaults(const KCalendarCore::Event::Ptr &event) const;
61 void journalDefaults(const KCalendarCore::Journal::Ptr &journal) const;
62};
63}
64
65KCalendarCore::Person IncidenceDefaultsPrivate::organizerAsPerson() const
66{
68
69 KCalendarCore::Person organizer;
70 organizer.setName(i18nc("@label", "no (valid) identities found"));
71 organizer.setEmail(invalidEmail);
72
73 if (mEmails.isEmpty()) {
74 // Don't bother any longer, either someone forget to call setFullEmails, or
75 // the user has no identities configured.
76 return organizer;
77 }
78
79 if (!mGroupWareDomain.isEmpty()) {
80 // Check if we have an identity with an email that ends with the groupware
81 // domain.
82 for (const QString &fullEmail : std::as_const(mEmails)) {
84 QString email;
85 const bool success = KEmailAddress::extractEmailAddressAndName(fullEmail, email, name);
86 if (success && email.endsWith(mGroupWareDomain)) {
87 organizer.setName(name);
88 organizer.setEmail(email);
89 break;
90 }
91 }
92 }
93
94 if (organizer.email() == invalidEmail) {
95 // Either, no groupware was used, or we didn't find a groupware email address.
96 // Now try to
97 for (const QString &fullEmail : std::as_const(mEmails)) {
99 QString email;
100 const bool success = KEmailAddress::extractEmailAddressAndName(fullEmail, email, name);
101 if (success) {
102 organizer.setName(name);
103 organizer.setEmail(email);
104 break;
105 }
106 }
107 }
108
109 return organizer;
110}
111
112KCalendarCore::Attendee IncidenceDefaultsPrivate::organizerAsAttendee(const KCalendarCore::Person &organizer) const
113{
114 KCalendarCore::Attendee organizerAsAttendee;
115 // Really, the appropriate values (even the fall back values) should come from
116 // organizer. (See organizerAsPerson for more details).
117 organizerAsAttendee.setName(organizer.name());
118 organizerAsAttendee.setEmail(organizer.email());
119 // NOTE: Don't set the status to None, this value is not supported by the attendee
120 // editor atm.
121 organizerAsAttendee.setStatus(KCalendarCore::Attendee::Accepted);
123 return organizerAsAttendee;
124}
125
126void IncidenceDefaultsPrivate::eventDefaults(const KCalendarCore::Event::Ptr &event) const
127{
128 QDateTime startDT;
129 if (mStartDt.isValid()) {
130 startDT = mStartDt;
131 } else {
132 startDT = QDateTime::currentDateTime();
133
134 if (KCalPrefs::instance()->startTime().isValid()) {
135 startDT.setTime(KCalPrefs::instance()->startTime().time());
136 }
137 }
138
139 if (startDT.timeSpec() == Qt::LocalTime) {
140 // Ensure the default is not "floating"
142 }
143
144 const QTime defaultDurationTime = KCalPrefs::instance()->defaultDuration().time();
145 const int defaultDuration = (defaultDurationTime.hour() * 3600) + (defaultDurationTime.minute() * 60);
146
147 QDateTime endDT = mEndDt.isValid() ? mEndDt : startDT.addSecs(defaultDuration);
148
149 if (endDT.timeSpec() == Qt::LocalTime) {
150 // Ensure the default is not "floating"
152 }
153
154 event->setDtStart(startDT);
155 event->setDtEnd(endDT);
156 event->setTransparency(KCalendarCore::Event::Opaque);
157
158 if (KCalPrefs::instance()->defaultEventReminders()) {
159 event->addAlarm(AlarmPresets::defaultAlarm(AlarmPresets::BeforeStart));
160 }
161}
162
163void IncidenceDefaultsPrivate::journalDefaults(const KCalendarCore::Journal::Ptr &journal) const
164{
165 QDateTime startDT = mStartDt.isValid() ? mStartDt : QDateTime::currentDateTime();
166 if (startDT.timeSpec() == Qt::LocalTime) {
167 // Ensure the default is not "floating"
169 }
170 journal->setDtStart(startDT);
171 journal->setAllDay(true);
172}
173
174void IncidenceDefaultsPrivate::todoDefaults(const KCalendarCore::Todo::Ptr &todo) const
175{
176 KCalendarCore::Todo::Ptr relatedTodo = mRelatedIncidence.dynamicCast<KCalendarCore::Todo>();
177 if (relatedTodo) {
178 todo->setCategories(relatedTodo->categories());
179 }
180
181 // Now, but not in the "floating" time zone.
183
184 if (mEndDt.isValid()) {
185 if (mEndDt.timeSpec() == Qt::LocalTime) {
186 // Ensure the default is not "floating"
187 todo->setDtDue(mEndDt.toTimeZone(QTimeZone::systemTimeZone()), true);
188 } else {
189 todo->setDtDue(mEndDt, true /* first */);
190 }
191 } else if (relatedTodo && relatedTodo->hasDueDate()) {
192 todo->setDtDue(relatedTodo->dtDue(true), true /** first */);
193 todo->setAllDay(relatedTodo->allDay());
194 } else if (relatedTodo) {
195 todo->setDtDue(QDateTime());
196 } else {
197 todo->setDtDue(systemNow.addDays(1), true /** first */);
198 }
199
200 if (mStartDt.isValid()) {
201 if (mStartDt.timeSpec() == Qt::LocalTime) {
202 // Ensure the default is not "floating"
203 todo->setDtStart(mStartDt.toTimeZone(QTimeZone::systemTimeZone()));
204 } else {
205 todo->setDtStart(mStartDt);
206 }
207 } else if (relatedTodo && !relatedTodo->hasStartDate()) {
208 todo->setDtStart(QDateTime());
209 } else if (relatedTodo && relatedTodo->hasStartDate() && relatedTodo->dtStart() <= todo->dtDue()) {
210 todo->setDtStart(relatedTodo->dtStart());
211 todo->setAllDay(relatedTodo->allDay());
212 } else if (!mEndDt.isValid() || systemNow < mEndDt) {
213 todo->setDtStart(systemNow);
214 } else {
215 todo->setDtStart(mEndDt.addDays(-1));
216 }
217
218 todo->setCompleted(false);
219 todo->setPercentComplete(0);
220
221 // I had a bunch of to-dos and couldn't distinguish between those that had priority '5'
222 // because I wanted, and those that had priority '5' because it was set by default
223 // and I forgot to unset it.
224 // So don't be smart and try to guess a good default priority for the user, just use unspecified.
225 todo->setPriority(UNSPECIFED_PRIORITY);
226
227 if (KCalPrefs::instance()->defaultTodoReminders()) {
228 todo->addAlarm(AlarmPresets::defaultAlarm(AlarmPresets::BeforeEnd));
229 }
230}
231
232/// IncidenceDefaults
233
234IncidenceDefaults::IncidenceDefaults(bool cleanupAttachmentTemporaryFiles)
235 : d_ptr(new IncidenceDefaultsPrivate)
236{
237 d_ptr->mCleanupTemporaryFiles = cleanupAttachmentTemporaryFiles;
238}
239
241 : d_ptr(new IncidenceDefaultsPrivate)
242{
243 *d_ptr = *other.d_ptr;
244}
245
246IncidenceDefaults::~IncidenceDefaults() = default;
247
248IncidenceDefaults &IncidenceDefaults::operator=(const IncidenceDefaults &other)
249{
250 if (&other != this) {
251 *d_ptr = *other.d_ptr;
252 }
253 return *this;
254}
255
257 const QStringList &attachmentMimetypes,
258 const QStringList &attachmentLabels,
259 bool inlineAttachment)
260{
262 d->mAttachments.clear();
263
265 int i = 0;
266 for (it = attachments.constBegin(); it != attachments.constEnd(); ++it, ++i) {
267 if (!(*it).isEmpty()) {
268 QString mimeType;
269 if (attachmentMimetypes.count() > i) {
270 mimeType = attachmentMimetypes[i];
271 }
272
273 KCalendarCore::Attachment attachment;
274 if (inlineAttachment) {
275 auto job = KIO::storedGet(QUrl::fromUserInput(*it));
276 if (job->exec()) {
277 const QByteArray data = job->data();
278 attachment = KCalendarCore::Attachment(data.toBase64(), mimeType);
279
280 if (i < attachmentLabels.count()) {
281 attachment.setLabel(attachmentLabels[i]);
282 }
283 } else {
284 qCCritical(INCIDENCEEDITOR_LOG) << "Error downloading uri " << *it << job->errorString();
285 }
286
287 if (d_ptr->mCleanupTemporaryFiles) {
288 QFile file(*it);
289 if (!file.remove()) {
290 qCCritical(INCIDENCEEDITOR_LOG) << "Uname to remove file " << *it;
291 }
292 }
293 } else {
294 attachment = KCalendarCore::Attachment(*it, mimeType);
295 if (i < attachmentLabels.count()) {
296 attachment.setLabel(attachmentLabels[i]);
297 }
298 }
299
300 if (!attachment.isEmpty()) {
301 if (attachment.label().isEmpty()) {
302 if (attachment.isUri()) {
303 attachment.setLabel(attachment.uri());
304 } else {
305 attachment.setLabel(i18nc("@label attachment contains binary data", "[Binary data]"));
306 }
307 }
308 d->mAttachments << attachment;
309 attachment.setShowInline(inlineAttachment);
310 }
311 }
312 }
313}
314
316{
318 d->mAttendees.clear();
320 for (it = attendees.begin(); it != attendees.end(); ++it) {
321 QString name;
322 QString email;
324 d->mAttendees << KCalendarCore::Attendee(name, email, true, KCalendarCore::Attendee::NeedsAction);
325 }
326}
327
329{
331 d->mEmails = fullEmails;
332}
333
335{
337 d->mGroupWareDomain = domain;
338}
339
341{
343 d->mRelatedIncidence = incidence;
344}
345
347{
349 d->mStartDt = startDT;
350}
351
353{
355 d->mEndDt = endDT;
356}
357
359{
360 Q_D(const IncidenceDefaults);
361
362 // First some general defaults
363 incidence->setSummary(QString(), false);
364 incidence->setLocation(QString(), false);
365 incidence->setCategories(QStringList());
366 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPublic);
367 incidence->setStatus(KCalendarCore::Incidence::StatusNone);
368 incidence->setAllDay(false);
369 incidence->setCustomStatus(QString());
370 incidence->setResources(QStringList());
371 incidence->setPriority(0);
372
373 if (d->mRelatedIncidence) {
374 incidence->setRelatedTo(d->mRelatedIncidence->uid());
375 }
376
377 incidence->clearAlarms();
378 incidence->clearAttachments();
379 incidence->clearAttendees();
380 incidence->clearComments();
381 incidence->clearContacts();
382 incidence->clearRecurrence();
383
384 const KCalendarCore::Person organizerAsPerson = d->organizerAsPerson();
385#if KDEPIM_ENTERPRISE_BUILD
386 incidence->addAttendee(d->organizerAsAttendee(organizerAsPerson));
387#endif
388 for (const KCalendarCore::Attendee &attendee : std::as_const(d->mAttendees)) {
389 incidence->addAttendee(attendee);
390 }
391 // Ical standard: No attendees -> must not have an organizer!
392 if (incidence->attendeeCount()) {
393 incidence->setOrganizer(organizerAsPerson);
394 }
395
396 for (const KCalendarCore::Attachment &attachment : std::as_const(d->mAttachments)) {
397 incidence->addAttachment(attachment);
398 }
399
400 switch (incidence->type()) {
402 d->eventDefaults(incidence.dynamicCast<KCalendarCore::Event>());
403 break;
405 d->todoDefaults(incidence.dynamicCast<KCalendarCore::Todo>());
406 break;
408 d->journalDefaults(incidence.dynamicCast<KCalendarCore::Journal>());
409 break;
410 default:
411 qCDebug(INCIDENCEEDITOR_LOG) << "Unsupported incidence type, keeping current values. Type: " << static_cast<int>(incidence->type());
412 }
413}
414
415/** static */
417{
418 IncidenceDefaults defaults(cleanupAttachmentTempFiles);
419
420 // Set the full emails manually here, to avoid that we get dependencies on
421 // KCalPrefs all over the place.
422 defaults.setFullEmails(CalendarSupport::KCalPrefs::instance()->fullEmails());
423
424 // NOTE: At some point this should be generalized. That is, we now use the
425 // freebusy url as a hack, but this assumes that the user has only one
426 // groupware account. Which doesn't have to be the case necessarily.
427 // This method should somehow depend on the calendar selected to which
428 // the incidence is added.
429 if (CalendarSupport::KCalPrefs::instance()->useGroupwareCommunication()) {
430 defaults.setGroupWareDomain(QUrl(Akonadi::CalendarSettings::self()->freeBusyRetrieveUrl()).host());
431 }
432 return defaults;
433}
434
435/** static */
437{
438 static const QString invalidEmail(i18nc("@label invalid email address marker", "invalid@email.address"));
439 return invalidEmail;
440}
The IncidenceDefaults class.
void setAttendees(const QStringList &attendees)
Sets the attendees that are added by default to incidences.
void setFullEmails(const QStringList &fullEmails)
Sets the list of identities to be used for the user.
static IncidenceDefaults minimalIncidenceDefaults(bool cleanupAttachmentTempFiles=false)
Returns minimal incidence defaults: e-mails and groupware domain.
void setGroupWareDomain(const QString &domain)
This is used to do a smarter guess about which identity to use for the organizer.
void setStartDateTime(const QDateTime &startDT)
Set the start date/time to use for passed incidences.
IncidenceDefaults(bool cleanupAttachmentTEmporaryFiles=false)
IncidenceDefaults.
void setEndDateTime(const QDateTime &endDT)
Set the end date/time to use for passed incidences.
void setDefaults(const KCalendarCore::Incidence::Ptr &incidence) const
Sets the default values for.
void setAttachments(const QStringList &attachments, const QStringList &attachmentMimetypes=QStringList(), const QStringList &attachmentLabels=QStringList(), bool inlineAttachment=false)
Sets the attachments that are added by default to incidences.
void setRelatedIncidence(const KCalendarCore::Incidence::Ptr &incidence)
Sets the incidence related to the incidence for which to set the defaults.
void setShowInline(bool showinline)
void setLabel(const QString &label)
void setStatus(PartStat status)
void setName(const QString &name)
void setRole(Role role)
void setEmail(const QString &email)
QString email() const
QString name() const
void setEmail(const QString &email)
void setName(const QString &name)
static void parseEmailAddress(const QString &rawEmail, QString &fullName, QString &email)
KCODECS_EXPORT bool extractEmailAddressAndName(const QString &aStr, QString &mail, QString &name)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
AKONADI_CALENDAR_EXPORT KCalendarCore::Journal::Ptr journal(const Akonadi::Item &item)
AKONADI_CALENDAR_EXPORT KCalendarCore::Todo::Ptr todo(const Akonadi::Item &item)
KIOCORE_EXPORT StoredTransferJob * storedGet(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
bool isValid(QStringView ifopt)
QString name(StandardAction id)
char * data()
QByteArray toBase64(Base64Options options) const const
QDateTime addDays(qint64 ndays) const const
QDateTime addSecs(qint64 s) const const
QDateTime currentDateTime()
bool isValid() const const
void setTime(QTime time)
void setTimeZone(const QTimeZone &toZone)
Qt::TimeSpec timeSpec() const const
QDateTime toTimeZone(const QTimeZone &timeZone) const const
bool remove()
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
iterator end()
bool isEmpty() const const
QSharedPointer< X > dynamicCast() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
LocalTime
int hour() const const
int minute() const const
QTimeZone systemTimeZone()
QUrl fromUserInput(const QString &userInput, const QString &workingDirectory, UserInputResolutionOptions options)
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:17:23 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.