mirror of
https://github.com/munki/munki.git
synced 2026-01-07 23:20:00 -06:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -34,7 +34,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 */
|
||||
|
||||
@@ -335,7 +335,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = MSC;
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0720;
|
||||
ORGANIZATIONNAME = "The Munki Project";
|
||||
TargetAttributes = {
|
||||
C0EF96B01ADDB90B002C02FF = {
|
||||
@@ -606,6 +606,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 +650,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 +665,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 +675,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 +689,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 +699,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 +731,7 @@
|
||||
"-framework",
|
||||
AppKit,
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SKIP_INSTALL = NO;
|
||||
@@ -741,7 +742,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 +770,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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,23 @@
|
||||
</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>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: "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: "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: "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 "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 "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: 28px 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 "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 "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: "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: "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;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -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 "Helvetica Neue", Helvetica
|
||||
}
|
||||
#updates #header .scan-progress {
|
||||
padding: 0 0 1px 13px
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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,9 @@
|
||||
<?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="7706" systemVersion="15D21" 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"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/>
|
||||
<plugIn identifier="com.apple.WebKitIBPlugin" version="7706"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication"/>
|
||||
@@ -211,11 +211,13 @@
|
||||
<webView id="S5Y-lq-2nB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1143" height="730"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<webPreferences key="preferences" defaultFontSize="12" defaultFixedFontSize="12" plugInsEnabled="NO" javaEnabled="NO">
|
||||
<nil key="identifier"/>
|
||||
</webPreferences>
|
||||
</webView>
|
||||
</subviews>
|
||||
<animations/>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="1393D938-3469-4EFC-8191-CC1B9C26850B" autosavesConfiguration="NO" allowsUserCustomization="NO" displayMode="iconAndLabel" sizeMode="regular" id="c5f-Iv-Tk6">
|
||||
<allowedToolbarItems>
|
||||
@@ -228,6 +230,7 @@
|
||||
<button key="view" id="CPR-oF-gUx" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="3" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
<buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="AllItemsTemplate" imagePosition="overlaps" alignment="center" inset="2" id="6UP-LY-MAs" customClass="MSCToolbarButtonCell">
|
||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
@@ -244,6 +247,7 @@
|
||||
<button key="view" id="YGi-pE-3SV" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="8" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
<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"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -260,6 +264,7 @@
|
||||
<button key="view" id="eek-hQ-v8t" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="4" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
<buttonCell key="cell" type="squareTextured" bezelStyle="texturedSquare" image="MyStuffTemplate" imagePosition="only" alignment="center" inset="2" id="Z0f-h1-4qy" customClass="MSCToolbarButtonCell">
|
||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -276,6 +281,7 @@
|
||||
<button key="view" id="caG-fL-UJh" customClass="MSCToolbarButton">
|
||||
<rect key="frame" x="1" y="14" width="48" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
<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"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -292,6 +298,7 @@
|
||||
<customView key="view" id="Mui-Sc-36b">
|
||||
<rect key="frame" x="38" y="14" width="4" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<animations/>
|
||||
</customView>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="DF488649-5B94-417F-B23E-C646362F6FCA" label="" paletteLabel="Navigation" tag="-1" id="CEb-sx-e3H">
|
||||
@@ -305,13 +312,15 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
</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"/>
|
||||
<animations/>
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="ZsM-fh-8z5">
|
||||
<font key="font" metaFont="system"/>
|
||||
<segments>
|
||||
@@ -324,9 +333,11 @@
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<animations/>
|
||||
</view>
|
||||
<animations/>
|
||||
<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,8 +352,9 @@
|
||||
<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"/>
|
||||
<animations/>
|
||||
<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"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -353,9 +365,11 @@
|
||||
</connections>
|
||||
</searchField>
|
||||
</subviews>
|
||||
<animations/>
|
||||
</view>
|
||||
<animations/>
|
||||
<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>
|
||||
@@ -417,6 +431,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="22.079999923706055" height="22.079999923706055"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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 |
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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()
|
||||
|
||||
@@ -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 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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -6,7 +6,7 @@ appleupdates.py
|
||||
Utilities for dealing with Apple Software Update.
|
||||
|
||||
"""
|
||||
# Copyright 2009-2015 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.
|
||||
@@ -1610,7 +1610,7 @@ class AppleUpdates(object):
|
||||
if key in metadata_to_copy and metadata[key]:
|
||||
if key == 'RestartAction':
|
||||
# Ensure that a heavier weighted 'RestartAction' is not
|
||||
# overriden by one supplied in metadata
|
||||
# overridden by one supplied in metadata
|
||||
if metadata[key] not in RestartActions.get(
|
||||
item.get(key, 'None')):
|
||||
munkicommon.display_debug2(
|
||||
|
||||
@@ -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.
|
||||
@@ -18,6 +18,7 @@
|
||||
gurl.py
|
||||
|
||||
Created by Greg Neagle on 2013-11-21.
|
||||
Modified in Feb 2016 to add support for NSURLSession.
|
||||
|
||||
curl replacement using NSURLConnection and friends
|
||||
"""
|
||||
@@ -32,22 +33,63 @@ from objc import super
|
||||
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
|
||||
# No name 'Foo' in module 'Bar' warnings. Disable them.
|
||||
# pylint: disable=E0611
|
||||
from Foundation import NSBundle
|
||||
from Foundation import NSRunLoop, NSDate
|
||||
from Foundation import NSObject, NSURL, NSURLConnection
|
||||
from Foundation import NSMutableURLRequest
|
||||
from Foundation import NSURLRequestReloadIgnoringLocalCacheData
|
||||
from Foundation import NSURLResponseUnknownLength
|
||||
from Foundation import NSLog
|
||||
from Foundation import NSURLCredential, NSURLCredentialPersistenceNone
|
||||
from Foundation import NSPropertyListSerialization
|
||||
from Foundation import NSPropertyListMutableContainersAndLeaves
|
||||
from Foundation import NSPropertyListXMLFormat_v1_0
|
||||
# pylint: enable=E0611
|
||||
from Foundation import NSBundle, \
|
||||
NSRunLoop, NSDate, \
|
||||
NSObject, NSURL, NSURLConnection, \
|
||||
NSMutableURLRequest, \
|
||||
NSURLRequestReloadIgnoringLocalCacheData, \
|
||||
NSURLResponseUnknownLength, \
|
||||
NSLog, \
|
||||
NSURLCredential, NSURLCredentialPersistenceNone, \
|
||||
NSPropertyListSerialization, \
|
||||
NSPropertyListMutableContainersAndLeaves, \
|
||||
NSPropertyListXMLFormat_v1_0
|
||||
|
||||
try:
|
||||
from Foundation import NSURLSession, NSURLSessionConfiguration
|
||||
NSURLSESSION_AVAILABLE = True
|
||||
except ImportError:
|
||||
NSURLSESSION_AVAILABLE = False
|
||||
|
||||
# Disable PyLint complaining about 'invalid' names
|
||||
# pylint: disable=C0103
|
||||
|
||||
if NSURLSESSION_AVAILABLE:
|
||||
# NSURLSessionAuthChallengeDisposition enum constants
|
||||
NSURLSessionAuthChallengeUseCredential = 0
|
||||
NSURLSessionAuthChallengePerformDefaultHandling = 1
|
||||
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2
|
||||
NSURLSessionAuthChallengeRejectProtectionSpace = 3
|
||||
|
||||
# NSURLSessionResponseDisposition enum constants
|
||||
NSURLSessionResponseCancel = 0
|
||||
NSURLSessionResponseAllow = 1
|
||||
NSURLSessionResponseBecomeDownload = 2
|
||||
|
||||
# TLS/SSLProtocol enum constants
|
||||
kSSLProtocolUnknown = 0
|
||||
kSSLProtocol3 = 2
|
||||
kTLSProtocol1 = 4
|
||||
kTLSProtocol11 = 7
|
||||
kTLSProtocol12 = 8
|
||||
kDTLSProtocol1 = 9
|
||||
|
||||
# define a helper function for block callbacks
|
||||
import ctypes
|
||||
import objc
|
||||
_objc_so = ctypes.cdll.LoadLibrary(
|
||||
os.path.join(objc.__path__[0], '_objc.so'))
|
||||
PyObjCMethodSignature_WithMetaData = (
|
||||
_objc_so.PyObjCMethodSignature_WithMetaData)
|
||||
PyObjCMethodSignature_WithMetaData.restype = ctypes.py_object
|
||||
|
||||
def objc_method_signature(signature_str):
|
||||
'''Return a PyObjCMethodSignature given a call signature in string
|
||||
format'''
|
||||
return PyObjCMethodSignature_WithMetaData(
|
||||
ctypes.create_string_buffer(signature_str), None, False)
|
||||
|
||||
# pylint: enable=E0611
|
||||
|
||||
# disturbing hack warning!
|
||||
# this works around an issue with App Transport Security on 10.11
|
||||
@@ -56,6 +98,11 @@ info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
|
||||
info['NSAppTransportSecurity'] = {'NSAllowsArbitraryLoads': True}
|
||||
|
||||
|
||||
def NSLogWrapper(message):
|
||||
'''A wrapper function for NSLog to prevent format string errors'''
|
||||
NSLog('%@', message)
|
||||
|
||||
|
||||
ssl_error_codes = {
|
||||
-9800: u'SSL protocol error',
|
||||
-9801: u'Cipher Suite negotiation failure',
|
||||
@@ -111,7 +158,7 @@ ssl_error_codes = {
|
||||
|
||||
class Gurl(NSObject):
|
||||
'''A class for getting content from a URL
|
||||
using NSURLConnection and friends'''
|
||||
using NSURLConnection/NSURLSession and friends'''
|
||||
|
||||
# since we inherit from NSObject, PyLint issues a few bogus warnings
|
||||
# pylint: disable=W0232,E1002
|
||||
@@ -139,8 +186,11 @@ class Gurl(NSObject):
|
||||
'download_only_if_changed', False)
|
||||
self.cache_data = options.get('cache_data')
|
||||
self.connection_timeout = options.get('connection_timeout', 60)
|
||||
if NSURLSESSION_AVAILABLE:
|
||||
self.minimum_tls_protocol = options.get(
|
||||
'minimum_tls_protocol', kTLSProtocol1)
|
||||
|
||||
self.log = options.get('logging_function', NSLog)
|
||||
self.log = options.get('logging_function', NSLogWrapper)
|
||||
|
||||
self.resume = False
|
||||
self.response = None
|
||||
@@ -155,6 +205,8 @@ class Gurl(NSObject):
|
||||
self.expectedLength = -1
|
||||
self.percentComplete = 0
|
||||
self.connection = None
|
||||
self.session = None
|
||||
self.task = None
|
||||
return self
|
||||
|
||||
def start(self):
|
||||
@@ -189,18 +241,34 @@ class Gurl(NSObject):
|
||||
if 'etag' in stored_data:
|
||||
request.setValue_forHTTPHeaderField_(
|
||||
stored_data['etag'], 'if-none-match')
|
||||
self.connection = NSURLConnection.alloc().initWithRequest_delegate_(
|
||||
request, self)
|
||||
if NSURLSESSION_AVAILABLE:
|
||||
configuration = \
|
||||
NSURLSessionConfiguration.defaultSessionConfiguration()
|
||||
# set minumum supported TLS protocol (defaults to TLS1)
|
||||
configuration.setTLSMinimumSupportedProtocol_(
|
||||
self.minimum_tls_protocol)
|
||||
|
||||
self.session = \
|
||||
NSURLSession.sessionWithConfiguration_delegate_delegateQueue_(
|
||||
configuration, self, None)
|
||||
self.task = self.session.dataTaskWithRequest_(request)
|
||||
self.task.resume()
|
||||
else:
|
||||
self.connection = NSURLConnection.alloc().initWithRequest_delegate_(
|
||||
request, self)
|
||||
|
||||
def cancel(self):
|
||||
'''Cancel the connection'''
|
||||
if self.connection:
|
||||
self.connection.cancel()
|
||||
if NSURLSESSION_AVAILABLE:
|
||||
self.session.invalidateAndCancel()
|
||||
else:
|
||||
self.connection.cancel()
|
||||
self.done = True
|
||||
|
||||
def isDone(self):
|
||||
'''Check if the connection request is complete. As a side effect,
|
||||
allow the delegates to work my letting the run loop run for a bit'''
|
||||
allow the delegates to work by letting the run loop run for a bit'''
|
||||
if self.done:
|
||||
return self.done
|
||||
# let the delegates do their thing
|
||||
@@ -255,13 +323,8 @@ class Gurl(NSObject):
|
||||
new_dict[key.lower()] = value
|
||||
return new_dict
|
||||
|
||||
def connection_didFailWithError_(self, connection, error):
|
||||
'''NSURLConnection delegate method
|
||||
Sent when a connection fails to load its request successfully.'''
|
||||
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
|
||||
def recordError_(self, error):
|
||||
'''Record any error info from completed connection/session'''
|
||||
self.error = error
|
||||
# If this was an SSL error, try to extract the SSL error code.
|
||||
if 'NSUnderlyingError' in error.userInfo():
|
||||
@@ -270,13 +333,40 @@ class Gurl(NSObject):
|
||||
if ssl_code:
|
||||
self.SSLerror = (ssl_code, ssl_error_codes.get(
|
||||
ssl_code, 'Unknown SSL error'))
|
||||
|
||||
def removeExpectedSizeFromStoredHeaders(self):
|
||||
'''If a successful transfer, clear the expected size so we
|
||||
don\'t attempt to resume the download next time'''
|
||||
if str(self.status).startswith('2'):
|
||||
# remove the expected-size from the stored headers
|
||||
headers = self.get_stored_headers()
|
||||
if 'expected-length' in headers:
|
||||
del headers['expected-length']
|
||||
self.store_headers(headers)
|
||||
|
||||
def URLSession_task_didCompleteWithError_(self, session, task, error):
|
||||
'''NSURLSessionTaskDelegate method.'''
|
||||
# we don't actually use the session or task arguments, so
|
||||
# pylint: disable=W0613
|
||||
if self.destination and self.destination_path:
|
||||
self.destination.close()
|
||||
self.removeExpectedSizeFromStoredHeaders()
|
||||
if error:
|
||||
self.recordError_(error)
|
||||
self.done = True
|
||||
|
||||
def connection_didFailWithError_(self, connection, error):
|
||||
'''NSURLConnectionDelegate method
|
||||
Sent when a connection fails to load its request successfully.'''
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
self.recordError_(error)
|
||||
self.done = True
|
||||
if self.destination and self.destination_path:
|
||||
self.destination.close()
|
||||
# delete it? Might not want to...
|
||||
|
||||
def connectionDidFinishLoading_(self, connection):
|
||||
'''NSURLConnectionDataDelegat delegate method
|
||||
'''NSURLConnectionDataDelegate method
|
||||
Sent when a connection has finished loading successfully.'''
|
||||
|
||||
# we don't actually use the connection argument, so
|
||||
@@ -285,18 +375,11 @@ class Gurl(NSObject):
|
||||
self.done = True
|
||||
if self.destination and self.destination_path:
|
||||
self.destination.close()
|
||||
if str(self.status).startswith('2'):
|
||||
# remove the expected-size from the stored headers
|
||||
headers = self.get_stored_headers()
|
||||
if 'expected-length' in headers:
|
||||
del headers['expected-length']
|
||||
self.store_headers(headers)
|
||||
|
||||
def connection_didReceiveResponse_(self, connection, response):
|
||||
'''NSURLConnectionDataDelegate delegate method
|
||||
Sent when the connection has received sufficient data to construct the
|
||||
URL response for its request.'''
|
||||
self.removeExpectedSizeFromStoredHeaders()
|
||||
|
||||
def handleResponse_withCompletionHandler_(
|
||||
self, response, completionHandler):
|
||||
'''Handle the response to the connection'''
|
||||
self.response = response
|
||||
self.bytesReceived = 0
|
||||
self.percentComplete = -1
|
||||
@@ -330,7 +413,12 @@ class Gurl(NSObject):
|
||||
# we have a partial for
|
||||
self.log(
|
||||
'Can\'t resume download; file on server has changed.')
|
||||
connection.cancel()
|
||||
if completionHandler:
|
||||
# tell the session task to cancel
|
||||
completionHandler(NSURLSessionResponseCancel)
|
||||
else:
|
||||
# cancel the connection
|
||||
self.connection.cancel()
|
||||
self.log('Removing %s' % self.destination_path)
|
||||
os.unlink(self.destination_path)
|
||||
# restart and attempt to download the entire file
|
||||
@@ -355,85 +443,107 @@ class Gurl(NSObject):
|
||||
# the downloadand for future checking if the file on the server
|
||||
# has changed
|
||||
self.store_headers(download_data)
|
||||
if completionHandler:
|
||||
# tell the session task to continue
|
||||
completionHandler(NSURLSessionResponseAllow)
|
||||
|
||||
def connection_willSendRequest_redirectResponse_(
|
||||
self, connection, request, response):
|
||||
def URLSession_dataTask_didReceiveResponse_completionHandler_(
|
||||
self, session, task, response, completionHandler):
|
||||
'''NSURLSessionDataDelegate method'''
|
||||
# we don't actually use the session or task arguments, so
|
||||
# pylint: disable=W0613
|
||||
completionHandler.__block_signature__ = objc_method_signature('v@i')
|
||||
self.handleResponse_withCompletionHandler_(response, completionHandler)
|
||||
|
||||
def connection_didReceiveResponse_(self, connection, response):
|
||||
'''NSURLConnectionDataDelegate delegate method
|
||||
Sent when the connection determines that it must change URLs in order to
|
||||
continue loading a request.'''
|
||||
|
||||
Sent when the connection has received sufficient data to construct the
|
||||
URL response for its request.'''
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
self.handleResponse_withCompletionHandler_(response, None)
|
||||
|
||||
def handleRedirect_newRequest_withCompletionHandler_(
|
||||
self, response, request, completionHandler):
|
||||
'''Handle the redirect request'''
|
||||
if response == None:
|
||||
# This isn't a real redirect, this is without talking to a server.
|
||||
# Pass it back as-is
|
||||
return request
|
||||
# But if we're here, it appears to be a real redirect attempt
|
||||
# the request has changed the NSURLRequest in order to standardize
|
||||
# its format, for example, changing a request for
|
||||
# http://www.apple.com to http://www.apple.com/. This occurs because
|
||||
# the standardized, or canonical, version of the request is used for
|
||||
# cache management. Pass the request back as-is
|
||||
# (it appears that at some point Apple also defined a redirect like
|
||||
# http://developer.apple.com to https://developer.apple.com to be
|
||||
# 'merely' a change in the canonical URL.)
|
||||
# Further -- it appears that this delegate method isn't called at
|
||||
# all in this scenario, unlike NSConnectionDelegate method
|
||||
# connection:willSendRequest:redirectResponse:
|
||||
# we'll leave this here anyway in case we're wrong about that
|
||||
if completionHandler:
|
||||
completionHandler(request)
|
||||
return
|
||||
else:
|
||||
return request
|
||||
# If we get here, it appears to be a real redirect attempt
|
||||
# Annoyingly, we apparently can't get access to the headers from the
|
||||
# site that told us to redirect. All we know is that we were told
|
||||
# to redirect and where the new location is.
|
||||
newURL = request.URL().absoluteString()
|
||||
self.redirection.append([newURL, dict(response.allHeaderFields())])
|
||||
newParsedURL = urlparse(newURL)
|
||||
# This code was largely based on the work of Andreas Fuchs
|
||||
# This code was largely based on the work of Andreas Fuchs
|
||||
# (https://github.com/munki/munki/pull/465)
|
||||
if self.follow_redirects == True or self.follow_redirects == 'all':
|
||||
# Allow the redirect
|
||||
self.log('Allowing redirect to: %s' % newURL)
|
||||
return request
|
||||
elif self.follow_redirects == 'https' and newParsedURL.scheme == 'https':
|
||||
if completionHandler:
|
||||
completionHandler(request)
|
||||
return
|
||||
else:
|
||||
return request
|
||||
elif (self.follow_redirects == 'https'
|
||||
and newParsedURL.scheme == 'https'):
|
||||
# Once again, allow the redirect
|
||||
self.log('Allowing redirect to: %s' % newURL)
|
||||
return request
|
||||
if completionHandler:
|
||||
completionHandler(request)
|
||||
return
|
||||
else:
|
||||
return request
|
||||
else:
|
||||
# If we're down here either the preference was set to 'none',
|
||||
# the url we're forwarding on to isn't https or follow_redirects
|
||||
# was explicitly set to False
|
||||
self.log('Denying redirect to: %s' % newURL)
|
||||
return None
|
||||
if completionHandler:
|
||||
completionHandler(None)
|
||||
return
|
||||
else:
|
||||
return None
|
||||
|
||||
def connection_willSendRequestForAuthenticationChallenge_(
|
||||
self, connection, challenge):
|
||||
'''NSURLConnection delegate method
|
||||
Tells the delegate that the connection will send a request for an
|
||||
authentication challenge.
|
||||
New in 10.7.'''
|
||||
def URLSession_task_willPerformHTTPRedirection_newRequest_completionHandler_(
|
||||
self, session, task, response, request, completionHandler):
|
||||
'''NSURLSessionTaskDelegate method'''
|
||||
# we don't actually use the session or task arguments, so
|
||||
# pylint: disable=W0613
|
||||
self.log(
|
||||
'URLSession_task_willPerformHTTPRedirection_newRequest_'
|
||||
'completionHandler_')
|
||||
completionHandler.__block_signature__ = objc_method_signature('v@@')
|
||||
self.handleRedirect_newRequest_withCompletionHandler_(
|
||||
response, request, completionHandler)
|
||||
|
||||
def connection_willSendRequest_redirectResponse_(
|
||||
self, connection, request, response):
|
||||
'''NSURLConnectionDataDelegate method
|
||||
Sent when the connection determines that it must change URLs in order to
|
||||
continue loading a request.'''
|
||||
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
|
||||
self.log('connection_willSendRequestForAuthenticationChallenge_')
|
||||
protectionSpace = challenge.protectionSpace()
|
||||
host = protectionSpace.host()
|
||||
realm = protectionSpace.realm()
|
||||
authenticationMethod = protectionSpace.authenticationMethod()
|
||||
self.log(
|
||||
'Authentication challenge for Host: %s Realm: %s AuthMethod: %s'
|
||||
% (host, realm, authenticationMethod))
|
||||
if challenge.previousFailureCount() > 0:
|
||||
# we have the wrong credentials. just fail
|
||||
self.log('Previous authentication attempt failed.')
|
||||
challenge.sender().cancelAuthenticationChallenge_(challenge)
|
||||
if self.username and self.password and authenticationMethod in [
|
||||
'NSURLAuthenticationMethodDefault',
|
||||
'NSURLAuthenticationMethodHTTPBasic',
|
||||
'NSURLAuthenticationMethodHTTPDigest']:
|
||||
self.log('Will attempt to authenticate.')
|
||||
self.log('Username: %s Password: %s'
|
||||
% (self.username, ('*' * len(self.password or ''))))
|
||||
credential = (
|
||||
NSURLCredential.credentialWithUser_password_persistence_(
|
||||
self.username, self.password,
|
||||
NSURLCredentialPersistenceNone))
|
||||
challenge.sender().useCredential_forAuthenticationChallenge_(
|
||||
credential, challenge)
|
||||
else:
|
||||
# fall back to system-provided default behavior
|
||||
self.log('Allowing OS to handle authentication request')
|
||||
challenge.sender(
|
||||
).performDefaultHandlingForAuthenticationChallenge_(
|
||||
challenge)
|
||||
self.log('connection_willSendRequest_redirectResponse_')
|
||||
return self.handleRedirect_newRequest_withCompletionHandler_(
|
||||
response, request, None)
|
||||
|
||||
def connection_canAuthenticateAgainstProtectionSpace_(
|
||||
self, connection, protectionSpace):
|
||||
@@ -464,17 +574,9 @@ class Gurl(NSObject):
|
||||
self.log('Allowing OS to handle authentication request')
|
||||
return False
|
||||
|
||||
def connection_didReceiveAuthenticationChallenge_(
|
||||
self, connection, challenge):
|
||||
'''NSURLConnection delegate method
|
||||
Sent when a connection must authenticate a challenge in order to
|
||||
download its request.
|
||||
Deprecated in 10.10'''
|
||||
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
|
||||
self.log('connection_didReceiveAuthenticationChallenge_')
|
||||
def handleChallenge_withCompletionHandler_(
|
||||
self, challenge, completionHandler):
|
||||
'''Handle an authentication challenge'''
|
||||
protectionSpace = challenge.protectionSpace()
|
||||
host = protectionSpace.host()
|
||||
realm = protectionSpace.realm()
|
||||
@@ -485,7 +587,12 @@ class Gurl(NSObject):
|
||||
if challenge.previousFailureCount() > 0:
|
||||
# we have the wrong credentials. just fail
|
||||
self.log('Previous authentication attempt failed.')
|
||||
challenge.sender().cancelAuthenticationChallenge_(challenge)
|
||||
if completionHandler:
|
||||
completionHandler(
|
||||
NSURLSessionAuthChallengeCancelAuthenticationChallenge,
|
||||
None)
|
||||
else:
|
||||
challenge.sender().cancelAuthenticationChallenge_(challenge)
|
||||
if self.username and self.password and authenticationMethod in [
|
||||
'NSURLAuthenticationMethodDefault',
|
||||
'NSURLAuthenticationMethodHTTPBasic',
|
||||
@@ -497,22 +604,65 @@ class Gurl(NSObject):
|
||||
NSURLCredential.credentialWithUser_password_persistence_(
|
||||
self.username, self.password,
|
||||
NSURLCredentialPersistenceNone))
|
||||
challenge.sender().useCredential_forAuthenticationChallenge_(
|
||||
credential, challenge)
|
||||
if completionHandler:
|
||||
completionHandler(
|
||||
NSURLSessionAuthChallengeUseCredential, credential)
|
||||
else:
|
||||
challenge.sender().useCredential_forAuthenticationChallenge_(
|
||||
credential, challenge)
|
||||
else:
|
||||
# fall back to system-provided default behavior
|
||||
self.log('Continuing without credential.')
|
||||
challenge.sender(
|
||||
).continueWithoutCredentialForAuthenticationChallenge_(
|
||||
challenge)
|
||||
|
||||
def connection_didReceiveData_(self, connection, data):
|
||||
'''NSURLConnectionDataDelegate method
|
||||
Sent as a connection loads data incrementally'''
|
||||
self.log('Allowing OS to handle authentication request')
|
||||
if completionHandler:
|
||||
completionHandler(
|
||||
NSURLSessionAuthChallengePerformDefaultHandling, None)
|
||||
else:
|
||||
if (challenge.sender().respondsToSelector_(
|
||||
'performDefaultHandlingForAuthenticationChallenge:')):
|
||||
self.log('Allowing OS to handle authentication request')
|
||||
challenge.sender(
|
||||
).performDefaultHandlingForAuthenticationChallenge_(
|
||||
challenge)
|
||||
else:
|
||||
# Mac OS X 10.6 doesn't support
|
||||
# performDefaultHandlingForAuthenticationChallenge:
|
||||
self.log('Continuing without credential.')
|
||||
challenge.sender(
|
||||
).continueWithoutCredentialForAuthenticationChallenge_(
|
||||
challenge)
|
||||
|
||||
def connection_willSendRequestForAuthenticationChallenge_(
|
||||
self, connection, challenge):
|
||||
'''NSURLConnection delegate method
|
||||
Tells the delegate that the connection will send a request for an
|
||||
authentication challenge. New in 10.7.'''
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
self.log('connection_willSendRequestForAuthenticationChallenge_')
|
||||
self.handleChallenge_withCompletionHandler_(challenge, None)
|
||||
|
||||
def URLSession_task_didReceiveChallenge_completionHandler_(
|
||||
self, session, task, challenge, completionHandler):
|
||||
'''NSURLSessionTaskDelegate method'''
|
||||
# we don't actually use the session or task arguments, so
|
||||
# pylint: disable=W0613
|
||||
completionHandler.__block_signature__ = objc_method_signature('v@i@')
|
||||
self.log('URLSession_task_didReceiveChallenge_completionHandler_')
|
||||
self.handleChallenge_withCompletionHandler_(
|
||||
challenge, completionHandler)
|
||||
|
||||
def connection_didReceiveAuthenticationChallenge_(
|
||||
self, connection, challenge):
|
||||
'''NSURLConnection delegate method
|
||||
Sent when a connection must authenticate a challenge in order to
|
||||
download its request. Deprecated in 10.10'''
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
self.log('connection_didReceiveAuthenticationChallenge_')
|
||||
self.handleChallenge_withCompletionHandler_(challenge, None)
|
||||
|
||||
def handleReceivedData_(self, data):
|
||||
'''Handle received data'''
|
||||
if self.destination:
|
||||
self.destination.write(str(data))
|
||||
else:
|
||||
@@ -521,3 +671,16 @@ class Gurl(NSObject):
|
||||
if self.expectedLength != NSURLResponseUnknownLength:
|
||||
self.percentComplete = int(
|
||||
float(self.bytesReceived)/float(self.expectedLength) * 100.0)
|
||||
|
||||
def URLSession_dataTask_didReceiveData_(self, session, task, data):
|
||||
'''NSURLSessionDataDelegate method'''
|
||||
# we don't actually use the session or task arguments, so
|
||||
# pylint: disable=W0613
|
||||
self.handleReceivedData_(data)
|
||||
|
||||
def connection_didReceiveData_(self, connection, data):
|
||||
'''NSURLConnectionDataDelegate method
|
||||
Sent as a connection loads data incrementally'''
|
||||
# we don't actually use the connection argument, so
|
||||
# pylint: disable=W0613
|
||||
self.handleReceivedData_(data)
|
||||
|
||||
@@ -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.
|
||||
@@ -467,7 +467,7 @@ 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(
|
||||
@@ -506,20 +506,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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -1467,7 +1467,7 @@ def lookForUpdates(itemname, cataloglist):
|
||||
update_list = []
|
||||
for catalogname in cataloglist:
|
||||
if not catalogname in CATALOG.keys():
|
||||
# in case the list refers to a non-existant catalog
|
||||
# in case the list refers to a non-existent catalog
|
||||
continue
|
||||
|
||||
updaters = CATALOG[catalogname]['updaters']
|
||||
@@ -1925,7 +1925,7 @@ def processInstall(manifestitem, cataloglist, installinfo):
|
||||
iteminfo[key] = item_pl[key]
|
||||
|
||||
if not 'apple_item' in iteminfo:
|
||||
# admin did not explictly mark this item; let's determine if
|
||||
# admin did not explicitly mark this item; let's determine if
|
||||
# it's from Apple
|
||||
if isAppleItem(item_pl):
|
||||
munkicommon.log("Marking %s as apple_item - this may affect \
|
||||
@@ -2370,7 +2370,7 @@ def processRemoval(manifestitem, cataloglist, installinfo):
|
||||
iteminfo[key] = uninstall_item[key]
|
||||
|
||||
if not 'apple_item' in iteminfo:
|
||||
# admin did not explictly mark this item; let's determine if
|
||||
# admin did not explicitly mark this item; let's determine if
|
||||
# it's from Apple
|
||||
if isAppleItem(item_pl):
|
||||
iteminfo['apple_item'] = True
|
||||
@@ -2892,7 +2892,7 @@ def download_icons(item_list):
|
||||
|
||||
def download_client_resources():
|
||||
"""Download client customization resources (if any)."""
|
||||
# Munki's preferences can specify an explict name
|
||||
# Munki's preferences can specify an explicit name
|
||||
# under ClientResourcesFilename
|
||||
# if that doesn't exist, use the primary manifest name as the
|
||||
# filename. If that fails, try site_default.zip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2010-2013 Google Inc. All Rights Reserved.
|
||||
# Copyright 2010-2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,7 @@ Created by Justin McWilliams on 2010-10-26.
|
||||
|
||||
Common utility functions used throughout Munki.
|
||||
|
||||
Note: this module should be 100% free of ObjC-dependant Python imports.
|
||||
Note: this module should be 100% free of ObjC-dependent Python imports.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.5.1</string>
|
||||
<string>2.6.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2011-2013 Google Inc. All Rights Reserved.
|
||||
# Copyright 2011-2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,228 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 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.
|
||||
|
||||
"""
|
||||
app2pkg.py
|
||||
|
||||
Created by Greg Neagle on 2009-09-28.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import stat
|
||||
import plistlib
|
||||
import subprocess
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
|
||||
# change these to suit yourself
|
||||
packagemaker = "/Developer/usr/bin/packagemaker"
|
||||
pkgidprefix = "com.myorg.pkg."
|
||||
pkgoutputdir = "/Users/Shared/pkgs"
|
||||
|
||||
|
||||
def makeDMG(pkgpath):
|
||||
print "Making disk image..."
|
||||
cmd = ["/usr/bin/hdiutil", "create", "-srcfolder", pkgpath,
|
||||
pkgpath + ".dmg"]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Disk image creation failed."
|
||||
return(-1)
|
||||
|
||||
|
||||
def main():
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--makedmg', '-d', action='store_true',
|
||||
help='Makes a disk image containing the package.')
|
||||
p.add_option('--name', '-n',
|
||||
help='Specify a name for the package.')
|
||||
p.add_option('--version', '-v',
|
||||
help='Specify a version number for the package.')
|
||||
p.add_option('--displayname',
|
||||
help='Specify a display name for the package.')
|
||||
p.add_option('--description',
|
||||
help='Specify a description for the package.')
|
||||
p.add_option('--id',
|
||||
help='Specify a package id for the package.')
|
||||
# Get our options and our package names
|
||||
options, app_paths = p.parse_args()
|
||||
|
||||
if not os.path.exists(packagemaker):
|
||||
print >>sys.stderr, "packagemaker tool not found at %s." % \
|
||||
packagemaker
|
||||
exit(-1)
|
||||
|
||||
if len(app_paths) == 0:
|
||||
print >>sys.stderr, "You must specify a path to an application!"
|
||||
exit(-1)
|
||||
|
||||
if len(app_paths) > 1:
|
||||
print >>sys.stderr, "You may package only one app at a time."
|
||||
exit(-1)
|
||||
|
||||
app_path = app_paths[0]
|
||||
|
||||
if not os.path.exists(app_path):
|
||||
print "Nothing exists at %s" % app_path
|
||||
exit(-1)
|
||||
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
if options.name:
|
||||
pkgname = options.name
|
||||
else:
|
||||
pkgname = os.path.splitext(os.path.basename(app_path))[0]
|
||||
|
||||
enclosingpath = os.path.dirname(app_path).lstrip("/") or "Applications"
|
||||
|
||||
# make packageroot directory
|
||||
packageroot = os.path.join(mytmpdir, pkgname)
|
||||
os.mkdir(packageroot)
|
||||
application_dir = os.path.join(packageroot, enclosingpath)
|
||||
os.makedirs(application_dir)
|
||||
copytodir = os.path.join(application_dir, os.path.basename(app_path))
|
||||
cmd = ['/usr/bin/ditto', '--noqtn', app_path, copytodir ]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
if p.returncode != 0:
|
||||
print >>sys.stderr, err
|
||||
exit(-1)
|
||||
if err:
|
||||
print >>sys.stderr, err
|
||||
exit(-1)
|
||||
|
||||
# fix uid/gid/perms on directories
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
srcdir = dirpath[len(packageroot):]
|
||||
if srcdir == "": srcdir = "/"
|
||||
os.chown(dirpath, os.stat(srcdir).st_uid, os.stat(srcdir).st_gid)
|
||||
os.chmod(dirpath, stat.S_IMODE(os.stat(srcdir).st_mode))
|
||||
|
||||
pkgid = pkgvers = ""
|
||||
if options.id:
|
||||
pkgid = options.id
|
||||
if options.version:
|
||||
pkgvers = options.version
|
||||
|
||||
appinfo = os.path.join(app_path, "Contents/Info.plist")
|
||||
if os.path.exists(appinfo):
|
||||
pl = plistlib.readPlist(appinfo)
|
||||
if "CFBundleIdentifier" in pl and pkgid == "":
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and pkgvers == "":
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
|
||||
if pkgid == "":
|
||||
pkgid = pkgidprefix + pkgname.lower().replace(" ","_")
|
||||
if pkgvers == "":
|
||||
pkgvers = "1.0.0"
|
||||
|
||||
print "Package name: %s" % pkgname
|
||||
newdisplayname = raw_input("Display name [%s]: "
|
||||
% options.displayname)
|
||||
options.displayname = newdisplayname or options.displayname
|
||||
newdescription = raw_input("Description [%s]: " % options.description)
|
||||
options.description = newdescription or options.description
|
||||
newid = raw_input("PackageID [%s]: " % pkgid)
|
||||
pkgid = newid or pkgid
|
||||
newversion = raw_input("Version [%s]: " % pkgvers)
|
||||
pkgvers = newversion or pkgvers
|
||||
|
||||
print
|
||||
print
|
||||
print "Package name: %s" % pkgname
|
||||
print "Display name: %s" % options.displayname
|
||||
print "Description: %s" % options.description
|
||||
print "PackageID: %s" % pkgid
|
||||
print "Version: %s" % pkgvers
|
||||
print
|
||||
answer = raw_input("Build the package? [y/n] ")
|
||||
if not answer.lower().startswith("y"):
|
||||
exit(0)
|
||||
|
||||
# build package
|
||||
outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
|
||||
cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
|
||||
'--version', pkgvers,
|
||||
'--no-recommend', '--out', outputname, '--verbose',
|
||||
'--filter', '.DS_Store$']
|
||||
print cmd
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Package creation failed."
|
||||
exit(-1)
|
||||
else:
|
||||
# remove relocatable stuff
|
||||
tokendefinitions = os.path.join(outputname,
|
||||
"Contents/Resources/TokenDefinitions.plist")
|
||||
if os.path.exists(tokendefinitions):
|
||||
os.remove(tokendefinitions)
|
||||
infoplist = os.path.join(outputname, "Contents/Info.plist")
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if 'IFPkgPathMappings' in pl:
|
||||
del pl['IFPkgPathMappings']
|
||||
plistlib.writePlist(pl, infoplist)
|
||||
|
||||
if options.displayname or options.description:
|
||||
languages = ['en.lproj', 'English.lproj']
|
||||
for item in languages:
|
||||
lprojpath = os.path.join(outputname,
|
||||
'Contents/Resources', item)
|
||||
if os.path.exists(lprojpath):
|
||||
descriptionplist = os.path.join(lprojpath,
|
||||
"Description.plist")
|
||||
pl = {}
|
||||
pl['IFPkgDescriptionTitle'] = (options.displayname or
|
||||
pkgname)
|
||||
pl['IFPkgDescriptionDescription'] = (options.description
|
||||
or "")
|
||||
plistlib.writePlist(pl, descriptionplist)
|
||||
break
|
||||
|
||||
print "Completed package is at %s" % outputname
|
||||
if options.makedmg:
|
||||
makeDMG(outputname)
|
||||
|
||||
#cleanup temp dir
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,354 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 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.
|
||||
"""
|
||||
filelist2pkg.py
|
||||
|
||||
Created by Greg Neagle on 2009-11-24.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import stat
|
||||
import plistlib
|
||||
import subprocess
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
# change these to suit yourself
|
||||
packagemaker = '/Developer/usr/bin/packagemaker'
|
||||
pkgidprefix = "com.myorg.pkg."
|
||||
pkgoutputdir = "/Users/Shared/pkgs"
|
||||
|
||||
|
||||
def nameAndVersion(s):
|
||||
"""
|
||||
Splits a string into the name and version numbers:
|
||||
'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
|
||||
'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
|
||||
'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
|
||||
"""
|
||||
index = 0
|
||||
for char in s:
|
||||
if char in "0123456789":
|
||||
possibleVersion = s[index:]
|
||||
if not (" " in possibleVersion or "_" in possibleVersion
|
||||
or "-" in possibleVersion or "v" in possibleVersion):
|
||||
return (s[0:index].rstrip(" .-_v"), possibleVersion)
|
||||
index += 1
|
||||
# no version number found, just return original string and empty string
|
||||
return (s, '')
|
||||
|
||||
|
||||
def makeDMG(pkgpath):
|
||||
print "Making disk image..."
|
||||
cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
|
||||
pkgpath, pkgpath + ".dmg"]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Disk image creation failed."
|
||||
return(-1)
|
||||
|
||||
|
||||
def copyItem(sourceitem, packageroot):
|
||||
if not os.path.lexists(sourceitem):
|
||||
print >>sys.stderr, "%s does not exist!" % sourceitem
|
||||
return
|
||||
|
||||
paths = []
|
||||
pathitem = sourceitem
|
||||
while pathitem != "/":
|
||||
pathitem = os.path.dirname(pathitem)
|
||||
paths.append(pathitem)
|
||||
|
||||
paths.reverse()
|
||||
for sourcepath in paths:
|
||||
targetpath = os.path.join(packageroot, sourcepath.lstrip('/'))
|
||||
if not os.path.exists(targetpath):
|
||||
os.mkdir(targetpath)
|
||||
os.chown(targetpath, os.stat(sourcepath).st_uid,
|
||||
os.stat(sourcepath).st_gid)
|
||||
os.chmod(targetpath, stat.S_IMODE(os.stat(sourcepath).st_mode))
|
||||
|
||||
targetitem = os.path.join(packageroot, sourceitem.lstrip('/'))
|
||||
if os.path.isdir(sourceitem) and not os.path.islink(sourceitem):
|
||||
os.mkdir(targetitem)
|
||||
os.chown(targetitem, os.stat(sourceitem).st_uid,
|
||||
os.stat(sourceitem).st_gid)
|
||||
os.chmod(targetitem, stat.S_IMODE(os.stat(sourceitem).st_mode))
|
||||
elif os.path.islink(sourceitem):
|
||||
# make sure we don't follow the symlink
|
||||
err = subprocess.call(['/bin/cp', "-a", sourceitem, targetitem])
|
||||
else:
|
||||
err = subprocess.call(['/bin/cp', "-p", sourceitem, targetitem])
|
||||
|
||||
|
||||
def copyItemsFromList(filelist, packageroot):
|
||||
f = open(filelist, 'rb')
|
||||
while 1:
|
||||
item = f.readline()
|
||||
if not item:
|
||||
break
|
||||
if not (item.startswith('.') or item.startswith("/")):
|
||||
continue
|
||||
item = item.lstrip('./').rstrip('\n')
|
||||
sourceitem = os.path.join("/", item)
|
||||
copyItem(sourceitem, packageroot)
|
||||
f.close()
|
||||
|
||||
|
||||
def main():
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--nomakedmg', action='store_true',
|
||||
help='Don\'t make a disk image containing the package.')
|
||||
p.add_option('--name', '-n',
|
||||
help='Specify a name for the package.')
|
||||
p.add_option('--version', '-v',
|
||||
help='Specify a version number for the package.')
|
||||
p.add_option('--id',
|
||||
help='Specify a package id for the package.')
|
||||
p.add_option('--displayname',
|
||||
help='Specify a display name for the package.')
|
||||
p.add_option('--description',
|
||||
help='Specify a description for the package.')
|
||||
p.add_option('--restart', action='store_true',
|
||||
help='Restart is required on install.')
|
||||
p.add_option('--logout', action='store_true',
|
||||
help='Logout is required to install.')
|
||||
|
||||
# Get our options and our package names
|
||||
options, filelists = p.parse_args()
|
||||
|
||||
if not os.path.exists(packagemaker):
|
||||
print >>sys.stderr, \
|
||||
"packagemaker tool not found at %s." % packagemaker
|
||||
exit(-1)
|
||||
|
||||
if len(filelists) == 0:
|
||||
print >>sys.stderr, "You must specify a file list!"
|
||||
exit(-1)
|
||||
|
||||
if len(filelists) > 1:
|
||||
print >>sys.stderr, "You may convert only one file list at a time."
|
||||
exit(-1)
|
||||
|
||||
filelist = filelists[0]
|
||||
if not os.path.exists(filelist):
|
||||
print "No file list at %s" % filelist
|
||||
exit(-1)
|
||||
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
if options.name:
|
||||
pkgname = options.name
|
||||
else:
|
||||
pkgname = os.path.splitext(os.path.basename(filelist))[0]
|
||||
|
||||
packageroot = os.path.join(mytmpdir, pkgname)
|
||||
os.mkdir(packageroot)
|
||||
copyItemsFromList(filelist, packageroot)
|
||||
|
||||
# some default values
|
||||
(name, versionInName) = nameAndVersion(pkgname)
|
||||
if options.id:
|
||||
pkgid = options.id
|
||||
else:
|
||||
pkgid = pkgidprefix + name.lower()
|
||||
if options.version:
|
||||
pkgvers = options.version
|
||||
elif versionInName:
|
||||
pkgvers = versionInName
|
||||
else:
|
||||
pkgvers = "1.0.0"
|
||||
|
||||
infopath = ""
|
||||
|
||||
# look through packageroot dir for Receipts
|
||||
pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
|
||||
if os.path.exists(pkgrootreceipts):
|
||||
receiptlist = os.listdir(pkgrootreceipts)
|
||||
if len(receiptlist) == 1:
|
||||
receipt = os.path.join(pkgrootreceipts, receiptlist[0])
|
||||
infopath = os.path.join(receipt,"Contents/Info.plist")
|
||||
if os.path.exists(infopath):
|
||||
print "Using package info from %s" % infopath
|
||||
else:
|
||||
infopath = ""
|
||||
else:
|
||||
print >>sys.stderr, \
|
||||
("Found multiple receipts, "
|
||||
"so cannot determine pkgid and version.")
|
||||
|
||||
if not infopath:
|
||||
# look for a single application bundle and get info from that
|
||||
appinfo = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith('.app'):
|
||||
if not appinfo:
|
||||
appinfo = os.path.join(dirpath, "Contents/Info.plist")
|
||||
if not os.path.exists(appinfo):
|
||||
appinfo = ""
|
||||
else:
|
||||
# crap, found more than one.
|
||||
appinfo = ""
|
||||
print >>sys.stderr, \
|
||||
("Found multiple application bundles, "
|
||||
"so cannot determine pkgid and version.")
|
||||
break
|
||||
|
||||
if appinfo:
|
||||
pl = plistlib.readPlist(appinfo)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
|
||||
appinfo)
|
||||
else:
|
||||
# let's look for any Contents/Info.plist
|
||||
infoplist = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith("/Contents") and \
|
||||
"Info.plist" in filenames:
|
||||
if not infoplist:
|
||||
infoplist = os.path.join(dirpath, "Info.plist")
|
||||
if not os.path.exists(infoplist):
|
||||
infoplist = ""
|
||||
else:
|
||||
# found more than one Info.plist
|
||||
infoplist = ""
|
||||
break
|
||||
|
||||
if infoplist:
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid,
|
||||
pkgvers,
|
||||
infoplist)
|
||||
|
||||
print "Package name: %s" % pkgname
|
||||
newdisplayname = raw_input("Display name [%s]: "
|
||||
% options.displayname)
|
||||
options.displayname = newdisplayname or options.displayname
|
||||
newdescription = raw_input("Description [%s]: " % options.description)
|
||||
options.description = newdescription or options.description
|
||||
newid = raw_input("PackageID [%s]: " % pkgid)
|
||||
pkgid = newid or pkgid
|
||||
newversion = raw_input("Version [%s]: " % pkgvers)
|
||||
pkgvers = newversion or pkgvers
|
||||
|
||||
print
|
||||
print
|
||||
print "Package name: %s" % pkgname
|
||||
print "Display name: %s" % options.displayname
|
||||
print "Description: %s" % options.description
|
||||
print "PackageID: %s" % pkgid
|
||||
print "Version: %s" % pkgvers
|
||||
print
|
||||
answer = raw_input("Build the package? [y/n] ")
|
||||
if not answer.lower().startswith("y"):
|
||||
exit(0)
|
||||
|
||||
# build package
|
||||
outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
|
||||
if os.path.exists(outputname):
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", outputname])
|
||||
|
||||
if infopath:
|
||||
cmd = [packagemaker, '--root', packageroot, '--info', infopath,
|
||||
'--no-recommend', '--out', outputname, '--verbose',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$']
|
||||
else:
|
||||
cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
|
||||
'--version', pkgvers, '--out', outputname,
|
||||
'--verbose', '--no-recommend',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$']
|
||||
print cmd
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Package creation failed."
|
||||
exit(-1)
|
||||
else:
|
||||
# remove relocatable stuff
|
||||
tokendefinitions = os.path.join(outputname,
|
||||
"Contents/Resources/TokenDefinitions.plist")
|
||||
if os.path.exists(tokendefinitions):
|
||||
os.remove(tokendefinitions)
|
||||
infoplist = os.path.join(outputname, "Contents/Info.plist")
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if 'IFPkgPathMappings' in pl:
|
||||
del pl['IFPkgPathMappings']
|
||||
|
||||
if options.restart:
|
||||
# add restart info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredRestart"
|
||||
elif options.logout:
|
||||
# add logout info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredLogout"
|
||||
|
||||
plistlib.writePlist(pl, infoplist)
|
||||
|
||||
if options.displayname or options.description:
|
||||
languages = ['en.lproj', 'English.lproj']
|
||||
for item in languages:
|
||||
lprojpath = os.path.join(outputname,
|
||||
'Contents/Resources', item)
|
||||
if os.path.exists(lprojpath):
|
||||
descriptionplist = os.path.join(lprojpath,
|
||||
"Description.plist")
|
||||
pl = {}
|
||||
pl['IFPkgDescriptionTitle'] = (options.displayname or
|
||||
pkgname)
|
||||
pl['IFPkgDescriptionDescription'] = (options.description
|
||||
or "")
|
||||
plistlib.writePlist(pl, descriptionplist)
|
||||
break
|
||||
|
||||
print "Completed package is at %s" % outputname
|
||||
if not options.nomakedmg:
|
||||
makeDMG(outputname)
|
||||
|
||||
#cleanup temp dir
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,313 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2011-2014 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.
|
||||
"""
|
||||
tar2pkg.py
|
||||
|
||||
Created by Greg Neagle on 2011-06-16.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import stat
|
||||
import plistlib
|
||||
import subprocess
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
# change these to suit yourself
|
||||
packagemaker = "/Developer/usr/bin/packagemaker"
|
||||
pkgidprefix = "com.myorg.pkg."
|
||||
pkgoutputdir = "/Users/Shared/pkgs"
|
||||
|
||||
|
||||
def nameAndVersion(s):
|
||||
"""
|
||||
Splits a string into the name and version numbers:
|
||||
'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
|
||||
'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
|
||||
'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
|
||||
"""
|
||||
index = 0
|
||||
for char in s:
|
||||
if char in "0123456789":
|
||||
possibleVersion = s[index:]
|
||||
if not (" " in possibleVersion or "_" in possibleVersion
|
||||
or "-" in possibleVersion or "v" in possibleVersion):
|
||||
return (s[0:index].rstrip(" .-_v"), possibleVersion)
|
||||
index += 1
|
||||
# no version number found, just return original string and empty string
|
||||
return (s, '')
|
||||
|
||||
|
||||
def makeDMG(pkgpath):
|
||||
print "Making disk image..."
|
||||
cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
|
||||
pkgpath, pkgpath + ".dmg"]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Disk image creation failed."
|
||||
return(-1)
|
||||
|
||||
|
||||
def untarItem(sourceitem, packageroot):
|
||||
'''untars item into packageroot'''
|
||||
os.chdir(packageroot)
|
||||
return subprocess.call(['/usr/bin/tar', '-xf', sourceitem])
|
||||
|
||||
|
||||
def main():
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--nomakedmg', action='store_true',
|
||||
help='Don\'t make a disk image containing the package.')
|
||||
p.add_option('--name', '-n',
|
||||
help='Specify a name for the package.')
|
||||
p.add_option('--version', '-v',
|
||||
help='Specify a version number for the package.')
|
||||
p.add_option('--id',
|
||||
help='Specify a package id for the package.')
|
||||
p.add_option('--displayname',
|
||||
help='Specify a display name for the package.')
|
||||
p.add_option('--description',
|
||||
help='Specify a description for the package.')
|
||||
p.add_option('--restart', action='store_true',
|
||||
help='Restart is required on install.')
|
||||
p.add_option('--logout', action='store_true',
|
||||
help='Logout is required to install.')
|
||||
|
||||
# Get our options and our package names
|
||||
options, tarballs = p.parse_args()
|
||||
|
||||
if not os.path.exists(packagemaker):
|
||||
print >>sys.stderr, \
|
||||
"packagemaker tool not found at %s." % packagemaker
|
||||
exit(-1)
|
||||
|
||||
if len(tarballs) == 0:
|
||||
print >>sys.stderr, "You must specify a tar archive!"
|
||||
exit(-1)
|
||||
|
||||
if len(tarballs) > 1:
|
||||
print >>sys.stderr, "You may convert only one tar archive at a time."
|
||||
exit(-1)
|
||||
|
||||
tarball = tarballs[0]
|
||||
if not os.path.exists(tarball):
|
||||
print "No tar archive at %s" % tarball
|
||||
exit(-1)
|
||||
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
if options.name:
|
||||
pkgname = options.name
|
||||
else:
|
||||
pkgname = os.path.splitext(os.path.basename(tarball))[0]
|
||||
|
||||
packageroot = os.path.join(mytmpdir, pkgname)
|
||||
os.mkdir(packageroot)
|
||||
untarItem(tarball, packageroot)
|
||||
|
||||
# some default values
|
||||
(name, versionInName) = nameAndVersion(pkgname)
|
||||
if options.id:
|
||||
pkgid = options.id
|
||||
else:
|
||||
pkgid = pkgidprefix + name.lower()
|
||||
if options.version:
|
||||
pkgvers = options.version
|
||||
elif versionInName:
|
||||
pkgvers = versionInName
|
||||
else:
|
||||
pkgvers = "1.0.0"
|
||||
|
||||
infopath = ""
|
||||
|
||||
# look through packageroot dir for Receipts
|
||||
pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
|
||||
if os.path.exists(pkgrootreceipts):
|
||||
receiptlist = os.listdir(pkgrootreceipts)
|
||||
if len(receiptlist) == 1:
|
||||
receipt = os.path.join(pkgrootreceipts, receiptlist[0])
|
||||
infopath = os.path.join(receipt,"Contents/Info.plist")
|
||||
if os.path.exists(infopath):
|
||||
print "Using package info from %s" % infopath
|
||||
else:
|
||||
infopath = ""
|
||||
else:
|
||||
print >>sys.stderr, \
|
||||
("Found multiple receipts, "
|
||||
"so cannot determine pkgid and version.")
|
||||
|
||||
if not infopath:
|
||||
# look for a single application bundle and get info from that
|
||||
appinfo = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith('.app'):
|
||||
if not appinfo:
|
||||
appinfo = os.path.join(dirpath, "Contents/Info.plist")
|
||||
if not os.path.exists(appinfo):
|
||||
appinfo = ""
|
||||
else:
|
||||
# crap, found more than one.
|
||||
appinfo = ""
|
||||
print >>sys.stderr, \
|
||||
("Found multiple application bundles, "
|
||||
"so cannot determine pkgid and version.")
|
||||
break
|
||||
|
||||
if appinfo:
|
||||
pl = plistlib.readPlist(appinfo)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
|
||||
appinfo)
|
||||
else:
|
||||
# let's look for any Contents/Info.plist
|
||||
infoplist = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith("/Contents") and \
|
||||
"Info.plist" in filenames:
|
||||
if not infoplist:
|
||||
infoplist = os.path.join(dirpath, "Info.plist")
|
||||
if not os.path.exists(infoplist):
|
||||
infoplist = ""
|
||||
else:
|
||||
# found more than one Info.plist
|
||||
infoplist = ""
|
||||
break
|
||||
|
||||
if infoplist:
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid,
|
||||
pkgvers,
|
||||
infoplist)
|
||||
|
||||
print "Package name: %s" % pkgname
|
||||
newdisplayname = raw_input("Display name [%s]: "
|
||||
% options.displayname)
|
||||
options.displayname = newdisplayname or options.displayname
|
||||
newdescription = raw_input("Description [%s]: " % options.description)
|
||||
options.description = newdescription or options.description
|
||||
newid = raw_input("PackageID [%s]: " % pkgid)
|
||||
pkgid = newid or pkgid
|
||||
newversion = raw_input("Version [%s]: " % pkgvers)
|
||||
pkgvers = newversion or pkgvers
|
||||
|
||||
print
|
||||
print
|
||||
print "Package name: %s" % pkgname
|
||||
print "Display name: %s" % options.displayname
|
||||
print "Description: %s" % options.description
|
||||
print "PackageID: %s" % pkgid
|
||||
print "Version: %s" % pkgvers
|
||||
print
|
||||
answer = raw_input("Build the package? [y/n] ")
|
||||
if not answer.lower().startswith("y"):
|
||||
exit(0)
|
||||
|
||||
# build package
|
||||
outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
|
||||
if os.path.exists(outputname):
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", outputname])
|
||||
|
||||
if infopath:
|
||||
cmd = [packagemaker, '--root', packageroot, '--info', infopath,
|
||||
'--no-recommend', '--out', outputname, '--verbose',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$']
|
||||
else:
|
||||
cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
|
||||
'--version', pkgvers, '--out', outputname,
|
||||
'--verbose', '--no-recommend',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$']
|
||||
print cmd
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Package creation failed."
|
||||
exit(-1)
|
||||
else:
|
||||
# remove relocatable stuff
|
||||
tokendefinitions = os.path.join(outputname,
|
||||
"Contents/Resources/TokenDefinitions.plist")
|
||||
if os.path.exists(tokendefinitions):
|
||||
os.remove(tokendefinitions)
|
||||
infoplist = os.path.join(outputname, "Contents/Info.plist")
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if 'IFPkgPathMappings' in pl:
|
||||
del pl['IFPkgPathMappings']
|
||||
|
||||
if options.restart:
|
||||
# add restart info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredRestart"
|
||||
elif options.logout:
|
||||
# add logout info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredLogout"
|
||||
|
||||
plistlib.writePlist(pl, infoplist)
|
||||
|
||||
if options.displayname or options.description:
|
||||
languages = ['en.lproj', 'English.lproj']
|
||||
for item in languages:
|
||||
lprojpath = os.path.join(outputname,
|
||||
'Contents/Resources', item)
|
||||
if os.path.exists(lprojpath):
|
||||
descriptionplist = os.path.join(lprojpath,
|
||||
"Description.plist")
|
||||
pl = {}
|
||||
pl['IFPkgDescriptionTitle'] = (options.displayname or
|
||||
pkgname)
|
||||
pl['IFPkgDescriptionDescription'] = (options.description
|
||||
or "")
|
||||
plistlib.writePlist(pl, descriptionplist)
|
||||
break
|
||||
|
||||
print "Completed package is at %s" % outputname
|
||||
if not options.nomakedmg:
|
||||
makeDMG(outputname)
|
||||
|
||||
#cleanup temp dir
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,331 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
#
|
||||
# Copyright 2009-2014 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.
|
||||
"""
|
||||
transcript2pkg.py
|
||||
|
||||
Created by Greg Neagle on 2009-06-04.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import stat
|
||||
import plistlib
|
||||
import subprocess
|
||||
import optparse
|
||||
import tempfile
|
||||
|
||||
# change these to suit yourself
|
||||
packagemaker = "/Developer/usr/bin/packagemaker"
|
||||
t2pkg = "/Users/Shared/bin/t2pkg"
|
||||
pkgidprefix = "com.myorg.pkg."
|
||||
pkgoutputdir = "/Users/Shared/pkgs"
|
||||
|
||||
|
||||
def nameAndVersion(s):
|
||||
"""
|
||||
Splits a string into the name and version numbers:
|
||||
'TextWrangler2.3b1' becomes ('TextWrangler', '2.3b1')
|
||||
'AdobePhotoshopCS3-11.2.1' becomes ('AdobePhotoshopCS3', '11.2.1')
|
||||
'MicrosoftOffice2008v12.2.1' becomes ('MicrosoftOffice2008', '12.2.1')
|
||||
"""
|
||||
index = 0
|
||||
for char in s:
|
||||
if char in "0123456789":
|
||||
possibleVersion = s[index:]
|
||||
if not (" " in possibleVersion or "_" in possibleVersion
|
||||
or "-" in possibleVersion or "v" in possibleVersion):
|
||||
return (s[0:index].rstrip(" .-_v"), possibleVersion)
|
||||
index += 1
|
||||
# no version number found, just return original string and empty string
|
||||
return (s, '')
|
||||
|
||||
|
||||
def makeDMG(pkgpath):
|
||||
print "Making disk image..."
|
||||
cmd = ["/usr/bin/hdiutil", "create", "-srcfolder",
|
||||
pkgpath, pkgpath + ".dmg"]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Disk image creation failed."
|
||||
return(-1)
|
||||
|
||||
|
||||
def main():
|
||||
# command-line options
|
||||
p = optparse.OptionParser()
|
||||
p.add_option('--makedmg', '-d', action='store_true',
|
||||
help='Makes a disk image containing the package.')
|
||||
p.add_option('--name', '-n',
|
||||
help='Specify a name for the package.')
|
||||
p.add_option('--version', '-v',
|
||||
help='Specify a version number for the package.')
|
||||
p.add_option('--id',
|
||||
help='Specify a package id for the package.')
|
||||
p.add_option('--displayname',
|
||||
help='Specify a display name for the package.')
|
||||
p.add_option('--description',
|
||||
help='Specify a description for the package.')
|
||||
p.add_option('--restart', action='store_true',
|
||||
help='Restart is required on install.')
|
||||
p.add_option('--logout', action='store_true',
|
||||
help='Logout is required to install.')
|
||||
|
||||
# Get our options and our package names
|
||||
options, transcripts = p.parse_args()
|
||||
|
||||
if not os.path.exists(packagemaker):
|
||||
print >>sys.stderr, "packagemaker tool not found at %s." % \
|
||||
packagemaker
|
||||
exit(-1)
|
||||
if not os.path.exists(t2pkg):
|
||||
print >>sys.stderr, "t2pkg tool not found at %s." % t2pkg
|
||||
exit(-1)
|
||||
|
||||
if len(transcripts) == 0:
|
||||
print >>sys.stderr, "You must specify a transcript!"
|
||||
exit(-1)
|
||||
|
||||
if len(transcripts) > 1:
|
||||
print >>sys.stderr, "You may convert only one transcript at a time."
|
||||
exit(-1)
|
||||
|
||||
transcript = transcripts[0]
|
||||
if transcript.find("/") == -1:
|
||||
# transcript name only; we'll prepend the standard
|
||||
# client path
|
||||
transcript = os.path.join("/var/radmind/client", transcript)
|
||||
|
||||
if not os.path.exists(transcript):
|
||||
print "No transcript file at %s" % transcript
|
||||
exit(-1)
|
||||
|
||||
mytmpdir = tempfile.mkdtemp()
|
||||
if options.name:
|
||||
pkgname = options.name
|
||||
else:
|
||||
pkgname = os.path.splitext(os.path.basename(transcript))[0]
|
||||
|
||||
# make packageroot directory using t2pkg
|
||||
packageroot = os.path.join(mytmpdir, pkgname)
|
||||
cmd = [t2pkg, '-d', packageroot, '-r', '/', transcript]
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
(output, err) = p.communicate()
|
||||
if p.returncode != 0:
|
||||
print >>sys.stderr, err
|
||||
exit(-1)
|
||||
if err:
|
||||
print >>sys.stderr, err
|
||||
exit(-1)
|
||||
|
||||
# fix uid/gid/perms on directories
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
srcdir = dirpath[len(packageroot):]
|
||||
if srcdir == "": srcdir = "/"
|
||||
os.chown(dirpath, os.stat(srcdir).st_uid, os.stat(srcdir).st_gid)
|
||||
os.chmod(dirpath, stat.S_IMODE(os.stat(srcdir).st_mode))
|
||||
|
||||
# some default values
|
||||
(name, versionInName) = nameAndVersion(pkgname)
|
||||
if options.id:
|
||||
pkgid = options.id
|
||||
else:
|
||||
pkgid = pkgidprefix + name.lower()
|
||||
if options.version:
|
||||
pkgvers = options.version
|
||||
elif versionInName:
|
||||
pkgvers = versionInName
|
||||
else:
|
||||
pkgvers = "1.0.0"
|
||||
|
||||
infopath = ""
|
||||
|
||||
# look through packageroot dir for Receipts
|
||||
pkgrootreceipts = os.path.join(packageroot, "Library/Receipts")
|
||||
if os.path.exists(pkgrootreceipts):
|
||||
receiptlist = os.listdir(pkgrootreceipts)
|
||||
if len(receiptlist) == 1:
|
||||
receipt = os.path.join(pkgrootreceipts, receiptlist[0])
|
||||
infopath = os.path.join(receipt,"Contents/Info.plist")
|
||||
if os.path.exists(infopath):
|
||||
print "Using package info from %s" % infopath
|
||||
else:
|
||||
infopath = ""
|
||||
else:
|
||||
print >>sys.stderr, \
|
||||
"Found multiple receipts, so cannot determine pkgid and version."
|
||||
|
||||
if not infopath:
|
||||
# look for a single application bundle and get info from that
|
||||
appinfo = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith('.app'):
|
||||
if not appinfo:
|
||||
appinfo = os.path.join(dirpath, "Contents/Info.plist")
|
||||
if not os.path.exists(appinfo):
|
||||
appinfo = ""
|
||||
else:
|
||||
# crap, found more than one.
|
||||
appinfo = ""
|
||||
print >>sys.stderr, \
|
||||
"Found multiple application bundles, so cannot determine pkgid and version."
|
||||
break
|
||||
|
||||
if appinfo:
|
||||
pl = plistlib.readPlist(appinfo)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid, pkgvers,
|
||||
appinfo)
|
||||
else:
|
||||
# let's look for any Contents/Info.plist
|
||||
infoplist = ""
|
||||
for dirpath, dirnames, filenames in os.walk(packageroot):
|
||||
if dirpath.endswith("/Contents") and \
|
||||
"Info.plist" in filenames:
|
||||
if not infoplist:
|
||||
infoplist = os.path.join(dirpath, "Info.plist")
|
||||
if not os.path.exists(infoplist):
|
||||
infoplist = ""
|
||||
else:
|
||||
# found more than one Info.plist
|
||||
infoplist = ""
|
||||
break
|
||||
|
||||
if infoplist:
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if "CFBundleIdentifier" in pl and not options.id:
|
||||
pkgid = pl["CFBundleIdentifier"] + ".pkg"
|
||||
if "CFBundleShortVersionString" in pl and not options.version:
|
||||
pkgvers = pl["CFBundleShortVersionString"]
|
||||
print "Using pkgid: %s, version: %s from %s" % (pkgid,
|
||||
pkgvers, infoplist)
|
||||
|
||||
print "Package name: %s" % pkgname
|
||||
newdisplayname = raw_input("Display name [%s]: "
|
||||
% options.displayname)
|
||||
options.displayname = newdisplayname or options.displayname
|
||||
newdescription = raw_input("Description [%s]: " % options.description)
|
||||
options.description = newdescription or options.description
|
||||
newid = raw_input("PackageID [%s]: " % pkgid)
|
||||
pkgid = newid or pkgid
|
||||
newversion = raw_input("Version [%s]: " % pkgvers)
|
||||
pkgvers = newversion or pkgvers
|
||||
|
||||
print
|
||||
print
|
||||
print "Package name: %s" % pkgname
|
||||
print "Display name: %s" % options.displayname
|
||||
print "Description: %s" % options.description
|
||||
print "PackageID: %s" % pkgid
|
||||
print "Version: %s" % pkgvers
|
||||
print
|
||||
answer = raw_input("Build the package? [y/n] ")
|
||||
if not answer.lower().startswith("y"):
|
||||
exit(0)
|
||||
|
||||
# build package
|
||||
outputname = os.path.join(pkgoutputdir, pkgname + ".pkg")
|
||||
if os.path.exists(outputname):
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", outputname])
|
||||
|
||||
if infopath:
|
||||
cmd = [packagemaker, '--root', packageroot, '--info', infopath,
|
||||
'--no-recommend', '--out', outputname, '--verbose',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$',
|
||||
'--filter', 'Library/FA/Applications']
|
||||
else:
|
||||
cmd = [packagemaker, '--root', packageroot, '--id', pkgid,
|
||||
'--version', pkgvers, '--out', outputname,
|
||||
'--verbose', '--no-recommend',
|
||||
'--filter', 'Library/Receipts',
|
||||
'--filter', '.DS_Store$',
|
||||
'--filter', 'Library/FA/Applications']
|
||||
print cmd
|
||||
p = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
|
||||
while True:
|
||||
output = p.stdout.readline()
|
||||
if not output and (p.poll() != None):
|
||||
break
|
||||
print output.rstrip("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
retcode = p.poll()
|
||||
if retcode:
|
||||
print >>sys.stderr, "Package creation failed."
|
||||
exit(-1)
|
||||
else:
|
||||
# remove relocatable stuff
|
||||
tokendefinitions = os.path.join(outputname,
|
||||
"Contents/Resources/TokenDefinitions.plist")
|
||||
if os.path.exists(tokendefinitions):
|
||||
os.remove(tokendefinitions)
|
||||
infoplist = os.path.join(outputname, "Contents/Info.plist")
|
||||
pl = plistlib.readPlist(infoplist)
|
||||
if 'IFPkgPathMappings' in pl:
|
||||
del pl['IFPkgPathMappings']
|
||||
|
||||
if options.restart:
|
||||
# add restart info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredRestart"
|
||||
elif options.logout:
|
||||
# add logout info to plist
|
||||
pl['IFPkgFlagRestartAction'] = "RequiredLogout"
|
||||
|
||||
plistlib.writePlist(pl, infoplist)
|
||||
|
||||
if options.displayname or options.description:
|
||||
languages = ['en.lproj', 'English.lproj']
|
||||
for item in languages:
|
||||
lprojpath = os.path.join(outputname,
|
||||
'Contents/Resources', item)
|
||||
if os.path.exists(lprojpath):
|
||||
descriptionplist = os.path.join(lprojpath,
|
||||
"Description.plist")
|
||||
pl = {}
|
||||
pl['IFPkgDescriptionTitle'] = (options.displayname or
|
||||
pkgname)
|
||||
pl['IFPkgDescriptionDescription'] = (options.description
|
||||
or "")
|
||||
plistlib.writePlist(pl, descriptionplist)
|
||||
break
|
||||
|
||||
print "Completed package is at %s" % outputname
|
||||
if options.makedmg:
|
||||
makeDMG(outputname)
|
||||
|
||||
#cleanup temp dir
|
||||
retcode = subprocess.call(["/bin/rm", "-rf", mytmpdir])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -6,7 +6,7 @@ munkicommon_display_unicode_test.py
|
||||
Unit tests for munkicommon's display_* functions.
|
||||
|
||||
"""
|
||||
# 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.
|
||||
|
||||
Reference in New Issue
Block a user