def alertedToRunningOnBatteryAndCancelled(self): '''Returns True if we are running on battery and user clicks the Cancel button''' power_info = munki.getPowerInfo() if (power_info.get('PowerSource') == 'Battery Power' and power_info.get('BatteryCharge', 0) < 50): alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString( u"Your computer is not connected to a power source.", u"No Power Source Warning text"), NSLocalizedString(u"Continue", u"Continue button text"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, u"%@", NSLocalizedString( (u"For best results, you should connect your computer to a " "power source before updating. Are you sure you want to " "continue the update?"), u"No Power Source Warning detail")) msclog.log("MSU", "alert_on_battery_power") # making UI consistent with Apple Software Update... # set Cancel button to be activated by return key alert.buttons()[1].setKeyEquivalent_('\r') # set Continue button to be activated by Escape key alert.buttons()[0].setKeyEquivalent_(chr(27)) buttonPressed = alert.runModal() if buttonPressed == NSAlertAlternateReturn: return True return False
def kickOffInstallSession(self): '''start an update install/removal session''' # check for need to logout, restart, firmware warnings # warn about blocking applications, etc... # then start an update session if MunkiItems.updatesRequireRestart() or MunkiItems.updatesRequireLogout(): # switch to updates view self.loadUpdatesPage_(self) # warn about need to logout or restart self.alert_controller.confirmUpdatesAndInstall() else: if self.alert_controller.alertedToBlockingAppsRunning(): self.loadUpdatesPage_(self) return if self.alert_controller.alertedToRunningOnBatteryAndCancelled(): self.loadUpdatesPage_(self) return self.managedsoftwareupdate_task = None msclog.log("user", "install_without_logout") self._update_in_progress = True self.displayUpdateCount() self.setStatusViewTitle_(NSLocalizedString(u"Updating...", u"Updating message")) result = munki.justUpdate() if result: msclog.debug_log("Error starting install session: %s" % result) self.munkiStatusSessionEnded_(2) else: self.managedsoftwareupdate_task = "installwithnologout" NSApp.delegate().statusController.startMunkiStatusSession() self.markPendingItemsAsInstalling()
def alertedToBlockingAppsRunning(self): '''Returns True if blocking_apps are running; alerts as a side-effect''' apps_to_check = [] for update_item in MunkiItems.getUpdateList(): if 'blocking_applications' in update_item: apps_to_check.extend(update_item['blocking_applications']) else: apps_to_check.extend( [os.path.basename(item.get('path')) for item in update_item.get('installs', []) if item['type'] == 'application'] ) running_apps = munki.getRunningBlockingApps(apps_to_check) if running_apps: current_user = munki.getconsoleuser() other_users_apps = [item['display_name'] for item in running_apps if item['user'] != current_user] my_apps = [item['display_name'] for item in running_apps if item['user'] == current_user] msclog.log( "MSC", "conflicting_apps", ','.join(other_users_apps + my_apps)) if other_users_apps: detailText = NSLocalizedString( u"Other logged in users are using the following " "applications. Try updating later when they are no longer " "in use:\n\n%s", u"Other Users Blocking Apps Running detail") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString( u"Applications in use by others", u"Other Users Blocking Apps Running title"), NSLocalizedString(u"OK", u'OKButtonText'), nil, nil, u"%@", detailText % u'\n'.join(set(other_users_apps)) ) else: detailText = NSLocalizedString( u"You must quit the following applications before " "proceeding with installation or removal:\n\n%s", u"Blocking Apps Running detail") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString( u"Conflicting applications running", u"Blocking Apps Running title"), NSLocalizedString(u"OK", u"OK button title"), nil, nil, u"%@", detailText % u'\n'.join(set(my_apps)) ) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.blockingAppsRunningAlertDidEnd_returnCode_contextInfo_, nil) return True else: return False
def forceLogoutWarningDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when the forced logout warning alert ends''' btn_pressed = self._force_warning_btns.get(returncode) if btn_pressed == self._force_warning_logout_btn: msclog.log("user", "install_with_logout") result = munki.logoutAndUpdate() elif btn_pressed == self._force_warning_ok_btn: msclog.log("user", "dismissed_forced_logout_warning")
def userNotificationCenter_didActivateNotification_(self, center, notification): '''User clicked on a Notification Center alert''' user_info = notification.userInfo() if user_info.get('action') == 'open_url': url = user_info.get('value', 'munki://updates') msclog.log("MSU", "Got user notification to open %s" % url) self.openMunkiURL(url) center.removeDeliveredNotification_(notification) else: msclog.log("MSU", "Got user notification with unrecognized userInfo")
def forceLogoutWarningDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when the forced logout warning alert ends''' btn_pressed = self._force_warning_btns.get(returncode) if btn_pressed == self._force_warning_logout_btn: msclog.log("user", "install_with_logout") self.handlePossibleAuthRestart() try: munki.logoutAndUpdate() except munki.ProcessStartError, err: self.installSessionErrorAlert_(err)
def updateAlertDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when alert invoked by alertToPendingUpdates ends''' if returncode == NSAlertDefaultReturn: msclog.log("user", "quit") NSApp.terminate_(self) elif returncode == NSAlertOtherReturn: msclog.log("user", "install_now_clicked") # make sure this alert panel is gone before we proceed # which might involve opening another alert sheet alert.window().orderOut_(self) # initiate the updates self.updateNow() self.loadUpdatesPage_(self)
def installSessionErrorAlert(self): '''Something has gone wrong and we can't trigger an install at logout''' msclog.log("user", "install_session_failed") alertMessageText = NSLocalizedString( u"Install session failed", u"Install Session Failed title") detailText = NSLocalizedString( u"There is a configuration problem with the managed software " "installer. Could not start the process. Contact your systems " "administrator.", u"Could Not Start Session message") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window(), self, self.installSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
def applicationDidFinishLaunching_(self, sender): '''NSApplication delegate method called at launch''' # Prevent automatic relaunching at login on Lion+ if NSApp.respondsToSelector_('disableRelaunchOnLogin'): NSApp.disableRelaunchOnLogin() ver = NSBundle.mainBundle().infoDictionary().get('CFBundleShortVersionString') msclog.log("MSC", "launched", "VER=%s" % ver) # if we're running under Snow Leopard, swap out the Dock icon for one # without the Retina assets to avoid an appearance issue when the # icon has a badge in the Dock (and App Switcher) # Darwin major version 10 is Snow Leopard (10.6) if os.uname()[2].split('.')[0] == '10': myImage = NSImage.imageNamed_("Managed Software Center 10_6") NSApp.setApplicationIconImage_(myImage) # setup client logging msclog.setup_logging() # have the statuscontroller register for its own notifications self.statusController.registerForNotifications() # user may have launched the app manually, or it may have # been launched by /usr/local/munki/managedsoftwareupdate # to display available updates if munki.thereAreUpdatesToBeForcedSoon(hours=2): # skip the check and just display the updates # by pretending the lastcheck is now lastcheck = NSDate.date() else: lastcheck = munki.pref('LastCheckDate') max_cache_age = munki.pref('CheckResultsCacheSeconds') # if there is no lastcheck timestamp, check for updates. if not lastcheck: self.mainWindowController.checkForUpdates() elif lastcheck.timeIntervalSinceNow() * -1 > int(max_cache_age): # check for updates if the last check is over the # configured manualcheck cache age max. self.mainWindowController.checkForUpdates() elif MunkiItems.updateCheckNeeded(): # check for updates if we have optional items selected for install # or removal that have not yet been processed self.mainWindowController.checkForUpdates() # load the initial view only if we are not already loading something else. # enables launching the app to a specific panel, eg. from URL handler if not self.mainWindowController.webView.isLoading(): self.mainWindowController.loadInitialView()
def doRestartAlert(self): '''Display a restart alert -- some item just installed or removed requires a restart''' msclog.log("MSC", "restart_required") self._status_restartAlertDismissed = 0 alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Restart Required", u"Restart Required title"), NSLocalizedString(u"Restart", u"Restart button title"), nil, nil, u"%@", NSLocalizedString( u"Software installed or removed requires a restart. You will " "have a chance to save open documents.", u"Restart Required alert detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.statusWindowController.window(), self, self.restartAlertDidEnd_returnCode_contextInfo_, nil)
def alertToExtraUpdates(self): '''Notify user of additional pending updates''' msclog.log("user", "extra_updates_pending") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString( u"Additional Pending Updates", u"Additional Pending Updates title"), NSLocalizedString(u"OK", u"OK button title"), nil, nil, u"%@", NSLocalizedString( u"There are additional pending updates to install or remove.", u"Additional Pending Updates detail") ) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.extraUpdatesAlertDidEnd_returnCode_contextInfo_, nil)
def openURL_withReplyEvent_(self, event, replyEvent): '''Handle openURL messages''' keyDirectObject = struct.unpack(">i", "----")[0] url = event.paramDescriptorForKeyword_(keyDirectObject).stringValue().decode('utf8') msclog.log("MSU", "Called by external URL: %s", url) parsed_url = urlparse(url) if parsed_url.scheme != 'munki': msclog.debug_log("URL %s has unsupported scheme" % url) return filename = mschtml.unquote(parsed_url.netloc) # add .html if no extension if not os.path.splitext(filename)[1]: filename += u'.html' if filename.endswith(u'.html'): mschtml.build_page(filename) self.mainWindowController.load_page(filename) else: msclog.debug_log("%s doesn't have a valid extension. Prevented from opening" % url)
def promptForPasswordForAuthRestart(self): '''Set up and display our alert that prompts for password''' # Set up all the fields and buttons with localized text alert = NSAlert.alloc().init() alert.addButtonWithTitle_( NSLocalizedString(u"Allow", u"Allow button text")) alert.addButtonWithTitle_( NSLocalizedString(u"Deny", u"Deny button text")) alert.setMessageText_(NSLocalizedString( u"Managed Software Center wants to unlock the startup disk after " "restarting to complete all pending updates.", u"Password prompt title")) alert.setInformativeText_(NSLocalizedString( u"To allow this, enter your login password.", u"Password explanation")) alert.setAccessoryView_(self.passwordView) self.passwordLabel.setStringValue_(NSLocalizedString( u"Password:"******"Password label")) self.passwordField.setStringValue_(u"") # resize label to fit the text self.passwordLabel.sizeToFit() # resize the password field to use the rest of the available space viewWidth = self.passwordView.frame().size.width labelWidth = self.passwordLabel.frame().size.width fieldFrame = self.passwordField.frame() fieldFrame.origin.x = labelWidth + 8 fieldFrame.size.width = viewWidth - labelWidth - 8 self.passwordField.setFrame_(fieldFrame) # add esc as a key equivalent for the Deny button alert.buttons().objectAtIndex_(1).setKeyEquivalent_(chr(27)) # change the Allow button to call our password validation method allowButton = alert.buttons().objectAtIndex_(0) allowButton.setTarget_(self) allowButton.setAction_(self.verifyPassword_) # make sure our password field is ready to accept input alert.window().setInitialFirstResponder_(self.passwordField) # we can finally run the alert! result = alert.runModal() if result == NSAlertFirstButtonReturn: # they clicked "Allow". We handled it in the verifyPassword method msclog.log("user", "stored password for auth restart") if result == NSAlertSecondButtonReturn: # they clicked "Deny" msclog.log("user", "denied password for auth restart")
def alertedToMultipleUsers(self): '''Returns True if there are multiple GUI logins; alerts as a side effect''' if len(munki.currentGUIusers()) > 1: msclog.log("MSC", "multiple_gui_users_update_cancelled") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Other users logged in", u"Other Users Logged In title"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, nil, u"%@", NSLocalizedString( (u"There are other users logged into this computer.\n" "Updating now could cause other users to lose their " "work.\n\nPlease try again later after the other users " "have logged out."), u"Other Users Logged In detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.multipleUserAlertDidEnd_returnCode_contextInfo_, nil) return True else: return False
def updateNow(self): '''If user has added to/removed from the list of things to be updated, run a check session. If there are no more changes, proceed to an update installation session if items to be installed/removed are exclusively those selected by the user in this session''' if self.stop_requested: # reset the flag self.stop_requested = False self.resetAndReload() return if MunkiItems.updateCheckNeeded(): # any item status changes that require an update check? msclog.debug_log('updateCheck needed') msclog.log("user", "check_then_install_without_logout") # since we are just checking for changed self-service items # we can suppress the Apple update check suppress_apple_update_check = True self._update_in_progress = True self.displayUpdateCount() result = munki.startUpdateCheck(suppress_apple_update_check) if result: msclog.debug_log("Error starting check-then-install session: %s" % result) self.munkiStatusSessionEnded_(2) else: self.managedsoftwareupdate_task = "checktheninstall" NSApp.delegate().statusController.startMunkiStatusSession() self.markRequestedItemsAsProcessing() elif (not self._alertedUserToOutstandingUpdates and MunkiItems.updatesContainNonUserSelectedItems()): # current list of updates contains some not explicitly chosen by the user msclog.debug_log('updateCheck not needed, items require user approval') self._update_in_progress = False self.displayUpdateCount() self.loadUpdatesPage_(self) self.alert_controller.alertToExtraUpdates() else: msclog.debug_log('updateCheck not needed') self._alertedUserToOutstandingUpdates = False self.kickOffInstallSession()
def logoutAlertDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when logout alert ends''' if returncode == NSAlertDefaultReturn: # make sure this alert panel is gone before we proceed, which # might involve opening another alert sheet alert.window().orderOut_(self) if self.alertedToFirmwareUpdatesAndCancelled(): msclog.log("user", "alerted_to_firmware_updates_and_cancelled") return elif self.alertedToRunningOnBatteryAndCancelled(): msclog.log("user", "alerted_on_battery_power_and_cancelled") return msclog.log("user", "install_with_logout") result = munki.logoutAndUpdate() if result: self.installSessionErrorAlert() elif returncode == NSAlertAlternateReturn: msclog.log("user", "cancelled")
def logoutAlertDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when logout alert ends''' if returncode == NSAlertDefaultReturn: # make sure this alert panel is gone before we proceed, which # might involve opening another alert sheet alert.window().orderOut_(self) if self.alertedToFirmwareUpdatesAndCancelled(): msclog.log("user", "alerted_to_firmware_updates_and_cancelled") return elif self.alertedToRunningOnBatteryAndCancelled(): msclog.log("user", "alerted_on_battery_power_and_cancelled") return msclog.log("user", "install_with_logout") self.handlePossibleAuthRestart() result = munki.logoutAndUpdate() if result: self.installSessionErrorAlert() elif returncode == NSAlertAlternateReturn: msclog.log("user", "cancelled")
def openURL_withReplyEvent_(self, event, replyEvent): '''Handle openURL messages''' keyDirectObject = struct.unpack(">i", "----")[0] url = event.paramDescriptorForKeyword_(keyDirectObject).stringValue().decode('utf8') msclog.log("MSU", "Called by external URL: %s", url) self.openMunkiURL(url)
def munkiStatusSessionEnded_(self, sessionResult): '''Called by StatusController when a Munki session ends''' msclog.debug_log(u"MunkiStatus session ended: %s" % sessionResult) msclog.debug_log(u"MunkiStatus session type: %s" % self.managedsoftwareupdate_task) tasktype = self.managedsoftwareupdate_task self.managedsoftwareupdate_task = None self._update_in_progress = False # The managedsoftwareupdate run will have changed state preferences # in ManagedInstalls.plist. Load the new values. munki.reload_prefs() lastCheckResult = munki.pref("LastCheckResult") if sessionResult != 0 or lastCheckResult < 0: OKButtonTitle = NSLocalizedString(u"OK", u"OK button title") alertMessageText = NSLocalizedString( u"Update check failed", u"Update Check Failed title") if tasktype == "installwithnologout": alertMessageText = NSLocalizedString( u"Install session failed", u"Install Session Failed title") if sessionResult == -1: # connection was dropped unexpectedly msclog.log("MSC", "cant_update", "unexpected process end") detailText = NSLocalizedString( (u"There is a configuration problem with the managed software installer. " "The process ended unexpectedly. Contact your systems administrator."), u"Unexpected Session End message") elif sessionResult == -2: # session never started msclog.log("MSC", "cant_update", "process did not start") detailText = NSLocalizedString( (u"There is a configuration problem with the managed software installer. " "Could not start the process. Contact your systems administrator."), u"Could Not Start Session message") elif lastCheckResult == -1: # server not reachable msclog.log("MSC", "cant_update", "cannot contact server") detailText = NSLocalizedString( (u"Managed Software Center cannot contact the update server at this time.\n" "Try again later. If this situation continues, " "contact your systems administrator."), u"Cannot Contact Server detail") elif lastCheckResult == -2: # preflight failed msclog.log("MSU", "cant_update", "failed preflight") detailText = NSLocalizedString( (u"Managed Software Center cannot check for updates now.\n" "Try again later. If this situation continues, " "contact your systems administrator."), u"Failed Preflight Check detail") # show the alert sheet self.window().makeKeyAndOrderFront_(self) alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window(), self, self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil) return if tasktype == 'checktheninstall': MunkiItems.reset() # possibly check again if choices have changed self.updateNow() return # all done checking and/or installing: display results self.resetAndReload() if MunkiItems.updateCheckNeeded(): # more stuff pending? Let's do it... self.updateNow()
def forcedLogoutWarning(self, notification_obj): '''Display a forced logout warning''' NSApp.activateIgnoringOtherApps_(True) info = notification_obj.userInfo() moreText = NSLocalizedString( (u"All pending updates will be installed. Unsaved work will be lost." "\nYou may avoid the forced logout by logging out now."), u"Forced Logout warning detail") logout_time = None if info: logout_time = info.get('logout_time') elif munki.thereAreUpdatesToBeForcedSoon(): logout_time = munki.earliestForceInstallDate() if not logout_time: return time_til_logout = int(logout_time.timeIntervalSinceNow() / 60) if time_til_logout > 55: deadline_str = munki.stringFromDate(logout_time) msclog.log("user", "forced_logout_warning_initial") formatString = NSLocalizedString( u"A logout will be forced at approximately %s.", u"Logout warning string when logout is an hour or more away") infoText = formatString % deadline_str + u"\n" + moreText elif time_til_logout > 0: msclog.log("user", "forced_logout_warning_%s" % time_til_logout) formatString = NSLocalizedString( u"A logout will be forced in less than %s minutes.", u"Logout warning string when logout is in < 60 minutes") infoText = formatString % time_til_logout + u"\n" + moreText else: msclog.log("user", "forced_logout_warning_final") infoText = NSLocalizedString( (u"A logout will be forced in less than a minute.\nAll pending " "updates will be installed. Unsaved work will be lost."), u"Logout warning string when logout is in less than a minute") # Set the OK button to default, unless less than 5 minutes to logout # in which case only the Logout button should be displayed. self._force_warning_logout_btn = NSLocalizedString( u"Log out and update now", u"Logout and Update Now button text") self._force_warning_ok_btn = NSLocalizedString(u"OK", u"OK button title") if time_til_logout > 5: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_ok_btn, NSAlertAlternateReturn: self._force_warning_logout_btn, } else: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_logout_btn, NSAlertAlternateReturn: nil, } if self.window.attachedSheet(): # there's an existing sheet open NSApp.endSheet_(self.window.attachedSheet()) alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString( u"Forced Logout for Mandatory Install", u"Forced Logout title text"), self._force_warning_btns[NSAlertDefaultReturn], self._force_warning_btns[NSAlertAlternateReturn], nil, u"%@", infoText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.forceLogoutWarningDidEnd_returnCode_contextInfo_, nil)
def forcedLogoutWarning(self, notification_obj): '''Display a forced logout warning''' NSApp.activateIgnoringOtherApps_(True) info = notification_obj.userInfo() moreText = NSLocalizedString( u"All pending updates will be installed. Unsaved work will be lost." "\nYou may avoid the forced logout by logging out now.", u"Forced Logout warning detail") logout_time = None if info: logout_time = info.get('logout_time') elif munki.thereAreUpdatesToBeForcedSoon(): logout_time = munki.earliestForceInstallDate() if not logout_time: return time_til_logout = int(logout_time.timeIntervalSinceNow() / 60) if time_til_logout > 55: deadline_str = munki.stringFromDate(logout_time) msclog.log("user", "forced_logout_warning_initial") formatString = NSLocalizedString( u"A logout will be forced at approximately %s.", u"Logout warning string when logout is an hour or more away") infoText = formatString % deadline_str + u"\n" + moreText elif time_til_logout > 0: msclog.log("user", "forced_logout_warning_%s" % time_til_logout) formatString = NSLocalizedString( u"A logout will be forced in less than %s minutes.", u"Logout warning string when logout is in < 60 minutes") infoText = formatString % time_til_logout + u"\n" + moreText else: msclog.log("user", "forced_logout_warning_final") infoText = NSLocalizedString( u"A logout will be forced in less than a minute.\nAll pending " "updates will be installed. Unsaved work will be lost.", u"Logout warning string when logout is in less than a minute") # Set the OK button to default, unless less than 5 minutes to logout # in which case only the Logout button should be displayed. self._force_warning_logout_btn = NSLocalizedString( u"Log out and update now", u"Logout and Update Now button text") self._force_warning_ok_btn = NSLocalizedString(u"OK", u"OK button title") if time_til_logout > 5: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_ok_btn, NSAlertAlternateReturn: self._force_warning_logout_btn, } else: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_logout_btn, NSAlertAlternateReturn: nil, } if self.window.attachedSheet(): # there's an existing sheet open NSApp.endSheet_(self.window.attachedSheet()) alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Forced Logout for Mandatory Install", u"Forced Logout title text"), self._force_warning_btns[NSAlertDefaultReturn], self._force_warning_btns[NSAlertAlternateReturn], nil, u"%@", infoText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.forceLogoutWarningDidEnd_returnCode_contextInfo_, nil)
def restartAlertDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when restartAlert ends''' msclog.log("MSC", "restart_confirmed") self._status_restartAlertDismissed = 1 munki.restartNow()
class AlertController(NSObject): '''An object that handles some of our alerts, if for no other reason than to move a giant bunch of ugly code out of the WindowController''' def setWindow_(self, the_window): '''Store our parent window''' self.window = the_window def handlePossibleAuthRestart(self): '''Ask for and store a password for auth restart if needed/possible''' username = NSUserName() if (MunkiItems.updatesRequireRestart() and authrestart.verify_user(username) and not authrestart.verify_recovery_key_present()): # FV is on and user is in list of FV users, so they can # authrestart, and we do not have a stored FV recovery # key/password. So we should prompt the user for a password # we can use for fdesetup authrestart NSApp.delegate( ).passwordAlertController.promptForPasswordForAuthRestart() def forcedLogoutWarning(self, notification_obj): '''Display a forced logout warning''' NSApp.activateIgnoringOtherApps_(True) info = notification_obj.userInfo() moreText = NSLocalizedString( u"All pending updates will be installed. Unsaved work will be lost." "\nYou may avoid the forced logout by logging out now.", u"Forced Logout warning detail") logout_time = None if info: logout_time = info.get('logout_time') elif munki.thereAreUpdatesToBeForcedSoon(): logout_time = munki.earliestForceInstallDate() if not logout_time: return time_til_logout = int(logout_time.timeIntervalSinceNow() / 60) if time_til_logout > 55: deadline_str = munki.stringFromDate(logout_time) msclog.log("user", "forced_logout_warning_initial") formatString = NSLocalizedString( u"A logout will be forced at approximately %s.", u"Logout warning string when logout is an hour or more away") infoText = formatString % deadline_str + u"\n" + moreText elif time_til_logout > 0: msclog.log("user", "forced_logout_warning_%s" % time_til_logout) formatString = NSLocalizedString( u"A logout will be forced in less than %s minutes.", u"Logout warning string when logout is in < 60 minutes") infoText = formatString % time_til_logout + u"\n" + moreText else: msclog.log("user", "forced_logout_warning_final") infoText = NSLocalizedString( u"A logout will be forced in less than a minute.\nAll pending " "updates will be installed. Unsaved work will be lost.", u"Logout warning string when logout is in less than a minute") # Set the OK button to default, unless less than 5 minutes to logout # in which case only the Logout button should be displayed. self._force_warning_logout_btn = NSLocalizedString( u"Log out and update now", u"Logout and Update Now button text") self._force_warning_ok_btn = NSLocalizedString(u"OK", u"OK button title") if time_til_logout > 5: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_ok_btn, NSAlertAlternateReturn: self._force_warning_logout_btn, } else: self._force_warning_btns = { NSAlertDefaultReturn: self._force_warning_logout_btn, NSAlertAlternateReturn: nil, } if self.window.attachedSheet(): # there's an existing sheet open NSApp.endSheet_(self.window.attachedSheet()) alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Forced Logout for Mandatory Install", u"Forced Logout title text"), self._force_warning_btns[NSAlertDefaultReturn], self._force_warning_btns[NSAlertAlternateReturn], nil, u"%@", infoText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.forceLogoutWarningDidEnd_returnCode_contextInfo_, nil) @AppHelper.endSheetMethod def forceLogoutWarningDidEnd_returnCode_contextInfo_( self, alert, returncode, contextinfo): '''Called when the forced logout warning alert ends''' btn_pressed = self._force_warning_btns.get(returncode) if btn_pressed == self._force_warning_logout_btn: msclog.log("user", "install_with_logout") self.handlePossibleAuthRestart() try: munki.logoutAndUpdate() except munki.ProcessStartError, err: self.installSessionErrorAlert_(err) elif btn_pressed == self._force_warning_ok_btn: msclog.log("user", "dismissed_forced_logout_warning")
# might involve opening another alert sheet alert.window().orderOut_(self) if self.alertedToFirmwareUpdatesAndCancelled(): msclog.log("user", "alerted_to_firmware_updates_and_cancelled") return elif self.alertedToRunningOnBatteryAndCancelled(): msclog.log("user", "alerted_on_battery_power_and_cancelled") return msclog.log("user", "install_with_logout") self.handlePossibleAuthRestart() try: munki.logoutAndUpdate() except munki.ProcessStartError, err: self.installSessionErrorAlert_(err) elif returncode == NSAlertAlternateReturn: msclog.log("user", "cancelled") def installSessionErrorAlert_(self, errmsg): '''Something has gone wrong and we can't trigger an install at logout''' msclog.log("user", "install_session_failed") alertMessageText = NSLocalizedString(u"Install session failed", u"Install Session Failed title") detailText = NSLocalizedString( u"There is a configuration problem with the managed software " "installer. Could not start the process. Contact your systems " "administrator.", u"Could Not Start Session message") detailText += u"\n\n" + unicode(errmsg) OKButtonTitle = NSLocalizedString(u"OK", u"OK button title") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
def restartAlertDidEnd_returnCode_contextInfo_(self, alert, returncode, contextinfo): '''Called when restartAlert ends''' msclog.log("MSC", "restart_confirmed") self._status_restartAlertDismissed = 1 munki.restartNow()
def applicationDidFinishLaunching_(self, sender): '''NSApplication delegate method called at launch''' NSLog("Finished launching") # setup client logging msclog.setup_logging() # userInfo dict can be nil, seems to be with 10.6 if sender.userInfo(): userNotification = sender.userInfo().get( 'NSApplicationLaunchUserNotificationKey') # we get this notification at launch because it's too early to have declared ourself # a NSUserNotificationCenterDelegate if userNotification: NSLog("Launched via Notification interaction") self.userNotificationCenter_didActivateNotification_( NSUserNotificationCenter.defaultUserNotificationCenter(), userNotification) # Prevent automatic relaunching at login on Lion+ if NSApp.respondsToSelector_('disableRelaunchOnLogin'): NSApp.disableRelaunchOnLogin() ver = NSBundle.mainBundle().infoDictionary().get( 'CFBundleShortVersionString') msclog.log("MSC", "launched", "VER=%s" % ver) # if we're running under Snow Leopard, swap out the Dock icon for one # without the Retina assets to avoid an appearance issue when the # icon has a badge in the Dock (and App Switcher) # Darwin major version 10 is Snow Leopard (10.6) if os.uname()[2].split('.')[0] == '10': myImage = NSImage.imageNamed_("Managed Software Center 10_6") NSApp.setApplicationIconImage_(myImage) # if we are running under Mountain Lion or later set ourselves as a delegate # for NSUserNotificationCenter notifications if os.uname()[2].split('.')[0] > '11': NSUserNotificationCenter.defaultUserNotificationCenter( ).setDelegate_(self) # have the statuscontroller register for its own notifications self.statusController.registerForNotifications() # user may have launched the app manually, or it may have # been launched by /usr/local/munki/managedsoftwareupdate # to display available updates if munki.thereAreUpdatesToBeForcedSoon(hours=2): # skip the check and just display the updates # by pretending the lastcheck is now lastcheck = NSDate.date() else: lastcheck = munki.pref('LastCheckDate') max_cache_age = munki.pref('CheckResultsCacheSeconds') # if there is no lastcheck timestamp, check for updates. if not lastcheck: self.mainWindowController.checkForUpdates() elif lastcheck.timeIntervalSinceNow() * -1 > int(max_cache_age): # check for updates if the last check is over the # configured manualcheck cache age max. self.mainWindowController.checkForUpdates() elif MunkiItems.updateCheckNeeded(): # check for updates if we have optional items selected for install # or removal that have not yet been processed self.mainWindowController.checkForUpdates() # load the initial view only if we are not already loading something else. # enables launching the app to a specific panel, eg. from URL handler if not self.mainWindowController.webView.isLoading(): self.mainWindowController.loadInitialView()
# might involve opening another alert sheet alert.window().orderOut_(self) if self.alertedToFirmwareUpdatesAndCancelled(): msclog.log("user", "alerted_to_firmware_updates_and_cancelled") return elif self.alertedToRunningOnBatteryAndCancelled(): msclog.log("user", "alerted_on_battery_power_and_cancelled") return msclog.log("user", "install_with_logout") self.handlePossibleAuthRestart() try: munki.logoutAndUpdate() except munki.ProcessStartError, err: self.installSessionErrorAlert_(err) elif returncode == NSAlertAlternateReturn: msclog.log("user", "cancelled") def installSessionErrorAlert_(self, errmsg): '''Something has gone wrong and we can't trigger an install at logout''' msclog.log("user", "install_session_failed") alertMessageText = NSLocalizedString( u"Install session failed", u"Install Session Failed title") detailText = NSLocalizedString( u"There is a configuration problem with the managed software " "installer. Could not start the process. Contact your systems " "administrator.", u"Could Not Start Session message") detailText += u"\n\n" + unicode(errmsg) OKButtonTitle = NSLocalizedString(u"OK", u"OK button title") alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(