blob: 60ca74495bc22eff241e959fd5ccf26ea3cefa8d [file] [log] [blame]
Edric Milaret67007d12015-05-07 09:40:09 -04001/***************************************************************************
Edric Milaretbab169d2016-01-07 15:13:33 -05002 * Copyright (C) 2015-2016 by Savoir-faire Linux *
Edric Milaret67007d12015-05-07 09:40:09 -04003 * 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>
Edric Milareta13b48c2016-01-18 14:42:35 -050022#include <QPainter>
Edric Milaret43f3c1e2015-07-16 17:52:47 -040023
24#include "personmodel.h"
25#include "categorizedcontactmodel.h"
26
27#include "utils.h"
28
Edric Milaret67007d12015-05-07 09:40:09 -040029WindowsContactEditor::WindowsContactEditor(CollectionMediator<Person> *m
30 , WindowsContactBackend *parent)
31 : CollectionEditor<Person>(m),collection_(parent)
32{
33
34}
35
36WindowsContactEditor::~WindowsContactEditor()
37{
38
39}
40
41bool
42WindowsContactEditor::save(const Person *item)
43{
Edric Milaret43f3c1e2015-07-16 17:52:47 -040044 QFile file(QStandardPaths::writableLocation
45 (QStandardPaths::HomeLocation) + "/Contacts/"
46 + item->formattedName() + ".contact");
47 if (!file.open(QIODevice::ReadWrite)) {
48 file.close();
49 qDebug() << "Cannot open contact file";
50 return false;
51 }
52
53 QDomDocument doc;
54 doc.setContent(&file);
55
56 auto root = doc.elementsByTagName("c:contact").at(0);
57 auto nodes = doc.elementsByTagName("c:PhoneNumberCollection");
58
59 //if PhoneNumberCollection already exists
60 QVector<QString> nodeNumberVector;
61 if (nodes.length()) {
62 auto phoneNumberCollection = nodes.at(0);
63 auto phoneNumbers = doc.elementsByTagName("c:Number");
64 auto virtualPhoneNumber = item->phoneNumbers();
65 for (int i = 0; i < phoneNumbers.length(); i++) {
66 auto node = phoneNumbers.at(i).toElement();
67 nodeNumberVector.append(node.text());
68 }
69 for (auto elem : virtualPhoneNumber) {
70 if (not nodeNumberVector.contains(elem->uri())) {
71 auto phoneNumber = doc.createElement("c:PhoneNumber");
72 phoneNumberCollection.appendChild(phoneNumber);
73 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
74 auto numberNode = doc.createElement("c:Number");
75 phoneNumber.appendChild(numberNode);
76 auto numberValue = doc.createTextNode(elem->uri());
77 numberNode.appendChild(numberValue);
78 }
79 }
80 } else {
81 auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
82 root.appendChild(phoneNumberCollection);
83 auto phoneNumber = doc.createElement("c:PhoneNumber");
84 phoneNumberCollection.appendChild(phoneNumber);
85 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
86 auto numberNode = doc.createElement("c:Number");
87 phoneNumber.appendChild(numberNode);
88 auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
89 numberNode.appendChild(numberValue);
90 }
91 file.resize(0);
92 file.write(doc.toByteArray());
93 file.close();
94 return true;
Edric Milaret67007d12015-05-07 09:40:09 -040095}
96
97bool
98WindowsContactEditor::remove(const Person *item)
99{
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400100 items_.removeOne(const_cast<Person*>(item));
101 mediator()->removeItem(item);
102 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400103}
104
105bool
106WindowsContactEditor::edit(Person *item)
107{
108 Q_UNUSED(item)
109 return false;
110}
111
112bool
Edric Milaret16f997d2015-08-04 13:46:22 -0400113WindowsContactEditor::addNew(Person *item)
Edric Milaret67007d12015-05-07 09:40:09 -0400114{
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400115 QDomDocument doc;
116 QFile file(QStandardPaths::writableLocation
117 (QStandardPaths::HomeLocation) + "/Contacts/"
118 + item->formattedName()+".contact");
119 if (file.exists())
120 return false;
121 if (!file.open(QIODevice::ReadWrite)) {
122 file.close();
123 qDebug() << "Cannot create contact file";
124 return false;
125 }
126 doc.appendChild(
127 doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""));
128
129 //Create root
130 auto root = doc.createElement("c:contact");
131 root.setAttribute("c:Version", "1");
132 root.setAttribute("xmlns:c", "http://schemas.microsoft.com/Contact");
133 root.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
134 root.setAttribute("xmlns:MSP2P","http://schemas.microsoft.com/Contact/Extended/MSP2P");
135 doc.appendChild(root);
136
137 auto date = Utils::GetISODate();
138
139 //Create creation date
140 auto creationDateNode = doc.createElement("c:CreationDate");
141 auto creationDateValue = doc.createTextNode(date);
142 creationDateNode.appendChild(creationDateValue);
143 root.appendChild(creationDateNode);
144
145 //Create extended node
146 auto extendedNode = doc.createElement("c:Extended");
147 extendedNode.setAttribute("xsi:nil", "true");
148 root.appendChild(extendedNode);
149
150 //Create contactID collection
151 auto contactIDCol = doc.createElement("c:ContactIDCollection");
152 root.appendChild(contactIDCol);
153 auto contactID = doc.createElement("c:ContactID");
154 contactID.setAttribute("c:ElementID", Utils::GenGUID());
155 auto contactValue = doc.createElement("c:Value");
156 auto value = doc.createTextNode(item->uid());
157 contactValue.appendChild(value);
158 contactID.appendChild(contactValue);
159 contactIDCol.appendChild(contactID);
160
161 //Create NameCollection
162 auto nameCollection = doc.createElement("c:NameCollection");
163 root.appendChild(nameCollection);
164 auto name = doc.createElement("c:Name");
165 nameCollection.appendChild(name);
166 name.setAttribute("c:ElementID", Utils::GenGUID());
167 auto formattedName = doc.createElement("c:FormattedName");
168 name.appendChild(formattedName);
169 auto formattedNameValue = doc.createTextNode(item->formattedName());
170 formattedName.appendChild(formattedNameValue);
171
172 //Create PhoneNumberCollection
173 auto phoneNumberCollection = doc.createElement("c:PhoneNumberCollection");
174 root.appendChild(phoneNumberCollection);
175 auto phoneNumber = doc.createElement("c:PhoneNumber");
176 phoneNumberCollection.appendChild(phoneNumber);
177 phoneNumber.setAttribute("c:ElementID", Utils::GenGUID());
178 auto numberNode = doc.createElement("c:Number");
179 phoneNumber.appendChild(numberNode);
180 auto numberValue = doc.createTextNode(item->phoneNumbers().at(0)->uri());
181 numberNode.appendChild(numberValue);
182
183 //Write to file
184 file.write(doc.toByteArray());
185 file.close();
186
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400187 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400188}
189
190bool
191WindowsContactEditor::addExisting(const Person *item)
192{
193 items_ << const_cast<Person*>(item);
194 mediator()->addItem(item);
195 return true;
196}
197
198QVector<Person*>
199WindowsContactEditor::items() const
200{
201 return items_;
202}
203
204WindowsContactBackend::WindowsContactBackend(CollectionMediator<Person>* mediator,
205 CollectionInterface* parent)
206 : CollectionInterface(new WindowsContactEditor(mediator,this), parent)
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400207 , mediator_(mediator), watcher_(new QFileSystemWatcher())
Edric Milaret67007d12015-05-07 09:40:09 -0400208{
209
210}
211
212WindowsContactBackend::~WindowsContactBackend()
213{
Edric Milaret508f99a2016-03-11 16:58:43 -0500214 delete watcher_;
Edric Milaret67007d12015-05-07 09:40:09 -0400215}
216
217bool
218WindowsContactBackend::load()
219{
220 QtConcurrent::run(this, &WindowsContactBackend::loadRun);
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400221 watcher_->addPath(QStandardPaths::writableLocation
222 (QStandardPaths::HomeLocation) + "/Contacts");
Edric Milaret7cb55bb2015-08-10 11:24:13 -0400223
224 QObject::connect(watcher_, &QFileSystemWatcher::directoryChanged, [=](QString path) {
225 Q_UNUSED(path)
226 QtConcurrent::run(this, &WindowsContactBackend::loadRun);
227 });
Edric Milaret67007d12015-05-07 09:40:09 -0400228 return true;
229}
230
231bool
232WindowsContactBackend::loadRun()
233{
234 QDir contactDir(QStandardPaths::writableLocation
235 (QStandardPaths::HomeLocation) + "/Contacts");
236 QStringList filesList = contactDir.entryList(QStringList("*.contact"));
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400237 auto ret = true;
Edric Milaret67007d12015-05-07 09:40:09 -0400238 for(auto contactFileName : filesList) {
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400239 if (not getPersonFromContactFile(contactDir, contactFileName))
240 ret = false;
241 }
242 return ret;
243}
244
245bool
246WindowsContactBackend::getPersonFromContactFile(const QDir& contactDir,
247 const QString &contactFileName)
248{
249 QString contactFilePath
250 (contactDir.absolutePath() + "/" + contactFileName);
251 QFile contactFile(contactFilePath);
252 if (contactFile.open(QIODevice::ReadOnly)) {
253 QXmlStreamReader reader;
254 Person *p = new Person(this);
255 QVector<ContactMethod*> contactMethod;
256 reader.setDevice(&contactFile);
257 while (!reader.atEnd()) {
258 reader.readNext();
259 if (reader.isStartElement()) {
260 QString name = reader.name().toString();
261 if (name == "FormattedName")
262 p->setFormattedName(reader.readElementText());
263 else if (name == "NickName")
264 p->setNickName(reader.readElementText());
265 else if (name == "GivenName")
266 p->setFirstName(reader.readElementText());
267 else if (name == "FamilyName")
268 p->setFamilyName(reader.readElementText());
269 else if (name == "Company")
270 p->setOrganization(reader.readElementText());
271 else if (name == "Department")
272 p->setDepartment(reader.readElementText());
273 else if (name == "Number") {
274 QString number = reader.readElementText();
275 if (not number.isEmpty()) {
276 ContactMethod *contact =
Edric Milareta3e47282015-10-23 15:20:30 -0400277 PhoneDirectoryModel::instance().getNumber(number,p);
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400278 contactMethod.append(contact);
Edric Milaret67007d12015-05-07 09:40:09 -0400279 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400280 } else if (name == "ContactID") {
281 while (reader.name().toString() != "Value")
Edric Milaret67007d12015-05-07 09:40:09 -0400282 reader.readNext();
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400283 p->setUid(reader.readElementText().toUtf8());
284 }
285 else if (name == "Photo") {
286 //FIXME: It seems to be possible to have multiple photo...
287 reader.readNext();
288 if (reader.name().toString() == "Url") {
289 QString photoValue = reader.readElementText();
290 QImage photo;
291 photo.load(photoValue);
Edric Milaret791f0002015-09-28 12:58:47 -0400292 if (not photo.isNull())
Edric Milareta13b48c2016-01-18 14:42:35 -0500293 p->setPhoto(getCirclePhoto(photo));
Edric Milaret67007d12015-05-07 09:40:09 -0400294 }
295 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400296 else if (name == "EmailAddress") {
297 QString address;
298 bool isPreferred = false;
299 reader.readNext();
300 while (reader.name().toString() != "EmailAddress"
301 && !reader.atEnd()) {
302 if (reader.isStartElement()) {
303 QString tag = reader.name().toString();
304 if (tag == "Address")
305 address = reader.readElementText();
306 else if (tag == "Label")
307 if (reader.readElementText() == "Preferred")
308 isPreferred = true;
309 }
310 reader.readNext();
311 }
312 if (isPreferred)
313 p->setPreferredEmail(address);
314 }
Edric Milaret67007d12015-05-07 09:40:09 -0400315 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400316 }
317 if (reader.hasError()) {
318 qDebug() << reader.errorString();
319 contactFile.close();
320 return false;
321 } else {
Edric Milareta3e47282015-10-23 15:20:30 -0400322 Person* existing = PersonModel::instance().getPersonByUid(p->uid());
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400323 if (existing) {
Edric Milaret67007d12015-05-07 09:40:09 -0400324 if (contactMethod.size() > 0)
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400325 existing->setContactMethods ( contactMethod );
326 existing->setNickName ( p->nickName() );
327 existing->setFirstName ( p->firstName() );
328 existing->setFamilyName ( p->secondName() );
329 existing->setFormattedName ( p->formattedName() );
330 existing->setOrganization ( p->organization() );
331 existing->setPreferredEmail ( p->preferredEmail() );
332 existing->setGroup ( p->group() );
333 existing->setDepartment ( p->department() );
334 existing->setPhoto ( p->photo() );
335 delete p;
336 } else {
337 p->setContactMethods(contactMethod);
Edric Milaret67007d12015-05-07 09:40:09 -0400338 editor<Person>()->addExisting(p);
339 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400340 return true;
Edric Milaret67007d12015-05-07 09:40:09 -0400341 }
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400342 } else {
343 qDebug() << "Error Opening contact file : " << contactFileName;
344 return false;
Edric Milaret67007d12015-05-07 09:40:09 -0400345 }
Edric Milaret67007d12015-05-07 09:40:09 -0400346}
347
Edric Milareta13b48c2016-01-18 14:42:35 -0500348QImage WindowsContactBackend::getCirclePhoto(const QImage original)
349{
350 QImage target(sizePhoto_, sizePhoto_, QImage::Format_ARGB32_Premultiplied);
351 target.fill(Qt::transparent);
352
353 QPainter painter(&target);
354 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
355 painter.setBrush(QBrush(Qt::white));
356 painter.drawEllipse(0, 0, sizePhoto_, sizePhoto_);
357 painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
358 painter.drawImage(0, 0,
359 original
360 .scaled(60, 60, Qt::KeepAspectRatio, Qt::SmoothTransformation)
361 .convertToFormat(QImage::Format_ARGB32_Premultiplied));
362 return target;
363}
364
Edric Milaret67007d12015-05-07 09:40:09 -0400365bool
366WindowsContactBackend::reload()
367{
368 return false;
369}
370
371bool
372WindowsContactBackend::clear()
373{
374 return false;
375}
376
377QString
378WindowsContactBackend::name() const
379{
380 return "Windows Contact Backend";
381}
382
383QString
384WindowsContactBackend::category() const
385{
386 return "Contacts";
387}
388
389bool
390WindowsContactBackend::isEnabled() const
391{
392 return true;
393}
394
395QByteArray
396WindowsContactBackend::id() const
397{
398 return "wincb";
399}
400
401FlagPack<CollectionInterface::SupportedFeatures> WindowsContactBackend::supportedFeatures() const
402{
403 return (
404 CollectionInterface::SupportedFeatures::NONE |
Edric Milaret43f3c1e2015-07-16 17:52:47 -0400405 CollectionInterface::SupportedFeatures::LOAD |
406 CollectionInterface::SupportedFeatures::SAVE |
407 CollectionInterface::SupportedFeatures::REMOVE |
408 CollectionInterface::SupportedFeatures::ADD);
Edric Milaret67007d12015-05-07 09:40:09 -0400409}
410