Merge lp:~dobey/ubuntu-printing-app/add-notifier-daemon into lp:ubuntu-printing-app

Proposed by dobey
Status: Merged
Approved by: Andrew Hayzen
Approved revision: 50
Merged at revision: 32
Proposed branch: lp:~dobey/ubuntu-printing-app/add-notifier-daemon
Merge into: lp:ubuntu-printing-app
Prerequisite: lp:~ahayzen/ubuntu-printing-app/add-ubuntu-printing-app
Diff against target: 2180 lines (+1994/-0)
32 files modified
.bzrignore (+5/-0)
CMakeLists.txt (+4/-0)
debian/control (+11/-0)
notifier/CMakeLists.txt (+49/-0)
notifier/TESTING (+37/-0)
notifier/data/CMakeLists.txt (+48/-0)
notifier/data/printing-notifier.conf.in (+23/-0)
notifier/data/printing-notifier.override (+1/-0)
notifier/data/printing-notifier.service.in (+8/-0)
notifier/src/CMakeLists.txt (+62/-0)
notifier/src/actions.cpp (+42/-0)
notifier/src/actions.h (+43/-0)
notifier/src/client.h (+56/-0)
notifier/src/cups-client.cpp (+337/-0)
notifier/src/cups-client.h (+65/-0)
notifier/src/dbus-names.h (+27/-0)
notifier/src/job.h (+50/-0)
notifier/src/main.cpp (+61/-0)
notifier/src/notification.cpp (+185/-0)
notifier/src/notification.h (+61/-0)
notifier/src/notify-engine.cpp (+204/-0)
notifier/src/notify-engine.h (+57/-0)
notifier/src/org.cups.cupsd.Notifier.xml (+147/-0)
notifier/src/printer.h (+45/-0)
notifier/src/utils.cpp (+37/-0)
notifier/src/utils.h (+32/-0)
notifier/tests/CMakeLists.txt (+28/-0)
notifier/tests/actions-mock.h (+42/-0)
notifier/tests/client-mock.h (+54/-0)
notifier/tests/mock-notification.h (+52/-0)
notifier/tests/test_notify-engine.cpp (+88/-0)
notifier/tests/test_utils.cpp (+33/-0)
To merge this branch: bzr merge lp:~dobey/ubuntu-printing-app/add-notifier-daemon
Reviewer Review Type Date Requested Status
Andrew Hayzen (community) Approve
Review via email: mp+315637@code.launchpad.net

Commit message

Add notifier daemon.

To post a comment you must log in.
Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Thanks for submitting this!

It looks like translation pot generation was missed, would you be able to add the parts the parts to po/CMakeLists.txt. It looks that all is required is to add --keyword=_ and provide the list of source files.

I've not yet been successful in creating a notification (on both unity7 and unity8), would you be able to provide instructions for how you are testing this?

review: Needs Information
39. By dobey

Add a TESTING document for the notifier.

40. By dobey

Guard against a nullptr to avoid crashing if printer doesn't exist.

Revision history for this message
dobey (dobey) wrote :

I've added a document to the tree to describe how to test it (as testing cups notifications can be a bit weird to do). I would recommend avoiding having the daemon running in unity 7 as it's not meant for it, and meant to replace the printing indicator for unity 8 session only. (The notification service under unity7 creates a GTK+ dialog that has some additional buttons, for the printer error notifications.)

As for the translations, I had set up the po/ dir already in my branch prior to adding it in yours, and I removed what I had to avoid the conflict. At this point, I think it's probably better to fix that in another branch on top of these (perhaps after they land, as I don't want to get into a weird situation where we keep adding conflicts). There's also some improvements that can be taken from my work here, and applied to the rest of the project tree, now that I see you've added more testing there, but I think that cleanup should happen in another branch too at this point.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

So I've been testing this a bit further and have been able to generate notifications. One thing that I noticed is if the job title is empty then the notification looks a bit strange, it comes up with the following: "" has printed . I'm going to improve the printing app to ensure that it always has some form of title, but I wonder if the notification should also have a fallback ?

41. By dobey

Fix deadlock with deletion of notification in idle timeout.
Remove the two not especially helpful tests.

42. By dobey

Remove the unused include.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

The change fixes the deadlock I was having, and I've now been able to confirm notifications appear when using the printing app as well :-)

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

This looks good, once the perquisite branch has landed we can land this. I agree that the folder structure of the project can be improved (it came from the SDK template when I only thought the printing-app would be in the code nothing else), we can discuss these improvements after these two branches land.

One thing to note is that the current WIP designs for the printer queue state that on a mobile device when a job is sent to the printer a notification with "Printing on Printer Name" will appear, tapping on that will launch the queue. This is different on desktop, where no notification will be used and just the launcher alert as the queue starts. However these designs are still WIP, so this can be done in a future branch.

review: Approve
Revision history for this message
dobey (dobey) wrote :

> One thing to note is that the current WIP designs for the printer queue state
> that on a mobile device when a job is sent to the printer a notification with
> "Printing on Printer Name" will appear, tapping on that will launch the queue.
> This is different on desktop, where no notification will be used and just the
> launcher alert as the queue starts. However these designs are still WIP, so
> this can be done in a future branch.

I've seen no indication that we should have these notifications in one form factor but not another. Nor does it make any sense for that to be the case, IMO, and would be a divergent experience, not a convergent one.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

@dobey, would you mind doing a pull of trunk to bring in the new changes?

Also when trying to get this to build in a silo I got "No package 'properties-cpp' found", which suggests the package libproperties-cpp-dev should be added to the build-deps?

Furthermore when discussing with Bill we have a few changes (these can be done in this branch or a further branch):
1) Currently the text in the notification doesn't wrap, so you only see the first part. Both the design specifications show this (#1.2 in [0] and in [1]). Is this a limitation of the notifications in unity8? or an issue in this notifier code?

2) We don't think there is an API yet for adding an entry for the printer queue into the launcher of unity8 when something is printing without launching an app (what design wants). So for now can you show a notification when something is sent to the printer ("Printing on printerName"), as shown in the design #1.1 in [0]

3) If the user clicks on the notification when something is sent to the printer, this should show the queue dialog. I'm currently building the queue dialog in another branch, not decided on the launcher name yet - will let you know (probably something like ubuntu-queue-dialog, or maybe we could have a url-handler call for it?).

0 - https://docs.google.com/document/d/1QaM9B1LGT5Pcun2bTQT_hgRIGHLjvg-qMe8pSbJcuj4/edit?ts=5808916c&pli=1#heading=h.8ddcj8k35qnq
1 - https://wiki.ubuntu.com/Printing#Printing_succeeds

review: Needs Fixing
Revision history for this message
dobey (dobey) wrote :

On Fri, 2017-03-17 at 15:56 +0000, Andrew Hayzen wrote:
> Review: Needs Fixing
>
> @dobey, would you mind doing a pull of trunk to bring in the new
> changes?

I'll get it updated.

> Also when trying to get this to build in a silo I got "No package
> 'properties-cpp' found", which suggests the package libproperties-
> cpp-dev should be added to the build-deps?

I'll see what's going on there.

> Furthermore when discussing with Bill we have a few changes (these
> can be done in this branch or a further branch):
> 1) Currently the text in the notification doesn't wrap, so you only
> see the first part. Both the design specifications show this (#1.2 in
> [0] and in [1]). Is this a limitation of the notifications in unity8?
> or an issue in this notifier code?

This would be an issue on the renderer's side. We have no way to know
what the width of the notification will be on the client (notifier
daemon) side, so we can't do anything about it there. It seems there is
no way to specify how this should be handled, from the client side.

> 2) We don't think there is an API yet for adding an entry for the
> printer queue into the launcher of unity8 when something is printing
> without launching an app (what design wants). So for now can you show
> a notification when something is sent to the printer ("Printing on
> printerName"), as shown in the design #1.1 in [0]

We can't really do this from notifier daemon which listens to cups for
status, no. It would really need to be a notification sent by the
printing app I think, but in 99.9999% of cases will be immediately
followed by the '"foo" has printed." notification. So there's no good
reason to pop a '"foo" is printing." notification immediately prior to.

> 3) If the user clicks on the notification when something is sent to
> the printer, this should show the queue dialog. I'm currently
> building the queue dialog in another branch, not decided on the
> launcher name yet - will let you know (probably something like
> ubuntu-queue-dialog, or maybe we could have a url-handler call for
> it?).

There is no way to implement this as "when the user clicks on the
notification." The design is wrong. To do this would require making the
notification interactive instead, and adding a "View print queue"
button to it.

However, looking at the doc, this is only specified for errors, and
it's specified to go to the printer's page in settings. The notifier
already adds a button to open settings on printer errors, which goes to
settings:///system/printers/${printer_name}. If that's already handled
in system-settings, then I think this is already implemented.

43. By dobey

Merge trunk.

44. By dobey

Need properties-cpp.

45. By dobey

Use the gdbus module from cmake-extras.

46. By dobey

Remove extraneous comment and cmake directory inclusion.

47. By dobey

GDbus package is required.

48. By dobey

fix typo.

49. By dobey

Move the comment to avoid confusing translators.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

OK, thanks for your responses for 1,2,3. I'm going to discuss with Bill our options for them.

I've found one more missing depends that needs to be added, after adding the following package I've been able to build in pbuilder and in a silo :-)
- libnotify-dev

Once the missing depends is fixed, I think this branch is ready and any further changes can be done in further branches :-)

review: Needs Fixing
50. By dobey

Add libnotify-dev to build depends.

Revision history for this message
Andrew Hayzen (ahayzen) wrote :

Thanks, this looks good now.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2017-03-20 13:16:02 +0000
4@@ -0,0 +1,5 @@
5+CMakeLists.txt.user*
6+/build
7+/po/Makefile.in.in
8+/po/POTFILES.in
9+
10
11=== modified file 'CMakeLists.txt'
12--- CMakeLists.txt 2017-03-07 15:01:56 +0000
13+++ CMakeLists.txt 2017-03-20 13:16:02 +0000
14@@ -1,6 +1,9 @@
15 project(ubuntu-printing-app)
16 cmake_minimum_required(VERSION 2.8.9)
17
18+# Always Be Testing
19+enable_testing()
20+
21 # Load translation tools
22 find_program(INTLTOOL_MERGE intltool-merge)
23 if(NOT INTLTOOL_MERGE)
24@@ -51,6 +54,7 @@
25
26 # Add subdirs
27 add_subdirectory(backend)
28+add_subdirectory(notifier)
29 add_subdirectory(runner)
30 add_subdirectory(ubuntu-printing-app)
31
32
33=== modified file 'debian/control'
34--- debian/control 2017-03-07 15:16:02 +0000
35+++ debian/control 2017-03-20 13:16:02 +0000
36@@ -3,10 +3,21 @@
37 Priority: optional
38 Maintainer: Andrew Hayzen <andrew.hayzen@canonical.com>
39 Build-Depends: cmake (>= 2.8.9),
40+ cmake-extras (>= 1.3),
41 debhelper (>=9),
42 dh-apparmor,
43 dh-translations,
44+ gcovr,
45+ googletest | google-mock,
46+ intltool,
47+ lcov,
48+ libboost-dev,
49+ libcups2-dev,
50+ libglib2.0-dev (>= 2.35.4),
51+ libnotify-dev,
52 libpoppler-qt5-dev,
53+ libproperties-cpp-dev,
54+ liburl-dispatcher1-dev,
55 qmlscene,
56 qml-module-qtquick2,
57 qml-module-qtquick-layouts,
58
59=== added directory 'notifier'
60=== added file 'notifier/CMakeLists.txt'
61--- notifier/CMakeLists.txt 1970-01-01 00:00:00 +0000
62+++ notifier/CMakeLists.txt 2017-03-20 13:16:02 +0000
63@@ -0,0 +1,49 @@
64+### Install path variables
65+include (GNUInstallDirs)
66+if (EXISTS "/etc/debian_version") # Workaround for libexecdir on debian
67+ set (CMAKE_INSTALL_LIBEXECDIR "${CMAKE_INSTALL_LIBDIR}")
68+ set (CMAKE_INSTALL_FULL_LIBEXECDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
69+endif ()
70+set (CMAKE_INSTALL_PKGLIBEXECDIR "${CMAKE_INSTALL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
71+set (CMAKE_INSTALL_FULL_PKGLIBEXECDIR "${CMAKE_INSTALL_FULL_LIBEXECDIR}/${CMAKE_PROJECT_NAME}")
72+
73+### Always Be Testing
74+enable_testing()
75+
76+### Set variables for our binary build targets
77+set(SERVICE_LIB_NAME "printing-notifier")
78+set(SERVICE_EXEC_NAME "printing-notifier-service")
79+
80+### Some default compile flags
81+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -g -fPIC")
82+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O2 -g -Wextra -Wall -Werror -Werror=conversion-null -Wno-ignored-qualifiers -fPIC")
83+
84+### Find deps
85+find_package(PkgConfig REQUIRED)
86+find_package(Threads)
87+
88+pkg_check_modules(SERVICE_DEPS REQUIRED
89+ gio-unix-2.0>=2.36
90+ glib-2.0>=2.36
91+ libnotify>=0.7.6
92+ properties-cpp
93+ url-dispatcher-1
94+)
95+
96+### Find libcups
97+find_program(CUPS_CONFIG cups-config)
98+execute_process(COMMAND ${CUPS_CONFIG} --cflags OUTPUT_VARIABLE CUPS_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE)
99+execute_process(COMMAND ${CUPS_CONFIG} --libs OUTPUT_VARIABLE CUPS_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE)
100+
101+### Add sub-directories
102+add_subdirectory(data)
103+add_subdirectory(src)
104+add_subdirectory(tests)
105+
106+### Enable coverage reporting
107+find_package(CoverageReport)
108+ENABLE_COVERAGE_REPORT(
109+ TARGETS ${SERVICE_LIB_NAME} ${SERVICE_EXEC_NAME}
110+ TESTS test-${SERVICE_LIB_NAME}
111+ FILTER /usr/include ${CMAKE_BINARY_DIR}/*
112+)
113
114=== added file 'notifier/TESTING'
115--- notifier/TESTING 1970-01-01 00:00:00 +0000
116+++ notifier/TESTING 2017-03-20 13:16:02 +0000
117@@ -0,0 +1,37 @@
118+This subtree contains the daemon for notifying of successful print jobs,
119+and of issues with the printer when jobs are printed, under Unity 8.
120+This document describes how to test some notifications.
121+
122+
123+Testing
124+
125+0) Make sure the notifier daemon is running:
126+
127+ $: ./build/notifier/src/printing-notifier-service
128+
129+1) Add a null printer, if one is not already configured, and enable it:
130+
131+ $: lpadmin -p nulldevice -E -v file:///dev/null
132+ $: cupsenable nulldevice
133+
134+2) Print a test page:
135+
136+ $: lpr -P nulldevice -J "Test Page" /usr/share/cups/data/default-testpage.pdf
137+
138+ The document should "print" immediately, and you should get a notification
139+ that reads '"Test Page" has printed."
140+
141+3) Pause the printer, print another test page, and emit the PrinterStateChanged
142+ signal over dbus:
143+
144+ $: cupsdisable nulldevice
145+ $: lpr -P nulldevice -J "Test Page" /usr/share/cups/data/default-testpage.pdf
146+ $: gdbus emit -y -o /org/cups/cupsd/Notifier \
147+ -s org.cups.cupsd.Notifier.PrinterStateChanged \
148+ "Some text" "cups://nulldevice" "nulldevice" 'uint32 0' "toner-low" true
149+
150+ You should see a notification for the toner being low, with one job
151+ queued on the printer. Clicking the OK button should close the notification
152+ and perform no further actions. Clicking the "Settings…" button should
153+ cause system-settings to open to the settings page for that particular
154+ printer.
155
156=== added directory 'notifier/data'
157=== added file 'notifier/data/CMakeLists.txt'
158--- notifier/data/CMakeLists.txt 1970-01-01 00:00:00 +0000
159+++ notifier/data/CMakeLists.txt 2017-03-20 13:16:02 +0000
160@@ -0,0 +1,48 @@
161+##
162+## Systemd Unit File
163+##
164+
165+# where to install
166+set (SYSTEMD_USER_DIR "${CMAKE_INSTALL_LIBDIR}/systemd/user")
167+message (STATUS "${SYSTEMD_USER_DIR} is the systemd user unit file install dir")
168+
169+set (SYSTEMD_USER_NAME "${SERVICE_LIB_NAME}.service")
170+set (SYSTEMD_USER_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SYSTEMD_USER_NAME}")
171+set (SYSTEMD_USER_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${SYSTEMD_USER_NAME}.in")
172+
173+# build it
174+set (pkglibexecdir "${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}")
175+configure_file ("${SYSTEMD_USER_FILE_IN}" "${SYSTEMD_USER_FILE}")
176+
177+# install it
178+install (FILES "${SYSTEMD_USER_FILE}"
179+ DESTINATION "${SYSTEMD_USER_DIR}")
180+
181+##
182+## Upstart systemd override Job File
183+##
184+
185+set (UPSTART_SYSTEMD_OVERRIDE_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/systemd-session/upstart")
186+message (STATUS "${UPSTART_SYSTEMD_OVERRIDE_DIR} is the Upstart override Job File for systemd dir")
187+
188+install (FILES "${CMAKE_CURRENT_SOURCE_DIR}/${SERVICE_LIB_NAME}.override"
189+ DESTINATION "${UPSTART_SYSTEMD_OVERRIDE_DIR}")
190+
191+##
192+## Upstart Job File
193+##
194+
195+# where to install
196+set (UPSTART_JOB_DIR "${CMAKE_INSTALL_FULL_DATADIR}/upstart/sessions")
197+message (STATUS "${UPSTART_JOB_DIR} is the Upstart Job File install dir")
198+
199+set (UPSTART_JOB_NAME "${SERVICE_LIB_NAME}.conf")
200+set (UPSTART_JOB_FILE "${CMAKE_CURRENT_BINARY_DIR}/${UPSTART_JOB_NAME}")
201+set (UPSTART_JOB_FILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/${UPSTART_JOB_NAME}.in")
202+
203+# build it
204+configure_file ("${UPSTART_JOB_FILE_IN}" "${UPSTART_JOB_FILE}")
205+
206+# install it
207+install (FILES "${UPSTART_JOB_FILE}"
208+ DESTINATION "${UPSTART_JOB_DIR}")
209
210=== added file 'notifier/data/printing-notifier.conf.in'
211--- notifier/data/printing-notifier.conf.in 1970-01-01 00:00:00 +0000
212+++ notifier/data/printing-notifier.conf.in 2017-03-20 13:16:02 +0000
213@@ -0,0 +1,23 @@
214+description "Printing Notification Service"
215+
216+# NOTE: Limiting only to Unity 8 right now as it's still using
217+# dbusmenu. That can be lifted after it is ported to GMenu
218+
219+start on indicator-services-start
220+stop on desktop-end or indicator-services-end
221+
222+respawn
223+respawn limit 2 10
224+
225+pre-start script
226+ # NOTE: Only used on Unity8 today, not 7
227+ # Still allows manual starting
228+ if [ "x$DESKTOP_SESSION" != "xubuntu-touch" ] &&
229+ [ "x$DESKTOP_SESSION" != "xunity8" ]; then
230+ if [ "x$UPSTART_EVENTS" != "x" ] ; then
231+ stop; exit 0
232+ fi
233+ fi
234+end script
235+
236+exec $SNAP@pkglibexecdir@/@SERVICE_EXEC_NAME@
237
238=== added file 'notifier/data/printing-notifier.override'
239--- notifier/data/printing-notifier.override 1970-01-01 00:00:00 +0000
240+++ notifier/data/printing-notifier.override 2017-03-20 13:16:02 +0000
241@@ -0,0 +1,1 @@
242+manual
243
244=== added file 'notifier/data/printing-notifier.service.in'
245--- notifier/data/printing-notifier.service.in 1970-01-01 00:00:00 +0000
246+++ notifier/data/printing-notifier.service.in 2017-03-20 13:16:02 +0000
247@@ -0,0 +1,8 @@
248+[Unit]
249+Description=Printing Notification Service
250+PartOf=graphical-session.target
251+After=indicators-pre.target
252+
253+[Service]
254+ExecStart=@pkglibexecdir@/@SERVICE_EXEC_NAME@
255+Restart=on-failure
256
257=== added directory 'notifier/src'
258=== added file 'notifier/src/CMakeLists.txt'
259--- notifier/src/CMakeLists.txt 1970-01-01 00:00:00 +0000
260+++ notifier/src/CMakeLists.txt 2017-03-20 13:16:02 +0000
261@@ -0,0 +1,62 @@
262+add_definitions(
263+ -DGETTEXT_PACKAGE=\"${PROJECT_NAME}\"
264+ -DGETTEXT_LOCALEDIR=\"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALEDIR}\"
265+ -DG_LOG_DOMAIN=\"${CMAKE_PROJECT_NAME}\"
266+)
267+
268+include_directories(
269+ ${CMAKE_CURRENT_SOURCE_DIR}
270+ ${CMAKE_CURRENT_BINARY_DIR}
271+ ${SERVICE_DEPS_INCLUDE_DIRS}
272+)
273+link_directories(
274+ ${SERVICE_DEPS_LIBRARY_DIRS}
275+)
276+
277+find_package(GDbus REQUIRED)
278+add_gdbus_codegen(
279+ SERVICE_GENERATED_SOURCES
280+ cups-cupsd-notifier
281+ org.cups.cupsd
282+ ${CMAKE_CURRENT_SOURCE_DIR}/org.cups.cupsd.Notifier.xml)
283+
284+add_library(${SERVICE_LIB_NAME} STATIC
285+ actions.cpp
286+ actions.h
287+ client.h
288+ cups-client.cpp
289+ cups-client.h
290+ job.h
291+ notification.cpp
292+ notification.h
293+ notify-engine.cpp
294+ notify-engine.h
295+ printer.h
296+ utils.cpp
297+ utils.h
298+ ${SERVICE_GENERATED_SOURCES}
299+)
300+
301+target_link_libraries(${SERVICE_LIB_NAME}
302+ ${SERVICE_DEPS_LIBRARIES}
303+ ${CUPS_LIBS}
304+)
305+
306+
307+add_executable(${SERVICE_EXEC_NAME}
308+ main.cpp
309+)
310+
311+target_link_libraries(${SERVICE_EXEC_NAME}
312+ ${SERVICE_LIB_NAME}
313+ ${SERVICE_DEPS_LIBRARIES}
314+ ${CUPS_LIBS}
315+
316+ ${CMAKE_THREAD_LIBS_INIT}
317+)
318+
319+install(
320+ TARGETS ${SERVICE_EXEC_NAME}
321+ RUNTIME
322+ DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}/${PROJECT_NAME}
323+)
324
325=== added file 'notifier/src/actions.cpp'
326--- notifier/src/actions.cpp 1970-01-01 00:00:00 +0000
327+++ notifier/src/actions.cpp 2017-03-20 13:16:02 +0000
328@@ -0,0 +1,42 @@
329+/*
330+ * Copyright 2017 Canonical Ltd.
331+ *
332+ * This program is free software: you can redistribute it and/or modify it
333+ * under the terms of the GNU General Public License version 3, as published
334+ * by the Free Software Foundation.
335+ *
336+ * This program is distributed in the hope that it will be useful, but
337+ * WITHOUT ANY WARRANTY; without even the implied warranties of
338+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
339+ * PURPOSE. See the GNU General Public License for more details.
340+ *
341+ * You should have received a copy of the GNU General Public License along
342+ * with this program. If not, see <http://www.gnu.org/licenses/>.
343+ */
344+
345+#include "actions.h"
346+
347+#include <glib.h>
348+#include <url-dispatcher.h>
349+
350+namespace ubuntu {
351+namespace printing {
352+namespace notifier {
353+
354+Actions::Actions()
355+{
356+}
357+
358+Actions::~Actions()
359+{
360+}
361+
362+void Actions::open_settings_app(const std::string& url)
363+{
364+ g_debug("Dispatching url '%s'", url.c_str());
365+ url_dispatch_send(url.c_str(), nullptr, nullptr);
366+}
367+
368+} // notifier
369+} // printing
370+} // ubuntu
371
372=== added file 'notifier/src/actions.h'
373--- notifier/src/actions.h 1970-01-01 00:00:00 +0000
374+++ notifier/src/actions.h 2017-03-20 13:16:02 +0000
375@@ -0,0 +1,43 @@
376+/*
377+ * Copyright 2017 Canonical Ltd.
378+ *
379+ * This program is free software: you can redistribute it and/or modify it
380+ * under the terms of the GNU General Public License version 3, as published
381+ * by the Free Software Foundation.
382+ *
383+ * This program is distributed in the hope that it will be useful, but
384+ * WITHOUT ANY WARRANTY; without even the implied warranties of
385+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
386+ * PURPOSE. See the GNU General Public License for more details.
387+ *
388+ * You should have received a copy of the GNU General Public License along
389+ * with this program. If not, see <http://www.gnu.org/licenses/>.
390+ */
391+
392+#pragma once
393+
394+#include <string>
395+
396+namespace ubuntu {
397+namespace printing {
398+namespace notifier {
399+
400+/**
401+ * \brief Interface for all the actions that can be activated by users.
402+ *
403+ * This is a simple C++ wrapper around our GActionGroup that gets exported
404+ * onto the bus. Subclasses implement the actual code that should be run
405+ * when a particular action is triggered.
406+ */
407+class Actions
408+{
409+public:
410+ Actions();
411+ virtual ~Actions();
412+
413+ virtual void open_settings_app(const std::string& url);
414+};
415+
416+} // notifier
417+} // printing
418+} // ubuntu
419
420=== added file 'notifier/src/client.h'
421--- notifier/src/client.h 1970-01-01 00:00:00 +0000
422+++ notifier/src/client.h 2017-03-20 13:16:02 +0000
423@@ -0,0 +1,56 @@
424+/*
425+ * Copyright 2016-2017 Canonical Ltd.
426+ *
427+ * This program is free software: you can redistribute it and/or modify it
428+ * under the terms of the GNU General Public License version 3, as published
429+ * by the Free Software Foundation.
430+ *
431+ * This program is distributed in the hope that it will be useful, but
432+ * WITHOUT ANY WARRANTY; without even the implied warranties of
433+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
434+ * PURPOSE. See the GNU General Public License for more details.
435+ *
436+ * You should have received a copy of the GNU General Public License along
437+ * with this program. If not, see <http://www.gnu.org/licenses/>.
438+ */
439+
440+#pragma once
441+
442+#include "job.h"
443+#include "printer.h"
444+
445+#include <core/signal.h>
446+
447+
448+namespace ubuntu {
449+namespace printing {
450+namespace notifier {
451+
452+ class Client {
453+ public:
454+ Client() = default;
455+ virtual ~Client() = default;
456+
457+ // Signals corresponding to printers
458+ virtual core::Signal<const Printer&>& printer_state_changed() = 0;
459+
460+ // Signals corresponding to jobs
461+ virtual core::Signal<const Job&>& job_state_changed() = 0;
462+
463+ // Methods to manage notification monitoring
464+ virtual void create_subscription() = 0;
465+ virtual void renew_subscription() = 0;
466+ virtual void cancel_subscription() = 0;
467+
468+ // To iniitalize the notifier with current jobs
469+ virtual void refresh() = 0;
470+
471+ private:
472+ // disable copying
473+ Client(const Client&) = delete;
474+ Client& operator=(const Client&) = delete;
475+ };
476+
477+} // notifier
478+} // printing
479+} // ubuntu
480
481=== added file 'notifier/src/cups-client.cpp'
482--- notifier/src/cups-client.cpp 1970-01-01 00:00:00 +0000
483+++ notifier/src/cups-client.cpp 2017-03-20 13:16:02 +0000
484@@ -0,0 +1,337 @@
485+/*
486+ * Copyright 2016-2017 Canonical Ltd.
487+ *
488+ * This program is free software: you can redistribute it and/or modify it
489+ * under the terms of the GNU General Public License version 3, as published
490+ * by the Free Software Foundation.
491+ *
492+ * This program is distributed in the hope that it will be useful, but
493+ * WITHOUT ANY WARRANTY; without even the implied warranties of
494+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
495+ * PURPOSE. See the GNU General Public License for more details.
496+ *
497+ * You should have received a copy of the GNU General Public License along
498+ * with this program. If not, see <http://www.gnu.org/licenses/>.
499+ */
500+
501+#include "cups-client.h"
502+#include "cups-cupsd-notifier.h"
503+
504+#include <cups/cups.h>
505+
506+#include <stdexcept>
507+
508+namespace ubuntu {
509+namespace printing {
510+namespace notifier {
511+
512+#define NOTIFY_LEASE_DURATION (24 * 60 * 60)
513+
514+
515+class CupsClient::Impl {
516+public:
517+ Impl()
518+ {
519+ GError *error = nullptr;
520+
521+ m_notifier_proxy = notifier_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
522+ G_DBUS_PROXY_FLAGS_NONE,
523+ nullptr,
524+ CUPS_DBUS_PATH,
525+ nullptr,
526+ &error);
527+
528+ if (error != nullptr) {
529+ std::string msg{"Error creating cups notifier proxy: "};
530+ throw std::runtime_error(msg + error->message);
531+ g_clear_error(&error);
532+ }
533+
534+ g_object_connect(m_notifier_proxy,
535+ "signal::job-created", on_job_changed, this,
536+ "signal::job-state", on_job_changed, this,
537+ "signal::job-completed", on_job_changed, this,
538+ "signal::printer-state-changed", on_printer_state_changed, this,
539+ nullptr);
540+ }
541+
542+ ~Impl()
543+ {
544+ if (m_notifier_proxy != nullptr) {
545+ g_signal_handlers_disconnect_by_data(m_notifier_proxy, this);
546+ g_clear_object(&m_notifier_proxy);
547+ }
548+
549+ // Cancel the subscription from cups, so its notifier can exit.
550+ cancel_subscription();
551+ }
552+
553+ // Signals to propagate
554+ core::Signal<const Printer&>& printer_state_changed()
555+ {
556+ return m_printer_state_changed;
557+ }
558+
559+ core::Signal<const Job&>& job_state_changed()
560+ {
561+ return m_job_state_changed;
562+ }
563+
564+ void create_subscription()
565+ {
566+ ipp_t *req;
567+ ipp_t *resp;
568+ ipp_attribute_t *attr;
569+
570+ req = ippNewRequest (IPP_CREATE_PRINTER_SUBSCRIPTION);
571+ ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
572+ "printer-uri", NULL, "/");
573+ ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_KEYWORD,
574+ "notify-events", NULL, "all");
575+ ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
576+ "notify-recipient-uri", NULL, "dbus://");
577+ ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
578+ "notify-lease-duration", NOTIFY_LEASE_DURATION);
579+
580+ resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
581+ if (!resp || cupsLastError() != IPP_OK) {
582+ g_warning ("Error subscribing to CUPS notifications: %s\n",
583+ cupsLastErrorString ());
584+ return;
585+ }
586+
587+ attr = ippFindAttribute (resp, "notify-subscription-id", IPP_TAG_INTEGER);
588+ if (attr) {
589+ m_subscription_id = ippGetInteger (attr, 0);
590+ } else {
591+ g_warning ("ipp-create-printer-subscription response doesn't contain "
592+ "subscription id.\n");
593+ }
594+ ippDelete (resp);
595+
596+ // Set up to renew the subscription a minute before it expires
597+ g_timeout_add_seconds(NOTIFY_LEASE_DURATION - 60,
598+ on_subscription_timeout,
599+ this);
600+ }
601+
602+ void renew_subscription()
603+ {
604+ ipp_t *req;
605+ ipp_t *resp;
606+
607+ req = ippNewRequest (IPP_RENEW_SUBSCRIPTION);
608+ ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
609+ "notify-subscription-id", m_subscription_id);
610+ ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
611+ "printer-uri", NULL, "/");
612+ ippAddString (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_URI,
613+ "notify-recipient-uri", NULL, "dbus://");
614+ ippAddInteger (req, IPP_TAG_SUBSCRIPTION, IPP_TAG_INTEGER,
615+ "notify-lease-duration", NOTIFY_LEASE_DURATION);
616+
617+ resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
618+ if (!resp || cupsLastError() != IPP_OK) {
619+ g_warning ("Error renewing CUPS subscription %d: %s\n",
620+ m_subscription_id, cupsLastErrorString ());
621+ create_subscription();
622+ }
623+
624+ ippDelete (resp);
625+ }
626+
627+ void cancel_subscription()
628+ {
629+ ipp_t *req;
630+ ipp_t *resp;
631+
632+ if (m_subscription_id <= 0) {
633+ return;
634+ }
635+
636+ req = ippNewRequest (IPP_CANCEL_SUBSCRIPTION);
637+ ippAddString (req, IPP_TAG_OPERATION, IPP_TAG_URI,
638+ "printer-uri", NULL, "/");
639+ ippAddInteger (req, IPP_TAG_OPERATION, IPP_TAG_INTEGER,
640+ "notify-subscription-id", m_subscription_id);
641+
642+ resp = cupsDoRequest (CUPS_HTTP_DEFAULT, req, "/");
643+ if (!resp || cupsLastError() != IPP_OK) {
644+ g_warning ("Error unsubscribing to CUPS notifications: %s\n",
645+ cupsLastErrorString ());
646+ return;
647+ }
648+
649+ ippDelete (resp);
650+ }
651+
652+ void refresh()
653+ {
654+ int num_dests;
655+ cups_dest_t* dests;
656+
657+ num_dests = cupsGetDests(&dests);
658+ for (int i = 0; i < num_dests; i++) {
659+ auto printer = get_printer_info(dests[i].name);
660+
661+ cups_job_t* jobs;
662+ const auto num_jobs = cupsGetJobs(&jobs, dests[i].name,
663+ true, CUPS_WHICHJOBS_ACTIVE);
664+ for (int j = 0; j < num_jobs; j++) {
665+ Job job;
666+ job.id = jobs[j].id;
667+ job.state = static_cast<Job::State>(jobs[j].state);
668+ job.name = jobs[j].title;
669+
670+ job.printer = printer;
671+
672+ m_job_state_changed(job);
673+ }
674+ cupsFreeJobs(num_jobs, jobs);
675+ }
676+ cupsFreeDests(num_dests, dests);
677+ }
678+
679+private:
680+ // Method to get Printer object from the name
681+ Printer get_printer_info(const std::string& name)
682+ {
683+ int num_dests;
684+ cups_dest_t* dests;
685+ cups_dest_t* our_dest;
686+ Printer printer;
687+ printer.name = name;
688+
689+ num_dests = cupsGetDests(&dests);
690+ our_dest = cupsGetDest(name.c_str(), nullptr,
691+ num_dests, dests);
692+
693+ if (our_dest != nullptr) {
694+ // Get the printer's description
695+ auto description = cupsGetOption("printer-info",
696+ our_dest->num_options,
697+ our_dest->options);
698+ if (description != nullptr) {
699+ printer.description = description;
700+ }
701+ }
702+
703+ cupsFreeDests(num_dests, dests);
704+ return printer;
705+ }
706+
707+ static gboolean on_subscription_timeout(gpointer gthis)
708+ {
709+ static_cast<Impl*>(gthis)->renew_subscription();
710+ return G_SOURCE_CONTINUE;
711+ }
712+
713+ static void on_job_changed (Notifier*,
714+ const char* printer_text,
715+ const char* printer_uri,
716+ const char *printer_name,
717+ unsigned int printer_state,
718+ const char *printer_state_reasons,
719+ bool printer_is_accepting_jobs,
720+ unsigned int job_id,
721+ unsigned int job_state,
722+ const char *job_state_reasons,
723+ const char *job_name,
724+ unsigned int job_impressions_completed,
725+ gpointer gthis)
726+ {
727+ auto self = static_cast<Impl*>(gthis);
728+
729+ auto printer = self->get_printer_info(printer_name);
730+ printer.state = static_cast<Printer::State>(printer_state);
731+ printer.text = printer_text;
732+ printer.uri = printer_uri;
733+ printer.state_reasons = printer_state_reasons;
734+ printer.accepting_jobs = printer_is_accepting_jobs;
735+
736+ Job job;
737+ job.printer = printer;
738+ job.state = static_cast<Job::State>(job_state);
739+ job.id = job_id;
740+ job.name = job_name;
741+ job.state_reasons = job_state_reasons;
742+ job.impressions_completed = job_impressions_completed;
743+
744+ self->m_job_state_changed(job);
745+ }
746+
747+ static void on_printer_state_changed(Notifier*,
748+ const char* text,
749+ const char* uri,
750+ const char* name,
751+ unsigned int state,
752+ const char* state_reasons,
753+ bool is_accepting_jobs,
754+ gpointer gthis)
755+ {
756+ auto self = static_cast<Impl*>(gthis);
757+
758+ auto printer = self->get_printer_info(name);
759+ printer.state = static_cast<Printer::State>(state);
760+ printer.text = text;
761+ printer.uri = uri;
762+ printer.state_reasons = state_reasons;
763+ printer.accepting_jobs = is_accepting_jobs;
764+
765+ // Get the number of jobs currently active on the printer
766+ cups_job_t* jobs;
767+ printer.num_jobs = cupsGetJobs(&jobs, name, true,
768+ CUPS_WHICHJOBS_ACTIVE);
769+ cupsFreeJobs(printer.num_jobs, jobs);
770+
771+ self->m_printer_state_changed(printer);
772+ }
773+
774+ int m_subscription_id = 0;
775+ Notifier* m_notifier_proxy = nullptr;
776+ core::Signal<const Printer&> m_printer_state_changed;
777+ core::Signal<const Job&> m_job_state_changed;
778+};
779+
780+CupsClient::CupsClient() :
781+ p(new Impl())
782+{
783+}
784+
785+CupsClient::~CupsClient()
786+{
787+}
788+
789+core::Signal<const Printer&>& CupsClient::printer_state_changed()
790+{
791+ return p->printer_state_changed();
792+}
793+
794+core::Signal<const Job&>& CupsClient::job_state_changed()
795+{
796+ return p->job_state_changed();
797+}
798+
799+void CupsClient::create_subscription()
800+{
801+ p->create_subscription();
802+}
803+
804+void CupsClient::renew_subscription()
805+{
806+ p->renew_subscription();
807+}
808+
809+void CupsClient::cancel_subscription()
810+{
811+ p->cancel_subscription();
812+}
813+
814+void CupsClient::refresh()
815+{
816+ p->refresh();
817+}
818+
819+} // notifier
820+} // printing
821+} // ubuntu
822
823=== added file 'notifier/src/cups-client.h'
824--- notifier/src/cups-client.h 1970-01-01 00:00:00 +0000
825+++ notifier/src/cups-client.h 2017-03-20 13:16:02 +0000
826@@ -0,0 +1,65 @@
827+/*
828+ * Copyright 2016-2017 Canonical Ltd.
829+ *
830+ * This program is free software: you can redistribute it and/or modify it
831+ * under the terms of the GNU General Public License version 3, as published
832+ * by the Free Software Foundation.
833+ *
834+ * This program is distributed in the hope that it will be useful, but
835+ * WITHOUT ANY WARRANTY; without even the implied warranties of
836+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
837+ * PURPOSE. See the GNU General Public License for more details.
838+ *
839+ * You should have received a copy of the GNU General Public License along
840+ * with this program. If not, see <http://www.gnu.org/licenses/>.
841+ */
842+
843+#pragma once
844+
845+#include "client.h"
846+#include "job.h"
847+#include "printer.h"
848+
849+#include <core/signal.h>
850+
851+#include <memory>
852+
853+namespace ubuntu {
854+namespace printing {
855+namespace notifier {
856+
857+#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
858+#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
859+#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
860+
861+ class CupsClient : public Client {
862+ public:
863+ CupsClient();
864+ virtual ~CupsClient();
865+
866+ // Signals corresponding to notifier
867+ core::Signal<const Printer&>& printer_state_changed() override;
868+
869+ // Signals corresponding to jobs
870+ core::Signal<const Job&>& job_state_changed() override;
871+
872+ // Methods to manage notification monitoring
873+ virtual void create_subscription() override;
874+ virtual void renew_subscription() override;
875+ virtual void cancel_subscription() override;
876+
877+ // To initialize the printing with current jobs
878+ virtual void refresh() override;
879+
880+ private:
881+ class Impl;
882+ std::unique_ptr<Impl> p;
883+
884+ // disable copying
885+ CupsClient(const CupsClient&) = delete;
886+ CupsClient& operator=(const CupsClient&) = delete;
887+ };
888+
889+} // notifier
890+} // printing
891+} // ubuntu
892
893=== added file 'notifier/src/dbus-names.h'
894--- notifier/src/dbus-names.h 1970-01-01 00:00:00 +0000
895+++ notifier/src/dbus-names.h 2017-03-20 13:16:02 +0000
896@@ -0,0 +1,27 @@
897+/*
898+ * Copyright 2012 Canonical Ltd.
899+ *
900+ * Authors: Lars Uebernickel <lars.uebernickel@canonical.com>
901+ *
902+ * This program is free software: you can redistribute it and/or modify it
903+ * under the terms of the GNU General Public License version 3, as published
904+ * by the Free Software Foundation.
905+ *
906+ * This program is distributed in the hope that it will be useful, but
907+ * WITHOUT ANY WARRANTY; without even the implied warranties of
908+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
909+ * PURPOSE. See the GNU General Public License for more details.
910+ *
911+ * You should have received a copy of the GNU General Public License along
912+ * with this program. If not, see <http://www.gnu.org/licenses/>.
913+ */
914+
915+#ifndef DBUS_NAMES_H
916+#define DBUS_NAMES_H
917+
918+#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
919+#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
920+#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
921+
922+#endif
923+
924
925=== added file 'notifier/src/job.h'
926--- notifier/src/job.h 1970-01-01 00:00:00 +0000
927+++ notifier/src/job.h 2017-03-20 13:16:02 +0000
928@@ -0,0 +1,50 @@
929+/*
930+ * Copyright 2016-2017 Canonical Ltd.
931+ *
932+ * This program is free software: you can redistribute it and/or modify it
933+ * under the terms of the GNU General Public License version 3, as published
934+ * by the Free Software Foundation.
935+ *
936+ * This program is distributed in the hope that it will be useful, but
937+ * WITHOUT ANY WARRANTY; without even the implied warranties of
938+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
939+ * PURPOSE. See the GNU General Public License for more details.
940+ *
941+ * You should have received a copy of the GNU General Public License along
942+ * with this program. If not, see <http://www.gnu.org/licenses/>.
943+ */
944+
945+#pragma once
946+
947+#include "printer.h"
948+
949+#include <string>
950+
951+namespace ubuntu {
952+namespace printing {
953+namespace notifier {
954+
955+ struct Job {
956+ // State to match ipp_jstate_t from cups.h
957+ typedef enum {
958+ PENDING = 3,
959+ HELD,
960+ PROCESSING,
961+ STOPPED,
962+ CANCELED,
963+ ABORTED,
964+ COMPLETED
965+ } State;
966+ State state = PENDING;
967+
968+ uint32_t id = 0;
969+ std::string name;
970+ std::string state_reasons;
971+ uint32_t impressions_completed = 0;
972+
973+ Printer printer;
974+ };
975+
976+} // notifier
977+} // printing
978+} // ubuntu
979
980=== added file 'notifier/src/main.cpp'
981--- notifier/src/main.cpp 1970-01-01 00:00:00 +0000
982+++ notifier/src/main.cpp 2017-03-20 13:16:02 +0000
983@@ -0,0 +1,61 @@
984+/*
985+ * Copyright 2016-2017 Canonical Ltd.
986+ *
987+ * This program is free software: you can redistribute it and/or modify it
988+ * under the terms of the GNU General Public License version 3, as published
989+ * by the Free Software Foundation.
990+ *
991+ * This program is distributed in the hope that it will be useful, but
992+ * WITHOUT ANY WARRANTY; without even the implied warranties of
993+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
994+ * PURPOSE. See the GNU General Public License for more details.
995+ *
996+ * You should have received a copy of the GNU General Public License along
997+ * with this program. If not, see <http://www.gnu.org/licenses/>.
998+ */
999+
1000+#include "actions.h"
1001+#include "cups-client.h"
1002+#include "notify-engine.h"
1003+#include "utils.h"
1004+
1005+#include <glib/gi18n.h>
1006+#include <gio/gio.h>
1007+#include <libnotify/notify.h>
1008+
1009+using namespace ubuntu::printing::notifier;
1010+
1011+int main(int /* argc */, char** argv)
1012+{
1013+ // Work around a deadlock in glib's type initialization.
1014+ // It can be removed when https://bugzilla.gnome.org/show_bug.cgi?id=674885 is fixed.
1015+ g_type_ensure(G_TYPE_DBUS_CONNECTION);
1016+
1017+ // boilerplate i18n
1018+ setlocale(LC_ALL, "");
1019+
1020+ // Need to prepend $SNAP to properly load translations
1021+ auto localedir = Utilities::prepend_snap_path(GETTEXT_LOCALEDIR);
1022+ bindtextdomain(GETTEXT_PACKAGE, localedir.c_str());
1023+ textdomain(GETTEXT_PACKAGE);
1024+
1025+ // set up us the machine
1026+ auto loop = g_main_loop_new(nullptr, false);
1027+
1028+ // Initialize notifications, and use program name for app name
1029+ if (!notify_init(basename(argv[0]))) {
1030+ g_critical("Unable to initialize libnotify.");
1031+ }
1032+
1033+ // create the client and set up the signal handling
1034+ auto client = std::make_shared<CupsClient>();
1035+ auto actions = std::make_shared<Actions>();
1036+ auto engine = std::make_shared<NotifyEngine>(client, actions);
1037+
1038+ g_main_loop_run(loop);
1039+
1040+ // cleanup
1041+ notify_uninit();
1042+ g_main_loop_unref(loop);
1043+ return 0;
1044+}
1045
1046=== added file 'notifier/src/notification.cpp'
1047--- notifier/src/notification.cpp 1970-01-01 00:00:00 +0000
1048+++ notifier/src/notification.cpp 2017-03-20 13:16:02 +0000
1049@@ -0,0 +1,185 @@
1050+/*
1051+ * Copyright 2017 Canonical Ltd.
1052+ *
1053+ * This program is free software: you can redistribute it and/or modify it
1054+ * under the terms of the GNU General Public License version 3, as published
1055+ * by the Free Software Foundation.
1056+ *
1057+ * This program is distributed in the hope that it will be useful, but
1058+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1059+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1060+ * PURPOSE. See the GNU General Public License for more details.
1061+ *
1062+ * You should have received a copy of the GNU General Public License along
1063+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1064+ */
1065+
1066+#include "notification.h"
1067+
1068+#include <libnotify/notification.h>
1069+#include <libnotify/notify.h>
1070+
1071+namespace ubuntu {
1072+namespace printing {
1073+namespace notifier {
1074+
1075+class Notification::Impl
1076+{
1077+public:
1078+ Impl(const std::string& summary,
1079+ const std::string& body,
1080+ const std::string& icon_name):
1081+ m_summary(summary),
1082+ m_body(body),
1083+ m_icon_name(icon_name)
1084+ {
1085+ m_nn.reset(notify_notification_new(m_summary.c_str(),
1086+ m_body.c_str(),
1087+ m_icon_name.c_str()),
1088+ [this](NotifyNotification* n) {
1089+ g_signal_handlers_disconnect_by_data(n, this);
1090+ g_object_unref(n);
1091+ });
1092+
1093+ g_signal_connect(m_nn.get(), "closed",
1094+ G_CALLBACK(on_notification_closed), this);
1095+ }
1096+
1097+ ~Impl()
1098+ {
1099+ }
1100+
1101+ core::Signal<const std::string&>& activated()
1102+ {
1103+ return m_activated;
1104+ }
1105+
1106+ core::Signal<>& closed()
1107+ {
1108+ return m_closed;
1109+ }
1110+
1111+ void add_action(const std::string& action, const std::string& label)
1112+ {
1113+ notify_notification_add_action(m_nn.get(),
1114+ action.c_str(), label.c_str(),
1115+ on_notify_activated,
1116+ this, nullptr);
1117+ }
1118+
1119+ void set_hint(const std::string& hint, const std::string& value)
1120+ {
1121+ notify_notification_set_hint_string(m_nn.get(),
1122+ hint.c_str(), value.c_str());
1123+ }
1124+
1125+ void close()
1126+ {
1127+ if (!notify_is_initted()) {
1128+ g_warning("Tried to close a notification without notify_init().");
1129+ return;
1130+ }
1131+
1132+ GError* error = nullptr;
1133+ notify_notification_close(m_nn.get(), &error);
1134+
1135+ if (error != nullptr) {
1136+ g_critical("Error closing notification: %s", error->message);
1137+ g_clear_error(&error);
1138+ }
1139+ }
1140+
1141+ void show()
1142+ {
1143+ if (!notify_is_initted()) {
1144+ g_critical("Unable to display notifications without notify_init().");
1145+ return;
1146+ }
1147+
1148+ if (m_summary.empty()) {
1149+ g_critical("Attempting to show notification with no summary!");
1150+ return;
1151+ }
1152+
1153+ GError* error = nullptr;
1154+ notify_notification_show(m_nn.get(), &error);
1155+
1156+ if (error != nullptr) {
1157+ g_critical("Error showing notification: %s", error->message);
1158+ g_clear_error(&error);
1159+ }
1160+ }
1161+
1162+private:
1163+ static void on_notify_activated(NotifyNotification*,
1164+ char* action,
1165+ gpointer gthis)
1166+ {
1167+ auto self = static_cast<Impl*>(gthis);
1168+ self->m_activated(action);
1169+ }
1170+
1171+ static void on_notification_closed(NotifyNotification*,
1172+ gpointer gthis)
1173+ {
1174+ auto self = static_cast<Impl*>(gthis);
1175+ g_debug("Notification was closed.");
1176+ self->m_closed();
1177+ }
1178+
1179+ std::shared_ptr<NotifyNotification> m_nn;
1180+
1181+ std::string m_summary;
1182+ std::string m_body;
1183+ std::string m_icon_name;
1184+
1185+ core::Signal<const std::string&> m_activated;
1186+ core::Signal<> m_closed;
1187+};
1188+
1189+Notification::Notification(const std::string& summary,
1190+ const std::string& body,
1191+ const std::string& icon_name):
1192+ p(new Impl(summary, body, icon_name))
1193+{
1194+}
1195+
1196+Notification::~Notification()
1197+{
1198+}
1199+
1200+core::Signal<const std::string&>& Notification::activated()
1201+{
1202+ return p->activated();
1203+}
1204+
1205+core::Signal<>& Notification::closed()
1206+{
1207+ return p->closed();
1208+}
1209+
1210+void Notification::add_action(const std::string& action,
1211+ const std::string& label)
1212+{
1213+ p->add_action(action, label);
1214+}
1215+
1216+void Notification::set_hint(const std::string& hint,
1217+ const std::string& value)
1218+{
1219+ p->set_hint(hint, value);
1220+}
1221+
1222+void Notification::close()
1223+{
1224+ p->close();
1225+}
1226+
1227+void Notification::show()
1228+{
1229+ p->show();
1230+}
1231+
1232+} // notifier
1233+} // printing
1234+} // ubuntu
1235
1236=== added file 'notifier/src/notification.h'
1237--- notifier/src/notification.h 1970-01-01 00:00:00 +0000
1238+++ notifier/src/notification.h 2017-03-20 13:16:02 +0000
1239@@ -0,0 +1,61 @@
1240+/*
1241+ * Copyright 2017 Canonical Ltd.
1242+ *
1243+ * This program is free software: you can redistribute it and/or modify it
1244+ * under the terms of the GNU General Public License version 3, as published
1245+ * by the Free Software Foundation.
1246+ *
1247+ * This program is distributed in the hope that it will be useful, but
1248+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1249+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1250+ * PURPOSE. See the GNU General Public License for more details.
1251+ *
1252+ * You should have received a copy of the GNU General Public License along
1253+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1254+ */
1255+
1256+#pragma once
1257+
1258+#include <string>
1259+
1260+#include <core/signal.h>
1261+
1262+namespace ubuntu {
1263+namespace printing {
1264+namespace notifier {
1265+
1266+ class Notification {
1267+ public:
1268+ Notification(const std::string& summary,
1269+ const std::string& body,
1270+ const std::string& icon_name);
1271+ virtual ~Notification();
1272+
1273+ // Signal for activation of actions in notification
1274+ virtual core::Signal<const std::string&>& activated();
1275+
1276+ // Signal for forced closing of notification
1277+ virtual core::Signal<>& closed();
1278+
1279+ virtual void add_action(const std::string& action,
1280+ const std::string& label);
1281+
1282+ // For hints
1283+ virtual void set_hint(const std::string& hint,
1284+ const std::string& value);
1285+
1286+ virtual void close();
1287+ virtual void show();
1288+
1289+ private:
1290+ class Impl;
1291+ std::unique_ptr<Impl> p;
1292+
1293+ // disable copying
1294+ Notification(const Notification&) = delete;
1295+ Notification& operator=(const Notification&) = delete;
1296+ };
1297+
1298+} // notifier
1299+} // printing
1300+} // ubuntu
1301
1302=== added file 'notifier/src/notify-engine.cpp'
1303--- notifier/src/notify-engine.cpp 1970-01-01 00:00:00 +0000
1304+++ notifier/src/notify-engine.cpp 2017-03-20 13:16:02 +0000
1305@@ -0,0 +1,204 @@
1306+/*
1307+ * Copyright 2017 Canonical Ltd.
1308+ *
1309+ * This program is free software: you can redistribute it and/or modify it
1310+ * under the terms of the GNU General Public License version 3, as published
1311+ * by the Free Software Foundation.
1312+ *
1313+ * This program is distributed in the hope that it will be useful, but
1314+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1315+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1316+ * PURPOSE. See the GNU General Public License for more details.
1317+ *
1318+ * You should have received a copy of the GNU General Public License along
1319+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1320+ */
1321+
1322+#include "notify-engine.h"
1323+
1324+#include <map>
1325+#include <regex>
1326+#include <unordered_set>
1327+
1328+#include <boost/algorithm/string.hpp>
1329+#include <boost/format.hpp>
1330+
1331+#include <glib/gi18n.h>
1332+#include <libnotify/notify.h>
1333+
1334+namespace ubuntu {
1335+namespace printing {
1336+namespace notifier {
1337+
1338+class NotifyEngine::Impl
1339+{
1340+ struct notification_data
1341+ {
1342+ std::shared_ptr<Notification> notification;
1343+ std::unordered_set<std::shared_ptr<Notification>> notifications;
1344+ };
1345+
1346+public:
1347+ Impl(const std::shared_ptr<Client>& client,
1348+ const std::shared_ptr<Actions>& actions):
1349+ m_client(client),
1350+ m_actions(actions),
1351+ // NOTE: sorted alphabetically by state
1352+ m_reasons({
1353+ {"cover-open", _("A cover is open on the printer “%s”.")},
1354+ {"cups-missing-filter", _("The printer “%s” can’t be used, because required software is missing.")},
1355+ {"door-open-report", _("A door is open on the printer “%s”.")},
1356+ {"media-empty", _("The printer “%s” is out of paper.")},
1357+ {"media-low", _("The printer “%s” is low on paper.")},
1358+ {"offline", _("The printer “%s” is currently off-line.")},
1359+ {"other", _("The printer “%s” has an unknown problem.")},
1360+ {"toner-empty", _("The printer “%s” is out of toner.")},
1361+ {"toner-low", _("The printer “%s” is low on toner.")},
1362+ })
1363+ {
1364+ }
1365+
1366+ ~Impl()
1367+ {
1368+ }
1369+
1370+ std::string get_displayable_reason(const std::string& reason)
1371+ {
1372+ return m_reasons[reason];
1373+ }
1374+
1375+ std::unordered_set<std::string> get_notified_reasons(const Printer& printer)
1376+ {
1377+ return m_notified[printer.name];
1378+ }
1379+
1380+ void set_notified_reasons(const Printer& printer,
1381+ std::unordered_set<std::string> reasons)
1382+ {
1383+ m_notified[printer.name] = reasons;
1384+ }
1385+
1386+ void show_notification(const std::shared_ptr<Notification>& notification)
1387+ {
1388+ if (notification.get() == nullptr) {
1389+ return;
1390+ }
1391+
1392+ notification->activated().connect([this](const std::string& action) {
1393+ static const std::regex actionsettings{"^settings:///.*"};
1394+ if (std::regex_match(action, actionsettings)) {
1395+ m_actions->open_settings_app(action);
1396+ }
1397+ // Otherwise we just ignore the action.
1398+ });
1399+ notification->closed().connect([this, &notification]() {
1400+ g_debug("Closed notification.");
1401+ auto data = new notification_data({notification, m_notifications});
1402+ g_idle_add(on_delete_later, data);
1403+ });
1404+ m_notifications.emplace(notification);
1405+ notification->show();
1406+ }
1407+
1408+private:
1409+ static gboolean on_delete_later(gpointer gdata)
1410+ {
1411+ auto data = static_cast<notification_data*>(gdata);
1412+ data->notifications.erase(data->notification);
1413+ return G_SOURCE_REMOVE;
1414+ }
1415+
1416+ std::shared_ptr<Client> m_client;
1417+ std::shared_ptr<Actions> m_actions;
1418+
1419+ // The set of current notifications
1420+ std::unordered_set<std::shared_ptr<Notification>> m_notifications;
1421+
1422+ // The map of "printer" -> set of notified notified states
1423+ std::map<std::string, std::unordered_set<std::string>> m_notified;
1424+
1425+ // The map of "reason" -> _("Translated displayable reason") strings
1426+ std::map<std::string, std::string> m_reasons;
1427+}; // class Impl
1428+
1429+
1430+NotifyEngine::NotifyEngine(const std::shared_ptr<Client>& client,
1431+ const std::shared_ptr<Actions>& actions):
1432+ p(new Impl(client, actions))
1433+{
1434+ client->job_state_changed().connect([this](const Job& job) {
1435+ const auto& printer = job.printer;
1436+ g_debug("State changed for job %u '%s` on printer '%s' and reasons were '%s'",
1437+ job.id,
1438+ job.name.c_str(),
1439+ printer.description.empty() ? printer.name.c_str() : printer.description.c_str(),
1440+ job.state_reasons.c_str());
1441+ if (job.state == Job::State::COMPLETED) {
1442+ auto notification = build_job_notification(job);
1443+ p->show_notification(notification);
1444+ }
1445+ });
1446+ client->printer_state_changed().connect([this](const Printer& printer) {
1447+ g_debug("Printer state changed for reasons: '%s'",
1448+ printer.state_reasons.c_str());
1449+ if (printer.num_jobs > 0) {
1450+ auto notified = p->get_notified_reasons(printer);
1451+ std::unordered_set<std::string> reasons;
1452+ boost::split(reasons, printer.state_reasons, boost::is_any_of(","));
1453+ for (const auto& reason: reasons) {
1454+ if (notified.count(reason) == 0) {
1455+ auto notification = build_printer_notification(printer, reason);
1456+ p->show_notification(notification);
1457+ }
1458+ }
1459+ p->set_notified_reasons(printer, reasons);
1460+ }
1461+ });
1462+}
1463+
1464+NotifyEngine::~NotifyEngine()
1465+{
1466+}
1467+
1468+std::shared_ptr<Notification> NotifyEngine::build_job_notification(const Job& job)
1469+{
1470+ std::shared_ptr<Notification> notification;
1471+
1472+ auto summary = boost::format(_("“%s” has printed.")) % job.name;
1473+ notification.reset(new Notification(summary.str(), "", NOTIFY_PRINTER_ICON));
1474+
1475+ return notification;
1476+}
1477+
1478+std::shared_ptr<Notification> NotifyEngine::build_printer_notification(const Printer& printer,
1479+ const std::string& reason)
1480+{
1481+ std::shared_ptr<Notification> notification;
1482+
1483+ const auto& displayname = printer.description.empty() ? printer.description : printer.name;
1484+
1485+ // Get the reason text and add summary/body if valid
1486+ auto untranslated = p->get_displayable_reason(reason);
1487+ if (!untranslated.empty()) {
1488+ auto summary = boost::format(untranslated) % displayname;
1489+
1490+ auto jobtext = ngettext("You have %d job queued to print on this printer.",
1491+ "You have %d jobs queued to print on this printer.",
1492+ printer.num_jobs);
1493+ auto body = boost::format(jobtext) % printer.num_jobs;
1494+
1495+ notification.reset(new Notification(summary.str(), body.str(), NOTIFY_ERROR_ICON));
1496+
1497+ notification->set_hint(NOTIFY_HINT_SNAP, "true");
1498+ notification->add_action("PRINTER_ACTION_IGNORE", _("OK"));
1499+ std::string settings_url{"settings:///system/printers/"};
1500+ notification->add_action(settings_url + printer.name, _("Settings…"));
1501+ }
1502+
1503+ return notification;
1504+}
1505+
1506+
1507+} //notifier
1508+} // printing
1509+} // ubuntu
1510
1511=== added file 'notifier/src/notify-engine.h'
1512--- notifier/src/notify-engine.h 1970-01-01 00:00:00 +0000
1513+++ notifier/src/notify-engine.h 2017-03-20 13:16:02 +0000
1514@@ -0,0 +1,57 @@
1515+/*
1516+ * Copyright 2017 Canonical Ltd.
1517+ *
1518+ * This program is free software: you can redistribute it and/or modify it
1519+ * under the terms of the GNU General Public License version 3, as published
1520+ * by the Free Software Foundation.
1521+ *
1522+ * This program is distributed in the hope that it will be useful, but
1523+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1524+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1525+ * PURPOSE. See the GNU General Public License for more details.
1526+ *
1527+ * You should have received a copy of the GNU General Public License along
1528+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1529+ */
1530+
1531+#pragma once
1532+
1533+#include "actions.h"
1534+#include "client.h"
1535+#include "notification.h"
1536+
1537+#include <memory>
1538+
1539+namespace ubuntu {
1540+namespace printing {
1541+namespace notifier {
1542+
1543+ // Icons for notifications
1544+ static constexpr const char* NOTIFY_PRINTER_ICON{"printer-symbolic"};
1545+ static constexpr const char* NOTIFY_ERROR_ICON{"printer-error-symbolic"};
1546+
1547+ // Hints for notifications
1548+ static constexpr const char* NOTIFY_HINT_SNAP{"x-canonical-snap-decisions"};
1549+
1550+ class NotifyEngine {
1551+ public:
1552+ NotifyEngine(const std::shared_ptr<Client>& client,
1553+ const std::shared_ptr<Actions>& actions);
1554+ virtual ~NotifyEngine();
1555+
1556+ virtual std::shared_ptr<Notification> build_job_notification(const Job& job);
1557+ virtual std::shared_ptr<Notification> build_printer_notification(const Printer& printer,
1558+ const std::string& reason);
1559+
1560+ private:
1561+ class Impl;
1562+ std::unique_ptr<Impl> p;
1563+
1564+ // disable copying
1565+ NotifyEngine(const NotifyEngine&) = delete;
1566+ NotifyEngine& operator=(const NotifyEngine&) = delete;
1567+ };
1568+
1569+} // notifier
1570+} // printing
1571+} // ubuntu
1572
1573=== added file 'notifier/src/org.cups.cupsd.Notifier.xml'
1574--- notifier/src/org.cups.cupsd.Notifier.xml 1970-01-01 00:00:00 +0000
1575+++ notifier/src/org.cups.cupsd.Notifier.xml 2017-03-20 13:16:02 +0000
1576@@ -0,0 +1,147 @@
1577+<node>
1578+
1579+ <interface name="org.cups.cupsd.Notifier">
1580+
1581+ <signal name="ServerStarted">
1582+ <arg type="s" name="text" />
1583+ </signal>
1584+
1585+ <signal name="ServerRestarted">
1586+ <arg type="s" name="text" />
1587+ </signal>
1588+
1589+ <signal name="ServerStopped">
1590+ <arg type="s" name="text" />
1591+ </signal>
1592+
1593+ <signal name="ServerAudit">
1594+ <arg type="s" name="text" />
1595+ </signal>
1596+
1597+ <signal name="PrinterAdded">
1598+ <arg type="s" name="text" />
1599+ <arg type="s" name="printer_uri" />
1600+ <arg type="s" name="printer_name" />
1601+ <arg type="u" name="printer_state" />
1602+ <arg type="s" name="printer_state_reasons" />
1603+ <arg type="b" name="printer_is_accepting_jobs" />
1604+ </signal>
1605+
1606+ <signal name="PrinterDeleted">
1607+ <arg type="s" name="text" />
1608+ <arg type="s" name="printer_uri" />
1609+ <arg type="s" name="printer_name" />
1610+ <arg type="u" name="printer_state" />
1611+ <arg type="s" name="printer_state_reasons" />
1612+ <arg type="b" name="printer_is_accepting_jobs" />
1613+ </signal>
1614+
1615+ <signal name="PrinterModified">
1616+ <arg type="s" name="text" />
1617+ <arg type="s" name="printer_uri" />
1618+ <arg type="s" name="printer_name" />
1619+ <arg type="u" name="printer_state" />
1620+ <arg type="s" name="printer_state_reasons" />
1621+ <arg type="b" name="printer_is_accepting_jobs" />
1622+ </signal>
1623+
1624+ <signal name="PrinterRestarted">
1625+ <arg type="s" name="text" />
1626+ <arg type="s" name="printer_uri" />
1627+ <arg type="s" name="printer_name" />
1628+ <arg type="u" name="printer_state" />
1629+ <arg type="s" name="printer_state_reasons" />
1630+ <arg type="b" name="printer_is_accepting_jobs" />
1631+ </signal>
1632+
1633+ <signal name="PrinterStopped">
1634+ <arg type="s" name="text" />
1635+ <arg type="s" name="printer_uri" />
1636+ <arg type="s" name="printer_name" />
1637+ <arg type="u" name="printer_state" />
1638+ <arg type="s" name="printer_state_reasons" />
1639+ <arg type="b" name="printer_is_accepting_jobs" />
1640+ </signal>
1641+
1642+ <signal name="PrinterShutdown">
1643+ <arg type="s" name="text" />
1644+ <arg type="s" name="printer_uri" />
1645+ <arg type="s" name="printer_name" />
1646+ <arg type="u" name="printer_state" />
1647+ <arg type="s" name="printer_state_reasons" />
1648+ <arg type="b" name="printer_is_accepting_jobs" />
1649+ </signal>
1650+
1651+ <signal name="PrinterStateChanged">
1652+ <arg type="s" name="text" />
1653+ <arg type="s" name="printer_uri" />
1654+ <arg type="s" name="printer_name" />
1655+ <arg type="u" name="printer_state" />
1656+ <arg type="s" name="printer_state_reasons" />
1657+ <arg type="b" name="printer_is_accepting_jobs" />
1658+ </signal>
1659+
1660+ <signal name="PrinterFinishingsChanged">
1661+ <arg type="s" name="text" />
1662+ <arg type="s" name="printer_uri" />
1663+ <arg type="s" name="printer_name" />
1664+ <arg type="u" name="printer_state" />
1665+ <arg type="s" name="printer_state_reasons" />
1666+ <arg type="b" name="printer_is_accepting_jobs" />
1667+ </signal>
1668+
1669+ <signal name="PrinterMediaChanged">
1670+ <arg type="s" name="text" />
1671+ <arg type="s" name="printer_uri" />
1672+ <arg type="s" name="printer_name" />
1673+ <arg type="u" name="printer_state" />
1674+ <arg type="s" name="printer_state_reasons" />
1675+ <arg type="b" name="printer_is_accepting_jobs" />
1676+ </signal>
1677+
1678+ <signal name="JobCreated">
1679+ <arg type="s" name="text" />
1680+ <arg type="s" name="printer_uri" />
1681+ <arg type="s" name="printer_name" />
1682+ <arg type="u" name="printer_state" />
1683+ <arg type="s" name="printer_state_reasons" />
1684+ <arg type="b" name="printer_is_accepting_jobs" />
1685+ <arg type="u" name="job_id" />
1686+ <arg type="u" name="job_state" />
1687+ <arg type="s" name="job_state_reasons" />
1688+ <arg type="s" name="job_name" />
1689+ <arg type="u" name="job_impressions_completed" />
1690+ </signal>
1691+
1692+ <signal name="JobCompleted">
1693+ <arg type="s" name="text" />
1694+ <arg type="s" name="printer_uri" />
1695+ <arg type="s" name="printer_name" />
1696+ <arg type="u" name="printer_state" />
1697+ <arg type="s" name="printer_state_reasons" />
1698+ <arg type="b" name="printer_is_accepting_jobs" />
1699+ <arg type="u" name="job_id" />
1700+ <arg type="u" name="job_state" />
1701+ <arg type="s" name="job_state_reasons" />
1702+ <arg type="s" name="job_name" />
1703+ <arg type="u" name="job_impressions_completed" />
1704+ </signal>
1705+
1706+ <signal name="JobState">
1707+ <arg type="s" name="text" />
1708+ <arg type="s" name="printer_uri" />
1709+ <arg type="s" name="printer_name" />
1710+ <arg type="u" name="printer_state" />
1711+ <arg type="s" name="printer_state_reasons" />
1712+ <arg type="b" name="printer_is_accepting_jobs" />
1713+ <arg type="u" name="job_id" />
1714+ <arg type="u" name="job_state" />
1715+ <arg type="s" name="job_state_reasons" />
1716+ <arg type="s" name="job_name" />
1717+ <arg type="u" name="job_impressions_completed" />
1718+ </signal>
1719+
1720+ </interface>
1721+
1722+</node>
1723+
1724
1725=== added file 'notifier/src/printer.h'
1726--- notifier/src/printer.h 1970-01-01 00:00:00 +0000
1727+++ notifier/src/printer.h 2017-03-20 13:16:02 +0000
1728@@ -0,0 +1,45 @@
1729+/*
1730+ * Copyright 2016-2017 Canonical Ltd.
1731+ *
1732+ * This program is free software: you can redistribute it and/or modify it
1733+ * under the terms of the GNU General Public License version 3, as published
1734+ * by the Free Software Foundation.
1735+ *
1736+ * This program is distributed in the hope that it will be useful, but
1737+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1738+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1739+ * PURPOSE. See the GNU General Public License for more details.
1740+ *
1741+ * You should have received a copy of the GNU General Public License along
1742+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1743+ */
1744+
1745+#pragma once
1746+
1747+#include <string>
1748+
1749+namespace ubuntu {
1750+namespace printing {
1751+namespace notifier {
1752+
1753+ struct Printer {
1754+ // State to match ipp_jstate_t from cups.h
1755+ typedef enum {
1756+ IDLE = 3,
1757+ PROCESSING,
1758+ STOPPED
1759+ } State;
1760+ State state = IDLE;
1761+
1762+ std::string name;
1763+ std::string description;
1764+ std::string text;
1765+ std::string uri;
1766+ std::string state_reasons;
1767+ bool accepting_jobs = false;
1768+ uint32_t num_jobs = 0;
1769+ };
1770+
1771+} // notifier
1772+} // printing
1773+} // ubuntu
1774
1775=== added file 'notifier/src/utils.cpp'
1776--- notifier/src/utils.cpp 1970-01-01 00:00:00 +0000
1777+++ notifier/src/utils.cpp 2017-03-20 13:16:02 +0000
1778@@ -0,0 +1,37 @@
1779+/*
1780+ * Copyright 2016-2017 Canonical Ltd.
1781+ *
1782+ * This program is free software: you can redistribute it and/or modify it
1783+ * under the terms of the GNU General Public License version 3, as published
1784+ * by the Free Software Foundation.
1785+ *
1786+ * This program is distributed in the hope that it will be useful, but
1787+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1788+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1789+ * PURPOSE. See the GNU General Public License for more details.
1790+ *
1791+ * You should have received a copy of the GNU General Public License along
1792+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1793+ */
1794+
1795+#include "utils.h"
1796+
1797+namespace ubuntu {
1798+namespace printing {
1799+namespace notifier {
1800+
1801+std::string Utilities::prepend_snap_path(const std::string& path)
1802+{
1803+ auto get_snap_path = []() {
1804+ const char* env_snap = getenv("SNAP");
1805+ if (env_snap == nullptr) {
1806+ return "";
1807+ }
1808+ return env_snap;
1809+ };
1810+ return get_snap_path() + path;
1811+}
1812+
1813+} // notifier
1814+} // printing
1815+} // ubuntu
1816
1817=== added file 'notifier/src/utils.h'
1818--- notifier/src/utils.h 1970-01-01 00:00:00 +0000
1819+++ notifier/src/utils.h 2017-03-20 13:16:02 +0000
1820@@ -0,0 +1,32 @@
1821+/*
1822+ * Copyright 2016-2017 Canonical Ltd.
1823+ *
1824+ * This program is free software: you can redistribute it and/or modify it
1825+ * under the terms of the GNU General Public License version 3, as published
1826+ * by the Free Software Foundation.
1827+ *
1828+ * This program is distributed in the hope that it will be useful, but
1829+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1830+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1831+ * PURPOSE. See the GNU General Public License for more details.
1832+ *
1833+ * You should have received a copy of the GNU General Public License along
1834+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1835+ */
1836+
1837+#pragma once
1838+
1839+#include <string>
1840+
1841+namespace ubuntu {
1842+namespace printing {
1843+namespace notifier {
1844+
1845+ class Utilities {
1846+ public:
1847+ static std::string prepend_snap_path(const std::string& path);
1848+ };
1849+
1850+} // notifier
1851+} // printing
1852+} // ubuntu
1853
1854=== added directory 'notifier/tests'
1855=== added file 'notifier/tests/CMakeLists.txt'
1856--- notifier/tests/CMakeLists.txt 1970-01-01 00:00:00 +0000
1857+++ notifier/tests/CMakeLists.txt 2017-03-20 13:16:02 +0000
1858@@ -0,0 +1,28 @@
1859+find_package(GMock REQUIRED)
1860+
1861+include_directories(
1862+ ${CMAKE_SOURCE_DIR}/notifier/src
1863+ ${SERVICE_DEPS_INCLUDE_DIRS}
1864+)
1865+link_directories(
1866+ ${SERVICE_DEPS_LIBRARY_DIRS}
1867+)
1868+
1869+add_executable(test-${SERVICE_LIB_NAME}
1870+ actions-mock.h
1871+ client-mock.h
1872+ mock-notification.h
1873+ test_notify-engine.cpp
1874+ test_utils.cpp
1875+)
1876+target_link_libraries(test-${SERVICE_LIB_NAME}
1877+ ${SERVICE_LIB_NAME}
1878+ ${SERVICE_DEPS_LIBRARIES}
1879+ ${CUPS_LIBS}
1880+
1881+ ${GTEST_LIBRARIES}
1882+ ${GMOCK_LIBRARIES}
1883+
1884+ ${CMAKE_THREAD_LIBS_INIT}
1885+)
1886+add_test(test-${SERVICE_LIB_NAME} test-${SERVICE_LIB_NAME})
1887
1888=== added file 'notifier/tests/actions-mock.h'
1889--- notifier/tests/actions-mock.h 1970-01-01 00:00:00 +0000
1890+++ notifier/tests/actions-mock.h 2017-03-20 13:16:02 +0000
1891@@ -0,0 +1,42 @@
1892+/*
1893+ * Copyright 2017 Canonical Ltd.
1894+ *
1895+ * This program is free software: you can redistribute it and/or modify it
1896+ * under the terms of the GNU General Public License version 3, as published
1897+ * by the Free Software Foundation.
1898+ *
1899+ * This program is distributed in the hope that it will be useful, but
1900+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1901+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1902+ * PURPOSE. See the GNU General Public License for more details.
1903+ *
1904+ * You should have received a copy of the GNU General Public License along
1905+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1906+ */
1907+
1908+#pragma once
1909+
1910+#include "actions.h"
1911+
1912+#include <gmock/gmock.h>
1913+
1914+namespace ubuntu {
1915+namespace printing {
1916+namespace notifier {
1917+
1918+ class MockActions: public Actions
1919+ {
1920+ public:
1921+ MockActions():
1922+ Actions()
1923+ {
1924+ }
1925+
1926+ ~MockActions() = default;
1927+
1928+ MOCK_METHOD1(open_settings_app, void(const std::string&));
1929+ };
1930+
1931+} // notifier
1932+} // printing
1933+} // ubuntu
1934
1935=== added file 'notifier/tests/client-mock.h'
1936--- notifier/tests/client-mock.h 1970-01-01 00:00:00 +0000
1937+++ notifier/tests/client-mock.h 2017-03-20 13:16:02 +0000
1938@@ -0,0 +1,54 @@
1939+/*
1940+ * Copyright 2016-2017 Canonical Ltd.
1941+ *
1942+ * This program is free software: you can redistribute it and/or modify it
1943+ * under the terms of the GNU General Public License version 3, as published
1944+ * by the Free Software Foundation.
1945+ *
1946+ * This program is distributed in the hope that it will be useful, but
1947+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1948+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1949+ * PURPOSE. See the GNU General Public License for more details.
1950+ *
1951+ * You should have received a copy of the GNU General Public License along
1952+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1953+ */
1954+
1955+#pragma once
1956+
1957+#include "client.h"
1958+
1959+#include <core/signal.h>
1960+#include <gmock/gmock.h>
1961+
1962+namespace ubuntu {
1963+namespace printing {
1964+namespace notifier {
1965+
1966+ class MockClient: public Client
1967+ {
1968+ public:
1969+ explicit MockClient(): Client() {}
1970+ ~MockClient() = default;
1971+
1972+ core::Signal<const Printer&>& printer_state_changed()
1973+ {
1974+ return m_printer_state_changed;
1975+ }
1976+ core::Signal<const Job&>& job_state_changed()
1977+ {
1978+ return m_job_state_changed;
1979+ }
1980+
1981+ MOCK_METHOD0(create_subscription, void());
1982+ MOCK_METHOD0(renew_subscription, void());
1983+ MOCK_METHOD0(cancel_subscription, void());
1984+ MOCK_METHOD0(refresh, void());
1985+
1986+ core::Signal<const Printer&> m_printer_state_changed;
1987+ core::Signal<const Job&> m_job_state_changed;
1988+ };
1989+
1990+} // notifier
1991+} // printing
1992+} // ubuntu
1993
1994=== added file 'notifier/tests/mock-notification.h'
1995--- notifier/tests/mock-notification.h 1970-01-01 00:00:00 +0000
1996+++ notifier/tests/mock-notification.h 2017-03-20 13:16:02 +0000
1997@@ -0,0 +1,52 @@
1998+/*
1999+ * Copyright 2017 Canonical Ltd.
2000+ *
2001+ * This program is free software: you can redistribute it and/or modify it
2002+ * under the terms of the GNU General Public License version 3, as published
2003+ * by the Free Software Foundation.
2004+ *
2005+ * This program is distributed in the hope that it will be useful, but
2006+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2007+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2008+ * PURPOSE. See the GNU General Public License for more details.
2009+ *
2010+ * You should have received a copy of the GNU General Public License along
2011+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2012+ */
2013+
2014+#include "notification.h"
2015+
2016+#include <core/signal.h>
2017+#include <gmock/gmock.h>
2018+
2019+namespace ubuntu {
2020+namespace printing {
2021+namespace notifier {
2022+
2023+class MockNotification: public Notification
2024+{
2025+ public:
2026+ MockNotification(const std::string& summary, const std::string& body,
2027+ const std::string& icon_name):
2028+ Notification(summary, body, icon_name)
2029+ {
2030+ }
2031+
2032+ ~MockNotification()
2033+ {
2034+ }
2035+
2036+ core::Signal<>& closed()
2037+ {
2038+ return m_closed;
2039+ }
2040+
2041+ MOCK_METHOD0(close, void());
2042+ MOCK_METHOD0(show, void());
2043+
2044+ core::Signal<> m_closed;
2045+};
2046+
2047+} // notifier
2048+} // printing
2049+} // ubuntu
2050
2051=== added file 'notifier/tests/test_notify-engine.cpp'
2052--- notifier/tests/test_notify-engine.cpp 1970-01-01 00:00:00 +0000
2053+++ notifier/tests/test_notify-engine.cpp 2017-03-20 13:16:02 +0000
2054@@ -0,0 +1,88 @@
2055+/*
2056+ * Copyright 2017 Canonical Ltd.
2057+ *
2058+ * This program is free software: you can redistribute it and/or modify it
2059+ * under the terms of the GNU General Public License version 3, as published
2060+ * by the Free Software Foundation.
2061+ *
2062+ * This program is distributed in the hope that it will be useful, but
2063+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2064+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2065+ * PURPOSE. See the GNU General Public License for more details.
2066+ *
2067+ * You should have received a copy of the GNU General Public License along
2068+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2069+ */
2070+
2071+#include "actions-mock.h"
2072+#include "client-mock.h"
2073+#include "mock-notification.h"
2074+#include "notify-engine.h"
2075+
2076+#include <memory>
2077+
2078+#include <gmock/gmock.h>
2079+
2080+using namespace ubuntu::printing::notifier;
2081+using namespace ::testing;
2082+
2083+class EngineFixture: public ::testing::Test
2084+{
2085+};
2086+
2087+class MockEngine: public NotifyEngine
2088+{
2089+public:
2090+ MockEngine(const std::shared_ptr<Client>& client,
2091+ const std::shared_ptr<Actions>& actions):
2092+ NotifyEngine(client, actions)
2093+ {
2094+ }
2095+
2096+ ~MockEngine()
2097+ {
2098+ }
2099+
2100+ MOCK_METHOD1(build_job_notification,
2101+ std::shared_ptr<Notification>(const Job&));
2102+ MOCK_METHOD2(build_printer_notification,
2103+ std::shared_ptr<Notification>(const Printer&,
2104+ const std::string&));
2105+};
2106+
2107+TEST_F(EngineFixture, NotifyEngine)
2108+{
2109+ auto client = std::make_shared<MockClient>();
2110+ auto actions = std::make_shared<MockActions>();
2111+
2112+ // Test for initialization
2113+ auto engine = std::make_shared<NotifyEngine>(client, actions);
2114+ ASSERT_FALSE(nullptr == engine);
2115+
2116+ // Test refresh
2117+ EXPECT_CALL(*client, refresh()).Times(1)
2118+ .WillOnce(Invoke([&client](){
2119+ // Notify a printer state change
2120+ Printer printer;
2121+ printer.name = "a-printer";
2122+ printer.description = "A Printer";
2123+ printer.accepting_jobs = true;
2124+ printer.num_jobs = 1;
2125+ printer.state_reasons = "door-open-report";
2126+ client->m_printer_state_changed(printer);
2127+
2128+ // Now with 2 reasons
2129+ printer.state_reasons = "door-open-report,something-else";
2130+ client->m_printer_state_changed(printer);
2131+
2132+ // Notify a COMPLETED job
2133+ Job fake_job;
2134+ fake_job.id = 42;
2135+ fake_job.state = Job::State::COMPLETED;
2136+ fake_job.name = "Life, The Universe, and Everything";
2137+ fake_job.printer.description = "Deep Thought";
2138+ fake_job.printer.name = "deep-thought";
2139+ client->m_job_state_changed(fake_job);
2140+ }));
2141+ client->refresh();
2142+}
2143
2144=== added file 'notifier/tests/test_utils.cpp'
2145--- notifier/tests/test_utils.cpp 1970-01-01 00:00:00 +0000
2146+++ notifier/tests/test_utils.cpp 2017-03-20 13:16:02 +0000
2147@@ -0,0 +1,33 @@
2148+/*
2149+ * Copyright 2016-2017 Canonical Ltd.
2150+ *
2151+ * This program is free software: you can redistribute it and/or modify it
2152+ * under the terms of the GNU General Public License version 3, as published
2153+ * by the Free Software Foundation.
2154+ *
2155+ * This program is distributed in the hope that it will be useful, but
2156+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2157+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2158+ * PURPOSE. See the GNU General Public License for more details.
2159+ *
2160+ * You should have received a copy of the GNU General Public License along
2161+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2162+ */
2163+
2164+#include "utils.h"
2165+
2166+#include <gtest/gtest.h>
2167+
2168+using namespace ubuntu::printing::notifier;
2169+
2170+
2171+TEST(Utilities, testPrependSnapPathSet) {
2172+ ASSERT_EQ(0, setenv("SNAP", "/snap", 1));
2173+ EXPECT_EQ("/snap/bar", Utilities::prepend_snap_path("/bar"));
2174+ ASSERT_EQ(0, unsetenv("SNAP"));
2175+}
2176+
2177+TEST(Utilities, testPrependSnapPathUnset) {
2178+ ASSERT_EQ(0, unsetenv("SNAP"));
2179+ EXPECT_EQ("/bar", Utilities::prepend_snap_path("/bar"));
2180+}

Subscribers

People subscribed via source and target branches

to all changes: