Skip to content

Commit

Permalink
Merge branch 'props' into 'master'
Browse files Browse the repository at this point in the history
[mkcal] Add a new table to store custom properties for notebooks.

See merge request mer-core/mkcal!26
  • Loading branch information
pvuorela committed Oct 21, 2019
2 parents 734166e + 341cfb2 commit a86b0ab
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 5 deletions.
23 changes: 22 additions & 1 deletion src/notebook.cpp
Expand Up @@ -37,6 +37,7 @@ using namespace KCalCore;
#include <kdebug.h>

#include <QtCore/QStringList>
#include <QtCore/QHash>

using namespace mKCal;

Expand Down Expand Up @@ -124,7 +125,7 @@ class mKCal::Notebook::Private
QStringList mSharedWith;
QString mSyncProfile;
KDateTime mCreationDate;

QHash<QByteArray, QString> mCustomProperties;
};
//@endcond

Expand Down Expand Up @@ -523,3 +524,23 @@ bool Notebook::incidenceAllowed(Incidence::Ptr incidence) const
// Default accept
return true;
}

void Notebook::setCustomProperty(const QByteArray &key, const QString &value)
{
d->mModifiedDate = KDateTime::currentUtcDateTime();
if (value.isEmpty()) {
d->mCustomProperties.remove(key);
} else {
d->mCustomProperties.insert(key, value);
}
}

QString Notebook::customProperty(const QByteArray &key, const QString &defaultValue) const
{
return d->mCustomProperties.value(key, defaultValue);
}

QList<QByteArray> Notebook::customPropertyKeys() const
{
return d->mCustomProperties.uniqueKeys();
}
21 changes: 21 additions & 0 deletions src/notebook.h
Expand Up @@ -436,6 +436,27 @@ class MKCAL_EXPORT Notebook
*/
int flags() const;

/**
Set a key/value property. Setting the value to the empty string
will remove the property.
@param key The name of the property.
@param value The value of the property.
*/
void setCustomProperty(const QByteArray &key, const QString &value);

/**
A getter function for a custom property, see
setCustomProperty().
@param key The name of the property.
@param default A default value if the property does not exists.
*/
QString customProperty(const QByteArray &key, const QString &defaultValue = QString()) const;

/**
List the keys of all stored custom properties.
*/
QList<QByteArray> customPropertyKeys() const;

/**
Assignment operator.
*/
Expand Down
125 changes: 124 additions & 1 deletion src/sqliteformat.cpp
Expand Up @@ -54,19 +54,30 @@ class mKCal::SqliteFormat::Private
public:
Private(SqliteStorage *storage, sqlite3 *database)
: mStorage(storage), mDatabase(database), mTimeSpec(KDateTime::UTC)
, mSelectCalProps(nullptr)
, mInsertCalProps(nullptr)
{
}
~Private() { }
~Private()
{
sqlite3_finalize(mSelectCalProps);
sqlite3_finalize(mInsertCalProps);
}
SqliteStorage *mStorage;
sqlite3 *mDatabase;
KDateTime::Spec mTimeSpec;

// Cache for various queries.
sqlite3_stmt *mSelectCalProps;
sqlite3_stmt *mInsertCalProps;

bool selectCustomproperties(Incidence::Ptr incidence, int rowid, sqlite3_stmt *stmt);
int selectRowId(Incidence::Ptr incidence);
bool selectRecursives(Incidence::Ptr incidence, int rowid, sqlite3_stmt *stmt);
bool selectAlarms(Incidence::Ptr incidence, int rowid, sqlite3_stmt *stmt);
bool selectAttendees(Incidence::Ptr incidence, int rowid, sqlite3_stmt *stmt);
bool selectRdates(Incidence::Ptr incidence, int rowid, sqlite3_stmt *stmt);
bool selectCalendarProperties(Notebook::Ptr notebook);
bool modifyCustomproperties(Incidence::Ptr incidence, int rowid, DBOperation dbop,
sqlite3_stmt *stmt1, sqlite3_stmt *stmt2);
bool modifyCustomproperty(int rowid, const QByteArray &key, const QString &value,
Expand All @@ -86,6 +97,10 @@ class mKCal::SqliteFormat::Private
sqlite3_stmt *stmt2);
bool modifyRdate(int rowid, int type, const KDateTime &rdate, DBOperation dbop,
sqlite3_stmt *stmt);
bool modifyCalendarProperties(Notebook::Ptr notebook, DBOperation dbop);
bool deleteCalendarProperties(const QByteArray &id);
bool insertCalendarProperty(const QByteArray &id, const QByteArray &key,
const QByteArray &value);
};
//@endcond

Expand Down Expand Up @@ -141,6 +156,10 @@ bool SqliteFormat::modifyCalendars(const Notebook::Ptr &notebook,

sqlite3_step(stmt);

if (!d->modifyCalendarProperties(notebook, dbop)) {
qCWarning(lcMkcal) << "failed to modify calendarproperties for notebook" << uid;
}

return true;

error:
Expand Down Expand Up @@ -949,6 +968,76 @@ bool SqliteFormat::Private::modifyAttendee(int rowid, Attendee::Ptr attendee, DB

return success;
}

bool SqliteFormat::Private::modifyCalendarProperties(Notebook::Ptr notebook, DBOperation dbop)
{
QByteArray id(notebook->uid().toUtf8());
// In Update always delete all first then insert all
if (dbop == DBUpdate && !deleteCalendarProperties(id)) {
qCWarning(lcMkcal) << "failed to delete calendarproperties for notebook" << id;
return false;
}

bool success = true;
if (dbop == DBInsert || dbop == DBUpdate) {
QList<QByteArray> properties = notebook->customPropertyKeys();
for (QList<QByteArray>::ConstIterator it = properties.constBegin();
it != properties.constEnd(); ++it) {
if (!insertCalendarProperty(id, *it, notebook->customProperty(*it).toUtf8())) {
qCWarning(lcMkcal) << "failed to insert calendarproperty" << *it << "in notebook" << id;
success = false;
}
}
}
return success;
}

bool SqliteFormat::Private::deleteCalendarProperties(const QByteArray &id)
{
int rv = 0;
int index = 1;
bool success = false;

const char *query = DELETE_CALENDARPROPERTIES;
int qsize = sizeof(DELETE_CALENDARPROPERTIES);
sqlite3_stmt *stmt = NULL;

sqlite3_prepare_v2(mDatabase, query, qsize, &stmt, NULL);
sqlite3_bind_text(stmt, index, id.constData(), id.length(), SQLITE_STATIC);
sqlite3_step(stmt);
success = true;

error:
sqlite3_finalize(stmt);

return success;
}

bool SqliteFormat::Private::insertCalendarProperty(const QByteArray &id,
const QByteArray &key,
const QByteArray &value)
{
int rv = 0;
int index = 1;
bool success = false;

if (!mInsertCalProps) {
const char *query = INSERT_CALENDARPROPERTIES;
int qsize = sizeof(INSERT_CALENDARPROPERTIES);
sqlite3_prepare_v2(mDatabase, query, qsize, &mInsertCalProps, NULL);
}

sqlite3_bind_text(mInsertCalProps, index, id.constData(), id.length(), SQLITE_STATIC);
sqlite3_bind_text(mInsertCalProps, index, key.constData(), key.length(), SQLITE_STATIC);
sqlite3_bind_text(mInsertCalProps, index, value.constData(), value.length(), SQLITE_STATIC);
sqlite3_step(mInsertCalProps);
success = true;

error:
sqlite3_reset(mInsertCalProps);

return success;
}
//@endcond

Notebook::Ptr SqliteFormat::selectCalendars(sqlite3_stmt *stmt)
Expand Down Expand Up @@ -992,6 +1081,10 @@ Notebook::Ptr SqliteFormat::selectCalendars(sqlite3_stmt *stmt)
notebook->setSyncProfile(syncProfile);
notebook->setCreationDate(creationDate);

if (!d->selectCalendarProperties(notebook)) {
qCWarning(lcMkcal) << "failed to get calendarproperties for notebook" << id;
}

// This has to be called last! Otherwise the last modified date
// will be roughly now, and not whenever notebook was really last
// modified.
Expand Down Expand Up @@ -1707,3 +1800,33 @@ Person::List SqliteFormat::selectContacts(sqlite3_stmt *stmt)
error:
return list;
}

bool SqliteFormat::Private::selectCalendarProperties(Notebook::Ptr notebook)
{
int rv = 0;
int index = 1;
const QByteArray id(notebook->uid().toUtf8());
bool success = false;

if (!mSelectCalProps) {
const char *query = SELECT_CALENDARPROPERTIES_BY_ID;
int qsize = sizeof(SELECT_CALENDARPROPERTIES_BY_ID);
sqlite3_prepare_v2(mDatabase, query, qsize, &mSelectCalProps, NULL);
}

sqlite3_bind_text(mSelectCalProps, index, id.constData(), id.length(), SQLITE_STATIC);
do {
sqlite3_step(mSelectCalProps);
if (rv == SQLITE_ROW) {
const QByteArray name = (const char *)sqlite3_column_text(mSelectCalProps, 1);
const QString value = QString::fromUtf8((const char *)sqlite3_column_text(mSelectCalProps, 2));
notebook->setCustomProperty(name, value);
}
} while (rv != SQLITE_DONE);
success = true;

error:
sqlite3_reset(mSelectCalProps);

return success;
}
2 changes: 1 addition & 1 deletion src/sqliteformat.h
Expand Up @@ -79,7 +79,7 @@ class MKCAL_EXPORT SqliteFormat
@param notebook notebook to update
@param dbop database operation
@param stmt prepared sqlite statement for components table
@param stmt prepared sqlite statement for calendars table
@return true if the operation was successful; false otherwise.
*/
bool modifyCalendars(const Notebook::Ptr &notebook, DBOperation dbop, sqlite3_stmt *stmt);
Expand Down
9 changes: 9 additions & 0 deletions src/sqlitestorage.cpp
Expand Up @@ -225,6 +225,9 @@ bool SqliteStorage::open()
query = CREATE_ATTENDEE;
sqlite3_exec(d->mDatabase);

query = CREATE_CALENDARPROPERTIES;
sqlite3_exec(d->mDatabase);

/* Create index on frequently used columns */
query = INDEX_CALENDAR;
sqlite3_exec(d->mDatabase);
Expand Down Expand Up @@ -253,6 +256,12 @@ bool SqliteStorage::open()
query = INDEX_ATTENDEE;
sqlite3_exec(d->mDatabase);

query = INDEX_CALENDARPROPERTIES;
sqlite3_exec(d->mDatabase);

query = "PRAGMA foreign_keys = ON";
sqlite3_exec(d->mDatabase);

if (!d->mChanged.open(QIODevice::Append)) {
qCWarning(lcMkcal) << "cannot open changed file for" << d->mDatabaseName;
goto error;
Expand Down
10 changes: 10 additions & 0 deletions src/sqlitestorage.h
Expand Up @@ -511,6 +511,8 @@ public Q_SLOTS:
"CREATE TABLE IF NOT EXISTS Alarm(ComponentId INTEGER, Action INTEGER, Repeat INTEGER, Duration INTEGER, Offset INTEGER, Relation TEXT, DateTrigger INTEGER, DateTriggerLocal INTEGER, triggerTimeZone TEXT, Description TEXT, Attachment TEXT, Summary TEXT, Address TEXT, CustomProperties TEXT, isEnabled INTEGER)"
#define CREATE_ATTENDEE \
"CREATE TABLE IF NOT EXISTS Attendee(ComponentId INTEGER, Email TEXT, Name TEXT, IsOrganizer INTEGER, Role INTEGER, PartStat INTEGER, Rsvp INTEGER, DelegatedTo TEXT, DelegatedFrom TEXT)"
#define CREATE_CALENDARPROPERTIES \
"CREATE TABLE IF NOT EXISTS Calendarproperties(CalendarId REFERENCES Calendars(CalendarId) ON DELETE CASCADE, Name TEXT NOT NULL, Value TEXT, UNIQUE (CalendarId, Name))"

#define INDEX_CALENDAR \
"CREATE INDEX IF NOT EXISTS IDX_CALENDAR on Calendars(CalendarId)"
Expand All @@ -532,6 +534,8 @@ public Q_SLOTS:
"CREATE INDEX IF NOT EXISTS IDX_ALARM on Alarm(ComponentId)"
#define INDEX_ATTENDEE \
"CREATE UNIQUE INDEX IF NOT EXISTS IDX_ATTENDEE on Attendee(ComponentId, Email)"
#define INDEX_CALENDARPROPERTIES \
"CREATE INDEX IF NOT EXISTS IDX_CALENDARPROPERTIES on Calendarproperties(CalendarId)"

#define INSERT_VERSION \
"insert into Version values (?, ?)"
Expand All @@ -545,6 +549,8 @@ public Q_SLOTS:
"insert into Components values (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, '', '', 0)"
#define INSERT_CUSTOMPROPERTIES \
"insert into Customproperties values (?, ?, ?, ?)"
#define INSERT_CALENDARPROPERTIES \
"insert into Calendarproperties values (?, ?, ?)"
#define INSERT_RDATES \
"insert into Rdates values (?, ?, ?, ?, ?)"
#define INSERT_RECURSIVE \
Expand Down Expand Up @@ -574,6 +580,8 @@ public Q_SLOTS:
"delete from Rdates where ComponentId=?"
#define DELETE_CUSTOMPROPERTIES \
"delete from Customproperties where ComponentId=?"
#define DELETE_CALENDARPROPERTIES \
"delete from Calendarproperties where CalendarId=?"
#define DELETE_RECURSIVE \
"delete from Recursive where ComponentId=?"
#define DELETE_ALARM \
Expand Down Expand Up @@ -659,6 +667,8 @@ public Q_SLOTS:
"select * from Alarm where ComponentId=?"
#define SELECT_ATTENDEE_BY_ID \
"select * from Attendee where ComponentId=?"
#define SELECT_CALENDARPROPERTIES_BY_ID \
"select * from Calendarproperties where CalendarId=?"
#define SELECT_COMPONENTS_BY_DUPLICATE \
"select * from Components where DateStart=? and Summary=? and DateDeleted=0"
#define SELECT_COMPONENTS_BY_DUPLICATE_AND_NOTEBOOK \
Expand Down
2 changes: 1 addition & 1 deletion tests/tests.pro
Expand Up @@ -8,7 +8,7 @@ INCLUDEPATH += ../src
QMAKE_LIBDIR += ../src

LIBS += -lmkcal-qt5
PKGCONFIG += libkcalcoren-qt5
PKGCONFIG += libkcalcoren-qt5 sqlite3

HEADERS += \
tst_storage.h
Expand Down

0 comments on commit a86b0ab

Please sign in to comment.