diff --git a/.gitignore b/.gitignore index 0d58ded..20cb567 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,3 @@ -# Ignore the following files *~ -*.[oa] -*.diff -*.kate-swp -*.kdev4 -.kdev_include_paths -*.kdevelop.pcs -*.moc -*.moc.cpp -*.orig -*.user -.*.swp -.swp.* -Doxyfile -Makefile -avail -random_seed -/build*/ -CMakeLists.txt.user* -*.unc-backup* +*.kdev* + diff --git a/src/personmanager.cpp b/src/personmanager.cpp index 7a5c785..97bd66f 100644 --- a/src/personmanager.cpp +++ b/src/personmanager.cpp @@ -1,293 +1,293 @@ /* Copyright (C) 2013 David Edmundson Copyright 2013 Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "personmanager_p.h" #include #include "kpeople_debug.h" #include #include #include #include #include class Transaction { public: Transaction(const QSqlDatabase &db); void cancel(); ~Transaction(); Transaction(const Transaction &) = delete; Transaction& operator=(const Transaction &) = delete; private: QSqlDatabase m_db; bool m_cancelled = false; }; Transaction::Transaction(const QSqlDatabase &db) : m_db(db), m_cancelled(false) { m_db.transaction(); } void Transaction::cancel() { m_db.rollback(); m_cancelled = true; } Transaction::~Transaction() { if (!m_cancelled) { m_db.commit(); } } PersonManager::PersonManager(const QString &databasePath, QObject *parent): QObject(parent), m_db(QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"), QStringLiteral("kpeoplePersonsManager"))) { m_db.setDatabaseName(databasePath); if (!m_db.open()) { qCWarning(KPEOPLE_LOG) << "Couldn't open the database at" << databasePath; } m_db.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS persons (contactID VARCHAR UNIQUE NOT NULL, personID INT NOT NULL)")); m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS contactIdIndex ON persons (contactId)")); m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS personIdIndex ON persons (personId)")); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson"), this, SIGNAL(contactAddedToPerson(QString,QString))); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson"), this, SIGNAL(contactRemovedFromPerson(QString))); } PersonManager::~PersonManager() { } QMultiHash< QString, QString > PersonManager::allPersons() const { QMultiHash contactMapping; QSqlQuery query = m_db.exec(QStringLiteral("SELECT personID, contactID FROM persons")); while (query.next()) { const QString personUri = QLatin1String("kpeople://") + query.value(0).toString(); // we store as ints internally, convert it to a string here const QString contactID = query.value(1).toString(); contactMapping.insertMulti(personUri, contactID); } return contactMapping; } QStringList PersonManager::contactsForPersonUri(const QString &personUri) const { if (!personUri.startsWith(QLatin1String("kpeople://"))) { return QStringList(); } QStringList contactUris; //TODO port to the proper qsql method for args QSqlQuery query(m_db); query.prepare(QStringLiteral("SELECT contactID FROM persons WHERE personId = ?")); query.bindValue(0, personUri.mid(strlen("kpeople://"))); query.exec(); while (query.next()) { contactUris << query.value(0).toString(); } return contactUris; } QString PersonManager::personUriForContact(const QString &contactUri) const { QSqlQuery query(m_db); query.prepare(QStringLiteral("SELECT personId FROM persons WHERE contactId = ?")); query.bindValue(0, contactUri); query.exec(); if (query.next()) { return QLatin1String("kpeople://") + query.value(0).toString(); } return QString(); } QString PersonManager::mergeContacts(const QStringList &ids) { // no merging if we have only 0 || 1 ids if (ids.size() < 2) { return QString(); } QStringList metacontacts; QStringList contacts; bool rc = true; QList pendingMessages; // separate the passed ids to metacontacts and simple contacts - for (const QString &id : ids) { + Q_FOREACH (const QString &id, ids) { if (id.startsWith(QLatin1String("kpeople://"))) { metacontacts << id; } else { contacts << id; } } // create new personUriString // - if we're merging two simple contacts, create completely new id // - if we're merging an existing metacontact, take the first id and use it QString personUriString; if (metacontacts.count() == 0) { // query for the highest existing ID in the database and +1 it int personUri = 0; QSqlQuery query = m_db.exec(QStringLiteral("SELECT MAX(personID) FROM persons")); if (query.next()) { personUri = query.value(0).toInt(); personUri++; } personUriString = QLatin1String("kpeople://") + QString::number(personUri); } else { personUriString = metacontacts.first(); } // start a db transaction (ends automatically on destruction) Transaction t(m_db); // processed passed metacontacts if (metacontacts.count() > 1) { // collect all the contacts from other persons QStringList personContacts; - for (const QString &id : qAsConst(metacontacts)) { + Q_FOREACH (const QString &id, metacontacts) { if (id != personUriString) { personContacts << contactsForPersonUri(id); } } // iterate over all of the contacts and change their personID to the new personUriString - for (const QString &id : qAsConst(personContacts)) { + Q_FOREACH (const QString &id, personContacts) { QSqlQuery updateQuery(m_db); updateQuery.prepare(QStringLiteral("UPDATE persons SET personID = ? WHERE contactID = ?")); updateQuery.bindValue(0, personUriString.mid(strlen("kpeople://"))); updateQuery.bindValue(1, id); if (!updateQuery.exec()) { rc = false; } QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson")); message.setArguments(QVariantList() << id); pendingMessages << message; message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson")); message.setArguments(QVariantList() << id << personUriString); pendingMessages << message; } } // process passed contacts if (!contacts.isEmpty()) { - for (const QString &id : qAsConst(contacts)) { + Q_FOREACH (const QString &id, contacts) { QSqlQuery insertQuery(m_db); insertQuery.prepare(QStringLiteral("INSERT INTO persons VALUES (?, ?)")); insertQuery.bindValue(0, id); insertQuery.bindValue(1, personUriString.mid(strlen("kpeople://"))); //strip kpeople:// if (!insertQuery.exec()) { rc = false; } //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactAddedToPerson")); message.setArguments(QVariantList() << id << personUriString); pendingMessages << message; } } //if success send all messages to other clients //otherwise roll back our database changes and return an empty string if (rc) { - for (const QDBusMessage &message : qAsConst(pendingMessages)) { + Q_FOREACH (const QDBusMessage &message, pendingMessages) { QDBusConnection::sessionBus().send(message); } } else { t.cancel(); personUriString.clear(); } return personUriString; } bool PersonManager::unmergeContact(const QString &id) { //remove rows from DB if (id.startsWith(QLatin1String("kpeople://"))) { QSqlQuery query(m_db); const QStringList contactUris = contactsForPersonUri(id); query.prepare(QStringLiteral("DELETE FROM persons WHERE personId = ?")); query.bindValue(0, id.mid(strlen("kpeople://"))); query.exec(); - for (const QString &contactUri : contactUris) { + Q_FOREACH (const QString &contactUri, contactUris) { //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"), QStringLiteral("ContactRemovedFromPerson")); message.setArguments(QVariantList() << contactUri); QDBusConnection::sessionBus().send(message); } } else { QSqlQuery query(m_db); query.prepare(QStringLiteral("DELETE FROM persons WHERE contactId = ?")); query.bindValue(0, id); query.exec(); //emit signal(dbus) Q_EMIT contactRemovedFromPerson(id); } //TODO return if removing rows worked return true; } PersonManager *PersonManager::instance(const QString &databasePath) { static PersonManager *s_instance = nullptr; if (!s_instance) { QString path = databasePath; if (path.isEmpty()) { path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kpeople/"); QDir().mkpath(path); path += QLatin1String("persondb"); } s_instance = new PersonManager(path); } return s_instance; }