[gnome-shell] endSessionDialog: Offer offline updates in the shutdown dialog



commit 645ef093f782f9c573d5364b6aad405377cc9450
Author: Kalev Lember <kalevlember gmail com>
Date:   Wed Feb 19 19:58:50 2014 +0100

    endSessionDialog: Offer offline updates in the shutdown dialog
    
    This adds a checkbox for installing software updates to the shut down
    dialog. The implementation relies on an already prepared offline update
    and uses PackageKit's pk-trigger-offline-update helper to trigger the
    installation.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=722898

 data/theme/gnome-shell.css |    7 +--
 js/ui/endSessionDialog.js  |  111 ++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 110 insertions(+), 8 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 0992169..35079d4 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -1947,10 +1947,6 @@ StScrollBar StButton#vhandle:active {
     padding-top: 20px;
 }
 
-.end-session-dialog-subject {
-    padding-bottom: 20px;
-}
-
 .end-session-dialog-layout {
     padding-left: 17px;
 }
@@ -1961,9 +1957,12 @@ StScrollBar StButton#vhandle:active {
 
 .end-session-dialog-description {
     width: 28em;
+    padding-bottom: 10px;
 }
 
 .end-session-dialog-description:rtl {
+    width: 28em;
+    padding-bottom: 10px;
     text-align: right;
 }
 
diff --git a/js/ui/endSessionDialog.js b/js/ui/endSessionDialog.js
index 7606d24..5f06fc1 100644
--- a/js/ui/endSessionDialog.js
+++ b/js/ui/endSessionDialog.js
@@ -25,9 +25,11 @@ const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 const Pango = imports.gi.Pango;
+const Polkit = imports.gi.Polkit;
 const St = imports.gi.St;
 const Shell = imports.gi.Shell;
 
+const CheckBox = imports.ui.checkBox;
 const GnomeSession = imports.misc.gnomeSession;
 const LoginManager = imports.misc.loginManager;
 const ModalDialog = imports.ui.modalDialog;
@@ -36,6 +38,8 @@ const UserWidget = imports.ui.userWidget;
 
 let _endSessionDialog = null;
 
+const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update';
+
 const _ITEM_ICON_SIZE = 48;
 const _DIALOG_ICON_SIZE = 48;
 
@@ -79,11 +83,13 @@ const logoutDialogContent = {
 
 const shutdownDialogContent = {
     subject: C_("title", "Power Off"),
+    subjectWithUpdates: C_("title", "Install Updates & Power Off"),
     description: function(seconds) {
         return ngettext("The system will power off automatically in %d second.",
                         "The system will power off automatically in %d seconds.",
                         seconds).format(seconds);
     },
+    checkBoxText: C_("checkbox", "Install pending software updates"),
     confirmButtons: [{ signal: 'ConfirmedReboot',
                        label:  C_("button", "Restart") },
                      { signal: 'ConfirmedShutdown',
@@ -195,6 +201,18 @@ function _setLabelText(label, text) {
     }
 }
 
+function _setCheckBoxLabel(checkBox, text) {
+    let label = checkBox.getLabelActor();
+
+    if (text) {
+        label.set_text(text);
+        checkBox.actor.show();
+    } else {
+        label.set_text('');
+        checkBox.actor.hide();
+    }
+}
+
 function init() {
     // This always returns the same singleton object
     // By instantiating it initially, we register the
@@ -214,6 +232,7 @@ const EndSessionDialog = new Lang.Class({
         this._userManager = AccountsService.UserManager.get_default();
         this._user = this._userManager.get_user(GLib.get_user_name());
         this._updatesFile = Gio.File.new_for_path('/system-update');
+        this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
 
         this._secondsLeft = 0;
         this._totalSecondsToStayOpen = 0;
@@ -261,6 +280,10 @@ const EndSessionDialog = new Lang.Class({
                           { y_fill:  true,
                             y_align: St.Align.START });
 
+        this._checkBox = new CheckBox.CheckBox();
+        this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
+        messageLayout.add(this._checkBox.actor);
+
         this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
         this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
         this.contentLayout.add(this._scrollView,
@@ -286,6 +309,12 @@ const EndSessionDialog = new Lang.Class({
         this._inhibitorSection.add_actor(this._sessionHeader);
         this._inhibitorSection.add_actor(this._sessionList);
 
+        try {
+            this._updatesPermission = 
Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
+        } catch(e) {
+            log('No permission to trigger offline updates: %s'.format(e.toString()));
+        }
+
         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
     },
@@ -304,6 +333,10 @@ const EndSessionDialog = new Lang.Class({
 
         let subject = dialogContent.subject;
 
+        // Use different title when we are installing updates
+        if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
+            subject = dialogContent.subjectWithUpdates;
+
         let description;
         let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
                                                   this._secondsLeft,
@@ -386,15 +419,75 @@ const EndSessionDialog = new Lang.Class({
     },
 
     _confirm: function(signal) {
-        this._fadeOutDialog();
-        this._stopTimer();
-        this._dbusImpl.emit_signal(signal, null);
+        let callback = Lang.bind(this, function() {
+            this._fadeOutDialog();
+            this._stopTimer();
+            this._dbusImpl.emit_signal(signal, null);
+        });
+
+        // Offline update not available; just emit the signal
+        if (!this._checkBox.actor.visible) {
+            callback();
+            return;
+        }
+
+        // Trigger the offline update as requested
+        if (this._checkBox.actor.checked) {
+            switch (signal) {
+                case "ConfirmedReboot":
+                    this._triggerOfflineUpdateReboot(callback);
+                    break;
+                case "ConfirmedShutdown":
+                    // To actually trigger the offline update, we need to
+                    // reboot to do the upgrade. When the upgrade is complete,
+                    // the computer will shut down automatically.
+                    signal = "ConfirmedReboot";
+                    this._triggerOfflineUpdateShutdown(callback);
+                    break;
+                default:
+                    callback();
+                    break;
+            }
+        } else {
+            this._triggerOfflineUpdateCancel(callback);
+        }
     },
 
     _onOpened: function() {
         this._sync();
     },
 
+    _triggerOfflineUpdateReboot: function(callback) {
+        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback);
+    },
+
+    _triggerOfflineUpdateShutdown: function(callback) {
+        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback);
+    },
+
+    _triggerOfflineUpdateCancel: function(callback) {
+        this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback);
+    },
+
+    _pkexecSpawn: function(argv, callback) {
+        let ret, pid;
+        try {
+            [ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null,
+                                          GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH,
+                                          null);
+        } catch (e) {
+            log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString()));
+            callback();
+            return;
+        }
+
+        GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
+            GLib.spawn_close_pid(pid);
+
+            callback();
+        });
+    },
+
     _startTimer: function() {
         let startTime = GLib.get_monotonic_time();
         this._secondsLeft = this._totalSecondsToStayOpen;
@@ -557,6 +650,8 @@ const EndSessionDialog = new Lang.Class({
             return;
         }
 
+        let dialogContent = DialogContent[this._type];
+
         for (let i = 0; i < inhibitorObjectPaths.length; i++) {
             let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, 
function(proxy, error) {
                 this._onInhibitorLoaded(proxy);
@@ -565,9 +660,17 @@ const EndSessionDialog = new Lang.Class({
             this._applications.push(inhibitor);
         }
 
-        if (DialogContent[type].showOtherSessions)
+        if (dialogContent.showOtherSessions)
             this._loadSessions();
 
+        let preparedUpdate = this._preparedUpdateFile.query_exists(null);
+        let updateAlreadyTriggered = this._updatesFile.query_exists(null);
+        let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
+
+        _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
+        this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed);
+        this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered);
+
         this._updateButtons();
 
         if (!this.open(timestamp)) {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]