Merge branch 'master' of https://github.com/munki/munki into MSCMojave

# Conflicts:
#	code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj
#	code/apps/Managed Software Center/Managed Software Center/Base.lproj/MainMenu.xib
#	code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift
#	code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/updates.css
#	code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings
#	code/apps/Managed Software Center/Managed Software Center/msclog.swift
This commit is contained in:
Steve Küng
2020-03-11 08:02:32 +01:00
354 changed files with 3298 additions and 24797 deletions

3
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,3 @@
Contributions to Munki are welcome!
Please see https://github.com/munki/munki/wiki/Contributing-to-Munki

View File

@@ -19,7 +19,7 @@
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>MSCDockTilePlugin</string>
</dict>

View File

@@ -2,7 +2,7 @@
File: MSCDockTilePlugIn.h
Copyright 2015-2019 Greg Neagle.
Copyright 2015-2020 Greg Neagle.
Liberally adapted from Apple sample code:
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html

View File

@@ -2,7 +2,7 @@
File: MSCDockTilePlugIn.m
Copyright 2015-2019 Greg Neagle.
Copyright 2015-2020 Greg Neagle.
Liberally adapted from Apple sample code:
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html

View File

@@ -284,7 +284,7 @@
attributes = {
CLASSPREFIX = MSC;
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1000;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Munki Project";
TargetAttributes = {
C0196316210507DB0009F51A = {

View File

@@ -3,7 +3,7 @@
// ManagedSoftwareCenter
//
// Created by Greg Neagle on 5/27/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa
@@ -86,7 +86,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
let keyword = AEKeyword(keyDirectObject)
let urlDescriptor = event.paramDescriptor(forKeyword: keyword)
if let urlString = urlDescriptor?.stringValue {
msc_log("MSU", "Called by external URL: \(urlString)")
msc_log("MSC", "Called by external URL: \(urlString)")
if let url = URL(string: urlString) {
mainWindowController.handleMunkiURL(url)
} else {
@@ -112,13 +112,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
}
if user_info["action"] as? String ?? "" == "open_url" {
let urlString = user_info["value"] as? String ?? "munki://updates"
msc_log("MSU", "Got user notification to open \(urlString)")
msc_log("MSC", "Got user notification to open \(urlString)")
if let url = URL(string: urlString) {
mainWindowController.handleMunkiURL(url)
}
center.removeDeliveredNotification(notification)
} else {
msc_log("MSU", "Got user notification with unrecognized userInfo")
msc_log("MSC", "Got user notification with unrecognized userInfo")
}
}

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 5/20/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 7/15/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa
@@ -407,7 +407,7 @@ class MSCAlertController: NSObject {
alert.buttons[1].keyEquivalent = "\r"
// set Continue button to be activated by Escape key
alert.buttons[0].keyEquivalent = "\u{1B}"
msc_log("MSU", "alert_on_battery_power")
msc_log("MSC", "alert_on_battery_power")
let response = alert.runModal()
if response == .alertSecondButtonReturn {
// user clicked Cancel

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 7/17/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 7/11/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/29/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa
@@ -160,6 +160,19 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
displayUpdateCount()
cached_self_service = SelfService()
}
func toolBarItemIsHighlighted(_ item: NSToolbarItem) -> Bool {
if let button = item.view as? NSButton {
return (button.state == .on)
}
return false
}
func setHighlightFor(item: NSToolbarItem, doHighlight: Bool) {
if let button = item.view as? NSButton {
button.state = (doHighlight ? .on : .off)
}
}
func highlightToolbarButtons(_ nameToHighlight: String) {
for (index, item) in items.enumerated() {
@@ -227,7 +240,7 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
comment: "Cannot Contact Server detail")
} else if lastCheckResult == -2 {
// preflight failed
msc_log("MSU", "cant_update", msg: "failed preflight")
msc_log("MSC", "cant_update", msg: "failed preflight")
detailText = NSLocalizedString(
("Managed Software Center cannot check for updates now.\n" +
"Try again later. If this situation continues, " +
@@ -1330,23 +1343,27 @@ class MainWindowController: NSWindowController, NSWindowDelegate, WKNavigationDe
_alertedUserToOutstandingUpdates = true
}
@IBAction func softwareToolbarButtonClicked(_ sender: Any) {
@IBAction func softwareToolbarItemClicked(_ sender: Any) {
// User clicked Software toolbar button
highlightToolbarButtons("Software")
loadAllSoftwarePage(sender)
}
@IBAction func categoriesToolbarButtonClicked(_ sender: Any) {
@IBAction func categoriesToolbarItemClicked(_ sender: Any) {
// User clicked Categories toolbar button'''
highlightToolbarButtons("Categories")
loadCategoriesPage(sender)
}
@IBAction func myItemsToolbarButtonClicked(_ sender: Any) {
@IBAction func myItemsToolbarItemClicked(_ sender: Any) {
// User clicked My Items toolbar button'''
highlightToolbarButtons("My Items")
loadMyItemsPage(sender)
}
@IBAction func updatesToolbarButtonClicked(_ sender: Any) {
@IBAction func updatesToolbarItemClicked(_ sender: Any) {
// User clicked Updates toolbar button'''
highlightToolbarButtons("Updates")
loadUpdatesPage(sender)
}

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 5/27/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/15/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -47,7 +47,7 @@
<key>NSDockTilePlugIn</key>
<string>MSCDockTilePlugin.docktileplugin</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 27.05.18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/5/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 5/27/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 9/9/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import WebKit

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/7/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation
@@ -131,7 +131,7 @@ class GenericItem: BaseItem {
my["developer"] = guess_developer()
}
if let description = my["description"] as? String {
my["raw_description"] = description
my["raw_description"] = filtered_html(description)
my["description"] = nil
}
my["icon"] = getIcon()
@@ -690,7 +690,7 @@ class OptionalItem: GenericItem {
func _get_status() -> String {
// Calculates initial status for an item and also sets a boolean
// if a updatecheck is needed
let managed_update_names = getInstallInfo()["managed_updates"] as? [String] ?? [String]()
let managed_update_names = cachedInstallInfo()["managed_updates"] as? [String] ?? [String]()
let self_service = SelfService()
my["updatecheck_needed"] = false
my["user_directed_action"] = false
@@ -775,7 +775,7 @@ class OptionalItem: GenericItem {
func _get_note_from_problem_items() -> String {
// Checks InstallInfo's problem_items for any notes for self that might
// give feedback why this item can't be downloaded or installed
let problem_items = getInstallInfo()["problem_items"] as? [[String:Any]] ?? [[String:Any]]()
let problem_items = cachedInstallInfo()["problem_items"] as? [[String:Any]] ?? [[String:Any]]()
// filter problem items for any whose name matches the name of
// the current item
let name = my["name"] as? String ?? ""
@@ -831,7 +831,7 @@ class OptionalItem: GenericItem {
my["updatecheck_needed"] = true
var self_service_change_success = true
let original_status = my["status"] as? String ?? ""
let managed_update_names = getInstallInfo()["managed_updates"] as? [String] ?? [String]()
let managed_update_names = cachedInstallInfo()["managed_updates"] as? [String] ?? [String]()
let status = my["status"] as? String ?? ""
let name = my["name"] as? String ?? ""
let needs_update = my["needs_update"] as? Bool ?? false
@@ -975,15 +975,27 @@ class UpdateItem: GenericItem {
}
}
func cachedInstallInfo() -> [String : Any] {
if !Cache.shared.keys.contains("InstallInfo") {
Cache.shared["InstallInfo"] = getInstallInfo()
}
return Cache.shared["InstallInfo"] as? [String : Any] ?? [String : Any]()
}
func optionalInstallsExist() -> Bool {
let optional_items = cachedInstallInfo()["optional_installs"] as? [[String : Any]] ?? [[String : Any]]()
return optional_items.count > 0
}
func getOptionalInstallItems() -> [OptionalItem] {
let appleSoftwareUpdatesOnly = pythonishBool(pref("AppleSoftwareUpdatesOnly"))
if appleSoftwareUpdatesOnly {
return [OptionalItem]()
}
if !Cache.shared.keys.contains("optional_install_items") {
let optional_items = getInstallInfo()["optional_installs"] as? [[String : Any]] ?? [[String : Any]]()
let optional_items = cachedInstallInfo()["optional_installs"] as? [[String : Any]] ?? [[String : Any]]()
var optional_install_items = optional_items.map({ OptionalItem($0) })
let featured_items = getInstallInfo()["featured_items"] as? [String] ?? [String]()
let featured_items = cachedInstallInfo()["featured_items"] as? [String] ?? [String]()
for index in 0..<optional_install_items.count {
if let name = optional_install_items[index]["name"] as? String {
if featured_items.contains(name) {
@@ -998,7 +1010,7 @@ func getOptionalInstallItems() -> [OptionalItem] {
func getProblemItems() -> [UpdateItem] {
if !Cache.shared.keys.contains("problem_items") {
var problem_items = getInstallInfo()["problem_items"] as? [[String:Any]] ?? [[String:Any]]()
var problem_items = cachedInstallInfo()["problem_items"] as? [[String:Any]] ?? [[String:Any]]()
for i in 0..<problem_items.count {
problem_items[i]["status"] = "problem-item"
}
@@ -1095,7 +1107,7 @@ func _build_update_list() -> [UpdateItem] {
}
}
}
let install_info = getInstallInfo()
let install_info = cachedInstallInfo()
if let managed_installs = install_info["managed_installs"] as? [[String: Any]] {
for var item in managed_installs {
item["status"] = "will-be-installed"
@@ -1138,7 +1150,7 @@ func updatesContainNonUserSelectedItems() -> Bool {
// available Apple updates are not user selected
return true
}
let install_info = getInstallInfo()
let install_info = cachedInstallInfo()
let install_items = install_info["managed_installs"] as? [[String: Any]] ?? [[String: Any]]()
let removal_items = install_info["removals"] as? [[String: Any]] ?? [[String: Any]]()
let filtered_installs = install_items.filter(
@@ -1192,7 +1204,7 @@ func dependentItems(_ item_name: String) -> [String] {
// optional item
if !Cache.shared.keys.contains("optional_installs_with_dependencies") {
let self_service_installs = SelfService().installs
if let optional_installs = getInstallInfo()["optional_instslls"] as? [PlistDict] {
if let optional_installs = cachedInstallInfo()["optional_instslls"] as? [PlistDict] {
Cache.shared["optional_installs_with_dependencies"] = (
optional_installs.filter(
{

View File

@@ -58,7 +58,7 @@ body.product .product-detail .product-info .product-review h4 {
color: var(--text-color-subdued);
/*text-shadow: rgba(255,255,255,1) 0 1px 0*/
}
body.product .product-detail .product-info .product-review p,body.product .product-detail .product-info .product-review ul {
body.product .product-detail .product-info .product-review p,body.product .product-detail .product-info .product-review div,body.product .product-detail .product-info .product-review ul {
font-size: 12px;
line-height: 16px;
color: var(--text-color-normal);

View File

@@ -579,7 +579,7 @@ div.progress > span.indeterminate > span {
color-stop(.76, transparent), to(transparent)
);
z-index: 1;
-webkit-background-size: 20px 20px;
-webkit-background-size: 21px 21px;
-webkit-animation: barber-pole 2s linear infinite;
-webkit-border-radius: 4px;
overflow: hidden;
@@ -587,10 +587,10 @@ div.progress > span.indeterminate > span {
@-webkit-keyframes barber-pole {
0% {
background-position: 0 20px;
background-position: 0 21px;
}
100% {
background-position: 20px 0;
background-position: 21px 0;
}
}

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/12/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation
@@ -43,10 +43,11 @@ struct SelfService {
}
init() {
let selfServiceData = readSelfServiceManifest()
_installs = Set(
readSelfServiceManifest()["managed_installs"] as? [String] ?? [String]())
selfServiceData["managed_installs"] as? [String] ?? [String]())
_uninstalls = Set(
readSelfServiceManifest()["managed_uninstalls"] as? [String] ?? [String]())
selfServiceData["managed_uninstalls"] as? [String] ?? [String]())
}
mutating func subscribe(_ item_name: String) -> Bool {

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 7/23/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Darwin

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/15/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
// A lightweight Swift implementation of something like Python's string.Template

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/29/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Geführte Softwareaktualisierung";
"CFBundleDisplayName" = "Geführte Softwareaktualisierung";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
NSHumanReadableCopyright = "Copyright © 2010-2018 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Centre";
"CFBundleDisplayName" = "Managed Software Centre";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -23,7 +23,7 @@
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Centro de aplicaciones";
"CFBundleDisplayName" = "Centro de aplicaciones";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Centre de gestion des logiciels";
"CFBundleDisplayName" = "Centre de gestion des logiciels";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/11/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Centro Gestione Applicazioni";
"CFBundleDisplayName" = "Centro Gestione Applicazioni";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/15/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/6/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -3,21 +3,104 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/6/18.
// Copyright © 2018 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation
let MSULOGDIR = "/Users/Shared/.com.googlecode.munki.ManagedSoftwareUpdate.logs"
// TO-DO: eliminate these global vars
var MSULOGFILE = ""
var MSULOGENABLED = false
var MSUDEBUGLOGENABLED = false
func is_safe_to_use(_ pathname: String) -> Bool {
// Returns true if we can open this file and it is a regular file owned by us
// mostly C functions since this a port from Python
var safe = false
if !(FileManager.default.fileExists(atPath: pathname)) {
if FileManager.default.createFile(atPath: pathname, contents: nil, attributes: [FileAttributeKey.posixPermissions: 0o0600]) == false {
NSLog("Could not create %@", pathname)
return false
}
}
let fref = open(UnsafePointer(pathname), O_RDWR | O_CREAT | O_NOFOLLOW, 0x0600)
if fref != -1 {
var st = stat()
if fstat(fref, UnsafeMutablePointer<stat>(&st)) == 0 {
safe = ((st.st_mode & S_IFREG) != 0) && (st.st_uid == getuid())
}
close(fref)
}
return safe
}
func setup_logging() {
// stub
if pythonishBool(pref("MSUDebugLogEnabled")) {
MSUDEBUGLOGENABLED = true
}
if pythonishBool(pref("MSULogEnabled")) {
MSULOGENABLED = true
}
if !(MSULOGENABLED) {
return
}
let username = NSUserName()
if !(FileManager.default.fileExists(atPath: MSULOGDIR)) {
do {
try FileManager.default.createDirectory(atPath: MSULOGDIR, withIntermediateDirectories: true, attributes: [FileAttributeKey.posixPermissions: 0o1777])
} catch {
NSLog("Could not create %@: %@", MSULOGDIR, "\(error)")
return
}
}
var pathIsDir = ObjCBool(false)
let msuLogDirExists = FileManager.default.fileExists(atPath: MSULOGDIR, isDirectory: &pathIsDir)
if !msuLogDirExists {
NSLog("%@ doesn't exist.", MSULOGDIR)
return
} else if pathIsDir.boolValue == false {
NSLog("%@ is not a directory", MSULOGDIR)
return
}
// try to set our preferred permissions
do {
try FileManager.default.setAttributes([FileAttributeKey.posixPermissions: 0o1777], ofItemAtPath: MSULOGDIR)
} catch {
// do nothing
}
// find a safe log file to write to for this user
var filename = NSString.path(withComponents: [MSULOGDIR, "\(username).log"])
//make sure we can write to this; that it's owned by us, and not writable by group or other
for _ in 0..<10 {
if is_safe_to_use(filename) {
MSULOGFILE = filename
NSLog("Using file %@ for user-level logging", filename)
return
}
NSLog("Not safe to use %@ for logging", filename)
filename = NSString.path(withComponents: [MSULOGDIR, "\(username)_\(arc4random()).log"])
}
NSLog("Could not set up user-level logging for %@", username)
}
func msc_log(_ source: String, _ event: String, msg: String = "") {
// stub
print("\(source): \(event) \(msg)")
// Log an event from a source.
if MSULOGFILE != "" {
let datestamp = NSDate().timeIntervalSince1970
let logString = "\(datestamp) \(source): \(event) \(msg)\n"
if let logData = logString.data(using: String.Encoding.utf8) {
if let fh = FileHandle(forUpdatingAtPath: MSULOGFILE) {
let _ = fh.seekToEndOfFile()
fh.write(logData)
}
}
}
}
func msc_debug_log(_ logMessage: String) {
// stub
print(logMessage)
// Log to Apple System Log facility and also to MSU log if configured
NSLog("%@", logMessage)
msc_log("MSC", "debug", msg: logMessage)
}

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 5/27/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 6/29/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -3,7 +3,7 @@
// Managed Software Center
//
// Created by Greg Neagle on 7/16/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Центр Управления ПО";
"CFBundleDisplayName" = "Центр Управления ПО";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Managed Software Center";
"CFBundleDisplayName" = "Managed Software Center";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -14,6 +14,7 @@
C0544D4C20AF0EDA00DC86F6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0544D4B20AF0EDA00DC86F6 /* AppDelegate.swift */; };
C0544D4E20AF0EDA00DC86F6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0544D4D20AF0EDA00DC86F6 /* Assets.xcassets */; };
C0544D5120AF0EDA00DC86F6 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0544D4F20AF0EDA00DC86F6 /* MainMenu.xib */; };
C0787F7B2315C6210054D130 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0787F7A2315C6210054D130 /* main.swift */; };
C0A7937E20AFAE0300F56DD5 /* MunkiStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A7937D20AFAE0300F56DD5 /* MunkiStatusViewController.swift */; };
C0A7938020B0EA7800F56DD5 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0A7937F20B0EA7800F56DD5 /* Utils.swift */; };
/* End PBXBuildFile section */
@@ -70,6 +71,7 @@
C0544D4B20AF0EDA00DC86F6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
C0544D4D20AF0EDA00DC86F6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
C0544D5020AF0EDA00DC86F6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
C0787F7A2315C6210054D130 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
C0A7937D20AFAE0300F56DD5 /* MunkiStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MunkiStatusViewController.swift; sourceTree = "<group>"; };
C0A7937F20B0EA7800F56DD5 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -105,6 +107,7 @@
isa = PBXGroup;
children = (
C04F825C20BB15F500F9C57D /* Localization.swift */,
C0787F7A2315C6210054D130 /* main.swift */,
C0168E8F20B1E1D80027BD78 /* LogViewController.swift */,
C0A7937D20AFAE0300F56DD5 /* MunkiStatusViewController.swift */,
C0544D4B20AF0EDA00DC86F6 /* AppDelegate.swift */,
@@ -146,7 +149,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0920;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "The Munki Project";
TargetAttributes = {
C0544D4720AF0EDA00DC86F6 = {
@@ -230,6 +233,7 @@
files = (
C0A7938020B0EA7800F56DD5 /* Utils.swift in Sources */,
C04F825D20BB15F500F9C57D /* Localization.swift in Sources */,
C0787F7B2315C6210054D130 /* main.swift in Sources */,
C0544D4C20AF0EDA00DC86F6 /* AppDelegate.swift in Sources */,
C0A7937E20AFAE0300F56DD5 /* MunkiStatusViewController.swift in Sources */,
C0168E9020B1E1D80027BD78 /* LogViewController.swift in Sources */,
@@ -331,6 +335,7 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -338,6 +343,7 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -365,7 +371,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@@ -389,6 +395,7 @@
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -396,6 +403,7 @@
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -417,7 +425,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";

View File

@@ -3,12 +3,11 @@
// MunkiStatus
//
// Created by Greg Neagle on 5/18/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var window: NSWindow!

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13771"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -73,7 +73,8 @@
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
<menuItem title="Quit MunkiStatus" keyEquivalent="q" id="4sb-4s-VLi">
<menuItem title="Quit MunkiStatus" id="4sb-4s-VLi">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
</connections>
@@ -165,7 +166,7 @@
</items>
<point key="canvasLocation" x="-711" y="-1056"/>
</menu>
<window title="Managed Software Center" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" animationBehavior="default" id="QvC-M9-y7g">
<window title="Managed Software Center" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" topStrut="YES"/>
<rect key="contentRect" x="335" y="543" width="532" height="123"/>
@@ -218,7 +219,7 @@
</view>
<point key="canvasLocation" x="-582" y="-908"/>
</window>
<window title="Log" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="zPZ-fc-m3c" userLabel="Log Window">
<window title="Log" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="zPZ-fc-m3c" userLabel="Log Window">
<windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
<windowCollectionBehavior key="collectionBehavior" fullScreenNone="YES"/>
<rect key="contentRect" x="636" y="390" width="512" height="360"/>
@@ -233,7 +234,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<clipView key="contentView" ambiguous="YES" id="Qss-f3-6kF">
<rect key="frame" x="0.0" y="0.0" width="514" height="325"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" typeSelect="NO" id="qBG-T9-hlB">
<rect key="frame" x="0.0" y="0.0" width="514" height="325"/>
@@ -271,11 +272,11 @@
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="tZh-JX-eMN">
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="tZh-JX-eMN">
<rect key="frame" x="0.0" y="-16" width="0.0" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="AHl-D0-o1j">
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="AHl-D0-o1j">
<rect key="frame" x="-16" y="0.0" width="16" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>

View File

@@ -31,7 +31,7 @@
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -3,7 +3,7 @@
// MunkiStatus
//
// Created by Greg Neagle on 27.05.18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -3,7 +3,7 @@
// MunkiStatus
//
// Created by Greg Neagle on 5/20/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// MunkiStatus
//
// Created by Greg Neagle on 5/18/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Cocoa

View File

@@ -3,7 +3,7 @@
// MunkiStatus
//
// Created by Greg Neagle on 5/19/18.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
import Foundation

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "Centre de gestion des logiciels";
"CFBundleDisplayName" = "Centre de gestion des logiciels";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -2,4 +2,4 @@
"CFBundleName" = "MunkiStatus";
"CFBundleDisplayName" = "MunkiStatus";
NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki";
NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki";

View File

@@ -0,0 +1,20 @@
//
// main.swift
// MunkiStatus
//
// Created by Greg Neagle on 8/27/19.
// Copyright © 2019-2020 The Munki Project. All rights reserved.
//
import Cocoa
// On Catalina, LaunchAgents run under "LimitLoadToSessionType : LoginWindow" on
// boot seem to be run before a CGSession is setup. Wait until the session is
// available before handing execution over to NSApplicationMain().
// Thanks to Tom Bergin for this insight.
while CGSessionCopyCurrentDictionary() == nil {
print("Waiting for a CGSession...")
usleep(500000)
}
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

View File

@@ -170,6 +170,7 @@
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
es,
@@ -181,6 +182,8 @@
nl,
ru,
sv,
da,
de,
);
mainGroup = C081D8AB1E5F4F5E00323D9C;
productRefGroup = C081D8B51E5F4F5E00323D9C /* Products */;
@@ -219,7 +222,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# add this number to Git revision index to get \"build\" number consistent with old SVN repo\nMAGICNUMBER=482\n\nBASEVERNUM=`/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"${INFOPLIST_FILE}\"`\n\nGIT=`which git`\n\nif [ \"$GIT\" != \"\" ] ; then\n# generate a pseudo-svn revision number from the list of Git revisions\nGITREV=`$GIT log -n1 --format=\"%H\" -- ./`\nGITREVINDEX=`$GIT rev-list --count $GITREV`\nREV=$(($GITREVINDEX + $MAGICNUMBER))\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $BASEVERNUM.$REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Set :GitRevision string $GITREV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nfi";
shellScript = "# add this number to Git revision index to get \"build\" number consistent with old SVN repo\nMAGICNUMBER=482\n\nBASEVERNUM=`/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"${INFOPLIST_FILE}\"`\n\nGIT=`which git`\n\nif [ \"$GIT\" != \"\" ] ; then\n# generate a pseudo-svn revision number from the list of Git revisions\nGITREV=`$GIT log -n1 --format=\"%H\" -- ./`\nGITREVINDEX=`$GIT rev-list --count $GITREV`\nREV=$(($GITREVINDEX + $MAGICNUMBER))\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $BASEVERNUM.$REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n/usr/libexec/PlistBuddy -c \"Set :GitRevision string $GITREV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */

View File

@@ -3,7 +3,7 @@
// munki-notifier
//
// Created by Greg Neagle on 2/23/17.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
#import <Cocoa/Cocoa.h>

View File

@@ -3,7 +3,7 @@
// munki-notifier
//
// Created by Greg Neagle on 2/23/17.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
// Much code lifted and adapted from https://github.com/julienXX/terminal-notifier
//

View File

@@ -27,7 +27,7 @@
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<string>Copyright © 2019-2020 The Munki Project. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -3,7 +3,7 @@
// munki-notifier
//
// Created by Greg Neagle on 2/23/17.
// Copyright © 2018-2019 The Munki Project. All rights reserved.
// Copyright © 2018-2020 The Munki Project. All rights reserved.
//
#import <Cocoa/Cocoa.h>

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,3 +1,3 @@
/* (No Comment) */
"NSHumanReadableCopyright" = "Copyright © 2019 The Munki Project. All rights reserved.";
"NSHumanReadableCopyright" = "Copyright © 2019-2020 The Munki Project. All rights reserved.";

View File

@@ -1,19 +0,0 @@
# localizable items generated by scripts as part of a build
Managed Software Center/Managed Software Center/Localize.ini
# ignore the MainMenu.xib for most localizations; it is generated at build time
Managed Software Center/Managed Software Center/*.lproj/MainMenu.xib
# but not the English one!
/!Managed Software Center/Managed Software Center/en.lproj/MainMenu.xib
# ignore generated localized strings in en.lproj
Managed Software Center/Managed Software Center/en.lproj/Localizable.strings
Managed Software Center/Managed Software Center/en.lproj/MainMenu.strings
# localizable items generated by scripts as part of a build
MunkiStatus/MunkiStatus/Localize.ini
# ignore the MainMenu.xib for most localizations; it is generated at build time
MunkiStatus/MunkiStatus/*.lproj/MainMenu.xib
# but not the English one!
/!MunkiStatus/MunkiStatus/en.lproj/MainMenu.xib
# ignore generated localized strings in en.lproj
MunkiStatus/MunkiStatus/en.lproj/Localizable.strings
MunkiStatus/MunkiStatus/en.lproj/MainMenu.strings

View File

@@ -1,10 +0,0 @@
# .DS_Store files!
.DS_Store
# don't track .pyc files
*.pyc
# Xcode 5 user data
*.xcodeproj/project.xcworkspace/
*.xcodeproj/xcuserdata/

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string>MSCDockTilePlugin</string>
</dict>
</plist>

View File

@@ -1,30 +0,0 @@
/*
File: MSCDockTilePlugIn.h
Copyright 2015-2019 Greg Neagle.
Liberally adapted from Apple sample code:
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html
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.
*/
#import <Cocoa/Cocoa.h>
@interface MSCDockTilePlugIn : NSObject <NSDockTilePlugIn> {
id updateObserver;
}
@property(retain) id updateObserver;
@end

View File

@@ -1,62 +0,0 @@
/*
File: MSCDockTilePlugIn.m
Copyright 2015-2019 Greg Neagle.
Liberally adapted from Apple sample code:
https://developer.apple.com/library/mac/samplecode/DockTile/Listings/DockTilePlugIn_DockTilePlugIn_h.html
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.
*/
#import "MSCDockTilePlugIn.h"
@implementation MSCDockTilePlugIn
@synthesize updateObserver;
static void updateCount(NSDockTile *tile) {
CFPreferencesAppSynchronize(CFSTR("ManagedInstalls"));
NSInteger count = CFPreferencesGetAppIntegerValue(CFSTR("PendingUpdateCount"), CFSTR("ManagedInstalls"), NULL);
if (count) {
[tile setBadgeLabel:[NSString stringWithFormat:@"%ld", (long)count]];
} else {
[tile setBadgeLabel: nil];
}
}
- (void)setDockTile:(NSDockTile *)dockTile {
if (dockTile) {
// Attach an observer that will update the count in the dock tile whenever it changes
self.updateObserver = [[NSDistributedNotificationCenter defaultCenter] addObserverForName:@"com.googlecode.munki.managedsoftwareupdate.dock.updateschanged" object:nil queue:nil usingBlock:^(NSNotification *notification) {
updateCount(dockTile); // Note that this block captures (and retains) dockTile for use later. Also note that it does not capture self, which means -dealloc may be called even while the notification is active. Although it's not clear this needs to be supported, this does eliminate a potential source of leaks.
}];
updateCount(dockTile); // Make sure count is updated as soon as we are invoked
} else {
// Strictly speaking this may not be necessary (since the plug-in may be terminated when it's removed from the dock),
/// but it's good practice
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.updateObserver];
self.updateObserver = nil;
}
}
- (void)dealloc {
if (self.updateObserver) {
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self.updateObserver];
self.updateObserver = nil;
}
[super dealloc];
}
@end

View File

@@ -1,834 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
650B29A419B69FC800A5E946 /* MSCToolbar.py in Resources */ = {isa = PBXBuildFile; fileRef = 650B29A319B69FC800A5E946 /* MSCToolbar.py */; };
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */ = {isa = PBXBuildFile; fileRef = C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */; };
C00F67571F016C9F00D9007D /* CocoaWrapper.py in Resources */ = {isa = PBXBuildFile; fileRef = C00F67561F016C9F00D9007D /* CocoaWrapper.py */; };
C01B396D1EEA6A9000DFBA3B /* libpython2.7.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C01B396C1EEA6A9000DFBA3B /* libpython2.7.dylib */; };
C01FBD311EA64CD600AE97EE /* passwdutil.py in Resources */ = {isa = PBXBuildFile; fileRef = C01FBD301EA64CD600AE97EE /* passwdutil.py */; };
C02C98891911B81D00425167 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C02C98871911B81D00425167 /* Localizable.strings */; };
C042CA151EAD86DE006CC681 /* MSCPasswordAlertController.py in Resources */ = {isa = PBXBuildFile; fileRef = C042CA141EAD86DE006CC681 /* MSCPasswordAlertController.py */; };
C0453A211CCEF7B60002D396 /* MSCLogWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */; };
C049C9951AEC77DD00251D45 /* updatesTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = C049C9941AEC77DD00251D45 /* updatesTemplate.png */; };
C05C3CEF188391F200095E65 /* munki.py in Resources */ = {isa = PBXBuildFile; fileRef = C05C3CEE188391F200095E65 /* munki.py */; };
C079D9C118BD435200BAD62E /* AlertController.py in Resources */ = {isa = PBXBuildFile; fileRef = C079D9C018BD435200BAD62E /* AlertController.py */; };
C09004F216CDD84E00BE34CE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C09004F116CDD84E00BE34CE /* Cocoa.framework */; };
C090050016CDD84E00BE34CE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C09004FF16CDD84E00BE34CE /* main.m */; };
C090050616CDD84E00BE34CE /* main.py in Resources */ = {isa = PBXBuildFile; fileRef = C090050516CDD84E00BE34CE /* main.py */; };
C090050816CDD84E00BE34CE /* MSCAppDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = C090050716CDD84E00BE34CE /* MSCAppDelegate.py */; };
C090050B16CDD84E00BE34CE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C090050916CDD84E00BE34CE /* MainMenu.xib */; };
C094B6CF188F7C7700E06897 /* MSCStatusController.py in Resources */ = {isa = PBXBuildFile; fileRef = C094B6CE188F7C7700E06897 /* MSCStatusController.py */; };
C0A71B76188A47C700A6EE82 /* MSCMainWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0A71B75188A47C700A6EE82 /* MSCMainWindowController.py */; };
C0AAA21C18B801400012663F /* MunkiItems.py in Resources */ = {isa = PBXBuildFile; fileRef = C0AAA21B18B801400012663F /* MunkiItems.py */; };
C0AAA21E18BAD8710012663F /* msclog.py in Resources */ = {isa = PBXBuildFile; fileRef = C0AAA21D18BAD8710012663F /* msclog.py */; };
C0AAA22018BC67F90012663F /* mschtml.py in Resources */ = {isa = PBXBuildFile; fileRef = C0AAA21F18BC67F90012663F /* mschtml.py */; };
C0AE8658186D2DF900C87AE7 /* Managed Software Center.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0AE8657186D2DF900C87AE7 /* Managed Software Center.icns */; };
C0AE865A186D32AF00C87AE7 /* MSCBadgedTemplateImage.py in Resources */ = {isa = PBXBuildFile; fileRef = C0AE8659186D32AF00C87AE7 /* MSCBadgedTemplateImage.py */; };
C0B3743F187089F300B6204E /* AllItemsTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = C0B3743A187089F300B6204E /* AllItemsTemplate.png */; };
C0B37442187089F300B6204E /* toolbarCategoriesTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = C0B3743D187089F300B6204E /* toolbarCategoriesTemplate.pdf */; };
C0B3744618708A0300B6204E /* templates in Resources */ = {isa = PBXBuildFile; fileRef = C0B3744418708A0300B6204E /* templates */; };
C0B3744718708A0300B6204E /* WebResources in Resources */ = {isa = PBXBuildFile; fileRef = C0B3744518708A0300B6204E /* WebResources */; };
C0B9E8B219AB8E5500DB7247 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C09004FC16CDD84E00BE34CE /* InfoPlist.strings */; };
C0B9E8B619AF7E9E00DB7247 /* Managed Software Center 10_6.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0B9E8B519AF7E9E00DB7247 /* Managed Software Center 10_6.icns */; };
C0D6D0691EA55B470099C126 /* authrestart.py in Resources */ = {isa = PBXBuildFile; fileRef = C0D6D0681EA55B470099C126 /* authrestart.py */; };
C0E098BC1857A3C80045DEEB /* msclib.py in Resources */ = {isa = PBXBuildFile; fileRef = C0E098BB1857A3C80045DEEB /* msclib.py */; };
C0EF96BA1ADDB9B2002C02FF /* MSCDockTilePlugIn.m in Sources */ = {isa = PBXBuildFile; fileRef = C0EF96B91ADDB9B2002C02FF /* MSCDockTilePlugIn.m */; };
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 */
/* Begin PBXContainerItemProxy section */
C0EF96C11ADDCBAD002C02FF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = C09004E416CDD84E00BE34CE /* Project object */;
proxyType = 1;
remoteGlobalIDString = C0EF96B01ADDB90B002C02FF;
remoteInfo = MSCDockTilePlugin;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
C0EF96BC1ADDBD77002C02FF /* Copy Files */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
C0EF96BD1ADDBD88002C02FF /* MSCDockTilePlugin.docktileplugin in Copy Files */,
);
name = "Copy Files";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
650B29A319B69FC800A5E946 /* MSCToolbar.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCToolbar.py; sourceTree = "<group>"; };
C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = FoundationPlist.py; sourceTree = "<group>"; };
C00F67561F016C9F00D9007D /* CocoaWrapper.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = CocoaWrapper.py; sourceTree = "<group>"; };
C01B396C1EEA6A9000DFBA3B /* libpython2.7.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpython2.7.dylib; path = ../../../../../../../../../System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config/libpython2.7.dylib; sourceTree = SDKROOT; };
C01E26911B4DADDC005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = "<group>"; };
C01E26921B4DADE4005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
C01E26931B4DADEB005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C01E26941B4DADF3005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/MainMenu.xib; sourceTree = "<group>"; };
C01FBD301EA64CD600AE97EE /* passwdutil.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = passwdutil.py; sourceTree = "<group>"; };
C02C98731911B55600425167 /* Managed Software Center-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Managed Software Center-Info.plist"; sourceTree = "<group>"; };
C02C98751911B63E00425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C98761911B63E00425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C98771911B64A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C98781911B64A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C98791911B65600425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C987A1911B65600425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C987B1911B66400425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C987C1911B66400425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C987D1911B67600425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fi; path = fi.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C987E1911B67600425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C987F1911B67F00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C98801911B67F00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C98811911B68800425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C98821911B68800425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C98831911B69000425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/MainMenu.xib; sourceTree = "<group>"; };
C02C98841911B69000425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C02C98881911B81D00425167 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988A1911B82900425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988B1911B82A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988C1911B82C00425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988D1911B82D00425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988E1911B82E00425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C988F1911B82E00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C98901911B83000425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
C02C98911911B83100425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
C042CA141EAD86DE006CC681 /* MSCPasswordAlertController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCPasswordAlertController.py; sourceTree = "<group>"; };
C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCLogWindowController.py; sourceTree = "<group>"; };
C046261519FFF8C000AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = "<group>"; };
C046261619FFF8CC00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
C046261719FFF8DA00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C046261819FFF98900AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/MainMenu.xib; sourceTree = "<group>"; };
C049C9941AEC77DD00251D45 /* updatesTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = updatesTemplate.png; sourceTree = "<group>"; };
C05C3CEE188391F200095E65 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = munki.py; sourceTree = "<group>"; };
C079D9C018BD435200BAD62E /* AlertController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = AlertController.py; sourceTree = "<group>"; };
C07E957E1913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/MainMenu.xib; sourceTree = "<group>"; };
C07E957F1913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
C07E95801913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C09004ED16CDD84E00BE34CE /* Managed Software Center.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Managed Software Center.app"; sourceTree = BUILT_PRODUCTS_DIR; };
C09004F116CDD84E00BE34CE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
C09004F616CDD84E00BE34CE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
C09004F716CDD84E00BE34CE /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
C09004F816CDD84E00BE34CE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
C09004FD16CDD84E00BE34CE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C09004FF16CDD84E00BE34CE /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
C090050116CDD84E00BE34CE /* Managed Software Center-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Managed Software Center-Prefix.pch"; sourceTree = "<group>"; };
C090050516CDD84E00BE34CE /* main.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = main.py; sourceTree = "<group>"; };
C090050716CDD84E00BE34CE /* MSCAppDelegate.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = MSCAppDelegate.py; sourceTree = "<group>"; };
C090050A16CDD84E00BE34CE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = "<group>"; };
C094B6CE188F7C7700E06897 /* MSCStatusController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCStatusController.py; sourceTree = "<group>"; };
C0A71B75188A47C700A6EE82 /* MSCMainWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCMainWindowController.py; sourceTree = "<group>"; };
C0AAA21B18B801400012663F /* MunkiItems.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MunkiItems.py; sourceTree = "<group>"; };
C0AAA21D18BAD8710012663F /* msclog.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = msclog.py; sourceTree = "<group>"; };
C0AAA21F18BC67F90012663F /* mschtml.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = mschtml.py; sourceTree = "<group>"; };
C0AE8657186D2DF900C87AE7 /* Managed Software Center.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Managed Software Center.icns"; sourceTree = "<group>"; };
C0AE8659186D32AF00C87AE7 /* MSCBadgedTemplateImage.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCBadgedTemplateImage.py; sourceTree = "<group>"; };
C0B3743A187089F300B6204E /* AllItemsTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AllItemsTemplate.png; sourceTree = "<group>"; };
C0B3743D187089F300B6204E /* toolbarCategoriesTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = toolbarCategoriesTemplate.pdf; sourceTree = "<group>"; };
C0B3744418708A0300B6204E /* templates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = templates; sourceTree = "<group>"; };
C0B3744518708A0300B6204E /* WebResources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = WebResources; sourceTree = "<group>"; };
C0B9E89F19AA2B6800DB7247 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A119AA2B8400DB7247 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A219AA2B8600DB7247 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A319AA2B8900DB7247 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A419AA2B8B00DB7247 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A519AA2B8D00DB7247 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A619AA2B9100DB7247 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A719AA2B9400DB7247 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A819AA2B9700DB7247 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8A919AA2B9A00DB7247 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8AD19AA55DC00DB7247 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8AE19AA55DC00DB7247 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8AF19AA55DC00DB7247 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/MainMenu.strings; sourceTree = "<group>"; };
C0B9E8B519AF7E9E00DB7247 /* Managed Software Center 10_6.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Managed Software Center 10_6.icns"; sourceTree = "<group>"; };
C0D6D0681EA55B470099C126 /* authrestart.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = authrestart.py; sourceTree = "<group>"; };
C0E098BB1857A3C80045DEEB /* msclib.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = msclib.py; sourceTree = "<group>"; };
C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MSCDockTilePlugin.docktileplugin; sourceTree = BUILT_PRODUCTS_DIR; };
C0EF96B41ADDB90B002C02FF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C0EF96B81ADDB9B2002C02FF /* MSCDockTilePlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCDockTilePlugIn.h; sourceTree = "<group>"; };
C0EF96B91ADDB9B2002C02FF /* MSCDockTilePlugIn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSCDockTilePlugIn.m; sourceTree = "<group>"; };
C0F1586D187D256200052F9A /* MyStuffTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MyStuffTemplate.png; sourceTree = "<group>"; };
E62967521993D4A400ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_GB; path = en_GB.lproj/MainMenu.xib; sourceTree = "<group>"; };
E62967541993D4AF00ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = "<group>"; };
E62967551993D4B900ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/InfoPlist.strings; sourceTree = "<group>"; };
E665C3B81993D58D00C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = "<group>"; };
E665C3BA1993D59600C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/InfoPlist.strings; sourceTree = "<group>"; };
E665C3BB1993D5A600C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_AU; path = en_AU.lproj/MainMenu.xib; sourceTree = "<group>"; };
E68EB44F1993C3950003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/InfoPlist.strings; sourceTree = "<group>"; };
E68EB4501993C3AC0003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_CA; path = en_CA.lproj/MainMenu.xib; sourceTree = "<group>"; };
E68EB4521993C40C0003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/Localizable.strings; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
C09004EA16CDD84E00BE34CE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C09004F216CDD84E00BE34CE /* Cocoa.framework in Frameworks */,
C01B396D1EEA6A9000DFBA3B /* libpython2.7.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0EF96AE1ADDB90B002C02FF /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
C09004E216CDD84E00BE34CE = {
isa = PBXGroup;
children = (
C09004F916CDD84E00BE34CE /* Managed Software Center */,
C0EF96B21ADDB90B002C02FF /* MSCDockTilePlugin */,
C09004F016CDD84E00BE34CE /* Frameworks */,
C09004EE16CDD84E00BE34CE /* Products */,
C01B396C1EEA6A9000DFBA3B /* libpython2.7.dylib */,
);
sourceTree = "<group>";
};
C09004EE16CDD84E00BE34CE /* Products */ = {
isa = PBXGroup;
children = (
C09004ED16CDD84E00BE34CE /* Managed Software Center.app */,
C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */,
);
name = Products;
sourceTree = "<group>";
};
C09004F016CDD84E00BE34CE /* Frameworks */ = {
isa = PBXGroup;
children = (
C09004F116CDD84E00BE34CE /* Cocoa.framework */,
C09004F516CDD84E00BE34CE /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
C09004F516CDD84E00BE34CE /* Other Frameworks */ = {
isa = PBXGroup;
children = (
C09004F616CDD84E00BE34CE /* AppKit.framework */,
C09004F716CDD84E00BE34CE /* CoreData.framework */,
C09004F816CDD84E00BE34CE /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
C09004F916CDD84E00BE34CE /* Managed Software Center */ = {
isa = PBXGroup;
children = (
C042CA141EAD86DE006CC681 /* MSCPasswordAlertController.py */,
C0B9E8B519AF7E9E00DB7247 /* Managed Software Center 10_6.icns */,
C094B6CE188F7C7700E06897 /* MSCStatusController.py */,
C05C3CEE188391F200095E65 /* munki.py */,
C0B37439187089B900B6204E /* Resources */,
C0AE8659186D32AF00C87AE7 /* MSCBadgedTemplateImage.py */,
C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */,
C090050516CDD84E00BE34CE /* main.py */,
C090050716CDD84E00BE34CE /* MSCAppDelegate.py */,
C0AE8657186D2DF900C87AE7 /* Managed Software Center.icns */,
C090050916CDD84E00BE34CE /* MainMenu.xib */,
C09004FA16CDD84E00BE34CE /* Supporting Files */,
C0E098BB1857A3C80045DEEB /* msclib.py */,
C0A71B75188A47C700A6EE82 /* MSCMainWindowController.py */,
C0AAA21B18B801400012663F /* MunkiItems.py */,
C0AAA21D18BAD8710012663F /* msclog.py */,
C0AAA21F18BC67F90012663F /* mschtml.py */,
C079D9C018BD435200BAD62E /* AlertController.py */,
C0D6D0681EA55B470099C126 /* authrestart.py */,
C01FBD301EA64CD600AE97EE /* passwdutil.py */,
650B29A319B69FC800A5E946 /* MSCToolbar.py */,
C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */,
C00F67561F016C9F00D9007D /* CocoaWrapper.py */,
);
path = "Managed Software Center";
sourceTree = "<group>";
};
C09004FA16CDD84E00BE34CE /* Supporting Files */ = {
isa = PBXGroup;
children = (
C0B9E89E19AA2B6800DB7247 /* MainMenu.strings */,
C02C98871911B81D00425167 /* Localizable.strings */,
C02C98731911B55600425167 /* Managed Software Center-Info.plist */,
C09004FC16CDD84E00BE34CE /* InfoPlist.strings */,
C09004FF16CDD84E00BE34CE /* main.m */,
C090050116CDD84E00BE34CE /* Managed Software Center-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
C0B37439187089B900B6204E /* Resources */ = {
isa = PBXGroup;
children = (
C049C9941AEC77DD00251D45 /* updatesTemplate.png */,
C0F1586D187D256200052F9A /* MyStuffTemplate.png */,
C0B3744418708A0300B6204E /* templates */,
C0B3744518708A0300B6204E /* WebResources */,
C0B3743A187089F300B6204E /* AllItemsTemplate.png */,
C0B3743D187089F300B6204E /* toolbarCategoriesTemplate.pdf */,
);
name = Resources;
sourceTree = "<group>";
};
C0EF96B21ADDB90B002C02FF /* MSCDockTilePlugin */ = {
isa = PBXGroup;
children = (
C0EF96B81ADDB9B2002C02FF /* MSCDockTilePlugIn.h */,
C0EF96B91ADDB9B2002C02FF /* MSCDockTilePlugIn.m */,
C0EF96B31ADDB90B002C02FF /* Supporting Files */,
);
path = MSCDockTilePlugin;
sourceTree = "<group>";
};
C0EF96B31ADDB90B002C02FF /* Supporting Files */ = {
isa = PBXGroup;
children = (
C0EF96B41ADDB90B002C02FF /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
C09004EC16CDD84E00BE34CE /* Managed Software Center */ = {
isa = PBXNativeTarget;
buildConfigurationList = C090050E16CDD84E00BE34CE /* Build configuration list for PBXNativeTarget "Managed Software Center" */;
buildPhases = (
C09CE1D918BEA41000B9724A /* Localize */,
C09004E916CDD84E00BE34CE /* Sources */,
C09004EA16CDD84E00BE34CE /* Frameworks */,
C09004EB16CDD84E00BE34CE /* Resources */,
C09CE1DA18BEA59E00B9724A /* Embed version info */,
C0EF96BC1ADDBD77002C02FF /* Copy Files */,
);
buildRules = (
);
dependencies = (
C0EF96C21ADDCBAD002C02FF /* PBXTargetDependency */,
);
name = "Managed Software Center";
productName = SomeDumbTest;
productReference = C09004ED16CDD84E00BE34CE /* Managed Software Center.app */;
productType = "com.apple.product-type.application";
};
C0EF96B01ADDB90B002C02FF /* MSCDockTilePlugin */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0EF96B51ADDB90B002C02FF /* Build configuration list for PBXNativeTarget "MSCDockTilePlugin" */;
buildPhases = (
C0EF96C01ADDC0AF002C02FF /* Resources */,
C0EF96AD1ADDB90B002C02FF /* Sources */,
C0EF96AE1ADDB90B002C02FF /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = MSCDockTilePlugin;
productName = MSCDockTilePlugin;
productReference = C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
C09004E416CDD84E00BE34CE /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = MSC;
LastUpgradeCheck = 0720;
ORGANIZATIONNAME = "The Munki Project";
TargetAttributes = {
C0EF96B01ADDB90B002C02FF = {
CreatedOnToolsVersion = 6.3;
};
};
};
buildConfigurationList = C09004E716CDD84E00BE34CE /* Build configuration list for PBXProject "Managed Software Center" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
da,
Dutch,
English,
fi,
French,
German,
nb,
ru,
Spanish,
sv,
ar,
ca,
cs,
el,
he,
hr,
hu,
Italian,
Japanese,
ko,
no,
pl,
pt,
pt_PT,
ro,
sk,
th,
tr,
uk,
zh_CN,
zh_TW,
fr,
de,
es,
"nl-NL",
nl,
en_CA,
en_GB,
en_AU,
en_US,
"en-CA",
"en-GB",
"en-AU",
it,
ja,
);
mainGroup = C09004E216CDD84E00BE34CE;
productRefGroup = C09004EE16CDD84E00BE34CE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
C09004EC16CDD84E00BE34CE /* Managed Software Center */,
C0EF96B01ADDB90B002C02FF /* MSCDockTilePlugin */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
C09004EB16CDD84E00BE34CE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0B9E8B219AB8E5500DB7247 /* InfoPlist.strings in Resources */,
C02C98891911B81D00425167 /* Localizable.strings in Resources */,
C090050B16CDD84E00BE34CE /* MainMenu.xib in Resources */,
C049C9951AEC77DD00251D45 /* updatesTemplate.png in Resources */,
C00F67571F016C9F00D9007D /* CocoaWrapper.py in Resources */,
C090050616CDD84E00BE34CE /* main.py in Resources */,
C0E098BC1857A3C80045DEEB /* msclib.py in Resources */,
C042CA151EAD86DE006CC681 /* MSCPasswordAlertController.py in Resources */,
C0AAA21E18BAD8710012663F /* msclog.py in Resources */,
C0AAA22018BC67F90012663F /* mschtml.py in Resources */,
C05C3CEF188391F200095E65 /* munki.py in Resources */,
C01FBD311EA64CD600AE97EE /* passwdutil.py in Resources */,
C079D9C118BD435200BAD62E /* AlertController.py in Resources */,
C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */,
C0D6D0691EA55B470099C126 /* authrestart.py in Resources */,
C0453A211CCEF7B60002D396 /* MSCLogWindowController.py in Resources */,
C090050816CDD84E00BE34CE /* MSCAppDelegate.py in Resources */,
C0AE865A186D32AF00C87AE7 /* MSCBadgedTemplateImage.py in Resources */,
C0A71B76188A47C700A6EE82 /* MSCMainWindowController.py in Resources */,
C094B6CF188F7C7700E06897 /* MSCStatusController.py in Resources */,
650B29A419B69FC800A5E946 /* MSCToolbar.py in Resources */,
C0AAA21C18B801400012663F /* MunkiItems.py in Resources */,
C0B9E8B619AF7E9E00DB7247 /* Managed Software Center 10_6.icns in Resources */,
C0AE8658186D2DF900C87AE7 /* Managed Software Center.icns in Resources */,
C0B37442187089F300B6204E /* toolbarCategoriesTemplate.pdf in Resources */,
C0F1586E187D256200052F9A /* MyStuffTemplate.png in Resources */,
C0B3743F187089F300B6204E /* AllItemsTemplate.png in Resources */,
C0B3744718708A0300B6204E /* WebResources in Resources */,
C0B3744618708A0300B6204E /* templates in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0EF96C01ADDC0AF002C02FF /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
C09CE1D918BEA41000B9724A /* Localize */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = Localize;
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "cd Managed\\ Software\\ Center\n\n# generate localizable strings\n./Localize.py --to en --genstrings \"./*.{h,m,py}\" --utf8\n\n# localize nibs\n./Localize.py --from en --to da --utf8\n./Localize.py --from en --to de --utf8\n./Localize.py --from en --to en_CA --utf8\n./Localize.py --from en --to en_GB --utf8\n./Localize.py --from en --to en_AU --utf8\n./Localize.py --from en --to es --utf8\n./Localize.py --from en --to fi --utf8\n./Localize.py --from en --to fr --utf8\n./Localize.py --from en --to it --utf8\n./Localize.py --from en --to ja --utf8\n./Localize.py --from en --to nb --utf8\n./Localize.py --from en --to nl --utf8\n./Localize.py --from en --to ru --utf8\n./Localize.py --from en --to sv --utf8";
};
C09CE1DA18BEA59E00B9724A /* Embed version info */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed version info";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/bash;
shellScript = "# add this number to Git revision index to get \"build\" number consistent with old SVN repo\nMAGICNUMBER=482\n\nBASEVERNUM=`/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"${INFOPLIST_FILE}\"`\n\n# Git isn't installed on 10.6 or earlier by default, so find it\nGIT=`which git`\nif [ \"$GIT\" == \"\" ] ; then\n # let's hope it's in /usr/local/bin\n if [ -x \"/usr/local/bin/git\" ] ; then\n GIT=/usr/local/bin/git\n fi\nfi\n\nif [ \"$GIT\" != \"\" ] ; then\n # generate a pseudo-svn revision number from the list of Git revisions\n GITREV=`$GIT log -n1 --format=\"%H\" -- ./`\n GITREVINDEX=`$GIT rev-list --count $GITREV`\n REV=$(($GITREVINDEX + $MAGICNUMBER))\n\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $BASEVERNUM.$REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $REV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\n /usr/libexec/PlistBuddy -c \"Set :GitRevision string $GITREV\" \"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\"\nfi";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
C09004E916CDD84E00BE34CE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C090050016CDD84E00BE34CE /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C0EF96AD1ADDB90B002C02FF /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0EF96BA1ADDB9B2002C02FF /* MSCDockTilePlugIn.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
C0EF96C21ADDCBAD002C02FF /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = C0EF96B01ADDB90B002C02FF /* MSCDockTilePlugin */;
targetProxy = C0EF96C11ADDCBAD002C02FF /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
C02C98871911B81D00425167 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
C02C98881911B81D00425167 /* en */,
C02C988A1911B82900425167 /* fr */,
C02C988B1911B82A00425167 /* de */,
C02C988C1911B82C00425167 /* es */,
C02C988D1911B82D00425167 /* da */,
C02C988E1911B82E00425167 /* fi */,
C02C988F1911B82E00425167 /* nb */,
C02C98901911B83000425167 /* ru */,
C02C98911911B83100425167 /* sv */,
C07E957F1913EE4600B40B9A /* nl */,
E68EB4521993C40C0003D10C /* en_CA */,
E62967541993D4AF00ADCB43 /* en_GB */,
E665C3B81993D58D00C428C4 /* en_AU */,
C046261619FFF8CC00AF1E48 /* it */,
C01E26921B4DADE4005ACFFB /* ja */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
C09004FC16CDD84E00BE34CE /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
C09004FD16CDD84E00BE34CE /* en */,
C02C98761911B63E00425167 /* fr */,
C02C98781911B64A00425167 /* de */,
C02C987A1911B65600425167 /* es */,
C02C987C1911B66400425167 /* da */,
C02C987E1911B67600425167 /* fi */,
C02C98801911B67F00425167 /* nb */,
C02C98821911B68800425167 /* ru */,
C02C98841911B69000425167 /* sv */,
C07E95801913EE4600B40B9A /* nl */,
E68EB44F1993C3950003D10C /* en_CA */,
E62967551993D4B900ADCB43 /* en_GB */,
E665C3BA1993D59600C428C4 /* en_AU */,
C046261719FFF8DA00AF1E48 /* it */,
C01E26931B4DADEB005ACFFB /* ja */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
C090050916CDD84E00BE34CE /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
C090050A16CDD84E00BE34CE /* en */,
C02C98751911B63E00425167 /* fr */,
C02C98771911B64A00425167 /* de */,
C02C98791911B65600425167 /* es */,
C02C987B1911B66400425167 /* da */,
C02C987D1911B67600425167 /* fi */,
C02C987F1911B67F00425167 /* nb */,
C02C98811911B68800425167 /* ru */,
C02C98831911B69000425167 /* sv */,
C07E957E1913EE4600B40B9A /* nl */,
E68EB4501993C3AC0003D10C /* en_CA */,
E62967521993D4A400ADCB43 /* en_GB */,
E665C3BB1993D5A600C428C4 /* en_AU */,
C046261819FFF98900AF1E48 /* it */,
C01E26941B4DADF3005ACFFB /* ja */,
);
name = MainMenu.xib;
sourceTree = "<group>";
};
C0B9E89E19AA2B6800DB7247 /* MainMenu.strings */ = {
isa = PBXVariantGroup;
children = (
C0B9E89F19AA2B6800DB7247 /* en */,
C0B9E8A119AA2B8400DB7247 /* fr */,
C0B9E8A219AA2B8600DB7247 /* de */,
C0B9E8A319AA2B8900DB7247 /* es */,
C0B9E8A419AA2B8B00DB7247 /* da */,
C0B9E8A519AA2B8D00DB7247 /* fi */,
C0B9E8A619AA2B9100DB7247 /* nb */,
C0B9E8A719AA2B9400DB7247 /* ru */,
C0B9E8A819AA2B9700DB7247 /* sv */,
C0B9E8A919AA2B9A00DB7247 /* nl */,
C0B9E8AD19AA55DC00DB7247 /* en_AU */,
C0B9E8AE19AA55DC00DB7247 /* en_CA */,
C0B9E8AF19AA55DC00DB7247 /* en_GB */,
C046261519FFF8C000AF1E48 /* it */,
C01E26911B4DADDC005ACFFB /* ja */,
);
name = MainMenu.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
C090050C16CDD84E00BE34CE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
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;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
C090050D16CDD84E00BE34CE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
SDKROOT = macosx;
};
name = Release;
};
C090050F16CDD84E00BE34CE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Managed Software Center/Managed Software Center-Prefix.pch";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7,
);
INFOPLIST_FILE = "$(SRCROOT)/Managed Software Center/Managed Software Center-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_LIBRARY_DIR)/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config",
);
MACOSX_DEPLOYMENT_TARGET = 10.8;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.ManagedSoftwareCenter;
PRODUCT_NAME = "Managed Software Center";
SDKROOT = macosx;
WRAPPER_EXTENSION = app;
};
name = Debug;
};
C090051016CDD84E00BE34CE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Managed Software Center/Managed Software Center-Prefix.pch";
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
/System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7,
);
INFOPLIST_FILE = "$(SRCROOT)/Managed Software Center/Managed Software Center-Info.plist";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SYSTEM_LIBRARY_DIR)/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config",
);
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.ManagedSoftwareCenter;
PRODUCT_NAME = "Managed Software Center";
SDKROOT = macosx;
WRAPPER_EXTENSION = app;
};
name = Release;
};
C0EF96B61ADDB90B002C02FF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = MSCDockTilePlugin/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.6;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = NO;
OTHER_LDFLAGS = (
"-framework",
Foundation,
"-framework",
AppKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = NO;
WRAPPER_EXTENSION = docktileplugin;
};
name = Debug;
};
C0EF96B71ADDB90B002C02FF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = NO;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = MSCDockTilePlugin/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.6;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_LDFLAGS = (
"-framework",
Foundation,
"-framework",
AppKit,
);
PRODUCT_BUNDLE_IDENTIFIER = "com.googlecode.munki.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = NO;
WRAPPER_EXTENSION = docktileplugin;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C09004E716CDD84E00BE34CE /* Build configuration list for PBXProject "Managed Software Center" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C090050C16CDD84E00BE34CE /* Debug */,
C090050D16CDD84E00BE34CE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C090050E16CDD84E00BE34CE /* Build configuration list for PBXNativeTarget "Managed Software Center" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C090050F16CDD84E00BE34CE /* Debug */,
C090051016CDD84E00BE34CE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0EF96B51ADDB90B002C02FF /* Build configuration list for PBXNativeTarget "MSCDockTilePlugin" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0EF96B61ADDB90B002C02FF /* Debug */,
C0EF96B71ADDB90B002C02FF /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = C09004E416CDD84E00BE34CE /* Project object */;
}

View File

@@ -1,422 +0,0 @@
# encoding: utf-8
#
# AlertController.py
# Managed Software Center
#
# Created by Greg Neagle on 2/25/14.
#
import os
import authrestart
import munki
import msclog
import MunkiItems
from objc import nil
from AppKit import *
from Foundation import *
from PyObjCTools import AppHelper
# Disable PyLint complaining about 'invalid' camelCase names
# pylint: disable=C0103
class AlertController(NSObject):
'''An object that handles some of our alerts, if for no other reason
than to move a giant bunch of ugly code out of the WindowController'''
def setWindow_(self, the_window):
'''Store our parent window'''
self.window = the_window
def handlePossibleAuthRestart(self):
'''Ask for and store a password for auth restart if needed/possible'''
username = NSUserName()
if (MunkiItems.updatesRequireRestart() and
authrestart.verify_user(username) and
not authrestart.verify_recovery_key_present()):
# FV is on and user is in list of FV users, so they can
# authrestart, and we do not have a stored FV recovery
# key/password. So we should prompt the user for a password
# we can use for fdesetup authrestart
NSApp.delegate(
).passwordAlertController.promptForPasswordForAuthRestart()
def forcedLogoutWarning(self, notification_obj):
'''Display a forced logout warning'''
NSApp.activateIgnoringOtherApps_(True)
info = notification_obj.userInfo()
moreText = NSLocalizedString(
u"All pending updates will be installed. Unsaved work will be lost."
"\nYou may avoid the forced logout by logging out now.",
u"Forced Logout warning detail")
logout_time = None
if info:
logout_time = info.get('logout_time')
elif munki.thereAreUpdatesToBeForcedSoon():
logout_time = munki.earliestForceInstallDate()
if not logout_time:
return
time_til_logout = int(logout_time.timeIntervalSinceNow() / 60)
if time_til_logout > 55:
deadline_str = munki.stringFromDate(logout_time)
msclog.log("user", "forced_logout_warning_initial")
formatString = NSLocalizedString(
u"A logout will be forced at approximately %s.",
u"Logout warning string when logout is an hour or more away")
infoText = formatString % deadline_str + u"\n" + moreText
elif time_til_logout > 0:
msclog.log("user", "forced_logout_warning_%s" % time_til_logout)
formatString = NSLocalizedString(
u"A logout will be forced in less than %s minutes.",
u"Logout warning string when logout is in < 60 minutes")
infoText = formatString % time_til_logout + u"\n" + moreText
else:
msclog.log("user", "forced_logout_warning_final")
infoText = NSLocalizedString(
u"A logout will be forced in less than a minute.\nAll pending "
"updates will be installed. Unsaved work will be lost.",
u"Logout warning string when logout is in less than a minute")
# Set the OK button to default, unless less than 5 minutes to logout
# in which case only the Logout button should be displayed.
self._force_warning_logout_btn = NSLocalizedString(
u"Log out and update now", u"Logout and Update Now button text")
self._force_warning_ok_btn = NSLocalizedString(u"OK",
u"OK button title")
if time_til_logout > 5:
self._force_warning_btns = {
NSAlertDefaultReturn: self._force_warning_ok_btn,
NSAlertAlternateReturn: self._force_warning_logout_btn,
}
else:
self._force_warning_btns = {
NSAlertDefaultReturn: self._force_warning_logout_btn,
NSAlertAlternateReturn: nil,
}
if self.window.attachedSheet():
# there's an existing sheet open
NSApp.endSheet_(self.window.attachedSheet())
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(
u"Forced Logout for Mandatory Install",
u"Forced Logout title text"),
self._force_warning_btns[NSAlertDefaultReturn],
self._force_warning_btns[NSAlertAlternateReturn],
nil,
u"%@", infoText)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.forceLogoutWarningDidEnd_returnCode_contextInfo_, nil)
@AppHelper.endSheetMethod
def forceLogoutWarningDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when the forced logout warning alert ends'''
btn_pressed = self._force_warning_btns.get(returncode)
if btn_pressed == self._force_warning_logout_btn:
msclog.log("user", "install_with_logout")
self.handlePossibleAuthRestart()
try:
munki.logoutAndUpdate()
except munki.ProcessStartError, err:
self.installSessionErrorAlert_(err)
elif btn_pressed == self._force_warning_ok_btn:
msclog.log("user", "dismissed_forced_logout_warning")
def alertToExtraUpdates(self):
'''Notify user of additional pending updates'''
msclog.log("user", "extra_updates_pending")
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(
u"Additional Pending Updates",
u"Additional Pending Updates title"),
NSLocalizedString(u"OK", u"OK button title"),
nil,
nil,
u"%@", NSLocalizedString(
u"There are additional pending updates to install or remove.",
u"Additional Pending Updates detail")
)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.extraUpdatesAlertDidEnd_returnCode_contextInfo_, nil)
@AppHelper.endSheetMethod
def extraUpdatesAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when the extra updates alert ends'''
pass
def confirmUpdatesAndInstall(self):
'''Make sure it's OK to proceed with installing if logout or restart is
required'''
if self.alertedToMultipleUsers():
return
elif MunkiItems.updatesRequireRestart():
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(u"Restart Required",
u"Restart Required title"),
NSLocalizedString(u"Log out and update",
u"Log out and Update button text"),
NSLocalizedString(u"Cancel",
u"Cancel button title/short action text"),
nil,
u"%@", NSLocalizedString(
u"A restart is required after updating. Please be patient "
"as there may be a short delay at the login window. Log "
"out and update now?", u"Restart Required detail")
)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.logoutAlertDidEnd_returnCode_contextInfo_, nil)
elif MunkiItems.updatesRequireLogout() or munki.installRequiresLogout():
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(u"Logout Required", u"Logout Required title"),
NSLocalizedString(u"Log out and update",
u"Log out and Update button text"),
NSLocalizedString(u"Cancel",
u"Cancel button title/short action text"),
nil,
u"%@", NSLocalizedString(
u"A logout is required before updating. Please be patient "
"as there may be a short delay at the login window. Log "
"out and update now?", u"Logout Required detail")
)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.logoutAlertDidEnd_returnCode_contextInfo_, nil)
else:
# we shouldn't have been invoked if neither a restart or logout was
# required
msclog.debug_log(
'confirmUpdatesAndInstall was called but no restart or logout '
'was needed')
@AppHelper.endSheetMethod
def logoutAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when logout alert ends'''
if returncode == NSAlertDefaultReturn:
# make sure this alert panel is gone before we proceed, which
# might involve opening another alert sheet
alert.window().orderOut_(self)
if self.alertedToFirmwareUpdatesAndCancelled():
msclog.log("user", "alerted_to_firmware_updates_and_cancelled")
return
elif self.alertedToRunningOnBatteryAndCancelled():
msclog.log("user", "alerted_on_battery_power_and_cancelled")
return
msclog.log("user", "install_with_logout")
self.handlePossibleAuthRestart()
try:
munki.logoutAndUpdate()
except munki.ProcessStartError, err:
self.installSessionErrorAlert_(err)
elif returncode == NSAlertAlternateReturn:
msclog.log("user", "cancelled")
def installSessionErrorAlert_(self, errmsg):
'''Something has gone wrong and we can't trigger an install at logout'''
msclog.log("user", "install_session_failed")
alertMessageText = NSLocalizedString(
u"Install session failed", u"Install Session Failed title")
detailText = NSLocalizedString(
u"There is a configuration problem with the managed software "
"installer. Could not start the process. Contact your systems "
"administrator.", u"Could Not Start Session message")
detailText += u"\n\n" + unicode(errmsg)
OKButtonTitle = NSLocalizedString(u"OK", u"OK button title")
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.installSessionErrorAlertDidEnd_returnCode_contextInfo_, nil)
@AppHelper.endSheetMethod
def installSessionErrorAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when installSessionErrorAlert ends'''
pass
def alertedToMultipleUsers(self):
'''Returns True if there are multiple GUI logins; alerts as a side
effect'''
if len(munki.currentGUIusers()) > 1:
msclog.log("MSC", "multiple_gui_users_update_cancelled")
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(u"Other users logged in",
u"Other Users Logged In title"),
NSLocalizedString(u"Cancel",
u"Cancel button title/short action text"),
nil,
nil,
u"%@", NSLocalizedString(
u"There are other users logged into this computer.\n"
"Updating now could cause other users to lose their "
"work.\n\nPlease try again later after the other users "
"have logged out.", u"Other Users Logged In detail")
)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.multipleUserAlertDidEnd_returnCode_contextInfo_, nil)
return True
else:
return False
@AppHelper.endSheetMethod
def multipleUserAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when multiple users alert ends'''
pass
def alertedToBlockingAppsRunning(self):
'''Returns True if blocking_apps are running; alerts as a side-effect'''
apps_to_check = []
for update_item in MunkiItems.getUpdateList():
if 'blocking_applications' in update_item:
apps_to_check.extend(update_item['blocking_applications'])
else:
apps_to_check.extend(
[os.path.basename(item.get('path'))
for item in update_item.get('installs', [])
if item['type'] == 'application']
)
running_apps = munki.getRunningBlockingApps(apps_to_check)
if running_apps:
current_user = munki.getconsoleuser()
other_users_apps = [item['display_name'] for item in running_apps
if item['user'] != current_user]
my_apps = [item['display_name'] for item in running_apps
if item['user'] == current_user]
msclog.log(
"MSC", "conflicting_apps", ','.join(other_users_apps + my_apps))
if other_users_apps:
detailText = NSLocalizedString(
u"Other logged in users are using the following "
"applications. Try updating later when they are no longer "
"in use:\n\n%s",
u"Other Users Blocking Apps Running detail")
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(
u"Applications in use by others",
u"Other Users Blocking Apps Running title"),
NSLocalizedString(u"OK", u'OKButtonText'),
nil,
nil,
u"%@", detailText % u'\n'.join(set(other_users_apps))
)
else:
detailText = NSLocalizedString(
u"You must quit the following applications before "
"proceeding with installation or removal:\n\n%s",
u"Blocking Apps Running detail")
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(
u"Conflicting applications running",
u"Blocking Apps Running title"),
NSLocalizedString(u"OK", u"OK button title"),
nil,
nil,
u"%@", detailText % u'\n'.join(set(my_apps))
)
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.window, self,
self.blockingAppsRunningAlertDidEnd_returnCode_contextInfo_,
nil)
return True
else:
return False
@AppHelper.endSheetMethod
def blockingAppsRunningAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when blocking apps alert ends'''
pass
def getFirmwareAlertInfo(self):
'''Get detail about a firmware update'''
info = []
for update_item in MunkiItems.getUpdateList():
if 'firmware_alert_text' in update_item:
info_item = {}
info_item['name'] = update_item.get('display_name', 'name')
alert_text = update_item['firmware_alert_text']
if alert_text == u'_DEFAULT_FIRMWARE_ALERT_TEXT_':
# substitute localized default alert text
alert_text = NSLocalizedString(
(u"Firmware will be updated on your computer. "
"Your computer's power cord must be connected "
"and plugged into a working power source. "
"It may take several minutes for the update to "
"complete. Do not disturb or shut off the power "
"on your computer during this update."),
u"Firmware Alert Default detail")
info_item['alert_text'] = alert_text
info.append(info_item)
return info
def alertedToFirmwareUpdatesAndCancelled(self):
'''Returns True if we have one or more firmware updates and
the user clicks the Cancel button'''
firmware_alert_info = self.getFirmwareAlertInfo()
if not firmware_alert_info:
return False
on_battery_power = munki.onBatteryPower()
for item in firmware_alert_info:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
item['name'],
NSLocalizedString(u"Continue", u"Continue button text"),
NSLocalizedString(
u"Cancel", u"Cancel button title/short action text"),
nil,
u"")
if on_battery_power:
alert_text = NSLocalizedString(
u"Your computer is not connected to a power source.",
u"No Power Source Warning text")
alert_text += "\n\n" + item['alert_text']
else:
alert_text = item['alert_text']
alert.setInformativeText_(alert_text)
alert.setAlertStyle_(NSCriticalAlertStyle)
if on_battery_power:
# set Cancel button to be activated by return key
alert.buttons()[1].setKeyEquivalent_('\r')
# set Continue button to be activated by Escape key
alert.buttons()[0].setKeyEquivalent_(chr(27))
buttonPressed = alert.runModal()
if buttonPressed == NSAlertAlternateReturn:
return True
return False
def alertedToRunningOnBatteryAndCancelled(self):
'''Returns True if we are running on battery and user clicks
the Cancel button'''
if munki.onBatteryPower() and munki.getBatteryPercentage() < 50:
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(
u"Your computer is not connected to a power source.",
u"No Power Source Warning text"),
NSLocalizedString(u"Continue", u"Continue button text"),
NSLocalizedString(u"Cancel",
u"Cancel button title/short action text"),
nil,
u"%@", NSLocalizedString(
u"For best results, you should connect your computer to a "
"power source before updating. Are you sure you want to "
"continue the update?", u"No Power Source Warning detail")
)
msclog.log("MSU", "alert_on_battery_power")
# making UI consistent with Apple Software Update...
# set Cancel button to be activated by return key
alert.buttons()[1].setKeyEquivalent_('\r')
# set Continue button to be activated by Escape key
alert.buttons()[0].setKeyEquivalent_(chr(27))
buttonPressed = alert.runModal()
if buttonPressed == NSAlertAlternateReturn:
return True
return False

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,90 +0,0 @@
# -*- coding: utf-8 -*-
#
# CocoaWrapper.py
# Managed Software Center
#
# Created by Greg Neagle on 6/26/17.
# Copyright (c) 2018-2019 The Munki Project. All rights reserved.
#
"""Selectively import Cocoa symbols to speed up app launch.
Idea from Per Olofsson's AutoDMG"""
# PyLint cannot properly find names inside Cocoa libraries, so issues bogus
# No name 'Foo' in module 'Bar' warnings. Disable them.
# pylint: disable=no-name-in-module
#
# disable unused-import warning, since we don't use any of these here.
# pylint: disable=unused-import
# put all Foundation imports used by the project here
from Foundation import (
NSAppleEventManager,
NSBundle,
NSCachesDirectory,
NSData,
NSDate,
NSDateFormatter,
NSDateFormatterBehavior10_4,
NSFileHandle,
NSFileManager,
NSInsetRect,
NSLocalizedString,
NSLog,
NSMakePoint,
NSMakeRect,
NSMakeSize,
NSMinX,
NSMinY,
NSMutableArray,
NSObject,
NSOffsetRect,
NSPoint,
NSPredicate,
NSString,
NSTimer,
NSURL,
NSURLFileScheme,
NSURLRequest,
NSURLRequestReloadIgnoringLocalCacheData,
NSUTF8StringEncoding,
NSUserDomainMask,
NSUserName,
NSZeroRect,
kCFDateFormatterLongStyle,
kCFDateFormatterShortStyle,
)
# put all AppKit imports used by the project here
from AppKit import (
NSAlert,
NSAlertAlternateReturn,
NSAlertDefaultReturn,
NSAlertFirstButtonReturn,
NSAlertOtherReturn,
NSAlertSecondButtonReturn,
NSApp,
NSApplication,
NSBezierPath,
NSButton,
NSButtonCell,
NSColor,
NSCompositeCopy,
NSCriticalAlertStyle,
NSDistributedNotificationCenter,
NSDragOperationAll,
NSFontAttributeName,
NSFontManager,
NSGraphicsContext,
NSImage,
NSNotFound,
NSNotificationDeliverImmediately,
NSNotificationPostToAllSessions,
NSNotificationSuspensionBehaviorDeliverImmediately,
NSOnState,
NSPasteboard,
NSScreen,
NSUserNotificationCenter,
NSWindowController,
NSWorkspace,
)

View File

@@ -1,116 +0,0 @@
# encoding: utf-8
#
# Copyright 2009-2019 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.
"""FoundationPlist.py -- a tool to generate and parse OS X .plist files.
This is intended as a drop-in replacement for Python's included plistlib,
with a few caveats:
- readPlist() and writePlist() operate only on a filepath,
not a file object.
- there is no support for the deprecated functions:
readPlistFromResource()
writePlistToResource()
- there is no support for the deprecated Plist class.
The Property List (.plist) file format is a simple XML pickle supporting
basic object types, like dictionaries, lists, numbers and strings.
Usually the top level object is a dictionary.
To write out a plist file, use the writePlist(rootObject, filepath)
function. 'rootObject' is the top level object, 'filepath' is a
filename.
To parse a plist from a file, use the readPlist(filepath) function,
with a file name. It returns the top level object (again, usually a
dictionary).
To work with plist data in strings, you can use readPlistFromString()
and writePlistToString().
"""
from Foundation import NSData, \
NSPropertyListSerialization, \
NSPropertyListMutableContainers, \
NSPropertyListXMLFormat_v1_0
class FoundationPlistException(Exception):
pass
class NSPropertyListSerializationException(FoundationPlistException):
pass
class NSPropertyListWriteException(FoundationPlistException):
pass
def readPlist(filepath):
"""
Read a .plist file from filepath. Return the unpacked root object
(which is usually a dictionary).
"""
plistData = NSData.dataWithContentsOfFile_(filepath)
dataObject, plistFormat, error = \
NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
plistData, NSPropertyListMutableContainers, None, None)
if error:
error = error.encode('ascii', 'ignore')
errmsg = "%s in file %s" % (error, filepath)
raise NSPropertyListSerializationException(errmsg)
else:
return dataObject
def readPlistFromString(data):
'''Read a plist data from a string. Return the root object.'''
plistData = buffer(data)
dataObject, plistFormat, error = \
NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
plistData, NSPropertyListMutableContainers, None, None)
if error:
error = error.encode('ascii', 'ignore')
raise NSPropertyListSerializationException(error)
else:
return dataObject
def writePlist(dataObject, filepath):
'''
Write 'rootObject' as a plist to filepath.
'''
plistData, error = \
NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(
dataObject, NSPropertyListXMLFormat_v1_0, None)
if error:
error = error.encode('ascii', 'ignore')
raise NSPropertyListSerializationException(error)
else:
if plistData.writeToFile_atomically_(filepath, True):
return
else:
raise NSPropertyListWriteException(
"Failed to write plist data to %s" % filepath)
def writePlistToString(rootObject):
'''Return 'rootObject' as a plist-formatted string.'''
plistData, error = \
NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(
rootObject, NSPropertyListXMLFormat_v1_0, None)
if error:
error = error.encode('ascii', 'ignore')
raise NSPropertyListSerializationException(error)
else:
return str(plistData)

View File

@@ -1,250 +0,0 @@
#!/usr/bin/python
'''
Wraps the ibtool commandline to generate nibs from .strings files.
An md5 checksum of the base nibs is stored in a Localize.ini file,
if a checksum for the file does not exist or the check does not match
a new localized nib is created.
Based on Philippe Casgrain's 'Automatically localize your nibs when building'
http://developer.casgrain.com/?p=94
And Wil Shipley's 'Pimp My Code, Part 17: Lost in Translations'
http://wilshipley.com/blog/2009/10/pimp-my-code-part-17-lost-in.html
Written by David Keegan for Murky
https://bitbucket.org/snej/murky
Usage:
Localize.py -help
Localize nibs:
Localize.py --from English --to "French|German" --nibs "MainMenu|Projects|Repo"
Generate Strings:
Localize.py --to English --genstrings "./**/*.[hm]"
Use the '--utf8' flag to convert the strings files from utf-16 to utf-8.
The MIT License
Copyright David Keegan 2009-1010
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
'''
from __future__ import with_statement
import time
import codecs
import os, re
import sys, glob
import subprocess
from optparse import OptionParser
from shutil import copyfile
k_valueParse = re.compile('(?P<key>.+)=(?P<value>.+)$', re.UNICODE)
k_localizePath = os.path.abspath('Localize.ini')
class LocalizationError(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return str(self.value)
def detectEncoding(filepath):
'''
Try to detect the file's encoding.
If it's not utf-16 assume it's utf-8, this should work for ascii
files because the first 128 characters are the same...
'''
f = open(filepath, 'r')
firstBytes = f.read(2)
f.close()
if firstBytes == codecs.BOM_UTF16_BE:
return 'utf_16_be'
elif firstBytes == codecs.BOM_UTF16_LE:
return 'utf_16_le'
#use sig just encase there is a BOM in the file
return 'utf_8_sig'
def fileToUtf8(stringFile):
'''
Convert the .strings file from utf-16 to utf-8
This will allow files diffs
'''
if os.path.isfile(stringFile):
tempStrings = stringFile+'temp'
stringsEncoding = detectEncoding(stringFile)
#if the file is not already utf-8 re-encode it
if stringsEncoding != 'utf_8_sig':
fromFile = codecs.open(stringFile, 'rU', stringsEncoding)
toFile = codecs.open(tempStrings, 'w', 'utf_8')
for eachLine in fromFile:
toFile.write(eachLine)
toFile.close()
fromFile.close()
os.remove(stringFile)
os.rename(tempStrings, stringFile)
def runCommand(command, args):
'''Run shell commands'''
commandAndArgs = '%s %s' % (command, args)
proc = subprocess.Popen(commandAndArgs, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
if proc.returncode:
raise LocalizationError(commandAndArgs + ' : ' + stderr)
return stdout
def md5(file):
'''Get the md5 checksum of a file'''
md5Sum = runCommand('/usr/bin/openssl md5', file)
return md5Sum.split('=')[1].strip()
def langProjName(language):
return language.strip()+'.lproj'
def nibToStringFileName(nibFile):
return nibFile.rstrip('.xib')+'.strings'
def ibtoolsGenerateStringsFile(nibFile, utf8=False):
'''
Generate a .strings file from a nib
If utf8 is True the .strings files will be re-encoded as utf-8
'''
nibFileStrings = nibToStringFileName(nibFile)
runCommand('ibtool', '--generate-strings-file %s %s' % (nibFileStrings, nibFile))
if utf8:
fileToUtf8(nibFileStrings)
print ' ', nibFileStrings, 'updated'
def ibtoolsWriteNib(fromFile, toFile, utf8=False):
'''convert one localized nib from one language to another'''
toStrings = nibToStringFileName(toFile)
runCommand('ibtool', '--strings-file %s --write %s %s' % (toStrings, toFile, fromFile))
if utf8:
fileToUtf8(toStrings)
print ' ', toFile, 'updated'
def genStrings(toLangs, globString, utf8=False):
for eachToLang in toLangs:
toLangLproj = langProjName(eachToLang)
runCommand('genstrings', '-o %s %s' % (toLangLproj, globString))
localizableStrings = os.path.join(toLangLproj, 'Localizable.strings')
if utf8:
fileToUtf8(localizableStrings)
print ' ', localizableStrings, 'updated'
def getDict():
'''Read the values from Localize.ini and return a dictionary'''
localizeDict = {}
if not os.path.isfile(k_localizePath):
return localizeDict
with open(k_localizePath, 'rU') as localizeFile:
for line in localizeFile:
line = line.strip()
match = k_valueParse.match(line)
if match:
localizeDict[match.group('key')] = match.group('value')
return localizeDict
def writeDict(dict):
'''Write a dictionary to Localize.ini'''
with open(k_localizePath, 'w') as localizeFile:
for key, value in sorted(dict.iteritems()):
localizeFile.write('%s=%s\n' % (key, value))
def localizeNibs(fromLang, toLangs, nibs=None, utf8=False, ignore=False):
'''Localize nibs from one language to others'''
#get the data from the ini file
iniData = getDict()
fromLangLproj = langProjName(fromLang)
#if nibs is none, get all the nibs in the from language project
if nibs is None:
nibs = []
for eachNib in glob.glob('%s/*.xib' % fromLangLproj):
nibs.append(eachNib.lstrip(fromLangLproj+'/').rstrip('.xib'))
for eachNib in nibs:
eachNib = eachNib.strip()
if not eachNib.endswith('.xib'):
eachNib += '.xib'
fromNib = os.path.join(fromLangLproj, eachNib)
#get md5 and update the ini data
fromNibMd5 = md5(fromNib)
#check if the strings for the fromNib need to the updated
if not os.path.isfile(nibToStringFileName(fromNib)) or fromNib not in iniData or iniData[fromNib] != fromNibMd5:
ibtoolsGenerateStringsFile(fromNib, utf8)
#write the localized nibs
for eachToLang in toLangs:
toLangLproj = langProjName(eachToLang)
toNib = os.path.join(toLangLproj, eachNib)
toStrings = nibToStringFileName(toNib)
#if there is no localized string file for the nib copy it from the 'from language'
if not os.path.isfile(toStrings):
fromStrings = nibToStringFileName(fromNib)
copyfile(fromStrings, toStrings)
toStringsMd5 = md5(toStrings)
if (not os.path.isfile(toNib) or fromNib not in iniData or iniData[fromNib] != fromNibMd5 or
toStrings not in iniData or iniData[toStrings] != toStringsMd5):
ibtoolsWriteNib(fromNib, toNib, utf8)
iniData[toStrings] = toStringsMd5
iniData[fromNib] = fromNibMd5
#update Localize.ini
writeDict(iniData)
if __name__ == '__main__':
'''Command line options'''
startTime = time.time()
opts = OptionParser()
opts.add_option('--from', '-f', dest='fromLang', help='The language to localize from.', metavar='LANG')
opts.add_option('--to', '-t', dest='toLangs', help="An array of languages to localize to, separated by '|'.", metavar='LANGS')
opts.add_option('--nibs', '-n', dest='nibs', help="An array of nibs to localize, separated by '|', .xib can be left off. If this flag is left out all the nibs in the from language will be used.", metavar='NIBS')
opts.add_option('--utf8', '-u', dest='utf8', help='If this flag is present the .strings files will be re-encoded as utf-8.', action="store_true", default=False)
opts.add_option('--ignore', '-i', dest='ignore', help='If this flag is present the md5 checksums will be ignored.', action="store_true", default=False)
opts.add_option('--genstrings', '-g', dest='genstrings', help='File name or glob string. If this argument is present the genstrings command line will be called.', metavar='GLOB', default=None)
options, arguments = opts.parse_args()
if options.genstrings != None:
genStrings(options.toLangs.split('|'), options.genstrings, options.utf8)
print 'Strings updated in %.2f seconds' % (time.time()-startTime)
else:
nibs = options.nibs
if nibs != None:
nibs = options.nibs.split('|')
localizeNibs(options.fromLang, options.toLangs.split('|'), nibs, options.utf8, options.ignore)
print 'Nibs updated in %.2f seconds' % (time.time()-startTime)

View File

@@ -1,165 +0,0 @@
# encoding: utf-8
#
# MSCAppDelegate.py
# Managed Software Center
#
# Copyright 2013-2019 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.
# struct for the url handler
import struct
import os
from urlparse import urlparse
from objc import YES, NO, IBAction, IBOutlet, nil
import PyObjCTools
#from Foundation import *
#from AppKit import *
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
from MSCStatusController import MSCStatusController
import munki
import mschtml
import msclog
import MunkiItems
class MSCAppDelegate(NSObject):
mainWindowController = IBOutlet()
statusController = IBOutlet()
passwordAlertController = IBOutlet()
def applicationShouldTerminate_(self, sender):
'''Called if user selects 'Quit' from menu'''
return self.mainWindowController.appShouldTerminate()
def applicationDidFinishLaunching_(self, sender):
'''NSApplication delegate method called at launch'''
NSLog("Finished launching")
# setup client logging
msclog.setup_logging()
# userInfo dict can be nil, seems to be with 10.6
if sender.userInfo():
userNotification = sender.userInfo().get('NSApplicationLaunchUserNotificationKey')
# we get this notification at launch because it's too early to have declared ourself
# a NSUserNotificationCenterDelegate
if userNotification:
NSLog("Launched via Notification interaction")
self.userNotificationCenter_didActivateNotification_(
NSUserNotificationCenter.defaultUserNotificationCenter(), userNotification)
# Prevent automatic relaunching at login on Lion+
if NSApp.respondsToSelector_('disableRelaunchOnLogin'):
NSApp.disableRelaunchOnLogin()
ver = NSBundle.mainBundle().infoDictionary().get('CFBundleShortVersionString')
msclog.log("MSC", "launched", "VER=%s" % ver)
# if we're running under Snow Leopard, swap out the Dock icon for one
# without the Retina assets to avoid an appearance issue when the
# icon has a badge in the Dock (and App Switcher)
# Darwin major version 10 is Snow Leopard (10.6)
if int(os.uname()[2].split('.')[0]) == 10:
myImage = NSImage.imageNamed_("Managed Software Center 10_6")
NSApp.setApplicationIconImage_(myImage)
# if we are running under Mountain Lion or later set ourselves as a delegate
# for NSUserNotificationCenter notifications
if int(os.uname()[2].split('.')[0]) > 11:
NSUserNotificationCenter.defaultUserNotificationCenter().setDelegate_(self)
# have the statuscontroller register for its own notifications
self.statusController.registerForNotifications()
# user may have launched the app manually, or it may have
# been launched by /usr/local/munki/managedsoftwareupdate
# to display available updates
if munki.thereAreUpdatesToBeForcedSoon(hours=2):
# skip the check and just display the updates
# by pretending the lastcheck is now
lastcheck = NSDate.date()
else:
lastcheck = munki.pref('LastCheckDate')
max_cache_age = munki.pref('CheckResultsCacheSeconds')
# if there is no lastcheck timestamp, check for updates.
if not lastcheck:
self.mainWindowController.checkForUpdates()
elif lastcheck.timeIntervalSinceNow() * -1 > int(max_cache_age):
# check for updates if the last check is over the
# configured manualcheck cache age max.
self.mainWindowController.checkForUpdates()
elif MunkiItems.updateCheckNeeded():
# check for updates if we have optional items selected for install
# or removal that have not yet been processed
self.mainWindowController.checkForUpdates()
# load the initial view only if we are not already loading something else.
# enables launching the app to a specific panel, eg. from URL handler
if not self.mainWindowController.webView.isLoading():
self.mainWindowController.loadInitialView()
def applicationWillFinishLaunching_(self, notification):
'''Installs URL handler for calls outside the app eg. web clicks'''
man = NSAppleEventManager.sharedAppleEventManager()
man.setEventHandler_andSelector_forEventClass_andEventID_(
self,
"openURL:withReplyEvent:",
struct.unpack(">i", "GURL")[0],
struct.unpack(">i", "GURL")[0])
def openMunkiURL(self, url):
'''Display page associated with munki:// url'''
parsed_url = urlparse(url)
if parsed_url.scheme != 'munki':
msclog.debug_log("URL %s has unsupported scheme" % url)
return
filename = mschtml.unquote(parsed_url.netloc)
# add .html if no extension
if not os.path.splitext(filename)[1]:
filename += u'.html'
if filename.endswith(u'.html'):
mschtml.build_page(filename)
self.mainWindowController.load_page(filename)
else:
msclog.debug_log("%s doesn't have a valid extension. Prevented from opening" % url)
def openURL_withReplyEvent_(self, event, replyEvent):
'''Handle openURL messages'''
keyDirectObject = struct.unpack(">i", "----")[0]
url = event.paramDescriptorForKeyword_(keyDirectObject).stringValue().decode('utf8')
msclog.log("MSU", "Called by external URL: %s", url)
self.openMunkiURL(url)
def userNotificationCenter_didActivateNotification_(self, center, notification):
'''User clicked on a Notification Center alert'''
user_info = notification.userInfo() or {}
if user_info.get('action') == 'open_url':
url = user_info.get('value', 'munki://updates')
msclog.log("MSU", "Got user notification to open %s" % url)
self.openMunkiURL(url)
center.removeDeliveredNotification_(notification)
else:
msclog.log("MSU", "Got user notification with unrecognized userInfo")
self.openMunkiURL('munki://updates')
def userNotificationCenter_shouldPresentNotification_(self, center, notification):
return True
def userNotificationCenter_didDeliverNotification_(self, center, notification):
pass

View File

@@ -1,115 +0,0 @@
# encoding: utf-8
#
# MSCBadgedTemplateImage.py
# Managed Software Center
#
# Copyright 2014-2019 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.
# builtin super doesn't work with Cocoa classes in recent PyObjC releases.
from objc import super
#from Foundation import *
#from AppKit import *
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
class MSCBadgedTemplateImage(NSImage):
'''Subclass to handle our updates template image with a badge showing the count
of available updates'''
@classmethod
def imageNamed_withCount_(self, name, count):
'''Returns a template image with a count badge composited in the upper-right
corner of the image'''
# some magic values
NSBoldFontMask = 2
badgeFontSize = 11
badgeFontFamilyName = u'Helvetica'
rrRadius = 7.0
if count == 0:
# no badge if there are no updates
return super(MSCBadgedTemplateImage, self).imageNamed_(name)
# build badge string and get its size
badgeString = NSString.stringWithString_(unicode(count))
badgeFont = NSFontManager.sharedFontManager().fontWithFamily_traits_weight_size_(
badgeFontFamilyName, NSBoldFontMask, 0, badgeFontSize)
stringAttributes = { NSFontAttributeName: badgeFont }
textSize = badgeString.sizeWithAttributes_(stringAttributes)
# use textSize as the basis for the badge outline rect
badgeOutlineHeight = textSize.height
badgeOutlineWidth = textSize.width + rrRadius
if textSize.height > badgeOutlineWidth:
badgeOutlineWidth = badgeOutlineHeight
# get our base image
baseImage = super(MSCBadgedTemplateImage, self).imageNamed_(name).copy()
# size our composite image large enough to include the badge
compositeImageSize = NSMakeSize(baseImage.size().width + badgeOutlineHeight,
baseImage.size().height + badgeOutlineHeight)
# layout the rect for the text
badgeStringRect = NSMakeRect(compositeImageSize.width - textSize.width,
compositeImageSize.height - textSize.height,
textSize.width, textSize.height)
# layout the rect for the badge outline
badgeOutlineRect = NSMakeRect(compositeImageSize.width - badgeOutlineWidth,
compositeImageSize.height - badgeOutlineHeight,
badgeOutlineWidth, badgeOutlineHeight)
# shift the rects around to look better. These are magic numbers.
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)
# build paths for the badge outline and the badge erase mask
badgeOutline = NSBezierPath.bezierPathWithRoundedRect_xRadius_yRadius_(
badgeOutlineRect, rrRadius, rrRadius)
badgeEraseMask = NSBezierPath.bezierPathWithRoundedRect_xRadius_yRadius_(
badgeEraseRect, rrRadius, rrRadius)
# start drawing our composite image
compositeImage = NSImage.alloc().initWithSize_(compositeImageSize)
compositeImage.lockFocus()
# draw base image
baseImageOrigin = NSMakePoint(badgeOutlineHeight/2, badgeOutlineHeight/2)
baseImage.drawAtPoint_fromRect_operation_fraction_(
baseImageOrigin, NSZeroRect, NSCompositeCopy, 1.0)
# erase the part that the badge will be drawn over
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.currentContext().setCompositingOperation_(NSCompositeCopy)
NSColor.blackColor().colorWithAlphaComponent_(0.0).setFill()
badgeEraseMask.fill()
NSGraphicsContext.restoreGraphicsState()
# draw badge outline
badgeOutline.stroke()
# draw count string
badgeString.drawWithRect_options_attributes_(badgeStringRect, 0, stringAttributes)
# all done drawing!
compositeImage.unlockFocus()
compositeImage.setTemplate_(True)
return compositeImage

View File

@@ -1,210 +0,0 @@
# -*- coding: utf-8 -*-
#
# MSCLogWindowController.py
# Managed Software Center
#
# Created by Greg Neagle on 4/25/16.
# Copyright (c) 2016-2019 The Munki Project. All rights reserved.
#
# Much code borrowed from https://github.com/MagerValp/LoginLog
# with the blessing of MagerValp
#
from objc import YES, NO, IBAction, IBOutlet
## pylint: disable=wildcard-import
## pylint: disable=unused-wildcard-import
## pylint: disable=redefined-builtin
#from Foundation import *
#from AppKit import *
## pylint: enable=redefined-builtin
## pylint: enable=wildcard-import
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
import munki
import os
# lots of camelCase names, following Cocoa convention
# pylint: disable=invalid-name
class MSCLogViewDataSource(NSObject):
"""Data source for an NSTableView that displays an array of text lines.
Line breaks are assumed to be LF, and partial lines from incremental
reading is handled."""
# since this subclasses NSObject,
# it doesn't have a Python __init__method
# pylint: disable=no-init
logFileData = NSMutableArray.alloc().init()
filteredData = logFileData
lastLineIsPartial = False
filterText = ''
def tableView_writeRowsWithIndexes_toPasteboard_(
self, aTableView, rowIndexes, pasteboard):
'''Implements drag-n-drop of text rows to external apps'''
text_to_copy = ''
index_set = aTableView.selectedRowIndexes()
index = index_set.firstIndex()
while index != NSNotFound:
line = self.filteredData.objectAtIndex_(index)
text_to_copy += line + '\n'
index = index_set.indexGreaterThanIndex_(index)
#changeCount = pasteboard.clearContents()
result = pasteboard.writeObjects_([text_to_copy])
return YES
def applyFilterToData(self):
'''Filter our log data'''
if len(self.filterText):
filterPredicate = NSPredicate.predicateWithFormat_(
'self CONTAINS[cd] %@', self.filterText)
self.filteredData = (
self.logFileData.filteredArrayUsingPredicate_(filterPredicate))
else:
self.filteredData = self.logFileData
def addLine_partial_(self, line, isPartial):
'''Add a line to our datasource'''
if self.lastLineIsPartial:
joinedLine = self.logFileData.lastObject() + line
self.logFileData.removeLastObject()
self.logFileData.addObject_(joinedLine)
else:
self.logFileData.addObject_(line)
self.lastLineIsPartial = isPartial
self.applyFilterToData()
def removeAllLines(self):
'''Remove all data from our datasource'''
self.logFileData.removeAllObjects()
def lineCount(self):
'''Return the number of lines in our filtered data'''
return self.filteredData.count()
def numberOfRowsInTableView_(self, tableView):
'''Required datasource method'''
return self.lineCount()
def tableView_objectValueForTableColumn_row_(self, tableView, column, row):
'''Required datasource method -- returns the text data for the
given row and column'''
if column.identifier() == 'data':
return self.filteredData.objectAtIndex_(row)
else:
return ''
class MSCLogWindowController(NSObject):
'''Controller object for our log window'''
# since this subclasses NSObject,
# it doesn't have a Python __init__method
# pylint: disable=no-init
window = IBOutlet()
logView = IBOutlet()
searchField = IBOutlet()
pathControl = IBOutlet()
logFileData = MSCLogViewDataSource.alloc().init()
fileHandle = None
updateTimer = None
def copy_(self, sender):
'''Implements copy operation so we can copy data from table view'''
text_to_copy = ''
index_set = self.logView.selectedRowIndexes()
index = index_set.firstIndex()
while index != NSNotFound:
line = self.logFileData.filteredData.objectAtIndex_(index)
text_to_copy += line + '\n'
index = index_set.indexGreaterThanIndex_(index)
pasteboard = NSPasteboard.generalPasteboard()
changeCount = pasteboard.clearContents()
result = pasteboard.writeObjects_([text_to_copy])
@IBAction
def searchFilterChanged_(self, sender):
'''User changed the search field'''
filterString = self.searchField.stringValue().lower()
self.logFileData.filterText = filterString
self.logFileData.applyFilterToData()
self.logView.reloadData()
@IBAction
def showLogWindow_(self, notification):
'''Show the log window.'''
if self.window.isVisible():
# It's already open, just move it to front
self.window.makeKeyAndOrderFront_(self)
return
screenRect = NSScreen.mainScreen().frame()
windowRect = screenRect.copy()
windowRect.origin.x = 100.0
windowRect.origin.y = 200.0
windowRect.size.width -= 200.0
windowRect.size.height -= 300.0
logfile = munki.pref('LogFile')
self.pathControl.setURL_(NSURL.fileURLWithPath_(logfile))
self.window.setTitle_(os.path.basename(logfile))
self.window.setFrame_display_(windowRect, NO)
self.window.makeKeyAndOrderFront_(self)
self.watchLogFile_(logfile)
# allow dragging from table view to outside of the app
self.logView.setDraggingSourceOperationMask_forLocal_(
NSDragOperationAll, NO)
def watchLogFile_(self, logFile):
'''Display and continuously update a log file in the main window.'''
self.stopWatching()
self.logFileData.removeAllLines()
self.logView.setDataSource_(self.logFileData)
self.logView.reloadData()
self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile)
self.refreshLog()
# Kick off a timer that updates the log view periodically.
self.updateTimer = (
NSTimer.
scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
0.25, self, self.refreshLog, None, YES))
def stopWatching(self):
'''Release the file handle and stop the update timer.'''
if self.fileHandle is not None:
self.fileHandle.closeFile()
self.fileHandle = None
if self.updateTimer is not None:
self.updateTimer.invalidate()
self.updateTimer = None
def refreshLog(self):
'''Check for new available data, read it, and scroll to the bottom.'''
data = self.fileHandle.availableData()
if data.length():
utf8string = NSString.alloc().initWithData_encoding_(
data, NSUTF8StringEncoding)
for line in utf8string.splitlines(True):
if line.endswith(u"\n"):
self.logFileData.addLine_partial_(line.rstrip(u"\n"), False)
else:
self.logFileData.addLine_partial_(line, True)
self.logView.reloadData()
self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1)
def windowWillClose_(self, notification):
'''NSWindow delegate method -- if our window is closing,
stop watching the log file.'''
self.stopWatching()

View File

@@ -1,127 +0,0 @@
# -*- coding: utf-8 -*-
#
# MSCPasswordAlertController.py
# Managed Software Center
#
# Created by Greg Neagle on 4/17/17.
# Copyright (c) 2018-2019 The Munki Project. All rights reserved.
#
'''Controller for our custom alert that prompts for password'''
from objc import IBAction, IBOutlet, nil
from PyObjCTools import AppHelper
from Quartz import CAKeyframeAnimation, CGPathCreateMutable
from Quartz import CGPathAddLineToPoint, CGPathMoveToPoint
from Quartz import CGPathCloseSubpath
#from Foundation import *
#from AppKit import *
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
import authrestart
import msclog
import munki
import passwdutil
# Disable PyLint complaining about 'invalid' camelCase names
# pylint: disable=C0103
class MSCPasswordAlertController(NSObject):
'''An object that handles our password alert'''
# Cocoa UI binding properties
passwordView = IBOutlet()
passwordLabel = IBOutlet()
passwordField = IBOutlet()
def promptForPasswordForAuthRestart(self):
'''Set up and display our alert that prompts for password'''
# Set up all the fields and buttons with localized text
alert = NSAlert.alloc().init()
alert.addButtonWithTitle_(
NSLocalizedString(u"Allow", u"Allow button text"))
alert.addButtonWithTitle_(
NSLocalizedString(u"Deny", u"Deny button text"))
alert.setMessageText_(NSLocalizedString(
u"Managed Software Center wants to unlock the startup disk after "
"restarting to complete all pending updates.",
u"Password prompt title"))
alert.setInformativeText_(NSLocalizedString(
u"To allow this, enter your login password.",
u"Password explanation"))
alert.setAccessoryView_(self.passwordView)
self.passwordLabel.setStringValue_(NSLocalizedString(
u"Password:",u"Password label"))
self.passwordField.setStringValue_(u"")
# resize label to fit the text
self.passwordLabel.sizeToFit()
# resize the password field to use the rest of the available space
viewWidth = self.passwordView.frame().size.width
labelWidth = self.passwordLabel.frame().size.width
fieldFrame = self.passwordField.frame()
fieldFrame.origin.x = labelWidth + 8
fieldFrame.size.width = viewWidth - labelWidth - 8
self.passwordField.setFrame_(fieldFrame)
# add esc as a key equivalent for the Deny button
alert.buttons().objectAtIndex_(1).setKeyEquivalent_(chr(27))
# change the Allow button to call our password validation method
allowButton = alert.buttons().objectAtIndex_(0)
allowButton.setTarget_(self)
allowButton.setAction_(self.verifyPassword_)
# make sure our password field is ready to accept input
alert.window().setInitialFirstResponder_(self.passwordField)
# we can finally run the alert!
result = alert.runModal()
if result == NSAlertFirstButtonReturn:
# they clicked "Allow". We handled it in the verifyPassword method
msclog.log("user", "stored password for auth restart")
if result == NSAlertSecondButtonReturn:
# they clicked "Deny"
msclog.log("user", "denied password for auth restart")
def verifyPassword_(self, alert):
username = NSUserName()
password = self.passwordField.stringValue()
if passwdutil.verifyPassword(username, password):
# store username and password and end modal alert
authrestart.store_password(password, username=username)
code = NSAlertFirstButtonReturn
NSApplication.sharedApplication().stopModalWithCode_(code)
NSApplication.sharedApplication().endSheet_returnCode_(
alert, code)
alert.window().orderOut_(None)
else:
# wrong password, shake the alert window
self.shake(alert.window())
def shake(self, the_window):
'''Uses CoreAnimation to "shake" the alert window'''
# adapted from here:
# http://stackoverflow.com/questions/10517386/how-to-give-nswindow-a-shake-effect-as-saying-no-as-in-login-failure-window/23491643#23491643
numberOfShakes = 3
durationOfShake = 0.5
vigourOfShake = 0.05
frame = the_window.frame()
shakeAnimation = CAKeyframeAnimation.animation()
shakePath = CGPathCreateMutable()
CGPathMoveToPoint(shakePath, None, NSMinX(frame), NSMinY(frame))
for index in range(numberOfShakes):
CGPathAddLineToPoint(
shakePath, None,
NSMinX(frame) - frame.size.width * vigourOfShake, NSMinY(frame))
CGPathAddLineToPoint(
shakePath, None,
NSMinX(frame) + frame.size.width * vigourOfShake, NSMinY(frame))
CGPathCloseSubpath(shakePath)
shakeAnimation.setPath_(shakePath)
shakeAnimation.setDuration_(durationOfShake)
the_window.setAnimations_({'frameOrigin': shakeAnimation})
the_window.animator().setFrameOrigin_(frame.origin)

View File

@@ -1,403 +0,0 @@
# encoding: utf-8
#
# MSCStatusController.py
#
# Copyright 2009-2019 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.
from objc import YES, IBOutlet, nil
from PyObjCTools import AppHelper
#from Foundation import *
#from AppKit import *
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
import munki
import msclog
debug = False
class MSCStatusController(NSObject):
'''
Handles status messages from managedsoftwareupdate
'''
session_started = False
got_status_update = False
timer = None
_status_stopBtnDisabled = False
_status_stopBtnHidden = False
_status_message = u''
_status_detail = u''
_status_percent = -1
_status_stopBtnState = 0
statusWindowController = IBOutlet()
def registerForNotifications(self):
'''Register for notification messages'''
notification_center = NSDistributedNotificationCenter.defaultCenter()
notification_center.addObserver_selector_name_object_suspensionBehavior_(
self,
self.updateStatus_,
'com.googlecode.munki.managedsoftwareupdate.statusUpdate',
None,
NSNotificationSuspensionBehaviorDeliverImmediately)
self.receiving_notifications = True
def unregisterForNotifications(self):
'''Tell the DistributedNotificationCenter to stop sending us notifications'''
NSDistributedNotificationCenter.defaultCenter().removeObserver_(self)
# set self.receiving_notifications to False so our process monitoring
# thread will exit
self.receiving_notifications = False
def startMunkiStatusSession(self):
'''Initialize things for monitoring a managedsoftwareupdate session'''
self.initStatusSession()
self.session_started = True
# start our process monitor timer so we can be notified about
# process failure
self.timeout_counter = 6
self.saw_process = False
self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
5.0, self, self.checkProcess_, None, YES)
def checkProcess_(self, timer):
'''Monitors managedsoftwareupdate process for failure to start
or unexpected exit, so we're not waiting around forever if
managedsoftwareupdate isn't running.'''
PYTHON_SCRIPT_NAME = u'managedsoftwareupdate'
NEVER_STARTED = -2
UNEXPECTEDLY_QUIT = -1
if self.session_started:
if self.got_status_update:
# we got a status update since we last checked; no need to
# check the process table
self.timeout_counter = 6
self.saw_process = True
# clear the flag so we have to get another status update
self.got_status_update = False
elif munki.pythonScriptRunning(PYTHON_SCRIPT_NAME):
self.timeout_counter = 6
self.saw_process = True
else:
msclog.debug_log('managedsoftwareupdate not running...')
self.timeout_counter -= 1
if self.timeout_counter == 0:
msclog.debug_log('Timed out waiting for managedsoftwareupdate.')
if self.saw_process:
self.sessionEnded_(UNEXPECTEDLY_QUIT)
else:
self.sessionEnded_(NEVER_STARTED)
def sessionStarted(self):
'''Accessor method'''
return self.session_started
def sessionEnded_(self, result):
'''clean up after a managedsoftwareupdate session ends'''
if self.timer:
self.timer.invalidate()
self.timer = None
self.cleanUpStatusSession()
# tell the window controller the update session is done
self.statusWindowController.munkiStatusSessionEndedWithStatus_errorMessage_(result, "")
def updateStatus_(self, notification):
'''Got update status notification from managedsoftwareupdate'''
msclog.debug_log('Got munkistatus update notification')
self.got_status_update = True
info = notification.userInfo()
msclog.debug_log('%s' % info)
# explicitly get keys from info object; PyObjC in Mountain Lion
# seems to need this
info_keys = info.keys()
if 'message' in info_keys:
self.setMessage_(info['message'])
if 'detail' in info_keys:
self.setDetail_(info['detail'])
if 'percent' in info_keys:
self.setPercentageDone_(info['percent'])
if 'stop_button_visible' in info_keys:
if info['stop_button_visible']:
self.showStopButton()
else:
self.hideStopButton()
if 'stop_button_enabled' in info_keys:
if info['stop_button_enabled']:
self.enableStopButton()
else:
self.disableStopButton()
command = info.get('command')
if not self.session_started and command not in ['showRestartAlert', 'quit']:
# we got a status message but we didn't start the session
# so switch to the right mode
self.startMunkiStatusSession()
if command:
msclog.debug_log('Received command: %s' % command)
if command == 'activate':
pass
elif command == 'showRestartAlert':
if self.session_started:
self.sessionEnded_(0)
self.doRestartAlert()
elif command == 'quit':
self.sessionEnded_(0)
##### required status methods #####
def initStatusSession(self):
'''Initialize the main window for update status'''
self.statusWindowController._update_in_progress = True
if self.statusWindowController.currentPageIsUpdatesPage():
self.statusWindowController.webView.reload_(self)
self.statusWindowController.displayUpdateCount()
def cleanUpStatusSession(self):
'''Clean up after status session ends'''
self.session_started = False
# reset all our status variables
self.statusWindowController._update_in_progress = False
self._status_stopBtnDisabled = False
self._status_stopBtnHidden = False
self._status_stopBtnState = 0
self._status_message = u''
self._status_detail = u''
self._status_percent = -1
def setPercentageDone_(self, percent):
'''Display percentage done'''
try:
if float(percent) > 100.0:
percent = 100
except ValueError:
percent = 0
self._status_percent = percent
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
progress = document.getElementById_('progress-bar')
if progress:
if float(percent) < 0:
# indeterminate
progress.setClassName_('indeterminate')
progress.removeAttribute_('style')
else:
progress.setClassName_('')
progress.setAttribute__(
'style', 'width: %s%%' % percent)
def doRestartAlert(self):
'''Display a restart alert -- some item just installed or removed
requires a restart'''
msclog.log("MSC", "restart_required")
self._status_restartAlertDismissed = 0
alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_(
NSLocalizedString(u"Restart Required", u"Restart Required title"),
NSLocalizedString(u"Restart", u"Restart button title"),
nil,
nil,
u"%@", NSLocalizedString(
u"Software installed or removed requires a restart. You will "
"have a chance to save open documents.",
u"Restart Required alert detail"))
alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_(
self.statusWindowController.window(),
self, self.restartAlertDidEnd_returnCode_contextInfo_, nil)
@AppHelper.endSheetMethod
def restartAlertDidEnd_returnCode_contextInfo_(
self, alert, returncode, contextinfo):
'''Called when restartAlert ends'''
msclog.log("MSC", "restart_confirmed")
self._status_restartAlertDismissed = 1
munki.restartNow()
def setMessage_(self, messageText):
'''Display main status message'''
messageText = NSBundle.mainBundle().localizedStringForKey_value_table_(
messageText, messageText, None)
self._status_message = messageText
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
textElement = document.getElementById_('primary-status-text')
if textElement:
if messageText:
textElement.setInnerText_(messageText)
else:
textElement.setInnerHTML_('&nbsp;')
def setDetail_(self, detailText):
'''Display status detail'''
detailText = NSBundle.mainBundle().localizedStringForKey_value_table_(
detailText, detailText, None)
self._status_detail = detailText
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
textElement = document.getElementById_('secondary-status-text')
if textElement:
if detailText:
textElement.setInnerText_(detailText)
else:
textElement.setInnerHTML_('&nbsp;')
def getStopBtnState(self):
'''Get the state (pressed or not) of the stop button'''
return self._status_stopBtnState
def hideStopButton(self):
'''Hide the stop button'''
if self._status_stopBtnState:
return
self._status_stopBtnHidden = True
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
install_btn = document.getElementById_(
'install-all-button-text')
if install_btn:
btn_classes = install_btn.className().split(' ')
if not 'hidden' in btn_classes:
btn_classes.append('hidden')
install_btn.setClassName_(' '.join(btn_classes))
def showStopButton(self):
'''Show the stop button'''
if self._status_stopBtnState:
return
self._status_stopBtnHidden = False
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
install_btn = document.getElementById_(
'install-all-button-text')
if install_btn:
btn_classes = install_btn.className().split(' ')
if 'hidden' in btn_classes:
btn_classes.remove('hidden')
install_btn.setClassName_(' '.join(btn_classes))
def enableStopButton(self):
'''Enable the stop button'''
if self._status_stopBtnState:
return
self._status_stopBtnDisabled = False
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
install_btn = document.getElementById_(
'install-all-button-text')
if install_btn:
btn_classes = install_btn.className().split(' ')
if 'disabled' in btn_classes:
btn_classes.remove('disabled')
install_btn.setClassName_(' '.join(btn_classes))
def disableStopButton(self):
'''Disable the stop button'''
if self._status_stopBtnState:
return
self._status_stopBtnDisabled = True
document = self.statusWindowController.webView.mainFrameDocument()
if document:
spinner = document.getElementById_('updates-progress-spinner')
if spinner: # we are displaying the updates status page
install_btn = document.getElementById_(
'install-all-button-text')
if install_btn:
btn_classes = install_btn.className().split(' ')
if not 'disabled' in btn_classes:
btn_classes.append('disabled')
install_btn.setClassName_(' '.join(btn_classes))
def getRestartAlertDismissed(self):
'''Was the restart alert dismissed?'''
return self._status_restartAlertDismissed
def more_localized_strings():
'''Some strings that are sent to us from managedsoftwareupdate. By putting
them here, the localize.py script will add them to the
en.lproj/Localizable.strings file so localizers will be able to discover
them'''
_ = NSLocalizedString(u"Starting...", "managedsoftwareupdate message")
_ = NSLocalizedString(u"Finishing...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Performing preflight tasks...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Performing postflight tasks...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Checking for available updates...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Checking for additional changes...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Software installed or removed requires a restart.",
"managedsoftwareupdate message")
_ = NSLocalizedString(
u"Waiting for network...", "managedsoftwareupdate message")
_ = NSLocalizedString(u"Done.", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Retrieving list of software for this machine...",
"managedsoftwareupdate message")
_ = NSLocalizedString(
u"Verifying package integrity...", "managedsoftwareupdate message")
_ = NSLocalizedString(u"The software was successfully installed.",
"managedsoftwareupdate message")
_ = NSLocalizedString(u"Gathering information on installed packages",
"managedsoftwareupdate message")
_ = NSLocalizedString(u"Determining which filesystem items to remove",
"managedsoftwareupdate message")
_ = NSLocalizedString(
u"Removing receipt info", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Nothing to remove.", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Package removal complete.", "managedsoftwareupdate message")
_ = NSLocalizedString(u"Checking for available Apple Software Updates...",
"managedsoftwareupdate message")
_ = NSLocalizedString(u"Checking Apple Software Update catalog...",
"managedsoftwareupdate message")
_ = NSLocalizedString(u"Downloading available Apple Software Updates...",
"managedsoftwareupdate message")
_ = NSLocalizedString(u"Installing available Apple Software Updates...",
"managedsoftwareupdate message")
_ = NSLocalizedString(
u"Running Adobe Setup", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Running Adobe Uninstall", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Starting Adobe installer...", "managedsoftwareupdate message")
_ = NSLocalizedString(
u"Running Adobe Patch Installer", "managedsoftwareupdate message")

View File

@@ -1,54 +0,0 @@
# encoding: utf-8
#
# MSCToolbar
# Managed Software Center
#
# Created by Daniel Hazelbaker on 9/2/14.
#
# builtin super doesn't work with Cocoa classes in recent PyObjC releases.
from objc import super
from objc import YES, NO, nil
#from Foundation import *
#from AppKit import *
# pylint: disable=wildcard-import
from CocoaWrapper import *
# pylint: enable=wildcard-import
class MSCToolbarButton(NSButton):
'''Subclass of NSButton which properly works inside of a toolbar item
to allow clicking on the label.'''
def hitTest_(self, aPoint):
view = super(MSCToolbarButton, self).hitTest_(aPoint)
if view == nil:
for v in self.superview().subviews():
if v != self and v.hitTest_(aPoint) != nil:
view = self
break
return view
class MSCToolbarButtonCell(NSButtonCell):
'''Subclass of NSButtonCell which properly works inside of a toolbar item
to allow clicking on the label.'''
def _hitTestForTrackMouseEvent_inRect_ofView_(self, theEvent,
rect, controlView):
aPoint = controlView.superview().convertPoint_fromView_(
theEvent.locationInWindow(), nil)
hit = NO
for v in controlView.superview().subviews():
if v.hitTest_(aPoint) != nil:
hit = YES
break
return hit

View File

@@ -1,62 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.8</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.googlecode.munki</string>
<key>CFBundleURLSchemes</key>
<array>
<string>munki</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>GitRevision</key>
<string></string>
<key>LSHasLocalizedDisplayName</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSDockTilePlugIn</key>
<string>MSCDockTilePlugin.docktileplugin</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 The Munki Project https://github.com/munki/munki</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>NSUserNotificationAlertStyle</key>
<string>alert</string>
</dict>
</plist>

View File

@@ -1,7 +0,0 @@
//
// Prefix header for all source files of the 'Managed Software Center' target in the 'Managed Software Center' project
//
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif

Some files were not shown because too many files have changed in this diff Show More