diff --git a/code/Managed Software Center/Managed Software Center/MSUAppDelegate.py b/code/Managed Software Center/Managed Software Center/MSUAppDelegate.py
index e5c1524b..4f59c6f1 100644
--- a/code/Managed Software Center/Managed Software Center/MSUAppDelegate.py
+++ b/code/Managed Software Center/Managed Software Center/MSUAppDelegate.py
@@ -45,8 +45,8 @@ class MSUAppDelegate(NSObject):
NSApp.disableRelaunchOnLogin()
ver = NSBundle.mainBundle().infoDictionary().get('CFBundleShortVersionString')
- NSLog("MSU GUI version: %s" % ver)
- munki.log("MSU", "launched", "VER=%s" % ver)
+ NSLog("MSC GUI version: %s" % ver)
+ munki.log("MSC", "launched", "VER=%s" % ver)
# register for notification messages so we can be told if available
# updates change while we are open
@@ -100,6 +100,7 @@ class MSUAppDelegate(NSObject):
def forcedLogoutWarning(self, notification_obj):
NSLog(u"Managed Software Center got forced logout warning")
+ # TO-DO: display the logout warning!
def munkiStatusSessionEnded_(self, socketSessionResult):
NSLog(u"MunkiStatus session ended: %s" % socketSessionResult)
@@ -114,7 +115,6 @@ class MSUAppDelegate(NSObject):
# kick off an update check
# attempt to start the update check
result = munki.startUpdateCheck(suppress_apple_update_check)
- result = 0
if result == 0:
self.managedsoftwareupdate_task = "manualcheck"
self.statusController.startMunkiStatusSession()
diff --git a/code/Managed Software Center/Managed Software Center/MSUMainWindowController.py b/code/Managed Software Center/Managed Software Center/MSUMainWindowController.py
index 9e12803a..60e3af73 100644
--- a/code/Managed Software Center/Managed Software Center/MSUMainWindowController.py
+++ b/code/Managed Software Center/Managed Software Center/MSUMainWindowController.py
@@ -56,6 +56,8 @@ class MSUMainWindowController(NSWindowController):
_status_detail = ''
_status_percent = -1
+ _current_page_filename = None
+
html_dir = None
tabControl = IBOutlet()
@@ -120,7 +122,10 @@ class MSUMainWindowController(NSWindowController):
def loadInitialView(self):
self.html_dir = msulib.setupHtmlDir()
- self.load_page('category-all.html')
+ if self.getEffectiveUpdateList():
+ self.loadUpdatesPage_(self)
+ else:
+ self.loadAllSoftwarePage_(self)
self.displayUpdateCount()
@AppHelper.endSheetMethod
@@ -134,97 +139,64 @@ class MSUMainWindowController(NSWindowController):
# The managedsoftwareupdate run will have changed state preferences
# in ManagedInstalls.plist. Load the new values.
munki.reload_prefs()
-
- alertMessageText = NSLocalizedString(u"Update check failed", u'UpdateCheckFailedTitle')
- if tasktype == "installwithnologout":
- alertMessageText = NSLocalizedString(
- u"Install session failed", u'InstallSessionFailedTitle')
-
- if sessionResult == -1:
- # connection was dropped unexpectedly
- self.window().makeKeyAndOrderFront_(self)
- alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
- alertMessageText,
- NSLocalizedString(u"OK", u'OKButtonTitle'),
- objc.nil,
- objc.nil,
- NSLocalizedString(
- (u"There is a configuration problem with the managed "
- "software installer. The process ended unexpectedly. "
- "Contact your systems administrator."), None))
- alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
- self.window(), self,
- self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
- return
-
- elif sessionResult == -2:
- # socket timed out before connection
- self.window().makeKeyAndOrderFront_(self)
- alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
- alertMessageText,
- NSLocalizedString(u"OK", u'OKButtonTitle'),
- objc.nil,
- objc.nil,
- NSLocalizedString(
- (u"There is a configuration problem with the managed "
- "software installer. Could not start the process. "
- "Contact your systems administrator."), None))
- alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
- self.window(), self,
- self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
- return
-
lastCheckResult = munki.pref("LastCheckResult")
+ if sessionResult != 0 or lastCheckResult < 0:
+ OKButtonTitle = NSLocalizedString(u"OK", u'OKButtonTitle')
+ alertMessageText = NSLocalizedString(u"Update check failed", u'UpdateCheckFailedTitle')
+ if tasktype == "installwithnologout":
+ alertMessageText = NSLocalizedString(
+ u"Install session failed", u'InstallSessionFailedTitle')
+
+ if sessionResult == -1:
+ # connection was dropped unexpectedly
+ munki.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'UnexpectedSessionEndMessage')
+ elif sessionResult == -2:
+ # session never started
+ munki.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'CouldNotStartSessionMessage')
+ elif lastCheckResult == -1:
+ # server not reachable
+ munki.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'CannotContactServerDetail')
+ elif lastCheckResult == -2:
+ # preflight failed
+ munki.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'FailedPreflightCheckDetail')
+ # show the alert sheet
+ self.window().makeKeyAndOrderFront_(self)
+ alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
+ alertMessageText, OKButtonTitle, nil, nil, detailText)
+ alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
+ self.window(), self,
+ self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
+ return
+
if lastCheckResult == 1:
# there are some updates to install; are we expected to install them now?
if tasktype == 'checktheninstall':
# done checking; now try to install...
self.kickOffUpdateSession()
- else:
- self.resetAndReload()
- if self._update_queue:
- # more stuff pending? Let's do it
- self._update_queue.clear()
- self.updateNow()
+ return
- elif lastCheckResult == -1:
- munki.log("MSU", "cant_update", "cannot contact server")
- alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
- NSLocalizedString(u"Cannot contact update server", u'CannotContactServerTitle'),
- NSLocalizedString(u"OK", u'OKButtonTitle'),
- nil,
- nil,
- 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'CannotContactServerDetail'))
- alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
- self.window(), self,
- self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
-
- elif lastCheckResult == -2:
- munki.log("MSU", "cant_update", "failed preflight")
- alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
- NSLocalizedString(u"Cannot process updates", u'FailedPreflightCheckTitle'),
- NSLocalizedString(u"OK", u'OKButtonTitle'),
- nil,
- nil,
- NSLocalizedString(
- (u"Managed Software Center cannot check for updates now.\n"
- "Try again later. If this situation continues, "
- "contact your systems administrator."), u'FailedPreflightCheckDetail'))
- alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
- self.window(), self,
- self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
-
- else:
- # all done checking and/or installing: display results
- self.resetAndReload()
- if self._update_queue:
- # more stuff pending? Let's do it
- self._update_queue.clear()
- self.updateNow()
+ # all done checking and/or installing: display results
+ self.resetAndReload()
+ if self._update_queue:
+ # more stuff pending? Let's do it
+ self._update_queue.clear()
+ self.updateNow()
def resetAndReload(self):
NSLog('resetAndReload method called')
@@ -235,8 +207,30 @@ class MSUMainWindowController(NSWindowController):
self._optionalItemList = None
self._self_service_info = None
self._alertedUserToOutstandingUpdates = False
- # then reload the current view
- self.webView.reload_(self)
+ # what page are we currently viewing?
+ filename = self._current_page_filename
+ name = os.path.splitext(filename)[0]
+ key, p, quoted_value = name.partition('-')
+ value = unquote_plus(quoted_value)
+ if key == 'detail':
+ # optional item detail page
+ self.webView.reload_(self)
+ if key in ['category', 'filter', 'developer']:
+ # optional item list page
+ self.updateListPage()
+ if key == 'categories':
+ # categories page
+ self.updateCategoriesPage()
+ if key == 'myitems':
+ # my items page
+ self.updateMyItemsPage()
+ if key == 'updates':
+ # updates page
+ self.webView.reload_(self)
+ self._alertedUserToOutstandingUpdates = True
+ if key == 'updatedetail':
+ # update detail page
+ self.webView.reload_(self)
# update count might have changed
self.displayUpdateCount()
@@ -275,9 +269,9 @@ class MSUMainWindowController(NSWindowController):
NSLog("Error starting install session: %s" % result)
NSApp.delegate().munkiStatusSessionEnded_(2)
else:
- self.markPendingItemsAsInstalling()
NSApp.delegate().managedsoftwareupdate_task = "installwithnologout"
NSApp.delegate().statusController.startMunkiStatusSession()
+ self.markPendingItemsAsInstalling()
def markPendingItemsAsInstalling(self):
install_info = munki.getInstallInfo()
@@ -441,96 +435,6 @@ class MSUMainWindowController(NSWindowController):
return item
return None
- def processItems_asRemovals_areOptional_(
- self, items, are_removals, are_optional):
- for item in items:
- # convert all unicode values to utf-8 strings
- for key, value in item.items():
- if isinstance(value, unicode):
- item[key] = value.encode('utf-8')
- if not 'developer' in item:
- item['developer'] = msulib.guessDeveloper(item)
- item['developer_sort'] = 1
- if not are_removals and item['developer'] == 'Apple':
- item['developer_sort'] = 0
- item['icon'] = msulib.getIcon(item, self.html_dir)
- if not item.get('detail_link'):
- item['detail_link'] = ('updatedetail-%s.html'
- % quote_plus(item['name']))
- if are_removals:
- item['will_be_removed'] = True
- removal_text = NSLocalizedString(
- u'Will be removed',
- u'WillBeRemovedDisplayText').encode('utf-8')
- item['version_label'] = ('%s'
- % removal_text)
- item['version_to_install'] = ''
- else:
- item['version_label'] = NSLocalizedString(
- u'Version',
- u'VersionLabel').encode('utf-8')
- if 'description' in item:
- item['description'] = msulib.filtered_html(item['description'])
- else:
- item['description'] = ''
- item['due_date_sort'] = NSDate.distantFuture()
- if not are_removals:
- force_install_after_date = item.get('force_install_after_date')
- if force_install_after_date:
- item['category'] = NSLocalizedString(
- u'Critical Update', u'CriticalUpdateType')
- item['due_date_sort'] = force_install_after_date
- # insert installation deadline into description
- local_date = munki.discardTimeZoneFromDate(
- force_install_after_date)
- date_str = munki.stringFromDate(local_date).encode('utf-8')
- forced_date_text = NSLocalizedString(
- u'This item must be installed by %s',
- u'ForcedDateWarning').encode('utf-8')
- description = item['description']
- # prepend deadline info to description.
- item['description'] = (
- '' + forced_date_text % date_str
- + '
' + description)
- if not 'category' in item and not are_optional:
- item['category'] = NSLocalizedString(u'Managed Update',
- u'ManagedUpdateType').encode('utf-8')
- if are_optional:
- item['hide_cancel_button'] = ''
- item['cancel_or_add'] = 'cancel'
- if item['status'] not in [
- 'will-be-removed', 'will-be-installed', 'update-will-be-installed']:
- item['cancel_or_add'] = 'add'
- else:
- item['hide_cancel_button'] = 'hidden'
- item['cancel_or_add'] = ''
-
- # sort items that need restart highest, then logout, then other
- if item.get('RestartAction') in [None, 'None']:
- item['restart_action_text'] = ''
- item['restart_sort'] = 2
- elif item['RestartAction'] in ['RequireRestart', 'RecommendRestart']:
- item['restart_sort'] = 0
- item['restart_action_text'] = NSLocalizedString(
- u'Restart Required', u'RequireRestartMessage').encode('utf-8')
- item['restart_action_text'] += '
'
- elif item['RestartAction'] in ['RequireLogout', 'RecommendLogout']:
- item['restart_sort'] = 1
- item['restart_action_text'] = NSLocalizedString(
- u'Logout Required', u'RequireLogoutMessage').encode('utf-8')
- item['restart_action_text'] += ''
-
- # sort bigger installs to the top
- if item.get('installed_size'):
- item['size_sort'] = -int(item['installed_size'])
- item['size'] = munki.humanReadable(item['installed_size'])
- elif item.get('installer_item_size'):
- item['size_sort'] = -int(item['installer_item_size'])
- item['size'] = munki.humanReadable(item['installer_item_size'])
- else:
- item['size_sort'] = 0
- item['size'] = ''
-
def getUpdatesList(self):
if self._updateList is not None:
return self._updateList
@@ -545,11 +449,11 @@ class MSUMainWindowController(NSWindowController):
install_info = munki.getInstallInfo()
update_items.extend(install_info.get('managed_installs', []))
- self.processItems_asRemovals_areOptional_(update_items, False, False)
+ msulib.processItems(update_items, self.html_dir)
removal_items = install_info.get('removals', [])
# TO-DO: handle the case where removal detail is suppressed
- self.processItems_asRemovals_areOptional_(removal_items, True, False)
+ msulib.processItems(removal_items, self.html_dir, are_removals=True)
update_items.extend(removal_items)
self._updateList = sorted(update_items, key=itemgetter(
@@ -617,14 +521,14 @@ class MSUMainWindowController(NSWindowController):
optional_items = self.getOptionalInstallItems()
items = [dict(item) for item in optional_items
if item['status'] in ['will-be-installed', 'update-will-be-installed']]
- self.processItems_asRemovals_areOptional_(items, False, True)
+ msulib.processItems(items, self.html_dir, are_optional=True)
return items
def getOptionalWillBeRemovedItems(self):
optional_items = self.getOptionalInstallItems()
items = [dict(item) for item in optional_items
if item['status'] == 'will-be-removed']
- self.processItems_asRemovals_areOptional_(items, True, True)
+ msulib.processItems(items, self.html_dir,are_removals=True, are_optional=True)
return items
def getEffectiveUpdateList(self):
@@ -643,13 +547,6 @@ class MSUMainWindowController(NSWindowController):
optional_removals = self.getOptionalWillBeRemovedItems()
return filtered_updates + optional_installs + optional_removals
- def getFooter(self, vars=None):
- '''Return html footer'''
- if not vars:
- vars = {}
- footer_template = msulib.get_template('footer_template.html')
- return footer_template.safe_substitute(vars)
-
def get_warning_text(self):
'''Return localized text warning about forced installs and/or
logouts and/or restarts'''
@@ -670,29 +567,26 @@ class MSUMainWindowController(NSWindowController):
warning_text = restart_text
return warning_text
- def build_myitems_page(self):
- page_name = 'myitems.html'
- page_template = msulib.get_template('myitems_template.html')
- item_template = msulib.get_template('myitems_row_template.html')
+ def getMyItemsList(self):
+ '''Returns a list of optional_installs items the user has chosen
+ to install or to remove'''
subscribed_item_names = self.subscribedItemNames()
-
item_list = [item for item in self.getOptionalInstallItems()
if item['name'] in subscribed_item_names]
-
unsubscribed_item_names = self.unsubscribedItemNames()
items_to_remove = [item for item in self.getOptionalInstallItems()
if item['name'] in unsubscribed_item_names
and item.get('installed')]
-
item_list.extend(items_to_remove)
-
- page = {}
- page['my_items_header_label'] = NSLocalizedString(
- u'My Items', u'MyItemsHeaderLabel').encode('utf-8')
+ return item_list
+
+ def build_myitems_rows(self):
+ item_list = self.getMyItemsList()
if item_list:
- page['myitems_rows'] = ''
+ item_template = msulib.get_template('myitems_row_template.html')
+ myitems_rows = ''
for item in sorted(item_list, key=itemgetter('display_name_lower')):
- page['myitems_rows'] += item_template.safe_substitute(item)
+ myitems_rows += item_template.safe_substitute(item)
else:
status_results_template = msulib.get_template('status_results_template.html')
alert = {}
@@ -703,9 +597,27 @@ class MSUMainWindowController(NSWindowController):
u'Select software to install.',
u'NoInstalledSoftwareSecondaryText').encode('utf-8')
alert['hide_progress_bar'] = 'hidden'
- page['myitems_rows'] = status_results_template.safe_substitute(alert)
+ myitems_rows = status_results_template.safe_substitute(alert)
+ return myitems_rows
+
+ def updateMyItemsPage(self):
+ '''Modifies the DOM to avoid ugly browser refresh'''
+ myitems_rows = self.build_myitems_rows()
+ document = self.webView.mainFrameDocument()
+ table_body_element = document.getElementById_('my_items_rows')
+ table_body_element.setInnerHTML_(myitems_rows)
- page['footer'] = self.getFooter()
+ def build_myitems_page(self):
+ page_name = 'myitems.html'
+ page_template = msulib.get_template('myitems_template.html')
+
+ item_list = self.getMyItemsList()
+
+ page = {}
+ page['my_items_header_label'] = NSLocalizedString(
+ u'My Items', u'MyItemsHeaderLabel').encode('utf-8')
+ page['myitems_rows'] = self.build_myitems_rows()
+ page['footer'] = msulib.getFooter()
html = page_template.safe_substitute(page)
@@ -755,7 +667,7 @@ class MSUMainWindowController(NSWindowController):
page['install_btn_label'] = NSLocalizedString(
u'Cancel', u'CancelButtonText').encode('utf-8')
page['warning_text'] = ''
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
page_template = msulib.get_template('updates_template.html')
html = page_template.safe_substitute(page)
@@ -765,6 +677,41 @@ class MSUMainWindowController(NSWindowController):
f.close()
return html_file
+ def build_update_items_html(self):
+ '''probably don't need this, because we'll never call it'''
+ html = {}
+ html['update_rows'] = ''
+ item_list = self.getEffectiveUpdateList()
+ other_updates = [item for item in self.getOptionalInstallItems()
+ if item.get('needs_update')
+ and item['status'] not in
+ ['installed', 'update-will-be-installed', 'will-be-removed']]
+ more_link_text = NSLocalizedString(u'More', u'MoreLinkText').encode('utf-8')
+ item_template = msulib.get_template('update_row_template.html')
+ if item_list:
+ for item in item_list:
+ html['update_rows'] += item_template.safe_substitute(
+ item, more_link_text=more_link_text)
+ elif not other_updates:
+ status_results_template = msulib.get_template('status_results_template.html')
+ alert = {}
+ alert['primary_status_text'] = NSLocalizedString(
+ u'Your software is up to date.', u'NoPendingUpdatesPrimaryText').encode('utf-8')
+ alert['secondary_status_text'] = NSLocalizedString(
+ u'There is no new software for your computer at this time.',
+ u'NoPendingUpdatesSecondaryText').encode('utf-8')
+ alert['hide_progress_bar'] = 'hidden'
+ alert['progress_bar_value'] = ''
+ html['update_rows'] = status_results_template.safe_substitute(alert)
+
+ html['other_update_rows'] = ''
+ if other_updates:
+ msulib.processItems(other_updates, self.html_dir, are_optional=True)
+ for item in other_updates:
+ html['other_update_rows'] += item_template.safe_substitute(
+ item, more_link_text=more_link_text)
+ return html
+
def build_updates_page(self):
'''available/pending updates'''
page_name = 'updates.html'
@@ -818,13 +765,12 @@ class MSUMainWindowController(NSWindowController):
if other_updates:
page['hide_other_updates'] = ''
- self.processItems_asRemovals_areOptional_(
- other_updates, False, True)
+ msulib.processItems(other_updates, self.html_dir, are_optional=True)
for item in other_updates:
page['other_update_rows'] += item_template.safe_substitute(
item, more_link_text=more_link_text)
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
page_template = msulib.get_template('updates_template.html')
html = page_template.safe_substitute(page)
@@ -840,7 +786,7 @@ class MSUMainWindowController(NSWindowController):
for item in items:
if item['name'] == item_name:
page = dict(item)
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
msulib.addSidebarLabels(page)
force_install_after_date = item.get('force_install_after_date')
if force_install_after_date:
@@ -911,7 +857,7 @@ class MSUMainWindowController(NSWindowController):
more_item['second_line'] = more_item.get('developer', '')
more_in_category_html += more_template.safe_substitute(more_item)
page['more_in_category'] = more_in_category_html
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
template = msulib.get_template('detail_template.html')
html = template.safe_substitute(page)
@@ -923,17 +869,22 @@ class MSUMainWindowController(NSWindowController):
NSLog('No detail found for %s' % item_name)
return None # TO-DO: need an error html file!
- def build_categories_page(self):
- all_items = self.getOptionalInstallItems()
- header = 'Categories'
- page_name = 'categories.html'
- category_list = []
- for item in all_items:
- if 'category' in item and item['category'] not in category_list:
- category_list.append(item['category'])
- item_template = msulib.get_template('category_item_template.html')
+ def updateCategoriesPage(self):
+ '''Modifies DOM on currently displayed page to avoid nasty page refresh'''
+ items_html = self.build_category_items_html()
+ document = self.webView.mainFrameDocument()
+ items_div_element = document.getElementById_('optional_installs_items')
+ items_div_element.setInnerHTML_(items_html)
+ def build_category_items_html(self):
+ all_items = self.getOptionalInstallItems()
if all_items:
+ category_list = []
+ for item in all_items:
+ if 'category' in item and item['category'] not in category_list:
+ category_list.append(item['category'])
+
+ item_template = msulib.get_template('category_item_template.html')
item_html = ''
for category in sorted(category_list):
category_data = {}
@@ -977,6 +928,18 @@ class MSUMainWindowController(NSWindowController):
alert['hide_progress_bar'] = 'hidden'
alert['progress_bar_value'] = ''
item_html = status_results_template.safe_substitute(alert)
+ return item_html
+
+ def build_categories_page(self):
+ all_items = self.getOptionalInstallItems()
+ header = 'Categories'
+ page_name = 'categories.html'
+ category_list = []
+ for item in all_items:
+ if 'category' in item and item['category'] not in category_list:
+ category_list.append(item['category'])
+
+ item_html = self.build_category_items_html()
categories_html = '\n'
for item in sorted(category_list):
@@ -986,7 +949,7 @@ class MSUMainWindowController(NSWindowController):
page['list_items'] = item_html
page['category_items'] = categories_html
page['header_text'] = header
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
page['hide_showcase'] = 'hidden'
html_template = msulib.get_template('list_template.html')
html = html_template.safe_substitute(page)
@@ -997,28 +960,39 @@ class MSUMainWindowController(NSWindowController):
f.close()
return html_file
- def build_list_page(self, category=None, developer=None, filter=None):
+ def updateListPage(self):
+ '''Modifies DOM on currently displayed page to avoid nasty page refresh'''
+ filename = self._current_page_filename
+ if not filename:
+ NSLog('updateListPage unexpected error: no _current_page_filename')
+ return
+ name = os.path.splitext(filename)[0]
+ key, p, quoted_value = name.partition('-')
+ category = None
+ filter = None
+ developer = None
+ value = unquote_plus(quoted_value)
+ if key == 'category':
+ if value != 'all':
+ category = value
+ elif key == 'filter':
+ filter = value
+ elif key == 'developer':
+ developer = value
+ else:
+ NSLog('updateListPage unexpected error: _current_page_filename is %s' %
+ filename)
+ return
+ NSLog('updating with category: %s, developer; %s, filter: %s' %
+ (category, developer, filter))
+ items_html = self.build_list_page_items_html(
+ category=category, developer=developer, filter=filter)
+ document = self.webView.mainFrameDocument()
+ items_div_element = document.getElementById_('optional_installs_items')
+ items_div_element.setInnerHTML_(items_html)
+
+ def build_list_page_items_html(self, category=None, developer=None, filter=None):
items = self.getOptionalInstallItems()
-
- header = 'All items'
- page_name = 'category-all.html'
- if category == 'all':
- category = None
- if category:
- header = category
- page_name = 'category-%s.html' % quote_plus(category)
- if developer:
- header = developer
- page_name = 'developer-%s.html' % quote_plus(developer)
- if filter:
- header = 'Search results for %s' % filter
- page_name = 'filter-%s.html' % quote_plus(filter)
-
- category_list = []
- for item in items:
- if 'category' in item and item['category'] not in category_list:
- category_list.append(item['category'])
-
item_html = ''
if filter:
filterStr = filter.encode('utf-8')
@@ -1077,6 +1051,32 @@ class MSUMainWindowController(NSWindowController):
alert['hide_progress_bar'] = 'hidden'
alert['progress_bar_value'] = ''
item_html = status_results_template.safe_substitute(alert)
+ return item_html
+
+ def build_list_page(self, category=None, developer=None, filter=None):
+ items = self.getOptionalInstallItems()
+
+ header = 'All items'
+ page_name = 'category-all.html'
+ if category == 'all':
+ category = None
+ if category:
+ header = category
+ page_name = 'category-%s.html' % quote_plus(category)
+ if developer:
+ header = developer
+ page_name = 'developer-%s.html' % quote_plus(developer)
+ if filter:
+ header = 'Search results for %s' % filter
+ page_name = 'filter-%s.html' % quote_plus(filter)
+
+ category_list = []
+ for item in items:
+ if 'category' in item and item['category'] not in category_list:
+ category_list.append(item['category'])
+
+ item_html = self.build_list_page_items_html(
+ category=category, developer=developer, filter=filter)
if category:
categories_html = '\n'
@@ -1093,7 +1093,7 @@ class MSUMainWindowController(NSWindowController):
page['list_items'] = item_html
page['category_items'] = categories_html
page['header_text'] = header
- page['footer'] = self.getFooter()
+ page['footer'] = msulib.getFooter()
if category or filter or developer:
page['hide_showcase'] = 'hidden'
else:
@@ -1114,7 +1114,8 @@ class MSUMainWindowController(NSWindowController):
self.webView.mainFrame().loadRequest_(request)
def build_page(self, filename):
- NSLog('build_page %s' % filename)
+ #NSLog('build_page %s' % filename)
+ self._current_page_filename = filename
name = os.path.splitext(filename)[0]
key, p, quoted_value = name.partition('-')
value = unquote_plus(quoted_value)
@@ -1140,11 +1141,13 @@ class MSUMainWindowController(NSWindowController):
# we generate each page dynamically; we want things
# that are changed in one page view to be reflected
# immediately in all page views
- identifier = 'com.googlecode.munki.ManagedSoftwareUpdate'
+ identifier = 'com.googlecode.munki.ManagedSoftwareCenter'
prefs = WebPreferences.alloc().initWithIdentifier_(identifier)
prefs.setUsesPageCache_(False)
self.webView.setPreferencesIdentifier_(identifier)
+##### WebView delegate methods #####
+
def webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_(
self, webView, actionInformation, request, frameName, listener):
# open link in default browser
@@ -1214,6 +1217,8 @@ class MSUMainWindowController(NSWindowController):
return NO # this selector is NOT _excluded_ from scripting, so it can be called.
return YES # disallow everything else
+#### handling DOM UI elements ####
+
def installButtonClicked(self):
# this method is called from JavaScript when the user
# clicks the Install All button in the Updates view
@@ -1340,10 +1345,10 @@ class MSUMainWindowController(NSWindowController):
# make some new HTML for the updated item
managed_update_names = munki.getInstallInfo().get('managed_updates', [])
if item['name'] in managed_update_names:
- self.processItems_asRemovals_areOptional_([item], False, False)
+ msulib.processItems([item], self.html_dir)
else:
# possible to change the status
- self.processItems_asRemovals_areOptional_([item], False, True)
+ msulib.processItems([item], self.html_dir, are_optional=True)
item_template = msulib.get_template('update_row_template.html')
more_link_text = NSLocalizedString(u'More', u'MoreLinkText').encode('utf-8')
@@ -1420,7 +1425,8 @@ class MSUMainWindowController(NSWindowController):
status_line.setInnerText_(item['status_text'])
status_line.setClassName_('status %s' % item['status'])
if not self._update_in_progress:
- if item['status'] in ['will-be-installed', 'update-will-be-installed', 'will-be-removed']:
+ if item['status'] in ['will-be-installed', 'update-will-be-installed',
+ 'will-be-removed']:
self.updateNow()
def updateDOMforOptionalItem(self, item):
@@ -1518,6 +1524,16 @@ class MSUMainWindowController(NSWindowController):
'''return True if current tab selected is updates'''
selectedCell = self.tabControl.selectedCell()
return (selectedCell is not None and selectedCell.tag() == 4)
+
+ def currentPageIsMyItemsPage(self):
+ '''return True if current tab selected is updates'''
+ selectedCell = self.tabControl.selectedCell()
+ return (selectedCell is not None and selectedCell.tag() == 3)
+
+ def currentPageIsCategoriesPage(self):
+ '''return True if current tab selected is updates'''
+ selectedCell = self.tabControl.selectedCell()
+ return (selectedCell is not None and selectedCell.tag() == 2)
##### required status methods #####
@@ -1561,6 +1577,7 @@ class MSUMainWindowController(NSWindowController):
def restartAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
self._status_restartAlertDismissed = 1
+ # TO-DO: initiate actual restart
def doRestartAlert(self):
self._status_restartAlertDismissed = 0
@@ -1603,56 +1620,60 @@ class MSUMainWindowController(NSWindowController):
return self._status_stopBtnState
def hideStopButton(self):
- if not self._status_stopBtnState:
- self._status_stopBtnHidden = True
- document = self.webView.mainFrameDocument()
- spinner = document.getElementById_('updates-progress-spinner')
- if spinner: # we are displaying the updates status page
- install_btn = document.getElementById_('install-all-button-text')
- if install_btn:
- btn_classes = install_btn.className().split(' ')
- if not 'hidden' in btn_classes:
- btn_classes.append('hidden')
- install_btn.setClassName_(' '.join(btn_classes))
+ if self._status_stopBtnState:
+ return
+ self._status_stopBtnHidden = True
+ document = self.webView.mainFrameDocument()
+ spinner = document.getElementById_('updates-progress-spinner')
+ if spinner: # we are displaying the updates status page
+ install_btn = document.getElementById_('install-all-button-text')
+ if install_btn:
+ btn_classes = install_btn.className().split(' ')
+ if not 'hidden' in btn_classes:
+ btn_classes.append('hidden')
+ install_btn.setClassName_(' '.join(btn_classes))
def showStopButton(self):
- if not self._status_stopBtnState:
- self._status_stopBtnHidden = False
- document = self.webView.mainFrameDocument()
- spinner = document.getElementById_('updates-progress-spinner')
- if spinner: # we are displaying the updates status page
- install_btn = document.getElementById_('install-all-button-text')
- if install_btn:
- btn_classes = install_btn.className().split(' ')
- if 'hidden' in btn_classes:
- btn_classes.remove('hidden')
- install_btn.setClassName_(' '.join(btn_classes))
+ if self._status_stopBtnState:
+ return
+ self._status_stopBtnHidden = False
+ document = self.webView.mainFrameDocument()
+ spinner = document.getElementById_('updates-progress-spinner')
+ if spinner: # we are displaying the updates status page
+ install_btn = document.getElementById_('install-all-button-text')
+ if install_btn:
+ btn_classes = install_btn.className().split(' ')
+ if 'hidden' in btn_classes:
+ btn_classes.remove('hidden')
+ install_btn.setClassName_(' '.join(btn_classes))
def enableStopButton(self):
- if not self._status_stopBtnState:
- self._status_stopBtnDisabled = False
- document = self.webView.mainFrameDocument()
- spinner = document.getElementById_('updates-progress-spinner')
- if spinner: # we are displaying the updates status page
- install_btn = document.getElementById_('install-all-button-text')
- if install_btn:
- btn_classes = install_btn.className().split(' ')
- if 'installed-not-removable' in btn_classes:
- btn_classes.remove('installed-not-removable')
- install_btn.setClassName_(' '.join(btn_classes))
+ if self._status_stopBtnState:
+ return
+ self._status_stopBtnDisabled = False
+ document = self.webView.mainFrameDocument()
+ spinner = document.getElementById_('updates-progress-spinner')
+ if spinner: # we are displaying the updates status page
+ install_btn = document.getElementById_('install-all-button-text')
+ if install_btn:
+ btn_classes = install_btn.className().split(' ')
+ if 'installed-not-removable' in btn_classes:
+ btn_classes.remove('installed-not-removable')
+ install_btn.setClassName_(' '.join(btn_classes))
def disableStopButton(self):
- if not self._status_stopBtnState:
- self._status_stopBtnDisabled = True
- document = self.webView.mainFrameDocument()
- spinner = document.getElementById_('updates-progress-spinner')
- if spinner: # we are displaying the updates status page
- install_btn = document.getElementById_('install-all-button-text')
- if install_btn:
- btn_classes = install_btn.className().split(' ')
- if not 'installed-not-removable' in btn_classes:
- btn_classes.append('installed-not-removable')
- install_btn.setClassName_(' '.join(btn_classes))
+ if self._status_stopBtnState:
+ return
+ self._status_stopBtnDisabled = True
+ document = self.webView.mainFrameDocument()
+ spinner = document.getElementById_('updates-progress-spinner')
+ if spinner: # we are displaying the updates status page
+ install_btn = document.getElementById_('install-all-button-text')
+ if install_btn:
+ btn_classes = install_btn.className().split(' ')
+ if not 'installed-not-removable' in btn_classes:
+ btn_classes.append('installed-not-removable')
+ install_btn.setClassName_(' '.join(btn_classes))
def getRestartAlertDismissed(self):
return self._status_restartAlertDismissed
diff --git a/code/Managed Software Center/Managed Software Center/MSUStatusController.py b/code/Managed Software Center/Managed Software Center/MSUStatusController.py
index 48c847a0..fad8eafa 100644
--- a/code/Managed Software Center/Managed Software Center/MSUStatusController.py
+++ b/code/Managed Software Center/Managed Software Center/MSUStatusController.py
@@ -49,7 +49,6 @@ class MSUStatusController(NSObject):
NSNotificationSuspensionBehaviorDeliverImmediately)
self.receiving_notifications = True
-
def unregisterForNotifications(self):
'''Tell the DistributedNotificationCenter to stop sending us notifications'''
NSDistributedNotificationCenter.defaultCenter().removeObserver_(self)
@@ -104,11 +103,11 @@ class MSUStatusController(NSObject):
sessionResult = UNEXPECTEDLY_QUIT
else:
sessionResult = NEVER_STARTED
+ self.performSelectorOnMainThread_withObject_waitUntilDone_(
+ self.sessionEnded_, sessionResult, NO)
break
time.sleep(5)
- if self.session_started:
- self.performSelectorOnMainThread_withObject_waitUntilDone_(
- self.sessionEnded_, sessionResult, NO)
+
# Clean up autorelease pool
del pool
diff --git a/code/Managed Software Center/Managed Software Center/msulib.py b/code/Managed Software Center/Managed Software Center/msulib.py
index 7acab699..26002d2d 100644
--- a/code/Managed Software Center/Managed Software Center/msulib.py
+++ b/code/Managed Software Center/Managed Software Center/msulib.py
@@ -23,6 +23,7 @@ import sys
import shutil
from string import Template
+from urllib import quote_plus
from HTMLParser import HTMLParser
@@ -30,6 +31,7 @@ from Foundation import *
from AppKit import *
import FoundationPlist
+import munki
class MSUHTMLFilter(HTMLParser):
'''Filters HTML and HTML fragments for use inside description paragraphs'''
@@ -422,3 +424,100 @@ def get_template(template_name):
except (IOError, OSError):
return None
+
+def getFooter(vars=None):
+ '''Return html footer'''
+ if not vars:
+ vars = {}
+ footer_template = get_template('footer_template.html')
+ return footer_template.safe_substitute(vars)
+
+
+def processItems(items, html_dir, are_removals=False, are_optional=False):
+ for item in items:
+ # convert all unicode values to utf-8 strings
+ for key, value in item.items():
+ if isinstance(value, unicode):
+ item[key] = value.encode('utf-8')
+ if not 'developer' in item:
+ item['developer'] = guessDeveloper(item)
+ item['developer_sort'] = 1
+ if not are_removals and item['developer'] == 'Apple':
+ item['developer_sort'] = 0
+ item['icon'] = getIcon(item, html_dir)
+ if not item.get('detail_link'):
+ item['detail_link'] = ('updatedetail-%s.html'
+ % quote_plus(item['name']))
+ if are_removals:
+ item['will_be_removed'] = True
+ removal_text = NSLocalizedString(
+ u'Will be removed',
+ u'WillBeRemovedDisplayText').encode('utf-8')
+ item['version_label'] = ('%s'
+ % removal_text)
+ item['version_to_install'] = ''
+ else:
+ item['version_label'] = NSLocalizedString(
+ u'Version',
+ u'VersionLabel').encode('utf-8')
+ if 'description' in item:
+ item['description'] = filtered_html(item['description'])
+ else:
+ item['description'] = ''
+ item['due_date_sort'] = NSDate.distantFuture()
+ if not are_removals:
+ force_install_after_date = item.get('force_install_after_date')
+ if force_install_after_date:
+ item['category'] = NSLocalizedString(
+ u'Critical Update', u'CriticalUpdateType')
+ item['due_date_sort'] = force_install_after_date
+ # insert installation deadline into description
+ local_date = munki.discardTimeZoneFromDate(
+ force_install_after_date)
+ date_str = munki.stringFromDate(local_date).encode('utf-8')
+ forced_date_text = NSLocalizedString(
+ u'This item must be installed by %s',
+ u'ForcedDateWarning').encode('utf-8')
+ description = item['description']
+ # prepend deadline info to description.
+ item['description'] = (
+ '' + forced_date_text % date_str
+ + '
' + description)
+ if not 'category' in item and not are_optional:
+ item['category'] = NSLocalizedString(u'Managed Update',
+ u'ManagedUpdateType').encode('utf-8')
+ if are_optional:
+ item['hide_cancel_button'] = ''
+ item['cancel_or_add'] = 'cancel'
+ if item['status'] not in [
+ 'will-be-removed', 'will-be-installed', 'update-will-be-installed']:
+ item['cancel_or_add'] = 'add'
+ else:
+ item['hide_cancel_button'] = 'hidden'
+ item['cancel_or_add'] = ''
+
+ # sort items that need restart highest, then logout, then other
+ if item.get('RestartAction') in [None, 'None']:
+ item['restart_action_text'] = ''
+ item['restart_sort'] = 2
+ elif item['RestartAction'] in ['RequireRestart', 'RecommendRestart']:
+ item['restart_sort'] = 0
+ item['restart_action_text'] = NSLocalizedString(
+ u'Restart Required', u'RequireRestartMessage').encode('utf-8')
+ item['restart_action_text'] += ''
+ elif item['RestartAction'] in ['RequireLogout', 'RecommendLogout']:
+ item['restart_sort'] = 1
+ item['restart_action_text'] = NSLocalizedString(
+ u'Logout Required', u'RequireLogoutMessage').encode('utf-8')
+ item['restart_action_text'] += ''
+
+ # sort bigger installs to the top
+ if item.get('installed_size'):
+ item['size_sort'] = -int(item['installed_size'])
+ item['size'] = munki.humanReadable(item['installed_size'])
+ elif item.get('installer_item_size'):
+ item['size_sort'] = -int(item['installer_item_size'])
+ item['size'] = munki.humanReadable(item['installer_item_size'])
+ else:
+ item['size_sort'] = 0
+ item['size'] = ''
diff --git a/code/Managed Software Center/Managed Software Center/munki.py b/code/Managed Software Center/Managed Software Center/munki.py
index c4b22d41..032deac8 100644
--- a/code/Managed Software Center/Managed Software Center/munki.py
+++ b/code/Managed Software Center/Managed Software Center/munki.py
@@ -113,7 +113,7 @@ def reload_prefs():
this needs to be run after returning from MunkiStatus"""
CFPreferencesAppSynchronize(BUNDLE_ID)
-DEFAULT_GUI_CACHE_AGE_SECS = 60
+DEFAULT_GUI_CACHE_AGE_SECS = 600
def pref(pref_name):
"""Return a preference. Since this uses CFPreferencesCopyAppValue,
diff --git a/code/Managed Software Center/Managed Software Center/templates/myitems_template.html b/code/Managed Software Center/Managed Software Center/templates/myitems_template.html
index 7b711aa8..37b906d5 100644
--- a/code/Managed Software Center/Managed Software Center/templates/myitems_template.html
+++ b/code/Managed Software Center/Managed Software Center/templates/myitems_template.html
@@ -32,7 +32,7 @@
-
+
${myitems_rows}
diff --git a/code/client/managedsoftwareupdate b/code/client/managedsoftwareupdate
index 791c5529..ac2e2621 100755
--- a/code/client/managedsoftwareupdate
+++ b/code/client/managedsoftwareupdate
@@ -895,10 +895,7 @@ def main():
if options.verbose:
print 'Done.'
- if options.manualcheck:
- # just quit munkistatus; Managed Software Update will notify
- munkistatus.quit()
- elif notify_user:
+ if notify_user:
# it may have been more than a minute since we ran our
# original updatecheck so tickle the updatecheck time
# so MSU.app knows to display results immediately