blob: ea6b3c2705f7875d89c5c76b22041e1795ddfa0c [file] [log] [blame]
Edric Milaret67007d12015-05-07 09:40:09 -04001/***************************************************************************
2 * Copyright (C) 2015 by Savoir-Faire Linux *
3 * Author: Edric Ladent Milaret <edric.ladent-milaret@savoirfairelinux.com>*
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 3 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program 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 *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
19#include "windowscontactbackend.h"
20
Edric Milaret43f3c1e2015-07-16 17:52:47 -040021#include <QtXml>
22
23#include "personmodel.h"
24#include "categorizedcontactmodel.h"
25
26#include "utils.h"
27
Edric Milaret67007d12015-05-07 09:40:09 -040028WindowsContactEditor::WindowsContactEditor(CollectionMediator<Person> *m
29 , WindowsContactBackend *parent)
30 : CollectionEditor<Person>(m),collection_(parent)
31{
32
33}
34
35WindowsContactEditor::~WindowsContactEditor()
36{
37
38}
39
40bool
41WindowsContactEditor::save(const Person *item)
42{
Edric Milaret43f3c1e2015-07-16 17:52:47 -040043 QFile file(QStandardPaths::writableLocation
44 (QStandardPaths::HomeLocation) + "/Contacts/"
45 + item->formattedName() + ".contact");
46 if (!file.open(QIODevice::ReadWrite)) {
47 file.close();
48 qDebug() << "Cannot open contact file";
49 return false;
50 }
51
52 QDomDocument doc;
53 doc.setContent(&file);
54
55 auto root = doc.elementsByTagName("c:contact").at(0);
56 auto nodes = doc.elementsByTagName("c:PhoneNumberCollection");
57
58 //if PhoneNumberCollection already exists
59 QVector<QString> nodeNumberVector;
60 if (nodes.length()) {
61 auto phoneNumberCollection = nodes.at(0);
62 auto phoneNumbers = doc.elementsByTagName("c:Number");
63 auto virtualPhoneNumber = item->phoneNumbers();
64 for (int i = 0; i < phoneNumbers.length(); i++) {
65 auto node = phoneNumbers.at(i).toElement();
66 nodeNumberVector.append(node.text());
67 }
68 for (auto elem : virtualPhoneNumber) {
69 if (not nodeNumberVector.contains(elem->uri())) {
70 auto phoneNumber = doc.createElement("c:PhoneNumber");
71 phoneNumberCollection.appendChild(phoneNumber);
72 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
73 auto numberNode = doc.createElement("c:Number");
74 phoneNumber.appendChild(numberNode);
75 auto numberValue = doc.createTextNode(elem->uri());
76 numberNode.appendChild(numberValue);
77 }
78 }
79 } else {
80 auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
81 root.appendChild(phoneNumberCollection);
82 auto phoneNumber = doc.createElement("c:PhoneNumber");
83 phoneNumberCollection.appendChild(phoneNumber);
84 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
85 auto numberNode = doc.createElement("c:Number");
86 phoneNumber.appendChild(numberNode);
87 auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
88 numberNode.appendChild(numberValue);
89 }
90 file.resize(0);
91 file.write(doc.toByteArray());
92 file.close();
93 return true;
Edric Milaret67007d12015-05-07 09:40:09 -040094}
95
96bool
97WindowsContactEditor::remove(const Person *item)
98{
Edric Milaret43f3c1e2015-07-16 17:52:47 -040099 items_.removeOne(const_cast<Person*>(item));
100 mediator()->removeItem(item);
101 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400102}
103
104bool
105WindowsContactEditor::edit(Person *item)
106{
107 Q_UNUSED(item)
108 return false;
109}
110
111bool
Edric Milaret16f997d2015-08-04 13:46:22 -0400112WindowsContactEditor::addNew(Person *item)
Edric Milaret67007d12015-05-07 09:40:09 -0400113{
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400114 QDomDocument doc;
115 QFile file(QStandardPaths::writableLocation
116 (QStandardPaths::HomeLocation) + "/Contacts/"
117 + item->formattedName()+".contact");
118 if (file.exists())
119 return false;
120 if (!file.open(QIODevice::ReadWrite)) {
121 file.close();
122 qDebug() << "Cannot create contact file";
123 return false;
124 }
125 doc.appendChild(
126 doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
127
128 //Create root
129 auto root = doc.createElement("c:contact");
130 root.setAttribute("c:Version", "1");
131 root.setAttribute("xmlns:c", "http://schemas.microsoft.com/Contact");
132 root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
133 root.setAttribute("xmlns:MSP2P","http://schemas.microsoft.com/Contact/Extended/MSP2P");
134 doc.appendChild(root);
135
136 auto date = Utils::GetISODate();
137
138 //Create creation date
139 auto creationDateNode = doc.createElement("c:CreationDate");
140 auto creationDateValue = doc.createTextNode(date);
141 creationDateNode.appendChild(creationDateValue);
142 root.appendChild(creationDateNode);
143
144 //Create extended node
145 auto extendedNode = doc.createElement("c:Extended");
146 extendedNode.setAttribute("xsi:nil", "true");
147 root.appendChild(extendedNode);
148
149 //Create contactID collection
150 auto contactIDCol = doc.createElement("c:ContactIDCollection");
151 root.appendChild(contactIDCol);
152 auto contactID = doc.createElement("c:ContactID");
153 contactID.setAttribute("c:ElementID", Utils::GenGUID());
154 auto contactValue = doc.createElement("c:Value");
155 auto value = doc.createTextNode(item->uid());
156 contactValue.appendChild(value);
157 contactID.appendChild(contactValue);
158 contactIDCol.appendChild(contactID);
159
160 //Create NameCollection
161 auto nameCollection = doc.createElement("c:NameCollection");
162 root.appendChild(nameCollection);
163 auto name = doc.createElement("c:Name");
164 nameCollection.appendChild(name);
165 name.setAttribute("c:ElementID", Utils::GenGUID());
166 auto formattedName = doc.createElement("c:FormattedName");
167 name.appendChild(formattedName);
168 auto formattedNameValue = doc.createTextNode(item->formattedName());
169 formattedName.appendChild(formattedNameValue);
170
171 //Create PhoneNumberCollection
172 auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
173 root.appendChild(phoneNumberCollection);
174 auto phoneNumber = doc.createElement("c:PhoneNumber");
175 phoneNumberCollection.appendChild(phoneNumber);
176 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
177 auto numberNode = doc.createElement("c:Number");
178 phoneNumber.appendChild(numberNode);
179 auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
180 numberNode.appendChild(numberValue);
181
182 //Write to file
183 file.write(doc.toByteArray());
184 file.close();
185
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400186 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400187}
188
189bool
190WindowsContactEditor::addExisting(const Person *item)
191{
192 items_ << const_cast<Person*>(item);
193 mediator()->addItem(item);
194 return true;
195}
196
197QVector<Person*>
198WindowsContactEditor::items() const
199{
200 return items_;
201}
202
203WindowsContactBackend::WindowsContactBackend(CollectionMediator<Person>* mediator,
204 CollectionInterface* parent)
205 : CollectionInterface(new WindowsContactEditor(mediator,this), parent)
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400206 , mediator_(mediator), watcher_(new QFileSystemWatcher())
Edric Milaret67007d12015-05-07 09:40:09 -0400207{
208
209}
210
211WindowsContactBackend::~WindowsContactBackend()
212{
213
214}
215
216bool
217WindowsContactBackend::load()
218{
219 QtConcurrent::run(this, &WindowsContactBackend::loadRun);
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400220 watcher_->addPath(QStandardPaths::writableLocation
221 (QStandardPaths::HomeLocation) + "/Contacts");
Edric Milaret7cb55bb2015-08-10 11:24:13 -0400222
223 QObject::connect(watcher_, &QFileSystemWatcher::directoryChanged, [=](QString path) {
224 Q_UNUSED(path)
225 QtConcurrent::run(this, &WindowsContactBackend::loadRun);
226 });
Edric Milaret67007d12015-05-07 09:40:09 -0400227 return true;
228}
229
230bool
231WindowsContactBackend::loadRun()
232{
233 QDir contactDir(QStandardPaths::writableLocation
234 (QStandardPaths::HomeLocation) + "/Contacts");
235 QStringList filesList = contactDir.entryList(QStringList("*.contact"));
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400236 auto ret = true;
Edric Milaret67007d12015-05-07 09:40:09 -0400237 for(auto contactFileName : filesList) {
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400238 if (not getPersonFromContactFile(contactDir, contactFileName))
239 ret = false;
240 }
241 return ret;
242}
243
244bool
245WindowsContactBackend::getPersonFromContactFile(const QDir& contactDir,
246 const QString &contactFileName)
247{
248 QString contactFilePath
249 (contactDir.absolutePath() + "/" + contactFileName);
250 QFile contactFile(contactFilePath);
251 if (contactFile.open(QIODevice::ReadOnly)) {
252 QXmlStreamReader reader;
253 Person *p = new Person(this);
254 QVector<ContactMethod*> contactMethod;
255 reader.setDevice(&contactFile);
256 while (!reader.atEnd()) {
257 reader.readNext();
258 if (reader.isStartElement()) {
259 QString name = reader.name().toString();
260 if (name == "FormattedName")
261 p->setFormattedName(reader.readElementText());
262 else if (name == "NickName")
263 p->setNickName(reader.readElementText());
264 else if (name == "GivenName")
265 p->setFirstName(reader.readElementText());
266 else if (name == "FamilyName")
267 p->setFamilyName(reader.readElementText());
268 else if (name == "Company")
269 p->setOrganization(reader.readElementText());
270 else if (name == "Department")
271 p->setDepartment(reader.readElementText());
272 else if (name == "Number") {
273 QString number = reader.readElementText();
274 if (not number.isEmpty()) {
275 ContactMethod *contact =
276 PhoneDirectoryModel::instance()->getNumber(number,p);
277 contactMethod.append(contact);
Edric Milaret67007d12015-05-07 09:40:09 -0400278 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400279 } else if (name == "ContactID") {
280 while (reader.name().toString() != "Value")
Edric Milaret67007d12015-05-07 09:40:09 -0400281 reader.readNext();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400282 p->setUid(reader.readElementText().toUtf8());
283 }
284 else if (name == "Photo") {
285 //FIXME: It seems to be possible to have multiple photo...
286 reader.readNext();
287 if (reader.name().toString() == "Url") {
288 QString photoValue = reader.readElementText();
289 QImage photo;
290 photo.load(photoValue);
291 p->setPhoto(photo.scaled(sizePhoto_,sizePhoto_, Qt::KeepAspectRatio,
292 Qt::SmoothTransformation));
Edric Milaret67007d12015-05-07 09:40:09 -0400293 }
294 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400295 else if (name == "EmailAddress") {
296 QString address;
297 bool isPreferred = false;
298 reader.readNext();
299 while (reader.name().toString() != "EmailAddress"
300 && !reader.atEnd()) {
301 if (reader.isStartElement()) {
302 QString tag = reader.name().toString();
303 if (tag == "Address")
304 address = reader.readElementText();
305 else if (tag == "Label")
306 if (reader.readElementText() == "Preferred")
307 isPreferred = true;
308 }
309 reader.readNext();
310 }
311 if (isPreferred)
312 p->setPreferredEmail(address);
313 }
Edric Milaret67007d12015-05-07 09:40:09 -0400314 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400315 }
316 if (reader.hasError()) {
317 qDebug() << reader.errorString();
318 contactFile.close();
319 return false;
320 } else {
321 Person* existing = PersonModel::instance()->getPersonByUid(p->uid());
322 if (existing) {
Edric Milaret67007d12015-05-07 09:40:09 -0400323 if (contactMethod.size() > 0)
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400324 existing->setContactMethods ( contactMethod );
325 existing->setNickName ( p->nickName() );
326 existing->setFirstName ( p->firstName() );
327 existing->setFamilyName ( p->secondName() );
328 existing->setFormattedName ( p->formattedName() );
329 existing->setOrganization ( p->organization() );
330 existing->setPreferredEmail ( p->preferredEmail() );
331 existing->setGroup ( p->group() );
332 existing->setDepartment ( p->department() );
333 existing->setPhoto ( p->photo() );
334 delete p;
335 } else {
336 p->setContactMethods(contactMethod);
Edric Milaret67007d12015-05-07 09:40:09 -0400337 editor<Person>()->addExisting(p);
338 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400339 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400340 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400341 } else {
342 qDebug() << "Error Opening contact file : " << contactFileName;
343 return false;
Edric Milaret67007d12015-05-07 09:40:09 -0400344 }
Edric Milaret67007d12015-05-07 09:40:09 -0400345}
346
347bool
348WindowsContactBackend::reload()
349{
350 return false;
351}
352
353bool
354WindowsContactBackend::clear()
355{
356 return false;
357}
358
359QString
360WindowsContactBackend::name() const
361{
362 return "Windows Contact Backend";
363}
364
365QString
366WindowsContactBackend::category() const
367{
368 return "Contacts";
369}
370
371bool
372WindowsContactBackend::isEnabled() const
373{
374 return true;
375}
376
377QByteArray
378WindowsContactBackend::id() const
379{
380 return "wincb";
381}
382
383FlagPack<CollectionInterface::SupportedFeatures> WindowsContactBackend::supportedFeatures() const
384{
385 return (
386 CollectionInterface::SupportedFeatures::NONE |
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400387 CollectionInterface::SupportedFeatures::LOAD |
388 CollectionInterface::SupportedFeatures::SAVE |
389 CollectionInterface::SupportedFeatures::REMOVE |
390 CollectionInterface::SupportedFeatures::ADD);
Edric Milaret67007d12015-05-07 09:40:09 -0400391}
392