def build_updatedetail_page(identifier): '''Build detail page for a non-optional update''' items = MunkiItems.getUpdateList() page_name = u'updatedetail-%s.html' % identifier name, sep, version = identifier.partition('--version-') for item in items: if item['name'] == name and item['version_to_install'] == version: page = MunkiItems.UpdateItem(item) addDetailSidebarLabels(page) force_install_after_date = item.get('force_install_after_date') if force_install_after_date: local_date = munki.discardTimeZoneFromDate( force_install_after_date) date_str = munki.shortRelativeStringFromDate(local_date) page['dueLabel'] += u' ' page['short_due_date'] = date_str else: page['dueLabel'] = u'' page['short_due_date'] = u'' footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updatedetail_template.html', page, footer=footer) return # if we get here we didn't find any item matching identifier msclog.debug_log('No update detail found for %s' % item_name) build_item_not_found_page(page_name)
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 confirmUpdatesAndInstall(self): '''Make sure it's OK to proceed with installing if logout or restart is required''' if self.alertedToMultipleUsers(): return elif MunkiItems.updatesRequireRestart(): alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Restart Required", u"Restart Required title"), NSLocalizedString(u"Log out and update", u"Log out and Update button text"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, u"%@", NSLocalizedString( (u"A restart is required after updating. Please be patient " "as there may be a short delay at the login window. Log " "out and update now?"), u"Restart Required detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.logoutAlertDidEnd_returnCode_contextInfo_, nil) elif MunkiItems.updatesRequireLogout() or munki.installRequiresLogout(): alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Logout Required", u"Logout Required title"), NSLocalizedString(u"Log out and update", u"Log out and Update button text"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, u"%@", NSLocalizedString( (u"A logout is required before updating. Please be patient " "as there may be a short delay at the login window. Log " "out and update now?"), u"Logout Required detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.logoutAlertDidEnd_returnCode_contextInfo_, nil) else: # we shouldn't have been invoked if neither a restart or logout was required msclog.debug_log( 'confirmUpdatesAndInstall was called but no restart or logout was needed')
def build_updatedetail_page(identifier): '''Build detail page for a non-optional update''' items = MunkiItems.getUpdateList() + MunkiItems.getProblemItems() page_name = u'updatedetail-%s.html' % identifier name, sep, version = identifier.partition('--version-') for item in items: if item['name'] == name and item.get('version_to_install', '') == version: page = MunkiItems.UpdateItem(item) escapeAndQuoteCommonFields(page) addDetailSidebarLabels(page) force_install_after_date = item.get('force_install_after_date') if force_install_after_date: try: local_date = munki.discardTimeZoneFromDate( force_install_after_date) date_str = munki.shortRelativeStringFromDate( local_date) page['dueLabel'] += u' ' page['short_due_date'] = date_str except munki.BadDateError: # some issue with the stored date msclog.debug_log('Problem with force_install_after_date for %s' % identifier) page['dueLabel'] = u'' page['short_due_date'] = u'' else: page['dueLabel'] = u'' page['short_due_date'] = u'' footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updatedetail_template.html', page, footer=footer) return # if we get here we didn't find any item matching identifier msclog.debug_log('No update detail found for %s' % identifier) build_item_not_found_page(page_name)
def build_updates_page(): '''available/pending updates''' page_name = u'updates.html' # need to consolidate/centralize this flag. Accessing it this way is ugly. if NSApp.delegate().mainWindowController._update_in_progress: return build_update_status_page() item_list = MunkiItems.getEffectiveUpdateList() other_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'update-available' ] page = {} page['update_rows'] = u'' page['hide_progress_spinner'] = u'hidden' page['hide_other_updates'] = u'hidden' page['install_all_button_classes'] = u'' item_template = get_template('update_row_template.html') if item_list: for item in item_list: escapeAndQuoteCommonFields(item) page['update_rows'] += item_template.safe_substitute(item) elif not other_updates: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"Your software is up to date.", u"No Pending Updates primary text") alert['secondary_status_text'] = NSLocalizedString( u"There is no new software for your computer at this time.", u"No Pending Updates secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' page['update_rows'] = status_results_template.safe_substitute(alert) count = len(item_list) page['update_count'] = msclib.updateCountMessage(count) page['install_btn_label'] = msclib.getInstallAllButtonTextForCount(count) page['warning_text'] = get_warning_text() page['other_updates_header_message'] = NSLocalizedString( u"Other available updates", u"Other Available Updates label") page['other_update_rows'] = u'' if other_updates: page['hide_other_updates'] = u'' for item in other_updates: escapeAndQuoteCommonFields(item) page['other_update_rows'] += item_template.safe_substitute(item) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updates_template.html', page, footer=footer)
def build_updates_page(): '''available/pending updates''' page_name = u'updates.html' # need to consolidate/centralize this flag. Accessing it this way is ugly. if NSApp.delegate().mainWindowController._update_in_progress: return build_update_status_page() item_list = MunkiItems.getEffectiveUpdateList() other_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'update-available'] page = {} page['update_rows'] = u'' page['hide_progress_spinner'] = u'hidden' page['hide_other_updates'] = u'hidden' page['install_all_button_classes'] = u'' item_template = get_template('update_row_template.html') if item_list: for item in item_list: escapeAndQuoteCommonFields(item) page['update_rows'] += item_template.safe_substitute(item) elif not other_updates: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"Your software is up to date.", u"No Pending Updates primary text") alert['secondary_status_text'] = NSLocalizedString( u"There is no new software for your computer at this time.", u"No Pending Updates secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' page['update_rows'] = status_results_template.safe_substitute(alert) count = len(item_list) page['update_count'] = msclib.updateCountMessage(count) page['install_btn_label'] = msclib.getInstallAllButtonTextForCount(count) page['warning_text'] = get_warning_text() page['other_updates_header_message'] = NSLocalizedString( u"Other available updates", u"Other Available Updates label") page['other_update_rows'] = u'' if other_updates: page['hide_other_updates'] = u'' for item in other_updates: escapeAndQuoteCommonFields(item) page['other_update_rows'] += item_template.safe_substitute(item) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updates_template.html', page, footer=footer)
def myItemsActionButtonClicked_(self, item_name): '''this method is called from JavaScript when the user clicks the Install/Remove/Cancel button in the My Items view''' document = self.webView.mainFrameDocument() item = MunkiItems.optionalItemForName_(item_name) status_line = document.getElementById_('%s_status_text' % item_name) btn = document.getElementById_('%s_action_button_text' % item_name) if not item or not btn or not status_line: msclog.debug_log('User clicked MyItems action button for %s' % item_name) msclog.debug_log('Unexpected error finding HTML elements') return prior_status = item['status'] item.update_status() self.displayUpdateCount() if item['status'] == 'not-installed': # we removed item from list of things to install # now remove from display table_row = document.getElementById_('%s_myitems_table_row' % item_name) if table_row: node = table_row.parentNode().removeChild_(table_row) else: btn.setInnerText_(item['myitem_action_text']) status_line.setInnerText_(item['status_text']) status_line.setClassName_('status %s' % item['status']) if item['status'] in ['install-requested', 'removal-requested']: self._alertedUserToOutstandingUpdates = False if not self._update_in_progress: self.updateNow() elif prior_status in ['will-be-installed', 'update-will-be-installed', 'will-be-removed']: # cancelled a pending install or removal; should run an updatecheck self.checkForUpdates(suppress_apple_update_check=True)
def markPendingItemsAsInstalling(self): '''While an install/removal session is happening, mark optional items that are being installed/removed with the appropriate status''' msclog.debug_log('marking pendingItems as installing') install_info = munki.getInstallInfo() items_to_be_installed_names = [item['name'] for item in install_info.get('managed_installs', [])] items_to_be_removed_names = [item['name'] for item in install_info.get('removals', [])] for name in items_to_be_installed_names: # remove names for user selections since we are installing MunkiItems.user_install_selections.discard(name) for name in items_to_be_removed_names: # remove names for user selections since we are removing MunkiItems.user_removal_selections.discard(name) for item in MunkiItems.getOptionalInstallItems(): new_status = None if item['name'] in items_to_be_installed_names: msclog.debug_log('Setting status for %s to "installing"' % item['name']) new_status = u'installing' elif item['name'] in items_to_be_removed_names: msclog.debug_log('Setting status for %s to "removing"' % item['name']) new_status = u'removing' if new_status: item['status'] = new_status self.updateDOMforOptionalItem(item)
def build_categories_page(): '''Build page showing available categories and some items in each one''' all_items = MunkiItems.getOptionalInstallItems() header = NSLocalizedString(u"Categories", u"Categories label") page_name = u'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 = build_category_items_html() all_categories_label = NSLocalizedString(u"All Categories", u"AllCategoriesLabel") categories_html = u'<option selected>%s</option>\n' % all_categories_label for item in sorted(category_list): categories_html += u'<option>%s</option>\n' % item page = {} page['list_items'] = item_html page['category_items'] = categories_html page['header_text'] = header footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'list_template.html', page, showcase=u'', sidebar=u'', footer=footer)
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 build_list_page(category=None, developer=None, filter=None): '''Build page listing available optional items''' items = MunkiItems.getOptionalInstallItems() header = NSLocalizedString(u"All items", u"AllItemsHeaderText") page_name = u'category-all.html' if category == 'all': category = None if category: header = category page_name = u'category-%s.html' % category if developer: header = developer page_name = u'developer-%s.html' % developer if filter: header = u'Search results for %s' % filter page_name = u'filter-%s.html' % 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 = build_list_page_items_html( category=category, developer=developer, filter=filter) # make HTML for Categories pop-up menu all_categories_label = NSLocalizedString(u"All Categories", u"AllCategoriesLabel") if category: categories_html = u'<option>%s</option>\n' % all_categories_label else: categories_html = u'<option selected>%s</option>\n' % all_categories_label for item in sorted(category_list): if item == category: categories_html += u'<option selected>%s</option>\n' % item else: categories_html += u'<option>%s</option>\n' % item categories_html_list = '' # make HTML for list of categories for item in sorted(category_list): categories_html_list += (u'<li class="link"><a href="category-%s.html">%s</a></li>\n' % (quote(item), item)) page = {} page['list_items'] = item_html page['category_items'] = categories_html page['category_list'] = categories_html_list page['header_text'] = header if category or filter or developer: showcase = '' else: showcase = get_template('showcase_template.html', raw=True) sidebar = get_template('sidebar_template.html', raw=True) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'list_template.html', page, showcase=showcase, sidebar=sidebar, footer=footer)
def build_detail_page(item_name): '''Build page showing detail for a single optional item''' msclog.debug_log('build_detail_page for %s' % item_name) items = MunkiItems.getOptionalInstallItems() page_name = u'detail-%s.html' % item_name for item in items: if item['name'] == item_name: page = MunkiItems.OptionalItem(item) addDetailSidebarLabels(page) # make "More in CategoryFoo" list page['hide_more_in_category'] = u'hidden' more_in_category_html = u'' more_in_category = [] if item.get('category'): category = item['category'] page['category_link'] = u'category-%s.html' % quote(category) more_in_category = [a for a in items if a.get('category') == category and a != item and a.get('status') != 'installed'] if more_in_category: page['hide_more_in_category'] = u'' page['moreInCategoryLabel'] = page['moreInCategoryLabel'] % page['category'] shuffle(more_in_category) more_template = get_template('detail_more_items_template.html') for more_item in more_in_category[:4]: 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 # make "More by DeveloperFoo" list page['hide_more_by_developer'] = u'hidden' more_by_developer_html = u'' more_by_developer = [] if item.get('developer'): developer = item['developer'] page['developer_link'] = u'developer-%s.html' % quote(developer) more_by_developer = [a for a in items if a.get('developer') == developer and a != item and a not in more_in_category and a.get('status') != 'installed'] if more_by_developer: page['hide_more_by_developer'] = u'' page['moreByDeveloperLabel'] = ( page['moreByDeveloperLabel'] % developer) shuffle(more_by_developer) more_template = get_template( 'detail_more_items_template.html') for more_item in more_by_developer[:4]: more_item['second_line'] = more_item.get('category', '') more_by_developer_html += more_template.safe_substitute(more_item) page['more_by_developer'] = more_by_developer_html footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'detail_template.html', page, footer=footer) return msclog.debug_log('No detail found for %s' % item_name) build_item_not_found_page(page_name)
def build_category_items_html(): '''Returns HTML for the items on the Categories page''' all_items = MunkiItems.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 = get_template('category_item_template.html') item_html = u'' for category in sorted(category_list): category_data = {} category_data['category_name_escaped'] = escape_html(category) category_data['category_link'] = u'category-%s.html' % quote(category) category_items = [item for item in all_items if item.get('category') == category] shuffle(category_items) category_data['item1_icon'] = category_items[0]['icon'] category_data['item1_display_name_escaped'] = escape_html( category_items[0]['display_name']) category_data['item1_detail_link'] = category_items[0]['detail_link'] if len(category_items) > 1: category_data['item2_display_name_escaped'] = escape_html( category_items[1]['display_name']) category_data['item2_detail_link'] = category_items[1]['detail_link'] else: category_data['item2_display_name_escaped'] = u'' category_data['item2_detail_link'] = u'#' if len(category_items) > 2: category_data['item3_display_name_escaped'] = escape_html( category_items[2]['display_name']) category_data['item3_detail_link'] = category_items[2]['detail_link'] else: category_data['item3_display_name_escaped'] = u'' category_data['item3_detail_link'] = u'#' item_html += item_template.safe_substitute(category_data) # pad with extra empty items so we have a multiple of 3 if len(category_list) % 3: for x in range(3 - (len(category_list) % 3)): item_html += u'<div class="lockup"></div>\n' else: # no items status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"There are no available software items.", u"No Items primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try again later.", u"No Items secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' item_html = status_results_template.safe_substitute(alert) return item_html
def confirmUpdatesAndInstall(self): '''Make sure it's OK to proceed with installing if logout or restart is required''' if self.alertedToMultipleUsers(): return elif MunkiItems.updatesRequireRestart(): alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Restart Required", u"Restart Required title"), NSLocalizedString(u"Log out and update", u"Log out and Update button text"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, u"%@", NSLocalizedString( u"A restart is required after updating. Please be patient " "as there may be a short delay at the login window. Log " "out and update now?", u"Restart Required detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.logoutAlertDidEnd_returnCode_contextInfo_, nil) elif MunkiItems.updatesRequireLogout() or munki.installRequiresLogout( ): alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( NSLocalizedString(u"Logout Required", u"Logout Required title"), NSLocalizedString(u"Log out and update", u"Log out and Update button text"), NSLocalizedString(u"Cancel", u"Cancel button title/short action text"), nil, u"%@", NSLocalizedString( u"A logout is required before updating. Please be patient " "as there may be a short delay at the login window. Log " "out and update now?", u"Logout Required detail")) alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( self.window, self, self.logoutAlertDidEnd_returnCode_contextInfo_, nil) else: # we shouldn't have been invoked if neither a restart or logout was # required msclog.debug_log( 'confirmUpdatesAndInstall was called but no restart or logout ' 'was needed')
def loadInitialView(self): '''Called by app delegate from applicationDidFinishLaunching:''' self.enableOrDisableSoftwareViewControls() optional_items = MunkiItems.getOptionalInstallItems() if not optional_items or self.getUpdateCount(): self.loadUpdatesPage_(self) else: self.loadAllSoftwarePage_(self) self.displayUpdateCount() self.cached_self_service = MunkiItems.SelfService()
def enableOrDisableSoftwareViewControls(self): '''Disable or enable the controls that let us view optional items''' optional_items = MunkiItems.getOptionalInstallItems() enabled_state = (len(optional_items) > 0) self.enableOrDisableToolbarButtons_(enabled_state) self.searchField.setEnabled_(enabled_state) self.findMenuItem.setEnabled_(enabled_state) self.softwareMenuItem.setEnabled_(enabled_state) self.softwareMenuItem.setEnabled_(enabled_state) self.categoriesMenuItem.setEnabled_(enabled_state) self.myItemsMenuItem.setEnabled_(enabled_state)
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 resetAndReload(self): '''Clear cached values, reload from disk. Display any changes. Typically called soon after a Munki session completes''' msclog.debug_log('resetAndReload method called') # need to clear out cached data MunkiItems.reset() # recache SelfService choices self.cached_self_service = MunkiItems.SelfService() # copy any new custom client resources msclib.get_custom_resources() # pending updates may have changed self._alertedUserToOutstandingUpdates = False # enable/disable controls as needed self.enableOrDisableSoftwareViewControls() # what page are we currently viewing? page_url = self.webView.mainFrameURL() filename = NSURL.URLWithString_(page_url).lastPathComponent() name = os.path.splitext(filename)[0] key, p, value = name.partition('-') 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()
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 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 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 markRequestedItemsAsProcessing(self): '''When an update check session is happening, mark optional items that have been requested as processing''' msclog.debug_log('marking requested items as processing') for item in MunkiItems.getOptionalInstallItems(): new_status = None if item['status'] == 'install-requested': msclog.debug_log('Setting status for %s to "downloading"' % item['name']) new_status = u'downloading' elif item['status'] == 'removal-requested': msclog.debug_log('Setting status for %s to "preparing-removal"' % item['name']) new_status = u'preparing-removal' if new_status: item['status'] = new_status self.updateDOMforOptionalItem(item)
def get_warning_text(): '''Return localized text warning about forced installs and/or logouts and/or restarts''' item_list = MunkiItems.getEffectiveUpdateList() warning_text = u'' forced_install_date = munki.earliestForceInstallDate(item_list) if forced_install_date: date_str = munki.stringFromDate(forced_install_date) forced_date_text = NSLocalizedString( u"One or more items must be installed by %s", u"Forced Install Date summary") warning_text = forced_date_text % date_str restart_text = getRestartActionForUpdateList(item_list) if restart_text: if warning_text: warning_text += u' • ' + restart_text else: warning_text = restart_text return warning_text
def build_myitems_rows(): '''Returns HTML for the items on the 'My Items' page''' item_list = MunkiItems.getMyItemsList() if item_list: item_template = get_template('myitems_row_template.html') myitems_rows = u'' for item in sorted(item_list, key=itemgetter('display_name_lower')): myitems_rows += item_template.safe_substitute(item) else: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"You have no selected software.", u"No Installed Software primary text") alert['secondary_status_text'] = ( u'<a href="category-all.html">%s</a>' % NSLocalizedString( u"Select software to install.", u"No Installed Software secondary text")) alert['hide_progress_bar'] = u'hidden' myitems_rows = status_results_template.safe_substitute(alert) return myitems_rows
def getFirmwareAlertInfo(self): '''Get detail about a firmware update''' info = [] for update_item in MunkiItems.getUpdateList(): if 'firmware_alert_text' in update_item: info_item = {} info_item['name'] = update_item.get('display_name', 'name') alert_text = update_item['firmware_alert_text'] if alert_text == u'_DEFAULT_FIRMWARE_ALERT_TEXT_': # substitute localized default alert text alert_text = NSLocalizedString( (u"Firmware will be updated on your computer. " "Your computer's power cord must be connected " "and plugged into a working power source. " "It may take several minutes for the update to " "complete. Do not disturb or shut off the power " "on your computer during this update."), u"Firmware Alert Default detail") info_item['alert_text'] = alert_text info.append(info_item) return info
def build_myitems_rows(): '''Returns HTML for the items on the 'My Items' page''' item_list = MunkiItems.getMyItemsList() if item_list: item_template = get_template('myitems_row_template.html') myitems_rows = u'' for item in sorted(item_list, key=itemgetter('display_name_lower')): myitems_rows += item_template.safe_substitute(item) else: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"You have no selected software.", u"No Installed Software primary text") alert['secondary_status_text'] = ( u'<a href="category-all.html">%s</a>' % NSLocalizedString(u"Select software to install.", u"No Installed Software secondary text")) alert['hide_progress_bar'] = u'hidden' myitems_rows = status_results_template.safe_substitute(alert) return myitems_rows
def actionButtonClicked_(self, item_name): '''this method is called from JavaScript when the user clicks the Install/Removel/Cancel button in the list or detail view''' item = MunkiItems.optionalItemForName_(item_name) if not item: msclog.debug_log( 'User clicked Install/Removel/Cancel button in the list or detail view') msclog.debug_log('Can\'t find item: %s' % item_name) return prior_status = item['status'] item.update_status() self.displayUpdateCount() self.updateDOMforOptionalItem(item) if item['status'] in ['install-requested', 'removal-requested']: self._alertedUserToOutstandingUpdates = False if not self._update_in_progress: self.updateNow() elif prior_status in ['will-be-installed', 'update-will-be-installed', 'will-be-removed']: # cancelled a pending install or removal; should run an updatecheck self.checkForUpdates(suppress_apple_update_check=True)
def build_updates_page(): '''available/pending updates''' page_name = u'updates.html' # need to consolidate/centralize this flag. Accessing it this way is ugly. if NSApp.delegate().mainWindowController._update_in_progress: return build_update_status_page() item_list = MunkiItems.getEffectiveUpdateList() problem_updates = MunkiItems.getProblemItems() for item in problem_updates: item['hide_cancel_button'] = u'hidden' # find any optional installs with update available other_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'update-available'] # find any listed optional install updates that require a higher OS # or have insufficient disk space or other blockers (because they have a # note) blocked_optional_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'installed' and item.get('note')] for item in blocked_optional_updates: item['hide_cancel_button'] = u'hidden' other_updates.extend(blocked_optional_updates) page = {} page['update_rows'] = u'' page['hide_progress_spinner'] = u'hidden' page['hide_problem_updates'] = u'hidden' page['hide_other_updates'] = u'hidden' page['install_all_button_classes'] = u'' item_template = get_template('update_row_template.html') # build pending updates table if item_list: for item in item_list: escapeAndQuoteCommonFields(item) page['update_rows'] += item_template.safe_substitute(item) elif not other_updates and not problem_updates: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"Your software is up to date.", u"No Pending Updates primary text") alert['secondary_status_text'] = NSLocalizedString( u"There is no new software for your computer at this time.", u"No Pending Updates secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' page['update_rows'] = status_results_template.safe_substitute(alert) count = len([item for item in item_list if item['status'] != 'problem-item']) page['update_count'] = msclib.updateCountMessage(count) page['install_btn_label'] = msclib.getInstallAllButtonTextForCount(count) page['warning_text'] = get_warning_text() # build problem updates table page['problem_updates_header_message'] = NSLocalizedString( u"Problem updates", u"Problem Updates label") page['problem_update_rows'] = u'' if problem_updates: page['hide_problem_updates'] = u'' for item in problem_updates: escapeAndQuoteCommonFields(item) page['problem_update_rows'] += item_template.safe_substitute(item) # build other available updates table page['other_updates_header_message'] = NSLocalizedString( u"Other available updates", u"Other Available Updates label") page['other_update_rows'] = u'' if other_updates: page['hide_other_updates'] = u'' for item in other_updates: escapeAndQuoteCommonFields(item) page['other_update_rows'] += item_template.safe_substitute(item) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updates_template.html', page, footer=footer)
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 windowDidBecomeMain_(self, notification): '''Our window was activated, make sure controls enabled as needed''' optional_items = MunkiItems.getOptionalInstallItems() enabled_state = (len(optional_items) > 0) self.enableOrDisableToolbarButtons_(enabled_state)
def getUpdateCount(self): '''Get the count of effective updates''' if self._update_in_progress: return 0 return len(MunkiItems.getEffectiveUpdateList())
def build_updates_page(): '''available/pending updates''' page_name = u'updates.html' # need to consolidate/centralize this flag. Accessing it this way is ugly. if NSApp.delegate().mainWindowController._update_in_progress: return build_update_status_page() item_list = MunkiItems.getEffectiveUpdateList() problem_updates = MunkiItems.getProblemItems() for item in problem_updates: item['hide_cancel_button'] = u'hidden' # find any optional installs with update available other_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'update-available' ] # find any listed optional install updates that require a higher OS # or have insufficient disk space or other blockers (because they have a # note) blocked_optional_updates = [ item for item in MunkiItems.getOptionalInstallItems() if item['status'] == 'installed' and item.get('note') ] for item in blocked_optional_updates: item['hide_cancel_button'] = u'hidden' other_updates.extend(blocked_optional_updates) page = {} page['update_rows'] = u'' page['hide_progress_spinner'] = u'hidden' page['hide_problem_updates'] = u'hidden' page['hide_other_updates'] = u'hidden' page['install_all_button_classes'] = u'' item_template = get_template('update_row_template.html') # build pending updates table if item_list: for item in item_list: escapeAndQuoteCommonFields(item) page['update_rows'] += item_template.safe_substitute(item) elif not other_updates and not problem_updates: status_results_template = get_template('status_results_template.html') alert = {} alert['primary_status_text'] = NSLocalizedString( u"Your software is up to date.", u"No Pending Updates primary text") alert['secondary_status_text'] = NSLocalizedString( u"There is no new software for your computer at this time.", u"No Pending Updates secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' page['update_rows'] = status_results_template.safe_substitute(alert) count = len( [item for item in item_list if item['status'] != 'problem-item']) page['update_count'] = msclib.updateCountMessage(count) page['install_btn_label'] = msclib.getInstallAllButtonTextForCount(count) page['warning_text'] = get_warning_text() # build problem updates table page['problem_updates_header_message'] = NSLocalizedString( u"Problem updates", u"Problem Updates label") page['problem_update_rows'] = u'' if problem_updates: page['hide_problem_updates'] = u'' for item in problem_updates: escapeAndQuoteCommonFields(item) page['problem_update_rows'] += item_template.safe_substitute(item) # build other available updates table page['other_updates_header_message'] = NSLocalizedString( u"Other available updates", u"Other Available Updates label") page['other_update_rows'] = u'' if other_updates: page['hide_other_updates'] = u'' for item in other_updates: escapeAndQuoteCommonFields(item) page['other_update_rows'] += item_template.safe_substitute(item) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'updates_template.html', page, footer=footer)
def updateOptionalInstallButtonClicked_(self, item_name): '''this method is called from JavaScript when a user clicks the cancel or add button in the updates list''' # TO-DO: better handling of all the possible "unexpected error"s document = self.webView.mainFrameDocument() item = MunkiItems.optionalItemForName_(item_name) if not item: msclog.debug_log('Unexpected error: Can\'t find item for %s' % item_name) return update_table_row = document.getElementById_('%s_update_table_row' % item_name) if not update_table_row: msclog.debug_log('Unexpected error: Can\'t find table row for %s' % item_name) return # remove this row from its current table node = update_table_row.parentNode().removeChild_(update_table_row) previous_status = item['status'] # update item status item.update_status() # do we need to add a new node to the other list? if item.get('needs_update'): # make some new HTML for the updated item managed_update_names = MunkiItems.getInstallInfo().get('managed_updates', []) item_template = mschtml.get_template('update_row_template.html') item_html = item_template.safe_substitute(item) if item['status'] in ['install-requested', 'update-will-be-installed', 'installed']: # add the node to the updates-to-install table table = document.getElementById_('updates-to-install-table') if item['status'] == 'update-available': # add the node to the other-updates table table = document.getElementById_('other-updates-table') if not table: msclog.debug_log('Unexpected error: could not find other-updates-table') return # this isn't the greatest way to add something to the DOM # but it works... table.setInnerHTML_(table.innerHTML() + item_html) # might need to toggle visibility of other updates div other_updates_div = document.getElementById_('other-updates') other_updates_div_classes = other_updates_div.className().split(' ') other_updates_table = document.getElementById_('other-updates-table') if other_updates_table.innerHTML().strip(): if 'hidden' in other_updates_div_classes: other_updates_div_classes.remove('hidden') other_updates_div.setClassName_(' '.join(other_updates_div_classes)) else: if not 'hidden' in other_updates_div_classes: other_updates_div_classes.append('hidden') other_updates_div.setClassName_(' '.join(other_updates_div_classes)) # update the updates-to-install header to reflect the new list of updates to install updateCount = self.getUpdateCount() update_count_message = msclib.updateCountMessage(updateCount) update_count_element = document.getElementById_('update-count-string') if update_count_element: update_count_element.setInnerText_(update_count_message) warning_text = mschtml.get_warning_text() warning_text_element = document.getElementById_('update-warning-text') if warning_text_element: warning_text_element.setInnerHTML_(warning_text) # update text of Install All button install_all_button_element = document.getElementById_('install-all-button-text') if install_all_button_element: install_all_button_element.setInnerText_( msclib.getInstallAllButtonTextForCount(updateCount)) # update count badges self.displayUpdateCount() if MunkiItems.updateCheckNeeded(): # check for updates after a short delay so UI changes visually complete first self.performSelector_withObject_afterDelay_(self.checkForUpdates, True, 1.0)
def build_list_page_items_html(category=None, developer=None, filter=None): '''Returns HTML for the items on the list page''' items = MunkiItems.getOptionalInstallItems() item_html = u'' if filter: # since the filter term came through the filesystem, # HFS+ does some unicode character decomposition which can cause issue # with comparisons # so before we do our comparison, we normalize the unicode string # using unicodedata.normalize filter = normalize('NFC', filter) msclog.debug_log(u'Filtering on %s' % filter) items = [ item for item in items if filter in item['display_name'].lower() or filter in item['description'].lower() or filter in item['developer'].lower() or filter in item['category'].lower() ] if category: items = [ item for item in items if category.lower() == item.get('category', '').lower() ] if developer: items = [ item for item in items if developer.lower() == item.get('developer', '').lower() ] if category is None and developer is None and filter is None: # this is the default (formerly) "all items" view # look for featured items and display those if we have them featured_items = [item for item in items if item.get('featured')] if featured_items: items = featured_items if items: item_template = get_template('list_item_template.html') for item in sorted(items, key=itemgetter('display_name_lower')): escapeAndQuoteCommonFields(item) item['category_and_developer_escaped'] = escape_html( item['category_and_developer']) item_html += item_template.safe_substitute(item) # pad with extra empty items so we have a multiple of 3 if len(items) % 3: for x in range(3 - (len(items) % 3)): item_html += u'<div class="lockup"></div>\n' else: # no items; build appropriate alert messages status_results_template = get_template('status_results_template.html') alert = {} if filter: alert['primary_status_text'] = NSLocalizedString( u"Your search had no results.", u"No Search Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try searching again.", u"No Search Results secondary text") elif category: alert['primary_status_text'] = NSLocalizedString( u"There are no items in this category.", u"No Category Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try selecting another category.", u"No Category Results secondary text") elif developer: alert['primary_status_text'] = NSLocalizedString( u"There are no items from this developer.", u"No Developer Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try selecting another developer.", u"No Developer Results secondary text") else: alert['primary_status_text'] = NSLocalizedString( u"There are no available software items.", u"No Items primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try again later.", u"No Items secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' item_html = status_results_template.safe_substitute(alert) return item_html
def build_list_page(category=None, developer=None, filter=None): '''Build page listing available optional items''' items = MunkiItems.getOptionalInstallItems() header = NSLocalizedString(u"All items", u"AllItemsHeaderText") page_name = u'category-all.html' if category == 'all': category = None if category: header = category page_name = u'category-%s.html' % category if developer: header = developer page_name = u'developer-%s.html' % developer if filter: header = u'Search results for %s' % filter page_name = u'filter-%s.html' % filter featured_items = [item for item in items if item.get('featured')] if (featured_items and category is None and developer is None and filter is None): header = NSLocalizedString(u"Featured items", u"FeaturedItemsHeaderText") category_list = [] for item in items: if 'category' in item and item['category'] not in category_list: category_list.append(item['category']) item_html = build_list_page_items_html(category=category, developer=developer, filter=filter) # make HTML for Categories pop-up menu if featured_items: all_categories_label = NSLocalizedString(u"Featured", u"FeaturedLabel") else: all_categories_label = NSLocalizedString(u"All Categories", u"AllCategoriesLabel") if category: categories_html = u'<option>%s</option>\n' % all_categories_label else: categories_html = u'<option selected>%s</option>\n' % all_categories_label for item in sorted(category_list): if item == category: categories_html += u'<option selected>%s</option>\n' % item else: categories_html += u'<option>%s</option>\n' % item categories_html_list = '' # make HTML for list of categories for item in sorted(category_list): categories_html_list += ( u'<li class="link"><a href="category-%s.html">%s</a></li>\n' % (quote(item), item)) page = {} page['list_items'] = item_html page['category_items'] = categories_html page['category_list'] = categories_html_list page['header_text'] = header if category or filter or developer: showcase = '' else: showcase = get_template('showcase_template.html', raw=True) sidebar = get_template('sidebar_template.html', raw=True) footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'list_template.html', page, showcase=showcase, sidebar=sidebar, footer=footer)
def build_detail_page(item_name): '''Build page showing detail for a single optional item''' msclog.debug_log('build_detail_page for %s' % item_name) items = MunkiItems.getOptionalInstallItems() page_name = u'detail-%s.html' % item_name for item in items: if item['name'] == item_name: # make a copy of the item to use to build our page page = MunkiItems.OptionalItem(item) escapeAndQuoteCommonFields(page) addDetailSidebarLabels(page) # make "More in CategoryFoo" list page['hide_more_in_category'] = u'hidden' more_in_category_html = u'' more_in_category = [] if item.get('category'): category = item['category'] page['category_link'] = u'category-%s.html' % quote(category) more_in_category = [ a for a in items if a.get('category') == category and a != item and a.get('status') != 'installed' ] if more_in_category: page['hide_more_in_category'] = u'' page['moreInCategoryLabel'] = page[ 'moreInCategoryLabel'] % page['category'] shuffle(more_in_category) more_template = get_template( 'detail_more_items_template.html') for more_item in more_in_category[:4]: more_item['display_name_escaped'] = escape_html( more_item['display_name']) 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 # make "More by DeveloperFoo" list page['hide_more_by_developer'] = u'hidden' more_by_developer_html = u'' more_by_developer = [] if item.get('developer'): developer = item['developer'] page['developer_link'] = u'developer-%s.html' % quote( developer) more_by_developer = [ a for a in items if a.get('developer') == developer and a != item and a not in more_in_category and a.get('status') != 'installed' ] if more_by_developer: page['hide_more_by_developer'] = u'' page['moreByDeveloperLabel'] = ( page['moreByDeveloperLabel'] % developer) shuffle(more_by_developer) more_template = get_template( 'detail_more_items_template.html') for more_item in more_by_developer[:4]: escapeAndQuoteCommonFields(more_item) more_item['second_line'] = more_item.get( 'category', '') more_by_developer_html += more_template.safe_substitute( more_item) page['more_by_developer'] = more_by_developer_html footer = get_template('footer_template.html', raw=True) generate_page(page_name, 'detail_template.html', page, footer=footer) return msclog.debug_log('No detail found for %s' % item_name) build_item_not_found_page(page_name)
def build_list_page_items_html(category=None, developer=None, filter=None): '''Returns HTML for the items on the list page''' items = MunkiItems.getOptionalInstallItems() item_html = u'' if filter: # since the filter term came through the filesystem, # HFS+ does some unicode character decomposition which can cause issues with comparisons # so before we do our comparison, we normalize the unicode string # using unicodedata.normalize filter = normalize('NFC', filter) msclog.debug_log(u'Filtering on %s' % filter) items = [item for item in items if filter in item['display_name'].lower() or filter in item['description'].lower() or filter in item['developer'].lower() or filter in item['category'].lower()] if category: items = [item for item in items if category.lower() in item.get('category', '').lower()] if developer: items = [item for item in items if developer.lower() in item.get('developer', '').lower()] if items: item_template = get_template('list_item_template.html') for item in sorted(items, key=itemgetter('display_name_lower')): escapeAndQuoteCommonFields(item) item['category_and_developer_escaped'] = escape_html(item['category_and_developer']) item_html += item_template.safe_substitute(item) # pad with extra empty items so we have a multiple of 3 if len(items) % 3: for x in range(3 - (len(items) % 3)): item_html += u'<div class="lockup"></div>\n' else: # no items; build appropriate alert messages status_results_template = get_template('status_results_template.html') alert = {} if filter: alert['primary_status_text'] = NSLocalizedString( u"Your search had no results.", u"No Search Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try searching again.", u"No Search Results secondary text") elif category: alert['primary_status_text'] = NSLocalizedString( u"There are no items in this category.", u"No Category Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try selecting another category.", u"No Category Results secondary text") elif developer: alert['primary_status_text'] = NSLocalizedString( u"There are no items from this developer.", u"No Developer Results primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try selecting another developer.", u"No Developer Results secondary text") else: alert['primary_status_text'] = NSLocalizedString( u"There are no available software items.", u"No Items primary text") alert['secondary_status_text'] = NSLocalizedString( u"Try again later.", u"No Items secondary text") alert['hide_progress_bar'] = u'hidden' alert['progress_bar_value'] = u'' item_html = status_results_template.safe_substitute(alert) return item_html