File indexing completed on 2024-04-28 05:11:34
0001 /* 0002 SPDX-FileCopyrightText: 2010 Bertjan Broeksema <broeksema@kde.org> 0003 SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include <config-enterprise.h> 0009 0010 #include "alarmpresets.h" 0011 #include "incidencedefaults.h" 0012 #include "incidenceeditor_debug.h" 0013 0014 #include <CalendarSupport/KCalPrefs> 0015 #include <akonadi/calendarsettings.h> //krazy:exclude=camelcase this is a generated file 0016 0017 #include <KContacts/Addressee> 0018 0019 #include <KCalendarCore/Alarm> 0020 #include <KCalendarCore/Event> 0021 #include <KCalendarCore/Journal> 0022 #include <KCalendarCore/Todo> 0023 0024 #include <KEmailAddress> 0025 0026 #include <KIO/StoredTransferJob> 0027 #include <KLocalizedString> 0028 0029 #include <QFile> 0030 #include <QUrl> 0031 0032 using namespace CalendarSupport; 0033 using namespace IncidenceEditorNG; 0034 using namespace KCalendarCore; 0035 0036 namespace IncidenceEditorNG 0037 { 0038 enum { UNSPECIFED_PRIORITY = 0 }; 0039 0040 class IncidenceDefaultsPrivate 0041 { 0042 public: 0043 /// Members 0044 KCalendarCore::Attachment::List mAttachments; 0045 QList<KCalendarCore::Attendee> mAttendees; 0046 QStringList mEmails; 0047 QString mGroupWareDomain; 0048 KCalendarCore::Incidence::Ptr mRelatedIncidence; 0049 QDateTime mStartDt; 0050 QDateTime mEndDt; 0051 bool mCleanupTemporaryFiles; 0052 0053 /// Methods 0054 [[nodiscard]] KCalendarCore::Person organizerAsPerson() const; 0055 [[nodiscard]] KCalendarCore::Attendee organizerAsAttendee(const KCalendarCore::Person &organizer) const; 0056 0057 void todoDefaults(const KCalendarCore::Todo::Ptr &todo) const; 0058 void eventDefaults(const KCalendarCore::Event::Ptr &event) const; 0059 void journalDefaults(const KCalendarCore::Journal::Ptr &journal) const; 0060 }; 0061 } 0062 0063 KCalendarCore::Person IncidenceDefaultsPrivate::organizerAsPerson() const 0064 { 0065 const QString invalidEmail = IncidenceDefaults::invalidEmailAddress(); 0066 0067 KCalendarCore::Person organizer; 0068 organizer.setName(i18nc("@label", "no (valid) identities found")); 0069 organizer.setEmail(invalidEmail); 0070 0071 if (mEmails.isEmpty()) { 0072 // Don't bother any longer, either someone forget to call setFullEmails, or 0073 // the user has no identities configured. 0074 return organizer; 0075 } 0076 0077 if (!mGroupWareDomain.isEmpty()) { 0078 // Check if we have an identity with an email that ends with the groupware 0079 // domain. 0080 for (const QString &fullEmail : std::as_const(mEmails)) { 0081 QString name; 0082 QString email; 0083 const bool success = KEmailAddress::extractEmailAddressAndName(fullEmail, email, name); 0084 if (success && email.endsWith(mGroupWareDomain)) { 0085 organizer.setName(name); 0086 organizer.setEmail(email); 0087 break; 0088 } 0089 } 0090 } 0091 0092 if (organizer.email() == invalidEmail) { 0093 // Either, no groupware was used, or we didn't find a groupware email address. 0094 // Now try to 0095 for (const QString &fullEmail : std::as_const(mEmails)) { 0096 QString name; 0097 QString email; 0098 const bool success = KEmailAddress::extractEmailAddressAndName(fullEmail, email, name); 0099 if (success) { 0100 organizer.setName(name); 0101 organizer.setEmail(email); 0102 break; 0103 } 0104 } 0105 } 0106 0107 return organizer; 0108 } 0109 0110 KCalendarCore::Attendee IncidenceDefaultsPrivate::organizerAsAttendee(const KCalendarCore::Person &organizer) const 0111 { 0112 KCalendarCore::Attendee organizerAsAttendee; 0113 // Really, the appropriate values (even the fall back values) should come from 0114 // organizer. (See organizerAsPerson for more details). 0115 organizerAsAttendee.setName(organizer.name()); 0116 organizerAsAttendee.setEmail(organizer.email()); 0117 // NOTE: Don't set the status to None, this value is not supported by the attendee 0118 // editor atm. 0119 organizerAsAttendee.setStatus(KCalendarCore::Attendee::Accepted); 0120 organizerAsAttendee.setRole(KCalendarCore::Attendee::ReqParticipant); 0121 return organizerAsAttendee; 0122 } 0123 0124 void IncidenceDefaultsPrivate::eventDefaults(const KCalendarCore::Event::Ptr &event) const 0125 { 0126 QDateTime startDT; 0127 if (mStartDt.isValid()) { 0128 startDT = mStartDt; 0129 } else { 0130 startDT = QDateTime::currentDateTime(); 0131 0132 if (KCalPrefs::instance()->startTime().isValid()) { 0133 startDT.setTime(KCalPrefs::instance()->startTime().time()); 0134 } 0135 } 0136 0137 if (startDT.timeSpec() == Qt::LocalTime) { 0138 // Ensure the default is not "floating" 0139 startDT.setTimeZone(QTimeZone::systemTimeZone()); 0140 } 0141 0142 const QTime defaultDurationTime = KCalPrefs::instance()->defaultDuration().time(); 0143 const int defaultDuration = (defaultDurationTime.hour() * 3600) + (defaultDurationTime.minute() * 60); 0144 0145 QDateTime endDT = mEndDt.isValid() ? mEndDt : startDT.addSecs(defaultDuration); 0146 0147 if (endDT.timeSpec() == Qt::LocalTime) { 0148 // Ensure the default is not "floating" 0149 endDT.setTimeZone(QTimeZone::systemTimeZone()); 0150 } 0151 0152 event->setDtStart(startDT); 0153 event->setDtEnd(endDT); 0154 event->setTransparency(KCalendarCore::Event::Opaque); 0155 0156 if (KCalPrefs::instance()->defaultEventReminders()) { 0157 event->addAlarm(AlarmPresets::defaultAlarm(AlarmPresets::BeforeStart)); 0158 } 0159 } 0160 0161 void IncidenceDefaultsPrivate::journalDefaults(const KCalendarCore::Journal::Ptr &journal) const 0162 { 0163 QDateTime startDT = mStartDt.isValid() ? mStartDt : QDateTime::currentDateTime(); 0164 if (startDT.timeSpec() == Qt::LocalTime) { 0165 // Ensure the default is not "floating" 0166 startDT.setTimeZone(QTimeZone::systemTimeZone()); 0167 } 0168 journal->setDtStart(startDT); 0169 journal->setAllDay(true); 0170 } 0171 0172 void IncidenceDefaultsPrivate::todoDefaults(const KCalendarCore::Todo::Ptr &todo) const 0173 { 0174 KCalendarCore::Todo::Ptr relatedTodo = mRelatedIncidence.dynamicCast<KCalendarCore::Todo>(); 0175 if (relatedTodo) { 0176 todo->setCategories(relatedTodo->categories()); 0177 } 0178 0179 // Now, but not in the "floating" time zone. 0180 auto const systemNow = QDateTime::currentDateTime().toTimeZone(QTimeZone::systemTimeZone()); 0181 0182 if (mEndDt.isValid()) { 0183 if (mEndDt.timeSpec() == Qt::LocalTime) { 0184 // Ensure the default is not "floating" 0185 todo->setDtDue(mEndDt.toTimeZone(QTimeZone::systemTimeZone()), true); 0186 } else { 0187 todo->setDtDue(mEndDt, true /* first */); 0188 } 0189 } else if (relatedTodo && relatedTodo->hasDueDate()) { 0190 todo->setDtDue(relatedTodo->dtDue(true), true /** first */); 0191 todo->setAllDay(relatedTodo->allDay()); 0192 } else if (relatedTodo) { 0193 todo->setDtDue(QDateTime()); 0194 } else { 0195 todo->setDtDue(systemNow.addDays(1), true /** first */); 0196 } 0197 0198 if (mStartDt.isValid()) { 0199 if (mStartDt.timeSpec() == Qt::LocalTime) { 0200 // Ensure the default is not "floating" 0201 todo->setDtStart(mStartDt.toTimeZone(QTimeZone::systemTimeZone())); 0202 } else { 0203 todo->setDtStart(mStartDt); 0204 } 0205 } else if (relatedTodo && !relatedTodo->hasStartDate()) { 0206 todo->setDtStart(QDateTime()); 0207 } else if (relatedTodo && relatedTodo->hasStartDate() && relatedTodo->dtStart() <= todo->dtDue()) { 0208 todo->setDtStart(relatedTodo->dtStart()); 0209 todo->setAllDay(relatedTodo->allDay()); 0210 } else if (!mEndDt.isValid() || systemNow < mEndDt) { 0211 todo->setDtStart(systemNow); 0212 } else { 0213 todo->setDtStart(mEndDt.addDays(-1)); 0214 } 0215 0216 todo->setCompleted(false); 0217 todo->setPercentComplete(0); 0218 0219 // I had a bunch of to-dos and couldn't distinguish between those that had priority '5' 0220 // because I wanted, and those that had priority '5' because it was set by default 0221 // and I forgot to unset it. 0222 // So don't be smart and try to guess a good default priority for the user, just use unspecified. 0223 todo->setPriority(UNSPECIFED_PRIORITY); 0224 0225 if (KCalPrefs::instance()->defaultTodoReminders()) { 0226 todo->addAlarm(AlarmPresets::defaultAlarm(AlarmPresets::BeforeEnd)); 0227 } 0228 } 0229 0230 /// IncidenceDefaults 0231 0232 IncidenceDefaults::IncidenceDefaults(bool cleanupAttachmentTemporaryFiles) 0233 : d_ptr(new IncidenceDefaultsPrivate) 0234 { 0235 d_ptr->mCleanupTemporaryFiles = cleanupAttachmentTemporaryFiles; 0236 } 0237 0238 IncidenceDefaults::IncidenceDefaults(const IncidenceDefaults &other) 0239 : d_ptr(new IncidenceDefaultsPrivate) 0240 { 0241 *d_ptr = *other.d_ptr; 0242 } 0243 0244 IncidenceDefaults::~IncidenceDefaults() = default; 0245 0246 IncidenceDefaults &IncidenceDefaults::operator=(const IncidenceDefaults &other) 0247 { 0248 if (&other != this) { 0249 *d_ptr = *other.d_ptr; 0250 } 0251 return *this; 0252 } 0253 0254 void IncidenceDefaults::setAttachments(const QStringList &attachments, 0255 const QStringList &attachmentMimetypes, 0256 const QStringList &attachmentLabels, 0257 bool inlineAttachment) 0258 { 0259 Q_D(IncidenceDefaults); 0260 d->mAttachments.clear(); 0261 0262 QStringList::ConstIterator it; 0263 int i = 0; 0264 for (it = attachments.constBegin(); it != attachments.constEnd(); ++it, ++i) { 0265 if (!(*it).isEmpty()) { 0266 QString mimeType; 0267 if (attachmentMimetypes.count() > i) { 0268 mimeType = attachmentMimetypes[i]; 0269 } 0270 0271 KCalendarCore::Attachment attachment; 0272 if (inlineAttachment) { 0273 auto job = KIO::storedGet(QUrl::fromUserInput(*it)); 0274 if (job->exec()) { 0275 const QByteArray data = job->data(); 0276 attachment = KCalendarCore::Attachment(data.toBase64(), mimeType); 0277 0278 if (i < attachmentLabels.count()) { 0279 attachment.setLabel(attachmentLabels[i]); 0280 } 0281 } else { 0282 qCCritical(INCIDENCEEDITOR_LOG) << "Error downloading uri " << *it << job->errorString(); 0283 } 0284 0285 if (d_ptr->mCleanupTemporaryFiles) { 0286 QFile file(*it); 0287 if (!file.remove()) { 0288 qCCritical(INCIDENCEEDITOR_LOG) << "Uname to remove file " << *it; 0289 } 0290 } 0291 } else { 0292 attachment = KCalendarCore::Attachment(*it, mimeType); 0293 if (i < attachmentLabels.count()) { 0294 attachment.setLabel(attachmentLabels[i]); 0295 } 0296 } 0297 0298 if (!attachment.isEmpty()) { 0299 if (attachment.label().isEmpty()) { 0300 if (attachment.isUri()) { 0301 attachment.setLabel(attachment.uri()); 0302 } else { 0303 attachment.setLabel(i18nc("@label attachment contains binary data", "[Binary data]")); 0304 } 0305 } 0306 d->mAttachments << attachment; 0307 attachment.setShowInline(inlineAttachment); 0308 } 0309 } 0310 } 0311 } 0312 0313 void IncidenceDefaults::setAttendees(const QStringList &attendees) 0314 { 0315 Q_D(IncidenceDefaults); 0316 d->mAttendees.clear(); 0317 QStringList::ConstIterator it; 0318 for (it = attendees.begin(); it != attendees.end(); ++it) { 0319 QString name; 0320 QString email; 0321 KContacts::Addressee::parseEmailAddress(*it, name, email); 0322 d->mAttendees << KCalendarCore::Attendee(name, email, true, KCalendarCore::Attendee::NeedsAction); 0323 } 0324 } 0325 0326 void IncidenceDefaults::setFullEmails(const QStringList &fullEmails) 0327 { 0328 Q_D(IncidenceDefaults); 0329 d->mEmails = fullEmails; 0330 } 0331 0332 void IncidenceDefaults::setGroupWareDomain(const QString &domain) 0333 { 0334 Q_D(IncidenceDefaults); 0335 d->mGroupWareDomain = domain; 0336 } 0337 0338 void IncidenceDefaults::setRelatedIncidence(const KCalendarCore::Incidence::Ptr &incidence) 0339 { 0340 Q_D(IncidenceDefaults); 0341 d->mRelatedIncidence = incidence; 0342 } 0343 0344 void IncidenceDefaults::setStartDateTime(const QDateTime &startDT) 0345 { 0346 Q_D(IncidenceDefaults); 0347 d->mStartDt = startDT; 0348 } 0349 0350 void IncidenceDefaults::setEndDateTime(const QDateTime &endDT) 0351 { 0352 Q_D(IncidenceDefaults); 0353 d->mEndDt = endDT; 0354 } 0355 0356 void IncidenceDefaults::setDefaults(const KCalendarCore::Incidence::Ptr &incidence) const 0357 { 0358 Q_D(const IncidenceDefaults); 0359 0360 // First some general defaults 0361 incidence->setSummary(QString(), false); 0362 incidence->setLocation(QString(), false); 0363 incidence->setCategories(QStringList()); 0364 incidence->setSecrecy(KCalendarCore::Incidence::SecrecyPublic); 0365 incidence->setStatus(KCalendarCore::Incidence::StatusNone); 0366 incidence->setAllDay(false); 0367 incidence->setCustomStatus(QString()); 0368 incidence->setResources(QStringList()); 0369 incidence->setPriority(0); 0370 0371 if (d->mRelatedIncidence) { 0372 incidence->setRelatedTo(d->mRelatedIncidence->uid()); 0373 } 0374 0375 incidence->clearAlarms(); 0376 incidence->clearAttachments(); 0377 incidence->clearAttendees(); 0378 incidence->clearComments(); 0379 incidence->clearContacts(); 0380 incidence->clearRecurrence(); 0381 0382 const KCalendarCore::Person organizerAsPerson = d->organizerAsPerson(); 0383 #if KDEPIM_ENTERPRISE_BUILD 0384 incidence->addAttendee(d->organizerAsAttendee(organizerAsPerson)); 0385 #endif 0386 for (const KCalendarCore::Attendee &attendee : std::as_const(d->mAttendees)) { 0387 incidence->addAttendee(attendee); 0388 } 0389 // Ical standard: No attendees -> must not have an organizer! 0390 if (incidence->attendeeCount()) { 0391 incidence->setOrganizer(organizerAsPerson); 0392 } 0393 0394 for (const KCalendarCore::Attachment &attachment : std::as_const(d->mAttachments)) { 0395 incidence->addAttachment(attachment); 0396 } 0397 0398 switch (incidence->type()) { 0399 case KCalendarCore::Incidence::TypeEvent: 0400 d->eventDefaults(incidence.dynamicCast<KCalendarCore::Event>()); 0401 break; 0402 case KCalendarCore::Incidence::TypeTodo: 0403 d->todoDefaults(incidence.dynamicCast<KCalendarCore::Todo>()); 0404 break; 0405 case KCalendarCore::Incidence::TypeJournal: 0406 d->journalDefaults(incidence.dynamicCast<KCalendarCore::Journal>()); 0407 break; 0408 default: 0409 qCDebug(INCIDENCEEDITOR_LOG) << "Unsupported incidence type, keeping current values. Type: " << static_cast<int>(incidence->type()); 0410 } 0411 } 0412 0413 /** static */ 0414 IncidenceDefaults IncidenceDefaults::minimalIncidenceDefaults(bool cleanupAttachmentTempFiles) 0415 { 0416 IncidenceDefaults defaults(cleanupAttachmentTempFiles); 0417 0418 // Set the full emails manually here, to avoid that we get dependencies on 0419 // KCalPrefs all over the place. 0420 defaults.setFullEmails(CalendarSupport::KCalPrefs::instance()->fullEmails()); 0421 0422 // NOTE: At some point this should be generalized. That is, we now use the 0423 // freebusy url as a hack, but this assumes that the user has only one 0424 // groupware account. Which doesn't have to be the case necessarily. 0425 // This method should somehow depend on the calendar selected to which 0426 // the incidence is added. 0427 if (CalendarSupport::KCalPrefs::instance()->useGroupwareCommunication()) { 0428 defaults.setGroupWareDomain(QUrl(Akonadi::CalendarSettings::self()->freeBusyRetrieveUrl()).host()); 0429 } 0430 return defaults; 0431 } 0432 0433 /** static */ 0434 QString IncidenceDefaults::invalidEmailAddress() 0435 { 0436 static const QString invalidEmail(i18nc("@label invalid email address marker", "invalid@email.address")); 0437 return invalidEmail; 0438 }