Fixing: 273759 - <MemLeak>memory leak observed in accounts-ui when trying to configur...
[accounts-sso:libaccounts-ui.git] / lib / AccountsUI / genericaccountsetupformview.cpp
1 /*
2  * This file is part of accounts-ui
3  *
4  * Copyright (C) 2009-2010 Nokia Corporation.
5  *
6  * Contact: Alberto Mardegan <alberto.mardegan@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  */
22
23 //project
24 #include "credentialwidget.h"
25 #include "genericaccountsetupform.h"
26 #include "genericaccountsetupformview.h"
27 #include "common.h"
28 #include "accountsmanagersingleton.h"
29 #include "provider-plugin-process.h"
30 #include "provider-plugin-proxy.h"
31
32 //M
33 #include <MLayout>
34 #include <MLinearLayoutPolicy>
35 #include <MGridLayoutPolicy>
36 #include <MLocale>
37 #include <MButton>
38 #include <MTextEdit>
39 #include <MInfoBanner>
40 #include <MBasicListItem>
41 #include <MSeparator>
42 #include <MLabel>
43 #include <MProgressIndicator>
44 #include <MImageWidget>
45 #include <MMessageBox>
46 #include <MLabelHighlighter>
47
48 //Qt
49 #include <QDebug>
50 #include <QDesktopServices>
51 #include <QUrl>
52 #include <QTimer>
53
54 using namespace Accounts;
55 const int BinaryTextVariantSeparator = 0x9c;
56
57 class GenericAccountSetupFormListItem: public MBasicListItem
58 {
59 public:
60     GenericAccountSetupFormListItem(QGraphicsWidget *parent)
61             : MBasicListItem(MBasicListItem::IconWithTitle, parent)
62     {
63         setStyleName("CommonBasicListItemInverted");
64         titleLabelWidget()->setStyleName("CommonHeaderInverted");
65         setEnabled(false);
66     }
67
68     ~GenericAccountSetupFormListItem()
69     {}
70 };
71
72 class GenericAccountSetupFormViewPrivate
73 {
74 public:
75     GenericAccountSetupFormViewPrivate(GenericAccountSetupFormView *publicObject)
76         : widgetModel(0),
77           isInDomDocumentUpdate(false),
78           q_ptr(publicObject)
79     {
80         controller = NULL;
81         mainLayoutPolicy = NULL;
82         mainLayout = NULL;
83         separator = NULL;
84         descriptionLabel = NULL;
85         providerInfoItem = NULL;
86         questionLabel = NULL;
87         subscribeLabel = NULL;
88         progressIndicator = NULL;
89         connectingLabel = NULL;
90         stopButton = NULL;
91         widgetModel = NULL;
92         credentialWidget = NULL;
93         doNotShowPassword = false;
94     }
95
96     ~GenericAccountSetupFormViewPrivate()
97     {}
98
99     void createUiFromXml(const QDomDocument &providerAccountDocument);
100     void showCredentialWidgetAndHideProgress();
101     void hideCredentialWidgetAndShowProgress();
102
103 public:
104     GenericAccountSetupForm *controller;
105     MLinearLayoutPolicy *mainLayoutPolicy;
106     MLayout *mainLayout;
107     MSeparator *separator;
108     MLabel *descriptionLabel;
109     GenericAccountSetupFormListItem *providerInfoItem;
110     CredentialWidgetModel *widgetModel;
111     CredentialWidget *credentialWidget;
112     MLabel *questionLabel;
113     MLabel *subscribeLabel;
114     MProgressIndicator *progressIndicator;
115     MLabel *connectingLabel;
116     MButton *stopButton;
117
118     QMap<MWidget*, QDomElement> formWidgetDomElementMap;
119     QDomDocument providerAccountDocument;
120     bool isInDomDocumentUpdate;
121     QString registerNewLink;
122     QString forgotPasswordUrl;
123     bool doNotShowPassword;
124     QString authDomainSeparator;
125     QString authDomainDefault;
126     QString provider;
127
128 protected:
129     Q_DECLARE_PUBLIC(GenericAccountSetupFormView);
130     GenericAccountSetupFormView *q_ptr;
131 };
132
133 void GenericAccountSetupFormViewPrivate::showCredentialWidgetAndHideProgress()
134 {
135     if (mainLayoutPolicy) {
136         for (int i = mainLayoutPolicy->count() - 1; i >= 0; i--)
137             mainLayoutPolicy->removeAt(i);
138     }
139
140     if (mainLayout) {
141         for (int i = mainLayout->count() - 1; i >= 0; i--)
142             mainLayout->removeAt(i);
143     }
144
145     if (mainLayoutPolicy) {
146         delete mainLayoutPolicy;
147         mainLayoutPolicy = NULL;
148     }
149
150     if (connectingLabel) {
151         delete connectingLabel;
152         connectingLabel = NULL;
153     }
154
155     if (stopButton) {
156         delete stopButton;
157         stopButton = NULL;
158     }
159
160     mainLayoutPolicy = new MLinearLayoutPolicy(mainLayout, Qt::Vertical);
161
162     mainLayoutPolicy->addItem(providerInfoItem, Qt::AlignTop);
163     mainLayoutPolicy->addItem(descriptionLabel, Qt::AlignTop);
164     mainLayoutPolicy->addItem(separator, Qt::AlignTop);
165     mainLayoutPolicy->addItem(credentialWidget, Qt::AlignTop);
166     mainLayoutPolicy->addStretch();
167     mainLayoutPolicy->addItem(questionLabel, Qt::AlignCenter | Qt::AlignBottom);
168     mainLayoutPolicy->addItem(subscribeLabel, Qt::AlignCenter | Qt::AlignBottom);
169 }
170
171 void GenericAccountSetupFormViewPrivate::hideCredentialWidgetAndShowProgress()
172 {
173     if (subscribeLabel)
174         subscribeLabel->removeAllHighlighters();
175
176     if (mainLayoutPolicy) {
177         for (int i = mainLayoutPolicy->count() - 1; i >= 0; i--)
178             mainLayoutPolicy->removeAt(i);
179     }
180
181     if (mainLayout) {
182         for (int i = mainLayout->count() - 1; i >= 0; i--)
183             mainLayout->removeAt(i);
184     }
185
186     if (mainLayoutPolicy) {
187         delete mainLayoutPolicy;
188         mainLayoutPolicy = NULL;
189     }
190
191     mainLayoutPolicy = new MLinearLayoutPolicy(mainLayout, Qt::Vertical);
192
193     mainLayoutPolicy->addItem(providerInfoItem, Qt::AlignTop);
194     mainLayoutPolicy->addItem(descriptionLabel, Qt::AlignTop);
195     mainLayoutPolicy->addItem(separator, Qt::AlignTop);
196     progressIndicator = new MProgressIndicator(NULL, MProgressIndicator::spinnerType);
197     progressIndicator->setUnknownDuration(true);
198     progressIndicator->setStyleName("CommonLargeSpinnerInverted");
199
200     //% "Connecting"
201     connectingLabel = new MLabel(qtTrId("qtn_acc_connecting"));
202     connectingLabel->setStyleName("ConnectingLabel");
203     connectingLabel->setObjectName("wgGenericAccountSetupFormViewLabel");
204     connectingLabel->setAlignment(Qt::AlignCenter);
205
206     //% "STOP"
207     stopButton = new MButton(qtTrId("qtn_comm_cancel"));
208     stopButton->setStyleName("CommonSingleButtonInverted");
209     stopButton->setObjectName("wgGenericAccountSetupFormViewButton");
210     QObject::connect(stopButton, SIGNAL(clicked()),
211                      controller, SIGNAL(stopButtonPressed()));
212
213     mainLayoutPolicy->addStretch();
214     mainLayoutPolicy->addStretch(10);
215     mainLayoutPolicy->addItem(progressIndicator, Qt::AlignCenter);
216     mainLayoutPolicy->addStretch(2);
217     mainLayoutPolicy->addItem(connectingLabel, Qt::AlignCenter);
218     mainLayoutPolicy->addStretch(2);
219     mainLayoutPolicy->addItem(stopButton, Qt::AlignCenter);
220     mainLayoutPolicy->addStretch();
221     mainLayoutPolicy->addStretch(10);
222 }
223
224 void GenericAccountSetupFormViewPrivate::createUiFromXml(const QDomDocument &aProviderAccountDocument)
225 {
226     Q_Q(GenericAccountSetupFormView);
227     MLocale locale;
228     locale.installTrCatalog("signon-ui");
229     MLocale::setDefault(locale);
230
231     // xml file that describes the ui elements for the provider
232     providerAccountDocument = aProviderAccountDocument;
233     QDomElement root = aProviderAccountDocument.documentElement();
234     QDomElement descriptionElement = root.firstChildElement("description");
235     QDomElement providerIcon = root.firstChildElement("icon");
236     QDomElement catalog = root.firstChildElement("translations");
237     QDomElement signUpLink = root.firstChildElement("sign-up-link");
238     QDomElement forgotPasswordLink = root.firstChildElement("forgot-password-link");
239     QDomElement doNotShowPasswordField = root.firstChildElement("do-not-show-password");
240     provider = root.attribute("id");
241
242     if (!catalog.text().isEmpty()) {
243         MLocale locale;
244         locale.installTrCatalog(catalog.text());
245         MLocale::setDefault(locale);
246     }
247     QString providerName = qtTrId(root.firstChildElement("name").text().toLatin1());
248     if (q->model() != NULL) {
249         q->model()->setProviderName(providerName);
250     }
251     QString descriptionText = descriptionElement.text();
252     QString providerIconId = providerIcon.text();
253     registerNewLink = signUpLink.text();
254     forgotPasswordUrl = forgotPasswordLink.text();
255     if (!doNotShowPasswordField.text().isEmpty() &&
256         !doNotShowPasswordField.text().compare(QLatin1String("yes"), Qt::CaseInsensitive))
257         doNotShowPassword = true;
258     else
259         doNotShowPassword = false;
260
261     QDomElement node = root.firstChildElement("account-setup").firstChildElement();
262     while (!node.isNull()) {
263         if (node.tagName() == "authdomain") {
264             /* we select the first occurrence*/
265             authDomainSeparator = node.attribute("separator");
266             authDomainDefault = node.attribute("default_domain");
267             break;
268         }
269         node = node.nextSiblingElement();
270     }
271
272     // Provider info widgets
273     providerInfoItem = new GenericAccountSetupFormListItem(controller);
274     providerInfoItem->setObjectName("wgGenericAccountSetupProviderInfoTitle");
275     providerInfoItem->setTitle(providerName);
276     providerInfoItem->imageWidget()->setImage(providerIconId);
277
278     descriptionLabel = new MLabel(qtTrId(descriptionText.toLatin1()));
279     descriptionLabel->setWordWrap(true);
280     descriptionLabel->setWrapMode(QTextOption::WordWrap);
281     descriptionLabel->setStyleName("CommonBodyTextInverted");
282     if(MLocale::directionForText(qtTrId(descriptionText.toLatin1())) == Qt::RightToLeft)
283         descriptionLabel->setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
284     else
285         descriptionLabel->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
286
287     // Credentials widget
288     if (widgetModel) {
289         delete widgetModel;
290         widgetModel = NULL;
291     }
292
293     separator = new MSeparator();
294     separator->setOrientation(Qt::Horizontal);
295     separator->setStyleName("CommonItemDividerInverted");
296
297     widgetModel = new CredentialWidgetModel();
298     widgetModel->setDialogsVisabilityConfig(CredentialWidgetModel::LoginDialogVisible);
299     widgetModel->setSignInButtonVisible(true);
300     if (!forgotPasswordUrl.isEmpty()) {
301         widgetModel->setForgotPasswordUrl(forgotPasswordUrl);
302         widgetModel->setForgotPasswordVisible(true);
303
304         QObject::connect(widgetModel, SIGNAL(forgotPasswordClicked(QString)),
305                          q_ptr, SLOT(forgotPassword(QString)));
306     }
307
308     widgetModel->setPasswordFieldVisible(!doNotShowPassword);
309
310     QObject::connect(widgetModel, SIGNAL(signInClicked()),
311                      q_ptr , SLOT(signIn()));
312     QObject::connect(widgetModel, SIGNAL(modified(QList<const char*>)),
313                      q_ptr, SLOT(updateModel(QList<const char*>)));
314
315     credentialWidget = new CredentialWidget(widgetModel);
316
317     MWidgetController *spacer = new MWidgetController(controller);
318     spacer->setStyleName("CommonSpacer");
319     mainLayoutPolicy->addItem(spacer);
320
321     // add the widgets to the layout
322     mainLayoutPolicy->addItem(providerInfoItem);
323     mainLayoutPolicy->addItem(descriptionLabel);
324     mainLayoutPolicy->addItem(separator);
325
326     spacer = new MWidgetController(controller);
327     spacer->setStyleName("CommonLargeSpacer");
328     mainLayoutPolicy->addItem(spacer);
329
330     mainLayoutPolicy->addItem(credentialWidget);
331     mainLayoutPolicy->addStretch(3);
332
333     if (!registerNewLink.isEmpty()) {
334
335         //% "Don't have an account yet?"
336         questionLabel = new MLabel(qtTrId("qtn_acc_login_new_to_x"));
337         questionLabel->setStyleName("CommonBodyTextInverted");
338         questionLabel->setAlignment(Qt::AlignCenter);
339         questionLabel->setWordWrap(true);
340         questionLabel->setWrapMode(QTextOption::WordWrap);
341         questionLabel->setObjectName("AccountsPrimaryInfoLabel");
342
343         //% "Get one here"
344         QString link = qtTrId("qtn_acc_login_register_here");
345         /* fetch the first lenght variant as qt doesnt provide
346             support for length variants for rich text */
347         link = link.left(link.indexOf(QChar(BinaryTextVariantSeparator)));
348         subscribeLabel = new MLabel(link.arg(registerNewLink));
349
350         subscribeLabel->setAlignment(Qt::AlignCenter);
351         subscribeLabel->setStyleName("GenericAccountSetupFormSignUpStyle");
352         MCommonLabelHighlighter *subscribeLabelHighligher = new MCommonLabelHighlighter(QRegExp("[^\r\n]*"));
353         subscribeLabel->addHighlighter(subscribeLabelHighligher);
354
355         QObject::connect(subscribeLabelHighligher, SIGNAL(clicked(QString)),
356                          q_ptr, SLOT(registerNew()));
357
358         mainLayoutPolicy->addItem(questionLabel, Qt::AlignCenter);
359         mainLayoutPolicy->addItem(subscribeLabel, Qt::AlignCenter);
360
361         MWidgetController *spacer = new MWidgetController(controller);
362         spacer->setStyleName("CommonXLargeSpacer");
363         mainLayoutPolicy->addItem(spacer);
364     }
365 }
366
367 GenericAccountSetupFormView::GenericAccountSetupFormView(GenericAccountSetupForm *controller)
368     : MWidgetView(controller)
369     , d_ptr(new GenericAccountSetupFormViewPrivate(this))
370 {
371     Q_ASSERT(controller);
372     Q_D(GenericAccountSetupFormView);
373     d->controller = controller;
374
375     // layouts
376     d->mainLayout = new MLayout();
377     d->mainLayout->setContentsMargins(0, 0, 0, 0);
378     d->mainLayoutPolicy = new MLinearLayoutPolicy(d->mainLayout, Qt::Vertical);
379
380     controller->setLayout(d->mainLayout);
381 }
382
383 GenericAccountSetupFormView::~GenericAccountSetupFormView()
384 {
385     delete d_ptr;
386 }
387
388 void GenericAccountSetupFormView::updateDomDocument()
389 {
390     qDebug()<<__PRETTY_FUNCTION__;
391     Q_D(GenericAccountSetupFormView);
392
393     Q_ASSERT(qobject_cast<MWidget*>(sender()));
394     MWidget *formElement = qobject_cast<MWidget*>(sender());
395
396     QDomElement domElement = d->formWidgetDomElementMap.value(formElement);
397
398     if (qobject_cast<MTextEdit*>(sender())) {
399         MTextEdit *textEdit = qobject_cast<MTextEdit*>(sender());
400         domElement.setAttribute("value", textEdit->text());
401     }
402
403     if (qobject_cast<MButton*>(sender())) {
404         MButton *button = qobject_cast<MButton*>(sender());
405         button->setStyleName("button");
406         domElement.setAttribute("value", QVariant(button->isChecked()).toString());
407     }
408
409     d->isInDomDocumentUpdate = true;
410
411     // save the updated dom document back to the model
412     model()->setProviderAccountDocumentAsString(d->providerAccountDocument.toString());
413     d->isInDomDocumentUpdate = false;
414 }
415
416 void GenericAccountSetupFormView::updateData(const QList<const char*> &modifications)
417 {
418     Q_D(GenericAccountSetupFormView);
419
420     MWidgetView::updateData(modifications);
421
422     foreach(const char *member, modifications) {
423         if (!qstrcmp(member, "usernameDisplayString")) {
424             d->credentialWidget->setUsernameDisplayString(model()->usernameDisplayString());
425         }
426         if (!qstrcmp(member, "providerAccountDocumentAsString") &&
427             d->isInDomDocumentUpdate == false) {
428             QDomDocument providerAccountDocument;
429             QString errorStr;
430             int errorLine;
431             int errorColumn;
432
433             if (!providerAccountDocument.setContent(
434                  model()->providerAccountDocumentAsString(),
435                  true, &errorStr, &errorLine, &errorColumn)) {
436                  Q_ASSERT_X(false, "DOM reading of provider account document",
437                                   QString("Parse error at line %1, column %2:\n%3")
438                                   .arg(errorLine)
439                                   .arg(errorColumn)
440                                   .arg(errorStr).toAscii());
441             }
442             d->createUiFromXml(providerAccountDocument);
443             connect(d->credentialWidget, SIGNAL(doSignIn()),
444                     this, SLOT(signIn()));
445         } else if (!qstrcmp(member, "credentialWidgetEnabled")) {
446             if (model()->credentialWidgetEnabled()) {
447                 d->showCredentialWidgetAndHideProgress();
448             } else {
449                 d->hideCredentialWidgetAndShowProgress();
450             }
451         }
452     }
453 }
454
455 void GenericAccountSetupFormView::setupModel()
456 {
457     MWidgetView::setupModel();
458 }
459
460 void GenericAccountSetupFormView::signIn()
461 {
462     Q_D(GenericAccountSetupFormView);
463
464     if (d->widgetModel->username().isEmpty()) {
465         //% "Fill in username"
466         showInfoBanner(qtTrId("qtn_acc_fill_in_username_infobanner"));
467         d->credentialWidget->setFocusOnUserNameField();
468         return;
469     }
470
471     if (!d->doNotShowPassword && d->widgetModel->password().isEmpty()) {
472         //% "Fill in password"
473         showInfoBanner(qtTrId("qtn_acc_fill_in_password_infobanner"));
474         d->credentialWidget->setFocusOnPasswordField();
475         return;
476     }
477
478     if (!d->authDomainSeparator.isEmpty() && !d->authDomainDefault.isEmpty() &&
479         !d->widgetModel->username().contains(d->authDomainSeparator)){
480         // User did not enter the required domain name. Hence using the default domain name.
481         d->widgetModel->blockSignals(true);
482         QString domainDefault = d->authDomainSeparator + qtTrId(d->authDomainDefault.toLatin1());
483         d->widgetModel->setUsername(d->widgetModel->username() + domainDefault);
484         d->widgetModel->blockSignals(false);
485     }
486
487     AccountIdList idList = AccountsManager::instance()->accountList();
488     foreach (Accounts::AccountId id, idList) {
489         Accounts::Account *account = AccountsManager::instance()->account(id);
490         if ((d->widgetModel->username() == account->displayName()) &&
491             (d->provider == account->providerName())) {
492
493             QString messageBoxText = QString("%1\n%2").
494                 arg(qtTrId("qtn_acc_one_account_allowed").arg(model()->providerName())).
495                 arg(qtTrId("qtn_acc_tune_it_in_accounts"));
496
497             MMessageBox queryBox(qtTrId("qtn_acc_account_exists"), messageBoxText, 0);
498             MButton *continueButton = new MButton(qtTrId("qtn_comm_command_continue"));
499             MButton *cancelButton = new MButton(qtTrId("qtn_comm_cancel"));
500             queryBox.addButton(continueButton->model());
501             queryBox.addButton(cancelButton->model());
502             queryBox.exec();
503
504             ProviderPluginProcess *pluginProcess = ProviderPluginProcess::instance();
505
506             if (queryBox.clickedButton() == continueButton->model()) {
507                 QVariant data(AccountsUI::ProviderPluginProxy::EDIT_EXISTING_ACCOUNT);
508                 pluginProcess->setExitData(data);
509                 pluginProcess->setEditExistingAccount(id);
510                 pluginProcess->quit();
511             } else if (queryBox.clickedButton() == cancelButton->model()) {
512                 pluginProcess->quit();
513             }
514
515             queryBox.removeButton(continueButton->model());
516             queryBox.removeButton(continueButton->model());
517
518             delete continueButton;
519             delete cancelButton;
520         }
521     }
522
523     model()->setRememberMe(d->widgetModel->checkboxPressed());
524     model()->setUsername(d->widgetModel->username());
525     model()->setPassword(d->widgetModel->password());
526
527     d->controller->validateCredentials();
528 }
529
530 void GenericAccountSetupFormView::updateModel(QList<const char*> modifications)
531 {
532     Q_D(GenericAccountSetupFormView);
533
534     foreach(const char *member, modifications) {
535         if (!qstrcmp(member, "username")) {
536             model()->setUsername(d->widgetModel->username());
537         } else if (!qstrcmp(member, "password")) {
538             model()->setPassword(d->widgetModel->password());
539         }
540      }
541 }
542
543 void GenericAccountSetupFormView::registerNew()
544 {
545     Q_D(GenericAccountSetupFormView);
546     if (!QDesktopServices::openUrl(QUrl(d->registerNewLink)))
547     qWarning() << Q_FUNC_INFO << "Unable to open web browser";
548 }
549
550 void GenericAccountSetupFormView::forgotPassword(QString link)
551 {
552     if (!QDesktopServices::openUrl(QUrl(link)))
553         qWarning() << Q_FUNC_INFO << "Unable to open web browser";
554 }
555
556
557 // bind view and controller together
558 M_REGISTER_VIEW_NEW(GenericAccountSetupFormView, GenericAccountSetupForm)