mirror of
https://github.com/munki/munki.git
synced 2026-04-24 05:49:42 -05:00
Merge remote-tracking branch 'munki/master'
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
install:
|
||||
- pip install pep8
|
||||
script:
|
||||
- pep8 code
|
||||
@@ -17,27 +17,17 @@ Munki is currently in use at organizations all over the world, managing software
|
||||
|
||||
Get started with Munki here: [Getting Started with Munki](https://github.com/munki/munki/wiki/)
|
||||
|
||||
Check out the Wiki for some notes and documentation, and browse and/or check out the source. See the [releases page](https://github.com/munki/munki/releases) for pre-built installer packages of supported releases, or [munkibuilds.org](https://munkibuilds.org) for packages built from the current Git revision, which may contain development, testing, or work-in-progress code.
|
||||
Check out the [Wiki](https://github.com/munki/munki/wiki) for some notes and documentation, and browse and/or check out the source. See the [releases page](https://github.com/munki/munki/releases) for pre-built installer packages of supported releases, or [munkibuilds.org](https://munkibuilds.org) for packages built from the current Git revision, which may contain development, testing, or work-in-progress code.
|
||||
|
||||
Some video learning resources are [here](https://github.com/munki/munki/wiki/More-Links-And-Tools#video-resources).
|
||||
|
||||
####Get help
|
||||
|
||||
If you have questions, or need additional help getting started, the [munki-discuss](https://groups.google.com/group/munki-discuss) group is the best place to start. Please don't post support questions as comments on wiki documentation pages, or as GitHub code issues.
|
||||
The wiki is an in-depth resource: https://github.com/munki/munki/wiki
|
||||
Frequently Asked Questions are here: https://github.com/munki/munki/wiki/FAQ
|
||||
|
||||
If you have additional questions, or need even more help getting started, post a question to [munki-discuss](https://groups.google.com/group/munki-discuss). Please don't post support questions as comments on wiki documentation pages, or as GitHub code issues.
|
||||
|
||||
Issues with MunkiWebAdmin should be discussed in its group: [munki-web-admin](https://groups.google.com/group/munki-web-admin).
|
||||
|
||||

|
||||
|
||||
###Announcement
|
||||
An exploit has been discovered against Munki tools older than version 2.1.
|
||||
|
||||
Untrusted input can be passed to the curl binary, causing arbitrary files to be downloaded to arbitrary locations.
|
||||
|
||||
Recommendation is to update to Munki 2.1 or later, which is not susceptible to this exploit, as version 2.1 and later no longer use the curl binary for http/https communication.
|
||||
|
||||
This vulnerability has been assigned a CVE ID: CVE-2015-2211
|
||||
|
||||
If you cannot update to Munki 2.1, there is a patch for Munki 2.0.1 here:
|
||||
https://github.com/munki/munki/releases/tag/v2.0.1.2254
|
||||
|
||||
And another for Munki 1.0.0 here:
|
||||
https://github.com/munki/munki/releases/tag/v1.0.0.1896.0
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
File: DockTilePlugIn.h
|
||||
|
||||
Copyright 2015 Greg Neagle.
|
||||
Copyright 2015-2016 Greg Neagle.
|
||||
Liberally adapted from Apple sample code:
|
||||
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
File: DockTilePlugIn.m
|
||||
|
||||
Copyright 2015 Greg Neagle.
|
||||
Copyright 2015-2016 Greg Neagle.
|
||||
Liberally adapted from Apple sample code:
|
||||
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
@@ -19,7 +19,7 @@
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 The Munki Project. All rights reserved.</string>
|
||||
<string>Copyright © 2016 The Munki Project. All rights reserved.</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>DockTile</string>
|
||||
</dict>
|
||||
|
||||
+11
-6
@@ -11,6 +11,7 @@
|
||||
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */ = {isa = PBXBuildFile; fileRef = C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */; };
|
||||
C02C98891911B81D00425167 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C02C98871911B81D00425167 /* Localizable.strings */; };
|
||||
C035274E18A9C582004A5AE4 /* libpython2.6.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C035274D18A9C582004A5AE4 /* libpython2.6.dylib */; };
|
||||
C0453A211CCEF7B60002D396 /* MSCLogWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */; };
|
||||
C049C9951AEC77DD00251D45 /* updatesTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = C049C9941AEC77DD00251D45 /* updatesTemplate.png */; };
|
||||
C05C3CEF188391F200095E65 /* munki.py in Resources */ = {isa = PBXBuildFile; fileRef = C05C3CEE188391F200095E65 /* munki.py */; };
|
||||
C079D9C118BD435200BAD62E /* AlertController.py in Resources */ = {isa = PBXBuildFile; fileRef = C079D9C018BD435200BAD62E /* AlertController.py */; };
|
||||
@@ -34,7 +35,7 @@
|
||||
C0B9E8B619AF7E9E00DB7247 /* Managed Software Center 10_6.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0B9E8B519AF7E9E00DB7247 /* Managed Software Center 10_6.icns */; };
|
||||
C0E098BC1857A3C80045DEEB /* msclib.py in Resources */ = {isa = PBXBuildFile; fileRef = C0E098BB1857A3C80045DEEB /* msclib.py */; };
|
||||
C0EF96BA1ADDB9B2002C02FF /* DockTilePlugIn.m in Sources */ = {isa = PBXBuildFile; fileRef = C0EF96B91ADDB9B2002C02FF /* DockTilePlugIn.m */; };
|
||||
C0EF96BD1ADDBD88002C02FF /* MSCDockTilePlugin.docktileplugin in Copy Files */ = {isa = PBXBuildFile; fileRef = C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
C0EF96BD1ADDBD88002C02FF /* MSCDockTilePlugin.docktileplugin in Copy Files */ = {isa = PBXBuildFile; fileRef = C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
C0F1586E187D256200052F9A /* MyStuffTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = C0F1586D187D256200052F9A /* MyStuffTemplate.png */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@@ -96,6 +97,7 @@
|
||||
C02C98901911B83000425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C02C98911911B83100425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C035274D18A9C582004A5AE4 /* libpython2.6.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; name = libpython2.6.dylib; path = ../../../../../../System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/config/libpython2.6.dylib; sourceTree = SOURCE_ROOT; };
|
||||
C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCLogWindowController.py; sourceTree = "<group>"; };
|
||||
C046261519FFF8C000AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
C046261619FFF8CC00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
C046261719FFF8DA00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -239,6 +241,7 @@
|
||||
C0AAA21F18BC67F90012663F /* mschtml.py */,
|
||||
C079D9C018BD435200BAD62E /* AlertController.py */,
|
||||
650B29A319B69FC800A5E946 /* MSCToolbar.py */,
|
||||
C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */,
|
||||
);
|
||||
path = "Managed Software Center";
|
||||
sourceTree = "<group>";
|
||||
@@ -335,7 +338,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = MSC;
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0720;
|
||||
ORGANIZATIONNAME = "The Munki Project";
|
||||
TargetAttributes = {
|
||||
C0EF96B01ADDB90B002C02FF = {
|
||||
@@ -422,6 +425,7 @@
|
||||
C05C3CEF188391F200095E65 /* munki.py in Resources */,
|
||||
C079D9C118BD435200BAD62E /* AlertController.py in Resources */,
|
||||
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */,
|
||||
C0453A211CCEF7B60002D396 /* MSCLogWindowController.py in Resources */,
|
||||
C090050816CDD84E00BE34CE /* MSCAppDelegate.py in Resources */,
|
||||
C0AE865A186D32AF00C87AE7 /* MSCBadgedTemplateImage.py in Resources */,
|
||||
C0A71B76188A47C700A6EE82 /* MSCMainWindowController.py in Resources */,
|
||||
@@ -606,6 +610,7 @@
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
@@ -649,7 +654,6 @@
|
||||
C090050F16CDD84E00BE34CE /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Managed Software Center/Managed Software Center-Prefix.pch";
|
||||
@@ -665,6 +669,7 @@
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.6;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.ManagedSoftwareCenter;
|
||||
PRODUCT_NAME = "Managed Software Center";
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = app;
|
||||
@@ -674,7 +679,6 @@
|
||||
C090051016CDD84E00BE34CE /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Managed Software Center/Managed Software Center-Prefix.pch";
|
||||
@@ -689,6 +693,7 @@
|
||||
"$(SYSTEM_LIBRARY_DIR)/Frameworks/Python.framework/Versions/2.6/lib/python2.6/config",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.6;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.ManagedSoftwareCenter;
|
||||
PRODUCT_NAME = "Managed Software Center";
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = app;
|
||||
@@ -698,7 +703,6 @@
|
||||
C0EF96B61ADDB90B002C02FF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
@@ -731,6 +735,7 @@
|
||||
"-framework",
|
||||
AppKit,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = NO;
|
||||
@@ -741,7 +746,6 @@
|
||||
C0EF96B71ADDB90B002C02FF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = NO;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
@@ -770,6 +774,7 @@
|
||||
"-framework",
|
||||
AppKit,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = NO;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# MSCAppDelegate.py
|
||||
# Managed Software Center
|
||||
#
|
||||
# Copyright 2013-2014 Greg Neagle.
|
||||
# Copyright 2013-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# MSCBadgedTemplateImage.py
|
||||
# Managed Software Center
|
||||
#
|
||||
# Copyright 2014 Greg Neagle.
|
||||
# Copyright 2014-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -68,8 +68,8 @@ class MSCBadgedTemplateImage(NSImage):
|
||||
badgeOutlineWidth, badgeOutlineHeight)
|
||||
|
||||
# shift the rects around to look better. These are magic numbers.
|
||||
badgeStringRect = NSOffsetRect(badgeStringRect, -4.75, -1)
|
||||
badgeOutlineRect = NSOffsetRect(badgeOutlineRect, -1, -4)
|
||||
badgeStringRect = NSOffsetRect(badgeStringRect, -4.75, -2)
|
||||
badgeOutlineRect = NSOffsetRect(badgeOutlineRect, -1, -5)
|
||||
|
||||
# our erase rect needs to be a little bigger than the badge itself
|
||||
badgeEraseRect = NSInsetRect(badgeOutlineRect, -1.5, -1.5)
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# MSCLogWindowController.py
|
||||
# Managed Software Center
|
||||
#
|
||||
# Created by Greg Neagle on 4/25/16.
|
||||
# Copyright (c) 2016 Munki Project. All rights reserved.
|
||||
#
|
||||
# Much code borrowed from https://github.com/MagerValp/LoginLog
|
||||
# with the blessing of MagerValp
|
||||
#
|
||||
|
||||
from objc import YES, NO, IBAction, IBOutlet
|
||||
# pylint: disable=wildcard-import
|
||||
# pylint: disable=unused-wildcard-import
|
||||
# pylint: disable=redefined-builtin
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
# pylint: enable=redefined-builtin
|
||||
# pylint: enable=wildcard-import
|
||||
|
||||
import munki
|
||||
import os
|
||||
|
||||
# lots of camelCase names, following Cocoa convention
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
|
||||
class MSCLogViewDataSource(NSObject):
|
||||
"""Data source for an NSTableView that displays an array of text lines.
|
||||
Line breaks are assumed to be LF, and partial lines from incremental
|
||||
reading is handled."""
|
||||
|
||||
# since this subclasses NSObject,
|
||||
# it doesn't have a Python __init__method
|
||||
# pylint: disable=no-init
|
||||
|
||||
logFileData = NSMutableArray.alloc().init()
|
||||
filteredData = logFileData
|
||||
|
||||
lastLineIsPartial = False
|
||||
filterText = ''
|
||||
|
||||
def tableView_writeRowsWithIndexes_toPasteboard_(
|
||||
self, aTableView, rowIndexes, pasteboard):
|
||||
'''Implements drag-n-drop of text rows to external apps'''
|
||||
text_to_copy = ''
|
||||
index_set = aTableView.selectedRowIndexes()
|
||||
index = index_set.firstIndex()
|
||||
while index != NSNotFound:
|
||||
line = self.filteredData.objectAtIndex_(index)
|
||||
text_to_copy += line + '\n'
|
||||
index = index_set.indexGreaterThanIndex_(index)
|
||||
#changeCount = pasteboard.clearContents()
|
||||
result = pasteboard.writeObjects_([text_to_copy])
|
||||
return YES
|
||||
|
||||
def applyFilterToData(self):
|
||||
'''Filter our log data'''
|
||||
if len(self.filterText):
|
||||
filterPredicate = NSPredicate.predicateWithFormat_(
|
||||
'self CONTAINS[cd] %@', self.filterText)
|
||||
self.filteredData = (
|
||||
self.logFileData.filteredArrayUsingPredicate_(filterPredicate))
|
||||
else:
|
||||
self.filteredData = self.logFileData
|
||||
|
||||
def addLine_partial_(self, line, isPartial):
|
||||
'''Add a line to our datasource'''
|
||||
if self.lastLineIsPartial:
|
||||
joinedLine = self.logFileData.lastObject() + line
|
||||
self.logFileData.removeLastObject()
|
||||
self.logFileData.addObject_(joinedLine)
|
||||
else:
|
||||
self.logFileData.addObject_(line)
|
||||
self.lastLineIsPartial = isPartial
|
||||
self.applyFilterToData()
|
||||
|
||||
def removeAllLines(self):
|
||||
'''Remove all data from our datasource'''
|
||||
self.logFileData.removeAllObjects()
|
||||
|
||||
def lineCount(self):
|
||||
'''Return the number of lines in our filtered data'''
|
||||
return self.filteredData.count()
|
||||
|
||||
def numberOfRowsInTableView_(self, tableView):
|
||||
'''Required datasource method'''
|
||||
return self.lineCount()
|
||||
|
||||
def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
|
||||
'''Required datasource method -- returns the text data for the
|
||||
given row and column'''
|
||||
if column.identifier() == 'data':
|
||||
return self.filteredData.objectAtIndex_(row)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class MSCLogWindowController(NSObject):
|
||||
'''Controller object for our log window'''
|
||||
|
||||
# since this subclasses NSObject,
|
||||
# it doesn't have a Python __init__method
|
||||
# pylint: disable=no-init
|
||||
|
||||
window = IBOutlet()
|
||||
logView = IBOutlet()
|
||||
searchField = IBOutlet()
|
||||
pathControl = IBOutlet()
|
||||
|
||||
logFileData = MSCLogViewDataSource.alloc().init()
|
||||
|
||||
fileHandle = None
|
||||
updateTimer = None
|
||||
|
||||
def copy_(self, sender):
|
||||
'''Implements copy operation so we can copy data from table view'''
|
||||
text_to_copy = ''
|
||||
index_set = self.logView.selectedRowIndexes()
|
||||
index = index_set.firstIndex()
|
||||
while index != NSNotFound:
|
||||
line = self.logFileData.filteredData.objectAtIndex_(index)
|
||||
text_to_copy += line + '\n'
|
||||
index = index_set.indexGreaterThanIndex_(index)
|
||||
pasteboard = NSPasteboard.generalPasteboard()
|
||||
changeCount = pasteboard.clearContents()
|
||||
result = pasteboard.writeObjects_([text_to_copy])
|
||||
|
||||
@IBAction
|
||||
def searchFilterChanged_(self, sender):
|
||||
'''User changed the search field'''
|
||||
filterString = self.searchField.stringValue().lower()
|
||||
self.logFileData.filterText = filterString
|
||||
self.logFileData.applyFilterToData()
|
||||
self.logView.reloadData()
|
||||
|
||||
@IBAction
|
||||
def showLogWindow_(self, notification):
|
||||
'''Show the log window.'''
|
||||
|
||||
if self.window.isVisible():
|
||||
# It's already open, just move it to front
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
return
|
||||
|
||||
screenRect = NSScreen.mainScreen().frame()
|
||||
windowRect = screenRect.copy()
|
||||
windowRect.origin.x = 100.0
|
||||
windowRect.origin.y = 200.0
|
||||
windowRect.size.width -= 200.0
|
||||
windowRect.size.height -= 300.0
|
||||
|
||||
logfile = munki.pref('LogFile')
|
||||
self.pathControl.setURL_(NSURL.fileURLWithPath_(logfile))
|
||||
self.window.setTitle_(os.path.basename(logfile))
|
||||
self.window.setFrame_display_(windowRect, NO)
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
self.watchLogFile_(logfile)
|
||||
|
||||
# allow dragging from table view to outside of the app
|
||||
self.logView.setDraggingSourceOperationMask_forLocal_(
|
||||
NSDragOperationAll, NO)
|
||||
|
||||
def watchLogFile_(self, logFile):
|
||||
'''Display and continuously update a log file in the main window.'''
|
||||
self.stopWatching()
|
||||
self.logFileData.removeAllLines()
|
||||
self.logView.setDataSource_(self.logFileData)
|
||||
self.logView.reloadData()
|
||||
self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile)
|
||||
self.refreshLog()
|
||||
# Kick off a timer that updates the log view periodically.
|
||||
self.updateTimer = (
|
||||
NSTimer.
|
||||
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
0.25, self, self.refreshLog, None, YES))
|
||||
|
||||
def stopWatching(self):
|
||||
'''Release the file handle and stop the update timer.'''
|
||||
if self.fileHandle is not None:
|
||||
self.fileHandle.closeFile()
|
||||
self.fileHandle = None
|
||||
if self.updateTimer is not None:
|
||||
self.updateTimer.invalidate()
|
||||
self.updateTimer = None
|
||||
|
||||
def refreshLog(self):
|
||||
'''Check for new available data, read it, and scroll to the bottom.'''
|
||||
data = self.fileHandle.availableData()
|
||||
if data.length():
|
||||
utf8string = NSString.alloc().initWithData_encoding_(
|
||||
data, NSUTF8StringEncoding)
|
||||
for line in utf8string.splitlines(True):
|
||||
if line.endswith(u"\n"):
|
||||
self.logFileData.addLine_partial_(line.rstrip(u"\n"), False)
|
||||
else:
|
||||
self.logFileData.addLine_partial_(line, True)
|
||||
self.logView.reloadData()
|
||||
self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1)
|
||||
|
||||
def windowWillClose_(self, notification):
|
||||
'''NSWindow delegate method -- if our window is closing,
|
||||
stop watching the log file.'''
|
||||
self.stopWatching()
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
# MSCMainWindowController.py
|
||||
# Managed Software Center
|
||||
#
|
||||
# Copyright 2013-2014 Greg Neagle.
|
||||
# Copyright 2013-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# MSCStatusController.py
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
+27
-22
@@ -2,22 +2,16 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSUserNotificationAlertStyle</key>
|
||||
<string>alert</string>
|
||||
<key>NSDockTilePlugIn</key>
|
||||
<string>MSCDockTilePlugin.docktileplugin</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.googlecode.munki.ManagedSoftwareCenter</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
@@ -25,21 +19,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.1</string>
|
||||
<string>4.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>GitRevision</key>
|
||||
<string></string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2015 The Munki Project https://github.com/munki/munki</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
@@ -51,5 +33,28 @@
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>GitRevision</key>
|
||||
<string></string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<true/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSDockTilePlugIn</key>
|
||||
<string>MSCDockTilePlugin.docktileplugin</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2016 The Munki Project https://github.com/munki/munki</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSUserNotificationAlertStyle</key>
|
||||
<string>alert</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Created by Greg Neagle on 2/21/14.
|
||||
#
|
||||
# Copyright 2014 Greg Neagle.
|
||||
# Copyright 2014-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -49,7 +49,7 @@ option {
|
||||
}
|
||||
|
||||
body, button, input {
|
||||
font-family: "Helvetica", "Arial";
|
||||
font-family: -apple-system, "Helvetica Neue", "Helvetica", "Arial";
|
||||
font-smooth: always;
|
||||
-webkit-font-smoothing: antialiased
|
||||
}
|
||||
@@ -223,8 +223,8 @@ a {
|
||||
html, body {
|
||||
-webkit-background-size: 36px 635px;
|
||||
background-repeat: repeat-x;
|
||||
background-color: #f5f6f7;
|
||||
font-family: "Lucida Grande", Helvetica, sans-serif;
|
||||
background-color: white;
|
||||
font-family: -apple-system, "Helvetica Neue", Helvetica, sans-serif;
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ div.columns {
|
||||
|
||||
.sidebar {
|
||||
width: 200px;
|
||||
margin-left: 15px
|
||||
margin-left: 26px;
|
||||
}
|
||||
|
||||
.sidebar h1, .main>h1 {
|
||||
@@ -284,15 +284,14 @@ div.columns {
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background-color: #002354;
|
||||
color: transparent;
|
||||
font-family: Helvetica;
|
||||
font-family: -apple-system, "Helvetica Neue", Helvetica;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
line-height: 26px;
|
||||
margin-bottom: 5px;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
text-shadow: rgba(255, 255, 255, .496094) 0 1px 2px;
|
||||
top: -5px
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
footer {
|
||||
@@ -309,7 +308,7 @@ footer {
|
||||
}
|
||||
|
||||
footer div.bottom-links {
|
||||
margin: 0 35px 0 0;
|
||||
margin: 0 0 0 0;
|
||||
border-top: 1px solid #d3d7db;
|
||||
position: relative
|
||||
}
|
||||
@@ -324,7 +323,6 @@ footer div.bottom-links * {
|
||||
color: #4f5f6e;
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
text-shadow: #fff 0 1px 0
|
||||
}
|
||||
|
||||
footer div.bottom-links>span {
|
||||
@@ -333,7 +331,7 @@ footer div.bottom-links>span {
|
||||
|
||||
footer div.bottom-links li a {
|
||||
color: #576982;
|
||||
font-weight: bold
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
footer div.bottom-links>ul>li:not(:last-child):after {
|
||||
@@ -396,14 +394,14 @@ div.select {
|
||||
div.select select {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #fff), to( #eaeaea));
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
color: #626262;
|
||||
font-weight: normal;
|
||||
/*color: #626262;*/
|
||||
outline: 0;
|
||||
line-height: 15px;
|
||||
margin: 0;
|
||||
padding: 0 25px 1px 5px;
|
||||
border: 0;
|
||||
text-shadow: #fff 0 1px 0;
|
||||
/*text-shadow: #fff 0 1px 0;*/
|
||||
-webkit-appearance: none;
|
||||
-webkit-box-shadow: rgba(0, 0, 0, .35) 0 1px 2px, rgba(0, 0, 0, .8) 0 0 1px;
|
||||
box-shadow: rgba(0, 0, 0, .35) 0 1px 2px, rgba(0, 0, 0, .2) 0 0 1px;
|
||||
@@ -443,19 +441,14 @@ div.select:after {
|
||||
}
|
||||
|
||||
div.titled-box {
|
||||
background-color: #fff;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
-webkit-border-radius: 5px;
|
||||
-webkit-box-shadow: rgba(0, 0, 0, .45) 0 1px 3px, rgba(0, 0, 0, .8) 0 0 1px;
|
||||
box-shadow: rgba(0, 0, 0, .45) 0 1px 4px, rgba(0, 0, 0, .2) 0 0 1px;
|
||||
-webkit-background-clip: padding
|
||||
}
|
||||
|
||||
div.titled-box h2 {
|
||||
font: bold 13px 'Lucida Grande';
|
||||
font: normal 14px -apple-system, "Helvetica Neue", 'Helvetica';
|
||||
color: #53565e;
|
||||
padding: 0 10px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -463,9 +456,9 @@ div.titled-box h2 {
|
||||
}
|
||||
|
||||
div.titled-box h2 a {
|
||||
font: bold 12px 'Lucida Grande';
|
||||
font: normal 12px -apple-system, "Helvetica Neue", 'Helvetica';
|
||||
color: #53565e;
|
||||
line-height: 25px
|
||||
line-height: 24px
|
||||
}
|
||||
|
||||
div.titled-box>div.content {
|
||||
@@ -477,11 +470,11 @@ div.titled-box.quick-links>div.content {
|
||||
}
|
||||
|
||||
div.titled-box h2, div.titled-box hr {
|
||||
border-width: 0 0 3px 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div.titled-box hr {
|
||||
margin: 8px 0
|
||||
margin: 14px 0
|
||||
}
|
||||
|
||||
div.titled-box li.popup {
|
||||
@@ -507,7 +500,7 @@ div.titled-box li.link {
|
||||
padding: 0 11px;
|
||||
height: 19px;
|
||||
line-height: 16px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
border-color: transparent;
|
||||
border-width: 1px 0;
|
||||
border-style: solid
|
||||
@@ -523,7 +516,7 @@ div.titled-box li.link.selected {
|
||||
div.titled-box li.link a {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
color: #222;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -539,32 +532,24 @@ body.screen-reader div.chart ol>li div.lockup div.lockup-info>div.multi-button {
|
||||
}
|
||||
|
||||
div.lockup-container, div.container, div.titled-container {
|
||||
background-color: #fff;
|
||||
-webkit-border-radius: 5px;
|
||||
-webkit-box-shadow: rgba(0, 0, 0, .45) 0 1px 3px, rgba(0, 0, 0, .8) 0 0 1px;
|
||||
box-shadow: rgba(0, 0, 0, .45) 0 1px 4px, rgba(0, 0, 0, .2) 0 0 1px;
|
||||
-webkit-background-clip: padding
|
||||
/*-webkit-border-radius: 5px;*/
|
||||
}
|
||||
|
||||
div.lockup-container .title, div.titled-container .title {
|
||||
padding: 0 10px;
|
||||
height: 32px;
|
||||
border-bottom: 1px solid #d7d7d7
|
||||
}
|
||||
|
||||
div.lockup-container .title h2, div.titled-container .title h2 {
|
||||
display: inline-block;
|
||||
font: bold 14px 'Lucida Grande';
|
||||
font: normal 14px -apple-system, "Helvetica Neue", 'Helvetica';
|
||||
color: #53565e;
|
||||
padding: 0 10px 0 0;
|
||||
line-height: 32px
|
||||
}
|
||||
|
||||
div.lockup-container .title, div.titled-container .title, div.titled-box h2 {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
-webkit-border-top-left-radius: 5px;
|
||||
-webkit-border-top-right-radius: 5px;
|
||||
background: -webkit-gradient(linear, left bottom, left top, from( #e3e3e3), color-stop(0.49, #ededed), color-stop(0.5, #f4f4f4), to(#fff))
|
||||
}
|
||||
|
||||
div.titled-box h2 {
|
||||
@@ -572,14 +557,16 @@ div.titled-box h2 {
|
||||
border-bottom: 1px solid #cacaca;
|
||||
border-width: 0 0 1px !important;
|
||||
margin-bottom: 6px;
|
||||
-webkit-border-image: none
|
||||
margin-top: 10px;
|
||||
-webkit-border-image: none;
|
||||
font-size: 13px;
|
||||
line-height: 22px
|
||||
}
|
||||
|
||||
div.lockup-container .title h1 {
|
||||
display: inline-block;
|
||||
font: bold 14px Lucida Grande;
|
||||
font: normal 14px -apple-system, "Helvetica Neue", Helvetica;
|
||||
color: #53565e;
|
||||
padding: 0 10px 0 0;
|
||||
line-height: 32px
|
||||
}
|
||||
|
||||
@@ -591,7 +578,7 @@ div.lockup-container .title h1 a {
|
||||
div.lockup-container .title span {
|
||||
color: #53565e;
|
||||
font-size: 10px;
|
||||
font-weight: bold
|
||||
font-weight: normal
|
||||
}
|
||||
|
||||
div.lockup-container .content-and-controls, div.titled-container .content-and-controls {
|
||||
@@ -604,7 +591,6 @@ div.lockup-container .content-and-controls:before, div.titled-container .content
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: -1px;
|
||||
border-width: 0 0 3px 0;
|
||||
}
|
||||
|
||||
div.lockup-container .content-and-controls:after, div.titled-container .content-and-controls:before {
|
||||
@@ -623,9 +609,6 @@ div.lockup-container div.lockup {
|
||||
float: left;
|
||||
width: 33.3%;
|
||||
height: 98px;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
border-left: 1px solid #fff
|
||||
}
|
||||
|
||||
div.lockup-container div.lockup div.artwork {
|
||||
@@ -673,18 +656,6 @@ div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(3n+2) {
|
||||
width: 33.4%
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+1),
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+2),
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+3) {
|
||||
background: #fff
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+4),
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+5),
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(6n+6) {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #f3f3f3), to( #eaeaea))
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(3n+1) {
|
||||
border-left: 0
|
||||
}
|
||||
@@ -693,43 +664,16 @@ div.lockup-container:not(.shelf)[data-columns-current="3"] div.lockup:nth-of-typ
|
||||
display: table-cell;
|
||||
/*float: none;*/
|
||||
/*height: auto;*/
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="3"] div.lockup:nth-last-of-type(3) {
|
||||
-webkit-border-bottom-left-radius: 5px
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+1),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+2),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+3),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+4) {
|
||||
background: #fff
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+5),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+6),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+7),
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(8n+8) {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #f3f3f3), to( #eaeaea))
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup, div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(3n+2) {
|
||||
width: 25%
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(3n+1) {
|
||||
border-left: 1px solid #fff
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(3n) {
|
||||
border-right: 1px solid #d9d9d9
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(4n+1) {
|
||||
border-left: 0
|
||||
}
|
||||
|
||||
div.lockup-container:not(.shelf)[data-columns-current="4"] div.lockup:nth-of-type(4n) {
|
||||
display: table-cell;
|
||||
/*float: none;
|
||||
@@ -749,41 +693,10 @@ div.lockup-container[data-columns-current="5"] div.lockup, div.lockup-container[
|
||||
width: 20%
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+1),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+2),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+3),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+4),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+5) {
|
||||
background: #fff
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+6),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+7),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+8),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+9),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(10n+10) {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #f3f3f3), to( #eaeaea))
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(3n+1),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(4n+1) {
|
||||
border-left: 1px solid #fff
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(3n),
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(4n) {
|
||||
border-right: 1px solid #d9d9d9
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(5n+1) {
|
||||
border-left: 0
|
||||
}
|
||||
|
||||
div.lockup-container:not(.shelf)[data-columns-current="5"] div.lockup:nth-of-type(5n) {
|
||||
display: table-cell;
|
||||
/*float: none;
|
||||
height: auto;*/
|
||||
border-right: 0
|
||||
}
|
||||
|
||||
div.lockup-container[data-columns-current="5"] div.lockup:nth-last-of-type(5) {
|
||||
@@ -817,7 +730,6 @@ div.lockup ul li {
|
||||
|
||||
div.lockup li.name {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
max-width: 190px
|
||||
}
|
||||
|
||||
@@ -839,8 +751,7 @@ div.lockup-container div.lockup ul li {
|
||||
|
||||
div.lockup-container div.lockup.category ul {
|
||||
margin-top: 6px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 0 #fff
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.lockup-container div.lockup.category li.genre a {
|
||||
@@ -853,13 +764,13 @@ div.msc-button.small {
|
||||
|
||||
div.msc-button-inner {
|
||||
height: 13px;
|
||||
-webkit-border-radius: 10px;
|
||||
background: -webkit-gradient(linear, 0% 0, 0% 100%, from( #A8AAAF), color-stop(0.05, #A6A8AE), color-stop(0.5, #A1A3A9), color-stop(0.96, #9C9EA4), to( #989DA3));
|
||||
-webkit-border-radius: 3px;
|
||||
background: -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(166, 168, 174)), to(rgb(157, 159, 164)));
|
||||
border: 1px solid rgba(136, 136, 138, .746094);
|
||||
text-transform: uppercase;
|
||||
/*text-shadow: rgba(0, 0, 0, .5) 0 -1px 0;*/
|
||||
color: #fff;
|
||||
font: normal normal bold 9px/11px 'Lucida Grande';
|
||||
font: normal normal bold 9px/12px 'Helvetica';
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
@@ -878,7 +789,7 @@ div.msc-button-inner.removing {
|
||||
}
|
||||
|
||||
div.msc-button-inner:not(.installed-not-removable):hover {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #85868d), color-stop(0.05, #84858c), color-stop(0.5, #888188), color-stop(0.96, #7b7c83), to( #7a7bb2))
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #85868d), to( #7a7bb2))
|
||||
}
|
||||
|
||||
div.msc-button button {
|
||||
@@ -901,11 +812,10 @@ div.msc-button-inner.large {
|
||||
font-size: 13px;
|
||||
font-family: "Helvetica";
|
||||
text-transform: capitalize;
|
||||
line-height: 24px;
|
||||
border: 0;
|
||||
line-height: 21px;
|
||||
border: 1px solid rgb(42, 73, 118);
|
||||
color: #fff;
|
||||
text-shadow: rgba(0, 0, 0, .5) 0 -1px 0;
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #367FD4), color-stop(0.50, #2255A0), color-stop(0.49, #2B68B6), to( #234F92));
|
||||
background: -webkit-gradient(linear, left top, left bottom, from(rgb(73,124,205)), to(rgb(47,79,143)));
|
||||
border-color: #1F4A7A;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
@@ -925,7 +835,7 @@ div.msc-button-inner.large.removing {
|
||||
}
|
||||
|
||||
div.msc-button-inner.large:not(.installed-not-removable):hover {
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #0067B8), color-stop(0.49, #00509A), color-stop(0.50, #003E85), to( #003777))
|
||||
background: -webkit-gradient(linear, left top, left bottom, from( #0067B8), to( #003777))
|
||||
}
|
||||
|
||||
div.msc-button.install-updates {
|
||||
@@ -1072,9 +982,9 @@ div.msc-button-inner.install-updates:not(.installed-not-removable):hover {
|
||||
|
||||
div.progress-spinner {
|
||||
background: url(progress-spinner.png) 0 0 no-repeat;
|
||||
-webkit-background-size: 21px 20px;
|
||||
-webkit-background-size: 20px 20px;
|
||||
opacity: 1;
|
||||
width: 21px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-animation-name: 'generic-loading-spinner-animation';
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
@@ -1202,7 +1112,6 @@ div.showcase {
|
||||
overflow: hidden;
|
||||
-webkit-transition: opacity .25s;
|
||||
-webkit-border-bottom-right-radius: 8px;
|
||||
-webkit-box-shadow: 0 1px 3px #999;
|
||||
position: relative;
|
||||
left: 0;
|
||||
margin-right: 0;
|
||||
@@ -1223,7 +1132,7 @@ div.stage>img {
|
||||
position: absolute;
|
||||
height: 200px;
|
||||
left: 0;
|
||||
background-color: #000;
|
||||
/*background-color: #000;*/
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
-webkit-transition: opacity 1s ease-in-out;
|
||||
@@ -1346,9 +1255,9 @@ div.lockup li.removing span.progress-spinner {
|
||||
visibility: visible !important;
|
||||
float: right;
|
||||
background: url(progress-spinner.png) 0 0 no-repeat;
|
||||
-webkit-background-size: 21px 20px;
|
||||
-webkit-background-size: 20px 20px;
|
||||
opacity: 1;
|
||||
width: 21px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-animation-name: 'generic-loading-spinner-animation';
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
@@ -1361,8 +1270,8 @@ div.lockup li.preparing-removal span.progress-spinner,
|
||||
div.lockup li.installing span.progress-spinner,
|
||||
div.lockup li.removing span.progress-spinner {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
div.lockup li {
|
||||
|
||||
@@ -40,11 +40,11 @@ body.product .product-detail .product-info {
|
||||
}
|
||||
body.product .product-detail .product-info .title h1 {
|
||||
margin-bottom: 12px;
|
||||
font-family: Helvetica;
|
||||
font-family: -apple-system, "Helvetica Neue", Helvetica;
|
||||
font-size: 24px;
|
||||
line-height: 22px;
|
||||
font-weight: bold;
|
||||
color: #6d83a2;
|
||||
/*font-weight: bold;*/
|
||||
/*color: #6d83a2;*/
|
||||
text-shadow: rgba(255,255,255,1) 0 1px 0
|
||||
}
|
||||
body.product .product-detail .product-info .title a {
|
||||
@@ -52,7 +52,7 @@ color: currentcolor
|
||||
}
|
||||
body.product .product-detail .product-info .product-review h4 {
|
||||
margin-bottom: 4px;
|
||||
font-family: Helvetica;
|
||||
font-family: -apple-system, "Helvetica Neue", Helvetica;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #6d83a2;
|
||||
@@ -63,7 +63,6 @@ font-size: 12px;
|
||||
line-height: 16px
|
||||
}
|
||||
body.product .sidebar .app-info .content {
|
||||
padding: 0 10px;
|
||||
font-size: 11px;
|
||||
color: #000
|
||||
}
|
||||
@@ -72,7 +71,7 @@ margin-bottom: 5px
|
||||
}
|
||||
body.product .sidebar .app-info li .label,
|
||||
body.product .sidebar .app-info p span.label {
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
color: #787878
|
||||
}
|
||||
body.product .sidebar .more-by {
|
||||
@@ -85,7 +84,6 @@ body.product .sidebar .more-by div.lockup {
|
||||
display: -webkit-box;
|
||||
padding: 7px 10px;
|
||||
border-width: 0 0 1px 0;
|
||||
background-image: -webkit-gradient(linear,left top,left bottom,from(#fff),to( #f9f9f9));
|
||||
height: 55px
|
||||
}
|
||||
body.product .sidebar .more-by div.lockup:last-child {
|
||||
@@ -115,7 +113,7 @@ overflow: hidden
|
||||
body.product .sidebar .more-by div.lockup li.name {
|
||||
margin-bottom: 1px;
|
||||
font-size: 11px;
|
||||
font-weight: bold
|
||||
/*font-weight: bold*/
|
||||
}
|
||||
body.product .sidebar .more-by div.lockup li.genre {
|
||||
color: #6c6c6c;
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.3 KiB |
+21
-26
@@ -22,6 +22,9 @@
|
||||
.main>div:not(:last-of-type) {
|
||||
margin-bottom: 20px
|
||||
}
|
||||
div.status-results {
|
||||
border-bottom: 1px solid #d9d9d9
|
||||
}
|
||||
div.installations {
|
||||
margin: 14px 0;
|
||||
position: relative
|
||||
@@ -41,19 +44,15 @@ div.installations table:not(.no-header):before {
|
||||
}
|
||||
div.installations thead {
|
||||
border-radius: 5px 5px 0 0;
|
||||
padding: 0 0 0 10px;
|
||||
height: 32px;
|
||||
border-bottom: 1px solid #d7d7d7;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
background: -webkit-gradient(linear,left bottom,left top,from( #e3e3e3),color-stop(0.49, #ededed),color-stop(0.5, #f4f4f4),to(#fff))
|
||||
}
|
||||
div.installations th {
|
||||
color: #53565e;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
height: 32px;
|
||||
line-height: 14px;
|
||||
padding: 0 10px;
|
||||
text-align: left
|
||||
}
|
||||
#updates div.installations th {
|
||||
@@ -63,7 +62,13 @@ div.installations th {
|
||||
}
|
||||
|
||||
#update-count-string {
|
||||
margin-left: -10px
|
||||
/*margin-left: -10px*/
|
||||
}
|
||||
|
||||
#updates-progress-spinner {
|
||||
margin-right: 4px;
|
||||
position: relative;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
div.installations th:first-child {
|
||||
@@ -72,9 +77,6 @@ div.installations th:first-child {
|
||||
div.installations th:last-child {
|
||||
-webkit-border-top-right-radius: 5px
|
||||
}
|
||||
div.installations tbody tr:nth-of-type(even) {
|
||||
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
|
||||
}
|
||||
.installation td {
|
||||
border-top: 1px solid #fff;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
@@ -83,6 +85,9 @@ div.installations tbody tr:nth-of-type(even) {
|
||||
.installation td,.installation td a {
|
||||
color: #494949
|
||||
}
|
||||
.installation td.install-info-cell,.installation td.install-info-cell a, li.install-info-cell {
|
||||
color: rgb(121,121,121)
|
||||
}
|
||||
.installation:first-child td {
|
||||
border-top: transparent
|
||||
}
|
||||
@@ -100,7 +105,7 @@ div.installations tbody tr:nth-of-type(even) {
|
||||
.installation h2 {
|
||||
color: #565656;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 300px
|
||||
@@ -133,7 +138,7 @@ div.installations tbody tr:nth-of-type(even) {
|
||||
-webkit-box-shadow: none
|
||||
}
|
||||
#other-updates #os-updates,#os-and-app-updates #os-updates {
|
||||
border-bottom: 1px solid #d9d9d9
|
||||
/*border-bottom: 1px solid #d9d9d9*/
|
||||
}
|
||||
#other-updates #os-updatesth:last-child,#os-and-app-updates #os-updates th:last-child {
|
||||
width: 100%;
|
||||
@@ -148,29 +153,21 @@ div.installations tbody tr:nth-of-type(even) {
|
||||
top: 6px;
|
||||
left: 0
|
||||
}
|
||||
#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(even) {
|
||||
background: 0
|
||||
}
|
||||
#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(odd) {
|
||||
background: -webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade-out-and-remove {
|
||||
0% {opacity: 1}
|
||||
100% {opacity: 0}
|
||||
}
|
||||
|
||||
#purchases {
|
||||
text-shadow:#fff 0 1px 0
|
||||
}
|
||||
div.purchases .installation td {
|
||||
height: 41px
|
||||
}
|
||||
div.purchases .installation:last-child td:first-child {
|
||||
/*div.purchases .installation:last-child td:first-child {
|
||||
-webkit-border-bottom-left-radius: 5px
|
||||
}
|
||||
div.purchases .installation:last-child td:last-child {
|
||||
-webkit-border-bottom-right-radius: 5px
|
||||
}
|
||||
}*/
|
||||
div.purchases .installation td:first-child {
|
||||
width: 225px
|
||||
}
|
||||
@@ -238,9 +235,8 @@ div.purchases .installation td.status span.preparing-removal
|
||||
}
|
||||
#updates #header h1 {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
font-weight: normal;
|
||||
color: #929292;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
text-align: center
|
||||
}
|
||||
div.updates table {
|
||||
@@ -385,13 +381,12 @@ div.os-updates.expanded tbody tr:nth-child(n+3):before {
|
||||
top: 0;
|
||||
right: 100px;
|
||||
left: 240px;
|
||||
background: -webkit-linear-gradient(left,rgba(0,0,0,.04) 0,rgba(0,0,0,.16) 20%,rgba(0,0,0,.16) 80%,rgba(0,0,0,.04) 100%)
|
||||
}
|
||||
div.os-updates.expanded tbody tr.sosumi:before {
|
||||
display: none
|
||||
}
|
||||
.activity-indicator {
|
||||
font: bold 20px Helvetica
|
||||
font: normal 20px -apple-system, "Helvetica Neue", Helvetica
|
||||
}
|
||||
#updates #header .scan-progress {
|
||||
padding: 0 0 1px 13px
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Opdateringer";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Vis log";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Frem";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Geführte Softwareaktualisierung";
|
||||
"CFBundleDisplayName" = "Geführte Softwareaktualisierung";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Updates";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Protokoll einblenden";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Weiter";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -1,9 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7531" systemVersion="14D136" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment version="1060" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7531"/>
|
||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="7531"/>
|
||||
<development version="6300" identifier="xcode"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
|
||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="10116"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
|
||||
@@ -182,6 +183,12 @@
|
||||
<action selector="toggleFullScreen:" target="-1" id="3pL-iV-Ndq"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="lBR-K5-svb"/>
|
||||
<menuItem title="Show Log" keyEquivalent="l" id="vPs-dO-LDa">
|
||||
<connections>
|
||||
<action selector="showLogWindow:" target="a6z-Lq-EqL" id="sqE-gM-htm"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
@@ -242,7 +249,7 @@
|
||||
<size key="minSize" width="48" height="30"/>
|
||||
<size key="maxSize" width="48" height="30"/>
|
||||
<button key="view" id="YGi-pE-3SV" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="8" y="14" width="48" height="30"/>
|
||||
<rect key="frame" x="9" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="toolbarCategoriesTemplate" imagePosition="only" alignment="center" inset="2" id="Vtx-wt-JLV" customClass="MSCToolbarButtonCell">
|
||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||
@@ -274,7 +281,7 @@
|
||||
<size key="minSize" width="48" height="30"/>
|
||||
<size key="maxSize" width="48" height="30"/>
|
||||
<button key="view" id="caG-fL-UJh" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="1" y="14" width="48" height="30"/>
|
||||
<rect key="frame" x="2" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="updatesTemplate" imagePosition="overlaps" alignment="center" inset="2" id="ZVD-Hl-dlF" customClass="MSCToolbarButtonCell">
|
||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||
@@ -290,7 +297,7 @@
|
||||
<size key="minSize" width="4" height="30"/>
|
||||
<size key="maxSize" width="4" height="31"/>
|
||||
<customView key="view" id="Mui-Sc-36b">
|
||||
<rect key="frame" x="38" y="14" width="4" height="30"/>
|
||||
<rect key="frame" x="39" y="14" width="4" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
</toolbarItem>
|
||||
@@ -305,12 +312,12 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="150" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<progressIndicator horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="mXP-yD-2El">
|
||||
<rect key="frame" x="62" y="2" width="16" height="16"/>
|
||||
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="mXP-yD-2El">
|
||||
<rect key="frame" x="62" y="3" width="16" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</progressIndicator>
|
||||
<segmentedControl verticalHuggingPriority="750" id="jcc-R8-zGb">
|
||||
<rect key="frame" x="1" y="-2" width="53" height="25"/>
|
||||
<rect key="frame" x="1" y="-1" width="53" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="ZsM-fh-8z5">
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -326,7 +333,7 @@
|
||||
</subviews>
|
||||
</view>
|
||||
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</box>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="E1A9A599-F94D-4780-9330-F2B0F75FA3BA" label="" paletteLabel="Search" tag="-1" id="fbJ-cF-weR">
|
||||
@@ -341,7 +348,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<searchField wantsLayer="YES" verticalHuggingPriority="750" id="gLI-O8-siB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="150" height="22"/>
|
||||
<rect key="frame" x="0.0" y="1" width="150" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsWholeSearchString="YES" id="Rxm-nO-bmO">
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -355,7 +362,7 @@
|
||||
</subviews>
|
||||
</view>
|
||||
<color key="borderColor" white="0.0" alpha="0.41999999999999998" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<color key="fillColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</box>
|
||||
</toolbarItem>
|
||||
</allowedToolbarItems>
|
||||
@@ -409,7 +416,105 @@
|
||||
<outlet property="windowMenuSeperatorItem" destination="92" id="eCt-83-MtH"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="a6z-Lq-EqL" customClass="MSCLogWindowController">
|
||||
<connections>
|
||||
<outlet property="logView" destination="UAy-M3-yRr" id="cvs-RK-Mg2"/>
|
||||
<outlet property="pathControl" destination="WBU-xF-NX3" id="90X-cm-AqK"/>
|
||||
<outlet property="searchField" destination="N3t-et-cTX" id="4hW-Ue-VWp"/>
|
||||
<outlet property="window" destination="X5I-7f-aJs" id="2H4-ci-ApV"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<userDefaultsController representsSharedInstance="YES" id="602"/>
|
||||
<window title="Log" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="X5I-7f-aJs" userLabel="Log Window">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="636" y="390" width="512" height="480"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<value key="minSize" type="size" width="512" height="360"/>
|
||||
<view key="contentView" id="dBq-Rn-ing">
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="Kh7-rR-yrV">
|
||||
<rect key="frame" x="-1" y="-1" width="514" height="449"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" id="rwq-IA-IvG">
|
||||
<rect key="frame" x="1" y="1" width="512" height="447"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" id="UAy-M3-yRr">
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="spacer" editable="NO" width="10" minWidth="8" maxWidth="3.4028234663852886e+38" id="BrL-gn-tp4">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" placeholderString="" id="a1e-6Q-iNS">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="data" editable="NO" width="502" minWidth="40" maxWidth="2560" id="LA1-bD-SzC">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="charWrapping" selectable="YES" allowsUndo="NO" alignment="left" title="Text Cell" id="Rl8-0j-Dpu">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="jbz-w3-Ald">
|
||||
<rect key="frame" x="1" y="427" width="630" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="050-d8-vNg">
|
||||
<rect key="frame" x="-100" y="-100" width="15" height="303"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<searchField wantsLayer="YES" verticalHuggingPriority="750" id="N3t-et-cTX">
|
||||
<rect key="frame" x="301" y="452" width="200" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="Nc2-ny-iaV">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</searchFieldCell>
|
||||
<connections>
|
||||
<action selector="searchFilterChanged:" target="a6z-Lq-EqL" id="bVX-7y-fs3"/>
|
||||
</connections>
|
||||
</searchField>
|
||||
<pathControl verticalHuggingPriority="750" allowsExpansionToolTips="YES" id="WBU-xF-NX3">
|
||||
<rect key="frame" x="4" y="452" width="278" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="gwm-XM-tLs">
|
||||
<font key="font" metaFont="system"/>
|
||||
<url key="url" string="file://localhost/Applications/"/>
|
||||
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</pathCell>
|
||||
</pathControl>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="a6z-Lq-EqL" id="BQa-PG-rdk"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="68" y="887"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="AllItemsTemplate" width="28" height="26"/>
|
||||
@@ -417,6 +522,6 @@
|
||||
<image name="NSGoLeftTemplate" width="9" height="12"/>
|
||||
<image name="NSGoRightTemplate" width="9" height="12"/>
|
||||
<image name="toolbarCategoriesTemplate" width="25" height="24"/>
|
||||
<image name="updatesTemplate" width="28" height="27.5"/>
|
||||
<image name="updatesTemplate" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Centre";
|
||||
"CFBundleDisplayName" = "Managed Software Centre";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Centre";
|
||||
"CFBundleDisplayName" = "Managed Software Centre";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Centre";
|
||||
"CFBundleDisplayName" = "Managed Software Centre";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Centro de aplicaciones";
|
||||
"CFBundleDisplayName" = "Centro de aplicaciones";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Actualizaciones";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Mostrar registro";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Siguiente";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Päivitykset";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Näytä loki";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Eteenpäin";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Centre de gestion des logiciels";
|
||||
"CFBundleDisplayName" = "Centre de gestion des logiciels";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Mises à jour";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Afficher l’historique";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Suivant";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Centro Gestione Applicazioni";
|
||||
"CFBundleDisplayName" = "Centro Gestione Applicazioni";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Aggiornamenti";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Mostra log";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Avanti";
|
||||
|
||||
@@ -134,5 +134,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "アップデート";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "ログを表示";
|
||||
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "次へ";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// main.m
|
||||
// Managed Software Center
|
||||
//
|
||||
// Copyright 2013-2014 Greg Neagle.
|
||||
// Copyright 2013-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -16,6 +16,7 @@ from PyObjCTools import AppHelper
|
||||
import MSCAppDelegate
|
||||
import MSCMainWindowController
|
||||
import MSCStatusController
|
||||
import MSCLogWindowController
|
||||
import MSCToolbar
|
||||
|
||||
# get more debugging info on exceptions
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# msclib.py
|
||||
#
|
||||
# Created by Greg Neagle on 12/10/13.
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Created by Greg Neagle on 2/23/14.
|
||||
# Original by John Randolph <jrand@google.com>
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Managed Software Center
|
||||
#
|
||||
# Created by Greg Neagle on 2/11/10.
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Oppdateringer";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Vis logg";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Fram";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Updates";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Toon logbestand";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Volgende";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Центр Управления ПО";
|
||||
"CFBundleDisplayName" = "Центр Управления ПО";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Обновления";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Показать журнал";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Вперед";
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Managed Software Center";
|
||||
"CFBundleDisplayName" = "Managed Software Center";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -146,5 +146,8 @@
|
||||
/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */
|
||||
"tv9-wZ-XWN.title" = "Uppdateringar";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */
|
||||
"vPs-dO-LDa.title" = "Visa logg";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */
|
||||
"z4Z-vu-XGX.title" = "Framåt";
|
||||
|
||||
+4
-4
@@ -7,12 +7,12 @@
|
||||
</a>
|
||||
<ul class="list info">
|
||||
<li><h2><a title="${display_name_escaped}" href="${detail_link}">${display_name_escaped}</a></h2></li>
|
||||
<li>${developer_escaped}</li>
|
||||
<li class="install-info-cell">${developer_escaped}</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>${version_to_install}</td>
|
||||
<td>${size}</td>
|
||||
<td class="status">
|
||||
<td class="install-info-cell">${version_to_install}</td>
|
||||
<td class="install-info-cell">${size}</td>
|
||||
<td class="status install-info-cell">
|
||||
<span class="${status}" id="${name_escaped}_status_text">
|
||||
${status_text}
|
||||
<a class="follow" href="updates.html">
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.2 KiB |
@@ -10,7 +10,6 @@
|
||||
C002ECE51913F6D6003DD155 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C002ECE21913F6D6003DD155 /* Localizable.strings */; };
|
||||
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */ = {isa = PBXBuildFile; fileRef = C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */; };
|
||||
C035275018A9C5BC004A5AE4 /* libpython2.6.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C035274F18A9C5BC004A5AE4 /* libpython2.6.dylib */; };
|
||||
C046261B1A00015600AF1E48 /* MunkiStatus-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C09004FB16CDD84E00BE34CE /* MunkiStatus-Info.plist */; };
|
||||
C046261E1A00019800AF1E48 /* MainMenu.strings in Resources */ = {isa = PBXBuildFile; fileRef = C002ECE01913F6D6003DD155 /* MainMenu.strings */; };
|
||||
C05C3CEF188391F200095E65 /* munki.py in Resources */ = {isa = PBXBuildFile; fileRef = C05C3CEE188391F200095E65 /* munki.py */; };
|
||||
C09004F216CDD84E00BE34CE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C09004F116CDD84E00BE34CE /* Cocoa.framework */; };
|
||||
@@ -23,6 +22,7 @@
|
||||
C094B6D61891826700E06897 /* BorderlessWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C094B6D31891826700E06897 /* BorderlessWindow.m */; };
|
||||
C094B6D71891826700E06897 /* ScaledImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = C094B6D51891826700E06897 /* ScaledImageView.m */; };
|
||||
C0AE8658186D2DF900C87AE7 /* MunkiStatus.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */; };
|
||||
C0D67B581CC55BFD009E8C2F /* MSULogWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
@@ -57,6 +57,7 @@
|
||||
C046261C1A00016200AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C046261D1A00019800AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
C05C3CEE188391F200095E65 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = munki.py; path = MunkiStatus/munki.py; sourceTree = SOURCE_ROOT; };
|
||||
C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BorderlessWindow.h; sourceTree = "<group>"; };
|
||||
C07E956C1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
C07E956D1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
C07E956E1913ECF400B40B9A /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@@ -91,6 +92,7 @@
|
||||
C094B6D41891826700E06897 /* ScaledImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScaledImageView.h; sourceTree = "<group>"; };
|
||||
C094B6D51891826700E06897 /* ScaledImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScaledImageView.m; sourceTree = "<group>"; };
|
||||
C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = MunkiStatus.icns; sourceTree = "<group>"; };
|
||||
C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSULogWindowController.py; sourceTree = "<group>"; };
|
||||
E65810B31993C96E00E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
E65810B51993C97500E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/MainMenu.strings; sourceTree = "<group>"; };
|
||||
E65810B61993C97C00E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -157,6 +159,7 @@
|
||||
C09004F916CDD84E00BE34CE /* MunkiStatus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */,
|
||||
C094B6D31891826700E06897 /* BorderlessWindow.m */,
|
||||
C094B6D41891826700E06897 /* ScaledImageView.h */,
|
||||
C094B6D51891826700E06897 /* ScaledImageView.m */,
|
||||
@@ -169,6 +172,7 @@
|
||||
C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */,
|
||||
C090050916CDD84E00BE34CE /* MainMenu.xib */,
|
||||
C09004FA16CDD84E00BE34CE /* Supporting Files */,
|
||||
C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */,
|
||||
);
|
||||
path = MunkiStatus;
|
||||
sourceTree = "<group>";
|
||||
@@ -288,7 +292,6 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C09004FE16CDD84E00BE34CE /* InfoPlist.strings in Resources */,
|
||||
C046261B1A00015600AF1E48 /* MunkiStatus-Info.plist in Resources */,
|
||||
C090050616CDD84E00BE34CE /* main.py in Resources */,
|
||||
C090050816CDD84E00BE34CE /* MSUAppDelegate.py in Resources */,
|
||||
C094B6D1188F7CE100E06897 /* MSUStatusWindowController.py in Resources */,
|
||||
@@ -298,6 +301,7 @@
|
||||
C090050B16CDD84E00BE34CE /* MainMenu.xib in Resources */,
|
||||
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */,
|
||||
C0AE8658186D2DF900C87AE7 /* MunkiStatus.icns in Resources */,
|
||||
C0D67B581CC55BFD009E8C2F /* MSULogWindowController.py in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// BorderlessWindow.h
|
||||
//
|
||||
// Created by Greg Neagle on 5/16/09.
|
||||
// Copyright 2009-2014 Greg Neagle.
|
||||
// Copyright 2009-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// BorderlessWindow.m
|
||||
//
|
||||
// Created by Greg Neagle on 5/16/09.
|
||||
// Copyright 2009-2014 Greg Neagle.
|
||||
// Copyright 2009-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# MSUAppDelegate.py
|
||||
# MunkiStatus
|
||||
#
|
||||
# Copyright 2013-2014 Greg Neagle.
|
||||
# Copyright 2013-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,7 @@ class MSUAppDelegate(NSObject):
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
statusWindowController = IBOutlet()
|
||||
logWindowController = IBOutlet()
|
||||
|
||||
def applicationWillFinishLaunching_(self, sender):
|
||||
'''NSApplicationDelegate method
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# MSULogWindowController.py
|
||||
# MunkiStatus
|
||||
#
|
||||
# Created by Greg Neagle on 4/18/16.
|
||||
# Copyright (c) 2016 Munki Project. All rights reserved.
|
||||
#
|
||||
# Much code borrowed from https://github.com/MagerValp/LoginLog
|
||||
# with the blessing of MagerValp
|
||||
#
|
||||
|
||||
from objc import YES, NO, IBAction, IBOutlet
|
||||
# pylint: disable=wildcard-import
|
||||
# pylint: disable=unused-wildcard-import
|
||||
# pylint: disable=redefined-builtin
|
||||
from Foundation import *
|
||||
from AppKit import *
|
||||
# pylint: enable=redefined-builtin
|
||||
# pylint: enable=wildcard-import
|
||||
|
||||
import munki
|
||||
import os
|
||||
|
||||
# lots of camelCase names, following Cocoa convention
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
|
||||
class MSULogViewDataSource(NSObject):
|
||||
"""Data source for an NSTableView that displays an array of text lines.
|
||||
Line breaks are assumed to be LF, and partial lines from incremental
|
||||
reading is handled."""
|
||||
|
||||
# since this subclasses NSObject,
|
||||
# it doesn't have a Python __init__method
|
||||
# pylint: disable=no-init
|
||||
|
||||
logFileData = NSMutableArray.alloc().init()
|
||||
filteredData = logFileData
|
||||
|
||||
lastLineIsPartial = False
|
||||
filterText = ''
|
||||
|
||||
def tableView_writeRowsWithIndexes_toPasteboard_(
|
||||
self, aTableView, rowIndexes, pasteboard):
|
||||
'''Implements drag-n-drop of text rows to external apps'''
|
||||
text_to_copy = ''
|
||||
index_set = aTableView.selectedRowIndexes()
|
||||
index = index_set.firstIndex()
|
||||
while index != NSNotFound:
|
||||
line = self.filteredData.objectAtIndex_(index)
|
||||
text_to_copy += line + '\n'
|
||||
index = index_set.indexGreaterThanIndex_(index)
|
||||
#changeCount = pasteboard.clearContents()
|
||||
result = pasteboard.writeObjects_([text_to_copy])
|
||||
return YES
|
||||
|
||||
def applyFilterToData(self):
|
||||
'''Filter our log data'''
|
||||
if len(self.filterText):
|
||||
filterPredicate = NSPredicate.predicateWithFormat_(
|
||||
'self CONTAINS[cd] %@', self.filterText)
|
||||
self.filteredData = (
|
||||
self.logFileData.filteredArrayUsingPredicate_(filterPredicate))
|
||||
else:
|
||||
self.filteredData = self.logFileData
|
||||
|
||||
def addLine_partial_(self, line, isPartial):
|
||||
'''Add a line to our datasource'''
|
||||
if self.lastLineIsPartial:
|
||||
joinedLine = self.logFileData.lastObject() + line
|
||||
self.logFileData.removeLastObject()
|
||||
self.logFileData.addObject_(joinedLine)
|
||||
else:
|
||||
self.logFileData.addObject_(line)
|
||||
self.lastLineIsPartial = isPartial
|
||||
self.applyFilterToData()
|
||||
|
||||
def removeAllLines(self):
|
||||
'''Remove all data from our datasource'''
|
||||
self.logFileData.removeAllObjects()
|
||||
|
||||
def lineCount(self):
|
||||
'''Return the number of lines in our filtered data'''
|
||||
return self.filteredData.count()
|
||||
|
||||
def numberOfRowsInTableView_(self, tableView):
|
||||
'''Required datasource method'''
|
||||
return self.lineCount()
|
||||
|
||||
def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
|
||||
'''Required datasource method -- returns the text data for the
|
||||
given row and column'''
|
||||
if column.identifier() == 'data':
|
||||
return self.filteredData.objectAtIndex_(row)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
class MSULogWindowController(NSObject):
|
||||
'''Controller object for our log window'''
|
||||
|
||||
# since this subclasses NSObject,
|
||||
# it doesn't have a Python __init__method
|
||||
# pylint: disable=no-init
|
||||
|
||||
window = IBOutlet()
|
||||
logView = IBOutlet()
|
||||
searchField = IBOutlet()
|
||||
pathControl = IBOutlet()
|
||||
|
||||
logFileData = MSULogViewDataSource.alloc().init()
|
||||
|
||||
fileHandle = None
|
||||
updateTimer = None
|
||||
|
||||
def copy_(self, sender):
|
||||
'''Implements copy operation so we can copy data from table view'''
|
||||
text_to_copy = ''
|
||||
index_set = self.logView.selectedRowIndexes()
|
||||
index = index_set.firstIndex()
|
||||
while index != NSNotFound:
|
||||
line = self.logFileData.filteredData.objectAtIndex_(index)
|
||||
text_to_copy += line + '\n'
|
||||
index = index_set.indexGreaterThanIndex_(index)
|
||||
pasteboard = NSPasteboard.generalPasteboard()
|
||||
changeCount = pasteboard.clearContents()
|
||||
result = pasteboard.writeObjects_([text_to_copy])
|
||||
|
||||
@IBAction
|
||||
def searchFilterChanged_(self, sender):
|
||||
'''User changed the search field'''
|
||||
filterString = self.searchField.stringValue().lower()
|
||||
self.logFileData.filterText = filterString
|
||||
self.logFileData.applyFilterToData()
|
||||
self.logView.reloadData()
|
||||
|
||||
def getWindowLevel(self):
|
||||
'''Gets our NSWindowLevel. Works around issues with the loginwindow
|
||||
PolicyBanner in 10.11+ Some code based on earlier work by Pepijn
|
||||
Bruienne'''
|
||||
window_level = NSScreenSaverWindowLevel - 1
|
||||
# Get our Darwin major version
|
||||
darwin_vers = int(os.uname()[2].split('.')[0])
|
||||
have_policy_banner = False
|
||||
for test_file in ['/Library/Security/PolicyBanner.txt',
|
||||
'/Library/Security/PolicyBanner.rtf',
|
||||
'/Library/Security/PolicyBanner.rtfd']:
|
||||
if os.path.exists(test_file):
|
||||
have_policy_banner = True
|
||||
break
|
||||
# bump our NSWindowLevel if we have a PolicyBanner in ElCap+
|
||||
if have_policy_banner and darwin_vers > 14:
|
||||
window_level = NSScreenSaverWindowLevel
|
||||
return window_level
|
||||
|
||||
@IBAction
|
||||
def showLogWindow_(self, notification):
|
||||
'''Show the log window.'''
|
||||
|
||||
if self.window.isVisible():
|
||||
# It's already open, just move it to front
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
return
|
||||
|
||||
consoleuser = munki.getconsoleuser()
|
||||
if consoleuser == None or consoleuser == u"loginwindow":
|
||||
self.window.setCanBecomeVisibleWithoutLogin_(True)
|
||||
self.window.setLevel_(self.getWindowLevel())
|
||||
|
||||
screenRect = NSScreen.mainScreen().frame()
|
||||
windowRect = screenRect.copy()
|
||||
windowRect.origin.x = 100.0
|
||||
windowRect.origin.y = 200.0
|
||||
windowRect.size.width -= 200.0
|
||||
windowRect.size.height -= 300.0
|
||||
|
||||
logfile = munki.pref('LogFile')
|
||||
self.pathControl.setURL_(NSURL.fileURLWithPath_(logfile))
|
||||
self.window.setTitle_(os.path.basename(logfile))
|
||||
self.window.setFrame_display_(windowRect, NO)
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
self.watchLogFile_(logfile)
|
||||
|
||||
# allow dragging from table view to outside of the app
|
||||
self.logView.setDraggingSourceOperationMask_forLocal_(
|
||||
NSDragOperationAll, NO)
|
||||
|
||||
def watchLogFile_(self, logFile):
|
||||
'''Display and continuously update a log file in the main window.'''
|
||||
self.stopWatching()
|
||||
self.logFileData.removeAllLines()
|
||||
self.logView.setDataSource_(self.logFileData)
|
||||
self.logView.reloadData()
|
||||
self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile)
|
||||
self.refreshLog()
|
||||
# Kick off a timer that updates the log view periodically.
|
||||
self.updateTimer = (
|
||||
NSTimer.
|
||||
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
0.25, self, self.refreshLog, None, YES))
|
||||
|
||||
def stopWatching(self):
|
||||
'''Release the file handle and stop the update timer.'''
|
||||
if self.fileHandle is not None:
|
||||
self.fileHandle.closeFile()
|
||||
self.fileHandle = None
|
||||
if self.updateTimer is not None:
|
||||
self.updateTimer.invalidate()
|
||||
self.updateTimer = None
|
||||
|
||||
def refreshLog(self):
|
||||
'''Check for new available data, read it, and scroll to the bottom.'''
|
||||
data = self.fileHandle.availableData()
|
||||
if data.length():
|
||||
utf8string = NSString.alloc().initWithData_encoding_(
|
||||
data, NSUTF8StringEncoding)
|
||||
for line in utf8string.splitlines(True):
|
||||
if line.endswith(u"\n"):
|
||||
self.logFileData.addLine_partial_(line.rstrip(u"\n"), False)
|
||||
else:
|
||||
self.logFileData.addLine_partial_(line, True)
|
||||
self.logView.reloadData()
|
||||
self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1)
|
||||
|
||||
def windowWillClose_(self, notification):
|
||||
'''NSWindow delegate method -- if our window is closing,
|
||||
stop watching the log file.'''
|
||||
self.stopWatching()
|
||||
@@ -3,7 +3,7 @@
|
||||
# MSUStatusWindowController.py
|
||||
# MunkiStatus
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ from objc import YES, NO, IBAction, IBOutlet, nil
|
||||
from PyObjCTools import AppHelper
|
||||
|
||||
import os
|
||||
|
||||
import munki
|
||||
import FoundationPlist
|
||||
|
||||
@@ -69,6 +70,7 @@ class MSUStatusWindowController(NSObject):
|
||||
# pylint: disable=no-init
|
||||
|
||||
window = IBOutlet()
|
||||
logWindow = IBOutlet()
|
||||
messageFld = IBOutlet()
|
||||
detailFld = IBOutlet()
|
||||
progressIndicator = IBOutlet()
|
||||
@@ -86,6 +88,8 @@ class MSUStatusWindowController(NSObject):
|
||||
timeout_counter = 0
|
||||
saw_process = False
|
||||
managedsoftwareupdate_pid = None
|
||||
window_level = NSScreenSaverWindowLevel - 1
|
||||
|
||||
|
||||
@IBAction
|
||||
def stopBtnClicked_(self, sender):
|
||||
@@ -146,43 +150,63 @@ class MSUStatusWindowController(NSObject):
|
||||
NSLog('managedsoftwareupdate pid %s ended'
|
||||
% notification.userInfo().get('pid'))
|
||||
|
||||
def haveElCapPolicyBanner(self):
|
||||
'''Returns True if we are running El Cap or later and there is
|
||||
a loginwindow PolicyBanner in place'''
|
||||
# Get our Darwin major version
|
||||
darwin_vers = int(os.uname()[2].split('.')[0])
|
||||
if darwin_vers > 14:
|
||||
for test_file in ['/Library/Security/PolicyBanner.txt',
|
||||
'/Library/Security/PolicyBanner.rtf',
|
||||
'/Library/Security/PolicyBanner.rtfd']:
|
||||
if os.path.exists(test_file):
|
||||
return True
|
||||
return False
|
||||
|
||||
def setWindowLevel(self):
|
||||
'''Sets our NSWindowLevel. Works around issues with the loginwindow
|
||||
PolicyBanner in 10.11+ Some code based on earlier work by Pepijn
|
||||
Bruienne'''
|
||||
# bump our NSWindowLevel if we have a PolicyBanner in ElCap+
|
||||
if self.haveElCapPolicyBanner():
|
||||
NSLog('El Capitan+ loginwindow PolicyBanner found')
|
||||
self.window_level = NSScreenSaverWindowLevel
|
||||
|
||||
def initStatusSession(self):
|
||||
'''Initialize our status session'''
|
||||
self.setWindowLevel()
|
||||
consoleuser = munki.getconsoleuser()
|
||||
if consoleuser == None or consoleuser == u"loginwindow":
|
||||
self.displayBackdropWindow()
|
||||
# needed so the window can show over the loginwindow
|
||||
self.window.setCanBecomeVisibleWithoutLogin_(True)
|
||||
self.window.setLevel_(self.window_level)
|
||||
|
||||
if self.window:
|
||||
if consoleuser == None or consoleuser == u"loginwindow":
|
||||
# needed so the window can show over the loginwindow
|
||||
self.window.setCanBecomeVisibleWithoutLogin_(True)
|
||||
self.window.setLevel_(NSScreenSaverWindowLevel - 1)
|
||||
self.window.center()
|
||||
self.messageFld.setStringValue_(
|
||||
NSLocalizedString(u"Starting…", None))
|
||||
self.detailFld.setStringValue_(u"")
|
||||
self.stopBtn.setHidden_(False)
|
||||
self.stopBtn.setEnabled_(True)
|
||||
self.stopBtnState = 0
|
||||
if self.imageFld:
|
||||
theImage = NSImage.imageNamed_("MunkiStatus")
|
||||
self.imageFld.setImage_(theImage)
|
||||
if self.progressIndicator:
|
||||
self.progressIndicator.setMinValue_(0.0)
|
||||
self.progressIndicator.setMaxValue_(100.0)
|
||||
self.progressIndicator.setIndeterminate_(True)
|
||||
self.progressIndicator.setUsesThreadedAnimation_(True)
|
||||
self.progressIndicator.startAnimation_(self)
|
||||
self.window.orderFrontRegardless()
|
||||
self.registerForNotifications()
|
||||
# start our process monitor timer so we can be notified about
|
||||
# process failure
|
||||
self.timeout_counter = 6
|
||||
self.saw_process = False
|
||||
# pylint: disable=line-too-long
|
||||
self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
5.0, self, self.checkProcess, None, YES)
|
||||
# pylint: enable=line-too-long
|
||||
self.window.center()
|
||||
self.messageFld.setStringValue_(
|
||||
NSLocalizedString(u"Starting…", None))
|
||||
self.detailFld.setStringValue_(u"")
|
||||
self.stopBtn.setHidden_(False)
|
||||
self.stopBtn.setEnabled_(True)
|
||||
self.stopBtnState = 0
|
||||
if self.imageFld:
|
||||
theImage = NSImage.imageNamed_("MunkiStatus")
|
||||
self.imageFld.setImage_(theImage)
|
||||
if self.progressIndicator:
|
||||
self.progressIndicator.setMinValue_(0.0)
|
||||
self.progressIndicator.setMaxValue_(100.0)
|
||||
self.progressIndicator.setIndeterminate_(True)
|
||||
self.progressIndicator.setUsesThreadedAnimation_(True)
|
||||
self.progressIndicator.startAnimation_(self)
|
||||
self.window.orderFrontRegardless()
|
||||
self.registerForNotifications()
|
||||
# start our process monitor timer so we can be notified about
|
||||
# process failure
|
||||
self.timeout_counter = 6
|
||||
self.saw_process = False
|
||||
self.timer = (NSTimer.
|
||||
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
|
||||
5.0, self, self.checkProcess, None, YES))
|
||||
|
||||
def checkProcess(self):
|
||||
'''Monitors managedsoftwareupdate process for failure to start
|
||||
@@ -194,6 +218,13 @@ class MSUStatusWindowController(NSObject):
|
||||
|
||||
NSLog('checkProcess timer fired')
|
||||
|
||||
if self.window_level == NSScreenSaverWindowLevel:
|
||||
# we're at the loginwindow, there is a PolicyBanner, and we're
|
||||
# running under 10.11+. Make sure we're in the front.
|
||||
NSApp.activateIgnoringOtherApps_(YES)
|
||||
if not self.logWindow.isVisible():
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
|
||||
if self.got_status_update:
|
||||
# we got a status update since we last checked; no need to
|
||||
# check the process table
|
||||
@@ -231,39 +262,79 @@ class MSUStatusWindowController(NSObject):
|
||||
self.timer.invalidate()
|
||||
self.timer = None
|
||||
|
||||
def configureAndDisplayBackdropWindow_(self, window):
|
||||
'''Sets all our configuration options for our masking windows'''
|
||||
window.setCanBecomeVisibleWithoutLogin_(True)
|
||||
if self.haveElCapPolicyBanner():
|
||||
self.backdropWindow.setLevel_(self.window_level)
|
||||
else:
|
||||
self.backdropWindow.setLevel_(self.window_level - 1)
|
||||
translucentColor = NSColor.blackColor().colorWithAlphaComponent_(0.35)
|
||||
window.setBackgroundColor_(translucentColor)
|
||||
window.setOpaque_(False)
|
||||
window.setIgnoresMouseEvents_(False)
|
||||
window.setAlphaValue_(0.0)
|
||||
window.orderFrontRegardless()
|
||||
window.animator().setAlphaValue_(1.0)
|
||||
|
||||
def displayBackdropWindow(self):
|
||||
'''Draw a window that covers the login UI'''
|
||||
if self.backdropWindow:
|
||||
self.backdropWindow.setCanBecomeVisibleWithoutLogin_(True)
|
||||
self.backdropWindow.setLevel_(NSStatusWindowLevel)
|
||||
screenRect = NSScreen.mainScreen().frame()
|
||||
self.backdropWindow.setFrame_display_(screenRect, True)
|
||||
self.backdropWindow.setCanBecomeVisibleWithoutLogin_(True)
|
||||
if self.haveElCapPolicyBanner():
|
||||
self.backdropWindow.setLevel_(self.window_level)
|
||||
else:
|
||||
self.backdropWindow.setLevel_(self.window_level - 1)
|
||||
screenRect = NSScreen.mainScreen().frame()
|
||||
self.backdropWindow.setFrame_display_(screenRect, True)
|
||||
|
||||
darwin_vers = int(os.uname()[2].split('.')[0])
|
||||
if darwin_vers < 11:
|
||||
if self.backdropImageFld:
|
||||
bgImage = getLoginwindowPicture()
|
||||
self.backdropImageFld.setImage_(bgImage)
|
||||
self.backdropWindow.orderFrontRegardless()
|
||||
else:
|
||||
# Lion+
|
||||
# draw a transparent/translucent window to prevent interaction
|
||||
# with the login UI
|
||||
self.backdropImageFld.setHidden_(True)
|
||||
translucentColor = NSColor.blackColor(
|
||||
).colorWithAlphaComponent_(0.35)
|
||||
self.backdropWindow.setBackgroundColor_(translucentColor)
|
||||
self.backdropWindow.setOpaque_(False)
|
||||
self.backdropWindow.setIgnoresMouseEvents_(False)
|
||||
self.backdropWindow.setAlphaValue_(0.0)
|
||||
darwin_vers = int(os.uname()[2].split('.')[0])
|
||||
if darwin_vers < 11:
|
||||
if self.backdropImageFld:
|
||||
bgImage = getLoginwindowPicture()
|
||||
self.backdropImageFld.setImage_(bgImage)
|
||||
self.backdropWindow.orderFrontRegardless()
|
||||
self.backdropWindow.animator().setAlphaValue_(1.0)
|
||||
else:
|
||||
# Lion+
|
||||
# draw transparent/translucent windows to prevent interaction
|
||||
# with the login UI
|
||||
self.backdropImageFld.setHidden_(True)
|
||||
self.configureAndDisplayBackdropWindow_(self.backdropWindow)
|
||||
# are there any other screens?
|
||||
for screen in NSScreen.screens():
|
||||
if screen != NSScreen.mainScreen():
|
||||
# create another masking window for this secondary screen
|
||||
window_rect = screen.frame()
|
||||
window_rect.origin = NSPoint(0.0, 0.0)
|
||||
child_window = NSWindow.alloc(
|
||||
).initWithContentRect_styleMask_backing_defer_screen_(
|
||||
window_rect,
|
||||
NSBorderlessWindowMask, NSBackingStoreBuffered,
|
||||
NO, screen)
|
||||
self.configureAndDisplayBackdropWindow_(child_window)
|
||||
if self.haveElCapPolicyBanner():
|
||||
self.backdropWindow.addChildWindow_ordered_(
|
||||
child_window, NSWindowAbove)
|
||||
|
||||
if self.haveElCapPolicyBanner():
|
||||
# preserve the relative ordering of the backdrop window and the
|
||||
# status window IOW, clicking the backdrop window will not bring it
|
||||
# in front of the status window
|
||||
self.backdropWindow.addChildWindow_ordered_(
|
||||
self.window, NSWindowAbove)
|
||||
|
||||
|
||||
def updateStatus_(self, notification):
|
||||
'''Called when we get a
|
||||
com.googlecode.munki.managedsoftwareupdate.statusUpdate notification;
|
||||
update our status display with information from the notification'''
|
||||
|
||||
if self.window_level == NSScreenSaverWindowLevel:
|
||||
# we're at the loginwindow, there is a PolicyBanner, and we're
|
||||
# running under 10.11+. Make sure we're in the front.
|
||||
NSApp.activateIgnoringOtherApps_(YES)
|
||||
if not self.logWindow.isVisible():
|
||||
self.window.makeKeyAndOrderFront_(self)
|
||||
|
||||
self.got_status_update = True
|
||||
info = notification.userInfo()
|
||||
# explictly get keys from info object; PyObjC in Mountain Lion
|
||||
@@ -288,6 +359,7 @@ class MSUStatusWindowController(NSObject):
|
||||
|
||||
command = info.get('command')
|
||||
if command == 'activate':
|
||||
NSApp.activateIgnoringOtherApps_(YES)
|
||||
self.window.orderFrontRegardless()
|
||||
elif command == 'showRestartAlert':
|
||||
# clean up timer
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014 The Munki Project https://github.com/munki/munki</string>
|
||||
<string>Copyright © 2016 The Munki Project https://github.com/munki/munki</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Created by Greg Neagle on 5/27/09.
|
||||
//
|
||||
// Copyright 2009-2014 Greg Neagle.
|
||||
// Copyright 2009-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// Created by Greg Neagle on 5/27/09.
|
||||
//
|
||||
// Copyright 2009-2014 Greg Neagle.
|
||||
// Copyright 2009-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoom";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Vis log";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Vindue";
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoomen";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Protokoll einblenden";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Fenster";
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "MunkiStatus";
|
||||
"CFBundleDisplayName" = "MunkiStatus";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5056" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
|
||||
<dependencies>
|
||||
<deployment version="1060" defaultVersion="1060" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5056"/>
|
||||
<deployment version="1060" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application">
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="373" id="374"/>
|
||||
</connections>
|
||||
@@ -122,6 +122,12 @@
|
||||
<action selector="performZoom:" target="-1" id="240"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="Wf0-EL-aNa"/>
|
||||
<menuItem title="Show Log" keyEquivalent="l" id="1B8-Pq-rf7">
|
||||
<connections>
|
||||
<action selector="showLogWindow:" target="2ep-ou-xmD" id="91u-X0-9md"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
@@ -140,6 +146,7 @@
|
||||
</menu>
|
||||
<customObject id="373" userLabel="MSUAppDelegate" customClass="MSUAppDelegate">
|
||||
<connections>
|
||||
<outlet property="logWindowController" destination="2ep-ou-xmD" id="3US-AY-I9H"/>
|
||||
<outlet property="statusWindowController" destination="I9V-I1-u8s" id="GJ8-cn-O6v"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
@@ -149,12 +156,21 @@
|
||||
<outlet property="backdropWindow" destination="VCy-58-vwp" id="FI2-0s-zHg"/>
|
||||
<outlet property="detailFld" destination="Xa0-XA-1hm" id="ffw-hJ-LcP"/>
|
||||
<outlet property="imageFld" destination="aZW-jZ-KiY" id="MKX-b8-T7K"/>
|
||||
<outlet property="logWindow" destination="LgH-P6-zEw" id="psa-Jx-p9K"/>
|
||||
<outlet property="messageFld" destination="aUN-kO-8dF" id="nB6-fN-F3i"/>
|
||||
<outlet property="progressIndicator" destination="yFK-Ya-d24" id="vCk-kB-lLo"/>
|
||||
<outlet property="stopBtn" destination="rxM-GK-Unu" id="ogB-ah-l40"/>
|
||||
<outlet property="window" destination="1Nd-E7-vfR" id="nMB-as-gvt"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="2ep-ou-xmD" customClass="MSULogWindowController">
|
||||
<connections>
|
||||
<outlet property="logView" destination="YcU-fs-F3z" id="BDP-0f-Yab"/>
|
||||
<outlet property="pathControl" destination="M6b-hp-fwf" id="UhS-g1-OVj"/>
|
||||
<outlet property="searchField" destination="43A-RJ-9xo" id="ERQ-GP-P0k"/>
|
||||
<outlet property="window" destination="LgH-P6-zEw" id="tzw-ed-aBF"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="420" customClass="NSFontManager"/>
|
||||
<userDefaultsController representsSharedInstance="YES" id="602"/>
|
||||
<window title="Managed Software Center" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="1Nd-E7-vfR" userLabel="Status Window">
|
||||
@@ -213,9 +229,9 @@
|
||||
<connections>
|
||||
<outlet property="delegate" destination="I9V-I1-u8s" id="1bd-RW-Bhf"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-486" y="451.5"/>
|
||||
</window>
|
||||
<window title="BackdropWindow" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hasShadow="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="VCy-58-vwp" userLabel="BackdropWindow" customClass="BorderlessWindow">
|
||||
<windowStyleMask key="styleMask" titled="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="480" height="230"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
|
||||
@@ -231,6 +247,96 @@
|
||||
</subviews>
|
||||
</view>
|
||||
</window>
|
||||
<window title="Log" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" id="LgH-P6-zEw" userLabel="Log Window">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
|
||||
<rect key="contentRect" x="636" y="390" width="512" height="480"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<value key="minSize" type="size" width="512" height="360"/>
|
||||
<view key="contentView" id="V0z-aa-fWm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="GSP-c9-k1b">
|
||||
<rect key="frame" x="-1" y="-1" width="514" height="449"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" id="MTK-yi-vFK">
|
||||
<rect key="frame" x="1" y="1" width="512" height="447"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" id="YcU-fs-F3z">
|
||||
<rect key="frame" x="0.0" y="0.0" width="514" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="spacer" editable="NO" width="10" minWidth="8" maxWidth="3.4028234663852886e+38" id="tsJ-4j-iZA">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" placeholderString="" id="nGI-3K-7Wv">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="data" editable="NO" width="502" minWidth="40" maxWidth="2560" id="UYm-QX-K69">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333298560000002" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="charWrapping" selectable="YES" allowsUndo="NO" alignment="left" title="Text Cell" id="8Nl-My-27d">
|
||||
<font key="font" metaFont="fixedUser" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
</tableView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="I4n-9v-poQ">
|
||||
<rect key="frame" x="1" y="427" width="630" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="Z2r-Od-tTj">
|
||||
<rect key="frame" x="-100" y="-100" width="15" height="303"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<searchField wantsLayer="YES" verticalHuggingPriority="750" id="43A-RJ-9xo">
|
||||
<rect key="frame" x="301" y="452" width="200" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="tIL-Oa-wuU">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</searchFieldCell>
|
||||
<connections>
|
||||
<action selector="searchFilterChanged:" target="2ep-ou-xmD" id="bxe-I5-Bq0"/>
|
||||
</connections>
|
||||
</searchField>
|
||||
<pathControl verticalHuggingPriority="750" allowsExpansionToolTips="YES" id="M6b-hp-fwf">
|
||||
<rect key="frame" x="4" y="452" width="278" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="RHv-ZI-67x">
|
||||
<font key="font" metaFont="system"/>
|
||||
<url key="url" string="file://localhost/Applications/"/>
|
||||
<color key="backgroundColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</pathCell>
|
||||
</pathControl>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="2ep-ou-xmD" id="FgY-Kk-GWr"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="114" y="398"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="MunkiStatus" width="512" height="512"/>
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoom";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Mostrar registro";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Ventana";
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoomaa";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Näytä loki";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Ikkuna";
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "Centre de gestion des logiciels";
|
||||
"CFBundleDisplayName" = "Centre de gestion des logiciels";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -77,6 +77,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoom";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Afficher l’historique";
|
||||
|
||||
/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */
|
||||
"1Nd-E7-vfR.title" = "Centre de gestion des logiciels";
|
||||
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
"CFBundleName" = "MunkiStatus";
|
||||
"CFBundleDisplayName" = "MunkiStatus";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2014 The Munki Project\nhttps://github.com/munki/munki";
|
||||
NSHumanReadableCopyright = "Copyright © 2010-2016 The Munki Project\nhttps://github.com/munki/munki";
|
||||
@@ -77,6 +77,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoom";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Mostra log";
|
||||
|
||||
/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */
|
||||
"1Nd-E7-vfR.title" = "Centro Gestione Applicazioni";
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "ズーム";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "ログを表示";
|
||||
|
||||
/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */
|
||||
"1Nd-E7-vfR.title" = "Managed Software Center";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// main.m
|
||||
// MunkiStatus
|
||||
//
|
||||
// Copyright 2013-2014 Greg Neagle.
|
||||
// Copyright 2013-2016 Greg Neagle.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# main.py
|
||||
# MunkiStatus
|
||||
#
|
||||
# Copyright 2013-2014 Greg Neagle.
|
||||
# Copyright 2013-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ from PyObjCTools import AppHelper
|
||||
# import modules containing classes required to start application and load MainMenu.nib
|
||||
import MSUAppDelegate
|
||||
import MSUStatusWindowController
|
||||
import MSULogWindowController
|
||||
|
||||
# get more debugging info on exceptions
|
||||
objc.setVerbose(1)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# MunkiStatus
|
||||
#
|
||||
# Created by Greg Neagle on 2/11/10.
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -23,10 +23,30 @@
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
|
||||
from Foundation import CFPreferencesCopyAppValue
|
||||
from SystemConfiguration import SCDynamicStoreCopyConsoleUser
|
||||
|
||||
INSTALLATLOGOUTFILE = "/private/tmp/com.googlecode.munki.installatlogout"
|
||||
|
||||
BUNDLE_ID = u'ManagedInstalls'
|
||||
|
||||
def pref(pref_name):
|
||||
"""Return a preference. Since this uses CFPreferencesCopyAppValue,
|
||||
Preferences can be defined several places. Precedence is:
|
||||
- MCX
|
||||
- ~/Library/Preferences/ManagedInstalls.plist
|
||||
- /Library/Preferences/ManagedInstalls.plist
|
||||
- default_prefs defined here.
|
||||
"""
|
||||
default_prefs = {
|
||||
'LogFile': '/Library/Managed Installs/Logs/ManagedSoftwareUpdate.log'
|
||||
}
|
||||
pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID)
|
||||
if pref_value == None:
|
||||
pref_value = default_prefs.get(pref_name)
|
||||
return pref_value
|
||||
|
||||
def call(cmd):
|
||||
'''Convenience function; works around an issue with subprocess.call
|
||||
in PyObjC in Snow Leopard'''
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zoom";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Vis logg";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Vindu";
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Vergroot/verklein";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Toon logbestand";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Venster";
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Изменить масштаб";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Показать журнал";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Окно";
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@
|
||||
/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */
|
||||
"239.title" = "Zooma";
|
||||
|
||||
/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */
|
||||
"1B8-Pq-rf7.title" = "Visa logg";
|
||||
|
||||
/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */
|
||||
"24.title" = "Fönster";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2011-2014 Greg Neagle.
|
||||
# Copyright 2011-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -100,13 +100,12 @@ def alertUserOfForcedLogout(info=None):
|
||||
launchfile = '/var/run/com.googlecode.munki.ManagedSoftwareUpdate'
|
||||
f = open(launchfile, 'w')
|
||||
f.close()
|
||||
time.sleep(0.5)
|
||||
if os.path.exists(launchfile):
|
||||
os.unlink(launchfile)
|
||||
# now wait a bit for it to launch before proceeding
|
||||
# because if we don't, sending the logoutwarn notification
|
||||
# may fall on deaf ears.
|
||||
time.sleep(4)
|
||||
time.sleep(5)
|
||||
if os.path.exists(launchfile):
|
||||
os.unlink(launchfile)
|
||||
|
||||
# if set, convert Python dictionary to NSDictionary.
|
||||
if info is not None:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2008-2014 Greg Neagle.
|
||||
# Copyright 2008-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -327,6 +327,13 @@ def getiteminfo(itempath):
|
||||
infodict[key] = plist[key]
|
||||
if 'LSMinimumSystemVersion' in plist:
|
||||
infodict['minosversion'] = plist['LSMinimumSystemVersion']
|
||||
elif 'LSMinimumSystemVersionByArchitecture' in plist:
|
||||
# just grab the highest version if more than one is listed
|
||||
versions = [item[1] for item in
|
||||
plist['LSMinimumSystemVersionByArchitecture'].items()]
|
||||
highest_version = str(max([munkicommon.MunkiLooseVersion(version)
|
||||
for version in versions]))
|
||||
infodict['minosversion'] = highest_version
|
||||
elif 'SystemVersionCheck:MinimumSystemVersion' in plist:
|
||||
infodict['minosversion'] = \
|
||||
plist['SystemVersionCheck:MinimumSystemVersion']
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -202,7 +202,7 @@ def runScript(script, display_name, runtype):
|
||||
except utils.ScriptNotFoundError:
|
||||
pass # script is not required, so pass
|
||||
except utils.RunExternalScriptError, err:
|
||||
munkicommon.display_warning(str(err))
|
||||
munkicommon.display_warning(unicode(err))
|
||||
return result
|
||||
|
||||
|
||||
@@ -435,7 +435,7 @@ def notifyUserOfUpdates(force=False):
|
||||
launchfile = '/var/run/com.googlecode.munki.ManagedSoftwareUpdate'
|
||||
f = open(launchfile, 'w')
|
||||
f.close()
|
||||
time.sleep(0.5)
|
||||
time.sleep(5)
|
||||
if os.path.exists(launchfile):
|
||||
os.unlink(launchfile)
|
||||
user_was_notified = True
|
||||
@@ -634,7 +634,7 @@ def main():
|
||||
munkicommon.log("### Starting managedsoftwareupdate run: %s ###" % runtype)
|
||||
if options.verbose:
|
||||
print 'Managed Software Update Tool'
|
||||
print 'Copyright 2010-2014 The Munki Project'
|
||||
print 'Copyright 2010-2016 The Munki Project'
|
||||
print 'https://github.com/munki/munki\n'
|
||||
|
||||
munkicommon.display_status_major('Starting...')
|
||||
@@ -710,6 +710,7 @@ def main():
|
||||
if options.manualcheck:
|
||||
# record our result
|
||||
recordUpdateCheckResult(-1)
|
||||
if options.munkistatusoutput:
|
||||
# connect to socket and quit
|
||||
munkistatus.activate()
|
||||
munkistatus.quit()
|
||||
@@ -738,7 +739,8 @@ def main():
|
||||
updatecheckresult = None
|
||||
if not skip_munki_check:
|
||||
try:
|
||||
updatecheckresult = updatecheck.check(client_id=options.id)
|
||||
updatecheckresult = updatecheck.check(
|
||||
client_id=options.id.decode('UTF-8'))
|
||||
except:
|
||||
munkicommon.display_error('Unexpected error in updatecheck:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
@@ -919,16 +921,19 @@ def main():
|
||||
# so we need to check InstallInfo.plist and
|
||||
# AppleUpdates.plist again
|
||||
updatesavailable = munkiUpdatesAvailable()
|
||||
try:
|
||||
appleupdatesavailable = \
|
||||
appleupdates.appleSoftwareUpdatesAvailable(
|
||||
suppresscheck=True, client_id=options.id)
|
||||
except:
|
||||
munkicommon.display_error(
|
||||
'Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
if appleupdatesavailable:
|
||||
# there were Apple updates available, but we might have
|
||||
# installed some unattended
|
||||
try:
|
||||
appleupdatesavailable = (
|
||||
appleupdates.appleSoftwareUpdatesAvailable(
|
||||
suppresscheck=True, client_id=options.id))
|
||||
except:
|
||||
munkicommon.display_error(
|
||||
'Unexpected error in appleupdates:')
|
||||
munkicommon.log(traceback.format_exc())
|
||||
munkicommon.savereport()
|
||||
raise
|
||||
if appleupdatesavailable or updatesavailable:
|
||||
# set a flag to notify the user of available updates
|
||||
# after we conclude this run.
|
||||
|
||||
+261
-219
File diff suppressed because it is too large
Load Diff
+132
-106
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -22,18 +22,54 @@ Created by Greg Neagle on 2010-09-29.
|
||||
|
||||
Assists with importing installer items into the munki repo
|
||||
"""
|
||||
|
||||
import sys
|
||||
import ctypes
|
||||
import os
|
||||
#import readline
|
||||
import readline
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import thread
|
||||
|
||||
from ctypes.util import find_library
|
||||
from optparse import OptionParser, BadOptionError, AmbiguousOptionError
|
||||
|
||||
from munkilib import iconutils
|
||||
from munkilib import munkicommon
|
||||
from munkilib import FoundationPlist
|
||||
|
||||
if 'libedit' in readline.__doc__:
|
||||
# readline module was compiled against libedit
|
||||
LIBEDIT = ctypes.cdll.LoadLibrary(find_library('libedit'))
|
||||
else:
|
||||
LIBEDIT = None
|
||||
|
||||
|
||||
def raw_input_with_default(prompt, default_text):
|
||||
'''A nasty, nasty hack to get around Python readline limitations under
|
||||
OS X. Gives us editable default text for munkiimport choices'''
|
||||
|
||||
def insert_default_text(prompt, text):
|
||||
'''Helper function'''
|
||||
time.sleep(0.01)
|
||||
LIBEDIT.rl_set_prompt(prompt)
|
||||
readline.insert_text(text)
|
||||
LIBEDIT.rl_forced_update_display()
|
||||
|
||||
readline.clear_history()
|
||||
if not default_text:
|
||||
return unicode(raw_input(prompt), encoding=sys.stdin.encoding)
|
||||
elif LIBEDIT:
|
||||
# readline module was compiled against libedit
|
||||
thread.start_new_thread(insert_default_text, (prompt, default_text))
|
||||
return unicode(raw_input(), encoding=sys.stdin.encoding)
|
||||
else:
|
||||
readline.set_startup_hook(lambda: readline.insert_text(default_text))
|
||||
try:
|
||||
return unicode(raw_input(prompt), encoding=sys.stdin.encoding)
|
||||
finally:
|
||||
readline.set_startup_hook()
|
||||
|
||||
|
||||
class PassThroughOptionParser(OptionParser):
|
||||
"""
|
||||
An unknown option pass-through implementation of OptionParser.
|
||||
@@ -56,18 +92,7 @@ class PassThroughOptionParser(OptionParser):
|
||||
return self.epilog
|
||||
|
||||
|
||||
def raw_input_with_default(prompt='', default=''):
|
||||
'''Get input from user with a prompt and a suggested default value'''
|
||||
|
||||
if default:
|
||||
prompt = '%s [%s]: ' % (prompt, default)
|
||||
return raw_input(prompt).decode('UTF-8') or default.decode('UTF-8')
|
||||
else:
|
||||
# no default value, just call raw_input
|
||||
return raw_input(prompt + ": ").decode('UTF-8')
|
||||
|
||||
|
||||
def makeDMG(pkgpath):
|
||||
def make_dmg(pkgpath):
|
||||
"""Wraps a non-flat package into a disk image.
|
||||
Returns path to newly-created disk image."""
|
||||
|
||||
@@ -94,7 +119,7 @@ def makeDMG(pkgpath):
|
||||
return diskimagepath
|
||||
|
||||
|
||||
def repoAvailable():
|
||||
def repo_available():
|
||||
"""Checks the repo path for proper directory structure.
|
||||
If the directories look wrong we probably don't have a
|
||||
valid repo path. Returns True if things look OK."""
|
||||
@@ -102,7 +127,7 @@ def repoAvailable():
|
||||
print >> sys.stderr, 'No repo path specified.'
|
||||
return False
|
||||
if not os.path.exists(REPO_PATH):
|
||||
mountRepoCLI()
|
||||
mount_repo_cli()
|
||||
if not os.path.exists(REPO_PATH):
|
||||
return False
|
||||
for subdir in ['catalogs', 'manifests', 'pkgs', 'pkgsinfo']:
|
||||
@@ -113,22 +138,7 @@ def repoAvailable():
|
||||
return True
|
||||
|
||||
|
||||
def mountRepoGUI():
|
||||
"""Attempts to connect to the repo fileshare
|
||||
Returns nothing whether we succeed or fail"""
|
||||
if not REPO_PATH or not REPO_URL:
|
||||
return
|
||||
print 'Attempting to connect to munki repo...'
|
||||
cmd = ['/usr/bin/open', REPO_URL]
|
||||
dummy_retcode = subprocess.call(cmd)
|
||||
for dummy_i in range(60):
|
||||
# wait up to 60 seconds to connect to repo
|
||||
if os.path.exists(REPO_PATH):
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def mountRepoCLI():
|
||||
def mount_repo_cli():
|
||||
"""Attempts to connect to the repo fileshare"""
|
||||
global WE_MOUNTED_THE_REPO
|
||||
if os.path.exists(REPO_PATH):
|
||||
@@ -151,7 +161,7 @@ def mountRepoCLI():
|
||||
WE_MOUNTED_THE_REPO = True
|
||||
|
||||
|
||||
def unmountRepoCLI():
|
||||
def unmount_repo_cli():
|
||||
"""Attempts to unmount the repo fileshare"""
|
||||
if not os.path.exists(REPO_PATH):
|
||||
return
|
||||
@@ -164,7 +174,7 @@ class RepoCopyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def copyItemToRepo(itempath, vers, subdirectory=''):
|
||||
def copy_item_to_repo(itempath, vers, subdirectory=''):
|
||||
"""Copies an item to the appropriate place in the repo.
|
||||
If itempath is a path within the repo/pkgs directory, copies nothing.
|
||||
Renames the item if an item already exists with that name.
|
||||
@@ -217,7 +227,7 @@ def copyItemToRepo(itempath, vers, subdirectory=''):
|
||||
return os.path.join(subdirectory, item_name)
|
||||
|
||||
|
||||
def getIconPath(pkginfo):
|
||||
def get_icon_path(pkginfo):
|
||||
"""Return path for icon"""
|
||||
icon_name = pkginfo.get('icon_name') or pkginfo['name']
|
||||
if not os.path.splitext(icon_name)[1]:
|
||||
@@ -225,17 +235,17 @@ def getIconPath(pkginfo):
|
||||
return os.path.join(REPO_PATH, u'icons', icon_name)
|
||||
|
||||
|
||||
def iconExistsInRepo(pkginfo):
|
||||
def icon_exists_in_repo(pkginfo):
|
||||
"""Returns True if there is an icon for this item in the repo"""
|
||||
icon_path = getIconPath(pkginfo)
|
||||
icon_path = get_icon_path(pkginfo)
|
||||
if os.path.exists(icon_path):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def addIconHashToPkginfo(pkginfo):
|
||||
def add_icon_hash_to_pkginfo(pkginfo):
|
||||
"""Adds the icon hash tp pkginfo if the icon exists in repo"""
|
||||
icon_path = getIconPath(pkginfo)
|
||||
icon_path = get_icon_path(pkginfo)
|
||||
if os.path.isfile(icon_path):
|
||||
pkginfo['icon_hash'] = munkicommon.getsha256hash(icon_path)
|
||||
|
||||
@@ -302,6 +312,7 @@ def generate_pngs_from_installer_pkg(item_path, pkginfo):
|
||||
|
||||
|
||||
def convert_and_install_icon(pkginfo, icon_path, index=None):
|
||||
'''Convert icon file to png and save to repo icon path'''
|
||||
destination_path = os.path.join(REPO_PATH, 'icons')
|
||||
if not os.path.exists(destination_path):
|
||||
try:
|
||||
@@ -324,7 +335,7 @@ def convert_and_install_icon(pkginfo, icon_path, index=None):
|
||||
print >> sys.stderr, u'Error converting %s to png.' % icon_path
|
||||
|
||||
|
||||
def copyIconToRepo(iconpath):
|
||||
def copy_icon_to_repo(iconpath):
|
||||
"""Saves a product icon to the repo"""
|
||||
destination_path = os.path.join(REPO_PATH, 'icons')
|
||||
if not os.path.exists(destination_path):
|
||||
@@ -351,7 +362,7 @@ def copyIconToRepo(iconpath):
|
||||
% (iconpath, destination_path_name))
|
||||
|
||||
|
||||
def copyPkginfoToRepo(pkginfo, subdirectory=''):
|
||||
def copy_pkginfo_to_repo(pkginfo, subdirectory=''):
|
||||
"""Saves pkginfo to munki_repo_path/pkgsinfo/subdirectory"""
|
||||
# less error checking because we copy the installer_item
|
||||
# first and bail if it fails...
|
||||
@@ -383,7 +394,7 @@ def copyPkginfoToRepo(pkginfo, subdirectory=''):
|
||||
return pkginfo_path
|
||||
|
||||
|
||||
def openPkginfoInEditor(pkginfo_path):
|
||||
def open_pkginfo_in_editor(pkginfo_path):
|
||||
"""Opens pkginfo list in the user's chosen editor."""
|
||||
editor = pref('editor')
|
||||
if editor:
|
||||
@@ -398,13 +409,13 @@ def openPkginfoInEditor(pkginfo_path):
|
||||
'Problem running editor %s: %s.' % (editor, err))
|
||||
|
||||
|
||||
def promptForSubdirectory(subdirectory):
|
||||
def prompt_for_subdirectory(subdirectory):
|
||||
"""Prompts the user for a subdirectory for the pkg and pkginfo"""
|
||||
while True:
|
||||
newdir = raw_input(
|
||||
'Upload item to subdirectory path [%s]: ' % subdirectory)
|
||||
if newdir:
|
||||
if not repoAvailable():
|
||||
if not repo_available():
|
||||
raise RepoCopyError('Could not connect to munki repo.')
|
||||
if APPLEMETADATA:
|
||||
destination_path = os.path.join(REPO_PATH, 'pkgsinfo', newdir)
|
||||
@@ -427,7 +438,7 @@ class CatalogDBException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def makeCatalogDB():
|
||||
def make_catalog_db():
|
||||
"""Returns a dict we can use like a database"""
|
||||
|
||||
all_items_path = os.path.join(REPO_PATH, 'catalogs', 'all')
|
||||
@@ -523,7 +534,7 @@ def makeCatalogDB():
|
||||
return pkgdb
|
||||
|
||||
|
||||
def findMatchingPkginfo(pkginfo):
|
||||
def find_matching_pkginfo(pkginfo):
|
||||
"""Looks through repo catalogs looking for matching pkginfo
|
||||
Returns a pkginfo dictionary, or an empty dict"""
|
||||
|
||||
@@ -533,7 +544,7 @@ def findMatchingPkginfo(pkginfo):
|
||||
munkicommon.MunkiLooseVersion(value_a))
|
||||
|
||||
try:
|
||||
catdb = makeCatalogDB()
|
||||
catdb = make_catalog_db()
|
||||
except CatalogDBException:
|
||||
return {}
|
||||
|
||||
@@ -600,7 +611,7 @@ def findMatchingPkginfo(pkginfo):
|
||||
return {}
|
||||
|
||||
|
||||
def makePkgInfo(options=None, test_mode=False):
|
||||
def make_pkginfo(options=None, test_mode=False):
|
||||
"""Calls makepkginfo to generate the pkginfo for item_path."""
|
||||
# first look for a makepkginfo in the same dir as us
|
||||
mydir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -645,7 +656,7 @@ def makePkgInfo(options=None, test_mode=False):
|
||||
return FoundationPlist.readPlistFromString(stdout)
|
||||
|
||||
|
||||
def makeCatalogs():
|
||||
def make_catalogs():
|
||||
"""Calls makecatalogs to rebuild our catalogs"""
|
||||
# first look for a makecatalogs in the same dir as us
|
||||
mydir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -653,7 +664,7 @@ def makeCatalogs():
|
||||
if not os.path.exists(makecatalogs_path):
|
||||
# didn't find it; assume the default install path
|
||||
makecatalogs_path = '/usr/local/munki/makecatalogs'
|
||||
if not repoAvailable():
|
||||
if not repo_available():
|
||||
raise RepoCopyError('Could not connect to munki repo.')
|
||||
if not VERBOSE:
|
||||
print 'Rebuilding catalogs at %s...' % REPO_PATH
|
||||
@@ -673,16 +684,16 @@ def makeCatalogs():
|
||||
print errors
|
||||
|
||||
|
||||
def cleanupAndExit(exitcode):
|
||||
def cleanup_and_exit(exitcode):
|
||||
"""Unmounts the repo if we mounted it, then exits"""
|
||||
result = 0
|
||||
if WE_MOUNTED_THE_REPO:
|
||||
if not NOINTERACTIVE:
|
||||
answer = raw_input('Unmount the repo fileshare? [y/n] ')
|
||||
if answer.lower().startswith('y'):
|
||||
result = unmountRepoCLI()
|
||||
result = unmount_repo_cli()
|
||||
else:
|
||||
result = unmountRepoCLI()
|
||||
result = unmount_repo_cli()
|
||||
# clean up tmpdir
|
||||
munkicommon.cleanUpTmpDir()
|
||||
|
||||
@@ -710,10 +721,11 @@ def configure():
|
||||
'Repo fileshare URL (example: afp://munki.example.com/repo)'),
|
||||
('pkginfo_extension', 'pkginfo extension (Example: .plist)'),
|
||||
('editor',
|
||||
'pkginfo editor (examples: /usr/bin/vi or TextMate.app)'),
|
||||
'pkginfo editor (examples: /usr/bin/vi or TextMate.app; '
|
||||
'leave empty to not open an editor after import)'),
|
||||
('default_catalog', 'Default catalog to use (example: testing)')]:
|
||||
|
||||
_prefs[key] = raw_input_with_default('%15s' % prompt, pref(key))
|
||||
_prefs[key] = raw_input_with_default('%15s: ' % prompt, pref(key))
|
||||
|
||||
try:
|
||||
FoundationPlist.writePlist(_prefs, PREFSPATH)
|
||||
@@ -826,20 +838,20 @@ def main():
|
||||
|
||||
# Verify that arguments, presumed to be for
|
||||
# 'makepkginfo' are valid and return installer_item
|
||||
return_dict = makePkgInfo(
|
||||
return_dict = make_pkginfo(
|
||||
options=arguments, test_mode=True)
|
||||
try:
|
||||
return_dict = FoundationPlist.readPlistFromString(return_dict)
|
||||
except FoundationPlist.FoundationPlistException, err:
|
||||
print >> sys.stderr, (
|
||||
'Error getting info from makepkginfo: %s' % err)
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
installer_item = return_dict.get('installeritem')
|
||||
uninstaller_item = return_dict.get('uninstalleritem')
|
||||
APPLEMETADATA = return_dict.get('installer_type') == 'apple_update_metadata'
|
||||
|
||||
if not installer_item and not APPLEMETADATA:
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
if not APPLEMETADATA:
|
||||
# Remove the installer_item from arguments
|
||||
@@ -870,7 +882,7 @@ def main():
|
||||
'tool, or provide with --repo-path')
|
||||
exit(-1)
|
||||
|
||||
if not repoAvailable():
|
||||
if not repo_available():
|
||||
print >> sys.stderr, ('Could not connect to munki repo. Check the '
|
||||
'configuration and try again.')
|
||||
exit(-1)
|
||||
@@ -880,17 +892,17 @@ def main():
|
||||
if munkicommon.hasValidDiskImageExt(installer_item):
|
||||
# a directory named foo.dmg or foo.iso!
|
||||
print >> sys.stderr, '%s is an unknown type.' % installer_item
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
else:
|
||||
# we need to convert to dmg
|
||||
dmg_path = makeDMG(installer_item)
|
||||
dmg_path = make_dmg(installer_item)
|
||||
if dmg_path:
|
||||
installer_item = dmg_path
|
||||
else:
|
||||
print >> sys.stderr, (
|
||||
'Could not convert %s to a disk image.'
|
||||
% installer_item)
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
# append the installer_item to arguments which
|
||||
# may have changed if bundle was wrapped into dmg
|
||||
@@ -902,31 +914,31 @@ def main():
|
||||
# a directory named foo.dmg or foo.iso!
|
||||
print >> sys.stderr, (
|
||||
'%s is an unknown type.' % uninstaller_item)
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
else:
|
||||
# we need to convert to dmg
|
||||
dmg_path = makeDMG(uninstaller_item)
|
||||
dmg_path = make_dmg(uninstaller_item)
|
||||
if dmg_path:
|
||||
uninstaller_item = dmg_path
|
||||
else:
|
||||
print >> sys.stderr, (
|
||||
'Could not convert %s to a disk image.'
|
||||
% uninstaller_item)
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
# if catalog/catalogs have not been explictly specified via command-line,
|
||||
# append our default catalog
|
||||
if not '--catalog' in arguments and not '-c' in arguments:
|
||||
default_catalog = pref('default_catalog') or 'testing'
|
||||
arguments.extend(['--catalog', default_catalog])
|
||||
pkginfo = makePkgInfo(arguments)
|
||||
pkginfo = make_pkginfo(arguments)
|
||||
if not pkginfo:
|
||||
# makepkginfo returned an error
|
||||
print >> sys.stderr, 'Getting package info failed.'
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
if not options.nointeractive:
|
||||
# try to find existing pkginfo items that match this one
|
||||
matchingpkginfo = findMatchingPkginfo(pkginfo)
|
||||
matchingpkginfo = find_matching_pkginfo(pkginfo)
|
||||
exactmatch = False
|
||||
if matchingpkginfo:
|
||||
if ('installer_item_hash' in matchingpkginfo and
|
||||
@@ -944,12 +956,13 @@ def main():
|
||||
('Installer item path', 'installer_item_location'))
|
||||
for (name, key) in fields:
|
||||
print '%21s: %s' % (
|
||||
name, matchingpkginfo.get(key, '').encode('UTF-8'))
|
||||
name, matchingpkginfo.get(key, '').encode(
|
||||
'UTF-8'))
|
||||
print
|
||||
if exactmatch:
|
||||
answer = raw_input('Import this item anyway? [y/n] ')
|
||||
if not answer.lower().startswith('y'):
|
||||
cleanupAndExit(0)
|
||||
cleanup_and_exit(0)
|
||||
|
||||
answer = raw_input('Use existing item as a template? [y/n] ')
|
||||
if answer.lower().startswith('y'):
|
||||
@@ -978,20 +991,31 @@ def main():
|
||||
pkginfo[key] = matchingpkginfo[key]
|
||||
|
||||
# now let user do some basic editing
|
||||
editfields = (('Item name', 'name'),
|
||||
('Display name', 'display_name'),
|
||||
('Description', 'description'),
|
||||
('Version', 'version'),
|
||||
('Category', 'category'),
|
||||
('Developer', 'developer'),
|
||||
editfields = (('Item name', 'name', 'str'),
|
||||
('Display name', 'display_name', 'str'),
|
||||
('Description', 'description', 'str'),
|
||||
('Version', 'version', 'str'),
|
||||
('Category', 'category', 'str'),
|
||||
('Developer', 'developer', 'str'),
|
||||
('Unattended install', 'unattended_install', 'bool'),
|
||||
('Unattended uninstall', 'unattended_uninstall', 'bool'),
|
||||
)
|
||||
for (name, key) in editfields:
|
||||
prompt = '%15s' % name
|
||||
default = pkginfo.get(key, '').encode('UTF-8')
|
||||
for (name, key, kind) in editfields:
|
||||
prompt = '%20s: ' % name
|
||||
if kind == 'bool':
|
||||
default = str(pkginfo.get(key, False))
|
||||
else:
|
||||
default = pkginfo.get(key, '').encode('UTF-8')
|
||||
pkginfo[key] = raw_input_with_default(prompt, default)
|
||||
if kind == 'bool':
|
||||
value = pkginfo[key].lower().strip()
|
||||
if value.startswith(('y', 't')):
|
||||
pkginfo[key] = True
|
||||
else:
|
||||
pkginfo[key] = False
|
||||
|
||||
# special handling for catalogs array
|
||||
prompt = '%15s' % 'Catalogs'
|
||||
prompt = '%20s: ' % 'Catalogs'
|
||||
default = ', '.join(pkginfo['catalogs'])
|
||||
newvalue = raw_input_with_default(prompt, default)
|
||||
pkginfo['catalogs'] = [item.strip()
|
||||
@@ -1003,17 +1027,19 @@ def main():
|
||||
'\'installs\' items for this installer '
|
||||
'item. You will need to add at least '
|
||||
'one item to the \'installs\' list.')
|
||||
#TO-DO: provide a way to add 'installs' items right here
|
||||
|
||||
print
|
||||
for (name, key) in editfields:
|
||||
print '%15s: %s' % (name, pkginfo.get(key, '').encode('UTF-8'))
|
||||
print '%15s: %s' % (
|
||||
'Catalogs', ', '.join(pkginfo['catalogs']).encode('UTF-8'))
|
||||
print
|
||||
#for (name, key, kind) in editfields:
|
||||
# if kind == 'bool':
|
||||
# print '%20s: %s' % (name, pkginfo.get(key, False))
|
||||
# else:
|
||||
# print '%20s: %s' % (name, pkginfo.get(key, '').encode('UTF-8'))
|
||||
#print '%20s: %s' % (
|
||||
# 'Catalogs', ', '.join(pkginfo['catalogs']).encode('UTF-8'))
|
||||
#print
|
||||
answer = raw_input('Import this item? [y/n] ')
|
||||
if not answer.lower().startswith('y'):
|
||||
cleanupAndExit(0)
|
||||
cleanup_and_exit(0)
|
||||
|
||||
if options.subdirectory == '':
|
||||
pkgs_path = os.path.join(REPO_PATH, 'pkgs')
|
||||
@@ -1023,10 +1049,10 @@ def main():
|
||||
installer_item_dirpath = os.path.dirname(installer_item)
|
||||
options.subdirectory = \
|
||||
installer_item_dirpath[len(pkgs_path)+1:]
|
||||
options.subdirectory = promptForSubdirectory(
|
||||
options.subdirectory = prompt_for_subdirectory(
|
||||
options.subdirectory)
|
||||
|
||||
if (not iconExistsInRepo(pkginfo) and not options.icon_path
|
||||
if (not icon_exists_in_repo(pkginfo) and not options.icon_path
|
||||
and not APPLEMETADATA
|
||||
and not pkginfo.get('installer_type') == 'profile'):
|
||||
print 'No existing product icon found.'
|
||||
@@ -1050,12 +1076,12 @@ def main():
|
||||
|
||||
if not APPLEMETADATA:
|
||||
try:
|
||||
uploaded_pkgpath = copyItemToRepo(installer_item,
|
||||
pkginfo.get('version'),
|
||||
options.subdirectory)
|
||||
uploaded_pkgpath = copy_item_to_repo(installer_item,
|
||||
pkginfo.get('version'),
|
||||
options.subdirectory)
|
||||
except RepoCopyError, errmsg:
|
||||
print >> sys.stderr, errmsg
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
# adjust the installer_item_location to match
|
||||
# the actual location and name
|
||||
@@ -1063,12 +1089,12 @@ def main():
|
||||
|
||||
if uninstaller_item:
|
||||
try:
|
||||
uploaded_pkgpath = copyItemToRepo(uninstaller_item,
|
||||
pkginfo.get('version'),
|
||||
options.subdirectory)
|
||||
uploaded_pkgpath = copy_item_to_repo(uninstaller_item,
|
||||
pkginfo.get('version'),
|
||||
options.subdirectory)
|
||||
except RepoCopyError, errmsg:
|
||||
print >> sys.stderr, errmsg
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
# adjust the uninstaller_item_location to match
|
||||
# the actual location and name; update size and hash
|
||||
@@ -1086,27 +1112,27 @@ def main():
|
||||
print >> sys.stderr, errmsg
|
||||
|
||||
# add icon to pkginfo if in repository
|
||||
addIconHashToPkginfo(pkginfo)
|
||||
add_icon_hash_to_pkginfo(pkginfo)
|
||||
|
||||
# installer_item upload was successful, so upload pkginfo to repo
|
||||
try:
|
||||
pkginfo_path = copyPkginfoToRepo(pkginfo, options.subdirectory)
|
||||
pkginfo_path = copy_pkginfo_to_repo(pkginfo, options.subdirectory)
|
||||
except RepoCopyError, errmsg:
|
||||
print >> sys.stderr, errmsg
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
if not options.nointeractive:
|
||||
# open the pkginfo file in the user's editor
|
||||
openPkginfoInEditor(pkginfo_path)
|
||||
open_pkginfo_in_editor(pkginfo_path)
|
||||
answer = raw_input('Rebuild catalogs? [y/n] ')
|
||||
if answer.lower().startswith('y'):
|
||||
try:
|
||||
makeCatalogs()
|
||||
make_catalogs()
|
||||
except RepoCopyError, errmsg:
|
||||
print >> sys.stderr, errmsg
|
||||
cleanupAndExit(-1)
|
||||
cleanup_and_exit(-1)
|
||||
|
||||
cleanupAndExit(0)
|
||||
cleanup_and_exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -7,7 +7,7 @@ Utilities to enable munki to install/uninstall Adobe CS3/CS4/CS5 products
|
||||
using the CS3/CS4/CS5 Deployment Toolkits.
|
||||
|
||||
"""
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2011-2014 Greg Neagle.
|
||||
# Copyright 2011-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ Created by Greg Neagle on 2011-09-29.
|
||||
#standard libs
|
||||
import calendar
|
||||
import errno
|
||||
import imp
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
@@ -60,6 +61,40 @@ DEFAULT_USER_AGENT = "managedsoftwareupdate/%s Darwin/%s" % (
|
||||
machine['munki_version'], darwin_version)
|
||||
|
||||
|
||||
def import_middleware():
|
||||
'''Check munki folder for a python file that starts with 'middleware'.
|
||||
If the file exists and has a callable 'process_request_options' attribute,
|
||||
the module is loaded under the 'middleware' name'''
|
||||
required_function_name = 'process_request_options'
|
||||
munki_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
for filename in os.listdir(munki_dir):
|
||||
if (filename.startswith('middleware')
|
||||
and os.path.splitext(filename)[1] == '.py'):
|
||||
name = os.path.splitext(filename)[0]
|
||||
filepath = os.path.join(munki_dir, filename)
|
||||
_tmp = imp.load_source(name, filepath)
|
||||
if hasattr(_tmp, required_function_name):
|
||||
if callable(getattr(_tmp, required_function_name)):
|
||||
munkicommon.display_debug1(
|
||||
'Loading middleware module %s' % filename)
|
||||
globals()['middleware'] = _tmp
|
||||
return
|
||||
else:
|
||||
munkicommon.display_warning(
|
||||
'%s attribute in %s is not callable.'
|
||||
% (required_function_name, filepath))
|
||||
munkicommon.display_warning('Ignoring %s' % filepath)
|
||||
else:
|
||||
munkicommon.display_warning(
|
||||
'%s does not have a %s function'
|
||||
% (filepath, required_function_name))
|
||||
munkicommon.display_warning('Ignoring %s' % filepath)
|
||||
return
|
||||
|
||||
middleware = None
|
||||
import_middleware()
|
||||
|
||||
|
||||
class GurlError(Exception):
|
||||
"""General exception for gurl errors"""
|
||||
pass
|
||||
@@ -159,6 +194,14 @@ def get_url(url, destinationpath,
|
||||
'logging_function': munkicommon.display_debug2}
|
||||
munkicommon.display_debug2('Options: %s' % options)
|
||||
|
||||
# Allow middleware to modify options
|
||||
if middleware:
|
||||
munkicommon.display_debug2('Processing options through middleware')
|
||||
# middleware module must have process_request_options function
|
||||
# and must return usable options
|
||||
options = middleware.process_request_options(options)
|
||||
munkicommon.display_debug2('Options: %s' % options)
|
||||
|
||||
connection = Gurl.alloc().initWithOptions_(options)
|
||||
stored_percent_complete = -1
|
||||
stored_bytes_received = 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2010-2014 Greg Neagle.
|
||||
# Copyright 2010-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -31,8 +31,10 @@ import adobeutils
|
||||
import launchd
|
||||
import munkicommon
|
||||
import munkistatus
|
||||
import powermgr
|
||||
import profiles
|
||||
import updatecheck
|
||||
import xattr
|
||||
import FoundationPlist
|
||||
from removepackages import removepackages
|
||||
|
||||
@@ -40,42 +42,11 @@ from removepackages import removepackages
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
# pylint: disable=E0611
|
||||
from Foundation import NSDate
|
||||
# stuff for IOKit/PowerManager, courtesy Michael Lynn, pudquick@github
|
||||
from ctypes import c_uint32, cdll, c_void_p, POINTER, byref
|
||||
from CoreFoundation import CFStringCreateWithCString
|
||||
from CoreFoundation import kCFStringEncodingASCII
|
||||
from objc import pyobjc_id
|
||||
# pylint: enable=E0611
|
||||
|
||||
# lots of camelCase names
|
||||
# pylint: disable=C0103
|
||||
|
||||
libIOKit = cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
|
||||
libIOKit.IOPMAssertionCreateWithName.argtypes = [
|
||||
c_void_p, c_uint32, c_void_p, POINTER(c_uint32)]
|
||||
libIOKit.IOPMAssertionRelease.argtypes = [c_uint32]
|
||||
|
||||
def CFSTR(py_string):
|
||||
'''Returns a CFString given a Python string'''
|
||||
return CFStringCreateWithCString(None, py_string, kCFStringEncodingASCII)
|
||||
|
||||
def raw_ptr(pyobjc_string):
|
||||
'''Returns a pointer to a CFString'''
|
||||
return pyobjc_id(pyobjc_string.nsstring())
|
||||
|
||||
def IOPMAssertionCreateWithName(assert_name, assert_level, assert_msg):
|
||||
'''Creaes a PowerManager assertion'''
|
||||
assertID = c_uint32(0)
|
||||
p_assert_name = raw_ptr(CFSTR(assert_name))
|
||||
p_assert_msg = raw_ptr(CFSTR(assert_msg))
|
||||
errcode = libIOKit.IOPMAssertionCreateWithName(
|
||||
p_assert_name, assert_level, p_assert_msg, byref(assertID))
|
||||
return (errcode, assertID)
|
||||
|
||||
IOPMAssertionRelease = libIOKit.IOPMAssertionRelease
|
||||
# end IOKit/PowerManager bindings
|
||||
|
||||
|
||||
# initialize our report fields
|
||||
# we do this here because appleupdates.installAppleUpdates()
|
||||
# calls installWithInfo()
|
||||
@@ -467,13 +438,23 @@ def copyItemsFromMountpoint(mountpoint, itemlist):
|
||||
# all tests passed, OK to copy
|
||||
munkicommon.display_status_minor(
|
||||
"Copying %s to %s" % (source_itemname, full_destpath))
|
||||
retcode = subprocess.call(["/bin/cp", "-pR",
|
||||
retcode = subprocess.call(["/usr/bin/ditto", "--noqtn",
|
||||
source_itempath, full_destpath])
|
||||
if retcode:
|
||||
munkicommon.display_error(
|
||||
"Error copying %s to %s" % (source_itempath, full_destpath))
|
||||
return retcode
|
||||
|
||||
# remove com.apple.quarantine xattr since `man ditto` lies and doesn't
|
||||
# seem to actually always remove it
|
||||
try:
|
||||
if "com.apple.quarantine" in xattr.xattr(full_destpath).list():
|
||||
xattr.xattr(full_destpath).remove("com.apple.quarantine")
|
||||
except BaseException as err:
|
||||
munkicommon.display_warning(
|
||||
"Error removing com.apple.quarantine from %s: %s",
|
||||
full_destpath, err)
|
||||
|
||||
# set owner
|
||||
user = item.get('user', 'root')
|
||||
munkicommon.display_detail(
|
||||
@@ -506,20 +487,6 @@ def copyItemsFromMountpoint(mountpoint, itemlist):
|
||||
"Error setting mode for %s" % (full_destpath))
|
||||
return retcode
|
||||
|
||||
# remove com.apple.quarantine attribute from copied item
|
||||
cmd = ["/usr/bin/xattr", full_destpath]
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
(out, dummy_err) = proc.communicate()
|
||||
if out:
|
||||
xattrs = str(out).splitlines()
|
||||
if "com.apple.quarantine" in xattrs:
|
||||
dummy_result = subprocess.call(
|
||||
["/usr/bin/xattr", "-d", "com.apple.quarantine",
|
||||
full_destpath])
|
||||
|
||||
# all items copied successfully!
|
||||
return 0
|
||||
|
||||
@@ -1019,7 +986,7 @@ def processRemovals(removallist, only_unattended=False):
|
||||
munkicommon.display_error(message)
|
||||
else:
|
||||
munkicommon.log(
|
||||
"Uninstall of %s was successful.", display_name)
|
||||
"Uninstall of %s was successful." % display_name)
|
||||
|
||||
elif uninstallmethod.startswith("Adobe"):
|
||||
retcode = adobeutils.doAdobeRemoval(item)
|
||||
@@ -1186,21 +1153,6 @@ def blockingApplicationsRunning(pkginfoitem):
|
||||
return False
|
||||
|
||||
|
||||
def assertNoIdleSleep():
|
||||
"""Uses IOKit functions to prevent idle sleep"""
|
||||
# based on code by Michael Lynn, pudquick@github
|
||||
|
||||
kIOPMAssertionTypeNoIdleSleep = "NoIdleSleepAssertion"
|
||||
kIOPMAssertionLevelOn = 255
|
||||
reason = "Munki is installing software"
|
||||
|
||||
dummy_errcode, assertID = IOPMAssertionCreateWithName(
|
||||
kIOPMAssertionTypeNoIdleSleep,
|
||||
kIOPMAssertionLevelOn,
|
||||
reason)
|
||||
return assertID
|
||||
|
||||
|
||||
def run(only_unattended=False):
|
||||
"""Runs the install/removal session.
|
||||
|
||||
@@ -1208,7 +1160,7 @@ def run(only_unattended=False):
|
||||
only_unattended: Boolean. If True, only do unattended_(un)install pkgs.
|
||||
"""
|
||||
# hold onto the assertionID so we can release it later
|
||||
no_idle_sleep_assertion_id = assertNoIdleSleep()
|
||||
no_idle_sleep_assertion_id = powermgr.assertNoIdleSleep()
|
||||
|
||||
managedinstallbase = munkicommon.pref('ManagedInstallDir')
|
||||
installdir = os.path.join(managedinstallbase, 'Cache')
|
||||
@@ -1328,7 +1280,6 @@ def run(only_unattended=False):
|
||||
|
||||
munkicommon.savereport()
|
||||
|
||||
# release our Power Manager assertion
|
||||
dummy_errcode = IOPMAssertionRelease(no_idle_sleep_assertion_id)
|
||||
powermgr.removeNoIdleSleepAssertion(no_idle_sleep_assertion_id)
|
||||
|
||||
return removals_need_restart or installs_need_restart
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2014 Greg Neagle.
|
||||
# Copyright 2014-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -483,7 +483,7 @@ def debug_output():
|
||||
munkicommon.display_debug1(
|
||||
security('show-keychain-info', keychainfile))
|
||||
except SecurityError, err:
|
||||
munkicommon.display_error(str(err))
|
||||
munkicommon.display_error(unicode(err))
|
||||
|
||||
|
||||
class SecurityError(Exception):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2011-2014 Greg Neagle.
|
||||
# Copyright 2011-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -519,7 +519,15 @@ def validateDateFormat(datetime_string):
|
||||
|
||||
def log(msg, logname=''):
|
||||
"""Generic logging function."""
|
||||
logging.info(msg) # noop unless configure_syslog() is called first.
|
||||
if len(msg) > 1000:
|
||||
# See http://bugs.python.org/issue11907 and RFC-3164
|
||||
# break up huge msg into chunks and send 1000 characters at a time
|
||||
msg_buffer = msg
|
||||
while msg_buffer:
|
||||
logging.info(msg_buffer[:1000])
|
||||
msg_buffer = msg_buffer[1000:]
|
||||
else:
|
||||
logging.info(msg) # noop unless configure_syslog() is called first.
|
||||
|
||||
# date/time format string
|
||||
formatstr = '%b %d %Y %H:%M:%S %z'
|
||||
@@ -2347,8 +2355,8 @@ def get_hardware_info():
|
||||
return {}
|
||||
|
||||
|
||||
def get_ipv4_addresses():
|
||||
'''Uses system profiler to get active IPv4 addresses for this machine'''
|
||||
def get_ip_addresses(version):
|
||||
'''Uses system profiler to get active IP addresses for this machine'''
|
||||
ip_addresses = []
|
||||
cmd = ['/usr/sbin/system_profiler', 'SPNetworkDataType', '-xml']
|
||||
proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
|
||||
@@ -2367,9 +2375,9 @@ def get_ipv4_addresses():
|
||||
|
||||
for item in items:
|
||||
try:
|
||||
ip_addresses.extend(item['IPv4']['Addresses'])
|
||||
ip_addresses.extend(item[version]['Addresses'])
|
||||
except KeyError:
|
||||
# 'IPv4" or 'Addresses' is empty, so we ignore
|
||||
# 'IPv4", 'IPv6' or 'Addresses' is empty, so we ignore
|
||||
# this item
|
||||
pass
|
||||
return ip_addresses
|
||||
@@ -2401,7 +2409,8 @@ def getMachineFacts():
|
||||
hardware_info = get_hardware_info()
|
||||
MACHINE['machine_model'] = hardware_info.get('machine_model', 'UNKNOWN')
|
||||
MACHINE['munki_version'] = get_version()
|
||||
MACHINE['ipv4_address'] = get_ipv4_addresses()
|
||||
MACHINE['ipv4_address'] = get_ip_addresses('IPv4')
|
||||
MACHINE['ipv6_address'] = get_ip_addresses('IPv6')
|
||||
MACHINE['serial_number'] = hardware_info.get('serial_number', 'UNKNOWN')
|
||||
|
||||
if MACHINE['arch'] == 'x86_64':
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""
|
||||
powermgr.py
|
||||
munki module to toggle IOKit/PowerManager idle sleep assertions.
|
||||
"""
|
||||
|
||||
# pylint: disable=E0611
|
||||
# stuff for IOKit/PowerManager, courtesy Michael Lynn, pudquick@github
|
||||
from ctypes import c_uint32, cdll, c_void_p, POINTER, byref
|
||||
from CoreFoundation import CFStringCreateWithCString
|
||||
from CoreFoundation import kCFStringEncodingASCII
|
||||
from objc import pyobjc_id
|
||||
# pylint: enable=E0611
|
||||
|
||||
# lots of camelCase names
|
||||
# pylint: disable=C0103
|
||||
|
||||
libIOKit = cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
|
||||
libIOKit.IOPMAssertionCreateWithName.argtypes = [
|
||||
c_void_p, c_uint32, c_void_p, POINTER(c_uint32)]
|
||||
libIOKit.IOPMAssertionRelease.argtypes = [c_uint32]
|
||||
|
||||
|
||||
def _CFSTR(py_string):
|
||||
"""Returns a CFString given a Python string."""
|
||||
return CFStringCreateWithCString(None, py_string, kCFStringEncodingASCII)
|
||||
|
||||
|
||||
def _rawPointer(pyobjc_string):
|
||||
"""Returns a pointer to a CFString."""
|
||||
return pyobjc_id(pyobjc_string.nsstring())
|
||||
|
||||
|
||||
def _IOPMAssertionCreateWithName(assert_name, assert_level, assert_msg):
|
||||
"""Creaes a PowerManager assertion."""
|
||||
assertID = c_uint32(0)
|
||||
p_assert_name = _rawPointer(_CFSTR(assert_name))
|
||||
p_assert_msg = _rawPointer(_CFSTR(assert_msg))
|
||||
errcode = libIOKit.IOPMAssertionCreateWithName(
|
||||
p_assert_name, assert_level, p_assert_msg, byref(assertID))
|
||||
return (errcode, assertID)
|
||||
|
||||
|
||||
def assertNoIdleSleep():
|
||||
"""Uses IOKit functions to prevent idle sleep."""
|
||||
# based on code by Michael Lynn, pudquick@github
|
||||
kIOPMAssertionTypeNoIdleSleep = "NoIdleSleepAssertion"
|
||||
kIOPMAssertionLevelOn = 255
|
||||
reason = "Munki is installing software"
|
||||
|
||||
dummy_errcode, assertID = _IOPMAssertionCreateWithName(
|
||||
kIOPMAssertionTypeNoIdleSleep,
|
||||
kIOPMAssertionLevelOn,
|
||||
reason)
|
||||
return assertID
|
||||
|
||||
|
||||
def removeNoIdleSleepAssertion(assertion_id):
|
||||
"""Uses IOKit functions to remove a "no idle sleep" assertion."""
|
||||
return libIOKit.IOPMAssertionRelease(assertion_id)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2014 Greg Neagle.
|
||||
# Copyright 2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 Greg Neagle.
|
||||
# Copyright 2009-2016 Greg Neagle.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -335,7 +335,7 @@ def ImportBom(bompath, curs):
|
||||
package database into our internal package database.
|
||||
"""
|
||||
# If we completely trusted the accuracy of Apple's database, we wouldn't
|
||||
# need the bom files, but in my enviroment at least, the bom files are
|
||||
# need the bom files, but in my environment at least, the bom files are
|
||||
# a better indicator of what flat packages have actually been installed
|
||||
# on the current machine.
|
||||
# We still need to consult Apple's package database
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user