mirror of
https://github.com/munki/munki.git
synced 2026-01-28 18:09:12 -06:00
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:
3
CONTRIBUTING.md
Normal file
3
CONTRIBUTING.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Contributions to Munki are welcome!
|
||||
|
||||
Please see https://github.com/munki/munki/wiki/Contributing-to-Munki
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
attributes = {
|
||||
CLASSPREFIX = MSC;
|
||||
LastSwiftUpdateCheck = 0920;
|
||||
LastUpgradeCheck = 1000;
|
||||
LastUpgradeCheck = 1020;
|
||||
ORGANIZATIONNAME = "The Munki Project";
|
||||
TargetAttributes = {
|
||||
C0196316210507DB0009F51A = {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
2
code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings
Executable file → Normal file
2
code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings
Executable file → Normal 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";
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
95
code/apps/Managed Software Center/Managed Software Center/msclog.swift
Executable file → Normal file
95
code/apps/Managed Software Center/Managed Software Center/msclog.swift
Executable file → Normal 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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
@@ -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";
|
||||
20
code/apps/MunkiStatus/MunkiStatus/main.swift
Normal file
20
code/apps/MunkiStatus/MunkiStatus/main.swift
Normal 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)
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
@@ -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.";
|
||||
|
||||
|
||||
19
code/apps/pyobjc/.gitignore
vendored
19
code/apps/pyobjc/.gitignore
vendored
@@ -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
|
||||
@@ -1,10 +0,0 @@
|
||||
# .DS_Store files!
|
||||
.DS_Store
|
||||
|
||||
# don't track .pyc files
|
||||
*.pyc
|
||||
|
||||
# Xcode 5 user data
|
||||
*.xcodeproj/project.xcworkspace/
|
||||
*.xcodeproj/xcuserdata/
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */;
|
||||
}
|
||||
@@ -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 |
@@ -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,
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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_(' ')
|
||||
|
||||
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_(' ')
|
||||
|
||||
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")
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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>
|
||||
@@ -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
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user