Merge lp:~mardy/ubuntu-system-settings-online-accounts/master into lp:ubuntu-system-settings-online-accounts

Proposed by Alberto Mardegan
Status: Merged
Approved by: David Barth
Approved revision: 129
Merged at revision: 154
Proposed branch: lp:~mardy/ubuntu-system-settings-online-accounts/master
Merge into: lp:ubuntu-system-settings-online-accounts
Diff against target: 5466 lines (+3258/-754)
81 files modified
.bzrignore (+10/-8)
client/OnlineAccountsClient/OnlineAccountsClient.pro (+2/-2)
client/OnlineAccountsClient/setup.cpp (+3/-1)
common-project-config.pri (+3/-0)
debian/control (+1/-1)
debian/rules (+1/-0)
debian/ubuntu-system-settings-online-accounts.install (+1/-0)
online-accounts-service/com.ubuntu.OnlineAccountsUi.service.in (+1/-1)
online-accounts-service/main.cpp (+109/-0)
online-accounts-service/mir-helper-stub.cpp (+69/-0)
online-accounts-service/mir-helper.cpp (+192/-0)
online-accounts-service/mir-helper.h (+68/-0)
online-accounts-service/online-accounts-service.pro (+82/-0)
online-accounts-service/request-manager.cpp (+42/-6)
online-accounts-service/request-manager.h (+3/-1)
online-accounts-service/request.cpp (+214/-0)
online-accounts-service/request.h (+70/-0)
online-accounts-service/service.cpp (+3/-11)
online-accounts-service/signonui-service.cpp (+19/-33)
online-accounts-service/ui-proxy.cpp (+301/-0)
online-accounts-service/ui-proxy.h (+54/-0)
online-accounts-ui/application-manager.cpp (+10/-0)
online-accounts-ui/browser-request.cpp (+12/-24)
online-accounts-ui/browser-request.h (+2/-4)
online-accounts-ui/globals.h (+1/-0)
online-accounts-ui/ipc.cpp (+184/-0)
online-accounts-ui/ipc.h (+67/-0)
online-accounts-ui/main.cpp (+18/-68)
online-accounts-ui/module/OAuth.qml (+0/-1)
online-accounts-ui/module/WebView.qml (+1/-2)
online-accounts-ui/module/qmldir.in (+2/-0)
online-accounts-ui/notification.cpp (+8/-0)
online-accounts-ui/notification.h (+1/-0)
online-accounts-ui/online-accounts-ui-helper.pro (+5/-37)
online-accounts-ui/online-accounts-ui.pro (+1/-1)
online-accounts-ui/panel-request.cpp (+6/-7)
online-accounts-ui/panel-request.h (+4/-3)
online-accounts-ui/provider-request.cpp (+4/-3)
online-accounts-ui/provider-request.h (+3/-2)
online-accounts-ui/qml/ProviderRequest.qml (+12/-5)
online-accounts-ui/qml/SignOnUiPage.qml (+40/-0)
online-accounts-ui/request-handler.cpp (+6/-0)
online-accounts-ui/request.cpp (+65/-52)
online-accounts-ui/request.h (+12/-6)
online-accounts-ui/signonui-request.cpp (+114/-56)
online-accounts-ui/signonui-request.h (+6/-6)
online-accounts-ui/ui-server.cpp (+199/-0)
online-accounts-ui/ui-server.h (+57/-0)
online-accounts-ui/ui.qrc (+1/-10)
system-settings-plugin/AccountEditPage.qml (+4/-20)
system-settings-plugin/MainPage.qml (+29/-45)
system-settings-plugin/NewAccountPage.qml (+0/-2)
system-settings-plugin/ProviderPluginList.qml (+16/-12)
system-settings-plugin/online-accounts.settings (+1/-1)
system-settings-plugin/plugin.cpp (+0/-89)
system-settings-plugin/plugin.h (+0/-38)
system-settings-plugin/system-settings-plugin.pro (+16/-29)
tests/client/tst_client.cpp (+1/-1)
tests/client/tst_qml_client.cpp (+1/-1)
tests/online-accounts-service/online-accounts-service.pro (+4/-0)
tests/online-accounts-service/tst_inactivity_timer.pro (+7/-3)
tests/online-accounts-service/tst_service.cpp (+128/-104)
tests/online-accounts-service/tst_service.pro (+14/-11)
tests/online-accounts-ui/data/com.ubuntu.tests_application.application (+19/-0)
tests/online-accounts-ui/mock/notification-mock.cpp (+81/-0)
tests/online-accounts-ui/mock/notification-mock.h (+66/-0)
tests/online-accounts-ui/mock/request-mock.cpp (+139/-0)
tests/online-accounts-ui/mock/request-mock.h (+61/-0)
tests/online-accounts-ui/mock/ui-server-mock.cpp (+68/-0)
tests/online-accounts-ui/mock/ui-server-mock.h (+53/-0)
tests/online-accounts-ui/online-accounts-ui.pri (+18/-0)
tests/online-accounts-ui/online-accounts-ui.pro (+1/-2)
tests/online-accounts-ui/qml/Source/qmldir (+1/-1)
tests/online-accounts-ui/tst_access_model.pro (+8/-15)
tests/online-accounts-ui/tst_application_manager.pro (+8/-15)
tests/online-accounts-ui/tst_notification.cpp (+33/-0)
tests/online-accounts-ui/tst_notification.pro (+3/-11)
tests/online-accounts-ui/tst_signonui_request.cpp (+336/-0)
tests/online-accounts-ui/tst_signonui_request.pro (+48/-0)
tests/tests.pro (+1/-0)
ubuntu-system-settings-online-accounts.pro (+4/-3)
To merge this branch: bzr merge lp:~mardy/ubuntu-system-settings-online-accounts/master
Reviewer Review Type Date Requested Status
David Barth (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Alexandre Abreu (community) Needs Information
Review via email: mp+222028@code.launchpad.net

Commit message

New version

- Use signon-ui-service, to fix co-installation with of signon-ui.
- Implement reauthentication (via snap-decisions)

Description of the change

New version

- Use signon-ui-service, to fix co-installation with of signon-ui.
- Implement reauthentication (via snap-decisions)

To post a comment you must log in.
Revision history for this message
Alexandre Abreu (abreu-alexandre) wrote :

So the signon-ui-service package provides the removed bits I guess ?

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alberto Mardegan (mardy) wrote :

> So the signon-ui-service package provides the removed bits I guess ?

Yes, the .service files are being moved there.

123. By Alberto Mardegan

Click hooks: remove stale file, handle updates

Remove the installed files when the click hook files get removed (meaning that an application has been unregistered).
Update the installed files when the date of the click hook file is newer.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
124. By Alberto Mardegan

Add a Notification class to wrap libnotify

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
125. By Alberto Mardegan

Minor reformatting of debian rules

This allows Jenkins hooks to enable coverage.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
126. By Alberto Mardegan

Add ApplicationManager::applicationFromProfile()

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
127. By Alberto Mardegan

Remove signon-ui provides

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
128. By Alberto Mardegan

Merge from trunk

129. By Alberto Mardegan

Implement account reauthentication and reauthorization

The need for a user interaction might arise at any time, depending on the remote service's policy. It will typically happen when an access token expires, or when an application requests new permissions or changes its key.
In order not to disrupt the user's activity, we preliminary implement this as a snap decision (as was suggested in https://docs.google.com/a/canonical.com/document/d/1puQ9Z0yKqzsQ1VQ1OOBkxgp78iWGnAhAkFXWJFTWIrE/edit#heading=h.topn0ejru38u). However, since design has not yet come up with a definitive approach, we keep the UI strings untranslated since they are not final.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
David Barth (dbarth) :
review: Approve
130. By Alberto Mardegan

From trunk

* Add forgotten test-dep on ubuntu-ui-toolkit-autopilot.
[ Leo Arias ]
* Refactored the autopilot tests to use the page object pattern. Added
  the method go to add account to be used in UX tests.
[ Michał Sawicz ]
* New icon from suru theme.

131. By Alberto Mardegan

Move the UI into a separate process

Introduce online-accounts-service as a UI-less DBus service which acts as a
dispatcher for authentication and authorization requests; the requests
themselves are processed by a subprocess, online-accounts-ui, of which multiple
instances can exists (each instance having single window).

This is the first step towards implementing trust session support.

132. By Alberto Mardegan

This branch moves the OA settings back into the main settings application, as an inline plugin. This should help with the overall UX, and in particular some annoying bugs ("black window", etc.)

The functional scope covers:
- listing online accounts
- changing apps permissions for an account
- adding a new online account
- removing an online account

The branch has currently some limitations:
- only support OAuth providers
- the system-widw OAuth provider path is hardcoded
- doesn't support providers installed in the home directory

The limitations should be easy to lift once the general logic is reviewed.

133. By Alberto Mardegan

Support Mir prompt sessions

134. By Alberto Mardegan

Avoid clicking multiple times

135. By Alberto Mardegan

Make the Mir dependency optional

Mir is not yet available for all architectures.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2014-06-05 13:28:30 +0000
3+++ .bzrignore 2014-08-18 13:33:35 +0000
4@@ -31,18 +31,20 @@
5 /debian/*.substvars
6 /plugins/example/libexample.so*
7 /po/*.mo
8-/src/com.ubuntu.OnlineAccountsUi.service
9-/src/module/OnlineAccountsPlugin.pc
10-/src/module/libOnlineAccountsPlugin.so*
11-/src/module/qmldir
12-/src/online-accounts-ui.desktop
13-/src/online-accounts-ui
14+/online-accounts-service/com.ubuntu.OnlineAccountsUi.service
15+/online-accounts-service/online-accounts-service
16+/online-accounts-ui/module/OnlineAccountsPlugin.pc
17+/online-accounts-ui/module/libOnlineAccountsPlugin.so*
18+/online-accounts-ui/module/qmldir
19+/online-accounts-ui/online-accounts-ui.desktop
20+/online-accounts-ui/online-accounts-ui
21 /tests/click-hooks/tst_online_accounts_hooks
22 /tests/client/tst_client
23 /tests/client/tst_qml_client
24+/tests/online-accounts-service/tst_inactivity_timer
25+/tests/online-accounts-service/tst_service
26 /tests/online-accounts-ui/qml/tst_online_accounts_qml
27 /tests/online-accounts-ui/tst_access_model
28 /tests/online-accounts-ui/tst_application_manager
29-/tests/online-accounts-ui/tst_inactivity_timer
30 /tests/online-accounts-ui/tst_notification
31-/tests/online-accounts-ui/tst_service
32+/tests/online-accounts-ui/tst_signonui_request
33
34=== modified file 'client/OnlineAccountsClient/OnlineAccountsClient.pro'
35--- client/OnlineAccountsClient/OnlineAccountsClient.pro 2014-03-19 12:14:14 +0000
36+++ client/OnlineAccountsClient/OnlineAccountsClient.pro 2014-08-18 13:33:35 +0000
37@@ -25,10 +25,10 @@
38 INCLUDEPATH += \
39 $${TOP_SRC_DIR}
40
41-ONLINE_ACCOUNTS_UI_SRC = $${TOP_SRC_DIR}/src
42+ONLINE_ACCOUNTS_SERVICE_SRC = $${TOP_SRC_DIR}/online-accounts-service
43
44 DBUS_INTERFACES += \
45- $${ONLINE_ACCOUNTS_UI_SRC}/com.ubuntu.OnlineAccountsUi.xml
46+ $${ONLINE_ACCOUNTS_SERVICE_SRC}/com.ubuntu.OnlineAccountsUi.xml
47
48 SOURCES += \
49 setup.cpp
50
51=== modified file 'client/OnlineAccountsClient/setup.cpp'
52--- client/OnlineAccountsClient/setup.cpp 2014-05-30 08:09:02 +0000
53+++ client/OnlineAccountsClient/setup.cpp 2014-08-18 13:33:35 +0000
54@@ -20,9 +20,9 @@
55 * <http://www.gnu.org/licenses/>.
56 */
57
58+#include "online-accounts-ui/globals.h"
59 #include "onlineaccountsui_interface.h"
60 #include "setup.h"
61-#include "src/globals.h"
62
63 #include <QDBusConnection>
64 #include <QDBusPendingCallWatcher>
65@@ -76,6 +76,8 @@
66 {
67 QVariantMap options;
68
69+ options.insert(OAU_KEY_PID, uint(getpid()));
70+
71 QWindow *window = clientWindow();
72 if (window) {
73 options.insert(OAU_KEY_WINDOW_ID, window->winId());
74
75=== modified file 'common-project-config.pri'
76--- common-project-config.pri 2013-08-06 15:06:54 +0000
77+++ common-project-config.pri 2014-08-18 13:33:35 +0000
78@@ -47,3 +47,6 @@
79 PLUGIN_MODULE_DIR = $$system("pkg-config --define-variable=prefix=$${INSTALL_PREFIX} --variable plugin_module_dir SystemSettings")
80 PLUGIN_QML_DIR = $$system("pkg-config --define-variable=prefix=$${INSTALL_PREFIX} --variable plugin_qml_dir SystemSettings")
81 PLUGIN_PRIVATE_MODULE_DIR = $$system("pkg-config --define-variable=prefix=$${INSTALL_PREFIX} --variable plugin_private_module_dir SystemSettings")
82+
83+I18N_DOMAIN="ubuntu-system-settings-online-accounts"
84+SIGNONUI_I18N_DOMAIN="signon-ui"
85
86=== modified file 'debian/control'
87--- debian/control 2014-06-04 12:00:43 +0000
88+++ debian/control 2014-08-18 13:33:35 +0000
89@@ -6,6 +6,7 @@
90 pkg-config,
91 python3:any,
92 libaccounts-qt5-dev,
93+ libmirclient-dev [!powerpc !ppc64 !ppc64el],
94 libnotify-dev,
95 libsignon-qt5-dev,
96 libsystemsettings-dev (>= 0.1+13.10.20130806),
97@@ -36,7 +37,6 @@
98 qtdeclarative5-ubuntu-ui-toolkit-plugin | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles,
99 signon-ui-service,
100 ubuntu-system-settings
101-Provides: signon-ui
102 Description: Online Accounts setup for Ubuntu Touch
103 Online Accounts setup utility for the Ubuntu Touch System Settings.
104
105
106=== modified file 'debian/rules'
107--- debian/rules 2014-06-06 08:10:04 +0000
108+++ debian/rules 2014-08-18 13:33:35 +0000
109@@ -8,6 +8,7 @@
110 dh_auto_configure -- \
111 LIBDIR=/usr/lib/$(DEB_HOST_MULTIARCH) \
112 "QMAKE_CXXFLAGS=$(CFLAGS)" \
113+ CONFIG+=enable-mir \
114 CONFIG+=ubuntu-docs \
115 ubuntu-system-settings-online-accounts.pro
116
117
118=== modified file 'debian/ubuntu-system-settings-online-accounts.install'
119--- debian/ubuntu-system-settings-online-accounts.install 2014-06-04 09:59:37 +0000
120+++ debian/ubuntu-system-settings-online-accounts.install 2014-08-18 13:33:35 +0000
121@@ -1,4 +1,5 @@
122 usr/bin/online-accounts-hooks
123+usr/bin/online-accounts-service
124 usr/bin/online-accounts-ui
125 usr/lib/*/pkgconfig/OnlineAccountsPlugin.pc
126 usr/lib/*/ubuntu-system-settings
127
128=== added directory 'online-accounts-service'
129=== renamed file 'src/com.canonical.indicators.webcredentials.xml' => 'online-accounts-service/com.canonical.indicators.webcredentials.xml'
130=== renamed file 'src/com.ubuntu.OnlineAccountsUi.service.in' => 'online-accounts-service/com.ubuntu.OnlineAccountsUi.service.in'
131--- src/com.ubuntu.OnlineAccountsUi.service.in 2013-11-21 12:26:22 +0000
132+++ online-accounts-service/com.ubuntu.OnlineAccountsUi.service.in 2014-08-18 13:33:35 +0000
133@@ -1,3 +1,3 @@
134 [D-BUS Service]
135 Name=com.ubuntu.OnlineAccountsUi
136-Exec=$${INSTALL_PREFIX}/bin/$${TARGET} --desktop_file_hint=$${desktop.path}/$${TARGET}.desktop
137+Exec=$${INSTALL_PREFIX}/bin/$${TARGET}
138
139=== renamed file 'src/com.ubuntu.OnlineAccountsUi.xml' => 'online-accounts-service/com.ubuntu.OnlineAccountsUi.xml'
140=== renamed file 'src/inactivity-timer.cpp' => 'online-accounts-service/inactivity-timer.cpp'
141=== renamed file 'src/inactivity-timer.h' => 'online-accounts-service/inactivity-timer.h'
142=== renamed file 'src/indicator-service.cpp' => 'online-accounts-service/indicator-service.cpp'
143=== renamed file 'src/indicator-service.h' => 'online-accounts-service/indicator-service.h'
144=== added file 'online-accounts-service/main.cpp'
145--- online-accounts-service/main.cpp 1970-01-01 00:00:00 +0000
146+++ online-accounts-service/main.cpp 2014-08-18 13:33:35 +0000
147@@ -0,0 +1,109 @@
148+/*
149+ * Copyright (C) 2013 Canonical Ltd.
150+ *
151+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
152+ *
153+ * This file is part of online-accounts-ui
154+ *
155+ * This program is free software: you can redistribute it and/or modify it
156+ * under the terms of the GNU General Public License version 3, as published
157+ * by the Free Software Foundation.
158+ *
159+ * This program is distributed in the hope that it will be useful, but
160+ * WITHOUT ANY WARRANTY; without even the implied warranties of
161+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
162+ * PURPOSE. See the GNU General Public License for more details.
163+ *
164+ * You should have received a copy of the GNU General Public License along
165+ * with this program. If not, see <http://www.gnu.org/licenses/>.
166+ */
167+
168+#include "debug.h"
169+#include "globals.h"
170+#include "inactivity-timer.h"
171+#include "indicator-service.h"
172+#include "request-manager.h"
173+#include "service.h"
174+#include "signonui-service.h"
175+
176+#include <QCoreApplication>
177+#include <QDBusConnection>
178+#include <QProcessEnvironment>
179+
180+using namespace OnlineAccountsUi;
181+
182+int main(int argc, char **argv)
183+{
184+ QCoreApplication app(argc, argv);
185+
186+ /* read environment variables */
187+ QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
188+ if (environment.contains(QLatin1String("OAU_LOGGING_LEVEL"))) {
189+ bool isOk;
190+ int value = environment.value(
191+ QLatin1String("OAU_LOGGING_LEVEL")).toInt(&isOk);
192+ if (isOk)
193+ setLoggingLevel(value);
194+ }
195+
196+ /* default daemonTimeout to 5 seconds */
197+ int daemonTimeout = 5;
198+
199+ /* override daemonTimeout if OAU_DAEMON_TIMEOUT is set */
200+ if (environment.contains(QLatin1String("OAU_DAEMON_TIMEOUT"))) {
201+ bool isOk;
202+ int value = environment.value(
203+ QLatin1String("OAU_DAEMON_TIMEOUT")).toInt(&isOk);
204+ if (isOk)
205+ daemonTimeout = value;
206+ }
207+
208+ RequestManager *requestManager = new RequestManager();
209+
210+ Service *service = new Service();
211+ QDBusConnection connection = QDBusConnection::sessionBus();
212+ connection.registerService(OAU_SERVICE_NAME);
213+ connection.registerObject(OAU_OBJECT_PATH, service);
214+
215+ SignOnUi::Service *signonuiService = new SignOnUi::Service();
216+ connection.registerService(SIGNONUI_SERVICE_NAME);
217+ connection.registerObject(SIGNONUI_OBJECT_PATH, signonuiService,
218+ QDBusConnection::ExportAllContents);
219+
220+ SignOnUi::IndicatorService *indicatorService =
221+ new SignOnUi::IndicatorService();
222+ connection.registerService(WEBCREDENTIALS_BUS_NAME);
223+ connection.registerObject(WEBCREDENTIALS_OBJECT_PATH,
224+ indicatorService->serviceObject());
225+
226+
227+ InactivityTimer *inactivityTimer = 0;
228+ if (daemonTimeout > 0) {
229+ inactivityTimer = new InactivityTimer(daemonTimeout * 1000);
230+ inactivityTimer->watchObject(requestManager);
231+ inactivityTimer->watchObject(indicatorService);
232+ QObject::connect(inactivityTimer, SIGNAL(timeout()),
233+ &app, SLOT(quit()));
234+ }
235+
236+ int ret = app.exec();
237+
238+ connection.unregisterService(WEBCREDENTIALS_BUS_NAME);
239+ connection.unregisterObject(WEBCREDENTIALS_OBJECT_PATH);
240+ delete indicatorService;
241+
242+ connection.unregisterService(SIGNONUI_SERVICE_NAME);
243+ connection.unregisterObject(SIGNONUI_OBJECT_PATH);
244+ delete signonuiService;
245+
246+ connection.unregisterService(OAU_SERVICE_NAME);
247+ connection.unregisterObject(OAU_OBJECT_PATH);
248+ delete service;
249+
250+ delete requestManager;
251+
252+ delete inactivityTimer;
253+
254+ return ret;
255+}
256+
257
258=== added file 'online-accounts-service/mir-helper-stub.cpp'
259--- online-accounts-service/mir-helper-stub.cpp 1970-01-01 00:00:00 +0000
260+++ online-accounts-service/mir-helper-stub.cpp 2014-08-18 13:33:35 +0000
261@@ -0,0 +1,69 @@
262+/*
263+ * Copyright (C) 2014 Canonical Ltd.
264+ *
265+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
266+ *
267+ * This file is part of online-accounts-ui
268+ *
269+ * This program is free software: you can redistribute it and/or modify it
270+ * under the terms of the GNU General Public License version 3, as published
271+ * by the Free Software Foundation.
272+ *
273+ * This program is distributed in the hope that it will be useful, but
274+ * WITHOUT ANY WARRANTY; without even the implied warranties of
275+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
276+ * PURPOSE. See the GNU General Public License for more details.
277+ *
278+ * You should have received a copy of the GNU General Public License along
279+ * with this program. If not, see <http://www.gnu.org/licenses/>.
280+ */
281+
282+#include "debug.h"
283+#include "mir-helper.h"
284+
285+using namespace OnlineAccountsUi;
286+
287+namespace OnlineAccountsUi {
288+
289+static MirHelper *m_instance = 0;
290+
291+} // namespace
292+
293+PromptSession::PromptSession(PromptSessionPrivate *priv):
294+ d_ptr(priv)
295+{
296+}
297+
298+PromptSession::~PromptSession()
299+{
300+}
301+
302+QString PromptSession::requestSocket()
303+{
304+ return QString();
305+}
306+
307+MirHelper::MirHelper(QObject *parent):
308+ QObject(parent),
309+ d_ptr(0)
310+{
311+}
312+
313+MirHelper::~MirHelper()
314+{
315+ m_instance = 0;
316+}
317+
318+MirHelper *MirHelper::instance()
319+{
320+ if (!m_instance) {
321+ m_instance = new MirHelper;
322+ }
323+ return m_instance;
324+}
325+
326+PromptSession *MirHelper::createPromptSession(pid_t initiatorPid)
327+{
328+ Q_UNUSED(initiatorPid);
329+ return 0;
330+}
331
332=== added file 'online-accounts-service/mir-helper.cpp'
333--- online-accounts-service/mir-helper.cpp 1970-01-01 00:00:00 +0000
334+++ online-accounts-service/mir-helper.cpp 2014-08-18 13:33:35 +0000
335@@ -0,0 +1,192 @@
336+/*
337+ * Copyright (C) 2014 Canonical Ltd.
338+ *
339+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
340+ *
341+ * This file is part of online-accounts-ui
342+ *
343+ * This program is free software: you can redistribute it and/or modify it
344+ * under the terms of the GNU General Public License version 3, as published
345+ * by the Free Software Foundation.
346+ *
347+ * This program is distributed in the hope that it will be useful, but
348+ * WITHOUT ANY WARRANTY; without even the implied warranties of
349+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
350+ * PURPOSE. See the GNU General Public License for more details.
351+ *
352+ * You should have received a copy of the GNU General Public License along
353+ * with this program. If not, see <http://www.gnu.org/licenses/>.
354+ */
355+
356+#include "debug.h"
357+#include "mir-helper.h"
358+
359+#include <mir_toolkit/mir_client_library.h>
360+#include <mir_toolkit/mir_prompt_session.h>
361+
362+#include <QList>
363+#include <QStandardPaths>
364+
365+using namespace OnlineAccountsUi;
366+
367+namespace OnlineAccountsUi {
368+
369+static MirHelper *m_instance = 0;
370+
371+class PromptSessionPrivate
372+{
373+public:
374+ inline PromptSessionPrivate(MirPromptSession *session);
375+ inline ~PromptSessionPrivate();
376+
377+ MirPromptSession *m_mirSession;
378+ QList<int> m_fds;
379+ mutable PromptSession *q_ptr;
380+};
381+
382+class MirHelperPrivate: public QObject
383+{
384+ Q_OBJECT
385+ Q_DECLARE_PUBLIC(MirHelper)
386+
387+public:
388+ inline MirHelperPrivate(MirHelper *helper);
389+ inline ~MirHelperPrivate();
390+
391+ PromptSession *createPromptSession(pid_t initiatorPid);
392+
393+private:
394+ friend class PromptSession;
395+ MirConnection *m_connection;
396+ QList<PromptSession*> m_sessions;
397+ mutable MirHelper *q_ptr;
398+};
399+
400+} // namespace
401+
402+PromptSessionPrivate::PromptSessionPrivate(MirPromptSession *session):
403+ m_mirSession(session)
404+{
405+}
406+
407+PromptSessionPrivate::~PromptSessionPrivate()
408+{
409+ mir_prompt_session_release_sync(m_mirSession);
410+ m_mirSession = 0;
411+}
412+
413+PromptSession::PromptSession(PromptSessionPrivate *priv):
414+ d_ptr(priv)
415+{
416+ MirHelperPrivate *helperPrivate = MirHelper::instance()->d_ptr;
417+ helperPrivate->m_sessions.append(this);
418+}
419+
420+PromptSession::~PromptSession()
421+{
422+ MirHelperPrivate *helperPrivate = MirHelper::instance()->d_ptr;
423+ helperPrivate->m_sessions.removeOne(this);
424+ delete d_ptr;
425+}
426+
427+static void client_fd_callback(MirPromptSession *, size_t count,
428+ int const *fds, void *context)
429+{
430+ PromptSessionPrivate *priv = (PromptSessionPrivate *)context;
431+ for (size_t i = 0; i < count; i++) {
432+ priv->m_fds.append(fds[i]);
433+ }
434+}
435+
436+QString PromptSession::requestSocket()
437+{
438+ Q_D(PromptSession);
439+
440+ d->m_fds.clear();
441+ mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(
442+ d->m_mirSession, 1, client_fd_callback, d));
443+ if (!d->m_fds.isEmpty()) {
444+ return QString("fd://%1").arg(d->m_fds[0]);
445+ } else {
446+ return QString();
447+ }
448+}
449+
450+MirHelperPrivate::MirHelperPrivate(MirHelper *helper):
451+ QObject(helper),
452+ q_ptr(helper)
453+{
454+ QString mirSocket =
455+ QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation) +
456+ "/mir_socket_trusted";
457+ m_connection = mir_connect_sync(mirSocket.toUtf8().constData(),
458+ "online-accounts-service");
459+ if (Q_UNLIKELY(!mir_connection_is_valid(m_connection))) {
460+ qWarning() << "Invalid Mir connection:" <<
461+ mir_connection_get_error_message(m_connection);
462+ return;
463+ }
464+}
465+
466+MirHelperPrivate::~MirHelperPrivate()
467+{
468+ if (m_connection) {
469+ mir_connection_release(m_connection);
470+ m_connection = 0;
471+ }
472+}
473+
474+static void session_event_callback(MirPromptSession *,
475+ MirPromptSessionState state,
476+ void *)
477+{
478+ DEBUG() << "Prompt Session state updated to" << state;
479+}
480+
481+
482+PromptSession *MirHelperPrivate::createPromptSession(pid_t initiatorPid)
483+{
484+ if (Q_UNLIKELY(!m_connection)) return 0;
485+
486+ MirPromptSession *mirSession =
487+ mir_connection_create_prompt_session_sync(m_connection,
488+ initiatorPid,
489+ session_event_callback,
490+ this);
491+ if (!mirSession) return 0;
492+
493+ if (Q_UNLIKELY(!mir_prompt_session_is_valid(mirSession))) {
494+ qWarning() << "Invalid prompt session:" <<
495+ mir_prompt_session_error_message(mirSession);
496+ return 0;
497+ }
498+
499+ return new PromptSession(new PromptSessionPrivate(mirSession));
500+}
501+
502+MirHelper::MirHelper(QObject *parent):
503+ QObject(parent),
504+ d_ptr(new MirHelperPrivate(this))
505+{
506+}
507+
508+MirHelper::~MirHelper()
509+{
510+ m_instance = 0;
511+}
512+
513+MirHelper *MirHelper::instance()
514+{
515+ if (!m_instance) {
516+ m_instance = new MirHelper;
517+ }
518+ return m_instance;
519+}
520+
521+PromptSession *MirHelper::createPromptSession(pid_t initiatorPid)
522+{
523+ Q_D(MirHelper);
524+ return d->createPromptSession(initiatorPid);
525+}
526+
527+#include "mir-helper.moc"
528
529=== added file 'online-accounts-service/mir-helper.h'
530--- online-accounts-service/mir-helper.h 1970-01-01 00:00:00 +0000
531+++ online-accounts-service/mir-helper.h 2014-08-18 13:33:35 +0000
532@@ -0,0 +1,68 @@
533+/*
534+ * Copyright (C) 2014 Canonical Ltd.
535+ *
536+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
537+ *
538+ * This file is part of online-accounts-ui
539+ *
540+ * This program is free software: you can redistribute it and/or modify it
541+ * under the terms of the GNU General Public License version 3, as published
542+ * by the Free Software Foundation.
543+ *
544+ * This program is distributed in the hope that it will be useful, but
545+ * WITHOUT ANY WARRANTY; without even the implied warranties of
546+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
547+ * PURPOSE. See the GNU General Public License for more details.
548+ *
549+ * You should have received a copy of the GNU General Public License along
550+ * with this program. If not, see <http://www.gnu.org/licenses/>.
551+ */
552+
553+#ifndef OAU_MIR_HELPER_H
554+#define OAU_MIR_HELPER_H
555+
556+#include <QObject>
557+
558+namespace OnlineAccountsUi {
559+
560+class PromptSessionPrivate;
561+class MirHelperPrivate;
562+
563+class PromptSession
564+{
565+public:
566+ ~PromptSession();
567+
568+ QString requestSocket();
569+
570+private:
571+ explicit PromptSession(PromptSessionPrivate *priv);
572+
573+private:
574+ friend class MirHelperPrivate;
575+ PromptSessionPrivate *d_ptr;
576+ Q_DECLARE_PRIVATE(PromptSession)
577+};
578+
579+class MirHelper: public QObject
580+{
581+ Q_OBJECT
582+
583+public:
584+ static MirHelper *instance();
585+
586+ PromptSession *createPromptSession(pid_t initiatorPid);
587+
588+private:
589+ explicit MirHelper(QObject *parent = 0);
590+ ~MirHelper();
591+
592+private:
593+ friend class PromptSession;
594+ MirHelperPrivate *d_ptr;
595+ Q_DECLARE_PRIVATE(MirHelper)
596+};
597+
598+} // namespace
599+
600+#endif // OAU_MIR_HELPER_H
601
602=== added file 'online-accounts-service/online-accounts-service.pro'
603--- online-accounts-service/online-accounts-service.pro 1970-01-01 00:00:00 +0000
604+++ online-accounts-service/online-accounts-service.pro 2014-08-18 13:33:35 +0000
605@@ -0,0 +1,82 @@
606+include(../common-project-config.pri)
607+include($${TOP_SRC_DIR}/common-vars.pri)
608+
609+TEMPLATE = app
610+TARGET = online-accounts-service
611+
612+CONFIG += \
613+ link_pkgconfig \
614+ no_keywords \
615+ qt
616+
617+QT += \
618+ dbus \
619+ network
620+
621+PKGCONFIG += \
622+ libnotify \
623+ libsignon-qt5 \
624+ signon-plugins-common
625+
626+
627+CONFIG(enable-mir) : system(pkg-config --exists mirclient) {
628+ PKGCONFIG += mirclient
629+ SOURCES += mir-helper.cpp
630+} else {
631+ SOURCES += mir-helper-stub.cpp
632+}
633+
634+DBUS_ADAPTORS += \
635+ com.ubuntu.OnlineAccountsUi.xml
636+
637+DEFINES += \
638+ DEBUG_ENABLED \
639+ SIGNONUI_I18N_DOMAIN=\\\"$${SIGNONUI_I18N_DOMAIN}\\\"
640+
641+COMMON_SRC = ../online-accounts-ui
642+
643+INCLUDEPATH += \
644+ $${COMMON_SRC}
645+
646+SOURCES += \
647+ $${COMMON_SRC}/debug.cpp \
648+ $${COMMON_SRC}/i18n.cpp \
649+ $${COMMON_SRC}/ipc.cpp \
650+ $${COMMON_SRC}/notification.cpp \
651+ inactivity-timer.cpp \
652+ indicator-service.cpp \
653+ main.cpp \
654+ reauthenticator.cpp \
655+ request.cpp \
656+ request-manager.cpp \
657+ service.cpp \
658+ signonui-service.cpp \
659+ ui-proxy.cpp
660+
661+HEADERS += \
662+ $${COMMON_SRC}/debug.h \
663+ $${COMMON_SRC}/i18n.h \
664+ $${COMMON_SRC}/ipc.h \
665+ $${COMMON_SRC}/notification.h \
666+ inactivity-timer.h \
667+ indicator-service.h \
668+ mir-helper.h \
669+ reauthenticator.h \
670+ request.h \
671+ request-manager.h \
672+ service.h \
673+ signonui-service.h \
674+ ui-proxy.h
675+
676+QMAKE_SUBSTITUTES += \
677+ com.ubuntu.OnlineAccountsUi.service.in
678+
679+DBUS_ADAPTORS += \
680+ com.canonical.indicators.webcredentials.xml
681+
682+service.path = $${INSTALL_PREFIX}/share/dbus-1/services
683+service.files = \
684+ com.ubuntu.OnlineAccountsUi.service
685+INSTALLS += service
686+
687+include($${TOP_SRC_DIR}/common-installs-config.pri)
688
689=== renamed file 'src/reauthenticator.cpp' => 'online-accounts-service/reauthenticator.cpp'
690=== renamed file 'src/reauthenticator.h' => 'online-accounts-service/reauthenticator.h'
691=== renamed file 'src/request-manager.cpp' => 'online-accounts-service/request-manager.cpp'
692--- src/request-manager.cpp 2014-04-29 12:11:55 +0000
693+++ online-accounts-service/request-manager.cpp 2014-08-18 13:33:35 +0000
694@@ -19,8 +19,10 @@
695 */
696
697 #include "debug.h"
698+#include "globals.h"
699 #include "request.h"
700 #include "request-manager.h"
701+#include "ui-proxy.h"
702
703 #include <QQueue>
704
705@@ -41,17 +43,19 @@
706 RequestManagerPrivate(RequestManager *service);
707 ~RequestManagerPrivate();
708
709- RequestQueue &queueForWindowId(WId windowId);
710+ RequestQueue &queueForWindowId(quint64 windowId);
711 void enqueue(Request *request);
712 void runQueue(RequestQueue &queue);
713
714 private Q_SLOTS:
715 void onRequestCompleted();
716+ void onProxyFinished();
717
718 private:
719 mutable RequestManager *q_ptr;
720 /* each window Id has a different queue */
721- QMap<WId,RequestQueue> m_requests;
722+ QMap<quint64,RequestQueue> m_requests;
723+ QList<UiProxy*> m_proxies;
724 };
725
726 } // namespace
727@@ -66,7 +70,7 @@
728 {
729 }
730
731-RequestQueue &RequestManagerPrivate::queueForWindowId(WId windowId)
732+RequestQueue &RequestManagerPrivate::queueForWindowId(quint64 windowId)
733 {
734 if (!m_requests.contains(windowId)) {
735 RequestQueue queue;
736@@ -78,9 +82,20 @@
737 void RequestManagerPrivate::enqueue(Request *request)
738 {
739 Q_Q(RequestManager);
740+
741+ /* First, see if any of the existing proxies can handle this request */
742+ Q_FOREACH(UiProxy *proxy, m_proxies) {
743+ if (proxy->hasHandlerFor(request->parameters())) {
744+ QObject::connect(request, SIGNAL(completed()),
745+ request, SLOT(deleteLater()));
746+ proxy->handleRequest(request);
747+ return;
748+ }
749+ }
750+
751 bool wasIdle = q->isIdle();
752
753- WId windowId = request->windowId();
754+ quint64 windowId = request->windowId();
755
756 RequestQueue &queue = queueForWindowId(windowId);
757 queue.enqueue(request);
758@@ -102,9 +117,20 @@
759 return; // Nothing to do
760 }
761
762+ pid_t clientPid = (request->interface() == OAU_INTERFACE) ?
763+ request->parameters().value(OAU_KEY_PID, 0).toUInt() : 0;
764+ UiProxy *proxy = new UiProxy(clientPid, this);
765+ if (Q_UNLIKELY(!proxy->init())) {
766+ qWarning() << "UiProxy initialization failed!";
767+ runQueue(queue);
768+ return;
769+ }
770 QObject::connect(request, SIGNAL(completed()),
771 this, SLOT(onRequestCompleted()));
772- request->start();
773+ QObject::connect(proxy, SIGNAL(finished()),
774+ this, SLOT(onProxyFinished()));
775+ m_proxies.append(proxy);
776+ proxy->handleRequest(request);
777 }
778
779 void RequestManagerPrivate::onRequestCompleted()
780@@ -112,7 +138,7 @@
781 Q_Q(RequestManager);
782
783 Request *request = qobject_cast<Request*>(sender());
784- WId windowId = request->windowId();
785+ quint64 windowId = request->windowId();
786
787 RequestQueue &queue = queueForWindowId(windowId);
788 if (request != queue.head()) {
789@@ -135,6 +161,16 @@
790 }
791 }
792
793+void RequestManagerPrivate::onProxyFinished()
794+{
795+ Q_Q(RequestManager);
796+
797+ UiProxy *proxy = qobject_cast<UiProxy*>(sender());
798+ m_proxies.removeOne(proxy);
799+
800+ proxy->deleteLater();
801+}
802+
803 RequestManager::RequestManager(QObject *parent):
804 QObject(parent),
805 d_ptr(new RequestManagerPrivate(this))
806
807=== renamed file 'src/request-manager.h' => 'online-accounts-service/request-manager.h'
808--- src/request-manager.h 2014-04-29 12:11:55 +0000
809+++ online-accounts-service/request-manager.h 2014-08-18 13:33:35 +0000
810@@ -27,8 +27,9 @@
811 namespace OnlineAccountsUi {
812
813 class Request;
814+class UiProxy;
815+
816 class RequestManagerPrivate;
817-
818 class RequestManager: public QObject
819 {
820 Q_OBJECT
821@@ -41,6 +42,7 @@
822 static RequestManager *instance();
823
824 void enqueue(Request *request);
825+ UiProxy *findMatching(const QVariantMap &parameters);
826
827 bool isIdle() const;
828
829
830=== added file 'online-accounts-service/request.cpp'
831--- online-accounts-service/request.cpp 1970-01-01 00:00:00 +0000
832+++ online-accounts-service/request.cpp 2014-08-18 13:33:35 +0000
833@@ -0,0 +1,214 @@
834+/*
835+ * Copyright (C) 2013 Canonical Ltd.
836+ *
837+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
838+ *
839+ * This file is part of online-accounts-ui
840+ *
841+ * This program is free software: you can redistribute it and/or modify it
842+ * under the terms of the GNU General Public License version 3, as published
843+ * by the Free Software Foundation.
844+ *
845+ * This program is distributed in the hope that it will be useful, but
846+ * WITHOUT ANY WARRANTY; without even the implied warranties of
847+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
848+ * PURPOSE. See the GNU General Public License for more details.
849+ *
850+ * You should have received a copy of the GNU General Public License along
851+ * with this program. If not, see <http://www.gnu.org/licenses/>.
852+ */
853+
854+#include "debug.h"
855+#include "globals.h"
856+#include "request.h"
857+
858+using namespace OnlineAccountsUi;
859+
860+static bool mapIsSuperset(const QVariantMap &test, const QVariantMap &set)
861+{
862+ QMapIterator<QString, QVariant> it(set);
863+ while (it.hasNext()) {
864+ it.next();
865+ if (test.value(it.key()) != it.value()) return false;
866+ }
867+
868+ return true;
869+}
870+
871+namespace OnlineAccountsUi {
872+
873+static QList<Request *> allRequests;
874+
875+class RequestPrivate: public QObject
876+{
877+ Q_OBJECT
878+ Q_DECLARE_PUBLIC(Request)
879+
880+public:
881+ RequestPrivate(const QDBusConnection &connection,
882+ const QDBusMessage &message,
883+ const QVariantMap &parameters,
884+ Request *request);
885+ ~RequestPrivate();
886+
887+ quint64 windowId() const {
888+ return m_parameters[OAU_KEY_WINDOW_ID].toUInt();
889+ }
890+
891+private:
892+ QString findClientApparmorProfile();
893+
894+private:
895+ mutable Request *q_ptr;
896+ QDBusConnection m_connection;
897+ QDBusMessage m_message;
898+ QVariantMap m_parameters;
899+ QString m_clientApparmorProfile;
900+ bool m_inProgress;
901+};
902+
903+} // namespace
904+
905+RequestPrivate::RequestPrivate(const QDBusConnection &connection,
906+ const QDBusMessage &message,
907+ const QVariantMap &parameters,
908+ Request *request):
909+ QObject(request),
910+ q_ptr(request),
911+ m_connection(connection),
912+ m_message(message),
913+ m_parameters(parameters),
914+ m_inProgress(false)
915+{
916+ m_clientApparmorProfile = findClientApparmorProfile();
917+}
918+
919+RequestPrivate::~RequestPrivate()
920+{
921+}
922+
923+QString RequestPrivate::findClientApparmorProfile()
924+{
925+ QString uniqueConnectionId = m_message.service();
926+ /* This is mainly for unit tests: real messages on the session bus always
927+ * have a service name. */
928+ if (uniqueConnectionId.isEmpty()) return QString();
929+
930+ QString appId;
931+
932+ QDBusMessage msg =
933+ QDBusMessage::createMethodCall("org.freedesktop.DBus",
934+ "/org/freedesktop/DBus",
935+ "org.freedesktop.DBus",
936+ "GetConnectionAppArmorSecurityContext");
937+ QVariantList args;
938+ args << uniqueConnectionId;
939+ msg.setArguments(args);
940+ QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block);
941+ if (reply.type() == QDBusMessage::ReplyMessage) {
942+ appId = reply.arguments().value(0, QString()).toString();
943+ DEBUG() << "App ID:" << appId;
944+ } else {
945+ qWarning() << "Error getting app ID:" << reply.errorName() <<
946+ reply.errorMessage();
947+ }
948+ return appId;
949+}
950+
951+Request::Request(const QDBusConnection &connection,
952+ const QDBusMessage &message,
953+ const QVariantMap &parameters,
954+ QObject *parent):
955+ QObject(parent),
956+ d_ptr(new RequestPrivate(connection, message, parameters, this))
957+{
958+ allRequests.append(this);
959+}
960+
961+Request::~Request()
962+{
963+ allRequests.removeOne(this);
964+}
965+
966+Request *Request::find(const QVariantMap &match)
967+{
968+ Q_FOREACH(Request *r, allRequests) {
969+ if (mapIsSuperset(r->parameters(), match)) {
970+ return r;
971+ }
972+ }
973+
974+ return 0;
975+}
976+
977+quint64 Request::windowId() const
978+{
979+ Q_D(const Request);
980+ return d->windowId();
981+}
982+
983+void Request::setInProgress(bool inProgress)
984+{
985+ Q_D(Request);
986+ d->m_inProgress = inProgress;
987+}
988+
989+bool Request::isInProgress() const
990+{
991+ Q_D(const Request);
992+ return d->m_inProgress;
993+}
994+
995+const QVariantMap &Request::parameters() const
996+{
997+ Q_D(const Request);
998+ return d->m_parameters;
999+}
1000+
1001+QString Request::clientApparmorProfile() const
1002+{
1003+ Q_D(const Request);
1004+ return d->m_clientApparmorProfile;
1005+}
1006+
1007+QString Request::interface() const {
1008+ Q_D(const Request);
1009+ return d->m_message.interface();
1010+}
1011+
1012+void Request::cancel()
1013+{
1014+ setCanceled();
1015+}
1016+
1017+void Request::fail(const QString &name, const QString &message)
1018+{
1019+ Q_D(Request);
1020+ QDBusMessage reply = d->m_message.createErrorReply(name, message);
1021+ d->m_connection.send(reply);
1022+
1023+ Q_EMIT completed();
1024+}
1025+
1026+void Request::setCanceled()
1027+{
1028+ Q_D(Request);
1029+ if (d->m_inProgress) {
1030+ fail(OAU_ERROR_USER_CANCELED, QStringLiteral("Canceled"));
1031+ d->m_inProgress = false;
1032+ }
1033+}
1034+
1035+void Request::setResult(const QVariantMap &result)
1036+{
1037+ Q_D(Request);
1038+ if (d->m_inProgress) {
1039+ QDBusMessage reply = d->m_message.createReply(result);
1040+ d->m_connection.send(reply);
1041+
1042+ Q_EMIT completed();
1043+ d->m_inProgress = false;
1044+ }
1045+}
1046+
1047+#include "request.moc"
1048
1049=== added file 'online-accounts-service/request.h'
1050--- online-accounts-service/request.h 1970-01-01 00:00:00 +0000
1051+++ online-accounts-service/request.h 2014-08-18 13:33:35 +0000
1052@@ -0,0 +1,70 @@
1053+/*
1054+ * Copyright (C) 2013 Canonical Ltd.
1055+ *
1056+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1057+ *
1058+ * This file is part of online-accounts-ui
1059+ *
1060+ * This program is free software: you can redistribute it and/or modify it
1061+ * under the terms of the GNU General Public License version 3, as published
1062+ * by the Free Software Foundation.
1063+ *
1064+ * This program is distributed in the hope that it will be useful, but
1065+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1066+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1067+ * PURPOSE. See the GNU General Public License for more details.
1068+ *
1069+ * You should have received a copy of the GNU General Public License along
1070+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1071+ */
1072+
1073+#ifndef OAU_REQUEST_H
1074+#define OAU_REQUEST_H
1075+
1076+#include <QDBusConnection>
1077+#include <QDBusMessage>
1078+#include <QObject>
1079+#include <QVariantMap>
1080+
1081+namespace OnlineAccountsUi {
1082+
1083+class RequestPrivate;
1084+class Request: public QObject
1085+{
1086+ Q_OBJECT
1087+
1088+public:
1089+ explicit Request(const QDBusConnection &connection,
1090+ const QDBusMessage &message,
1091+ const QVariantMap &parameters,
1092+ QObject *parent = 0);
1093+ ~Request();
1094+
1095+ static Request *find(const QVariantMap &match);
1096+
1097+ quint64 windowId() const;
1098+ void setInProgress(bool inProgress);
1099+ bool isInProgress() const;
1100+ const QVariantMap &parameters() const;
1101+ QString clientApparmorProfile() const;
1102+ QString interface() const;
1103+
1104+public Q_SLOTS:
1105+ void cancel();
1106+
1107+Q_SIGNALS:
1108+ void completed();
1109+
1110+public Q_SLOTS:
1111+ void fail(const QString &name, const QString &message);
1112+ void setCanceled();
1113+ void setResult(const QVariantMap &result);
1114+
1115+private:
1116+ RequestPrivate *d_ptr;
1117+ Q_DECLARE_PRIVATE(Request)
1118+};
1119+
1120+} // namespace
1121+
1122+#endif // OAU_REQUEST_H
1123
1124=== renamed file 'src/service.cpp' => 'online-accounts-service/service.cpp'
1125--- src/service.cpp 2014-05-30 08:09:02 +0000
1126+++ online-accounts-service/service.cpp 2014-08-18 13:33:35 +0000
1127@@ -44,17 +44,9 @@
1128 /* The following line tells QtDBus not to generate a reply now */
1129 setDelayedReply(true);
1130
1131- Request *request = Request::newRequest(connection(),
1132- message(),
1133- options,
1134- this);
1135- if (request) {
1136- RequestManager *manager = RequestManager::instance();
1137- manager->enqueue(request);
1138- } else {
1139- sendErrorReply(OAU_ERROR_INVALID_PARAMETERS,
1140- QStringLiteral("Invalid request"));
1141- }
1142+ Request *request = new Request(connection(), message(), options, this);
1143+ RequestManager *manager = RequestManager::instance();
1144+ manager->enqueue(request);
1145
1146 return QVariantMap();
1147 }
1148
1149=== renamed file 'src/service.h' => 'online-accounts-service/service.h'
1150=== renamed file 'src/signonui-service.cpp' => 'online-accounts-service/signonui-service.cpp'
1151--- src/signonui-service.cpp 2014-04-29 14:34:44 +0000
1152+++ online-accounts-service/signonui-service.cpp 2014-08-18 13:33:35 +0000
1153@@ -20,14 +20,12 @@
1154
1155 #include "debug.h"
1156 #include "request.h"
1157-#include "request-handler.h"
1158 #include "request-manager.h"
1159-#include "signonui-request.h"
1160 #include "signonui-service.h"
1161-#include "browser-request.h"
1162
1163 #include <QDBusArgument>
1164-#include <QtQml>
1165+#include <QDir>
1166+#include <QStandardPaths>
1167 #include <SignOn/uisessiondata_priv.h>
1168
1169 using namespace SignOnUi;
1170@@ -77,6 +75,8 @@
1171 void cancelUiRequest(const QString &requestId);
1172 void removeIdentityData(quint32 id);
1173
1174+ static QString rootDirForIdentity(quint32 id);
1175+
1176 private:
1177 mutable Service *q_ptr;
1178 };
1179@@ -106,20 +106,24 @@
1180 }
1181 }
1182
1183+QString ServicePrivate::rootDirForIdentity(quint32 id)
1184+{
1185+ QString cachePath =
1186+ QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
1187+ return cachePath + QString("/id-%1").arg(id);
1188+}
1189+
1190 void ServicePrivate::removeIdentityData(quint32 id)
1191 {
1192 /* Remove any data associated with the given identity. */
1193-
1194- /* The BrowserRequest class creates a directory to store the cookies */
1195- BrowserRequest::removeIdentityData(id);
1196+ QDir rootDir(ServicePrivate::rootDirForIdentity(id));
1197+ rootDir.removeRecursively();
1198 }
1199
1200 Service::Service(QObject *parent):
1201 QObject(parent),
1202 d_ptr(new ServicePrivate(this))
1203 {
1204- qmlRegisterType<SignOnUi::RequestHandler>("Ubuntu.OnlineAccounts.Plugin",
1205- 1, 0, "RequestHandler");
1206 }
1207
1208 Service::~Service()
1209@@ -135,29 +139,12 @@
1210 setDelayedReply(true);
1211
1212 OnlineAccountsUi::Request *request =
1213- OnlineAccountsUi::Request::newRequest(connection(),
1214- message(),
1215- cleanParameters,
1216- this);
1217-
1218- /* Check if a RequestHandler has been setup to handle this request. If
1219- * so, bing the request object to the handler and start the request
1220- * immediately. */
1221- SignOnUi::Request *signonRequest =
1222- qobject_cast<SignOnUi::Request*>(request);
1223- Q_ASSERT(signonRequest != 0);
1224-
1225- RequestHandler *handler = RequestHandler::findMatching(cleanParameters);
1226- if (handler != 0) {
1227- DEBUG() << "Found RequestHandler!";
1228- signonRequest->setHandler(handler);
1229- QObject::connect(signonRequest, SIGNAL(completed()),
1230- signonRequest, SLOT(deleteLater()));
1231- signonRequest->start();
1232- } else {
1233- // proceed normally
1234- OnlineAccountsUi::RequestManager::instance()->enqueue(request);
1235- }
1236+ new OnlineAccountsUi::Request(connection(),
1237+ message(),
1238+ cleanParameters,
1239+ this);
1240+
1241+ OnlineAccountsUi::RequestManager::instance()->enqueue(request);
1242
1243 return QVariantMap();
1244 }
1245@@ -165,7 +152,6 @@
1246 QVariantMap Service::refreshDialog(const QVariantMap &newParameters)
1247 {
1248 QVariantMap cleanParameters = expandDBusArguments(newParameters);
1249- QString requestId = Request::id(cleanParameters);
1250 // TODO find the request and update it
1251
1252 /* The following line tells QtDBus not to generate a reply now */
1253
1254=== renamed file 'src/signonui-service.h' => 'online-accounts-service/signonui-service.h'
1255=== added file 'online-accounts-service/ui-proxy.cpp'
1256--- online-accounts-service/ui-proxy.cpp 1970-01-01 00:00:00 +0000
1257+++ online-accounts-service/ui-proxy.cpp 2014-08-18 13:33:35 +0000
1258@@ -0,0 +1,301 @@
1259+/*
1260+ * Copyright (C) 2014 Canonical Ltd.
1261+ *
1262+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1263+ *
1264+ * This file is part of online-accounts-ui
1265+ *
1266+ * This program is free software: you can redistribute it and/or modify it
1267+ * under the terms of the GNU General Public License version 3, as published
1268+ * by the Free Software Foundation.
1269+ *
1270+ * This program is distributed in the hope that it will be useful, but
1271+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1272+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1273+ * PURPOSE. See the GNU General Public License for more details.
1274+ *
1275+ * You should have received a copy of the GNU General Public License along
1276+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1277+ */
1278+
1279+#include "debug.h"
1280+#include "ipc.h"
1281+#include "mir-helper.h"
1282+#include "request.h"
1283+#include "ui-proxy.h"
1284+
1285+#include <QDir>
1286+#include <QFileInfo>
1287+#include <QLocalServer>
1288+#include <QLocalSocket>
1289+#include <QProcess>
1290+#include <QProcessEnvironment>
1291+#include <QStandardPaths>
1292+#include <SignOn/uisessiondata_priv.h>
1293+
1294+using namespace OnlineAccountsUi;
1295+
1296+static int socketCounter = 1;
1297+
1298+namespace OnlineAccountsUi {
1299+
1300+class UiProxyPrivate: public QObject
1301+{
1302+ Q_OBJECT
1303+ Q_DECLARE_PUBLIC(UiProxy)
1304+
1305+public:
1306+ inline UiProxyPrivate(pid_t clientPid, UiProxy *pluginProxy);
1307+ inline ~UiProxyPrivate();
1308+
1309+ bool setupSocket();
1310+ bool init();
1311+ void sendOperation(const QVariantMap &data);
1312+ void sendRequest(int requestId, Request *request);
1313+ void setupPromptSession();
1314+
1315+private Q_SLOTS:
1316+ void onNewConnection();
1317+ void onDataReady(QByteArray &data);
1318+ void onRequestCompleted();
1319+
1320+private:
1321+ QProcess m_process;
1322+ QLocalServer m_server;
1323+ QLocalSocket *m_socket;
1324+ OnlineAccountsUi::Ipc m_ipc;
1325+ int m_nextRequestId;
1326+ QMap<int,Request*> m_requests;
1327+ QStringList m_handlers;
1328+ pid_t m_clientPid;
1329+ PromptSession *m_promptSession;
1330+ mutable UiProxy *q_ptr;
1331+};
1332+
1333+} // namespace
1334+
1335+UiProxyPrivate::UiProxyPrivate(pid_t clientPid, UiProxy *uiProxy):
1336+ QObject(uiProxy),
1337+ m_socket(0),
1338+ m_nextRequestId(0),
1339+ m_clientPid(clientPid),
1340+ m_promptSession(0),
1341+ q_ptr(uiProxy)
1342+{
1343+ QObject::connect(&m_server, SIGNAL(newConnection()),
1344+ this, SLOT(onNewConnection()));
1345+ QObject::connect(&m_ipc, SIGNAL(dataReady(QByteArray &)),
1346+ this, SLOT(onDataReady(QByteArray &)));
1347+ m_process.setProcessChannelMode(QProcess::ForwardedChannels);
1348+}
1349+
1350+UiProxyPrivate::~UiProxyPrivate()
1351+{
1352+ /* Cancel all requests */
1353+ Q_FOREACH(Request *request, m_requests) {
1354+ request->cancel();
1355+ }
1356+
1357+ delete m_promptSession;
1358+
1359+ if (m_socket) {
1360+ m_socket->abort();
1361+ delete m_socket;
1362+ }
1363+ m_server.close();
1364+}
1365+
1366+void UiProxyPrivate::sendOperation(const QVariantMap &data)
1367+{
1368+ QByteArray ba;
1369+ QDataStream stream(&ba, QIODevice::WriteOnly);
1370+ stream << data;
1371+ m_ipc.write(ba);
1372+}
1373+
1374+void UiProxyPrivate::onNewConnection()
1375+{
1376+ Q_Q(UiProxy);
1377+
1378+ QLocalSocket *socket = m_server.nextPendingConnection();
1379+ if (Q_UNLIKELY(socket == 0)) return;
1380+
1381+ if (Q_UNLIKELY(m_socket != 0)) {
1382+ qWarning() << "A socket is already active!";
1383+ socket->deleteLater();
1384+ return;
1385+ }
1386+
1387+ m_socket = socket;
1388+ QObject::connect(socket, SIGNAL(disconnected()),
1389+ q, SIGNAL(finished()));
1390+ m_ipc.setChannels(socket, socket);
1391+ m_server.close(); // stop listening
1392+
1393+ /* Execute any pending requests */
1394+ QMapIterator<int, Request*> it(m_requests);
1395+ while (it.hasNext()) {
1396+ it.next();
1397+ sendRequest(it.key(), it.value());
1398+ }
1399+}
1400+
1401+void UiProxyPrivate::onDataReady(QByteArray &data)
1402+{
1403+ Q_Q(UiProxy);
1404+
1405+ QVariantMap map;
1406+ QDataStream stream(&data, QIODevice::ReadOnly);
1407+ stream >> map;
1408+
1409+ DEBUG() << map;
1410+
1411+ int requestId = map.value(OAU_OPERATION_ID, -1).toInt();
1412+ Request *request = m_requests.value(requestId, 0);
1413+
1414+ QString code = map.value(OAU_OPERATION_CODE).toString();
1415+ if (code == OAU_OPERATION_CODE_REQUEST_FINISHED) {
1416+ Q_ASSERT(request);
1417+ request->setResult(map.value(OAU_OPERATION_DATA).toMap());
1418+ } else if (code == OAU_OPERATION_CODE_REQUEST_FAILED) {
1419+ Q_ASSERT(request);
1420+ request->fail(map.value(OAU_OPERATION_ERROR_NAME).toString(),
1421+ map.value(OAU_OPERATION_ERROR_MESSAGE).toString());
1422+ } else if (code == OAU_OPERATION_CODE_REGISTER_HANDLER) {
1423+ m_handlers.append(map.value(OAU_OPERATION_HANDLER_ID).toString());
1424+ } else {
1425+ qWarning() << "Invalid operation code: " << code;
1426+ }
1427+}
1428+
1429+bool UiProxyPrivate::setupSocket()
1430+{
1431+ QString runtimeDir =
1432+ QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
1433+ QDir socketDir(runtimeDir + "/online-accounts-ui");
1434+ if (!socketDir.exists()) socketDir.mkpath(".");
1435+
1436+ QString uniqueName = QString("ui-%1").arg(socketCounter++);
1437+
1438+ /* If the file exists, it's a stale file: online-accounts-ui is a single
1439+ * instance process, and the only one creating files in this directory. */
1440+ if (Q_UNLIKELY(socketDir.exists(uniqueName))) {
1441+ socketDir.remove(uniqueName);
1442+ }
1443+
1444+ /* Create the socket and set it into listen mode */
1445+ return m_server.listen(socketDir.filePath(uniqueName));
1446+}
1447+
1448+void UiProxyPrivate::setupPromptSession()
1449+{
1450+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
1451+ if (!env.value("QT_QPA_PLATFORM").startsWith("ubuntu")) return;
1452+
1453+ PromptSession *session =
1454+ MirHelper::instance()->createPromptSession(m_clientPid);
1455+ if (!session) return;
1456+
1457+ QString mirSocket = session->requestSocket();
1458+ if (mirSocket.isEmpty()) {
1459+ delete session;
1460+ return;
1461+ }
1462+
1463+ m_promptSession = session;
1464+
1465+ env.insert("MIR_SOCKET", mirSocket);
1466+ m_process.setProcessEnvironment(env);
1467+}
1468+
1469+bool UiProxyPrivate::init()
1470+{
1471+ if (Q_UNLIKELY(!setupSocket())) return false;
1472+
1473+ QStringList arguments;
1474+ /* the first argument is required to be the desktop file */
1475+ arguments.append("--desktop_file_hint=/usr/share/applications/online-accounts-ui.desktop");
1476+ arguments.append("--socket");
1477+ arguments.append(m_server.fullServerName());
1478+
1479+ if (m_clientPid) {
1480+ setupPromptSession();
1481+ }
1482+
1483+ m_process.start("/usr/bin/online-accounts-ui", arguments);
1484+ return m_process.waitForStarted();
1485+}
1486+
1487+void UiProxyPrivate::sendRequest(int requestId, Request *request)
1488+{
1489+ QVariantMap operation;
1490+ operation.insert(OAU_OPERATION_CODE, OAU_OPERATION_CODE_PROCESS);
1491+ operation.insert(OAU_OPERATION_ID, requestId);
1492+ operation.insert(OAU_OPERATION_DATA, request->parameters());
1493+ operation.insert(OAU_OPERATION_INTERFACE, request->interface());
1494+ operation.insert(OAU_OPERATION_CLIENT_PROFILE,
1495+ request->clientApparmorProfile());
1496+ sendOperation(operation);
1497+}
1498+
1499+void UiProxyPrivate::onRequestCompleted()
1500+{
1501+ Request *request = qobject_cast<Request*>(sender());
1502+ int id = m_requests.key(request, -1);
1503+ if (id != -1) {
1504+ m_requests.remove(id);
1505+ }
1506+}
1507+
1508+UiProxy::UiProxy(pid_t clientPid, QObject *parent):
1509+ QObject(parent),
1510+ d_ptr(new UiProxyPrivate(clientPid, this))
1511+{
1512+}
1513+
1514+UiProxy::~UiProxy()
1515+{
1516+}
1517+
1518+bool UiProxy::init()
1519+{
1520+ Q_D(UiProxy);
1521+ return d->init();
1522+}
1523+
1524+void UiProxy::handleRequest(Request *request)
1525+{
1526+ Q_D(UiProxy);
1527+
1528+ int requestId = d->m_nextRequestId++;
1529+ d->m_requests.insert(requestId, request);
1530+ QObject::connect(request, SIGNAL(completed()),
1531+ d, SLOT(onRequestCompleted()));
1532+ request->setInProgress(true);
1533+
1534+ if (d->m_socket && d->m_socket->isValid()) {
1535+ d->sendRequest(requestId, request);
1536+ }
1537+}
1538+
1539+bool UiProxy::hasHandlerFor(const QVariantMap &parameters)
1540+{
1541+ Q_D(UiProxy);
1542+
1543+ /* Find if there's any handlers expecting to handle the SignOnUi
1544+ * request having "parameters" as parameters.
1545+ * This is simply done by matching on the X-RequestHandler key (aka
1546+ * matchKey()), if present. We expect that account plugins add that field
1547+ * to their AuthSession requests which they want to handle themselves.
1548+ */
1549+ DEBUG() << parameters;
1550+ if (!parameters.contains(SSOUI_KEY_CLIENT_DATA)) return false;
1551+ QVariant variant = parameters[SSOUI_KEY_CLIENT_DATA];
1552+ QVariantMap clientData = variant.toMap();
1553+ QString matchId = clientData.value(OAU_REQUEST_MATCH_KEY).toString();
1554+ if (matchId.isEmpty()) return false;
1555+
1556+ return d->m_handlers.contains(matchId);
1557+}
1558+
1559+#include "ui-proxy.moc"
1560
1561=== added file 'online-accounts-service/ui-proxy.h'
1562--- online-accounts-service/ui-proxy.h 1970-01-01 00:00:00 +0000
1563+++ online-accounts-service/ui-proxy.h 2014-08-18 13:33:35 +0000
1564@@ -0,0 +1,54 @@
1565+/*
1566+ * Copyright (C) 2014 Canonical Ltd.
1567+ *
1568+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1569+ *
1570+ * This file is part of online-accounts-ui
1571+ *
1572+ * This program is free software: you can redistribute it and/or modify it
1573+ * under the terms of the GNU General Public License version 3, as published
1574+ * by the Free Software Foundation.
1575+ *
1576+ * This program is distributed in the hope that it will be useful, but
1577+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1578+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1579+ * PURPOSE. See the GNU General Public License for more details.
1580+ *
1581+ * You should have received a copy of the GNU General Public License along
1582+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1583+ */
1584+
1585+#ifndef OAU_UI_PROXY_H
1586+#define OAU_UI_PROXY_H
1587+
1588+#include <QObject>
1589+#include <QVariantMap>
1590+
1591+namespace OnlineAccountsUi {
1592+
1593+class Request;
1594+
1595+class UiProxyPrivate;
1596+class UiProxy: public QObject
1597+{
1598+ Q_OBJECT
1599+
1600+public:
1601+ explicit UiProxy(pid_t clientPid, QObject *parent = 0);
1602+ ~UiProxy();
1603+
1604+ bool init();
1605+ void handleRequest(Request *request);
1606+ bool hasHandlerFor(const QVariantMap &parameters);
1607+
1608+Q_SIGNALS:
1609+ void finished();
1610+
1611+private:
1612+ UiProxyPrivate *d_ptr;
1613+ Q_DECLARE_PRIVATE(UiProxy)
1614+};
1615+
1616+} // namespace
1617+
1618+#endif // OAU_UI_PROXY_H
1619
1620=== renamed directory 'src' => 'online-accounts-ui'
1621=== modified file 'online-accounts-ui/application-manager.cpp'
1622--- src/application-manager.cpp 2014-06-06 07:19:34 +0000
1623+++ online-accounts-ui/application-manager.cpp 2014-08-18 13:33:35 +0000
1624@@ -130,6 +130,16 @@
1625
1626 if (Q_UNLIKELY(profile.isEmpty())) return QVariantMap();
1627
1628+ /* Special case: when the applicationId is "system-settings", we don't
1629+ * require the existance of the .application file, because this request
1630+ * will always be about creating a new account. */
1631+ if (claimedAppId == "system-settings") {
1632+ QVariantMap app;
1633+ app.insert(QStringLiteral("id"), claimedAppId);
1634+ app.insert(QStringLiteral("profile"), profile);
1635+ return app;
1636+ }
1637+
1638 QString applicationId = claimedAppId;
1639 Accounts::Application application =
1640 AccountManager::instance()->application(applicationId);
1641
1642=== modified file 'online-accounts-ui/browser-request.cpp'
1643--- src/browser-request.cpp 2014-04-29 14:34:44 +0000
1644+++ online-accounts-ui/browser-request.cpp 2014-08-18 13:33:35 +0000
1645@@ -28,6 +28,7 @@
1646
1647 #include <QDir>
1648 #include <QQmlContext>
1649+#include <QQmlEngine>
1650 #include <QStandardPaths>
1651 #include <QTimer>
1652 #include <SignOn/uisessiondata_priv.h>
1653@@ -101,6 +102,7 @@
1654
1655 BrowserRequestPrivate::~BrowserRequestPrivate()
1656 {
1657+ DEBUG();
1658 closeView();
1659 delete m_dialog;
1660 }
1661@@ -133,18 +135,9 @@
1662 QObject::connect(m_dialog, SIGNAL(finished(int)),
1663 this, SLOT(onFinished()));
1664
1665- QUrl webview("qrc:/MainWindow.qml");
1666- QDir qmlDir("/usr/share/signon-ui/qml");
1667- if (qmlDir.exists())
1668- {
1669- QFileInfo qmlFile(qmlDir.absolutePath() + "/MainWindow.qml");
1670- if (qmlFile.exists())
1671- webview.setUrl(qmlFile.absoluteFilePath());
1672- }
1673-
1674+ m_dialog->engine()->addImportPath(PLUGIN_PRIVATE_MODULE_DIR);
1675 m_dialog->rootContext()->setContextProperty("request", this);
1676- m_dialog->rootContext()->setContextProperty("rootDir", m_rootDir);
1677- m_dialog->setSource(webview);
1678+ m_dialog->setSource(QUrl("qrc:/qml/SignOnUiPage.qml"));
1679 } else {
1680 DEBUG() << "Setting request on handler";
1681 q->handler()->setRequest(this);
1682@@ -187,6 +180,7 @@
1683 m_dialog->accept();
1684 }
1685 } else {
1686+ DEBUG();
1687 onFinished();
1688 }
1689 }
1690@@ -208,7 +202,7 @@
1691
1692 void BrowserRequestPrivate::onLoadFinished(bool ok)
1693 {
1694- Q_Q(const BrowserRequest);
1695+ Q_Q(BrowserRequest);
1696
1697 DEBUG() << "Load finished" << ok;
1698
1699@@ -219,9 +213,7 @@
1700
1701 if (m_dialog && !m_dialog->isVisible()) {
1702 if (m_responseUrl.isEmpty()) {
1703- Dialog::ShowMode mode =
1704- (q->windowId() == 0) ? Dialog::TopLevel : Dialog::Transient;
1705- m_dialog->show(q->windowId(), mode);
1706+ q->setWindow(m_dialog);
1707 } else {
1708 onFinished();
1709 }
1710@@ -277,6 +269,7 @@
1711 {
1712 Q_Q(BrowserRequest);
1713
1714+ DEBUG();
1715 if (q->hasHandler()) {
1716 q->handler()->setRequest(0);
1717 } else if (m_dialog) {
1718@@ -284,23 +277,18 @@
1719 }
1720 }
1721
1722-BrowserRequest::BrowserRequest(const QDBusConnection &connection,
1723- const QDBusMessage &message,
1724+BrowserRequest::BrowserRequest(int id,
1725+ const QString &clientProfile,
1726 const QVariantMap &parameters,
1727 QObject *parent):
1728- Request(connection, message, parameters, parent),
1729+ Request(id, clientProfile, parameters, parent),
1730 d_ptr(new BrowserRequestPrivate(this))
1731 {
1732 }
1733
1734 BrowserRequest::~BrowserRequest()
1735 {
1736-}
1737-
1738-void BrowserRequest::removeIdentityData(quint32 id)
1739-{
1740- QDir rootDir(BrowserRequestPrivate::rootDirForIdentity(id));
1741- rootDir.removeRecursively();
1742+ delete d_ptr;
1743 }
1744
1745 void BrowserRequest::start()
1746
1747=== modified file 'online-accounts-ui/browser-request.h'
1748--- src/browser-request.h 2014-04-29 14:34:44 +0000
1749+++ online-accounts-ui/browser-request.h 2014-08-18 13:33:35 +0000
1750@@ -34,14 +34,12 @@
1751 Q_OBJECT
1752
1753 public:
1754- explicit BrowserRequest(const QDBusConnection &connection,
1755- const QDBusMessage &message,
1756+ explicit BrowserRequest(int id,
1757+ const QString &clientProfile,
1758 const QVariantMap &parameters,
1759 QObject *parent = 0);
1760 ~BrowserRequest();
1761
1762- static void removeIdentityData(quint32 id);
1763-
1764 // reimplemented virtual methods
1765 void start();
1766
1767
1768=== modified file 'online-accounts-ui/globals.h'
1769--- src/globals.h 2014-04-29 12:11:55 +0000
1770+++ online-accounts-ui/globals.h 2014-08-18 13:33:35 +0000
1771@@ -29,6 +29,7 @@
1772 #define OAU_KEY_PROVIDER QStringLiteral("provider")
1773 #define OAU_KEY_SERVICE_TYPE QStringLiteral("serviceType")
1774 #define OAU_KEY_WINDOW_ID QStringLiteral("windowId")
1775+#define OAU_KEY_PID QStringLiteral("pid")
1776
1777 // D-Bus error names
1778 #define OAU_ERROR_PREFIX "com.ubuntu.OnlineAccountsUi."
1779
1780=== added file 'online-accounts-ui/ipc.cpp'
1781--- online-accounts-ui/ipc.cpp 1970-01-01 00:00:00 +0000
1782+++ online-accounts-ui/ipc.cpp 2014-08-18 13:33:35 +0000
1783@@ -0,0 +1,184 @@
1784+/*
1785+ * Copyright (C) 2014 Canonical Ltd.
1786+ *
1787+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1788+ *
1789+ * This file is part of online-accounts-ui
1790+ *
1791+ * This program is free software: you can redistribute it and/or modify it
1792+ * under the terms of the GNU General Public License version 3, as published
1793+ * by the Free Software Foundation.
1794+ *
1795+ * This program is distributed in the hope that it will be useful, but
1796+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1797+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1798+ * PURPOSE. See the GNU General Public License for more details.
1799+ *
1800+ * You should have received a copy of the GNU General Public License along
1801+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1802+ */
1803+
1804+#include "ipc.h"
1805+
1806+#include <QByteArray>
1807+#include <QFile>
1808+#include <QIODevice>
1809+#include <QSocketNotifier>
1810+
1811+using namespace OnlineAccountsUi;
1812+
1813+static const QByteArray welcomeMessage = "OAUinitIPC";
1814+
1815+namespace OnlineAccountsUi {
1816+
1817+class IpcPrivate: public QObject
1818+{
1819+ Q_OBJECT
1820+ Q_DECLARE_PUBLIC(Ipc)
1821+
1822+public:
1823+ inline IpcPrivate(Ipc *ipc);
1824+ ~IpcPrivate() {};
1825+
1826+ void setChannels(QIODevice *readChannel, QIODevice *writeChannel);
1827+
1828+private Q_SLOTS:
1829+ void onReadyRead();
1830+
1831+private:
1832+ bool waitWelcomeMessage();
1833+
1834+private:
1835+ QIODevice *m_readChannel;
1836+ QIODevice *m_writeChannel;
1837+ int m_expectedLength;
1838+ bool m_gotWelcomeMessage;
1839+ QByteArray m_readBuffer;
1840+ mutable Ipc *q_ptr;
1841+};
1842+
1843+}; // namespace
1844+
1845+IpcPrivate::IpcPrivate(Ipc *ipc):
1846+ QObject(ipc),
1847+ m_readChannel(0),
1848+ m_writeChannel(0),
1849+ m_expectedLength(0),
1850+ m_gotWelcomeMessage(false),
1851+ q_ptr(ipc)
1852+{
1853+}
1854+
1855+void IpcPrivate::setChannels(QIODevice *readChannel, QIODevice *writeChannel)
1856+{
1857+ m_readChannel = readChannel;
1858+ m_writeChannel = writeChannel;
1859+ QObject::connect(m_readChannel, SIGNAL(readyRead()),
1860+ this, SLOT(onReadyRead()));
1861+ /* QFile need special handling */
1862+ QFile *file = qobject_cast<QFile*>(m_readChannel);
1863+ if (file != 0) {
1864+ QSocketNotifier *notifier = new QSocketNotifier(file->handle(),
1865+ QSocketNotifier::Read,
1866+ this);
1867+ QObject::connect(notifier, SIGNAL(activated(int)),
1868+ this, SLOT(onReadyRead()));
1869+ } else {
1870+ /* If the read channel is not a QFile, it means it's not the standard
1871+ * input, therefore we won't have the need to wait for the welcome
1872+ * message. */
1873+ m_gotWelcomeMessage = true;
1874+ }
1875+ onReadyRead();
1876+
1877+ file = qobject_cast<QFile*>(m_writeChannel);
1878+ if (file != 0) {
1879+ m_writeChannel->write(welcomeMessage);
1880+ }
1881+}
1882+
1883+void IpcPrivate::onReadyRead()
1884+{
1885+ Q_Q(Ipc);
1886+
1887+ while (true) {
1888+ if (m_expectedLength == 0) {
1889+ /* We are beginning a new read */
1890+
1891+ /* skip all noise */
1892+ if (!waitWelcomeMessage()) break;
1893+
1894+ int length;
1895+ int bytesRead = m_readChannel->read((char *)&length,
1896+ sizeof(length));
1897+ if (bytesRead < int(sizeof(length))) break;
1898+ m_expectedLength = length;
1899+ m_readBuffer.clear();
1900+ }
1901+
1902+ int neededBytes = m_expectedLength - m_readBuffer.length();
1903+ QByteArray buffer = m_readChannel->read(neededBytes);
1904+ m_readBuffer += buffer;
1905+ if (buffer.length() < neededBytes) break;
1906+ if (m_readBuffer.length() == m_expectedLength) {
1907+ Q_EMIT q->dataReady(m_readBuffer);
1908+ m_expectedLength = 0;
1909+ }
1910+ }
1911+}
1912+
1913+bool IpcPrivate::waitWelcomeMessage()
1914+{
1915+ if (m_gotWelcomeMessage) return true;
1916+
1917+ /* All Qt applications on the Nexus 4 write some dust to stdout when
1918+ * starting. So, skip all input until a well-defined welcome message is
1919+ * found */
1920+
1921+ QByteArray buffer;
1922+ int startCheckIndex = 0;
1923+ do {
1924+ buffer = m_readChannel->peek(40);
1925+ int found = buffer.indexOf(welcomeMessage, startCheckIndex);
1926+ int skip = (found >= 0) ? found : buffer.length() - welcomeMessage.length();
1927+ if (found >= 0) {
1928+ buffer = m_readChannel->read(skip + welcomeMessage.length());
1929+ m_gotWelcomeMessage = true;
1930+ return true;
1931+ }
1932+ if (skip > 0) {
1933+ buffer = m_readChannel->read(skip);
1934+ } else {
1935+ buffer.clear();
1936+ }
1937+ } while (!buffer.isEmpty());
1938+
1939+ return false;
1940+}
1941+
1942+Ipc::Ipc(QObject *parent):
1943+ QObject(parent),
1944+ d_ptr(new IpcPrivate(this))
1945+{
1946+}
1947+
1948+Ipc::~Ipc()
1949+{
1950+}
1951+
1952+void Ipc::setChannels(QIODevice *readChannel, QIODevice *writeChannel)
1953+{
1954+ Q_D(Ipc);
1955+ d->setChannels(readChannel, writeChannel);
1956+}
1957+
1958+void Ipc::write(const QByteArray &data)
1959+{
1960+ Q_D(Ipc);
1961+ int length = data.count();
1962+ d->m_writeChannel->write((char *)&length, sizeof(length));
1963+ d->m_writeChannel->write(data);
1964+ d->m_writeChannel->waitForBytesWritten(-1);
1965+}
1966+
1967+#include "ipc.moc"
1968
1969=== added file 'online-accounts-ui/ipc.h'
1970--- online-accounts-ui/ipc.h 1970-01-01 00:00:00 +0000
1971+++ online-accounts-ui/ipc.h 2014-08-18 13:33:35 +0000
1972@@ -0,0 +1,67 @@
1973+/*
1974+ * Copyright (C) 2014 Canonical Ltd.
1975+ *
1976+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
1977+ *
1978+ * This file is part of online-accounts-ui
1979+ *
1980+ * This program is free software: you can redistribute it and/or modify it
1981+ * under the terms of the GNU General Public License version 3, as published
1982+ * by the Free Software Foundation.
1983+ *
1984+ * This program is distributed in the hope that it will be useful, but
1985+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1986+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1987+ * PURPOSE. See the GNU General Public License for more details.
1988+ *
1989+ * You should have received a copy of the GNU General Public License along
1990+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1991+ */
1992+
1993+#ifndef OAU_IPC_H
1994+#define OAU_IPC_H
1995+
1996+#include <QObject>
1997+
1998+class QByteArray;
1999+class QIODevice;
2000+
2001+#define OAU_OPERATION_CODE "code"
2002+#define OAU_OPERATION_CODE_PROCESS "process"
2003+#define OAU_OPERATION_CODE_REGISTER_HANDLER "newHandler"
2004+#define OAU_OPERATION_CODE_REQUEST_FINISHED "finished"
2005+#define OAU_OPERATION_CODE_REQUEST_FAILED "failed"
2006+#define OAU_OPERATION_ID "id"
2007+#define OAU_OPERATION_DATA "data"
2008+#define OAU_OPERATION_INTERFACE "interface"
2009+#define OAU_OPERATION_CLIENT_PROFILE "profile"
2010+#define OAU_OPERATION_ERROR_NAME "errname"
2011+#define OAU_OPERATION_ERROR_MESSAGE "errmsg"
2012+#define OAU_OPERATION_HANDLER_ID "handlerId"
2013+#define OAU_REQUEST_MATCH_KEY "X-RequestHandler"
2014+
2015+namespace OnlineAccountsUi {
2016+
2017+class IpcPrivate;
2018+class Ipc: public QObject
2019+{
2020+ Q_OBJECT
2021+
2022+public:
2023+ Ipc(QObject *parent = 0);
2024+ ~Ipc();
2025+
2026+ void setChannels(QIODevice *readChannel, QIODevice *writeChannel);
2027+ void write(const QByteArray &data);
2028+
2029+Q_SIGNALS:
2030+ void dataReady(QByteArray &data);
2031+
2032+private:
2033+ IpcPrivate *d_ptr;
2034+ Q_DECLARE_PRIVATE(Ipc)
2035+};
2036+
2037+}; // namespace
2038+
2039+#endif // OAU_IPC_H
2040
2041=== modified file 'online-accounts-ui/main.cpp'
2042--- src/main.cpp 2014-04-29 12:11:55 +0000
2043+++ online-accounts-ui/main.cpp 2014-08-18 13:33:35 +0000
2044@@ -1,5 +1,5 @@
2045 /*
2046- * Copyright (C) 2013 Canonical Ltd.
2047+ * Copyright (C) 2013-2014 Canonical Ltd.
2048 *
2049 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
2050 *
2051@@ -21,14 +21,9 @@
2052 #include "debug.h"
2053 #include "globals.h"
2054 #include "i18n.h"
2055-#include "inactivity-timer.h"
2056-#include "indicator-service.h"
2057-#include "request-manager.h"
2058-#include "service.h"
2059-#include "signonui-service.h"
2060+#include "ui-server.h"
2061
2062 #include <QGuiApplication>
2063-#include <QDBusConnection>
2064 #include <QLibrary>
2065 #include <QProcessEnvironment>
2066
2067@@ -37,7 +32,6 @@
2068 int main(int argc, char **argv)
2069 {
2070 QGuiApplication app(argc, argv);
2071- app.setQuitOnLastWindowClosed(false);
2072
2073 /* The testability driver is only loaded by QApplication but not by
2074 * QGuiApplication. However, QApplication depends on QWidget which would
2075@@ -70,66 +64,22 @@
2076 setLoggingLevel(value);
2077 }
2078
2079- /* default daemonTimeout to 5 seconds */
2080- int daemonTimeout = 5;
2081-
2082- /* override daemonTimeout if OAU_DAEMON_TIMEOUT is set */
2083- if (environment.contains(QLatin1String("OAU_DAEMON_TIMEOUT"))) {
2084- bool isOk;
2085- int value = environment.value(
2086- QLatin1String("OAU_DAEMON_TIMEOUT")).toInt(&isOk);
2087- if (isOk)
2088- daemonTimeout = value;
2089- }
2090-
2091 initTr(I18N_DOMAIN, NULL);
2092
2093- RequestManager *requestManager = new RequestManager();
2094-
2095- Service *service = new Service();
2096- QDBusConnection connection = QDBusConnection::sessionBus();
2097- connection.registerService(OAU_SERVICE_NAME);
2098- connection.registerObject(OAU_OBJECT_PATH, service);
2099-
2100- SignOnUi::Service *signonuiService = new SignOnUi::Service();
2101- connection.registerService(SIGNONUI_SERVICE_NAME);
2102- connection.registerObject(SIGNONUI_OBJECT_PATH, signonuiService,
2103- QDBusConnection::ExportAllContents);
2104-
2105- SignOnUi::IndicatorService *indicatorService =
2106- new SignOnUi::IndicatorService();
2107- connection.registerService(WEBCREDENTIALS_BUS_NAME);
2108- connection.registerObject(WEBCREDENTIALS_OBJECT_PATH,
2109- indicatorService->serviceObject());
2110-
2111-
2112- InactivityTimer *inactivityTimer = 0;
2113- if (daemonTimeout > 0) {
2114- inactivityTimer = new InactivityTimer(daemonTimeout * 1000);
2115- inactivityTimer->watchObject(requestManager);
2116- inactivityTimer->watchObject(indicatorService);
2117- QObject::connect(inactivityTimer, SIGNAL(timeout()),
2118- &app, SLOT(quit()));
2119- }
2120-
2121- int ret = app.exec();
2122-
2123- connection.unregisterService(WEBCREDENTIALS_BUS_NAME);
2124- connection.unregisterObject(WEBCREDENTIALS_OBJECT_PATH);
2125- delete indicatorService;
2126-
2127- connection.unregisterService(SIGNONUI_SERVICE_NAME);
2128- connection.unregisterObject(SIGNONUI_OBJECT_PATH);
2129- delete signonuiService;
2130-
2131- connection.unregisterService(OAU_SERVICE_NAME);
2132- connection.unregisterObject(OAU_OBJECT_PATH);
2133- delete service;
2134-
2135- delete requestManager;
2136-
2137- delete inactivityTimer;
2138-
2139- return ret;
2140+ QStringList arguments = app.arguments();
2141+ int i = arguments.indexOf("--socket");
2142+ if (i < 0 || i + 1 >= arguments.count()) {
2143+ qWarning() << "Missing --socket argument";
2144+ return EXIT_FAILURE;
2145+ }
2146+
2147+ UiServer server(arguments[i + 1]);
2148+ QObject::connect(&server, SIGNAL(finished()),
2149+ &app, SLOT(quit()));
2150+ if (Q_UNLIKELY(!server.init())) {
2151+ qWarning() << "Could not connect to socket";
2152+ return EXIT_FAILURE;
2153+ }
2154+
2155+ return app.exec();
2156 }
2157-
2158
2159=== modified file 'online-accounts-ui/module/OAuth.qml'
2160--- src/module/OAuth.qml 2014-04-29 12:11:55 +0000
2161+++ online-accounts-ui/module/OAuth.qml 2014-08-18 13:33:35 +0000
2162@@ -55,7 +55,6 @@
2163 if (request) {
2164 console.log("RequestHandler captured request!")
2165 loader.setSource("WebView.qml", {
2166- "rootDir": request.rootDir,
2167 "signonRequest": request
2168 })
2169 } else {
2170
2171=== modified file 'online-accounts-ui/module/WebView.qml'
2172--- src/module/WebView.qml 2014-04-29 12:11:55 +0000
2173+++ online-accounts-ui/module/WebView.qml 2014-08-18 13:33:35 +0000
2174@@ -5,7 +5,6 @@
2175
2176 UbuntuWebView {
2177 property QtObject signonRequest
2178- property url rootDir
2179
2180 Component.onCompleted: url = signonRequest.startUrl
2181
2182@@ -22,6 +21,6 @@
2183 onUrlChanged: signonRequest.currentUrl = url
2184
2185 context: UbuntuWebContext {
2186- dataPath: rootDir
2187+ dataPath: signonRequest.rootDir
2188 }
2189 }
2190
2191=== modified file 'online-accounts-ui/module/qmldir.in'
2192--- src/module/qmldir.in 2013-06-05 11:33:39 +0000
2193+++ online-accounts-ui/module/qmldir.in 2014-08-18 13:33:35 +0000
2194@@ -1,7 +1,9 @@
2195 module $${API_URI}
2196+KeyboardRectangle 1.0 KeyboardRectangle.qml
2197 OAuthMain 1.0 OAuthMain.qml
2198 OAuth 1.0 OAuth.qml
2199 Options 1.0 Options.qml
2200 RemovalConfirmation 1.0 RemovalConfirmation.qml
2201 ServiceItem 1.0 ServiceItem.qml
2202 ServiceSwitches 1.0 ServiceSwitches.qml
2203+WebView 1.0 WebView.qml
2204
2205=== modified file 'online-accounts-ui/notification.cpp'
2206--- src/notification.cpp 2014-06-05 13:28:30 +0000
2207+++ online-accounts-ui/notification.cpp 2014-08-18 13:33:35 +0000
2208@@ -108,6 +108,14 @@
2209 this, NULL);
2210 }
2211
2212+void Notification::setSnapDecision(bool snapDecision)
2213+{
2214+ Q_D(Notification);
2215+ notify_notification_set_hint(d->m_notification,
2216+ "x-canonical-snap-decisions",
2217+ g_variant_new_boolean(snapDecision));
2218+}
2219+
2220 void Notification::show()
2221 {
2222 Q_D(Notification);
2223
2224=== modified file 'online-accounts-ui/notification.h'
2225--- src/notification.h 2014-06-05 13:28:30 +0000
2226+++ online-accounts-ui/notification.h 2014-08-18 13:33:35 +0000
2227@@ -37,6 +37,7 @@
2228 ~Notification();
2229
2230 void addAction(const QString &action, const QString &label);
2231+ void setSnapDecision(bool snapDecision);
2232
2233 public Q_SLOTS:
2234 void show();
2235
2236=== renamed file 'src/online-accounts-ui.pro' => 'online-accounts-ui/online-accounts-ui-helper.pro'
2237--- src/online-accounts-ui.pro 2014-06-05 13:28:30 +0000
2238+++ online-accounts-ui/online-accounts-ui-helper.pro 2014-08-18 13:33:35 +0000
2239@@ -21,16 +21,10 @@
2240 libsignon-qt5 \
2241 signon-plugins-common
2242
2243-I18N_DOMAIN="ubuntu-system-settings-online-accounts"
2244-SIGNONUI_I18N_DOMAIN="signon-ui"
2245-
2246 DEFINES += \
2247 I18N_DOMAIN=\\\"$${I18N_DOMAIN}\\\" \
2248 SIGNONUI_I18N_DOMAIN=\\\"$${SIGNONUI_I18N_DOMAIN}\\\"
2249
2250-DBUS_ADAPTORS += \
2251- com.ubuntu.OnlineAccountsUi.xml
2252-
2253 DEFINES += \
2254 DEBUG_ENABLED \
2255 OAU_PLUGIN_DIR=\\\"$${ONLINE_ACCOUNTS_PLUGIN_DIR}/\\\" \
2256@@ -44,19 +38,15 @@
2257 debug.cpp \
2258 dialog.cpp \
2259 i18n.cpp \
2260- inactivity-timer.cpp \
2261- indicator-service.cpp \
2262+ ipc.cpp \
2263 main.cpp \
2264 notification.cpp \
2265 panel-request.cpp \
2266 provider-request.cpp \
2267- reauthenticator.cpp \
2268 request.cpp \
2269 request-handler.cpp \
2270- request-manager.cpp \
2271- service.cpp \
2272 signonui-request.cpp \
2273- signonui-service.cpp
2274+ ui-server.cpp
2275
2276 HEADERS += \
2277 access-model.h \
2278@@ -66,33 +56,20 @@
2279 debug.h \
2280 dialog.h \
2281 i18n.h \
2282- inactivity-timer.h \
2283- indicator-service.h \
2284+ ipc.h \
2285 notification.h \
2286 panel-request.h \
2287 provider-request.h \
2288- reauthenticator.h \
2289 request.h \
2290 request-handler.h \
2291- request-manager.h \
2292- service.h \
2293 signonui-request.h \
2294- signonui-service.h
2295+ ui-server.h
2296
2297 QML_SOURCES = \
2298 qml/AccountCreationPage.qml \
2299- qml/AccountEditPage.qml \
2300- qml/AccountItem.qml \
2301- qml/AccountsPage.qml \
2302- qml/AddAccountLabel.qml \
2303 qml/AuthorizationPage.qml \
2304- qml/MainPage.qml \
2305- qml/NewAccountPage.qml \
2306- qml/NoAccountsPage.qml \
2307- qml/NormalStartupPage.qml \
2308- qml/ProviderPluginList.qml \
2309 qml/ProviderRequest.qml \
2310- qml/ProvidersList.qml
2311+ qml/SignOnUiPage.qml
2312
2313 RESOURCES += \
2314 ui.qrc
2315@@ -102,17 +79,8 @@
2316 $${RESOURCES}
2317
2318 QMAKE_SUBSTITUTES += \
2319- com.ubuntu.OnlineAccountsUi.service.in \
2320 online-accounts-ui.desktop.in
2321
2322-DBUS_ADAPTORS += \
2323- com.canonical.indicators.webcredentials.xml
2324-
2325-service.path = $${INSTALL_PREFIX}/share/dbus-1/services
2326-service.files = \
2327- com.ubuntu.OnlineAccountsUi.service
2328-INSTALLS += service
2329-
2330 desktop.path = $${INSTALL_PREFIX}/share/applications
2331 desktop.files += online-accounts-ui.desktop
2332 INSTALLS += desktop
2333
2334=== renamed file 'src/src.pro' => 'online-accounts-ui/online-accounts-ui.pro'
2335--- src/src.pro 2014-04-29 12:11:55 +0000
2336+++ online-accounts-ui/online-accounts-ui.pro 2014-08-18 13:33:35 +0000
2337@@ -1,4 +1,4 @@
2338 TEMPLATE = subdirs
2339 SUBDIRS = \
2340 module \
2341- online-accounts-ui.pro
2342+ online-accounts-ui-helper.pro
2343
2344=== modified file 'online-accounts-ui/panel-request.cpp'
2345--- src/panel-request.cpp 2014-05-30 08:09:02 +0000
2346+++ online-accounts-ui/panel-request.cpp 2014-08-18 13:33:35 +0000
2347@@ -65,7 +65,9 @@
2348 PanelRequestPrivate::~PanelRequestPrivate()
2349 {
2350 DEBUG() << "view:" << m_view;
2351+ /* TODO Uncomment this once QTBUG-40766 is resolved:
2352 delete m_view;
2353+ */
2354 }
2355
2356 void PanelRequestPrivate::start()
2357@@ -103,18 +105,15 @@
2358
2359 if (!visible) {
2360 q->setResult(QVariantMap());
2361- /* FIXME HACK: remove when window reparenting is implemented */
2362- if (QGuiApplication::platformName().startsWith("ubuntu")) {
2363- QDesktopServices::openUrl(QUrl("application:///ubuntu-system-settings.desktop"));
2364- }
2365 }
2366 }
2367
2368-PanelRequest::PanelRequest(const QDBusConnection &connection,
2369- const QDBusMessage &message,
2370+PanelRequest::PanelRequest(const QString &interface,
2371+ int id,
2372+ const QString &clientProfile,
2373 const QVariantMap &parameters,
2374 QObject *parent):
2375- Request(connection, message, parameters, parent),
2376+ Request(interface, id, clientProfile, parameters, parent),
2377 d_ptr(new PanelRequestPrivate(this))
2378 {
2379 }
2380
2381=== modified file 'online-accounts-ui/panel-request.h'
2382--- src/panel-request.h 2014-05-30 08:09:02 +0000
2383+++ online-accounts-ui/panel-request.h 2014-08-18 13:33:35 +0000
2384@@ -1,5 +1,5 @@
2385 /*
2386- * Copyright (C) 2013 Canonical Ltd.
2387+ * Copyright (C) 2013-2014 Canonical Ltd.
2388 *
2389 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
2390 *
2391@@ -31,8 +31,9 @@
2392 Q_OBJECT
2393
2394 public:
2395- explicit PanelRequest(const QDBusConnection &connection,
2396- const QDBusMessage &message,
2397+ explicit PanelRequest(const QString &interface,
2398+ int id,
2399+ const QString &clientProfile,
2400 const QVariantMap &parameters,
2401 QObject *parent = 0);
2402 ~PanelRequest();
2403
2404=== modified file 'online-accounts-ui/provider-request.cpp'
2405--- src/provider-request.cpp 2014-05-30 09:28:43 +0000
2406+++ online-accounts-ui/provider-request.cpp 2014-08-18 13:33:35 +0000
2407@@ -164,11 +164,12 @@
2408 m_view->close();
2409 }
2410
2411-ProviderRequest::ProviderRequest(const QDBusConnection &connection,
2412- const QDBusMessage &message,
2413+ProviderRequest::ProviderRequest(const QString &interface,
2414+ int id,
2415+ const QString &clientProfile,
2416 const QVariantMap &parameters,
2417 QObject *parent):
2418- Request(connection, message, parameters, parent),
2419+ Request(interface, id, clientProfile, parameters, parent),
2420 d_ptr(new ProviderRequestPrivate(this))
2421 {
2422 }
2423
2424=== modified file 'online-accounts-ui/provider-request.h'
2425--- src/provider-request.h 2014-02-03 10:27:39 +0000
2426+++ online-accounts-ui/provider-request.h 2014-08-18 13:33:35 +0000
2427@@ -31,8 +31,9 @@
2428 Q_OBJECT
2429
2430 public:
2431- explicit ProviderRequest(const QDBusConnection &connection,
2432- const QDBusMessage &message,
2433+ explicit ProviderRequest(const QString &interface,
2434+ int id,
2435+ const QString &clientProfile,
2436 const QVariantMap &parameters,
2437 QObject *parent = 0);
2438 ~ProviderRequest();
2439
2440=== modified file 'online-accounts-ui/qml/ProviderRequest.qml'
2441--- src/qml/ProviderRequest.qml 2014-05-29 21:14:11 +0000
2442+++ online-accounts-ui/qml/ProviderRequest.qml 2014-08-18 13:33:35 +0000
2443@@ -54,7 +54,7 @@
2444 id: loader
2445 anchors.fill: parent
2446 active: false
2447- sourceComponent: accessModel.count <= 1 ? accountCreationPage : authorizationPage
2448+ sourceComponent: (accessModel.count <= 1 || applicationInfo.id === "system-settings") ? accountCreationPage : authorizationPage
2449 onLoaded: {
2450 // use this trick to break the sourceComponent binding
2451 var tmp = sourceComponent
2452@@ -165,10 +165,17 @@
2453 }
2454
2455 function grantAccessIfReady() {
2456- if (root.__createdAccountId != 0 &&
2457- accountsModel.indexOfAccount(root.__createdAccountId) >= 0) {
2458- root.grantAccess(root.__createdAccountId)
2459- root.__createdAccountId = 0
2460+ if (root.__createdAccountId != 0) {
2461+ // If the request comes from system settings, stop here
2462+ if (applicationInfo.id === "system-settings") {
2463+ root.allowed(root.__createdAccountId)
2464+ return
2465+ }
2466+
2467+ if (accountsModel.indexOfAccount(root.__createdAccountId) >= 0) {
2468+ root.grantAccess(root.__createdAccountId)
2469+ root.__createdAccountId = 0
2470+ }
2471 }
2472 }
2473
2474
2475=== added file 'online-accounts-ui/qml/SignOnUiPage.qml'
2476--- online-accounts-ui/qml/SignOnUiPage.qml 1970-01-01 00:00:00 +0000
2477+++ online-accounts-ui/qml/SignOnUiPage.qml 2014-08-18 13:33:35 +0000
2478@@ -0,0 +1,40 @@
2479+import QtQuick 2.0
2480+import Ubuntu.Components 0.1
2481+import Ubuntu.Components.ListItems 0.1 as ListItem
2482+import Ubuntu.OnlineAccounts.Plugin 1.0
2483+
2484+MainView {
2485+ id: root
2486+
2487+ property var signonRequest: request
2488+
2489+ width: units.gu(60)
2490+ height: units.gu(90)
2491+
2492+ Page {
2493+ WebView {
2494+ id: loader
2495+ signonRequest: root.signonRequest
2496+
2497+ anchors {
2498+ fill: parent
2499+ bottomMargin: Math.max(osk.height, cancelButton.height)
2500+ }
2501+ }
2502+
2503+ ListItem.SingleControl {
2504+ id: cancelButton
2505+ anchors.bottom: parent.bottom
2506+ showDivider: false
2507+ control: Button {
2508+ text: i18n.dtr("ubuntu-system-settings-online-accounts", "Cancel")
2509+ width: parent.width - units.gu(4)
2510+ onClicked: signonRequest.cancel()
2511+ }
2512+ }
2513+
2514+ KeyboardRectangle {
2515+ id: osk
2516+ }
2517+ }
2518+}
2519
2520=== modified file 'online-accounts-ui/request-handler.cpp'
2521--- src/request-handler.cpp 2014-04-29 14:34:44 +0000
2522+++ online-accounts-ui/request-handler.cpp 2014-08-18 13:33:35 +0000
2523@@ -21,6 +21,7 @@
2524 #include "request-handler.h"
2525
2526 #include "debug.h"
2527+#include "ui-server.h"
2528
2529 #include <SignOn/uisessiondata_priv.h>
2530 #include <QDBusArgument>
2531@@ -66,6 +67,11 @@
2532 d_ptr(new RequestHandlerPrivate(this))
2533 {
2534 allRequestHandlers.append(this);
2535+
2536+ OnlineAccountsUi::UiServer *server =
2537+ OnlineAccountsUi::UiServer::instance();
2538+ Q_ASSERT(server);
2539+ server->registerHandler(this);
2540 }
2541
2542 RequestHandler::~RequestHandler()
2543
2544=== modified file 'online-accounts-ui/request.cpp'
2545--- src/request.cpp 2014-05-30 08:09:02 +0000
2546+++ online-accounts-ui/request.cpp 2014-08-18 13:33:35 +0000
2547@@ -51,8 +51,9 @@
2548 Q_DECLARE_PUBLIC(Request)
2549
2550 public:
2551- RequestPrivate(const QDBusConnection &connection,
2552- const QDBusMessage &message,
2553+ RequestPrivate(const QString &interface,
2554+ int id,
2555+ const QString &clientProfile,
2556 const QVariantMap &parameters,
2557 Request *request);
2558 ~RequestPrivate();
2559@@ -63,33 +64,36 @@
2560
2561 private:
2562 void setWindow(QWindow *window);
2563- QString findClientApparmorProfile();
2564
2565 private:
2566 mutable Request *q_ptr;
2567- QDBusConnection m_connection;
2568- QDBusMessage m_message;
2569+ QString m_interface;
2570+ int m_id;
2571 QVariantMap m_parameters;
2572 QString m_clientApparmorProfile;
2573 bool m_inProgress;
2574 QPointer<QWindow> m_window;
2575+ QString m_errorName;
2576+ QString m_errorMessage;
2577+ QVariantMap m_result;
2578 };
2579
2580 } // namespace
2581
2582-RequestPrivate::RequestPrivate(const QDBusConnection &connection,
2583- const QDBusMessage &message,
2584+RequestPrivate::RequestPrivate(const QString &interface,
2585+ int id,
2586+ const QString &clientProfile,
2587 const QVariantMap &parameters,
2588 Request *request):
2589 QObject(request),
2590 q_ptr(request),
2591- m_connection(connection),
2592- m_message(message),
2593+ m_interface(interface),
2594+ m_id(id),
2595 m_parameters(parameters),
2596+ m_clientApparmorProfile(clientProfile),
2597 m_inProgress(false),
2598 m_window(0)
2599 {
2600- m_clientApparmorProfile = findClientApparmorProfile();
2601 }
2602
2603 RequestPrivate::~RequestPrivate()
2604@@ -113,41 +117,14 @@
2605 window->show();
2606 }
2607
2608-QString RequestPrivate::findClientApparmorProfile()
2609-{
2610- QString uniqueConnectionId = m_message.service();
2611- /* This is mainly for unit tests: real messages on the session bus always
2612- * have a service name. */
2613- if (uniqueConnectionId.isEmpty()) return QString();
2614-
2615- QString appId;
2616-
2617- QDBusMessage msg =
2618- QDBusMessage::createMethodCall("org.freedesktop.DBus",
2619- "/org/freedesktop/DBus",
2620- "org.freedesktop.DBus",
2621- "GetConnectionAppArmorSecurityContext");
2622- QVariantList args;
2623- args << uniqueConnectionId;
2624- msg.setArguments(args);
2625- QDBusMessage reply = QDBusConnection::sessionBus().call(msg, QDBus::Block);
2626- if (reply.type() == QDBusMessage::ReplyMessage) {
2627- appId = reply.arguments().value(0, QString()).toString();
2628- DEBUG() << "App ID:" << appId;
2629- } else {
2630- qWarning() << "Error getting app ID:" << reply.errorName() <<
2631- reply.errorMessage();
2632- }
2633- return appId;
2634-}
2635-
2636 /* Some unit tests might need to provide a different implementation for the
2637 * Request::newRequest() factory method; for this reason, we allow the method
2638 * to be excluded from compilation.
2639 */
2640 #ifndef NO_REQUEST_FACTORY
2641-Request *Request::newRequest(const QDBusConnection &connection,
2642- const QDBusMessage &message,
2643+Request *Request::newRequest(const QString &interface,
2644+ int id,
2645+ const QString &clientProfile,
2646 const QVariantMap &parameters,
2647 QObject *parent)
2648 {
2649@@ -155,26 +132,29 @@
2650 * different subclasses for handling them, and in this method we examine
2651 * the @parameters argument to figure out which subclass is the most apt to
2652 * handle the request. */
2653- if (message.interface() == OAU_INTERFACE) {
2654+ if (interface == OAU_INTERFACE) {
2655 if (parameters.contains(OAU_KEY_PROVIDER)) {
2656- return new ProviderRequest(connection, message, parameters, parent);
2657+ return new ProviderRequest(interface, id, clientProfile,
2658+ parameters, parent);
2659 } else {
2660- return new PanelRequest(connection, message, parameters, parent);
2661+ return new PanelRequest(interface, id, clientProfile,
2662+ parameters, parent);
2663 }
2664 } else {
2665- Q_ASSERT(message.interface() == SIGNONUI_INTERFACE);
2666- return SignOnUi::Request::newRequest(connection, message,
2667+ Q_ASSERT(interface == SIGNONUI_INTERFACE);
2668+ return SignOnUi::Request::newRequest(id, clientProfile,
2669 parameters, parent);
2670 }
2671 }
2672 #endif
2673
2674-Request::Request(const QDBusConnection &connection,
2675- const QDBusMessage &message,
2676+Request::Request(const QString &interface,
2677+ int id,
2678+ const QString &clientProfile,
2679 const QVariantMap &parameters,
2680 QObject *parent):
2681 QObject(parent),
2682- d_ptr(new RequestPrivate(connection, message, parameters, this))
2683+ d_ptr(new RequestPrivate(interface, id, clientProfile, parameters, this))
2684 {
2685 allRequests.append(this);
2686 }
2687@@ -195,6 +175,18 @@
2688 return 0;
2689 }
2690
2691+QString Request::interface() const
2692+{
2693+ Q_D(const Request);
2694+ return d->m_interface;
2695+}
2696+
2697+int Request::id() const
2698+{
2699+ Q_D(const Request);
2700+ return d->m_id;
2701+}
2702+
2703 void Request::setWindow(QWindow *window)
2704 {
2705 Q_D(Request);
2706@@ -231,6 +223,24 @@
2707 return d->m_window;
2708 }
2709
2710+QVariantMap Request::result() const
2711+{
2712+ Q_D(const Request);
2713+ return d->m_result;
2714+}
2715+
2716+QString Request::errorName() const
2717+{
2718+ Q_D(const Request);
2719+ return d->m_errorName;
2720+}
2721+
2722+QString Request::errorMessage() const
2723+{
2724+ Q_D(const Request);
2725+ return d->m_errorMessage;
2726+}
2727+
2728 void Request::start()
2729 {
2730 Q_D(Request);
2731@@ -249,8 +259,11 @@
2732 void Request::fail(const QString &name, const QString &message)
2733 {
2734 Q_D(Request);
2735- QDBusMessage reply = d->m_message.createErrorReply(name, message);
2736- d->m_connection.send(reply);
2737+
2738+ DEBUG() << name << message;
2739+
2740+ d->m_errorName = name;
2741+ d->m_errorMessage = message;
2742
2743 Q_EMIT completed();
2744 }
2745@@ -268,8 +281,8 @@
2746 {
2747 Q_D(Request);
2748 if (d->m_inProgress) {
2749- QDBusMessage reply = d->m_message.createReply(result);
2750- d->m_connection.send(reply);
2751+ DEBUG() << result;
2752+ d->m_result = result;
2753
2754 Q_EMIT completed();
2755 d->m_inProgress = false;
2756
2757=== modified file 'online-accounts-ui/request.h'
2758--- src/request.h 2014-05-30 08:09:02 +0000
2759+++ online-accounts-ui/request.h 2014-08-18 13:33:35 +0000
2760@@ -21,8 +21,6 @@
2761 #ifndef OAU_REQUEST_H
2762 #define OAU_REQUEST_H
2763
2764-#include <QDBusConnection>
2765-#include <QDBusMessage>
2766 #include <QObject>
2767 #include <QVariantMap>
2768 #include <QWindow>
2769@@ -35,20 +33,27 @@
2770 Q_OBJECT
2771
2772 public:
2773- static Request *newRequest(const QDBusConnection &connection,
2774- const QDBusMessage &message,
2775+ static Request *newRequest(const QString &interface,
2776+ int id,
2777+ const QString &clientProfile,
2778 const QVariantMap &parameters,
2779 QObject *parent = 0);
2780 ~Request();
2781
2782 static Request *find(const QVariantMap &match);
2783
2784+ QString interface() const;
2785+ int id() const;
2786 WId windowId() const;
2787 bool isInProgress() const;
2788 const QVariantMap &parameters() const;
2789 QString clientApparmorProfile() const;
2790 QWindow *window() const;
2791
2792+ QVariantMap result() const;
2793+ QString errorName() const;
2794+ QString errorMessage() const;
2795+
2796 public Q_SLOTS:
2797 virtual void start();
2798 void cancel();
2799@@ -57,8 +62,9 @@
2800 void completed();
2801
2802 protected:
2803- explicit Request(const QDBusConnection &connection,
2804- const QDBusMessage &message,
2805+ explicit Request(const QString &interface,
2806+ int id,
2807+ const QString &clientProfile,
2808 const QVariantMap &parameters,
2809 QObject *parent = 0);
2810 virtual void setWindow(QWindow *window);
2811
2812=== modified file 'online-accounts-ui/signonui-request.cpp'
2813--- src/signonui-request.cpp 2014-06-05 13:28:30 +0000
2814+++ online-accounts-ui/signonui-request.cpp 2014-08-18 13:33:35 +0000
2815@@ -1,7 +1,7 @@
2816 /*
2817 * This file is part of online-accounts-ui
2818 *
2819- * Copyright (C) 2011 Canonical Ltd.
2820+ * Copyright (C) 2011-2014 Canonical Ltd.
2821 *
2822 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
2823 *
2824@@ -20,14 +20,19 @@
2825
2826 #include "signonui-request.h"
2827
2828+#include "account-manager.h"
2829+#include "application-manager.h"
2830 #include "browser-request.h"
2831 #include "debug.h"
2832 #include "globals.h"
2833-#include "indicator-service.h"
2834+#include "notification.h"
2835+#include "request-handler.h"
2836
2837 #include <Accounts/Account>
2838-#include <Accounts/Manager>
2839+#include <Accounts/Application>
2840+#include <Accounts/Provider>
2841 #include <QDBusArgument>
2842+#include <QPointer>
2843 #include <SignOn/uisessiondata.h>
2844 #include <SignOn/uisessiondata_priv.h>
2845
2846@@ -45,16 +50,19 @@
2847 ~RequestPrivate();
2848
2849 private:
2850- bool setWindow(QWindow *window);
2851+ void setWindow(QWindow *window);
2852 Accounts::Account *findAccount();
2853- bool dispatchToIndicator();
2854+
2855+private Q_SLOTS:
2856+ void onActionInvoked(const QString &action);
2857+ void onNotificationClosed();
2858
2859 private:
2860 mutable Request *q_ptr;
2861 QVariantMap m_clientData;
2862- bool m_inProgress;
2863- RequestHandler *m_handler;
2864- Accounts::Manager *m_accountManager;
2865+ QPointer<RequestHandler> m_handler;
2866+ OnlineAccountsUi::Notification *m_notification;
2867+ QWindow *m_window;
2868 };
2869
2870 } // namespace
2871@@ -62,9 +70,9 @@
2872 RequestPrivate::RequestPrivate(Request *request):
2873 QObject(request),
2874 q_ptr(request),
2875- m_inProgress(false),
2876 m_handler(0),
2877- m_accountManager(0)
2878+ m_notification(0),
2879+ m_window(0)
2880 {
2881 const QVariantMap &parameters = request->parameters();
2882 if (parameters.contains(SSOUI_KEY_CLIENT_DATA)) {
2883@@ -77,20 +85,51 @@
2884
2885 RequestPrivate::~RequestPrivate()
2886 {
2887+ delete m_notification;
2888+ m_notification = 0;
2889 }
2890
2891-bool RequestPrivate::setWindow(QWindow *window)
2892+void RequestPrivate::setWindow(QWindow *window)
2893 {
2894 Q_Q(Request);
2895- Q_UNUSED(window);
2896
2897- /* If the window has no parent and the webcredentials indicator service is
2898- * up, dispatch the request to it. */
2899- if (q->windowId() == 0 && dispatchToIndicator()) {
2900- return true;
2901+ /* Don't show the window yet: the user must be presented with a
2902+ * snap-decision, and we'll show the window only if he decides to
2903+ * authenticate. */
2904+ Accounts::Account *account = findAccount();
2905+ if (Q_UNLIKELY(!account)) {
2906+ QVariantMap result;
2907+ result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_FORBIDDEN;
2908+ q->setResult(result);
2909+ return;
2910 }
2911
2912- return false;
2913+ OnlineAccountsUi::ApplicationManager *appManager =
2914+ OnlineAccountsUi::ApplicationManager::instance();
2915+ Accounts::Application application =
2916+ appManager->applicationFromProfile(q->clientApparmorProfile());
2917+
2918+ OnlineAccountsUi::AccountManager *accountManager =
2919+ OnlineAccountsUi::AccountManager::instance();
2920+ Accounts::Provider provider =
2921+ accountManager->provider(account->providerName());
2922+
2923+ QString summary =
2924+ QString("Please authorize %1 to access your %2 account %3").
2925+ arg(application.isValid() ? application.displayName() : "Ubuntu").
2926+ arg(provider.displayName()).
2927+ arg(account->displayName());
2928+ m_notification =
2929+ new OnlineAccountsUi::Notification("Authentication request", summary);
2930+ m_notification->addAction("cancel", "Cancel");
2931+ m_notification->addAction("continue", "Authorize...");
2932+ m_notification->setSnapDecision(true);
2933+ QObject::connect(m_notification, SIGNAL(actionInvoked(const QString &)),
2934+ this, SLOT(onActionInvoked(const QString &)));
2935+ QObject::connect(m_notification, SIGNAL(closed()),
2936+ this, SLOT(onNotificationClosed()));
2937+ m_notification->show();
2938+ m_window = window;
2939 }
2940
2941 Accounts::Account *RequestPrivate::findAccount()
2942@@ -104,11 +143,10 @@
2943 /* Find the account using this identity.
2944 * FIXME: there might be more than one!
2945 */
2946- if (m_accountManager == 0) {
2947- m_accountManager = new Accounts::Manager(this);
2948- }
2949- Q_FOREACH(Accounts::AccountId accountId, m_accountManager->accountList()) {
2950- Accounts::Account *account = m_accountManager->account(accountId);
2951+ OnlineAccountsUi::AccountManager *manager =
2952+ OnlineAccountsUi::AccountManager::instance();
2953+ Q_FOREACH(Accounts::AccountId accountId, manager->accountList()) {
2954+ Accounts::Account *account = manager->account(accountId);
2955 if (account == 0) continue;
2956
2957 QVariant value(QVariant::UInt);
2958@@ -122,52 +160,60 @@
2959 return 0;
2960 }
2961
2962-bool RequestPrivate::dispatchToIndicator()
2963+void RequestPrivate::onActionInvoked(const QString &action)
2964 {
2965 Q_Q(Request);
2966
2967- Accounts::Account *account = findAccount();
2968- if (account == 0) {
2969- return false;
2970+ DEBUG() << action;
2971+
2972+ QObject::disconnect(m_notification, 0, this, 0);
2973+ m_notification->deleteLater();
2974+ m_notification = 0;
2975+
2976+ if (action == QStringLiteral("continue")) {
2977+ q->setWindow(m_window);
2978+ } else {
2979+ q->cancel();
2980 }
2981-
2982- QVariantMap notification;
2983- notification["DisplayName"] = account->displayName();
2984- notification["ClientData"] = m_clientData;
2985- notification["Identity"] = q->identity();
2986- notification["Method"] = q->method();
2987- notification["Mechanism"] = q->mechanism();
2988-
2989- IndicatorService *indicator = IndicatorService::instance();
2990- indicator->reportFailure(account->id(), notification);
2991-
2992- /* the account has been reported as failing. We can now close this
2993- * request, and tell the application that UI interaction is forbidden.
2994- */
2995+}
2996+
2997+void RequestPrivate::onNotificationClosed()
2998+{
2999+ Q_Q(Request);
3000+
3001+ DEBUG();
3002+
3003+ /* setResult() should have been called by onActionInvoked(), but calling it
3004+ * twice won't harm because only the first invocation counts. */
3005 QVariantMap result;
3006 result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_FORBIDDEN;
3007 q->setResult(result);
3008- return true;
3009+
3010+ m_notification->deleteLater();
3011+ m_notification = 0;
3012 }
3013
3014-Request *Request::newRequest(const QDBusConnection &connection,
3015- const QDBusMessage &message,
3016+#ifndef NO_REQUEST_FACTORY
3017+Request *Request::newRequest(int id,
3018+ const QString &clientProfile,
3019 const QVariantMap &parameters,
3020 QObject *parent)
3021 {
3022 if (parameters.contains(SSOUI_KEY_OPENURL)) {
3023- return new SignOnUi::BrowserRequest(connection, message,
3024+ return new SignOnUi::BrowserRequest(id, clientProfile,
3025 parameters, parent);
3026 } else {
3027 return 0; // TODO new DialogRequest(connection, message, parameters, parent);
3028 }
3029 }
3030+#endif
3031
3032-Request::Request(const QDBusConnection &connection,
3033- const QDBusMessage &message,
3034+Request::Request(int id,
3035+ const QString &clientProfile,
3036 const QVariantMap &parameters,
3037 QObject *parent):
3038- OnlineAccountsUi::Request(connection, message, parameters, parent),
3039+ OnlineAccountsUi::Request(SIGNONUI_INTERFACE, id, clientProfile,
3040+ parameters, parent),
3041 d_ptr(new RequestPrivate(this))
3042 {
3043 }
3044@@ -176,21 +222,35 @@
3045 {
3046 }
3047
3048-QString Request::id(const QVariantMap &parameters)
3049+QString Request::ssoId(const QVariantMap &parameters)
3050 {
3051 return parameters[SSOUI_KEY_REQUESTID].toString();
3052 }
3053
3054-QString Request::id() const
3055+QString Request::ssoId() const
3056 {
3057- return Request::id(parameters());
3058+ return Request::ssoId(parameters());
3059 }
3060
3061 void Request::setWindow(QWindow *window)
3062 {
3063 Q_D(Request);
3064- if (!d->setWindow(window))
3065+
3066+ /* While a notification is shown, ignore any further calls to
3067+ * setWindow(). */
3068+ if (d->m_notification) return;
3069+
3070+ /* The first time that this method is called, we handle it by presenting a
3071+ * snap decision to the user.
3072+ * Then, if this is called again with the same QWindow, it means that the
3073+ * snap decision was accepted, and we show the window.
3074+ */
3075+ if (window == d->m_window) {
3076 OnlineAccountsUi::Request::setWindow(window);
3077+ d->m_window = 0;
3078+ } else {
3079+ d->setWindow(window);
3080+ }
3081 }
3082
3083 uint Request::identity() const
3084@@ -232,12 +292,10 @@
3085
3086 void Request::setCanceled()
3087 {
3088- if (isInProgress()) {
3089- QVariantMap result;
3090- result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_CANCELED;
3091+ QVariantMap result;
3092+ result[SSOUI_KEY_ERROR] = SignOn::QUERY_ERROR_CANCELED;
3093
3094- setResult(result);
3095- }
3096+ setResult(result);
3097 }
3098
3099 #include "signonui-request.moc"
3100
3101=== modified file 'online-accounts-ui/signonui-request.h'
3102--- src/signonui-request.h 2014-04-29 14:34:44 +0000
3103+++ online-accounts-ui/signonui-request.h 2014-08-18 13:33:35 +0000
3104@@ -33,14 +33,14 @@
3105 Q_OBJECT
3106
3107 public:
3108- static Request *newRequest(const QDBusConnection &connection,
3109- const QDBusMessage &message,
3110+ static Request *newRequest(int id,
3111+ const QString &clientProfile,
3112 const QVariantMap &parameters,
3113 QObject *parent = 0);
3114 ~Request();
3115
3116- static QString id(const QVariantMap &parameters);
3117- QString id() const;
3118+ static QString ssoId(const QVariantMap &parameters);
3119+ QString ssoId() const;
3120
3121 uint identity() const;
3122 QString method() const;
3123@@ -53,8 +53,8 @@
3124 bool hasHandler() const { return handler() != 0; }
3125
3126 protected:
3127- explicit Request(const QDBusConnection &connection,
3128- const QDBusMessage &message,
3129+ explicit Request(int id,
3130+ const QString &clientProfile,
3131 const QVariantMap &parameters,
3132 QObject *parent = 0);
3133 virtual void setWindow(QWindow *window) Q_DECL_OVERRIDE;
3134
3135=== added file 'online-accounts-ui/ui-server.cpp'
3136--- online-accounts-ui/ui-server.cpp 1970-01-01 00:00:00 +0000
3137+++ online-accounts-ui/ui-server.cpp 2014-08-18 13:33:35 +0000
3138@@ -0,0 +1,199 @@
3139+/*
3140+ * Copyright (C) 2014 Canonical Ltd.
3141+ *
3142+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
3143+ *
3144+ * This file is part of online-accounts-ui
3145+ *
3146+ * This program is free software: you can redistribute it and/or modify it
3147+ * under the terms of the GNU General Public License version 3, as published
3148+ * by the Free Software Foundation.
3149+ *
3150+ * This program is distributed in the hope that it will be useful, but
3151+ * WITHOUT ANY WARRANTY; without even the implied warranties of
3152+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3153+ * PURPOSE. See the GNU General Public License for more details.
3154+ *
3155+ * You should have received a copy of the GNU General Public License along
3156+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3157+ */
3158+
3159+#include "debug.h"
3160+#include "ipc.h"
3161+#include "request.h"
3162+#include "request-handler.h"
3163+#include "signonui-request.h"
3164+#include "ui-server.h"
3165+
3166+#include <QLocalSocket>
3167+#include <QtQml>
3168+#include <SignOn/uisessiondata_priv.h>
3169+
3170+using namespace OnlineAccountsUi;
3171+
3172+static UiServer *m_instance = 0;
3173+
3174+namespace OnlineAccountsUi {
3175+
3176+class UiServerPrivate: public QObject
3177+{
3178+ Q_OBJECT
3179+ Q_DECLARE_PUBLIC(UiServer)
3180+
3181+public:
3182+ inline UiServerPrivate(const QString &address, UiServer *pluginServer);
3183+ inline ~UiServerPrivate();
3184+
3185+ bool setupSocket();
3186+ bool init();
3187+ void sendOperation(const QVariantMap &data);
3188+
3189+private Q_SLOTS:
3190+ void onDataReady(QByteArray &data);
3191+ void onRequestCompleted();
3192+
3193+private:
3194+ QLocalSocket m_socket;
3195+ OnlineAccountsUi::Ipc m_ipc;
3196+ mutable UiServer *q_ptr;
3197+};
3198+
3199+} // namespace
3200+
3201+UiServerPrivate::UiServerPrivate(const QString &address,
3202+ UiServer *pluginServer):
3203+ QObject(pluginServer),
3204+ q_ptr(pluginServer)
3205+{
3206+ QObject::connect(&m_ipc, SIGNAL(dataReady(QByteArray &)),
3207+ this, SLOT(onDataReady(QByteArray &)));
3208+ QObject::connect(&m_socket, SIGNAL(disconnected()),
3209+ q_ptr, SIGNAL(finished()));
3210+ m_socket.connectToServer(address);
3211+}
3212+
3213+UiServerPrivate::~UiServerPrivate()
3214+{
3215+ DEBUG();
3216+}
3217+
3218+void UiServerPrivate::sendOperation(const QVariantMap &data)
3219+{
3220+ QByteArray ba;
3221+ QDataStream stream(&ba, QIODevice::WriteOnly);
3222+ stream << data;
3223+ m_ipc.write(ba);
3224+}
3225+
3226+void UiServerPrivate::onDataReady(QByteArray &data)
3227+{
3228+ Q_Q(UiServer);
3229+
3230+ QVariantMap map;
3231+ QDataStream stream(&data, QIODevice::ReadOnly);
3232+ stream >> map;
3233+
3234+ DEBUG() << map;
3235+
3236+ QString code = map.value(OAU_OPERATION_CODE).toString();
3237+ if (code == OAU_OPERATION_CODE_PROCESS) {
3238+ QVariantMap parameters = map[OAU_OPERATION_DATA].toMap();
3239+ Request *request =
3240+ Request::newRequest(map[OAU_OPERATION_INTERFACE].toString(),
3241+ map[OAU_OPERATION_ID].toInt(),
3242+ map[OAU_OPERATION_CLIENT_PROFILE].toString(),
3243+ parameters,
3244+ this);
3245+ QObject::connect(request, SIGNAL(completed()),
3246+ this, SLOT(onRequestCompleted()));
3247+
3248+ /* Check if a RequestHandler has been setup to handle this request. If
3249+ * so, bing the request object to the handler and start the request
3250+ * immediately. */
3251+ SignOnUi::Request *signonRequest =
3252+ qobject_cast<SignOnUi::Request*>(request);
3253+ if (signonRequest) {
3254+ SignOnUi::RequestHandler *handler =
3255+ SignOnUi::RequestHandler::findMatching(parameters);
3256+ if (handler) {
3257+ signonRequest->setHandler(handler);
3258+ }
3259+ }
3260+ request->start();
3261+ } else {
3262+ qWarning() << "Invalid operation code: " << code;
3263+ }
3264+}
3265+
3266+void UiServerPrivate::onRequestCompleted()
3267+{
3268+ Request *request = qobject_cast<Request*>(sender());
3269+ request->disconnect(this);
3270+ request->deleteLater();
3271+
3272+ if (request->errorName().isEmpty()) {
3273+ QVariantMap operation;
3274+ operation.insert(OAU_OPERATION_CODE,
3275+ OAU_OPERATION_CODE_REQUEST_FINISHED);
3276+ operation.insert(OAU_OPERATION_ID, request->id());
3277+ operation.insert(OAU_OPERATION_DATA, request->result());
3278+ operation.insert(OAU_OPERATION_INTERFACE, request->interface());
3279+ sendOperation(operation);
3280+ } else {
3281+ QVariantMap operation;
3282+ operation.insert(OAU_OPERATION_CODE,
3283+ OAU_OPERATION_CODE_REQUEST_FAILED);
3284+ operation.insert(OAU_OPERATION_ID, request->id());
3285+ operation.insert(OAU_OPERATION_INTERFACE, request->interface());
3286+ operation.insert(OAU_OPERATION_ERROR_NAME, request->errorName());
3287+ operation.insert(OAU_OPERATION_ERROR_MESSAGE, request->errorMessage());
3288+ sendOperation(operation);
3289+ }
3290+}
3291+
3292+bool UiServerPrivate::init()
3293+{
3294+ if (Q_UNLIKELY(!m_socket.waitForConnected())) return false;
3295+
3296+ m_ipc.setChannels(&m_socket, &m_socket);
3297+ return true;
3298+}
3299+
3300+UiServer::UiServer(const QString &address, QObject *parent):
3301+ QObject(parent),
3302+ d_ptr(new UiServerPrivate(address, this))
3303+{
3304+ m_instance = this;
3305+
3306+ qmlRegisterType<SignOnUi::RequestHandler>("Ubuntu.OnlineAccounts.Plugin",
3307+ 1, 0, "RequestHandler");
3308+}
3309+
3310+UiServer::~UiServer()
3311+{
3312+ m_instance = 0;
3313+}
3314+
3315+UiServer *UiServer::instance()
3316+{
3317+ return m_instance;
3318+}
3319+
3320+bool UiServer::init()
3321+{
3322+ Q_D(UiServer);
3323+ return d->init();
3324+}
3325+
3326+void UiServer::registerHandler(SignOnUi::RequestHandler *handler)
3327+{
3328+ Q_D(UiServer);
3329+
3330+ QVariantMap operation;
3331+ operation.insert(OAU_OPERATION_CODE,
3332+ OAU_OPERATION_CODE_REGISTER_HANDLER);
3333+ operation.insert(OAU_OPERATION_HANDLER_ID, handler->matchId());
3334+ d->sendOperation(operation);
3335+}
3336+
3337+#include "ui-server.moc"
3338
3339=== added file 'online-accounts-ui/ui-server.h'
3340--- online-accounts-ui/ui-server.h 1970-01-01 00:00:00 +0000
3341+++ online-accounts-ui/ui-server.h 2014-08-18 13:33:35 +0000
3342@@ -0,0 +1,57 @@
3343+/*
3344+ * Copyright (C) 2014 Canonical Ltd.
3345+ *
3346+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
3347+ *
3348+ * This file is part of online-accounts-ui
3349+ *
3350+ * This program is free software: you can redistribute it and/or modify it
3351+ * under the terms of the GNU General Public License version 3, as published
3352+ * by the Free Software Foundation.
3353+ *
3354+ * This program is distributed in the hope that it will be useful, but
3355+ * WITHOUT ANY WARRANTY; without even the implied warranties of
3356+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3357+ * PURPOSE. See the GNU General Public License for more details.
3358+ *
3359+ * You should have received a copy of the GNU General Public License along
3360+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3361+ */
3362+
3363+#ifndef OAU_UI_SERVER_H
3364+#define OAU_UI_SERVER_H
3365+
3366+#include <QObject>
3367+#include <QVariantMap>
3368+
3369+namespace SignOnUi {
3370+class RequestHandler;
3371+}
3372+
3373+namespace OnlineAccountsUi {
3374+
3375+class UiServerPrivate;
3376+class UiServer: public QObject
3377+{
3378+ Q_OBJECT
3379+
3380+public:
3381+ explicit UiServer(const QString &address, QObject *parent = 0);
3382+ ~UiServer();
3383+
3384+ static UiServer *instance();
3385+
3386+ bool init();
3387+ void registerHandler(SignOnUi::RequestHandler *handler);
3388+
3389+Q_SIGNALS:
3390+ void finished();
3391+
3392+private:
3393+ UiServerPrivate *d_ptr;
3394+ Q_DECLARE_PRIVATE(UiServer)
3395+};
3396+
3397+} // namespace
3398+
3399+#endif // OAU_UI_SERVER_H
3400
3401=== modified file 'online-accounts-ui/ui.qrc'
3402--- src/ui.qrc 2014-03-19 12:14:14 +0000
3403+++ online-accounts-ui/ui.qrc 2014-08-18 13:33:35 +0000
3404@@ -1,17 +1,8 @@
3405 <!DOCTYPE RCC><RCC version="1.0">
3406 <qresource>
3407 <file>qml/AccountCreationPage.qml</file>
3408- <file>qml/AccountEditPage.qml</file>
3409- <file>qml/AccountItem.qml</file>
3410- <file>qml/AccountsPage.qml</file>
3411- <file>qml/AddAccountLabel.qml</file>
3412 <file>qml/AuthorizationPage.qml</file>
3413- <file>qml/MainPage.qml</file>
3414- <file>qml/NewAccountPage.qml</file>
3415- <file>qml/NoAccountsPage.qml</file>
3416- <file>qml/NormalStartupPage.qml</file>
3417- <file>qml/ProviderPluginList.qml</file>
3418 <file>qml/ProviderRequest.qml</file>
3419- <file>qml/ProvidersList.qml</file>
3420+ <file>qml/SignOnUiPage.qml</file>
3421 </qresource>
3422 </RCC>
3423
3424=== renamed file 'src/qml/AccountEditPage.qml' => 'system-settings-plugin/AccountEditPage.qml'
3425--- src/qml/AccountEditPage.qml 2014-03-19 12:14:14 +0000
3426+++ system-settings-plugin/AccountEditPage.qml 2014-08-18 13:33:35 +0000
3427@@ -19,6 +19,7 @@
3428 import QtQuick 2.0
3429 import Ubuntu.Components 0.1
3430 import Ubuntu.OnlineAccounts 0.1
3431+import Ubuntu.OnlineAccounts.Plugin 1.0
3432
3433 Page {
3434 id: root
3435@@ -34,26 +35,9 @@
3436 objectHandle: accountHandle
3437 }
3438
3439- Loader {
3440- id: loader
3441- property var account: account
3442-
3443- anchors.fill: parent
3444- source: localQmlPluginPath + account.provider.id + "/Main.qml"
3445-
3446- onStatusChanged: {
3447- if (loader.status == Loader.Error) {
3448- loader.source = systemQmlPluginPath + account.provider.id + "/Main.qml"
3449- }
3450- }
3451-
3452-
3453- Connections {
3454- target: loader.item
3455- onFinished: {
3456- console.log("====== PLUGIN FINISHED ======")
3457- root.finished()
3458- }
3459+ Options {
3460+ onFinished: {
3461+ root.finished()
3462 }
3463 }
3464 }
3465
3466=== renamed file 'src/qml/AccountItem.qml' => 'system-settings-plugin/AccountItem.qml'
3467=== renamed file 'src/qml/AccountsPage.qml' => 'system-settings-plugin/AccountsPage.qml'
3468=== renamed file 'src/qml/AddAccountLabel.qml' => 'system-settings-plugin/AddAccountLabel.qml'
3469=== renamed file 'src/qml/MainPage.qml' => 'system-settings-plugin/MainPage.qml'
3470--- src/qml/MainPage.qml 2014-05-29 21:14:11 +0000
3471+++ system-settings-plugin/MainPage.qml 2014-08-18 13:33:35 +0000
3472@@ -17,52 +17,36 @@
3473 */
3474
3475 import QtQuick 2.0
3476+import SystemSettings 1.0
3477 import Ubuntu.Components 0.1
3478+import Ubuntu.Components.ListItems 0.1 as ListItem
3479 import Ubuntu.OnlineAccounts 0.1
3480
3481-MainView {
3482+ItemPage {
3483 id: root
3484- width: units.gu(48)
3485- height: units.gu(60)
3486- useDeprecatedToolbar: false
3487-
3488- Component.onCompleted: {
3489- i18n.domain = "ubuntu-system-settings-online-accounts"
3490- pageStack.push(mainPage)
3491- }
3492-
3493- PageStack {
3494- id: pageStack
3495-
3496- Page {
3497- id: mainPage
3498- title: i18n.tr("Accounts")
3499- tools: ToolbarItems {
3500- back: ActionItem {
3501- action: Action {
3502- iconName: "back"
3503- onTriggered: mainWindow.close()
3504- }
3505- }
3506- }
3507-
3508- Loader {
3509- id: loader
3510- anchors.fill: parent
3511- sourceComponent: pluginOptions.provider ? accountCreationPage : normalStartupPage
3512- }
3513- }
3514- }
3515-
3516- Component {
3517- id: normalStartupPage
3518- NormalStartupPage {}
3519- }
3520-
3521- Component {
3522- id: accountCreationPage
3523- AccountCreationPage {
3524- providerId: pluginOptions.provider
3525- }
3526- }
3527-}
3528+ objectName: "accountsPage"
3529+
3530+ title: i18n.tr("Accounts")
3531+
3532+ property Item flickable: accountsPage.visible ? accountsPage : noAccountsPage
3533+
3534+ AccountServiceModel {
3535+ id: accountsModel
3536+ service: "global"
3537+ includeDisabled: true
3538+ }
3539+
3540+ AccountsPage {
3541+ id: accountsPage
3542+ anchors.fill: parent
3543+ accountsModel: accountsModel
3544+ visible: accountsModel.count > 0
3545+ }
3546+
3547+ NoAccountsPage {
3548+ id: noAccountsPage
3549+ anchors.fill: parent
3550+ accountsModel: accountsModel
3551+ visible:!accountsPage.visible
3552+ }
3553+}
3554\ No newline at end of file
3555
3556=== renamed file 'src/qml/NewAccountPage.qml' => 'system-settings-plugin/NewAccountPage.qml'
3557--- src/qml/NewAccountPage.qml 2013-10-17 11:08:57 +0000
3558+++ system-settings-plugin/NewAccountPage.qml 2014-08-18 13:33:35 +0000
3559@@ -29,8 +29,6 @@
3560
3561 ProviderPluginList {
3562 onCreationFinished: {
3563- // pop the creation page and this page (go back to parent page)
3564- pageStack.pop()
3565 pageStack.pop()
3566 }
3567 }
3568
3569=== renamed file 'src/qml/NoAccountsPage.qml' => 'system-settings-plugin/NoAccountsPage.qml'
3570=== renamed file 'src/qml/NormalStartupPage.qml' => 'system-settings-plugin/NormalStartupPage.qml'
3571=== renamed file 'src/qml/ProviderPluginList.qml' => 'system-settings-plugin/ProviderPluginList.qml'
3572--- src/qml/ProviderPluginList.qml 2013-10-17 11:08:57 +0000
3573+++ system-settings-plugin/ProviderPluginList.qml 2014-08-18 13:33:35 +0000
3574@@ -18,25 +18,29 @@
3575
3576 import QtQuick 2.0
3577 import Ubuntu.Components 0.1
3578+import Ubuntu.OnlineAccounts.Client 0.1
3579
3580 ProvidersList {
3581 id: root
3582
3583- property variant __creationPage: null
3584+ property bool __processing: false
3585
3586 signal creationFinished
3587
3588+ Setup {
3589+ id: setup
3590+ applicationId: "system-settings"
3591+ onFinished: {
3592+ __processing = false
3593+ creationFinished()
3594+ }
3595+ }
3596+
3597 onProviderClicked: {
3598- __creationPage = accountCreationPage.createObject(null, {
3599- "providerId": providerId })
3600- __creationPage.finished.connect(__onCreationFinished)
3601- pageStack.push(__creationPage)
3602- }
3603-
3604- function __onCreationFinished() {
3605- __creationPage.destroy(1000)
3606- __creationPage.finished.disconnect(__onCreationFinished)
3607- __creationPage = null
3608- creationFinished()
3609+ if (!__processing) {
3610+ __processing = true
3611+ setup.providerId = providerId
3612+ setup.exec()
3613+ }
3614 }
3615 }
3616
3617=== renamed file 'src/qml/ProvidersList.qml' => 'system-settings-plugin/ProvidersList.qml'
3618=== modified file 'system-settings-plugin/online-accounts.settings'
3619--- system-settings-plugin/online-accounts.settings 2013-09-09 10:56:53 +0000
3620+++ system-settings-plugin/online-accounts.settings 2014-08-18 13:33:35 +0000
3621@@ -11,5 +11,5 @@
3622 ],
3623 "has-dynamic-keywords": true,
3624 "has-dynamic-visibility": false,
3625- "plugin": "online-accounts"
3626+ "page-component": "MainPage.qml"
3627 }
3628
3629=== removed file 'system-settings-plugin/plugin.cpp'
3630--- system-settings-plugin/plugin.cpp 2014-05-30 08:55:40 +0000
3631+++ system-settings-plugin/plugin.cpp 1970-01-01 00:00:00 +0000
3632@@ -1,89 +0,0 @@
3633-/*
3634- * Copyright (C) 2013 Canonical Ltd.
3635- *
3636- * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
3637- *
3638- * This program is free software: you can redistribute it and/or modify it
3639- * under the terms of the GNU General Public License version 3, as published
3640- * by the Free Software Foundation.
3641- *
3642- * This program is distributed in the hope that it will be useful, but
3643- * WITHOUT ANY WARRANTY; without even the implied warranties of
3644- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3645- * PURPOSE. See the GNU General Public License for more details.
3646- *
3647- * You should have received a copy of the GNU General Public License along
3648- * with this program. If not, see <http://www.gnu.org/licenses/>.
3649- */
3650-
3651-#include "plugin.h"
3652-
3653-#include <OnlineAccountsClient/Setup>
3654-#include <QDebug>
3655-#include <QStringList>
3656-#include <SystemSettings/ItemBase>
3657-
3658-using namespace SystemSettings;
3659-
3660-class Item: public ItemBase
3661-{
3662- Q_OBJECT
3663-
3664-public:
3665- Item(const QVariantMap &staticData, QObject *parent = 0);
3666- ~Item();
3667-
3668- QQmlComponent *pageComponent(QQmlEngine *engine,
3669- QObject *parent = 0) Q_DECL_OVERRIDE;
3670-
3671-private Q_SLOTS:
3672- void onFinished();
3673-
3674-private:
3675- OnlineAccountsClient::Setup m_setup;
3676- bool m_isOpen;
3677-};
3678-
3679-Item::Item(const QVariantMap &staticData, QObject *parent):
3680- ItemBase(staticData, parent),
3681- m_isOpen(false)
3682-{
3683- QObject::connect(&m_setup, SIGNAL(finished()),
3684- this, SLOT(onFinished()));
3685-}
3686-
3687-Item::~Item()
3688-{
3689-}
3690-
3691-QQmlComponent *Item::pageComponent(QQmlEngine *engine,
3692- QObject *parent)
3693-{
3694- Q_UNUSED(engine);
3695- Q_UNUSED(parent);
3696-
3697- if (!m_isOpen) {
3698- qDebug() << "Opening Online Accounts";
3699- m_isOpen = true;
3700- m_setup.exec();
3701- }
3702- return 0;
3703-}
3704-
3705-void Item::onFinished()
3706-{
3707- m_isOpen = false;
3708-}
3709-
3710-Plugin::Plugin():
3711- QObject()
3712-{
3713-}
3714-
3715-ItemBase *Plugin::createItem(const QVariantMap &staticData,
3716- QObject *parent)
3717-{
3718- return new Item(staticData, parent);
3719-}
3720-
3721-#include "plugin.moc"
3722
3723=== removed file 'system-settings-plugin/plugin.h'
3724--- system-settings-plugin/plugin.h 2013-09-09 10:56:53 +0000
3725+++ system-settings-plugin/plugin.h 1970-01-01 00:00:00 +0000
3726@@ -1,38 +0,0 @@
3727-/*
3728- * Copyright (C) 2013 Canonical Ltd.
3729- *
3730- * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
3731- *
3732- * This program is free software: you can redistribute it and/or modify it
3733- * under the terms of the GNU General Public License version 3, as published
3734- * by the Free Software Foundation.
3735- *
3736- * This program is distributed in the hope that it will be useful, but
3737- * WITHOUT ANY WARRANTY; without even the implied warranties of
3738- * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3739- * PURPOSE. See the GNU General Public License for more details.
3740- *
3741- * You should have received a copy of the GNU General Public License along
3742- * with this program. If not, see <http://www.gnu.org/licenses/>.
3743- */
3744-
3745-#ifndef ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
3746-#define ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
3747-
3748-#include <QObject>
3749-#include <SystemSettings/PluginInterface>
3750-
3751-class Plugin: public QObject, public SystemSettings::PluginInterface
3752-{
3753- Q_OBJECT
3754- Q_PLUGIN_METADATA(IID "com.ubuntu.SystemSettings.PluginInterface")
3755- Q_INTERFACES(SystemSettings::PluginInterface)
3756-
3757-public:
3758- Plugin();
3759-
3760- SystemSettings::ItemBase *createItem(const QVariantMap &staticData,
3761- QObject *parent = 0);
3762-};
3763-
3764-#endif // ONLINE_ACCOUNTS_SYSTEM_SETTINGS_PLUGIN_H
3765
3766=== modified file 'system-settings-plugin/system-settings-plugin.pro'
3767--- system-settings-plugin/system-settings-plugin.pro 2013-09-09 10:56:53 +0000
3768+++ system-settings-plugin/system-settings-plugin.pro 2014-08-18 13:33:35 +0000
3769@@ -1,41 +1,28 @@
3770 include(../common-project-config.pri)
3771 include($${TOP_SRC_DIR}/common-vars.pri)
3772
3773-TEMPLATE = lib
3774-TARGET = online-accounts
3775-
3776-CONFIG += \
3777- link_pkgconfig \
3778- plugin \
3779- qt
3780-
3781-QT += \
3782- core \
3783- qml
3784-
3785-PKGCONFIG += \
3786- SystemSettings
3787-
3788-LIBS += -lonline-accounts-client
3789-QMAKE_LIBDIR += $${TOP_BUILD_DIR}/client/OnlineAccountsClient
3790-INCLUDEPATH += \
3791- $${TOP_SRC_DIR}/client \
3792- /usr/include
3793-
3794-SOURCES += \
3795- plugin.cpp
3796-
3797-HEADERS += \
3798- plugin.h
3799+TEMPLATE=aux
3800+
3801+QML_SOURCES = \
3802+ AccountEditPage.qml \
3803+ AccountItem.qml \
3804+ AccountsPage.qml \
3805+ AddAccountLabel.qml \
3806+ MainPage.qml \
3807+ NewAccountPage.qml \
3808+ NoAccountsPage.qml \
3809+ NormalStartupPage.qml \
3810+ ProviderPluginList.qml \
3811+ ProvidersList.qml
3812
3813 settings.files = online-accounts.settings
3814 settings.path = $${PLUGIN_MANIFEST_DIR}
3815 INSTALLS += settings
3816
3817-target.path = $${PLUGIN_MODULE_DIR}
3818-INSTALLS += target
3819-
3820 image.files = settings-accounts.svg
3821 image.path = $${PLUGIN_MANIFEST_DIR}/icons
3822 INSTALLS += image
3823
3824+qml.files = $${QML_SOURCES}
3825+qml.path = $${PLUGIN_QML_DIR}/online-accounts
3826+INSTALLS += qml
3827
3828=== modified file 'tests/client/tst_client.cpp'
3829--- tests/client/tst_client.cpp 2014-03-19 12:14:14 +0000
3830+++ tests/client/tst_client.cpp 2014-08-18 13:33:35 +0000
3831@@ -20,7 +20,7 @@
3832 * <http://www.gnu.org/licenses/>.
3833 */
3834
3835-#include "src/globals.h"
3836+#include "online-accounts-ui/globals.h"
3837
3838 #include <OnlineAccountsClient/Setup>
3839 #include <QDBusConnection>
3840
3841=== modified file 'tests/client/tst_qml_client.cpp'
3842--- tests/client/tst_qml_client.cpp 2014-03-19 12:14:14 +0000
3843+++ tests/client/tst_qml_client.cpp 2014-08-18 13:33:35 +0000
3844@@ -20,7 +20,7 @@
3845 * <http://www.gnu.org/licenses/>.
3846 */
3847
3848-#include "src/globals.h"
3849+#include "online-accounts-ui/globals.h"
3850
3851 #include <QDBusConnection>
3852 #include <QDebug>
3853
3854=== added directory 'tests/online-accounts-service'
3855=== added file 'tests/online-accounts-service/online-accounts-service.pro'
3856--- tests/online-accounts-service/online-accounts-service.pro 1970-01-01 00:00:00 +0000
3857+++ tests/online-accounts-service/online-accounts-service.pro 2014-08-18 13:33:35 +0000
3858@@ -0,0 +1,4 @@
3859+TEMPLATE = subdirs
3860+SUBDIRS = \
3861+ tst_inactivity_timer.pro \
3862+ tst_service.pro
3863
3864=== renamed file 'tests/online-accounts-ui/tst_inactivity_timer.cpp' => 'tests/online-accounts-service/tst_inactivity_timer.cpp'
3865=== renamed file 'tests/online-accounts-ui/tst_inactivity_timer.pro' => 'tests/online-accounts-service/tst_inactivity_timer.pro'
3866--- tests/online-accounts-ui/tst_inactivity_timer.pro 2014-03-07 07:44:07 +0000
3867+++ tests/online-accounts-service/tst_inactivity_timer.pro 2014-08-18 13:33:35 +0000
3868@@ -9,15 +9,19 @@
3869 core \
3870 testlib
3871
3872+ONLINE_ACCOUNTS_SERVICE_DIR = $${TOP_SRC_DIR}/online-accounts-service
3873+COMMON_SRC_DIR = $${TOP_SRC_DIR}/online-accounts-ui
3874+
3875 SOURCES += \
3876- $${TOP_SRC_DIR}/src/inactivity-timer.cpp \
3877+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/inactivity-timer.cpp \
3878 tst_inactivity_timer.cpp
3879
3880 HEADERS += \
3881- $${TOP_SRC_DIR}/src/inactivity-timer.h
3882+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/inactivity-timer.h
3883
3884 INCLUDEPATH += \
3885- $${TOP_SRC_DIR}/src
3886+ $${COMMON_SRC_DIR} \
3887+ $${ONLINE_ACCOUNTS_SERVICE_DIR}
3888
3889 check.commands = "xvfb-run -s '-screen 0 640x480x24' -a ./$${TARGET}"
3890 check.depends = $${TARGET}
3891
3892=== renamed file 'tests/online-accounts-ui/tst_service.cpp' => 'tests/online-accounts-service/tst_service.cpp'
3893--- tests/online-accounts-ui/tst_service.cpp 2014-04-29 12:11:55 +0000
3894+++ tests/online-accounts-service/tst_service.cpp 2014-08-18 13:33:35 +0000
3895@@ -22,7 +22,7 @@
3896 #include "request.h"
3897 #include "request-manager.h"
3898 #include "service.h"
3899-#include "window-watcher.h"
3900+#include "ui-proxy.h"
3901
3902 #include <QDBusArgument>
3903 #include <QDBusConnection>
3904@@ -38,58 +38,12 @@
3905
3906 using namespace OnlineAccountsUi;
3907
3908-static const QString keyTimeout(QStringLiteral("timeout"));
3909-static const QString keyFail(QStringLiteral("fail"));
3910-
3911 #define P2P_SOCKET "unix:path=/tmp/tst_service_%1"
3912 #define TEST_SERVICE_NAME \
3913 QStringLiteral("com.ubuntu.OnlineAccountsUi.Test")
3914 #define TEST_OBJECT_PATH QStringLiteral("/")
3915
3916-class TestRequest: public Request
3917-{
3918- Q_OBJECT
3919-
3920-public:
3921- TestRequest(const QDBusConnection &connection,
3922- const QDBusMessage &message,
3923- const QVariantMap &parameters,
3924- QObject *parent = 0):
3925- Request(connection, message, parameters, parent)
3926- {
3927- m_timer.setSingleShot(true);
3928- m_timer.setInterval(parameters.value(keyTimeout).toInt());
3929- QObject::connect(&m_timer, SIGNAL(timeout()),
3930- this, SLOT(onTimeout()));
3931- }
3932-
3933- void start() Q_DECL_OVERRIDE {
3934- Request::start();
3935- QWindow *window = new QWindow;
3936- setWindow(window);
3937- m_timer.start();
3938- }
3939-
3940-private Q_SLOTS:
3941- void onTimeout() {
3942- if (parameters().contains(keyFail)) {
3943- fail(parameters().value(keyFail).toString(), "Request failed");
3944- } else {
3945- setResult(parameters());
3946- }
3947- }
3948-
3949-private:
3950- QTimer m_timer;
3951-};
3952-
3953-Request *Request::newRequest(const QDBusConnection &connection,
3954- const QDBusMessage &message,
3955- const QVariantMap &parameters,
3956- QObject *parent)
3957-{
3958- return new TestRequest(connection, message, parameters, parent);
3959-}
3960+QList<UiProxyPrivate *> m_uiProxies;
3961
3962 class RequestReply: public QDBusPendingCallWatcher
3963 {
3964@@ -107,6 +61,7 @@
3965 bool isError() const { return m_isError; }
3966 QVariantMap reply() const { return m_reply; }
3967 QString errorName() const { return m_errorName; }
3968+ QString errorMessage() const { return m_errorMessage; }
3969
3970 private Q_SLOTS:
3971 void onFinished() {
3972@@ -114,6 +69,7 @@
3973 if (reply.isError()) {
3974 m_isError = true;
3975 m_errorName = reply.error().name();
3976+ m_errorMessage = reply.error().message();
3977 } else {
3978 m_reply = qdbus_cast<QVariantMap>(reply.argumentAt(0).
3979 value<QDBusArgument>());
3980@@ -128,6 +84,7 @@
3981 bool m_isError;
3982 QVariantMap m_reply;
3983 QString m_errorName;
3984+ QString m_errorMessage;
3985 };
3986
3987 class ServiceTest: public QObject
3988@@ -154,8 +111,6 @@
3989 void testResults();
3990 void testFailure();
3991 void testIdle();
3992- void testWindow();
3993- void testWindowTransiency();
3994
3995 protected Q_SLOTS:
3996 void onNewConnection(const QDBusConnection &connection);
3997@@ -166,6 +121,73 @@
3998 QDBusConnection m_connection;
3999 };
4000
4001+/* Mocking UiProxy { */
4002+namespace OnlineAccountsUi {
4003+
4004+class UiProxyPrivate: public QObject
4005+{
4006+ Q_OBJECT
4007+ Q_DECLARE_PUBLIC(UiProxy)
4008+
4009+public:
4010+ UiProxyPrivate(UiProxy *uiProxy):
4011+ QObject(uiProxy),
4012+ m_initCount(0),
4013+ m_initReply(true),
4014+ q_ptr(uiProxy)
4015+ {
4016+ }
4017+ ~UiProxyPrivate() {};
4018+
4019+ void emitFinished() { Q_EMIT q_ptr->finished(); }
4020+
4021+Q_SIGNALS:
4022+ void handleRequestCalled();
4023+
4024+public:
4025+ int m_initCount;
4026+ bool m_initReply;
4027+ QList<Request*> m_requests;
4028+ QVariantMap m_expectedHasHandlerFor;
4029+ mutable UiProxy *q_ptr;
4030+};
4031+
4032+} // namespace
4033+
4034+UiProxy::UiProxy(pid_t, QObject *parent):
4035+ QObject(parent),
4036+ d_ptr(new UiProxyPrivate(this))
4037+{
4038+ m_uiProxies.append(d_ptr);
4039+}
4040+
4041+UiProxy::~UiProxy()
4042+{
4043+ m_uiProxies.removeOne(d_ptr);
4044+}
4045+
4046+bool UiProxy::init()
4047+{
4048+ Q_D(UiProxy);
4049+ d->m_initCount++;
4050+ return d->m_initReply;
4051+}
4052+
4053+void UiProxy::handleRequest(Request *request)
4054+{
4055+ Q_D(UiProxy);
4056+ d->m_requests.append(request);
4057+ Q_EMIT d->handleRequestCalled();
4058+}
4059+
4060+bool UiProxy::hasHandlerFor(const QVariantMap &parameters)
4061+{
4062+ Q_D(UiProxy);
4063+ return parameters == d->m_expectedHasHandlerFor;
4064+}
4065+
4066+/* } mocking UiProxy */
4067+
4068 ServiceTest::ServiceTest():
4069 QObject(0),
4070 m_connection(QStringLiteral("uninitialized"))
4071@@ -205,30 +227,60 @@
4072 void ServiceTest::testResults()
4073 {
4074 QVariantMap parameters;
4075- parameters.insert(keyTimeout, 10);
4076 parameters.insert("hello", QString("world"));
4077 RequestReply *call = sendRequest(parameters);
4078 QSignalSpy callFinished(call, SIGNAL(finished()));
4079
4080+ QTRY_COMPARE(m_uiProxies.count(), 1);
4081+
4082+ UiProxyPrivate *proxy = m_uiProxies[0];
4083+ QCOMPARE(proxy->m_initCount, 1);
4084+ QCOMPARE(proxy->m_requests.count(), 1);
4085+
4086+ Request *request = proxy->m_requests.last();
4087+ QCOMPARE(request->parameters(), parameters);
4088+
4089+ request->setInProgress(true);
4090+ request->setResult(parameters);
4091+
4092 QVERIFY(callFinished.wait());
4093 QCOMPARE(call->isError(), false);
4094 QCOMPARE(call->reply(), parameters);
4095 delete call;
4096+
4097+ proxy->emitFinished();
4098+ QTRY_COMPARE(m_uiProxies.count(), 0);
4099 }
4100
4101 void ServiceTest::testFailure()
4102 {
4103+ QVariantMap parameters;
4104+ parameters.insert("hi", "there");
4105+ RequestReply *call = sendRequest(parameters);
4106+ QSignalSpy callFinished(call, SIGNAL(finished()));
4107+
4108+ QTRY_COMPARE(m_uiProxies.count(), 1);
4109+
4110+ UiProxyPrivate *proxy = m_uiProxies[0];
4111+ QCOMPARE(proxy->m_initCount, 1);
4112+ QCOMPARE(proxy->m_requests.count(), 1);
4113+
4114+ Request *request = proxy->m_requests.last();
4115+ QCOMPARE(request->parameters(), parameters);
4116+
4117+ request->setInProgress(true);
4118 QString errorName("com.ubuntu.OnlineAccountsUi.BadLuck");
4119- QVariantMap parameters;
4120- parameters.insert(keyTimeout, 10);
4121- parameters.insert("fail", errorName);
4122- RequestReply *call = sendRequest(parameters);
4123- QSignalSpy callFinished(call, SIGNAL(finished()));
4124+ QString errorMessage("really unlucky");
4125+ request->fail(errorName, errorMessage);
4126
4127 QVERIFY(callFinished.wait());
4128 QCOMPARE(call->isError(), true);
4129 QCOMPARE(call->errorName(), errorName);
4130+ QCOMPARE(call->errorMessage(), errorMessage);
4131 delete call;
4132+
4133+ proxy->emitFinished();
4134+ QTRY_COMPARE(m_uiProxies.count(), 0);
4135 }
4136
4137 void ServiceTest::testIdle()
4138@@ -238,65 +290,37 @@
4139 QSignalSpy isIdleChanged(&m_requestManager, SIGNAL(isIdleChanged()));
4140
4141 QVariantMap parameters;
4142- parameters.insert(keyTimeout, 10);
4143+ parameters.insert("time", "out");
4144 RequestReply *call = sendRequest(parameters);
4145 QSignalSpy callFinished(call, SIGNAL(finished()));
4146
4147 QVERIFY(isIdleChanged.wait());
4148 QCOMPARE(m_requestManager.isIdle(), false);
4149-
4150- /* the request will terminate after 10 milliseconds, so expect the service
4151+ isIdleChanged.clear();
4152+
4153+ QTRY_COMPARE(m_uiProxies.count(), 1);
4154+
4155+ UiProxyPrivate *proxy = m_uiProxies[0];
4156+ QCOMPARE(proxy->m_initCount, 1);
4157+ QCOMPARE(proxy->m_requests.count(), 1);
4158+
4159+ Request *request = proxy->m_requests.last();
4160+ QCOMPARE(request->parameters(), parameters);
4161+
4162+ request->setInProgress(true);
4163+ request->setResult(parameters);
4164+
4165+ /* the request will terminate, so expect the service
4166 * to be idle again */
4167- QVERIFY(isIdleChanged.wait());
4168+ QTRY_COMPARE(isIdleChanged.count(), 1);
4169 QCOMPARE(m_requestManager.isIdle(), true);
4170
4171 QVERIFY(callFinished.wait());
4172 QCOMPARE(call->isError(), false);
4173 delete call;
4174-}
4175-
4176-void ServiceTest::testWindow()
4177-{
4178- QVariantMap parameters;
4179- parameters.insert(keyTimeout, 10);
4180- RequestReply *call = sendRequest(parameters);
4181- QSignalSpy callFinished(call, SIGNAL(finished()));
4182- QSignalSpy windowShown(WindowWatcher::instance(),
4183- SIGNAL(windowShown(QObject*)));
4184-
4185- QVERIFY(windowShown.wait());
4186- QWindow *window =
4187- qobject_cast<QWindow*>(windowShown.at(0).at(0).value<QObject*>());
4188- QVERIFY(window->property("transientParent").isNull());
4189- QVERIFY(callFinished.wait());
4190- QCOMPARE(call->isError(), false);
4191- delete call;
4192-
4193- QCOMPARE(windowShown.count(), 1);
4194-}
4195-
4196-void ServiceTest::testWindowTransiency()
4197-{
4198- QVariantMap parameters;
4199- parameters.insert(keyTimeout, 10);
4200- parameters.insert(OAU_KEY_WINDOW_ID, 371);
4201- RequestReply *call = sendRequest(parameters);
4202- QSignalSpy callFinished(call, SIGNAL(finished()));
4203- QSignalSpy windowShown(WindowWatcher::instance(),
4204- SIGNAL(windowShown(QObject*)));
4205-
4206- QVERIFY(windowShown.wait());
4207- QWindow *window =
4208- qobject_cast<QWindow*>(windowShown.at(0).at(0).value<QObject*>());
4209- QObject *transientParentObject =
4210- window->property("transientParent").value<QObject*>();
4211- QWindow *transientParent = qobject_cast<QWindow*>(transientParentObject);
4212- QCOMPARE(transientParent->property("winId").toUInt(), uint(371));
4213- QVERIFY(callFinished.wait());
4214- QCOMPARE(call->isError(), false);
4215- delete call;
4216-
4217- QCOMPARE(windowShown.count(), 1);
4218+
4219+ proxy->emitFinished();
4220+ QTRY_COMPARE(m_uiProxies.count(), 0);
4221 }
4222
4223 QTEST_MAIN(ServiceTest);
4224
4225=== renamed file 'tests/online-accounts-ui/tst_service.pro' => 'tests/online-accounts-service/tst_service.pro'
4226--- tests/online-accounts-ui/tst_service.pro 2014-04-29 12:11:55 +0000
4227+++ tests/online-accounts-service/tst_service.pro 2014-08-18 13:33:35 +0000
4228@@ -13,23 +13,26 @@
4229 DEFINES += \
4230 NO_REQUEST_FACTORY
4231
4232+ONLINE_ACCOUNTS_SERVICE_DIR = $${TOP_SRC_DIR}/online-accounts-service
4233+COMMON_SRC_DIR = $${TOP_SRC_DIR}/online-accounts-ui
4234+
4235 SOURCES += \
4236- $${TOP_BUILD_DIR}/src/onlineaccountsui_adaptor.cpp \
4237- $${TOP_SRC_DIR}/src/request.cpp \
4238- $${TOP_SRC_DIR}/src/request-manager.cpp \
4239- $${TOP_SRC_DIR}/src/service.cpp \
4240- mock/qwindow.cpp \
4241+ $${TOP_BUILD_DIR}/online-accounts-service/onlineaccountsui_adaptor.cpp \
4242+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/request.cpp \
4243+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/request-manager.cpp \
4244+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/service.cpp \
4245 tst_service.cpp
4246
4247 HEADERS += \
4248- $${TOP_BUILD_DIR}/src/onlineaccountsui_adaptor.h \
4249- $${TOP_SRC_DIR}/src/request.h \
4250- $${TOP_SRC_DIR}/src/request-manager.h \
4251- $${TOP_SRC_DIR}/src/service.h \
4252- window-watcher.h
4253+ $${TOP_BUILD_DIR}/online-accounts-service/onlineaccountsui_adaptor.h \
4254+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/request.h \
4255+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/request-manager.h \
4256+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/service.h \
4257+ $${ONLINE_ACCOUNTS_SERVICE_DIR}/ui-proxy.h \
4258
4259 INCLUDEPATH += \
4260- $${TOP_SRC_DIR}/src
4261+ $${ONLINE_ACCOUNTS_SERVICE_DIR} \
4262+ $${COMMON_SRC_DIR}
4263
4264 check.commands = "xvfb-run -s '-screen 0 640x480x24' -a dbus-test-runner -t ./$${TARGET}"
4265 check.depends = $${TARGET}
4266
4267=== added file 'tests/online-accounts-ui/data/com.ubuntu.tests_application.application'
4268--- tests/online-accounts-ui/data/com.ubuntu.tests_application.application 1970-01-01 00:00:00 +0000
4269+++ tests/online-accounts-ui/data/com.ubuntu.tests_application.application 2014-08-18 13:33:35 +0000
4270@@ -0,0 +1,19 @@
4271+<?xml version="1.0" encoding="UTF-8" ?>
4272+<application id="com.ubuntu.tests_application">
4273+ <description>Mailer</description>
4274+ <translations>mailer-catalog</translations>
4275+ <desktop-entry>mailer.desktop</desktop-entry>
4276+
4277+ <service-types>
4278+ <service-type id="e-mail">
4279+ <description>Mailer can retrieve your e-mails</description>
4280+ </service-type>
4281+ </service-types>
4282+
4283+ <services>
4284+ <service id="coolshare">
4285+ <description>Mailer can even share stuff on CoolShare</description>
4286+ </service>
4287+ </services>
4288+ <profile>com.ubuntu.tests_application_0.3</profile>
4289+</application>
4290
4291=== added file 'tests/online-accounts-ui/mock/notification-mock.cpp'
4292--- tests/online-accounts-ui/mock/notification-mock.cpp 1970-01-01 00:00:00 +0000
4293+++ tests/online-accounts-ui/mock/notification-mock.cpp 2014-08-18 13:33:35 +0000
4294@@ -0,0 +1,81 @@
4295+/*
4296+ * Copyright (C) 2014 Canonical Ltd.
4297+ *
4298+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4299+ *
4300+ * This file is part of online-accounts-ui
4301+ *
4302+ * This program is free software: you can redistribute it and/or modify it
4303+ * under the terms of the GNU General Public License version 3, as published
4304+ * by the Free Software Foundation.
4305+ *
4306+ * This program is distributed in the hope that it will be useful, but
4307+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4308+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4309+ * PURPOSE. See the GNU General Public License for more details.
4310+ *
4311+ * You should have received a copy of the GNU General Public License along
4312+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4313+ */
4314+
4315+#include "notification-mock.h"
4316+
4317+#include <QByteArray>
4318+#include <QDebug>
4319+#include <QString>
4320+
4321+using namespace OnlineAccountsUi;
4322+
4323+QList<Notification*> NotificationPrivate::allNotifications;
4324+
4325+NotificationPrivate::NotificationPrivate(const QString &summary,
4326+ const QString &body,
4327+ Notification *notification):
4328+ QObject(notification),
4329+ m_summary(summary),
4330+ m_body(body),
4331+ q_ptr(notification)
4332+{
4333+ allNotifications.append(notification);
4334+}
4335+
4336+NotificationPrivate::~NotificationPrivate()
4337+{
4338+ allNotifications.removeAll(q_ptr);
4339+}
4340+
4341+void NotificationPrivate::invokeAction(const QString &action)
4342+{
4343+ Q_Q(Notification);
4344+ Q_EMIT q->actionInvoked(action);
4345+}
4346+
4347+Notification::Notification(const QString &summary,
4348+ const QString &body,
4349+ QObject *parent):
4350+ QObject(parent),
4351+ d_ptr(new NotificationPrivate(summary, body, this))
4352+{
4353+}
4354+
4355+Notification::~Notification()
4356+{
4357+}
4358+
4359+void Notification::addAction(const QString &action, const QString &label)
4360+{
4361+ Q_D(Notification);
4362+ d->m_actions.append(ActionPair(action, label));
4363+}
4364+
4365+void Notification::setSnapDecision(bool snapDecision)
4366+{
4367+ Q_D(Notification);
4368+ d->m_isSnapDecision = snapDecision;
4369+}
4370+
4371+void Notification::show()
4372+{
4373+ Q_D(Notification);
4374+ Q_EMIT d->showCalled();
4375+}
4376
4377=== added file 'tests/online-accounts-ui/mock/notification-mock.h'
4378--- tests/online-accounts-ui/mock/notification-mock.h 1970-01-01 00:00:00 +0000
4379+++ tests/online-accounts-ui/mock/notification-mock.h 2014-08-18 13:33:35 +0000
4380@@ -0,0 +1,66 @@
4381+/*
4382+ * Copyright (C) 2014 Canonical Ltd.
4383+ *
4384+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4385+ *
4386+ * This file is part of online-accounts-ui
4387+ *
4388+ * This program is free software: you can redistribute it and/or modify it
4389+ * under the terms of the GNU General Public License version 3, as published
4390+ * by the Free Software Foundation.
4391+ *
4392+ * This program is distributed in the hope that it will be useful, but
4393+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4394+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4395+ * PURPOSE. See the GNU General Public License for more details.
4396+ *
4397+ * You should have received a copy of the GNU General Public License along
4398+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4399+ */
4400+
4401+#ifndef MOCK_NOTIFICATiON_H
4402+#define MOCK_NOTIFICATiON_H
4403+
4404+#include "notification.h"
4405+
4406+#include <QByteArray>
4407+#include <QList>
4408+#include <QPair>
4409+#include <QString>
4410+
4411+using namespace OnlineAccountsUi;
4412+
4413+namespace OnlineAccountsUi {
4414+
4415+typedef QPair<QString,QString> ActionPair;
4416+
4417+class NotificationPrivate: public QObject
4418+{
4419+ Q_OBJECT
4420+ Q_DECLARE_PUBLIC(Notification)
4421+
4422+public:
4423+ NotificationPrivate(const QString &summary,
4424+ const QString &body,
4425+ Notification *notification);
4426+ ~NotificationPrivate();
4427+ static NotificationPrivate *mocked(Notification *n) { return n->d_ptr; }
4428+
4429+ static QList<Notification *> allNotifications;
4430+
4431+ void invokeAction(const QString &action);
4432+
4433+Q_SIGNALS:
4434+ void showCalled();
4435+
4436+public:
4437+ QString m_summary;
4438+ QString m_body;
4439+ QList<ActionPair> m_actions;
4440+ bool m_isSnapDecision;
4441+ mutable Notification *q_ptr;
4442+};
4443+
4444+} // namespace
4445+
4446+#endif // MOCK_NOTIFICATiON_H
4447
4448=== added file 'tests/online-accounts-ui/mock/request-mock.cpp'
4449--- tests/online-accounts-ui/mock/request-mock.cpp 1970-01-01 00:00:00 +0000
4450+++ tests/online-accounts-ui/mock/request-mock.cpp 2014-08-18 13:33:35 +0000
4451@@ -0,0 +1,139 @@
4452+/*
4453+ * Copyright (C) 2014 Canonical Ltd.
4454+ *
4455+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4456+ *
4457+ * This file is part of online-accounts-ui
4458+ *
4459+ * This program is free software: you can redistribute it and/or modify it
4460+ * under the terms of the GNU General Public License version 3, as published
4461+ * by the Free Software Foundation.
4462+ *
4463+ * This program is distributed in the hope that it will be useful, but
4464+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4465+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4466+ * PURPOSE. See the GNU General Public License for more details.
4467+ *
4468+ * You should have received a copy of the GNU General Public License along
4469+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4470+ */
4471+
4472+#include "globals.h"
4473+#include "request-mock.h"
4474+
4475+#include <QDebug>
4476+
4477+using namespace OnlineAccountsUi;
4478+
4479+RequestPrivate::RequestPrivate(const QString &interface,
4480+ int id,
4481+ const QString &clientProfile,
4482+ const QVariantMap &parameters,
4483+ Request *request):
4484+ QObject(request),
4485+ q_ptr(request),
4486+ m_parameters(parameters),
4487+ m_clientApparmorProfile(clientProfile),
4488+ m_window(0),
4489+ m_inProgress(false)
4490+{
4491+ Q_UNUSED(interface);
4492+ Q_UNUSED(id);
4493+}
4494+
4495+RequestPrivate::~RequestPrivate()
4496+{
4497+}
4498+
4499+Request::Request(const QString &interface,
4500+ int id,
4501+ const QString &clientProfile,
4502+ const QVariantMap &parameters,
4503+ QObject *parent):
4504+ QObject(parent),
4505+ d_ptr(new RequestPrivate(interface, id, clientProfile, parameters, this))
4506+{
4507+}
4508+
4509+Request::~Request()
4510+{
4511+}
4512+
4513+void Request::setWindow(QWindow *window)
4514+{
4515+ Q_D(Request);
4516+ Q_EMIT d->setWindowCalled(window);
4517+}
4518+
4519+WId Request::windowId() const
4520+{
4521+ Q_D(const Request);
4522+ return d->m_parameters[OAU_KEY_WINDOW_ID].toUInt();
4523+}
4524+
4525+bool Request::isInProgress() const
4526+{
4527+ Q_D(const Request);
4528+ return d->m_inProgress;
4529+}
4530+
4531+const QVariantMap &Request::parameters() const
4532+{
4533+ Q_D(const Request);
4534+ return d->m_parameters;
4535+}
4536+
4537+QString Request::clientApparmorProfile() const
4538+{
4539+ Q_D(const Request);
4540+ return d->m_clientApparmorProfile;
4541+}
4542+
4543+QWindow *Request::window() const
4544+{
4545+ Q_D(const Request);
4546+ return d->m_window;
4547+}
4548+
4549+void Request::start()
4550+{
4551+ Q_D(Request);
4552+ if (d->m_inProgress) {
4553+ qWarning() << "Request already started!";
4554+ return;
4555+ }
4556+ d->m_inProgress = true;
4557+}
4558+
4559+void Request::cancel()
4560+{
4561+ setCanceled();
4562+}
4563+
4564+void Request::fail(const QString &name, const QString &message)
4565+{
4566+ Q_D(Request);
4567+ Q_EMIT d->failCalled(name, message);
4568+
4569+ Q_EMIT completed();
4570+}
4571+
4572+void Request::setCanceled()
4573+{
4574+ Q_D(Request);
4575+ if (d->m_inProgress) {
4576+ fail(OAU_ERROR_USER_CANCELED, QStringLiteral("Canceled"));
4577+ d->m_inProgress = false;
4578+ }
4579+}
4580+
4581+void Request::setResult(const QVariantMap &result)
4582+{
4583+ Q_D(Request);
4584+ if (d->m_inProgress) {
4585+ Q_EMIT d->setResultCalled(result);
4586+
4587+ Q_EMIT completed();
4588+ d->m_inProgress = false;
4589+ }
4590+}
4591
4592=== added file 'tests/online-accounts-ui/mock/request-mock.h'
4593--- tests/online-accounts-ui/mock/request-mock.h 1970-01-01 00:00:00 +0000
4594+++ tests/online-accounts-ui/mock/request-mock.h 2014-08-18 13:33:35 +0000
4595@@ -0,0 +1,61 @@
4596+/*
4597+ * Copyright (C) 2014 Canonical Ltd.
4598+ *
4599+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4600+ *
4601+ * This file is part of online-accounts-ui
4602+ *
4603+ * This program is free software: you can redistribute it and/or modify it
4604+ * under the terms of the GNU General Public License version 3, as published
4605+ * by the Free Software Foundation.
4606+ *
4607+ * This program is distributed in the hope that it will be useful, but
4608+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4609+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4610+ * PURPOSE. See the GNU General Public License for more details.
4611+ *
4612+ * You should have received a copy of the GNU General Public License along
4613+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4614+ */
4615+
4616+#ifndef MOCK_REQUEST_H
4617+#define MOCK_REQUEST_H
4618+
4619+#include "request.h"
4620+
4621+#include <QObject>
4622+#include <QString>
4623+#include <QVariantMap>
4624+
4625+namespace OnlineAccountsUi {
4626+
4627+class RequestPrivate: public QObject
4628+{
4629+ Q_OBJECT
4630+ Q_DECLARE_PUBLIC(Request)
4631+
4632+public:
4633+ RequestPrivate(const QString &interface,
4634+ int id,
4635+ const QString &clientProfile,
4636+ const QVariantMap &parameters,
4637+ Request *request);
4638+ ~RequestPrivate();
4639+ static RequestPrivate *mocked(Request *r) { return r->d_ptr; }
4640+
4641+Q_SIGNALS:
4642+ void setWindowCalled(QWindow *);
4643+ void failCalled(const QString &name, const QString &message);
4644+ void setResultCalled(const QVariantMap &result);
4645+
4646+private:
4647+ mutable Request *q_ptr;
4648+ QVariantMap m_parameters;
4649+ QString m_clientApparmorProfile;
4650+ QWindow *m_window;
4651+ bool m_inProgress;
4652+};
4653+
4654+} // namespace
4655+
4656+#endif // MOCK_REQUEST_H
4657
4658=== added file 'tests/online-accounts-ui/mock/ui-server-mock.cpp'
4659--- tests/online-accounts-ui/mock/ui-server-mock.cpp 1970-01-01 00:00:00 +0000
4660+++ tests/online-accounts-ui/mock/ui-server-mock.cpp 2014-08-18 13:33:35 +0000
4661@@ -0,0 +1,68 @@
4662+/*
4663+ * Copyright (C) 2014 Canonical Ltd.
4664+ *
4665+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4666+ *
4667+ * This file is part of online-accounts-ui
4668+ *
4669+ * This program is free software: you can redistribute it and/or modify it
4670+ * under the terms of the GNU General Public License version 3, as published
4671+ * by the Free Software Foundation.
4672+ *
4673+ * This program is distributed in the hope that it will be useful, but
4674+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4675+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4676+ * PURPOSE. See the GNU General Public License for more details.
4677+ *
4678+ * You should have received a copy of the GNU General Public License along
4679+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4680+ */
4681+
4682+#include "ui-server-mock.h"
4683+
4684+#include <QDebug>
4685+
4686+using namespace OnlineAccountsUi;
4687+
4688+static UiServer *m_instance = 0;
4689+
4690+UiServerPrivate::UiServerPrivate(const QString &address,
4691+ UiServer *server):
4692+ QObject(server),
4693+ q_ptr(server),
4694+ m_address(address)
4695+{
4696+}
4697+
4698+UiServerPrivate::~UiServerPrivate()
4699+{
4700+}
4701+
4702+UiServer::UiServer(const QString &address,
4703+ QObject *parent):
4704+ QObject(parent),
4705+ d_ptr(new UiServerPrivate(address, this))
4706+{
4707+ m_instance = this;
4708+}
4709+
4710+UiServer::~UiServer()
4711+{
4712+ m_instance = 0;
4713+}
4714+
4715+UiServer *UiServer::instance()
4716+{
4717+ return m_instance;
4718+}
4719+
4720+bool UiServer::init()
4721+{
4722+ return true;
4723+}
4724+
4725+void UiServer::registerHandler(SignOnUi::RequestHandler *handler)
4726+{
4727+ Q_D(UiServer);
4728+ Q_EMIT d->registerHandlerCalled(handler);
4729+}
4730
4731=== added file 'tests/online-accounts-ui/mock/ui-server-mock.h'
4732--- tests/online-accounts-ui/mock/ui-server-mock.h 1970-01-01 00:00:00 +0000
4733+++ tests/online-accounts-ui/mock/ui-server-mock.h 2014-08-18 13:33:35 +0000
4734@@ -0,0 +1,53 @@
4735+/*
4736+ * Copyright (C) 2014 Canonical Ltd.
4737+ *
4738+ * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
4739+ *
4740+ * This file is part of online-accounts-ui
4741+ *
4742+ * This program is free software: you can redistribute it and/or modify it
4743+ * under the terms of the GNU General Public License version 3, as published
4744+ * by the Free Software Foundation.
4745+ *
4746+ * This program is distributed in the hope that it will be useful, but
4747+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4748+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4749+ * PURPOSE. See the GNU General Public License for more details.
4750+ *
4751+ * You should have received a copy of the GNU General Public License along
4752+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4753+ */
4754+
4755+#ifndef MOCK_UI_SERVER_H
4756+#define MOCK_UI_SERVER_H
4757+
4758+#include "ui-server.h"
4759+
4760+#include <QObject>
4761+#include <QString>
4762+
4763+namespace OnlineAccountsUi {
4764+
4765+class UiServerPrivate: public QObject
4766+{
4767+ Q_OBJECT
4768+ Q_DECLARE_PUBLIC(UiServer)
4769+
4770+public:
4771+ UiServerPrivate(const QString &address, UiServer *pluginServer);
4772+ ~UiServerPrivate();
4773+ static UiServerPrivate *mocked(UiServer *r) { return r->d_ptr; }
4774+
4775+ void emitFinished() { Q_EMIT q_ptr->finished(); }
4776+
4777+Q_SIGNALS:
4778+ void registerHandlerCalled(SignOnUi::RequestHandler *handler);
4779+
4780+public:
4781+ mutable UiServer *q_ptr;
4782+ QString m_address;
4783+};
4784+
4785+} // namespace
4786+
4787+#endif // MOCK_UI_SERVER_H
4788
4789=== added file 'tests/online-accounts-ui/online-accounts-ui.pri'
4790--- tests/online-accounts-ui/online-accounts-ui.pri 1970-01-01 00:00:00 +0000
4791+++ tests/online-accounts-ui/online-accounts-ui.pri 2014-08-18 13:33:35 +0000
4792@@ -0,0 +1,18 @@
4793+include(../../common-project-config.pri)
4794+
4795+CONFIG += \
4796+ debug
4797+
4798+QT += \
4799+ core \
4800+ testlib
4801+
4802+DEFINES += \
4803+ DEBUG_ENABLED
4804+
4805+ONLINE_ACCOUNTS_UI_DIR = $${TOP_SRC_DIR}/online-accounts-ui
4806+COMMON_SRC_DIR = $${TOP_SRC_DIR}/online-accounts-ui
4807+
4808+INCLUDEPATH += \
4809+ $${ONLINE_ACCOUNTS_SERVICE_DIR} \
4810+ $${COMMON_SRC_DIR}
4811
4812=== modified file 'tests/online-accounts-ui/online-accounts-ui.pro'
4813--- tests/online-accounts-ui/online-accounts-ui.pro 2014-06-05 13:28:30 +0000
4814+++ tests/online-accounts-ui/online-accounts-ui.pro 2014-08-18 13:33:35 +0000
4815@@ -3,6 +3,5 @@
4816 qml \
4817 tst_access_model.pro \
4818 tst_application_manager.pro \
4819- tst_inactivity_timer.pro \
4820 tst_notification.pro \
4821- tst_service.pro
4822+ tst_signonui_request.pro
4823
4824=== modified file 'tests/online-accounts-ui/qml/Source/qmldir'
4825--- tests/online-accounts-ui/qml/Source/qmldir 2013-09-18 08:35:43 +0000
4826+++ tests/online-accounts-ui/qml/Source/qmldir 2014-08-18 13:33:35 +0000
4827@@ -1,2 +1,2 @@
4828 module Source
4829-AccountCreationPage 1.0 ../../../../src/qml/AccountCreationPage.qml
4830+AccountCreationPage 1.0 ../../../../online-accounts-ui/qml/AccountCreationPage.qml
4831
4832=== modified file 'tests/online-accounts-ui/tst_access_model.pro'
4833--- tests/online-accounts-ui/tst_access_model.pro 2014-02-03 10:27:39 +0000
4834+++ tests/online-accounts-ui/tst_access_model.pro 2014-08-18 13:33:35 +0000
4835@@ -1,37 +1,30 @@
4836-include(../../common-project-config.pri)
4837+include(online-accounts-ui.pri)
4838
4839 TARGET = tst_access_model
4840
4841 CONFIG += \
4842- debug \
4843 link_pkgconfig
4844
4845 QT += \
4846- core \
4847 dbus \
4848- qml \
4849- testlib
4850+ qml
4851
4852 PKGCONFIG += \
4853 accounts-qt5
4854
4855 DEFINES += \
4856- DEBUG_ENABLED \
4857 TEST_DATA_DIR=\\\"$${PWD}/data\\\"
4858
4859 SOURCES += \
4860- $${TOP_SRC_DIR}/src/access-model.cpp \
4861- $${TOP_SRC_DIR}/src/account-manager.cpp \
4862- $${TOP_SRC_DIR}/src/debug.cpp \
4863+ $${ONLINE_ACCOUNTS_UI_DIR}/access-model.cpp \
4864+ $${ONLINE_ACCOUNTS_UI_DIR}/account-manager.cpp \
4865+ $${ONLINE_ACCOUNTS_UI_DIR}/debug.cpp \
4866 tst_access_model.cpp
4867
4868 HEADERS += \
4869- $${TOP_SRC_DIR}/src/access-model.h \
4870- $${TOP_SRC_DIR}/src/account-manager.h \
4871- $${TOP_SRC_DIR}/src/debug.h
4872-
4873-INCLUDEPATH += \
4874- $${TOP_SRC_DIR}/src
4875+ $${ONLINE_ACCOUNTS_UI_DIR}/access-model.h \
4876+ $${ONLINE_ACCOUNTS_UI_DIR}/account-manager.h \
4877+ $${ONLINE_ACCOUNTS_UI_DIR}/debug.h
4878
4879 check.commands = "xvfb-run -a dbus-test-runner -t ./$${TARGET}"
4880 check.depends = $${TARGET}
4881
4882=== modified file 'tests/online-accounts-ui/tst_application_manager.pro'
4883--- tests/online-accounts-ui/tst_application_manager.pro 2014-05-14 13:31:42 +0000
4884+++ tests/online-accounts-ui/tst_application_manager.pro 2014-08-18 13:33:35 +0000
4885@@ -1,37 +1,30 @@
4886-include(../../common-project-config.pri)
4887+include(online-accounts-ui.pri)
4888
4889 TARGET = tst_application_manager
4890
4891 CONFIG += \
4892- debug \
4893 link_pkgconfig
4894
4895 QT += \
4896- core \
4897 dbus \
4898- qml \
4899- testlib
4900+ qml
4901
4902 PKGCONFIG += \
4903 accounts-qt5
4904
4905 DEFINES += \
4906- DEBUG_ENABLED \
4907 TEST_DATA_DIR=\\\"$${PWD}/data\\\"
4908
4909 SOURCES += \
4910- $${TOP_SRC_DIR}/src/application-manager.cpp \
4911- $${TOP_SRC_DIR}/src/account-manager.cpp \
4912- $${TOP_SRC_DIR}/src/debug.cpp \
4913+ $${ONLINE_ACCOUNTS_UI_DIR}/application-manager.cpp \
4914+ $${ONLINE_ACCOUNTS_UI_DIR}/account-manager.cpp \
4915+ $${ONLINE_ACCOUNTS_UI_DIR}/debug.cpp \
4916 tst_application_manager.cpp
4917
4918 HEADERS += \
4919- $${TOP_SRC_DIR}/src/application-manager.h \
4920- $${TOP_SRC_DIR}/src/account-manager.h \
4921- $${TOP_SRC_DIR}/src/debug.h
4922-
4923-INCLUDEPATH += \
4924- $${TOP_SRC_DIR}/src
4925+ $${ONLINE_ACCOUNTS_UI_DIR}/application-manager.h \
4926+ $${ONLINE_ACCOUNTS_UI_DIR}/account-manager.h \
4927+ $${ONLINE_ACCOUNTS_UI_DIR}/debug.h
4928
4929 check.commands = "xvfb-run -a dbus-test-runner -t ./$${TARGET}"
4930 check.depends = $${TARGET}
4931
4932=== modified file 'tests/online-accounts-ui/tst_notification.cpp'
4933--- tests/online-accounts-ui/tst_notification.cpp 2014-06-05 13:28:30 +0000
4934+++ tests/online-accounts-ui/tst_notification.cpp 2014-08-18 13:33:35 +0000
4935@@ -26,6 +26,7 @@
4936 #include <QPair>
4937 #include <QSignalSpy>
4938 #include <QTest>
4939+#include <QVariantMap>
4940 #include <libnotify/notification.h>
4941 #include <libnotify/notify.h>
4942
4943@@ -52,6 +53,7 @@
4944 QString summary;
4945 QString body;
4946 QList<ActionPair> actions;
4947+ QVariantMap hints;
4948 bool visible;
4949
4950 MockNotification();
4951@@ -167,6 +169,22 @@
4952 callbackData.userData = user_data;
4953 }
4954
4955+void notify_notification_set_hint(NotifyNotification *notification,
4956+ const char *key,
4957+ GVariant *value)
4958+{
4959+ MockNotification *mock =
4960+ reinterpret_cast<MockNotification*>(notification);
4961+ QVariant variant;
4962+ if (g_variant_is_of_type(value, G_VARIANT_TYPE_BOOLEAN)) {
4963+ variant = bool(g_variant_get_boolean(value));
4964+ } else {
4965+ /* Add support for any needed types */
4966+ qWarning() << "Unsupported variant type";
4967+ }
4968+ mock->hints.insert(QString::fromUtf8(key), variant);
4969+}
4970+
4971 /* End of mock code */
4972
4973 class NotificationTest: public QObject
4974@@ -180,6 +198,7 @@
4975 void testInitialization();
4976 void testDestruction();
4977 void testContents();
4978+ void testSnapDecision();
4979 void testVisibility();
4980 void testClosing();
4981 void testActions();
4982@@ -220,6 +239,20 @@
4983 MockNotification *mock = *(notifications.begin());
4984 QCOMPARE(mock->summary, QString("Summary"));
4985 QCOMPARE(mock->body, QString("Body"));
4986+ QVERIFY(mock->hints.isEmpty());
4987+}
4988+
4989+void NotificationTest::testSnapDecision()
4990+{
4991+ Notification first("Summary", "Body");
4992+ MockNotification *mock = *(notifications.begin());
4993+ QVERIFY(mock->hints.isEmpty());
4994+
4995+ first.setSnapDecision(true);
4996+ QCOMPARE(mock->hints["x-canonical-snap-decisions"].toBool(), true);
4997+
4998+ first.setSnapDecision(false);
4999+ QCOMPARE(mock->hints["x-canonical-snap-decisions"].toBool(), false);
5000 }
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches