Merge remote-tracking branch 'original/Munki2' into Munki2

This commit is contained in:
Christopher Grande
2014-03-28 13:40:27 -04:00
6 changed files with 101 additions and 98 deletions
@@ -19,7 +19,7 @@
import os
from urllib import quote_plus, unquote_plus
from urllib import quote, unquote
import munki
import msuhtml
@@ -242,7 +242,7 @@ class MSUMainWindowController(NSWindowController):
filename = self._current_page_filename
name = os.path.splitext(filename)[0]
key, p, quoted_value = name.partition('-')
value = unquote_plus(quoted_value)
#value = unquote(quoted_value)
if key == 'detail':
# optional item detail page
self.webView.reload_(self)
@@ -480,7 +480,7 @@ class MSUMainWindowController(NSWindowController):
category = None
filter = None
developer = None
value = unquote_plus(quoted_value)
value = unquote(quoted_value)
if key == 'category':
if value != 'all':
category = value
@@ -806,7 +806,7 @@ class MSUMainWindowController(NSWindowController):
changes the category selected in the sidebar popup'''
if category == 'All Categories':
category = u'all'
self.load_page('category-%s.html' % quote_plus(category))
self.load_page('category-%s.html' % category)
def setStatusViewTitle_(self, title_text):
'''When displaying status during a managedsoftwareupdate run, this method
@@ -868,7 +868,7 @@ class MSUMainWindowController(NSWindowController):
'''User changed the search field'''
filterString = self.searchField.stringValue().lower()
if filterString:
self.load_page('filter-%s.html' % quote_plus(filterString))
self.load_page('filter-%s.html' % filterString)
def currentPageIsUpdatesPage(self):
'''return True if current tab selected is updates'''
@@ -25,43 +25,37 @@ import msulib
import munki
from operator import itemgetter
from urllib import quote_plus, unquote_plus
from urllib import quote, unquote
from Foundation import NSLocalizedString
from Foundation import NSDate
from Foundation import NSLog
# place to cache our expensive-to-calculate data
_cache = {}
# cache reads from AppleUpdates.plist, InstallInfo.plist
_cache['apple_updates'] = None
_cache['install_info'] = None
# cache lists
_cache['optional_install_items'] = None
_cache['update_list'] = None
_cache['dependent_items'] = None
def reset():
'''clear all our cached values'''
for key in _cache.keys():
_cache[key] = None
global _cache
_cache = {}
def getAppleUpdates():
if _cache['apple_updates'] is None:
if not 'apple_updates' in _cache:
_cache['apple_updates'] = munki.getAppleUpdates()
return _cache['apple_updates']
def getInstallInfo():
if _cache['install_info'] is None:
if not 'install_info' in _cache:
_cache['install_info'] = munki.getInstallInfo()
return _cache['install_info']
def getOptionalInstallItems():
if _cache['optional_install_items'] is None:
if not 'optional_install_items' in _cache:
_cache['optional_install_items'] = [OptionalItem(item)
for item in getInstallInfo().get('optional_installs', [])]
return _cache['optional_install_items']
@@ -85,7 +79,7 @@ def getOptionalWillBeRemovedItems():
def getUpdateList():
if _cache['update_list'] is None:
if not 'update_list'in _cache:
_cache['update_list'] = _build_update_list()
return _cache['update_list']
@@ -161,7 +155,7 @@ def getEffectiveUpdateList():
'''Combine the updates Munki has found with any optional choices to
make the effective list of updates'''
managed_update_names = getInstallInfo().get('managed_updates', [])
optional_item_names = [item['name'] for item in getInstallInfo().get('optional_installs')]
optional_item_names = [item['name'] for item in getInstallInfo().get('optional_installs', [])]
self_service_installs = SelfService().installs()
self_service_uninstalls = SelfService().uninstalls()
# items in the update_list that are part of optional_items
@@ -192,14 +186,15 @@ def getMyItemsList():
def dependentItems(this_name):
'''Returns the names of any optional items that require this optional item'''
'''Returns the names of any selected optional items that require this optional item'''
if not 'optional_installs_with_dependencies' in _cache:
self_service_installs = SelfService().installs()
optional_installs = getInstallInfo().get('optional_installs', [])
_cache['optional_installs_with_dependencies'] = [item for item in optional_installs
if item['name'] in self_service_installs
and 'requires' in item]
dependent_items = []
self_service_installs = SelfService().installs()
optional_installs = getInstallInfo().get('optional_installs', [])
optional_installs_with_dependencies = [item for item in optional_installs
if item['name'] in self_service_installs
and 'requires' in item]
for item in optional_installs_with_dependencies:
for item in _cache['optional_installs_with_dependencies']:
if this_name in item['requires']:
dependent_items.append(item['name'])
return dependent_items
@@ -602,7 +597,7 @@ class OptionalItem(GenericItem):
self['size'] = munki.humanReadable(self['installed_size'])
else:
self['size'] = u''
self['detail_link'] = u'detail-%s.html' % quote_plus(self['name'])
self['detail_link'] = u'detail-%s.html' % quote(self['name'])
self['hide_cancel_button'] = u''
def _get_status(self):
@@ -706,7 +701,7 @@ class UpdateItem(GenericItem):
super(UpdateItem, self).__init__(*arg, **kw)
identifier = self.get('name', '') + '--version-' + self.get('version_to_install', '')
self['detail_link'] = ('updatedetail-%s.html'
% quote_plus(identifier))
% quote(identifier))
if not self['status'] == 'will-be-removed':
force_install_after_date = self.get('force_install_after_date')
if force_install_after_date:
@@ -10,7 +10,7 @@ import os
from operator import itemgetter
from random import shuffle
from urllib import quote_plus, unquote_plus
from urllib import quote, unquote
import MunkiItems
import msulib
@@ -25,7 +25,7 @@ def build_page(filename):
'''Dispatch request to build a page to the appropriate function'''
name = os.path.splitext(filename)[0]
key, p, quoted_value = name.partition('-')
value = unquote_plus(quoted_value)
value = unquote(quoted_value)
if key == 'detail':
build_detail_page(value)
if key == 'category':
@@ -55,22 +55,42 @@ def write_page(page_name, html):
def build_detail_page(item_name):
'''Build page showing detail for a single optional item'''
items = MunkiItems.getOptionalInstallItems()
page_name = u'detail-%s.html' % quote_plus(item_name)
page_name = u'detail-%s.html' % item_name
for item in items:
if item['name'] == item_name:
page = MunkiItems.OptionalItem(item)
msulib.addSidebarLabels(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 = msulib.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_plus(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''
@@ -83,26 +103,6 @@ def build_detail_page(item_name):
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
# make "More by CategoryFoo" list
page['hide_more_in_category'] = u'hidden'
more_in_category_html = u''
if item.get('category'):
category = item['category']
page['category_link'] = u'category-%s.html' % quote_plus(category)
more_in_category = [a for a in items
if a.get('category') == category
and a != item
and a not in more_by_developer
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 = msulib.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
page['footer'] = msulib.getFooter()
template = msulib.get_template('detail_template.html')
@@ -123,13 +123,13 @@ def build_list_page(category=None, developer=None, filter=None):
category = None
if category:
header = category
page_name = u'category-%s.html' % quote_plus(category)
page_name = u'category-%s.html' % category
if developer:
header = developer
page_name = u'developer-%s.html' % quote_plus(developer)
page_name = u'developer-%s.html' % developer
if filter:
header = u'Search results for %s' % filter
page_name = u'filter-%s.html' % quote_plus(filter)
page_name = u'filter-%s.html' % filter
category_list = []
for item in items:
@@ -267,7 +267,7 @@ def build_category_items_html():
for category in sorted(category_list):
category_data = {}
category_data['category_name'] = category
category_data['category_link'] = u'category-%s.html' % quote_plus(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']
@@ -479,7 +479,7 @@ def get_warning_text():
def build_updatedetail_page(identifier):
'''Build detail page for a non-optional update'''
items = MunkiItems.getUpdateList()
page_name = u'updatedetail-%s.html' % quote_plus(identifier)
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:
@@ -78,13 +78,6 @@
</ul>
</div>
</div>
<!-- More by developer sidebar list-->
<div class="titled-box more-by ${hide_more_by_developer}">
<h2>
<a href="${developer_link}">${moreByDeveloperLabel}</a>
</h2>
<div class="content">${more_by_developer}</div>
</div>
<!-- More in category sidebar list-->
<div class="titled-box more-by ${hide_more_in_category}">
<h2>
@@ -92,6 +85,13 @@
</h2>
<div class="content">${more_in_category}</div>
</div>
<!-- More by developer sidebar list-->
<div class="titled-box more-by ${hide_more_by_developer}">
<h2>
<a href="${developer_link}">${moreByDeveloperLabel}</a>
</h2>
<div class="content">${more_by_developer}</div>
</div>
</div>
</div>
</div>
+40 -31
View File
@@ -65,12 +65,12 @@ def findIconForApp(app_path):
return None
try:
info = FoundationPlist.readPlist(
os.path.join(app_path, 'Contents/Info.plist'))
os.path.join(app_path, u'Contents/Info.plist'))
except (FoundationPlist.FoundationPlistException):
return None
app_name = os.path.basename(app_path)
icon_filename = info.get('CFBundleIconFile', app_name)
icon_path = os.path.join(app_path, 'Contents/Resources', icon_filename)
icon_path = os.path.join(app_path, u'Contents/Resources', icon_filename)
if not os.path.splitext(icon_path)[1]:
# no file extension, so add '.icns'
icon_path += '.icns'
@@ -116,7 +116,7 @@ def extractAppIconsFromFlatPkg(pkg_path):
# bomfile path is of the form:
# /tmp/FlashPlayer.pkg.boms.2Rxa1z/AdobeFlashPlayerComponent.pkg/Bom
pkgname = os.path.basename(os.path.dirname(bomfile))
if not pkgname.endswith('.pkg'):
if not pkgname.endswith(u'.pkg'):
# no subpackages; this is a component pkg
pkgname = ''
cmd = ['/usr/bin/lsbom', '-s', bomfile]
@@ -129,20 +129,20 @@ def extractAppIconsFromFlatPkg(pkg_path):
# record paths to all app Info.plist files
pkg_dict[pkgname]= [
os.path.normpath(line) for line in output.splitlines()
if line.endswith('.app/Contents/Info.plist')]
if line.endswith(u'.app/Contents/Info.plist')]
if not pkg_dict[pkgname]:
# remove empty lists
del(pkg_dict[pkgname])
if not pkg_dict:
return []
icon_paths = []
pkgtmp = os.path.join(tempfile.mkdtemp(dir='/tmp'), 'pkg')
pkgtmp = os.path.join(tempfile.mkdtemp(dir=u'/tmp'), u'pkg')
exporttmp = tempfile.mkdtemp(dir='/tmp')
cmd = ['/usr/sbin/pkgutil', '--expand', pkg_path, pkgtmp]
result = subprocess.call(cmd)
if result == 0:
for pkg in pkg_dict:
archive_path = os.path.join(pkgtmp, pkg, 'Payload')
archive_path = os.path.join(pkgtmp, pkg, u'Payload')
err = extractAppBitsFromPkgArchive(archive_path, exporttmp)
if err == 0:
for info_path in pkg_dict[pkg]:
@@ -163,9 +163,11 @@ def extractAppIconsFromFlatPkg(pkg_path):
return icon_paths
def extractAppIconsFromBundlePkg(pkg_path):
def findInfoPlistPathsInBundlePkg(pkg_path):
'''Returns a dict with pkg paths as keys and filename lists
as values'''
pkg_dict = {}
bomfile = os.path.join(pkg_path, 'Contents', 'Archive.bom')
bomfile = os.path.join(pkg_path, u'Contents/Archive.bom')
if os.path.exists(bomfile):
info_paths = getAppInfoPathsFromBundleComponentPkg(pkg_path)
if info_paths:
@@ -174,26 +176,33 @@ def extractAppIconsFromBundlePkg(pkg_path):
# mpkg or dist pkg; look for component pkgs within
pkg_dict = {}
original_dir = os.getcwd()
pkg_contents_dir = os.path.join(pkg_path, 'Contents')
os.chdir(pkg_contents_dir)
pkgs = (glob.glob('*.pkg') + glob.glob('*/*.pkg')
+ glob.glob('*/*/*.pkg') + glob.glob('*.mpkg') +
glob.glob('*/*.mpkg') + glob.glob('*/*/*.mpkg'))
os.chdir(original_dir)
pkg_contents_dir = os.path.join(pkg_path, u'Contents')
if os.path.isdir(pkg_contents_dir):
os.chdir(pkg_contents_dir)
pkgs = (glob.glob('*.pkg') + glob.glob('*/*.pkg')
+ glob.glob('*/*/*.pkg') + glob.glob('*.mpkg') +
glob.glob('*/*.mpkg') + glob.glob('*/*/*.mpkg'))
os.chdir(original_dir)
else:
pkgs = []
for pkg in pkgs:
full_path = os.path.join(pkg_contents_dir, pkg)
info_paths = getAppInfoPathsFromBundleComponentPkg(full_path)
if info_paths:
pkg_dict[full_path] = info_paths
pkg_dict.update(findInfoPlistPathsInBundlePkg(full_path))
return pkg_dict
def extractAppIconsFromBundlePkg(pkg_path):
'''Returns a list of paths for application icons found
inside the bundle pkg at pkg_path'''
pkg_dict = findInfoPlistPathsInBundlePkg(pkg_path)
icon_paths = []
exporttmp = tempfile.mkdtemp(dir='/tmp')
for pkg in pkg_dict:
archive_path = os.path.join(pkg, 'Contents/Archive.pax.gz')
archive_path = os.path.join(pkg, u'Contents/Archive.pax.gz')
err = extractAppBitsFromPkgArchive(archive_path, exporttmp)
if err == 0:
for info_path in pkg_dict[pkg]:
full_path = os.path.join(exporttmp, info_path)
full_path = os.path.normpath(os.path.join(exporttmp, info_path))
app_path = os.path.dirname(os.path.dirname(full_path))
icon_path = findIconForApp(app_path)
if icon_path:
@@ -203,7 +212,7 @@ def extractAppIconsFromBundlePkg(pkg_path):
def getAppInfoPathsFromBundleComponentPkg(pkg_path):
'''Returns a list of paths to application Info.plists'''
bomfile = os.path.join(pkg_path, 'Contents', 'Archive.bom')
bomfile = os.path.join(pkg_path, u'Contents/Archive.bom')
if os.path.exists(bomfile):
cmd = ['/usr/bin/lsbom', '-s', bomfile]
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
@@ -228,7 +237,7 @@ def generate_png_from_copy_from_dmg_item(install_item, repo_path):
icon_path = findIconForApp(app_path)
if icon_path:
png_path = os.path.join(
repo_path, 'icons', install_item['name'] + '.png')
repo_path, u'icons', install_item['name'] + u'.png')
result = convertIconToPNG(icon_path, png_path)
if result:
print_utf8(u'\tWrote: %s' % png_path)
@@ -246,7 +255,7 @@ def generate_pngs_from_installer_pkg(install_item, repo_path):
mountpoint = None
pkg_path = None
item_path = os.path.join(
repo_path, 'pkgs', install_item['installer_item_location'])
repo_path, u'pkgs', install_item['installer_item_location'])
if munkicommon.hasValidDiskImageExt(item_path):
dmg_path = item_path
mountpoints = munkicommon.mountdmg(dmg_path)
@@ -274,7 +283,7 @@ def generate_pngs_from_installer_pkg(install_item, repo_path):
if len(icon_paths) == 1:
png_path = os.path.join(
repo_path, 'icons', install_item['name'] + '.png')
repo_path, u'icons', install_item['name'] + u'.png')
result = convertIconToPNG(icon_paths[0], png_path)
if result:
print_utf8(u'\tWrote: %s' % png_path)
@@ -282,8 +291,8 @@ def generate_pngs_from_installer_pkg(install_item, repo_path):
index = 1
for icon_path in icon_paths:
png_path = os.path.join(
repo_path, 'icons',
install_item['name'] + '_' + str(index) + '.png')
repo_path, u'icons',
install_item['name'] + '_' + str(index) + u'.png')
result = convertIconToPNG(icon_path, png_path)
if result:
print_utf8(u'\tWrote: %s' % png_path)
@@ -316,16 +325,16 @@ def findItemsToCheck(repo_path, itemlist=None):
def generate_pngs_from_munki_items(repo_path, force=False, itemlist=None):
itemlist = findItemsToCheck(repo_path, itemlist=None)
icons_dir = os.path.join(repo_path, 'icons')
icons_dir = os.path.join(repo_path, u'icons')
if not os.path.exists(icons_dir):
os.makedir(icons_dir)
os.mkdir(icons_dir)
for item in itemlist:
print_utf8(u'Processing %s...' % item['name'])
icon_name = item.get('icon_name') or item['name']
if not os.path.splitext(icon_name)[1]:
icon_name += '.png'
icon_name += u'.png'
icon_path = os.path.join(
repo_path, 'icons', icon_name)
repo_path, u'icons', icon_name)
if os.path.exists(icon_path) and not force:
print_utf8(u'Found existing icon at %s' % icon_name)
continue
@@ -353,7 +362,7 @@ def getConditionalOptionalItems(plist):
def findAllOptionalInstalls(repo_path):
optional_items = set()
errors = []
manifests_path = os.path.join(repo_path, 'manifests')
manifests_path = os.path.join(repo_path, u'manifests')
# Walk through the manifest files
for dirpath, dirnames, filenames in os.walk(manifests_path):
for dirname in dirnames:
@@ -405,7 +414,7 @@ def pref(prefname):
PREFSNAME = 'com.googlecode.munki.munkiimport.plist'
PREFSPATH = os.path.expanduser(os.path.join('~/Library/Preferences',
PREFSPATH = os.path.expanduser(os.path.join(u'~/Library/Preferences',
PREFSNAME))
def main():
'''Main'''
-1
View File
@@ -780,7 +780,6 @@ def main():
('Version', 'version'),
('Category', 'category'),
('Developer', 'developer'),
('Icon name', 'icon_name'),
)
for (name, key) in editfields:
prompt = '%15s' % name