• Skip to content
  • Skip to link menu
KDE API Documentation - personmanager.cpp Source File (KPeople)
  • KDE Home
  • Contact Us
 

KPeople

  • frameworks
  • kpeople
  • src
personmanager.cpp
1 /*
2  Copyright (C) 2013 David Edmundson <davidedmundson@kde.org>
3  Copyright 2013 Martin Klapetek <mklapetek@kde.org>
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #include "personmanager_p.h"
21 
22 // #include "personmanager.moc"
23 #include <QVariant>
24 #include <QDebug>
25 #include <QStandardPaths>
26 #include <QDir>
27 #include <QSqlError>
28 #include <QDBusConnection>
29 #include <QDBusMessage>
30 
31 class Transaction
32 {
33 public:
34  Transaction(const QSqlDatabase &db);
35  void cancel();
36  ~Transaction();
37 private:
38  QSqlDatabase m_db;
39  bool m_cancelled;
40 };
41 
42 Transaction::Transaction(const QSqlDatabase &db) :
43  m_db(db),
44  m_cancelled(false)
45 {
46  m_db.transaction();
47 }
48 
49 void Transaction::cancel()
50 {
51  m_db.rollback();
52  m_cancelled = true;
53 }
54 
55 Transaction::~Transaction()
56 {
57  if (!m_cancelled) {
58  m_db.commit();
59  }
60 }
61 
62 PersonManager::PersonManager(const QString &databasePath, QObject *parent):
63  QObject(parent),
64  m_db(QSqlDatabase::addDatabase(QStringLiteral("QSQLITE")))
65 {
66  m_db.setDatabaseName(databasePath);
67  if (!m_db.open()) {
68  qWarning() << "Couldn't open the database at" << databasePath;
69  }
70  m_db.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS persons (contactID VARCHAR UNIQUE NOT NULL, personID INT NOT NULL)"));
71  m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS contactIdIndex ON persons (contactId)"));
72  m_db.exec(QStringLiteral("CREATE INDEX IF NOT EXISTS personIdIndex ON persons (personId)"));
73 
74  QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"),
75  QStringLiteral("ContactAddedToPerson"), this, SIGNAL(contactAddedToPerson(QString,QString)));
76  QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KPeople"), QStringLiteral("org.kde.KPeople"),
77  QStringLiteral("ContactRemovedFromPerson"), this, SIGNAL(contactRemovedFromPerson(QString)));
78 }
79 
80 PersonManager::~PersonManager()
81 {
82 
83 }
84 
85 QMultiHash< QString, QString > PersonManager::allPersons() const
86 {
87  QMultiHash<QString /*PersonID*/, QString /*ContactID*/> contactMapping;
88 
89  QSqlQuery query = m_db.exec(QStringLiteral("SELECT personID, contactID FROM persons"));
90  while (query.next()) {
91  const QString personUri = QLatin1String("kpeople://") + query.value(0).toString(); // we store as ints internally, convert it to a string here
92  const QString contactID = query.value(1).toString();
93  contactMapping.insertMulti(personUri, contactID);
94  }
95  return contactMapping;
96 }
97 
98 QStringList PersonManager::contactsForPersonUri(const QString &personUri) const
99 {
100  if (!personUri.startsWith(QLatin1String("kpeople://"))) {
101  return QStringList();
102  }
103 
104  QStringList contactUris;
105  //TODO port to the proper qsql method for args
106  QSqlQuery query(m_db);
107  query.prepare(QStringLiteral("SELECT contactID FROM persons WHERE personId = ?"));
108  query.bindValue(0, personUri.mid(strlen("kpeople://")));
109  query.exec();
110 
111  while (query.next()) {
112  contactUris << query.value(0).toString();
113  }
114  return contactUris;
115 }
116 
117 QString PersonManager::personUriForContact(const QString &contactUri) const
118 {
119  QSqlQuery query(m_db);
120  query.prepare(QStringLiteral("SELECT personId FROM persons WHERE contactId = ?"));
121  query.bindValue(0, contactUri);
122  query.exec();
123  if (query.next()) {
124  return QLatin1String("kpeople://") + query.value(0).toString();
125  }
126  return QString();
127 }
128 
129 QString PersonManager::mergeContacts(const QStringList &ids)
130 {
131  // no merging if we have only 0 || 1 ids
132  if (ids.size() < 2) {
133  return QString();
134  }
135 
136  QStringList metacontacts;
137  QStringList contacts;
138 
139  bool rc = true;
140 
141  QList<QDBusMessage> pendingMessages;
142 
143  // separate the passed ids to metacontacts and simple contacts
144  Q_FOREACH (const QString &id, ids) {
145  if (id.startsWith(QLatin1String("kpeople://"))) {
146  metacontacts << id;
147  } else {
148  contacts << id;
149  }
150  }
151 
152  // create new personUriString
153  // - if we're merging two simple contacts, create completely new id
154  // - if we're merging an existing metacontact, take the first id and use it
155  QString personUriString;
156  if (metacontacts.count() == 0) {
157  // query for the highest existing ID in the database and +1 it
158  int personUri = 0;
159  QSqlQuery query = m_db.exec(QStringLiteral("SELECT MAX(personID) FROM persons"));
160  if (query.next()) {
161  personUri = query.value(0).toInt();
162  personUri++;
163  }
164 
165  personUriString = QLatin1String("kpeople://") + QString::number(personUri);
166  } else {
167  personUriString = metacontacts.first();
168  }
169 
170  // start a db transaction (ends automatically on destruction)
171  Transaction t(m_db);
172 
173  // processed passed metacontacts
174  if (metacontacts.count() > 1) {
175  // collect all the contacts from other persons
176  QStringList personContacts;
177  Q_FOREACH (const QString &id, metacontacts) {
178  if (id != personUriString) {
179  personContacts << contactsForPersonUri(id);
180  }
181  }
182 
183  // iterate over all of the contacts and change their personID to the new personUriString
184  Q_FOREACH (const QString &id, personContacts) {
185  QSqlQuery updateQuery(m_db);
186  updateQuery.prepare(QStringLiteral("UPDATE persons SET personID = ? WHERE contactID = ?"));
187  updateQuery.bindValue(0, personUriString.mid(strlen("kpeople://")));
188  updateQuery.bindValue(1, id);
189  if (!updateQuery.exec()) {
190  rc = false;
191  }
192 
193  QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/KPeople"),
194  QLatin1String("org.kde.KPeople"),
195  QLatin1String("ContactRemovedFromPerson"));
196 
197  message.setArguments(QVariantList() << id);
198  pendingMessages << message;
199 
200  message = QDBusMessage::createSignal(QLatin1String("/KPeople"),
201  QLatin1String("org.kde.KPeople"),
202  QLatin1String("ContactAddedToPerson"));
203 
204  message.setArguments(QVariantList() << id << personUriString);
205 
206  }
207  }
208 
209  // process passed contacts
210  if (contacts.size() > 0) {
211 
212  Q_FOREACH (const QString &id, contacts) {
213  QSqlQuery insertQuery(m_db);
214  insertQuery.prepare(QStringLiteral("INSERT INTO persons VALUES (?, ?)"));
215  insertQuery.bindValue(0, id);
216  insertQuery.bindValue(1, personUriString.mid(strlen("kpeople://"))); //strip kpeople://
217  if (!insertQuery.exec()) {
218  rc = false;
219  }
220 
221  //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex
222  QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/KPeople"),
223  QLatin1String("org.kde.KPeople"),
224  QLatin1String("ContactAddedToPerson"));
225 
226  message.setArguments(QVariantList() << id << personUriString);
227  pendingMessages << message;
228  }
229  }
230 
231  //if success send all messages to other clients
232  //otherwise roll back our database changes and return an empty string
233  if (rc) {
234  Q_FOREACH (const QDBusMessage &message, pendingMessages) {
235  QDBusConnection::sessionBus().send(message);
236  }
237  } else {
238  t.cancel();
239  personUriString.clear();
240  }
241 
242  return personUriString;
243 }
244 
245 bool PersonManager::unmergeContact(const QString &id)
246 {
247  //remove rows from DB
248  if (id.startsWith(QLatin1String("kpeople://"))) {
249  QSqlQuery query(m_db);
250 
251  const QStringList contactUris = contactsForPersonUri(id);
252  query.prepare(QStringLiteral("DELETE FROM persons WHERE personId = ?"));
253  query.bindValue(0, id.mid(strlen("kpeople://")));
254  query.exec();
255 
256  Q_FOREACH (const QString &contactUri, contactUris) {
257  //FUTURE OPTIMIZATION - this would be best as one signal, but arguments become complex
258  QDBusMessage message = QDBusMessage::createSignal(QLatin1String("/KPeople"),
259  QLatin1String("org.kde.KPeople"),
260  QLatin1String("ContactRemovedFromPerson"));
261 
262  message.setArguments(QVariantList() << contactUri);
263  QDBusConnection::sessionBus().send(message);
264  }
265  } else {
266  QSqlQuery query(m_db);
267  query.prepare(QStringLiteral("DELETE FROM persons WHERE contactId = ?"));
268  query.bindValue(0, id);
269  query.exec();
270  //emit signal(dbus)
271  Q_EMIT contactRemovedFromPerson(id);
272  }
273 
274  //TODO return if removing rows worked
275  return true;
276 }
277 
278 PersonManager *PersonManager::instance(const QString &databasePath)
279 {
280  static PersonManager *s_instance = 0;
281  if (!s_instance) {
282  QString path = databasePath;
283  if (path.isEmpty()) {
284  path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/kpeople/");
285 
286  QDir().mkpath(path);
287  path += QLatin1String("persondb");
288  }
289  s_instance = new PersonManager(path);
290  }
291  return s_instance;
292 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2015 The KDE developers.
Generated on Fri Feb 13 2015 15:16:39 by doxygen 1.8.9.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KPeople

Skip menu "KPeople"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • File List

Class Picker

Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal