1 /** This file is part of Contacts daemon
3 ** Copyright (c) 2010-2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** Contact: Nokia Corporation (info@qt.nokia.com)
7 ** GNU Lesser General Public License Usage
8 ** This file may be used under the terms of the GNU Lesser General Public License
9 ** version 2.1 as published by the Free Software Foundation and appearing in the
10 ** file LICENSE.LGPL included in the packaging of this file. Please review the
11 ** following information to ensure the GNU Lesser General Public License version
12 ** 2.1 requirements will be met:
13 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
15 ** In addition, as a special exception, Nokia gives you certain additional rights.
16 ** These rights are described in the Nokia Qt LGPL Exception version 1.1, included
17 ** in the file LGPL_EXCEPTION.txt in this package.
20 ** Alternatively, this file may be used in accordance with the terms and
21 ** conditions contained in a signed written agreement between you and Nokia.
24 #include <QStringBuilder>
26 #include <QContactName>
27 #include <QContactBirthday>
31 #include <recurrencerule.h>
33 #include "cdbirthdaycalendar.h"
36 using namespace Contactsd;
39 const QLatin1String calNotebookId("b1376da7-5555-1111-2222-227549c4e570");
40 const QLatin1String calNotebookColor("#e00080"); // Pink
42 CDBirthdayCalendar::CDBirthdayCalendar(SyncMode syncMode, QObject *parent) :
47 mCalendar = mKCal::ExtendedCalendar::Ptr(new mKCal::ExtendedCalendar(KDateTime::Spec::LocalZone()));
48 mStorage = mKCal::ExtendedCalendar::defaultStorage(mCalendar);
50 MLocale * const locale = new MLocale(this);
52 if (not locale->isInstalledTrCatalog(QLatin1String("calendar"))) {
53 locale->installTrCatalog(QLatin1String("calendar"));
56 locale->connectSettings();
57 connect(locale, SIGNAL(settingsChanged()), this, SLOT(onLocaleChanged()));
59 MLocale::setDefault(*locale);
63 mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);
65 if (notebook.isNull()) {
66 notebook = createNotebook();
67 mStorage->addNotebook(notebook);
69 // Clear the calendar database if and only if restoring from a backup.
72 // Force calendar name update, if a locale change happened while contactsd was not running.
77 mStorage->deleteNotebook(notebook);
78 notebook = createNotebook();
79 mStorage->addNotebook(notebook);
85 CDBirthdayCalendar::~CDBirthdayCalendar()
91 debug() << "Destroyed birthday calendar";
94 mKCal::Notebook::Ptr CDBirthdayCalendar::createNotebook()
96 return mKCal::Notebook::Ptr(new mKCal::Notebook(calNotebookId,
97 qtTrId("qtn_caln_birthdays"),
100 false, // Not shared.
102 false, // Not synced to Ovi.
105 QLatin1String("Birthday-Nokia"),
110 void CDBirthdayCalendar::updateBirthday(const QContact &contact)
112 // Retrieve contact details.
113 const QContactDisplayLabel displayName = contact.detail<QContactDisplayLabel>();
114 const QDate contactBirthday = contact.detail<QContactBirthday>().date();
116 if (displayName.isEmpty() || contactBirthday.isNull()) {
117 warning() << Q_FUNC_INFO << "Contact without name or birthday, local ID: "
118 << contact.localId();
122 // Retrieve birthday event.
123 if (not mStorage->isValidNotebook(calNotebookId)) {
124 warning() << Q_FUNC_INFO << "Invalid notebook ID: " << calNotebookId;
128 KCalCore::Event::Ptr event = calendarEvent(contact.localId());
130 if (event.isNull()) {
132 event = KCalCore::Event::Ptr(new KCalCore::Event());
133 event->startUpdates();
134 event->setUid(calendarEventId(contact.localId()));
135 event->setAllDay(true);
137 // Ensure events appear as birthdays in the calendar, NB#259710.
138 event->setCategories(QStringList() << QLatin1String("BIRTHDAY"));
140 if (not mCalendar->addEvent(event, calNotebookId)) {
141 warning() << Q_FUNC_INFO << "Failed to add event to calendar";
145 // Update the existing event.
146 event->setReadOnly(false);
147 event->startUpdates();
150 // Transfer birthday details from contact to calendar event.
151 event->setSummary(displayName.label());
153 // Event has only date information, no time.
154 event->setDtStart(KDateTime(contactBirthday, QTime(), KDateTime::ClockTime));
155 event->setDtEnd(KDateTime(contactBirthday.addDays(1), QTime(), KDateTime::ClockTime));
157 // Must always set the recurrence as it depends on the event date.
158 KCalCore::Recurrence *const recurrence = event->recurrence();
160 if (contactBirthday.month() != 2 || contactBirthday.day() < 29) {
161 // Simply setup yearly recurrence for trivial dates.
162 recurrence->setStartDateTime(event->dtStart());
163 recurrence->setYearly(1); /* every year */
165 // For birthdays on February 29th the event shall occur on the
166 // last day of February. This is February 29th in leap years,
167 // and February 28th in all other years.
169 // NOTE: Actually this recurrence pattern will fail badly for
170 // people born on February 29th of the years 2100, 2200, 2300,
171 // 2500, ... - but I seriously doubt we care.
173 // NOTE2: Using setByYearDays() instead of just setting proper
174 // start dates, since libmkcal fails to store the start dates
175 // of recurrence rules.
176 KCalCore::RecurrenceRule *rule;
178 // 1. Include February 29th in leap years.
179 rule = new KCalCore::RecurrenceRule;
180 rule->setStartDt(event->dtStart());
181 rule->setByYearDays(QList<int>() << 60); // Feb 29th
182 rule->setRecurrenceType(KCalCore::RecurrenceRule::rYearly);
183 rule->setFrequency(4); // every 4th year
184 recurrence->addRRule(rule);
186 // 2. Include February 28th starting from year after birth.
187 rule = new KCalCore::RecurrenceRule;
188 rule->setStartDt(event->dtStart());
189 rule->setByYearDays(QList<int>() << 59); // Feb 28th
190 rule->setRecurrenceType(KCalCore::RecurrenceRule::rYearly);
191 rule->setFrequency(1); // every year
192 recurrence->addRRule(rule);
194 // 3. Exclude February 28th in leap years.
195 rule = new KCalCore::RecurrenceRule;
196 rule->setStartDt(event->dtStart());
197 rule->setByYearDays(QList<int>() << 59); // Feb 28th
198 rule->setRecurrenceType(KCalCore::RecurrenceRule::rYearly);
199 rule->setFrequency(4); // every 4th year
200 recurrence->addExRule(rule);
204 // Set the alarms on the event
205 event->clearAlarms();
206 KCalCore::Alarm::Ptr alarm = event->newAlarm();
207 alarm->setType(KCalCore::Alarm::Audio);
208 alarm->setEnabled(true);
209 alarm->setDisplayAlarm(event->summary());
210 alarm->setStartOffset(KCalCore::Duration(-36 * 3600 /* seconds */));
212 event->setReadOnly(true);
215 debug() << "Updated birthday event in calendar, local ID: " << contact.localId();
218 void CDBirthdayCalendar::deleteBirthday(QContactLocalId contactId)
220 KCalCore::Event::Ptr event = calendarEvent(contactId);
222 if (event.isNull()) {
223 debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
227 mCalendar->deleteEvent(event);
229 debug() << "Deleted birthday event in calendar, local ID: " << event->uid();
232 void CDBirthdayCalendar::save()
234 if (not mStorage->save()) {
235 warning() << Q_FUNC_INFO << "Failed to update birthdays in calendar";
239 QDate CDBirthdayCalendar::birthdayDate(QContactLocalId contactId)
241 KCalCore::Event::Ptr event = calendarEvent(contactId);
243 if (event.isNull()) {
247 return event->dtStart().date();
250 QString CDBirthdayCalendar::summary(QContactLocalId contactId)
252 KCalCore::Event::Ptr event = calendarEvent(contactId);
254 if (event.isNull()) {
258 return event->summary();
261 QString CDBirthdayCalendar::calendarEventId(QContactLocalId contactId)
263 static const QLatin1String calIdExtension("com.nokia.birthday/");
264 return calIdExtension + QString::number(contactId);
267 KCalCore::Event::Ptr CDBirthdayCalendar::calendarEvent(QContactLocalId contactId)
269 const QString eventId = calendarEventId(contactId);
271 if (not mStorage->load(eventId)) {
272 warning() << Q_FUNC_INFO << "Unable to load event from calendar";
273 return KCalCore::Event::Ptr();
276 KCalCore::Event::Ptr event = mCalendar->event(eventId);
278 if (event.isNull()) {
279 debug() << Q_FUNC_INFO << "Not found in calendar:" << contactId;
285 void CDBirthdayCalendar::onLocaleChanged()
287 mKCal::Notebook::Ptr notebook = mStorage->notebook(calNotebookId);
289 if (notebook.isNull()) {
290 warning() << Q_FUNC_INFO << "Calendar not found while changing locale";
294 const QString name = qtTrId("qtn_caln_birthdays");
296 debug() << Q_FUNC_INFO << "Updating calendar name to" << name;
297 notebook->setName(name);
299 if (not mStorage->updateNotebook(notebook)) {
300 warning() << Q_FUNC_INFO << "Could not save calendar";