diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..b99e97e0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +Contributions to Munki are welcome! + +Please see https://github.com/munki/munki/wiki/Contributing-to-Munki diff --git a/code/apps/Managed Software Center/MSCDockTilePlugin/Info.plist b/code/apps/Managed Software Center/MSCDockTilePlugin/Info.plist index a65d44a6..b9bdd196 100755 --- a/code/apps/Managed Software Center/MSCDockTilePlugin/Info.plist +++ b/code/apps/Managed Software Center/MSCDockTilePlugin/Info.plist @@ -19,7 +19,7 @@ CFBundleVersion 1 NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. + Copyright © 2019-2020 The Munki Project. All rights reserved. NSPrincipalClass MSCDockTilePlugin diff --git a/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h b/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h index f046933a..58d5d4a0 100755 --- a/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h +++ b/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h @@ -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 diff --git a/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m b/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m index 213c6f85..448f0803 100755 --- a/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m +++ b/code/apps/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj b/code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj index 39c41894..d619c5c1 100755 --- a/code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj +++ b/code/apps/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj @@ -284,7 +284,7 @@ attributes = { CLASSPREFIX = MSC; LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "The Munki Project"; TargetAttributes = { C0196316210507DB0009F51A = { diff --git a/code/apps/Managed Software Center/Managed Software Center/AppDelegate.swift b/code/apps/Managed Software Center/Managed Software Center/AppDelegate.swift index c3252c73..ab791640 100755 --- a/code/apps/Managed Software Center/Managed Software Center/AppDelegate.swift +++ b/code/apps/Managed Software Center/Managed Software Center/AppDelegate.swift @@ -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") } } diff --git a/code/apps/Managed Software Center/Managed Software Center/Base.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/Base.lproj/InfoPlist.strings index 1e9d483e..aa4a2874 100644 --- a/code/apps/Managed Software Center/Managed Software Center/Base.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/Base.lproj/InfoPlist.strings @@ -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"; diff --git a/code/apps/Managed Software Center/Managed Software Center/Controllers/LogViewController.swift b/code/apps/Managed Software Center/Managed Software Center/Controllers/LogViewController.swift index 098f84c9..302ab58c 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Controllers/LogViewController.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Controllers/LogViewController.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCAlertController.swift b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCAlertController.swift index ee97a75a..7a5cddfb 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCAlertController.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCAlertController.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCPasswordAlertController.swift b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCPasswordAlertController.swift index 859b2480..89323b26 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCPasswordAlertController.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCPasswordAlertController.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCStatusController.swift b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCStatusController.swift index b03e0265..0943c813 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCStatusController.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Controllers/MSCStatusController.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift b/code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift index 8a2ab494..73228953 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Controllers/MainWindowController.swift @@ -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) } diff --git a/code/apps/Managed Software Center/Managed Software Center/FoundationPlist.swift b/code/apps/Managed Software Center/Managed Software Center/FoundationPlist.swift index 8fd4e528..86e9c50e 100755 --- a/code/apps/Managed Software Center/Managed Software Center/FoundationPlist.swift +++ b/code/apps/Managed Software Center/Managed Software Center/FoundationPlist.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/HtmlFilter.swift b/code/apps/Managed Software Center/Managed Software Center/HtmlFilter.swift index 65d91bf2..7ddef160 100755 --- a/code/apps/Managed Software Center/Managed Software Center/HtmlFilter.swift +++ b/code/apps/Managed Software Center/Managed Software Center/HtmlFilter.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Info.plist b/code/apps/Managed Software Center/Managed Software Center/Info.plist index 37d0779c..9b711d9a 100644 --- a/code/apps/Managed Software Center/Managed Software Center/Info.plist +++ b/code/apps/Managed Software Center/Managed Software Center/Info.plist @@ -47,7 +47,7 @@ NSDockTilePlugIn MSCDockTilePlugin.docktileplugin NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. + Copyright © 2019-2020 The Munki Project. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/code/apps/Managed Software Center/Managed Software Center/Localization.swift b/code/apps/Managed Software Center/Managed Software Center/Localization.swift index fbce77ce..38ff64f2 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Localization.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Localization.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.swift b/code/apps/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.swift index 9bf8332c..83757719 100644 --- a/code/apps/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.swift +++ b/code/apps/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/MSCToolbarButton.swift b/code/apps/Managed Software Center/Managed Software Center/MSCToolbarButton.swift index e86f1f77..60bfd236 100755 --- a/code/apps/Managed Software Center/Managed Software Center/MSCToolbarButton.swift +++ b/code/apps/Managed Software Center/Managed Software Center/MSCToolbarButton.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/MSCWebView.swift b/code/apps/Managed Software Center/Managed Software Center/MSCWebView.swift index 6979b90a..be2f7ae5 100755 --- a/code/apps/Managed Software Center/Managed Software Center/MSCWebView.swift +++ b/code/apps/Managed Software Center/Managed Software Center/MSCWebView.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/MunkiItems.swift b/code/apps/Managed Software Center/Managed Software Center/MunkiItems.swift index 08fd4cdf..079dd9b5 100755 --- a/code/apps/Managed Software Center/Managed Software Center/MunkiItems.swift +++ b/code/apps/Managed Software Center/Managed Software Center/MunkiItems.swift @@ -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.. [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.. [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( { diff --git a/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/detail.css b/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/detail.css index afebc5f0..b6f1c1bd 100644 --- a/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/detail.css +++ b/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/detail.css @@ -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); diff --git a/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/updates.css b/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/updates.css index c03f861e..1821280d 100644 --- a/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/updates.css +++ b/code/apps/Managed Software Center/Managed Software Center/Resources/WebResources/updates.css @@ -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; } } diff --git a/code/apps/Managed Software Center/Managed Software Center/SelfService.swift b/code/apps/Managed Software Center/Managed Software Center/SelfService.swift index 70a59649..e48278be 100755 --- a/code/apps/Managed Software Center/Managed Software Center/SelfService.swift +++ b/code/apps/Managed Software Center/Managed Software Center/SelfService.swift @@ -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 { diff --git a/code/apps/Managed Software Center/Managed Software Center/Socket.swift b/code/apps/Managed Software Center/Managed Software Center/Socket.swift index 4897c5ed..4c5edeb7 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Socket.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Socket.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/Template.swift b/code/apps/Managed Software Center/Managed Software Center/Template.swift index 9661123c..6dd52624 100755 --- a/code/apps/Managed Software Center/Managed Software Center/Template.swift +++ b/code/apps/Managed Software Center/Managed Software Center/Template.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/authrestart.swift b/code/apps/Managed Software Center/Managed Software Center/authrestart.swift index 50c7fdcd..b63e44d7 100755 --- a/code/apps/Managed Software Center/Managed Software Center/authrestart.swift +++ b/code/apps/Managed Software Center/Managed Software Center/authrestart.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings index 8a1ea041..eb02dda4 100755 --- a/code/apps/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Center"; "CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings index fe8eea53..ffa12b92 100755 --- a/code/apps/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Geführte Softwareaktualisierung"; "CFBundleDisplayName" = "Geführte Softwareaktualisierung"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings old mode 100755 new mode 100644 index b1aa5fb8..84e74e8a --- a/code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/en-AU.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Centre"; "CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2018 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/en-CA.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en-CA.lproj/InfoPlist.strings index 4a7411be..84e74e8a 100644 --- a/code/apps/Managed Software Center/Managed Software Center/en-CA.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/en-CA.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Centre"; "CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/en-GB.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en-GB.lproj/InfoPlist.strings index 4a7411be..84e74e8a 100644 --- a/code/apps/Managed Software Center/Managed Software Center/en-GB.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/en-GB.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Centre"; "CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/en.lproj/Info.plist b/code/apps/Managed Software Center/Managed Software Center/en.lproj/Info.plist index 415c3da7..892eeab7 100755 --- a/code/apps/Managed Software Center/Managed Software Center/en.lproj/Info.plist +++ b/code/apps/Managed Software Center/Managed Software Center/en.lproj/Info.plist @@ -23,7 +23,7 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. + Copyright © 2019-2020 The Munki Project. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/code/apps/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings index 1e9d483e..aa4a2874 100755 --- a/code/apps/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings @@ -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"; diff --git a/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings index 0fafd7da..f938d6ed 100755 --- a/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings @@ -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"; diff --git a/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings index 8a1ea041..eb02dda4 100755 --- a/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Center"; "CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings index 581ce409..bff80d0e 100755 --- a/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings @@ -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"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/iconutils.swift b/code/apps/Managed Software Center/Managed Software Center/iconutils.swift index 878b7d94..aa6802b0 100755 --- a/code/apps/Managed Software Center/Managed Software Center/iconutils.swift +++ b/code/apps/Managed Software Center/Managed Software Center/iconutils.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings index e2dc43ac..21ff56ee 100755 --- a/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Centro Gestione Applicazioni"; "CFBundleDisplayName" = "Centro Gestione Applicazioni"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/mschtml.swift b/code/apps/Managed Software Center/Managed Software Center/mschtml.swift index af71c9a6..143b2538 100755 --- a/code/apps/Managed Software Center/Managed Software Center/mschtml.swift +++ b/code/apps/Managed Software Center/Managed Software Center/mschtml.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/msclib.swift b/code/apps/Managed Software Center/Managed Software Center/msclib.swift index 2f9edb93..2ed9f2e2 100755 --- a/code/apps/Managed Software Center/Managed Software Center/msclib.swift +++ b/code/apps/Managed Software Center/Managed Software Center/msclib.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/msclog.swift b/code/apps/Managed Software Center/Managed Software Center/msclog.swift old mode 100755 new mode 100644 index 63f6e236..e26792ac --- a/code/apps/Managed Software Center/Managed Software Center/msclog.swift +++ b/code/apps/Managed Software Center/Managed Software Center/msclog.swift @@ -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(&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) } diff --git a/code/apps/Managed Software Center/Managed Software Center/munki.swift b/code/apps/Managed Software Center/Managed Software Center/munki.swift index c66ab22b..f902cd1e 100755 --- a/code/apps/Managed Software Center/Managed Software Center/munki.swift +++ b/code/apps/Managed Software Center/Managed Software Center/munki.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings index 8a1ea041..eb02dda4 100755 --- a/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Center"; "CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings index 8a1ea041..eb02dda4 100755 --- a/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Center"; "CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/Managed Software Center/Managed Software Center/passwdutil.swift b/code/apps/Managed Software Center/Managed Software Center/passwdutil.swift index 9170c072..8706663e 100755 --- a/code/apps/Managed Software Center/Managed Software Center/passwdutil.swift +++ b/code/apps/Managed Software Center/Managed Software Center/passwdutil.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/power.swift b/code/apps/Managed Software Center/Managed Software Center/power.swift index 31d570e5..3b3fed72 100755 --- a/code/apps/Managed Software Center/Managed Software Center/power.swift +++ b/code/apps/Managed Software Center/Managed Software Center/power.swift @@ -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 diff --git a/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings index 9fa0a5c0..2508a654 100755 --- a/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings @@ -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"; diff --git a/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings b/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings index 8a1ea041..eb02dda4 100755 --- a/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings +++ b/code/apps/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "Managed Software Center"; "CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj b/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj index 8c750a55..acb39ef2 100644 --- a/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj +++ b/code/apps/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj @@ -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 = ""; }; C0544D4D20AF0EDA00DC86F6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C0544D5020AF0EDA00DC86F6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + C0787F7A2315C6210054D130 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; C0A7937D20AFAE0300F56DD5 /* MunkiStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MunkiStatusViewController.swift; sourceTree = ""; }; C0A7937F20B0EA7800F56DD5 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; /* 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"; diff --git a/code/apps/MunkiStatus/MunkiStatus/AppDelegate.swift b/code/apps/MunkiStatus/MunkiStatus/AppDelegate.swift index 37063d0f..d30bbc34 100644 --- a/code/apps/MunkiStatus/MunkiStatus/AppDelegate.swift +++ b/code/apps/MunkiStatus/MunkiStatus/AppDelegate.swift @@ -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! diff --git a/code/apps/MunkiStatus/MunkiStatus/Base.lproj/MainMenu.xib b/code/apps/MunkiStatus/MunkiStatus/Base.lproj/MainMenu.xib index dc4c4067..908a13d6 100644 --- a/code/apps/MunkiStatus/MunkiStatus/Base.lproj/MainMenu.xib +++ b/code/apps/MunkiStatus/MunkiStatus/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -73,7 +73,8 @@ - + + @@ -165,7 +166,7 @@ - + @@ -218,7 +219,7 @@ - + @@ -233,7 +234,7 @@ - + @@ -271,11 +272,11 @@ - + - + diff --git a/code/apps/MunkiStatus/MunkiStatus/Info.plist b/code/apps/MunkiStatus/MunkiStatus/Info.plist index dec8de44..2348752b 100644 --- a/code/apps/MunkiStatus/MunkiStatus/Info.plist +++ b/code/apps/MunkiStatus/MunkiStatus/Info.plist @@ -31,7 +31,7 @@ LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. + Copyright © 2019-2020 The Munki Project. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/code/apps/MunkiStatus/MunkiStatus/Localization.swift b/code/apps/MunkiStatus/MunkiStatus/Localization.swift index f4cbda75..5d9be0ec 100644 --- a/code/apps/MunkiStatus/MunkiStatus/Localization.swift +++ b/code/apps/MunkiStatus/MunkiStatus/Localization.swift @@ -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 diff --git a/code/apps/MunkiStatus/MunkiStatus/LogViewController.swift b/code/apps/MunkiStatus/MunkiStatus/LogViewController.swift index 212fddac..124e6d08 100644 --- a/code/apps/MunkiStatus/MunkiStatus/LogViewController.swift +++ b/code/apps/MunkiStatus/MunkiStatus/LogViewController.swift @@ -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 diff --git a/code/apps/MunkiStatus/MunkiStatus/MunkiStatusViewController.swift b/code/apps/MunkiStatus/MunkiStatus/MunkiStatusViewController.swift index c7828143..6fdefaf8 100644 --- a/code/apps/MunkiStatus/MunkiStatus/MunkiStatusViewController.swift +++ b/code/apps/MunkiStatus/MunkiStatus/MunkiStatusViewController.swift @@ -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 diff --git a/code/apps/MunkiStatus/MunkiStatus/Utils.swift b/code/apps/MunkiStatus/MunkiStatus/Utils.swift index 2b229d21..1c982abc 100644 --- a/code/apps/MunkiStatus/MunkiStatus/Utils.swift +++ b/code/apps/MunkiStatus/MunkiStatus/Utils.swift @@ -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 diff --git a/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings b/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings index 581ce409..bff80d0e 100644 --- a/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings +++ b/code/apps/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings @@ -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"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings b/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings index 06da609f..64583d86 100644 --- a/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings +++ b/code/apps/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "CFBundleName" = "MunkiStatus"; "CFBundleDisplayName" = "MunkiStatus"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file +NSHumanReadableCopyright = "Copyright © 2010-2020 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/MunkiStatus/MunkiStatus/main.swift b/code/apps/MunkiStatus/MunkiStatus/main.swift new file mode 100644 index 00000000..00d056c2 --- /dev/null +++ b/code/apps/MunkiStatus/MunkiStatus/main.swift @@ -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) diff --git a/code/apps/munki-notifier/munki-notifier.xcodeproj/project.pbxproj b/code/apps/munki-notifier/munki-notifier.xcodeproj/project.pbxproj index cc75784b..a155ebbe 100644 --- a/code/apps/munki-notifier/munki-notifier.xcodeproj/project.pbxproj +++ b/code/apps/munki-notifier/munki-notifier.xcodeproj/project.pbxproj @@ -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 */ diff --git a/code/apps/munki-notifier/munki-notifier/AppDelegate.h b/code/apps/munki-notifier/munki-notifier/AppDelegate.h index 4e4ee9a3..bc2ca086 100644 --- a/code/apps/munki-notifier/munki-notifier/AppDelegate.h +++ b/code/apps/munki-notifier/munki-notifier/AppDelegate.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. // #import diff --git a/code/apps/munki-notifier/munki-notifier/AppDelegate.m b/code/apps/munki-notifier/munki-notifier/AppDelegate.m index d1987fc8..a123fb46 100644 --- a/code/apps/munki-notifier/munki-notifier/AppDelegate.m +++ b/code/apps/munki-notifier/munki-notifier/AppDelegate.m @@ -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 // diff --git a/code/apps/munki-notifier/munki-notifier/Info.plist b/code/apps/munki-notifier/munki-notifier/Info.plist index 3e30f2e2..2b00886e 100644 --- a/code/apps/munki-notifier/munki-notifier/Info.plist +++ b/code/apps/munki-notifier/munki-notifier/Info.plist @@ -27,7 +27,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. + Copyright © 2019-2020 The Munki Project. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/code/apps/munki-notifier/munki-notifier/da.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/da.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/da.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/da.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/de.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/de.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/de.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/de.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/en.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/en.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/en.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/en.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/es.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/es.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/es.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/es.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/fi.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/fi.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/fi.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/fi.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/fr.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/fr.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/fr.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/fr.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/it.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/it.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/it.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/it.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/ja.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/ja.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/ja.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/ja.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/main.m b/code/apps/munki-notifier/munki-notifier/main.m index a21b4702..e0035617 100644 --- a/code/apps/munki-notifier/munki-notifier/main.m +++ b/code/apps/munki-notifier/munki-notifier/main.m @@ -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 diff --git a/code/apps/munki-notifier/munki-notifier/nb.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/nb.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/nb.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/nb.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/nl.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/nl.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/nl.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/nl.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/ru.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/ru.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/ru.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/ru.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/munki-notifier/munki-notifier/sv.lproj/InfoPlist.strings b/code/apps/munki-notifier/munki-notifier/sv.lproj/InfoPlist.strings index 49f191ba..941bc21e 100644 --- a/code/apps/munki-notifier/munki-notifier/sv.lproj/InfoPlist.strings +++ b/code/apps/munki-notifier/munki-notifier/sv.lproj/InfoPlist.strings @@ -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."; diff --git a/code/apps/pyobjc/.gitignore b/code/apps/pyobjc/.gitignore deleted file mode 100644 index ce1cc012..00000000 --- a/code/apps/pyobjc/.gitignore +++ /dev/null @@ -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 diff --git a/code/apps/pyobjc/Managed Software Center/.gitignore b/code/apps/pyobjc/Managed Software Center/.gitignore deleted file mode 100644 index 447a2713..00000000 --- a/code/apps/pyobjc/Managed Software Center/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# .DS_Store files! -.DS_Store - -# don't track .pyc files -*.pyc - -# Xcode 5 user data -*.xcodeproj/project.xcworkspace/ -*.xcodeproj/xcuserdata/ - diff --git a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/Info.plist b/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/Info.plist deleted file mode 100644 index 2eece796..00000000 --- a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - NSHumanReadableCopyright - Copyright © 2019 The Munki Project. All rights reserved. - NSPrincipalClass - MSCDockTilePlugin - - diff --git a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h b/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h deleted file mode 100644 index f046933a..00000000 --- a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.h +++ /dev/null @@ -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 - -@interface MSCDockTilePlugIn : NSObject { - id updateObserver; -} - -@property(retain) id updateObserver; -@end diff --git a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m b/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m deleted file mode 100644 index 8f8041f6..00000000 --- a/code/apps/pyobjc/Managed Software Center/MSCDockTilePlugin/MSCDockTilePlugIn.m +++ /dev/null @@ -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 diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj b/code/apps/pyobjc/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj deleted file mode 100644 index 2ac9d18b..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center.xcodeproj/project.pbxproj +++ /dev/null @@ -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 = ""; }; - C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = FoundationPlist.py; sourceTree = ""; }; - C00F67561F016C9F00D9007D /* CocoaWrapper.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = CocoaWrapper.py; sourceTree = ""; }; - 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 = ""; }; - C01E26921B4DADE4005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; - C01E26931B4DADEB005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; - C01E26941B4DADF3005ACFFB /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/MainMenu.xib; sourceTree = ""; }; - C01FBD301EA64CD600AE97EE /* passwdutil.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = passwdutil.py; sourceTree = ""; }; - C02C98731911B55600425167 /* Managed Software Center-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Managed Software Center-Info.plist"; sourceTree = ""; }; - C02C98751911B63E00425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/MainMenu.xib; sourceTree = ""; }; - C02C98761911B63E00425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C98771911B64A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = ""; }; - C02C98781911B64A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C98791911B65600425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/MainMenu.xib; sourceTree = ""; }; - C02C987A1911B65600425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C987B1911B66400425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/MainMenu.xib; sourceTree = ""; }; - C02C987C1911B66400425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C987D1911B67600425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fi; path = fi.lproj/MainMenu.xib; sourceTree = ""; }; - C02C987E1911B67600425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C987F1911B67F00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/MainMenu.xib; sourceTree = ""; }; - C02C98801911B67F00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C98811911B68800425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/MainMenu.xib; sourceTree = ""; }; - C02C98821911B68800425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C98831911B69000425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/MainMenu.xib; sourceTree = ""; }; - C02C98841911B69000425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; - C02C98881911B81D00425167 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - C02C988A1911B82900425167 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; - C02C988B1911B82A00425167 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; - C02C988C1911B82C00425167 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; - C02C988D1911B82D00425167 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; - C02C988E1911B82E00425167 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; - C02C988F1911B82E00425167 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; - C02C98901911B83000425167 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; - C02C98911911B83100425167 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; - C042CA141EAD86DE006CC681 /* MSCPasswordAlertController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCPasswordAlertController.py; sourceTree = ""; }; - C0453A201CCEF7B60002D396 /* MSCLogWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCLogWindowController.py; sourceTree = ""; }; - C046261519FFF8C000AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = ""; }; - C046261619FFF8CC00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; - C046261719FFF8DA00AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; - C046261819FFF98900AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/MainMenu.xib; sourceTree = ""; }; - C049C9941AEC77DD00251D45 /* updatesTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = updatesTemplate.png; sourceTree = ""; }; - C05C3CEE188391F200095E65 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = munki.py; sourceTree = ""; }; - C079D9C018BD435200BAD62E /* AlertController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = AlertController.py; sourceTree = ""; }; - C07E957E1913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/MainMenu.xib; sourceTree = ""; }; - C07E957F1913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; - C07E95801913EE4600B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; - 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 = ""; }; - C09004FF16CDD84E00BE34CE /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - C090050116CDD84E00BE34CE /* Managed Software Center-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Managed Software Center-Prefix.pch"; sourceTree = ""; }; - C090050516CDD84E00BE34CE /* main.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = main.py; sourceTree = ""; }; - C090050716CDD84E00BE34CE /* MSCAppDelegate.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = MSCAppDelegate.py; sourceTree = ""; }; - C090050A16CDD84E00BE34CE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; - C094B6CE188F7C7700E06897 /* MSCStatusController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCStatusController.py; sourceTree = ""; }; - C0A71B75188A47C700A6EE82 /* MSCMainWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCMainWindowController.py; sourceTree = ""; }; - C0AAA21B18B801400012663F /* MunkiItems.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MunkiItems.py; sourceTree = ""; }; - C0AAA21D18BAD8710012663F /* msclog.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = msclog.py; sourceTree = ""; }; - C0AAA21F18BC67F90012663F /* mschtml.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = mschtml.py; sourceTree = ""; }; - C0AE8657186D2DF900C87AE7 /* Managed Software Center.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Managed Software Center.icns"; sourceTree = ""; }; - C0AE8659186D32AF00C87AE7 /* MSCBadgedTemplateImage.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSCBadgedTemplateImage.py; sourceTree = ""; }; - C0B3743A187089F300B6204E /* AllItemsTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AllItemsTemplate.png; sourceTree = ""; }; - C0B3743D187089F300B6204E /* toolbarCategoriesTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = toolbarCategoriesTemplate.pdf; sourceTree = ""; }; - C0B3744418708A0300B6204E /* templates */ = {isa = PBXFileReference; lastKnownFileType = folder; path = templates; sourceTree = ""; }; - C0B3744518708A0300B6204E /* WebResources */ = {isa = PBXFileReference; lastKnownFileType = folder; path = WebResources; sourceTree = ""; }; - C0B9E89F19AA2B6800DB7247 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A119AA2B8400DB7247 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A219AA2B8600DB7247 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A319AA2B8900DB7247 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A419AA2B8B00DB7247 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A519AA2B8D00DB7247 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A619AA2B9100DB7247 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A719AA2B9400DB7247 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A819AA2B9700DB7247 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8A919AA2B9A00DB7247 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8AD19AA55DC00DB7247 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8AE19AA55DC00DB7247 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8AF19AA55DC00DB7247 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/MainMenu.strings; sourceTree = ""; }; - C0B9E8B519AF7E9E00DB7247 /* Managed Software Center 10_6.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = "Managed Software Center 10_6.icns"; sourceTree = ""; }; - C0D6D0681EA55B470099C126 /* authrestart.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = authrestart.py; sourceTree = ""; }; - C0E098BB1857A3C80045DEEB /* msclib.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = msclib.py; sourceTree = ""; }; - 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 = ""; }; - C0EF96B81ADDB9B2002C02FF /* MSCDockTilePlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSCDockTilePlugIn.h; sourceTree = ""; }; - C0EF96B91ADDB9B2002C02FF /* MSCDockTilePlugIn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSCDockTilePlugIn.m; sourceTree = ""; }; - C0F1586D187D256200052F9A /* MyStuffTemplate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MyStuffTemplate.png; sourceTree = ""; }; - E62967521993D4A400ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_GB; path = en_GB.lproj/MainMenu.xib; sourceTree = ""; }; - E62967541993D4AF00ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = ""; }; - E62967551993D4B900ADCB43 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/InfoPlist.strings; sourceTree = ""; }; - E665C3B81993D58D00C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = ""; }; - E665C3BA1993D59600C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/InfoPlist.strings; sourceTree = ""; }; - E665C3BB1993D5A600C428C4 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_AU; path = en_AU.lproj/MainMenu.xib; sourceTree = ""; }; - E68EB44F1993C3950003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/InfoPlist.strings; sourceTree = ""; }; - E68EB4501993C3AC0003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_CA; path = en_CA.lproj/MainMenu.xib; sourceTree = ""; }; - E68EB4521993C40C0003D10C /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/Localizable.strings; sourceTree = ""; }; -/* 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 = ""; - }; - C09004EE16CDD84E00BE34CE /* Products */ = { - isa = PBXGroup; - children = ( - C09004ED16CDD84E00BE34CE /* Managed Software Center.app */, - C0EF96B11ADDB90B002C02FF /* MSCDockTilePlugin.docktileplugin */, - ); - name = Products; - sourceTree = ""; - }; - C09004F016CDD84E00BE34CE /* Frameworks */ = { - isa = PBXGroup; - children = ( - C09004F116CDD84E00BE34CE /* Cocoa.framework */, - C09004F516CDD84E00BE34CE /* Other Frameworks */, - ); - name = Frameworks; - sourceTree = ""; - }; - C09004F516CDD84E00BE34CE /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - C09004F616CDD84E00BE34CE /* AppKit.framework */, - C09004F716CDD84E00BE34CE /* CoreData.framework */, - C09004F816CDD84E00BE34CE /* Foundation.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; - 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 = ""; - }; - 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 = ""; - }; - C0B37439187089B900B6204E /* Resources */ = { - isa = PBXGroup; - children = ( - C049C9941AEC77DD00251D45 /* updatesTemplate.png */, - C0F1586D187D256200052F9A /* MyStuffTemplate.png */, - C0B3744418708A0300B6204E /* templates */, - C0B3744518708A0300B6204E /* WebResources */, - C0B3743A187089F300B6204E /* AllItemsTemplate.png */, - C0B3743D187089F300B6204E /* toolbarCategoriesTemplate.pdf */, - ); - name = Resources; - sourceTree = ""; - }; - C0EF96B21ADDB90B002C02FF /* MSCDockTilePlugin */ = { - isa = PBXGroup; - children = ( - C0EF96B81ADDB9B2002C02FF /* MSCDockTilePlugIn.h */, - C0EF96B91ADDB9B2002C02FF /* MSCDockTilePlugIn.m */, - C0EF96B31ADDB90B002C02FF /* Supporting Files */, - ); - path = MSCDockTilePlugin; - sourceTree = ""; - }; - C0EF96B31ADDB90B002C02FF /* Supporting Files */ = { - isa = PBXGroup; - children = ( - C0EF96B41ADDB90B002C02FF /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; -/* 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 = ""; - }; - 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 = ""; - }; - 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 = ""; - }; - 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 = ""; - }; -/* 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 */; -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/AlertController.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/AlertController.py deleted file mode 100644 index 01392694..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/AlertController.py +++ /dev/null @@ -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 diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/AllItemsTemplate.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/AllItemsTemplate.png deleted file mode 100644 index 15444f8b..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/AllItemsTemplate.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/CocoaWrapper.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/CocoaWrapper.py deleted file mode 100644 index 0597e0ab..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/CocoaWrapper.py +++ /dev/null @@ -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, -) diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/FoundationPlist.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/FoundationPlist.py deleted file mode 100644 index 71be1e7f..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/FoundationPlist.py +++ /dev/null @@ -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) - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Localize.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Localize.py deleted file mode 100755 index 12945a48..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Localize.py +++ /dev/null @@ -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.+)=(?P.+)$', 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) diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSC.icns b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSC.icns deleted file mode 100644 index c6f61d81..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSC.icns and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCAppDelegate.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCAppDelegate.py deleted file mode 100644 index c96b5e55..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCAppDelegate.py +++ /dev/null @@ -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 diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.py deleted file mode 100644 index 2648f356..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCBadgedTemplateImage.py +++ /dev/null @@ -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 diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCLogWindowController.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCLogWindowController.py deleted file mode 100644 index 2017ef1a..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCLogWindowController.py +++ /dev/null @@ -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() diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCMainWindowController.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCMainWindowController.py deleted file mode 100644 index d42bdaeb..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCMainWindowController.py +++ /dev/null @@ -1,1290 +0,0 @@ -# encoding: utf-8 -# -# MSCMainWindowController.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. - -import os - -import munki -import mschtml -import msclib -import msclog -import MunkiItems - -from urlparse import urlparse - -from AlertController import AlertController -from MSCBadgedTemplateImage import MSCBadgedTemplateImage - -from objc import YES, NO, IBAction, IBOutlet, nil -from PyObjCTools import AppHelper - -## 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 - -#from WebKit import * -from WebKit import WebView, WebPreferences - -# Disable PyLint complaining about 'invalid' camelCase names -# pylint: disable=C0103 - - -class MSCMainWindowController(NSWindowController): - - _alertedUserToOutstandingUpdates = False - - _update_in_progress = False - managedsoftwareupdate_task = None - _update_queue = set() - - # status vars - _status_title = u'' - stop_requested = False - user_warned_about_extra_updates = False - html_dir = None - alert_context_info = None - cached_self_service = None - alert_controller = None - - # Cocoa UI binding properties - softwareToolbarButton = IBOutlet() - categoriesToolbarButton = IBOutlet() - myItemsToolbarButton = IBOutlet() - updatesToolbarButton = IBOutlet() - webView = IBOutlet() - navigateBackBtn = IBOutlet() - navigateForwardBtn = IBOutlet() - progressSpinner = IBOutlet() - searchField = IBOutlet() - updateButtonCell = IBOutlet() - windowMenuSeperatorItem = IBOutlet() - fullScreenMenuItem = IBOutlet() - findMenuItem = IBOutlet() - softwareMenuItem = IBOutlet() - categoriesMenuItem = IBOutlet() - myItemsMenuItem = IBOutlet() - - def appShouldTerminate(self): - '''called by app delegate - when it receives applicationShouldTerminate:''' - if self.getUpdateCount() == 0: - # no pending updates - return YES - if (self.currentPageIsUpdatesPage() - and not munki.thereAreUpdatesToBeForcedSoon()): - # We're already at the updates view, so user is aware of the - # pending updates, so OK to just terminate - # (unless there are some updates to be forced soon) - return YES - if (self.currentPageIsUpdatesPage() - and self._alertedUserToOutstandingUpdates): - return YES - # we have pending updates and we have not yet warned the user - # about them - self.alertToPendingUpdates() - return NO - - def alertToPendingUpdates(self): - '''Alert user to pending updates before quitting the application''' - self._alertedUserToOutstandingUpdates = True - # show the updates - self.loadUpdatesPage_(self) - if munki.thereAreUpdatesToBeForcedSoon(): - alertTitle = NSLocalizedString(u"Mandatory Updates Pending", - u"Mandatory Updates Pending text") - deadline = munki.earliestForceInstallDate() - time_til_logout = deadline.timeIntervalSinceNow() - if time_til_logout > 0: - deadline_str = munki.stringFromDate(deadline) - formatString = NSLocalizedString( - (u"One or more updates must be installed by %s. A logout " - "may be forced if you wait too long to update."), - u"Mandatory Updates Pending detail") - alertDetail = formatString % deadline_str - else: - alertDetail = NSLocalizedString( - (u"One or more mandatory updates are overdue for " - "installation. A logout will be forced soon."), - u"Mandatory Updates Imminent detail") - else: - alertTitle = NSLocalizedString( - u"Pending updates", u"Pending Updates alert title") - alertDetail = NSLocalizedString( - u"There are pending updates for this computer.", - u"Pending Updates alert detail text") - alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - alertTitle, - NSLocalizedString(u"Quit", u"Quit button title"), - nil, - NSLocalizedString(u"Update now", u"Update Now button title"), - u"%@", alertDetail) - alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( - self.window(), self, - self.updateAlertDidEnd_returnCode_contextInfo_, nil) - - @AppHelper.endSheetMethod - def updateAlertDidEnd_returnCode_contextInfo_( - self, alert, returncode, contextinfo): - '''Called when alert invoked by alertToPendingUpdates ends''' - if returncode == NSAlertDefaultReturn: - msclog.log("user", "quit") - NSApp.terminate_(self) - elif returncode == NSAlertOtherReturn: - msclog.log("user", "install_now_clicked") - # make sure this alert panel is gone before we proceed - # which might involve opening another alert sheet - alert.window().orderOut_(self) - # initiate the updates - self.updateNow() - self.loadUpdatesPage_(self) - - def loadInitialView(self): - '''Called by app delegate from applicationDidFinishLaunching:''' - self.enableOrDisableSoftwareViewControls() - optional_items = MunkiItems.getOptionalInstallItems() - if not optional_items or self.getUpdateCount() or MunkiItems.getProblemItems(): - self.loadUpdatesPage_(self) - else: - self.loadAllSoftwarePage_(self) - self.displayUpdateCount() - self.cached_self_service = MunkiItems.SelfService() - - def highlightToolbarButtons_(self, nameToHighlight): - '''Highlight/dim buttons in our toolbar''' - self.softwareToolbarButton.setState_(nameToHighlight == "Software") - self.categoriesToolbarButton.setState_(nameToHighlight == "Categories") - self.myItemsToolbarButton.setState_(nameToHighlight == "My Items") - self.updatesToolbarButton.setState_(nameToHighlight == "Updates") - - def enableOrDisableToolbarButtons_(self, enabled_state): - '''Enable or disable buttons in our toolbar''' - if self.window().isMainWindow() == NO: - enabled_state = NO - updates_button_state = NO - else: - updates_button_state = YES - self.softwareToolbarButton.setEnabled_(enabled_state) - self.categoriesToolbarButton.setEnabled_(enabled_state) - self.myItemsToolbarButton.setEnabled_(enabled_state) - self.updatesToolbarButton.setEnabled_(updates_button_state) - - def enableOrDisableSoftwareViewControls(self): - '''Disable or enable the controls that let us view optional items''' - optional_items = MunkiItems.getOptionalInstallItems() - enabled_state = (len(optional_items) > 0) - self.enableOrDisableToolbarButtons_(enabled_state) - self.searchField.setEnabled_(enabled_state) - self.findMenuItem.setEnabled_(enabled_state) - self.softwareMenuItem.setEnabled_(enabled_state) - self.softwareMenuItem.setEnabled_(enabled_state) - self.categoriesMenuItem.setEnabled_(enabled_state) - self.myItemsMenuItem.setEnabled_(enabled_state) - - def munkiStatusSessionEndedWithStatus_errorMessage_(self, sessionResult, errmsg): - '''Called by StatusController when a Munki session ends''' - msclog.debug_log(u"MunkiStatus session ended: %s" % sessionResult) - msclog.debug_log( - u"MunkiStatus session type: %s" % self.managedsoftwareupdate_task) - tasktype = self.managedsoftwareupdate_task - self.managedsoftwareupdate_task = None - self._update_in_progress = False - - # The managedsoftwareupdate run will have changed state preferences - # in ManagedInstalls.plist. Load the new values. - munki.reload_prefs() - lastCheckResult = munki.pref("LastCheckResult") - if sessionResult != 0 or lastCheckResult < 0: - OKButtonTitle = NSLocalizedString(u"OK", u"OK button title") - alertMessageText = NSLocalizedString( - u"Update check failed", u"Update Check Failed title") - detailText = u"" - if tasktype == "installwithnologout": - msclog.log("MSC", "cant_update", "Install session failed") - alertMessageText = NSLocalizedString( - u"Install session failed", u"Install Session Failed title") - - if sessionResult == -1: - # connection was dropped unexpectedly - msclog.log("MSC", "cant_update", "unexpected process end") - detailText = NSLocalizedString( - (u"There is a configuration problem with the managed " - "software installer. The process ended unexpectedly. " - "Contact your systems administrator."), - u"Unexpected Session End message") - elif sessionResult == -2: - # session never started - msclog.log("MSC", "cant_update", "process did not start") - 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") - elif lastCheckResult == -1: - # server not reachable - msclog.log("MSC", "cant_update", "cannot contact server") - detailText = NSLocalizedString( - (u"Managed Software Center cannot contact the update " - "server at this time.\n" - "Try again later. If this situation continues, " - "contact your systems administrator."), - u"Cannot Contact Server detail") - elif lastCheckResult == -2: - # preflight failed - msclog.log("MSU", "cant_update", "failed preflight") - detailText = NSLocalizedString( - (u"Managed Software Center cannot check for updates now.\n" - "Try again later. If this situation continues, " - "contact your systems administrator."), - u"Failed Preflight Check detail") - if errmsg: - detailText += u"\n\n" + unicode(errmsg) - # show the alert sheet - self.window().makeKeyAndOrderFront_(self) - alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - alertMessageText, OKButtonTitle, nil, nil, u"%@", detailText) - alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( - self.window(), self, - self.munkiSessionErrorAlertDidEnd_returnCode_contextInfo_, nil) - return - - if tasktype == 'checktheninstall': - MunkiItems.reset() - # possibly check again if choices have changed - self.updateNow() - return - - # all done checking and/or installing: display results - self.resetAndReload() - - if MunkiItems.updateCheckNeeded(): - # more stuff pending? Let's do it... - self.updateNow() - - @AppHelper.endSheetMethod - def munkiSessionErrorAlertDidEnd_returnCode_contextInfo_( - self, alert, returncode, contextinfo): - '''Called when alert raised by munkiStatusSessionEnded ends''' - self.resetAndReload() - - def resetAndReload(self): - '''Clear cached values, reload from disk. Display any changes. - Typically called soon after a Munki session completes''' - msclog.debug_log('resetAndReload method called') - # need to clear out cached data - MunkiItems.reset() - # recache SelfService choices - self.cached_self_service = MunkiItems.SelfService() - # copy any new custom client resources - msclib.get_custom_resources() - # pending updates may have changed - self._alertedUserToOutstandingUpdates = False - # enable/disable controls as needed - self.enableOrDisableSoftwareViewControls() - # what page are we currently viewing? - page_url = self.webView.mainFrameURL() - filename = NSURL.URLWithString_(page_url).lastPathComponent() - name = os.path.splitext(filename)[0] - key = name.partition('-')[0] - if key == 'detail': - # optional item detail page - self.webView.reload_(self) - if key in ['category', 'filter', 'developer']: - # optional item list page - self.updateListPage() - if key == 'categories': - # categories page - self.updateCategoriesPage() - if key == 'myitems': - # my items page - self.updateMyItemsPage() - if key == 'updates': - # updates page - self.webView.reload_(self) - self._alertedUserToOutstandingUpdates = True - if key == 'updatedetail': - # update detail page - self.webView.reload_(self) - # update count might have changed - self.displayUpdateCount() - - def windowShouldClose_(self, sender): - '''NSWindowDelegate method called when user closes a window''' - # closing the main window should be the same as quitting - NSApp.terminate_(self) - return NO - - def windowDidBecomeMain_(self, notification): - '''Our window was activated, make sure controls enabled as needed''' - optional_items = MunkiItems.getOptionalInstallItems() - enabled_state = (len(optional_items) > 0) - self.enableOrDisableToolbarButtons_(enabled_state) - - def windowDidResignMain_(self, notification): - '''Our window was deactivated, make sure controls enabled as needed''' - self.enableOrDisableToolbarButtons_(NO) - - def configureFullScreenMenuItem(self): - '''check to see if NSWindow's toggleFullScreen: selector is implemented. - if so, unhide the menu items for going full screen''' - if self.window().respondsToSelector_('toggleFullScreen:'): - self.windowMenuSeperatorItem.setHidden_(False) - self.fullScreenMenuItem.setHidden_(False) - self.fullScreenMenuItem.setEnabled_(True) - - def awakeFromNib(self): - '''Stuff we need to initialize when we start''' - self.configureFullScreenMenuItem() - self.webView.setDrawsBackground_(NO) - self.webView.setUIDelegate_(self) - self.webView.setFrameLoadDelegate_(self) - self.webView.setResourceLoadDelegate_(self) - self.webView.setPolicyDelegate_(self) - self.setNoPageCache() - self.alert_controller = AlertController.alloc().init() - self.alert_controller.setWindow_(self.window()) - self.html_dir = msclib.html_dir() - self.registerForNotifications() - - def registerForNotifications(self): - '''register for notification messages''' - notification_center = NSDistributedNotificationCenter.defaultCenter() - # register for notification if user switches to/from Dark Mode - notification_center.addObserver_selector_name_object_suspensionBehavior_( - self, - self.interfaceThemeChanged, - 'AppleInterfaceThemeChangedNotification', - None, - NSNotificationSuspensionBehaviorDeliverImmediately) - - # register for notification if available updates change - notification_center.addObserver_selector_name_object_suspensionBehavior_( - self, - self.updateAvailableUpdates, - 'com.googlecode.munki.managedsoftwareupdate.updateschanged', - None, - NSNotificationSuspensionBehaviorDeliverImmediately) - - # register for notification to display a logout warning - # from the logouthelper - notification_center.addObserver_selector_name_object_suspensionBehavior_( - self, - self.forcedLogoutWarning, - 'com.googlecode.munki.ManagedSoftwareUpdate.logoutwarn', - None, - NSNotificationSuspensionBehaviorDeliverImmediately) - - def interfaceThemeChanged(self): - '''Called when user switches to/from Dark Mode''' - interface_style = mschtml.interfaceStyle() - scriptObject = self.webView.windowScriptObject() - args = [interface_style] - # call JavaScript in the webview to update the appearance CSS - scriptObject.callWebScriptMethod_withArguments_("changeAppearanceModeTo", args) - - def updateAvailableUpdates(self): - '''If a Munki session is not in progress (that we know of) and - we get a updateschanged notification, resetAndReload''' - msclog.debug_log(u"Managed Software Center got update notification") - if not self._update_in_progress: - self.resetAndReload() - - def forcedLogoutWarning(self, notification_obj): - '''Received a logout warning from the logouthelper for an - upcoming forced install''' - msclog.debug_log(u"Managed Software Center got forced logout warning") - # got a notification of an upcoming forced install - # switch to updates view, then display alert - self.loadUpdatesPage_(self) - self.alert_controller.forcedLogoutWarning(notification_obj) - - def checkForUpdates(self, suppress_apple_update_check=False): - '''start an update check session''' - # attempt to start the update check - if self._update_in_progress: - return - try: - munki.startUpdateCheck(suppress_apple_update_check) - except munki.ProcessStartError, err: - self.munkiStatusSessionEndedWithStatus_errorMessage_(-2, unicode(err)) - return - - self._update_in_progress = True - self.displayUpdateCount() - self.managedsoftwareupdate_task = "manualcheck" - NSApp.delegate().statusController.startMunkiStatusSession() - self.markRequestedItemsAsProcessing() - - - @IBAction - def reloadPage_(self, sender): - '''User selected Reload page menu item. Reload the page and kick off an updatecheck''' - msclog.log('user', 'reload_page_menu_item_selected') - self.checkForUpdates() - self.webView.reload_(sender) - - def kickOffInstallSession(self): - '''start an update install/removal session''' - # check for need to logout, restart, firmware warnings - # warn about blocking applications, etc... - # then start an update session - if (MunkiItems.updatesRequireRestart() - or MunkiItems.updatesRequireLogout()): - # switch to updates view - self.loadUpdatesPage_(self) - # warn about need to logout or restart - self.alert_controller.confirmUpdatesAndInstall() - else: - if self.alert_controller.alertedToBlockingAppsRunning(): - self.loadUpdatesPage_(self) - return - if self.alert_controller.alertedToRunningOnBatteryAndCancelled(): - self.loadUpdatesPage_(self) - return - self.managedsoftwareupdate_task = None - msclog.log("user", "install_without_logout") - self._update_in_progress = True - self.displayUpdateCount() - self.setStatusViewTitle_( - NSLocalizedString(u"Updating...", u"Updating message")) - try: - munki.justUpdate() - except munki.ProcessStartError, err: - msclog.debug_log("Error starting install session: %s" % err) - self.munkiStatusSessionEndedWithStatus_errorMessage_(-2, err) - else: - self.managedsoftwareupdate_task = "installwithnologout" - NSApp.delegate().statusController.startMunkiStatusSession() - self.markPendingItemsAsInstalling() - - def markPendingItemsAsInstalling(self): - '''While an install/removal session is happening, mark optional items - that are being installed/removed with the appropriate status''' - msclog.debug_log('marking pendingItems as installing') - install_info = munki.getInstallInfo() - items_to_be_installed_names = [ - item['name'] for item in install_info.get('managed_installs', [])] - items_to_be_removed_names = [ - item['name'] for item in install_info.get('removals', [])] - - for name in items_to_be_installed_names: - # remove names for user selections since we are installing - MunkiItems.user_install_selections.discard(name) - - for name in items_to_be_removed_names: - # remove names for user selections since we are removing - MunkiItems.user_removal_selections.discard(name) - - for item in MunkiItems.getOptionalInstallItems(): - new_status = None - if item['name'] in items_to_be_installed_names: - msclog.debug_log( - 'Setting status for %s to "installing"' % item['name']) - new_status = u'installing' - elif item['name'] in items_to_be_removed_names: - msclog.debug_log( - 'Setting status for %s to "removing"' % item['name']) - new_status = u'removing' - if new_status: - item['status'] = new_status - self.updateDOMforOptionalItem(item) - - def markRequestedItemsAsProcessing(self): - '''When an update check session is happening, mark optional items - that have been requested as processing''' - msclog.debug_log('marking requested items as processing') - for item in MunkiItems.getOptionalInstallItems(): - new_status = None - if item['status'] == 'install-requested': - msclog.debug_log( - 'Setting status for %s to "downloading"' % item['name']) - new_status = u'downloading' - elif item['status'] == 'removal-requested': - msclog.debug_log( - 'Setting status for %s to "preparing-removal"' - % item['name']) - new_status = u'preparing-removal' - if new_status: - item['status'] = new_status - self.updateDOMforOptionalItem(item) - - def updateNow(self): - '''If user has added to/removed from the list of things to be updated, - run a check session. If there are no more changes, proceed to an update - installation session if items to be installed/removed are exclusively - those selected by the user in this session''' - if self.stop_requested: - # reset the flag - self.stop_requested = False - self.resetAndReload() - return - if MunkiItems.updateCheckNeeded(): - # any item status changes that require an update check? - msclog.debug_log('updateCheck needed') - msclog.log("user", "check_then_install_without_logout") - # since we are just checking for changed self-service items - # we can suppress the Apple update check - suppress_apple_update_check = True - self._update_in_progress = True - self.displayUpdateCount() - try: - munki.startUpdateCheck(suppress_apple_update_check) - except munki.ProcessStartError, err: - msclog.debug_log( - "Error starting check-then-install session: %s" % err) - self.munkiStatusSessionEndedWithStatus_errorMessage_(-2, err) - else: - self.managedsoftwareupdate_task = "checktheninstall" - NSApp.delegate().statusController.startMunkiStatusSession() - self.markRequestedItemsAsProcessing() - elif (not self._alertedUserToOutstandingUpdates - and MunkiItems.updatesContainNonUserSelectedItems()): - # current list of updates contains some not explicitly chosen by - # the user - msclog.debug_log( - 'updateCheck not needed, items require user approval') - self._update_in_progress = False - self.displayUpdateCount() - self.loadUpdatesPage_(self) - self.alert_controller.alertToExtraUpdates() - else: - msclog.debug_log('updateCheck not needed') - self._alertedUserToOutstandingUpdates = False - self.kickOffInstallSession() - - def getUpdateCount(self): - '''Get the count of effective updates''' - if self._update_in_progress: - return 0 - return len(MunkiItems.getEffectiveUpdateList()) - - def displayUpdateCount(self): - '''Display the update count as a badge in the window toolbar - and as an icon badge in the Dock''' - updateCount = self.getUpdateCount() - btn_image = MSCBadgedTemplateImage.imageNamed_withCount_( - 'updatesTemplate.pdf', updateCount) - self.updateButtonCell.setImage_(btn_image) - if updateCount not in [u'★', 0]: - NSApp.dockTile().setBadgeLabel_(str(updateCount)) - else: - NSApp.dockTile().setBadgeLabel_(None) - - def updateMyItemsPage(self): - '''Update the "My Items" page with current data. - Modifies the DOM to avoid ugly browser refresh''' - myitems_rows = mschtml.build_myitems_rows() - document = self.webView.mainFrameDocument() - table_body_element = document.getElementById_('my_items_rows') - table_body_element.setInnerHTML_(myitems_rows) - - def updateCategoriesPage(self): - '''Update the Categories page with current data. - Modifies the DOM to avoid ugly browser refresh''' - items_html = mschtml.build_category_items_html() - document = self.webView.mainFrameDocument() - items_div_element = document.getElementById_('optional_installs_items') - items_div_element.setInnerHTML_(items_html) - - def updateListPage(self): - '''Update the optional items list page with current data. - Modifies the DOM to avoid ugly browser refresh''' - page_url = self.webView.mainFrameURL() - filename = NSURL.URLWithString_(page_url).lastPathComponent() - name = os.path.splitext(filename)[0] - key, _, value = name.partition('-') - category = None - our_filter = None - developer = None - if key == 'category': - if value != 'all': - category = value - elif key == 'filter': - our_filter = value - elif key == 'developer': - developer = value - else: - msclog.debug_log( - 'updateListPage unexpected error: _current_page_filename is %s' - % filename) - return - msclog.debug_log( - 'updating software list page with category: ' - '%s, developer; %s, filter: %s' % (category, developer, our_filter)) - items_html = mschtml.build_list_page_items_html( - category=category, developer=developer, filter=our_filter) - document = self.webView.mainFrameDocument() - items_div_element = document.getElementById_('optional_installs_items') - items_div_element.setInnerHTML_(items_html) - - def load_page(self, url_fragment): - '''Tells the WebView to load the appropriate page''' - msclog.debug_log('load_page request for %s' % url_fragment) - html_file = os.path.join(self.html_dir, url_fragment) - request = NSURLRequest.requestWithURL_cachePolicy_timeoutInterval_( - NSURL.fileURLWithPath_(html_file), - NSURLRequestReloadIgnoringLocalCacheData, 10) - self.webView.mainFrame().loadRequest_(request) - if url_fragment == 'updates.html': - # clear all earlier update notifications - NSUserNotificationCenter.defaultUserNotificationCenter( - ).removeAllDeliveredNotifications() - - def setNoPageCache(self): - '''We disable the back/forward page cache because - we generate each page dynamically; we want things - that are changed in one page view to be reflected - immediately in all page views''' - identifier = u'com.googlecode.munki.ManagedSoftwareCenter' - prefs = WebPreferences.alloc().initWithIdentifier_(identifier) - prefs.setUsesPageCache_(False) - self.webView.setPreferencesIdentifier_(identifier) - -##### WebView delegate methods ##### - - def webView_decidePolicyForNewWindowAction_request_newFrameName_decisionListener_( - self, sender, actionInformation, request, frameName, listener): - '''open link in default browser instead of in our app's WebView''' - listener.ignore() - NSWorkspace.sharedWorkspace().openURL_(request.URL()) - - def webView_decidePolicyForMIMEType_request_frame_decisionListener_( - self, sender, mimetype, request, frame, listener): - '''Decide whether to show or download content''' - if WebView.canShowMIMEType_(mimetype): - listener.use() - else: - # send the request to the user's default browser instead, where it - # can display or download it - listener.ignore() - NSWorkspace.sharedWorkspace().openURL_(request.URL()) - - def webView_resource_willSendRequest_redirectResponse_fromDataSource_( - self, sender, identifier, request, redirectResponse, dataSource): - '''By reacting to this delegate notification, we can build the page - the WebView wants to load''' - msclog.debug_log( - 'webView_resource_willSendRequest_redirectResponse_fromDataSource_') - url = request.URL() - msclog.debug_log('Got URL scheme: %s' % url.scheme()) - if url.scheme() == NSURLFileScheme: - msclog.debug_log(u'Request path is %s' % url.path()) - if self.html_dir in url.path(): - msclog.debug_log(u'request for %s' % url.path()) - filename = unicode(url.lastPathComponent()) - if (filename.endswith(u'.html') - and (filename.startswith(u'detail-') - or filename.startswith(u'category-') - or filename.startswith(u'filter-') - or filename.startswith(u'developer-') - or filename.startswith(u'updatedetail-') - or filename == u'myitems.html' - or filename == u'updates.html' - or filename == u'categories.html')): - try: - mschtml.build_page(filename) - except BaseException, err: - msclog.debug_log(u'Could not build page for %s: %s' - % (filename, err)) - return request - - def webView_didClearWindowObject_forFrame_( - self, sender, windowScriptObject, frame): - '''Configure webView to let JavaScript talk to this object.''' - windowScriptObject.setValue_forKey_(self, 'AppController') - - def webView_didStartProvisionalLoadForFrame_(self, view, frame): - '''Animate progress spinner while we load a page and highlight the - proper toolbar button''' - self.progressSpinner.startAnimation_(self) - main_url = self.webView.mainFrameURL() - parts = urlparse(main_url) - pagename = os.path.basename(parts.path) - msclog.debug_log('Requested pagename is %s' % pagename) - if (pagename == 'category-all.html' - or pagename.startswith('detail-') - or pagename.startswith('filter-') - or pagename.startswith('developer-')): - self.highlightToolbarButtons_("Software") - elif pagename == 'categories.html' or pagename.startswith('category-'): - self.highlightToolbarButtons_("Categories") - elif pagename == 'myitems.html': - self.highlightToolbarButtons_("My Items") - elif pagename == 'updates.html' or pagename.startswith('updatedetail-'): - self.highlightToolbarButtons_("Updates") - else: - # no idea what type of item it is - self.highlightToolbarButtons_(None) - - def webView_didFinishLoadForFrame_(self, view, frame): - '''Stop progress spinner and update state of back/forward buttons''' - self.progressSpinner.stopAnimation_(self) - self.navigateBackBtn.setEnabled_(self.webView.canGoBack()) - self.navigateForwardBtn.setEnabled_(self.webView.canGoForward()) - - def webView_didFailProvisionalLoadWithError_forFrame_( - self, view, error, frame): - '''Stop progress spinner and log''' - self.progressSpinner.stopAnimation_(self) - msclog.debug_log(u'Provisional load error: %s' % error) - files = os.listdir(self.html_dir) - msclog.debug_log('Files in html_dir: %s' % files) - - def webView_didFailLoadWithError_forFrame_(self, view, error, frame): - '''Stop progress spinner and log error''' - #TO-DO: display an error page? - self.progressSpinner.stopAnimation_(self) - msclog.debug_log('Committed load error: %s' % error) - - def isSelectorExcludedFromWebScript_(self, aSelector): - '''Declare which methods can be called from JavaScript''' - # For security, you must explicitly allow a selector to be called - # from JavaScript. - if aSelector in ['openExternalLink:', - 'actionButtonClicked:', - 'myItemsActionButtonClicked:', - 'changeSelectedCategory:', - 'installButtonClicked', - 'updateOptionalInstallButtonClicked:', - 'updateOptionalInstallButtonFinishAction:']: - return NO # this selector is NOT _excluded_ from scripting - return YES # disallow everything else - -#### handling DOM UI elements #### - - def openExternalLink_(self, url): - '''open a link in the default browser''' - NSWorkspace.sharedWorkspace().openURL_(NSURL.URLWithString_(url)) - - def installButtonClicked(self): - '''this method is called from JavaScript when the user - clicks the Install button in the Updates view''' - if self._update_in_progress: - # this is now a stop/cancel button - msclog.log('user', 'cancel_updates') - NSApp.delegate().statusController.disableStopButton() - NSApp.delegate().statusController._status_stopBtnState = 1 - self.stop_requested = True - # send a notification that stop button was clicked - STOP_REQUEST_FLAG = ( - u'/private/tmp/' - 'com.googlecode.munki.managedsoftwareupdate.stop_requested') - if not os.path.exists(STOP_REQUEST_FLAG): - open(STOP_REQUEST_FLAG, 'w').close() - - elif self.getUpdateCount() == 0: - # no updates, this button must say "Check Again" - msclog.log('user', 'refresh_clicked') - self.checkForUpdates() - else: - # must say "Update" - # we're on the Updates page, so users can see all the pending/ - # outstanding updates - self._alertedUserToOutstandingUpdates = True - self.updateNow() - - def showUpdateProgressSpinner(self): - '''This method is currently unused''' - # update the status header on the updates page - document = self.webView.mainFrameDocument() - spinner = document.getElementById_('updates-progress-spinner') - if spinner: - spinner_classes = spinner.className().split(' ') - if 'hidden' in spinner_classes: - spinner_classes.remove('hidden') - spinner.setClassName_(' '.join(spinner_classes)) - update_count_element = document.getElementById_('update-count-string') - if update_count_element: - update_count_element.setInnerText_( - NSLocalizedString(u"Checking for updates...", - u"Checking For Updates message")) - warning_text_element = document.getElementById_('update-warning-text') - if warning_text_element: - warning_text_element.setInnerHTML_('') - install_all_button = document.getElementById_('install-all-button-text') - if install_all_button: - install_all_button.setInnerText_( - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text")) - - container_div = document.getElementById_('os-and-app-updates') - if container_div: - container_div_classes = container_div.className().split(' ') - if not 'updating' in container_div_classes: - container_div_classes.append('updating') - container_div.setClassName_(' '.join(container_div_classes)) - - def updateOptionalInstallButtonClicked_(self, item_name): - '''this method is called from JavaScript when a user clicks - the cancel or add button in the updates list''' - item = MunkiItems.optionalItemForName_(item_name) - if not item: - msclog.debug_log( - 'Unexpected error: Can\'t find item for %s' % item_name) - return - if (item['status'] == 'update-available' - and item.get('preupgrade_alert')): - self.displayPreInstallUninstallAlert_Action_Item_( - item['preupgrade_alert'], - self.updateOptionalInstallButtonBeginAction_, item_name) - else: - self.updateOptionalInstallButtonBeginAction_(item_name) - - def updateOptionalInstallButtonBeginAction_(self, item_name): - scriptObject = self.webView.windowScriptObject() - args = [item_name] - scriptObject.callWebScriptMethod_withArguments_( - 'fadeOutAndRemove', args) - - def update_status_for_item(self, item): - '''Attempts to update an item's status; displays an error dialog - if SelfServeManifest is not writable. - Returns a boolean to indicate success''' - try: - item.update_status() - return True - except MunkiItems.SelfServiceError, err: - msclog.debug_log(str(err)) - alertTitle = NSLocalizedString( - u"System configuration problem", - u"System configuration problem alert title") - alertDetail = NSLocalizedString( - u"A systems configuration issue is preventing Managed Software " - "Center from operating correctly. The reported issue is: ", - u"System configuration problem alert detail") - alertDetail = alertDetail + "\n" + str(err) - alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - alertTitle, - NSLocalizedString(u"OK", u"OK button title"), - nil, - nil, - u"%@", alertDetail) - result = alert.runModal() - return False - - def updateOptionalInstallButtonFinishAction_(self, item_name): - '''Perform the required action when a user clicks - the cancel or add button in the updates list''' - # TO-DO: better handling of all the possible "unexpected error"s - document = self.webView.mainFrameDocument() - item = MunkiItems.optionalItemForName_(item_name) - if not item: - msclog.debug_log( - 'Unexpected error: Can\'t find item for %s' % item_name) - return - update_table_row = document.getElementById_('%s_update_table_row' - % item_name) - if not update_table_row: - msclog.debug_log( - 'Unexpected error: Can\'t find table row for %s' % item_name) - return - # remove this row from its current table - update_table_row.parentNode().removeChild_(update_table_row) - - previous_status = item['status'] - # update item status - if not self.update_status_for_item(item): - # there was a problem, can't continue - return - - msclog.log('user', 'optional_install_' + item['status'], item_name) - - # do we need to add a new node to the other list? - if item.get('needs_update'): - # make some new HTML for the updated item - managed_update_names = MunkiItems.getInstallInfo().get( - 'managed_updates', []) - item_template = mschtml.get_template('update_row_template.html') - item_html = item_template.safe_substitute(item) - - if item['status'] in ['install-requested', - 'update-will-be-installed', 'installed']: - # add the node to the updates-to-install table - table = document.getElementById_('updates-to-install-table') - if item['status'] == 'update-available': - # add the node to the other-updates table - table = document.getElementById_('other-updates-table') - if not table: - msclog.debug_log( - 'Unexpected error: could not find other-updates-table') - return - # this isn't the greatest way to add something to the DOM - # but it works... - table.setInnerHTML_(table.innerHTML() + item_html) - - # might need to toggle visibility of other updates div - other_updates_div = document.getElementById_('other-updates') - other_updates_div_classes = other_updates_div.className().split(' ') - other_updates_table = document.getElementById_('other-updates-table') - if other_updates_table.innerHTML().strip(): - if 'hidden' in other_updates_div_classes: - other_updates_div_classes.remove('hidden') - other_updates_div.setClassName_( - ' '.join(other_updates_div_classes)) - else: - if not 'hidden' in other_updates_div_classes: - other_updates_div_classes.append('hidden') - other_updates_div.setClassName_( - ' '.join(other_updates_div_classes)) - - # update the updates-to-install header to reflect the new list of - # updates to install - updateCount = self.getUpdateCount() - update_count_message = msclib.updateCountMessage(updateCount) - update_count_element = document.getElementById_('update-count-string') - if update_count_element: - update_count_element.setInnerText_(update_count_message) - - warning_text = mschtml.get_warning_text() - warning_text_element = document.getElementById_('update-warning-text') - if warning_text_element: - warning_text_element.setInnerHTML_(warning_text) - - # update text of Install All button - install_all_button_element = document.getElementById_( - 'install-all-button-text') - if install_all_button_element: - install_all_button_element.setInnerText_( - msclib.getInstallAllButtonTextForCount(updateCount)) - - # update count badges - self.displayUpdateCount() - - if MunkiItems.updateCheckNeeded(): - # check for updates after a short delay so UI changes visually - # complete first - self.performSelector_withObject_afterDelay_( - self.checkForUpdates, True, 1.0) - - def myItemsActionButtonClicked_(self, item_name): - '''this method is called from JavaScript when the user clicks - the Install/Remove/Cancel button in the My Items view''' - item = MunkiItems.optionalItemForName_(item_name) - if not item: - msclog.debug_log( - 'Unexpected error: Can\'t find item for %s' % item_name) - return - if item['status'] == 'installed' and item.get('preuninstall_alert'): - self.displayPreInstallUninstallAlert_Action_Item_( - item['preuninstall_alert'], - self.myItemsActionButtonPerformAction_, item_name) - else: - self.myItemsActionButtonPerformAction_(item_name) - - def myItemsActionButtonPerformAction_(self, item_name): - '''perform action needed when user clicks - the Install/Remove/Cancel button in the My Items view''' - document = self.webView.mainFrameDocument() - item = MunkiItems.optionalItemForName_(item_name) - status_line = document.getElementById_('%s_status_text' % item_name) - btn = document.getElementById_('%s_action_button_text' % item_name) - if not item or not btn or not status_line: - msclog.debug_log( - 'User clicked MyItems action button for %s' % item_name) - msclog.debug_log('Unexpected error finding HTML elements') - return - prior_status = item['status'] - if not self.update_status_for_item(item): - # there was a problem, can't continue - return - - self.displayUpdateCount() - if item['status'] == 'not-installed': - # we removed item from list of things to install - # now remove from display - table_row = document.getElementById_( - '%s_myitems_table_row' % item_name) - if table_row: - table_row.parentNode().removeChild_(table_row) - else: - btn.setInnerText_(item['myitem_action_text']) - status_line.setInnerText_(item['status_text']) - status_line.setClassName_('status %s' % item['status']) - - if item['status'] in ['install-requested', 'removal-requested']: - self._alertedUserToOutstandingUpdates = False - if not self._update_in_progress: - self.updateNow() - elif prior_status in ['will-be-installed', 'update-will-be-installed', - 'will-be-removed']: - # cancelled a pending install or removal; should run an updatecheck - self.checkForUpdates(suppress_apple_update_check=True) - - def updateDOMforOptionalItem(self, item): - '''Update displayed status of an item''' - document = self.webView.mainFrameDocument() - if not document: - return - status_line = document.getElementById_('%s_status_text' % item['name']) - status_text_span = document.getElementById_( - '%s_status_text_span' % item['name']) - btn = document.getElementById_('%s_action_button_text' % item['name']) - if not btn or not status_line: - msclog.debug_log('ERROR in updateDOMforOptionalItem: ' - 'could not find items in DOM') - return - btn_classes = btn.className().split(' ') - # filter out status class - btn_classes = [class_name for class_name in btn_classes - if class_name in ['msc-button-inner', 'large', 'small', - 'install-updates']] - btn_classes.append(item['status']) - btn.setClassName_(' '.join(btn_classes)) - if 'install-updates' in btn_classes: - btn.setInnerText_(item['myitem_action_text']) - elif 'large' in btn_classes: - btn.setInnerText_(item['long_action_text']) - else: - btn.setInnerText_(item['short_action_text']) - if status_text_span: - # use setInnerHTML_ instead of setInnerText_ because sometimes the status - # text contains html, like 'Some warning' - status_text_span.setInnerHTML_(item['status_text']) - status_line.setClassName_(item['status']) - - def actionButtonClicked_(self, item_name): - '''this method is called from JavaScript when the user clicks - the Install/Remove/Cancel button in the list or detail view''' - - item = MunkiItems.optionalItemForName_(item_name) - if not item: - msclog.debug_log( - 'User clicked Install/Remove/Upgrade/Cancel button in the list ' - 'or detail view') - msclog.debug_log('Can\'t find item: %s' % item_name) - return - - showAlert = True - if item['status'] == 'not-installed' and item.get('preinstall_alert'): - self.displayPreInstallUninstallAlert_Action_Item_( - item['preinstall_alert'], - self.actionButtonPerformAction_, item_name) - elif item['status'] == 'installed' and item.get('preuninstall_alert'): - self.displayPreInstallUninstallAlert_Action_Item_( - item['preuninstall_alert'], - self.actionButtonPerformAction_, item_name) - elif (item['status'] == 'update-available' - and item.get('preupgrade_alert')): - self.displayPreInstallUninstallAlert_Action_Item_( - item['preupgrade_alert'], - self.actionButtonPerformAction_, item_name) - else: - self.actionButtonPerformAction_(item_name) - showAlert = False - if showAlert: - msclog.log("user", "show_alert") - - def displayPreInstallUninstallAlert_Action_Item_( - self, alert_dict, action_selector, item_name): - ''' Display an alert sheet before processing item install/upgrade - or uninstall ''' - defaultAlertTitle = NSLocalizedString( - u'Attention', u'Pre Install Uninstall Upgrade Alert Title') - defaultAlertDetail = NSLocalizedString( - u'Some conditions apply to this software. ' - 'Please contact your administrator for more details', - u'Pre Install Uninstall Upgrade Alert Detail') - defaultOKLabel = NSLocalizedString( - u'OK', u'Pre Install Uninstall Upgrade OK Label') - defaultCancelLabel = NSLocalizedString( - u'Cancel', u'Pre Install Uninstall Upgrade Cancel Label') - - alertTitle = alert_dict.get('alert_title') or defaultAlertTitle - alertDetail = alert_dict.get('alert_detail', defaultAlertDetail) - OKLabel = alert_dict.get('ok_label') or defaultOKLabel - cancelLabel = alert_dict.get('cancel_label') or defaultCancelLabel - - self.alert_context_info = {'selector': action_selector, - 'item_name': item_name} - - # show the alert sheet - self.window().makeKeyAndOrderFront_(self) - alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - alertTitle, - cancelLabel, - OKLabel, - nil, - u"%@", alertDetail) - alert.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( - self.window(), - self, - self.actionAlertDidEnd_returnCode_contextInfo_, - nil) - - def actionAlertDidEnd_returnCode_contextInfo_( - self, alert, returncode, contextinfo): - '''Called when alert invoked by actionButtonClicked_ ends''' - alert.window().orderOut_(self) - if returncode == NSAlertDefaultReturn: - msclog.log("user", "alert_canceled") - else: - msclog.log("user", "alert_accepted") - selector = self.alert_context_info.get('selector') - item_name = self.alert_context_info.get('item_name') - if selector and item_name: - selector(item_name) - - def actionButtonPerformAction_(self, item_name): - '''Perform the action requested when clicking the action button - in the list or detail view''' - item = MunkiItems.optionalItemForName_(item_name) - if not item: - msclog.debug_log( - 'User clicked Install/Upgrade/Removal/Cancel button ' - 'in the list or detail view') - msclog.debug_log('Can\'t find item: %s' % item_name) - return - - prior_status = item['status'] - if not self.update_status_for_item(item): - # there was a problem, can't continue - return - msclog.log('user', 'action_button_' + item['status'], item_name) - - self.displayUpdateCount() - self.updateDOMforOptionalItem(item) - - if item['status'] in ['install-requested', 'removal-requested']: - self._alertedUserToOutstandingUpdates = False - if not self._update_in_progress: - self.updateNow() - elif prior_status in ['will-be-installed', 'update-will-be-installed', - 'will-be-removed']: - # cancelled a pending install or removal; should run an updatecheck - self.checkForUpdates(suppress_apple_update_check=True) - - def changeSelectedCategory_(self, category): - '''this method is called from JavaScript when the user - changes the category selected in the sidebar popup''' - all_categories_label = NSLocalizedString( - u"All Categories", u"AllCategoriesLabel") - featured_label = NSLocalizedString(u"Featured", u"FeaturedLabel") - if category in [all_categories_label, featured_label]: - category = u'all' - self.load_page('category-%s.html' % category) - - def setStatusViewTitle_(self, title_text): - '''When displaying status during a managedsoftwareupdate run, this - method is used to display info where the update count message - usually is''' - document = self.webView.mainFrameDocument() - self._status_title = title_text - # we re-purpose the update count message for this - update_count_element = document.getElementById_('update-count-string') - if update_count_element: - update_count_element.setInnerText_(title_text) - -#### some Cocoa UI bindings ##### - - @IBAction - def showHelp_(self, sender): - helpURL = munki.pref('HelpURL') - if helpURL: - NSWorkspace.sharedWorkspace().openURL_( - NSURL.URLWithString_(helpURL)) - else: - alertTitle = NSLocalizedString(u"Help", u"No help alert title") - alertDetail = NSLocalizedString( - u"Help isn't available for Managed Software Center.", - u"No help alert detail") - alert = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - alertTitle, - NSLocalizedString(u"OK", u"OK button title"), - nil, - nil, - u"%@", alertDetail) - result = alert.runModal() - - @IBAction - def navigateBackBtnClicked_(self, sender): - '''Handle WebView back button''' - self.webView.goBack_(self) - - @IBAction - def navigateForwardBtnClicked_(self, sender): - '''Handle WebView forward button''' - self.webView.goForward_(self) - - @IBAction - def loadAllSoftwarePage_(self, sender): - '''Called by Navigate menu item''' - self.load_page('category-all.html') - - @IBAction - def loadCategoriesPage_(self, sender): - '''Called by Navigate menu item''' - self.load_page('categories.html') - - @IBAction - def loadMyItemsPage_(self, sender): - '''Called by Navigate menu item''' - self.load_page('myitems.html') - - @IBAction - def loadUpdatesPage_(self, sender): - '''Called by Navigate menu item''' - self.load_page('updates.html') - self._alertedUserToOutstandingUpdates = True - - @IBAction - def softwareToolbarButtonClicked_(self, sender): - '''User clicked Software toolbar button''' - self.loadAllSoftwarePage_(sender) - - @IBAction - def categoriesToolbarButtonClicked_(self, sender): - '''User clicked Categories toolbar button''' - self.loadCategoriesPage_(sender) - - @IBAction - def myItemsToolbarButtonClicked_(self, sender): - '''User clicked My Items toolbar button''' - self.loadMyItemsPage_(sender) - - @IBAction - def updatesToolbarButtonClicked_(self, sender): - '''User clicked Updates toolbar button''' - self.loadUpdatesPage_(sender) - - @IBAction - def searchFilterChanged_(self, sender): - '''User changed the search field''' - filterString = self.searchField.stringValue().lower() - if filterString: - msclog.debug_log('Search filter is: %s' - % repr(filterString.encode('utf-8'))) - self.load_page(u'filter-%s.html' % filterString) - - def currentPageIsUpdatesPage(self): - '''return True if current tab selected is Updates''' - return self.updatesToolbarButton.state() == NSOnState - - #def currentPageIsMyItemsPage(self): - # '''return True if current tab selected is My Items''' - # return (self.myItemsToolbarButton.state() == NSOnState) - - #def currentPageIsCategoriesPage(self): - # '''return True if current tab selected is Categories''' - # return (self.categoriesToolbarButton.state() == NSOnState) diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCPasswordAlertController.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCPasswordAlertController.py deleted file mode 100644 index 59b49694..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCPasswordAlertController.py +++ /dev/null @@ -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) \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCStatusController.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCStatusController.py deleted file mode 100644 index 429b3c02..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCStatusController.py +++ /dev/null @@ -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") diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCToolbar.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCToolbar.py deleted file mode 100644 index f846524e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MSCToolbar.py +++ /dev/null @@ -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 - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center 10_6.icns b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center 10_6.icns deleted file mode 100644 index ad4c86cc..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center 10_6.icns and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Info.plist b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Info.plist deleted file mode 100644 index c76e71fd..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Info.plist +++ /dev/null @@ -1,62 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - ${EXECUTABLE_NAME} - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 4.8 - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLName - com.googlecode.munki - CFBundleURLSchemes - - munki - - - - CFBundleVersion - 1 - GitRevision - - LSHasLocalizedDisplayName - - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSDockTilePlugIn - MSCDockTilePlugin.docktileplugin - NSHumanReadableCopyright - Copyright © 2019 The Munki Project https://github.com/munki/munki - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - NSRequiresAquaSystemAppearance - - NSUserNotificationAlertStyle - alert - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Prefix.pch b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Prefix.pch deleted file mode 100644 index c4062bec..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center-Prefix.pch +++ /dev/null @@ -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 -#endif diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.icns b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.icns deleted file mode 100644 index c6f61d81..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.icns and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128.png deleted file mode 100644 index 65db3aef..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128@2x.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128@2x.png deleted file mode 100644 index dd08a78b..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_128x128@2x.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16.png deleted file mode 100644 index 66ba2ac5..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16@2x.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16@2x.png deleted file mode 100644 index b0dc549c..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_16x16@2x.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256.png deleted file mode 100644 index dd08a78b..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256@2x.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256@2x.png deleted file mode 100644 index 146940e7..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_256x256@2x.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32.png deleted file mode 100644 index 813fbae8..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32@2x.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32@2x.png deleted file mode 100644 index d33b7849..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_32x32@2x.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512.png deleted file mode 100644 index 146940e7..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512@2x.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512@2x.png deleted file mode 100644 index 7d3b368b..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/Managed Software Center.iconset/icon_512x512@2x.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MunkiItems.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MunkiItems.py deleted file mode 100644 index 3ba3e1e0..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MunkiItems.py +++ /dev/null @@ -1,1267 +0,0 @@ -# encoding: utf-8 -# -# MunkiItems.py -# Managed Software Center -# -# Created by Greg Neagle on 2/21/14. -# -# 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. - -import os -#import sys -import msclib -import munki - -from operator import itemgetter -from HTMLParser import HTMLParser, HTMLParseError - -# pylint: disable=wildcard-import -from CocoaWrapper import * -# pylint: enable=wildcard-import - -# 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 -from Quartz import (CGImageSourceCreateWithURL, CGImageSourceCreateImageAtIndex, - CGImageDestinationCreateWithURL, CGImageDestinationAddImage, - CGImageDestinationFinalize, - CGImageSourceGetCount, CGImageSourceCopyPropertiesAtIndex, - kCGImagePropertyDPIHeight, kCGImagePropertyPixelHeight) -# pylint: enable=no-name-in-module - -import FoundationPlist - -# Disable PyLint complaining about 'invalid' camelCase names -# pylint: disable=C0103 - -user_install_selections = set() -user_removal_selections = set() - -# place to cache our expensive-to-calculate data -_cache = {} - - -def quote(a_string): - '''Replacement for urllib.quote that handles Unicode strings''' - return str( - NSString.stringWithString_( - a_string).stringByAddingPercentEscapesUsingEncoding_( - NSUTF8StringEncoding)) - - -def reset(): - '''clear all our cached values''' - global _cache - _cache = {} - - -def getAppleUpdates(): - if not 'apple_updates' in _cache: - _cache['apple_updates'] = munki.getAppleUpdates() - return _cache['apple_updates'] - - -def getInstallInfo(): - if not 'install_info' in _cache: - _cache['install_info'] = munki.getInstallInfo() - return _cache['install_info'] - - -def getOptionalInstallItems(): - if munki.pref('AppleSoftwareUpdatesOnly'): - return [] - if not 'optional_install_items' in _cache: - _cache['optional_install_items'] = [ - OptionalItem(item) - for item in getInstallInfo().get('optional_installs', [])] - featured_items = getInstallInfo().get('featured_items', []) - for item in _cache['optional_install_items']: - if item['name'] in featured_items: - item['featured'] = True - return _cache['optional_install_items'] - - -def getProblemItems(): - if not 'problem_items' in _cache: - problem_items = getInstallInfo().get('problem_items', []) - for item in problem_items: - item['status'] = 'problem-item' - _cache['problem_items'] = sorted( - [UpdateItem(item) for item in problem_items], - key=itemgetter('due_date_sort', 'restart_sort', - 'developer_sort', 'size_sort')) - return _cache['problem_items'] - - -def updateCheckNeeded(): - '''Returns True if any item in optional installs list has - 'updatecheck_needed' == True''' - return len([item for item in getOptionalInstallItems() - if item.get('updatecheck_needed')]) != 0 - - -def optionalItemForName_(item_name): - for item in getOptionalInstallItems(): - if item['name'] == item_name: - return item - return None - - -def getOptionalWillBeInstalledItems(): - return [item for item in getOptionalInstallItems() - if item['status'] in ['install-requested', 'will-be-installed', - 'update-will-be-installed', 'install-error']] - - -def getOptionalWillBeRemovedItems(): - return [item for item in getOptionalInstallItems() - if item['status'] in ['removal-requested', 'will-be-removed', - 'removal-error']] - - -def getUpdateList(): - if not 'update_list'in _cache: - _cache['update_list'] = _build_update_list() - return _cache['update_list'] - - -def display_name(item_name): - '''Returns a display_name for item_name, or item_name if not found''' - for item in getOptionalInstallItems(): - if item['name'] == item_name: - return item['display_name'] - return item_name - - -def _build_update_list(): - update_items = [] - if not munki.munkiUpdatesContainAppleItems(): - apple_updates = getAppleUpdates() - apple_update_items = apple_updates.get('AppleUpdates', []) - for item in apple_update_items: - item['developer'] = u'Apple' - item['status'] = u'will-be-installed' - update_items.extend(apple_update_items) - - install_info = getInstallInfo() - managed_installs = install_info.get('managed_installs', []) - for item in managed_installs: - item['status'] = u'will-be-installed' - update_items.extend(managed_installs) - - removal_items = install_info.get('removals', []) - for item in removal_items: - item['status'] = u'will-be-removed' - # TO-DO: handle the case where removal detail is suppressed - update_items.extend(removal_items) - -# problem_items = install_info.get('problem_items', []) -# for item in problem_items: -# item['status'] = u'problem-item' -# update_items.extend(problem_items) - - # use our list to make UpdateItems - update_list = [UpdateItem(item) for item in update_items] - # sort it and return it - return sorted(update_list, key=itemgetter( - 'due_date_sort', 'restart_sort', 'developer_sort', 'size_sort')) - - -def updatesRequireLogout(): - '''Return True if any item in the update list requires a logout or if - Munki's InstallRequiresLogout preference is true.''' - if munki.installRequiresLogout(): - return True - return len([item for item in getUpdateList() - if 'Logout' in item.get('RestartAction', '')]) > 0 - - -def updatesRequireRestart(): - '''Return True if any item in the update list requires a restart''' - return len([item for item in getUpdateList() - if 'Restart' in item.get('RestartAction', '')]) > 0 - - -def updatesContainNonUserSelectedItems(): - '''Does the list of updates contain items not selected by the user?''' - if not munki.munkiUpdatesContainAppleItems() and getAppleUpdates(): - # available Apple updates are not user selected - return True - install_info = getInstallInfo() - install_items = install_info.get('managed_installs', []) - removal_items = install_info.get('removals', []) - filtered_installs = [item for item in install_items - if item['name'] not in user_install_selections] - if filtered_installs: - return True - filtered_uninstalls = [item for item in removal_items - if item['name'] not in user_removal_selections] - if filtered_uninstalls: - return True - return False - - -def getEffectiveUpdateList(): - '''Combine the updates Munki has found with any optional choices to - make the effective list of updates''' - # get pending optional items separately since OptionalItems have - # extra details/attributes - optional_installs = getOptionalWillBeInstalledItems() - optional_removals = getOptionalWillBeRemovedItems() - optional_item_names = [item['name'] - for item in optional_installs + optional_removals] - # filter out pending optional items from the list of all pending updates - # so we can add in the items with additional optional detail - mandatory_updates = [item for item in getUpdateList() - if item['name'] not in optional_item_names] - - return mandatory_updates + optional_installs + optional_removals - - -def getMyItemsList(): - '''Returns a list of optional_installs items the user has chosen - to install or to remove''' - self_service_installs = SelfService().installs() - self_service_uninstalls = SelfService().uninstalls() - item_list = [item for item in getOptionalInstallItems() - if item['name'] in self_service_installs] - items_to_remove = [item for item in getOptionalInstallItems() - if item['name'] in self_service_uninstalls - and item.get('installed')] - item_list.extend(items_to_remove) - return item_list - - -def dependentItems(this_name): - '''Returns the names of any selected optional items that require this - optional item''' - if not 'optional_installs_with_dependencies' in _cache: - self_service_installs = SelfService().installs() - optional_installs = getInstallInfo().get('optional_installs', []) - _cache['optional_installs_with_dependencies'] = [ - item for item in optional_installs - if item['name'] in self_service_installs and 'requires' in item] - dependent_items = [] - for item in _cache['optional_installs_with_dependencies']: - if this_name in item['requires']: - dependent_items.append(item['name']) - return dependent_items - - -def convertIconToPNG(app_name, destination_path, desired_size): - '''Converts an application icns file to a png file, choosing the - representation closest to (but >= than if possible) the desired_size. - Returns True if successful, False otherwise''' - # find the application - app_path = os.path.join('/Applications', app_name + '.app') - if not os.path.exists(app_path): - return False - try: - # read the Info.plist - info = FoundationPlist.readPlist( - os.path.join(app_path, 'Contents/Info.plist')) - except FoundationPlist.FoundationPlistException: - info = {} - try: - try: - # look for an icon name in the Info.plist, falling back to the - # appname - icon_filename = info.get('CFBundleIconFile', app_name) - except AttributeError: - icon_filename = app_name - icon_path = os.path.join(app_path, 'Contents/Resources', icon_filename) - if not os.path.splitext(icon_path)[1]: - # no file extension, so add '.icns' - icon_path += u'.icns' - if os.path.exists(icon_path): - # we found an icns file, convert to png - icns_url = NSURL.fileURLWithPath_(icon_path) - png_url = NSURL.fileURLWithPath_(destination_path) - desired_dpi = 72 - - image_source = CGImageSourceCreateWithURL(icns_url, None) - if not image_source: - return False - number_of_images = CGImageSourceGetCount(image_source) - if number_of_images == 0: - return False - - selected_index = 0 - candidate = {} - # iterate through the individual icon sizes to find the "best" one - for index in range(number_of_images): - try: - properties = CGImageSourceCopyPropertiesAtIndex( - image_source, index, None) - dpi = int(properties.get(kCGImagePropertyDPIHeight, 0)) - height = int(properties.get(kCGImagePropertyPixelHeight, 0)) - if (not candidate or - (height < desired_size and - height > candidate['height']) or - (height >= desired_size and - height < candidate['height']) or - (height == candidate['height'] and - dpi == desired_dpi)): - candidate = {'index': index, 'dpi': dpi, - 'height': height} - selected_index = index - except ValueError: - pass - - image = CGImageSourceCreateImageAtIndex( - image_source, selected_index, None) - image_dest = CGImageDestinationCreateWithURL( - png_url, 'public.png', 1, None) - CGImageDestinationAddImage(image_dest, image, None) - return CGImageDestinationFinalize(image_dest) - - except Exception: - return False - - return False - - -class MSCHTMLFilter(HTMLParser): - '''Filters HTML and HTML fragments for use inside description paragraphs''' - def __init__(self): - HTMLParser.__init__(self) - # ignore everything inside one of these tags - self.ignore_elements = ['script', 'style', 'head', 'table', 'form'] - # preserve these tags - self.preserve_tags = ['a', 'b', 'i', 'strong', 'em', 'small', 'sub', - 'sup', 'ins', 'del', 'mark', 'span', 'br', 'img'] - # transform these tags - self.transform_starttags = {'ul': '', - 'ol': '', - 'li': ' • ', - 'h1': '', - 'h2': '', - 'h3': '', - 'h4': '', - 'h5': '', - 'h6': '', - 'p': ''} - self.transform_endtags = {'ul': '', - 'ol': '', - 'li': '', - 'h1': '', - 'h2': '', - 'h3': '', - 'h4': '', - 'h5': '', - 'h6': '', - 'p': ''} - # track the currently-ignored element if any - self.current_ignore_element = None - # track the number of tags we found - self.tag_count = 0 - # track the number of HTML entities we found - self.entity_count = 0 - # store our filtered/transformed html fragment - self.filtered_html = u'' - - def handle_starttag(self, tag, attrs): - self.tag_count += 1 - if not self.current_ignore_element: - if tag in self.ignore_elements: - self.current_ignore_element = tag - elif tag in self.transform_starttags: - self.filtered_html += self.transform_starttags[tag] - elif tag in self.preserve_tags: - self.filtered_html += self.get_starttag_text() - - def handle_endtag(self, tag): - if tag == self.current_ignore_element: - self.current_ignore_element = None - elif not self.current_ignore_element: - if tag in self.transform_endtags: - self.filtered_html += self.transform_endtags[tag] - elif tag in self.preserve_tags: - self.filtered_html += u'%s>' % tag - - def handle_data(self, data): - if not self.current_ignore_element: - self.filtered_html += data - - def handle_entityref(self, name): - self.entity_count += 1 - if not self.current_ignore_element: - # add the entity reference as-is - self.filtered_html += u'&%s;' % name - - def handle_charref(self, name): - self.entity_count += 1 - if not self.current_ignore_element: - # add the char reference as-is - self.filtered_html += u'%s;' % name - - -def filtered_html(text, filter_images=False): - '''Returns filtered HTML for use in description paragraphs - or converts plain text into basic HTML for the same use''' - parser = MSCHTMLFilter() - if filter_images: - parser.preserve_tags.remove('img') - parser.feed(text) - if parser.tag_count or parser.entity_count: - # found at least one HTML tag or HTML entity, so this is probably HTML - return parser.filtered_html - else: - # might be plain text, so we should escape a few entities and - # add for line breaks - text = text.replace('&', '&') - text = text.replace('<', '<') - text = text.replace('>', '>') - return text.replace('\n', '\n') - - -def post_install_request_notification(event, item): - '''Post an NSDistributedNotification to be recorded by the - app_usage_monitor''' - user_info = {'event': event, - 'name': item['name'], - 'version': item.get('version_to_install', '0')} - dnc = NSDistributedNotificationCenter.defaultCenter() - dnc.postNotificationName_object_userInfo_options_( - 'com.googlecode.munki.managedsoftwareupdate.installrequest', - None, - user_info, - NSNotificationDeliverImmediately + NSNotificationPostToAllSessions) - - -class SelfServiceError(Exception): - '''General error class for SelfService exceptions''' - pass - - -class SelfService(object): - '''An object to wrap interactions with the SelfServiceManifest''' - def __init__(self): - self._installs = set( - munki.readSelfServiceManifest().get('managed_installs', [])) - self._uninstalls = set( - munki.readSelfServiceManifest().get('managed_uninstalls', [])) - - def __eq__(self, other): - return (sorted(self._installs) == sorted(other._installs) - and sorted(self._uninstalls) == sorted(other._uninstalls)) - - def __ne__(self, other): - return (sorted(self._installs) != sorted(other._installs) - or sorted(self._uninstalls) != sorted(other._uninstalls)) - - def installs(self): - return list(self._installs) - - def uninstalls(self): - return list(self._uninstalls) - - def subscribe(self, item): - self._installs.add(item['name']) - self._uninstalls.discard(item['name']) - self._save_self_service_choices() - - def unsubscribe(self, item): - self._installs.discard(item['name']) - self._uninstalls.add(item['name']) - self._save_self_service_choices() - - def unmanage(self, item): - self._installs.discard(item['name']) - self._uninstalls.discard(item['name']) - self._save_self_service_choices() - - def _save_self_service_choices(self): - current_choices = {} - current_choices['managed_installs'] = list(self._installs) - current_choices['managed_uninstalls'] = list(self._uninstalls) - if not munki.writeSelfServiceManifest(current_choices): - raise SelfServiceError( - 'Could not save self-service choices to %s' - % munki.WRITEABLE_SELF_SERVICE_MANIFEST_PATH) - -def subscribe(item): - '''Add item to SelfServeManifest's managed_installs. - Also track user selections.''' - SelfService().subscribe(item) - user_install_selections.add(item['name']) - post_install_request_notification('install', item) - - -def unsubscribe(item): - '''Add item to SelfServeManifest's managed_uninstalls. - Also track user selections.''' - SelfService().unsubscribe(item) - user_removal_selections.add(item['name']) - post_install_request_notification('remove', item) - - -def unmanage(item): - '''Remove item from SelfServeManifest. - Also track user selections.''' - SelfService().unmanage(item) - user_install_selections.discard(item['name']) - user_removal_selections.discard(item['name']) - - -def getLocalizedShortNoteForItem(item, is_update=False): - '''Attempt to localize a note. Currently handle only two types.''' - note = item.get('note') - if is_update: - return NSLocalizedString(u"Update available", - u"Update available display text") - if note.startswith('Insufficient disk space to download and install'): - return NSLocalizedString(u"Not enough disk space", - u"Not Enough Disk Space display text") - if note.startswith('Requires macOS version '): - return NSLocalizedString(u"macOS update required", - u"macOS update required text") - # we don't know how to localize this note, return None - return None - - -def getLocalizedLongNoteForItem(item, is_update=False): - '''Attempt to localize a note. Currently handle only two types.''' - note = item.get('note') - if note.startswith('Insufficient disk space to download and install'): - if is_update: - return NSLocalizedString( - u"An older version is currently installed. There is not enough " - "disk space to download and install this update.", - u"Long Not Enough Disk Space For Update display text") - else: - return NSLocalizedString( - u"There is not enough disk space to download and install this " - "item.", - u"Long Not Enough Disk Space display text") - if note.startswith('Requires macOS version '): - if is_update: - base_string = NSLocalizedString( - u"An older version is currently installed. You must upgrade to " - "macOS version %s or higher to be able to install this update.", - u"Long update requires a higher OS version text") - else: - base_string = NSLocalizedString( - u"You must upgrade to macOS version %s to be able to " - "install this item.", - u"Long item requires a higher OS version text") - os_version = item.get('minimum_os_version', 'UNKNOWN') - return base_string % os_version - # we don't know how to localize this note, return None - return None - -class GenericItem(dict): - '''Base class for our types of Munki items''' - - def __init__(self, *arg, **kw): - super(GenericItem, self).__init__(*arg, **kw) - if self.get('localized_strings'): - self.add_localizations() - # now normalize values - if not self.get('display_name'): - self['display_name'] = self['name'] - self['display_name_lower'] = self['display_name'].lower() - if not self.get('developer'): - self['developer'] = self.guess_developer() - if self.get('description'): - try: - self['raw_description'] = filtered_html(self['description']) - except HTMLParseError: - self['raw_description'] = ( - 'Invalid HTML in description for %s' % self['display_name']) - del self['description'] - if not 'raw_description' in self: - self['raw_description'] = u'' - del self['description'] - self['icon'] = self.getIcon() - self['due_date_sort'] = NSDate.distantFuture() - # sort items that need restart highest, then logout, then other - self['restart_action_text'] = u'' - self['restart_sort'] = 2 - if self.get('RestartAction') in ['RequireRestart', 'RecommendRestart']: - self['restart_sort'] = 0 - self['restart_action_text'] = NSLocalizedString( - u"Restart Required", u"Restart Required title") - self['restart_action_text'] += ( - u'') - elif self.get('RestartAction') in ['RequireLogout', 'RecommendLogout']: - self['restart_sort'] = 1 - self['restart_action_text'] = NSLocalizedString( - u"Logout Required", u"Logout Required title") - self['restart_action_text'] += ( - u'') - - # sort bigger installs to the top - if self.get('installed_size'): - self['size_sort'] = -int(self['installed_size']) - self['size'] = munki.humanReadable(self['installed_size']) - elif self.get('installer_item_size'): - self['size_sort'] = -int(self['installer_item_size']) - self['size'] = munki.humanReadable(self['installer_item_size']) - else: - self['size_sort'] = 0 - self['size'] = u'' - - def __getitem__(self, name): - '''Allow access to instance variables and methods via dictionary syntax. - This allows us to use class instances as a data source - for our HTML templates (which want a dictionary-like object)''' - try: - return super(GenericItem, self).__getitem__(name) - except KeyError, err: - try: - attr = getattr(self, name) - except AttributeError: - raise KeyError(err) - if callable(attr): - return attr() - else: - return attr - - def description(self): - return self['raw_description'] - - def description_without_images(self): - return filtered_html(self.description(), filter_images=True) - - def dependency_description(self): - '''Return an html description of items this item depends on''' - description = u'' - prologue = NSLocalizedString( - u"This item is required by:", u"Dependency List prologue text") - if self.get('dependent_items'): - description = u'' + prologue - for item in self['dependent_items']: - description += u' • ' + display_name(item) - description += u'' - return description - - def guess_developer(self): - '''Figure out something to use for the developer name''' - if self.get('apple_item'): - return 'Apple' - if self.get('installer_type', '').startswith('Adobe'): - return 'Adobe' - # now we must dig - if self.get('installs'): - for install_item in self['installs']: - if install_item.get('CFBundleIdentifier'): - parts = install_item['CFBundleIdentifier'].split('.') - if (len(parts) > 1 - and parts[0] in ['com', 'org', 'net', 'edu']): - return parts[1].title() - return '' - - def getIcon(self): - '''Return name/relative path of image file to use for the icon''' - # first look for downloaded icons - icon_known_exts = ['.bmp', '.gif', '.icns', '.jpg', '.jpeg', '.png', - '.psd', '.tga', '.tif', '.tiff', '.yuv'] - icon_name = self.get('icon_name') or self['name'] - if not os.path.splitext(icon_name)[1] in icon_known_exts: - icon_name += '.png' - icon_path = os.path.join(msclib.html_dir(), 'icons', icon_name) - if os.path.exists(icon_path): - return 'icons/' + quote(icon_name) - # didn't find one in the downloaded icons - # so create one if needed from a locally installed app - for key in ['icon_name', 'display_name', 'name']: - if key in self: - name = self[key] - icon_name = name - if not os.path.splitext(icon_name)[1] in icon_known_exts: - icon_name += '.png' - icon_path = os.path.join(msclib.html_dir(), icon_name) - if (os.path.exists(icon_path) - or convertIconToPNG(name, icon_path, 350)): - return quote(icon_name) - - # use the Generic package icon - return 'static/Generic.png' - - def unavailable_reason_text(self, is_update=False): - '''There are several reasons an item might be unavailable for install. - Return the relevant reason''' - if ('licensed_seats_available' in self - and not self['licensed_seats_available']): - return NSLocalizedString(u"No licenses available", - u"No Licenses Available display text") - localizedNote = getLocalizedShortNoteForItem(self, is_update=is_update) - if localizedNote: - return '' + localizedNote + '' - # return generic reason - return NSLocalizedString(u"Not currently available", - u"Not Currently Available display text") - - def status_text(self): - '''Return localized status display text''' - if self['status'] == 'unavailable': - return self.unavailable_reason_text() - if (self['status'] in ['installed', 'installed-not-removable'] and - self.get('note')): - return self.unavailable_reason_text(is_update=True) - text_map = { - 'install-error': - NSLocalizedString(u"Installation Error", - u"Install Error status text"), - 'removal-error': - NSLocalizedString(u"Removal Error", - u"Removal Error status text"), - 'installed': - NSLocalizedString(u"Installed", - u"Installed status text"), - 'installing': - NSLocalizedString(u"Installing", - u"Installing status text"), - 'installed-not-removable': - NSLocalizedString(u"Installed", - u"Installed status text"), - 'not-installed': - NSLocalizedString(u"Not installed", - u"Not Installed status text"), - 'install-requested': - NSLocalizedString(u"Install requested", - u"Install Requested status text"), - 'downloading': - NSLocalizedString(u"Downloading", - u"Downloading status text"), - 'will-be-installed': - NSLocalizedString(u"Will be installed", - u"Will Be Installed status text"), - 'must-be-installed': - NSLocalizedString(u"Will be installed", - u"Will Be Installed status text"), - 'removal-requested': - NSLocalizedString(u"Removal requested", - u"Removal Requested status text"), - 'preparing-removal': - NSLocalizedString(u"Preparing removal", - u"Preparing Removal status text"), - 'will-be-removed': - NSLocalizedString(u"Will be removed", - u"Will Be Removed status text"), - 'removing': - NSLocalizedString(u"Removing", - u"Removing status text"), - 'update-will-be-installed': - NSLocalizedString(u"Update will be installed", - u"Update Will Be Installed status text"), - 'update-must-be-installed': - NSLocalizedString(u"Update will be installed", - u"Update Will Be Installed status text"), - 'update-available': - NSLocalizedString(u"Update available", - u"Update Available status text"), - 'unavailable': - NSLocalizedString(u"Unavailable", - u"Unavailable status text"), - } - return text_map.get(self['status'], self['status']) - - def short_action_text(self): - '''Return localized 'short' action text for button''' - text_map = { - 'install-error': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'removal-error': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'installed': - NSLocalizedString(u"Remove", - u"Remove action text"), - 'installing': - NSLocalizedString(u"Installing", - u"Installing status text"), - 'installed-not-removable': - NSLocalizedString(u"Installed", - u"Installed status text"), - 'not-installed': - NSLocalizedString(u"Install", - u"Install action text"), - 'install-requested': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'downloading': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'will-be-installed': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'must-be-installed': - NSLocalizedString(u"Required", - u"Install Required action text"), - 'removal-requested': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'preparing-removal': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'will-be-removed': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'removing': - NSLocalizedString(u"Removing", - u"Removing status text"), - 'update-will-be-installed': - NSLocalizedString(u"Cancel", - u"Cancel button title/short action text"), - 'update-must-be-installed': - NSLocalizedString(u"Required", - u"Install Required action text"), - 'update-available': - NSLocalizedString(u"Update", - u"Update button title/action text"), - 'unavailable': - NSLocalizedString(u"Unavailable", - u"Unavailable status text"), - } - return text_map.get(self['status'], self['status']) - - def long_action_text(self): - '''Return localized 'long' action text for button''' - text_map = { - 'install-error': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'removal-error': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'installed': - NSLocalizedString(u"Remove", - u"Remove action text"), - 'installing': - NSLocalizedString(u"Installing", - u"Installing status text"), - 'installed-not-removable': - NSLocalizedString(u"Installed", - u"Installed status text"), - 'not-installed': - NSLocalizedString(u"Install", - u"Install action text"), - 'install-requested': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'downloading': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'will-be-installed': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'must-be-installed': - NSLocalizedString(u"Install Required", - u"Install Required action text"), - 'removal-requested': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'preparing-removal': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'will-be-removed': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'removing': - NSLocalizedString(u"Removing", - u"Removing status text"), - 'update-will-be-installed': - NSLocalizedString(u"Cancel update", - u"Cancel Update long action text"), - 'update-must-be-installed': - NSLocalizedString(u"Update Required", - u"Update Required long action text"), - 'update-available': - NSLocalizedString(u"Update", - u"Update button title/action text"), - 'unavailable': - NSLocalizedString(u"Currently Unavailable", - u"Unavailable long action text"), - } - return text_map.get(self['status'], self['status']) - - def myitem_action_text(self): - '''Return localized 'My Items' action text for button''' - text_map = { - 'install-error': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'removal-error': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'installed': - NSLocalizedString(u"Remove", - u"Remove action text"), - 'installing': - NSLocalizedString(u"Installing", - u"Installing status text"), - 'installed-not-removable': - NSLocalizedString(u"Installed", - u"Installed status text"), - 'removal-requested': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'preparing-removal': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'will-be-removed': - NSLocalizedString(u"Cancel removal", - u"Cancel Removal long action text"), - 'removing': - NSLocalizedString(u"Removing", - u"Removing status text"), - 'update-available': - NSLocalizedString(u"Update", - u"Update button title/action text"), - 'update-will-be-installed': - NSLocalizedString(u"Remove", - u"Remove action text"), - 'update-must-be-installed': - NSLocalizedString(u"Update Required", - u"Update Required long action text"), - 'install-requested': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'downloading': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'will-be-installed': - NSLocalizedString(u"Cancel install", - u"Cancel Install long action text"), - 'must-be-installed': - NSLocalizedString(u"Required", - u"Install Required action text"), - } - return text_map.get(self['status'], self['status']) - - def version_label(self): - '''Text for the version label''' - if self['status'] == 'will-be-removed': - removal_text = NSLocalizedString( - u"Will be removed", u"Will Be Removed status text") - return '%s' % removal_text - if self['status'] == 'removal-requested': - removal_text = NSLocalizedString( - u"Removal requested", u"Removal Requested status text") - return '%s' % removal_text - else: - return NSLocalizedString(u"Version", u"Sidebar Version label") - - def display_version(self): - '''Version number for display''' - if self['status'] == 'will-be-removed': - return '' - else: - return self.get('version_to_install', '') - - def developer_sort(self): - '''returns sort priority based on developer and install/removal - status''' - if self['status'] != 'will-be-removed' and self['developer'] == 'Apple': - return 0 - return 1 - - def more_link_text(self): - return NSLocalizedString(u"More", u"More link text") - - def add_localizations(self): - available_locales = list(self['localized_strings']) - fallback_locale = self['localized_strings'].get('fallback_locale') - if fallback_locale: - available_locales.remove('fallback_locale') - available_locales.append(fallback_locale) - language_code = self._get_preferred_locale(available_locales) - if language_code != fallback_locale: - locale_dict = self['localized_strings'].get(language_code) - if locale_dict: - localized_keys = ['category', - 'description', - 'display_name', - 'preinstall_alert', - 'preuninstall_alert', - 'preupgrade_alert'] - for key in localized_keys: - if key in locale_dict: - self[key] = locale_dict[key] - - def _get_preferred_locale(self, available_locales): - code = NSBundle.preferredLocalizationsFromArray_forPreferences_( - available_locales, None) - return code[0] - -class OptionalItem(GenericItem): - '''Dictionary subclass that models a given optional install item''' - - def __init__(self, *arg, **kw): - '''Initialize an OptionalItem from a item dict from the - InstallInfo.plist optional_installs array''' - super(OptionalItem, self).__init__(*arg, **kw) - if 'category' not in self: - self['category'] = NSLocalizedString(u"Uncategorized", - u"No Category name") - if 'featured' not in self: - self['featured'] = False - if self['developer']: - self['category_and_developer'] = u'%s - %s' % ( - self['category'], self['developer']) - else: - self['category_and_developer'] = self['category'] - self['dependent_items'] = dependentItems(self['name']) - if self.get('installer_item_size'): - self['size'] = munki.humanReadable(self['installer_item_size']) - elif self.get('installed_size'): - self['size'] = munki.humanReadable(self['installed_size']) - else: - self['size'] = u'' - self['detail_link'] = u'detail-%s.html' % quote(self['name']) - self['hide_cancel_button'] = u'' - if not self.get('note'): - self['note'] = self._get_note_from_problem_items() - if not self.get('status'): - self['status'] = self._get_status() - - def _get_status(self): - '''Calculates initial status for an item and also sets a boolean - if a updatecheck is needed''' - managed_update_names = getInstallInfo().get('managed_updates', []) - self_service_installs = SelfService().installs() - self_service_uninstalls = SelfService().uninstalls() - self['updatecheck_needed'] = False - self['user_directed_action'] = False - if self.get('installed'): - if self.get('removal_error'): - status = u'removal-error' - elif self.get('will_be_removed'): - status = u'will-be-removed' - elif self['dependent_items']: - status = u'installed-not-removable' - elif self['name'] in self_service_uninstalls: - status = u'removal-requested' - self['updatecheck_needed'] = True - else: # not in managed_uninstalls - if not self.get('needs_update'): - if self.get('uninstallable'): - status = u'installed' - else: # not uninstallable - status = u'installed-not-removable' - else: # there is an update available - if self['name'] in managed_update_names: - status = u'update-must-be-installed' - elif self['dependent_items']: - status = u'update-must-be-installed' - elif self['name'] in self_service_installs: - status = u'update-will-be-installed' - else: # not in managed_installs - status = u'update-available' - else: # not installed - if self.get('install_error'): - status = u'install-error' - elif self.get('note'): - # TO-DO: handle this case better - # some reason we can't install - # usually not enough disk space - # but can also be: - # 'Integrity check failed' - # 'Download failed (%s)' % errmsg - # 'Can\'t install %s because: %s', manifestitemname, errmsg - # 'Insufficient disk space to download and install.' - # and others in the future - # - # for now we prevent install this way - status = u'unavailable' - elif ('licensed_seats_available' in self - and not self['licensed_seats_available']): - status = u'unavailable' - elif self['dependent_items']: - status = u'must-be-installed' - elif self.get('will_be_installed'): - status = u'will-be-installed' - elif self['name'] in self_service_installs: - status = u'install-requested' - self['updatecheck_needed'] = True - else: # not in managed_installs - status = u'not-installed' - return status - - def _get_note_from_problem_items(self): - '''Checks InstallInfo's problem_items for any notes for self that might - give feedback why this item can't be downloaded or installed''' - problem_items = getInstallInfo().get('problem_items', []) - # check problem items for any whose name matches the name of - # the current item - matches = [item for item in problem_items - if item['name'] == self['name']] - if len(matches): - return matches[0].get('note', '') - - def description(self): - '''return a full description for the item, inserting dynamic data - if needed''' - start_text = '' - if self.get('install_error'): - warning_text = NSLocalizedString( - u"An installation attempt failed. " - "Installation will be attempted again.\n" - "If this situation continues, contact your systems " - "administrator.", - u"Install Error message") - start_text += ('%s' - % filtered_html(warning_text)) - if self.get('removal_error'): - warning_text = NSLocalizedString( - u"A removal attempt failed. " - "Removal will be attempted again.\n" - "If this situation continues, contact your systems " - "administrator.", - u"Removal Error message") - start_text += ('%s' - % filtered_html(warning_text)) - if self.get('note'): - is_update = self['status'] in ['installed', 'installed-not-removable'] - warning_text = getLocalizedLongNoteForItem(self, is_update=is_update) - if not warning_text: - # some other note. Probably won't be localized, but we can try - warning_text = NSBundle.mainBundle().localizedStringForKey_value_table_( - self['note'], self['note'], None) - start_text += ('%s' - % filtered_html(warning_text)) - if self.get('dependent_items'): - start_text += self.dependency_description() - - return start_text + self['raw_description'] - - def update_status(self): - # user clicked an item action button - update the item's state - # also sets a boolean indicating if we should run an updatecheck - self['updatecheck_needed'] = True - original_status = self['status'] - managed_update_names = getInstallInfo().get('managed_updates', []) - if self['status'] == 'update-available': - # mark the update for install - self['status'] = u'install-requested' - subscribe(self) - elif self['status'] == 'update-will-be-installed': - # cancel the update - self['status'] = u'update-available' - unmanage(self) - elif self['status'] in ['will-be-removed', 'removal-requested', - 'preparing-removal', 'removal-error']: - if self['name'] in managed_update_names: - # update is managed, so user can't opt out - self['status'] = u'installed' - elif self.get('needs_update'): - # update being installed; can opt-out - self['status'] = u'update-will-be-installed' - else: - # item is simply installed - self['status'] = u'installed' - if self.get('was_self_service_install'): - subscribe(self) - else: - unmanage(self) - if original_status == 'removal-requested': - self['updatecheck_needed'] = False - elif self['status'] in ['will-be-installed', 'install-requested', - 'downloading', 'install-error']: - # cancel install - if self.get('needs_update'): - self['status'] = u'update-available' - else: - self['status'] = u'not-installed' - unmanage(self) - if original_status == 'install-requested': - self['updatecheck_needed'] = False - elif self['status'] == 'not-installed': - # mark for install - self['status'] = u'install-requested' - subscribe(self) - elif self['status'] == 'installed': - # mark for removal - self['status'] = u'removal-requested' - if self['name'] in SelfService().installs(): - self['was_self_service_install'] = True - unsubscribe(self) - - -class UpdateItem(GenericItem): - '''GenericItem subclass that models an update install item''' - - def __init__(self, *arg, **kw): - super(UpdateItem, self).__init__(*arg, **kw) - identifier = (self.get('name', '') + '--version-' - + self.get('version_to_install', '')) - self['detail_link'] = 'updatedetail-%s.html' % quote(identifier) - if not self.get('status') == 'will-be-removed': - force_install_after_date = self.get('force_install_after_date') - if force_install_after_date: - self['type'] = NSLocalizedString( - u"Critical Update", u"Critical Update type") - self['due_date_sort'] = force_install_after_date - - if not 'type' in self: - self['type'] = NSLocalizedString(u"Managed Update", - u"Managed Update type") - self['hide_cancel_button'] = u'hidden' - self['dependent_items'] = dependentItems(self['name']) - - def description(self): - start_text = '' - if not self['status'] == 'will-be-removed': - force_install_after_date = self.get('force_install_after_date') - if force_install_after_date: - # insert installation deadline into description - try: - local_date = munki.discardTimeZoneFromDate( - force_install_after_date) - except munki.BadDateError: - # some issue with the stored date - pass - else: - date_str = munki.stringFromDate(local_date) - forced_date_text = NSLocalizedString( - u"This item must be installed by %s", - u"Forced Date warning") - start_text += ('' - + forced_date_text % date_str - + '') - elif self['status'] == 'problem-item': - if self.get('install_error'): - warning_text = NSLocalizedString( - u"An installation attempt failed. " - "Installation will be attempted again.\n" - "If this situation continues, contact your systems " - "administrator.", - u"Install Error message") - start_text += ('%s' - % filtered_html(warning_text)) - elif self.get('removal_error'): - warning_text = NSLocalizedString( - u"A removal attempt failed. " - "Removal will be attempted again.\n" - "If this situation continues, contact your systems " - "administrator.", - u"Removal Error message") - start_text += ('%s' - % filtered_html(warning_text)) - elif self.get('note'): - warning_text = getLocalizedLongNoteForItem(self) - if not warning_text: - # some other note. Probably won't be localized, but we can try - warning_text = NSBundle.mainBundle().localizedStringForKey_value_table_( - self['note'], self['note'], None) - start_text += ('%s' - % filtered_html(warning_text)) - if self.get('dependent_items'): - start_text += self.dependency_description() - - return start_text + self['raw_description'] diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MyStuffTemplate.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/MyStuffTemplate.png deleted file mode 100644 index 059644f1..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/MyStuffTemplate.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/FollowLink.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/FollowLink.png deleted file mode 100644 index e932e6a7..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/FollowLink.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/Generic.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/Generic.png deleted file mode 100644 index 7f036945..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/Generic.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/LogOutReq.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/LogOutReq.png deleted file mode 100644 index 5cb88619..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/LogOutReq.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/RestartReq.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/RestartReq.png deleted file mode 100644 index e6228d1a..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/RestartReq.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/add-button-sprite.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/add-button-sprite.png deleted file mode 100644 index 08a2495d..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/add-button-sprite.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/appearance.js b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/appearance.js deleted file mode 100644 index 379621f8..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/appearance.js +++ /dev/null @@ -1,13 +0,0 @@ -/* appearance.js - Managed Software Center - - Created by Greg Neagle on 8/27/18. - */ - -function changeAppearanceModeTo(theme) { - document.documentElement.classList.add('appearance-transition') - document.documentElement.setAttribute('data-theme', theme) - window.setTimeout(function() { - document.documentElement.classList.remove('appearance-transition') - }, 1000) -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/base.css b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/base.css deleted file mode 100644 index ffc120d3..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/base.css +++ /dev/null @@ -1,1321 +0,0 @@ -/* -** base.css for Managed Software Center.app -** Based on App Store CSS © 2013 Apple Inc. -*/ - -html { - --background-color: hsl(0, 0%, 100%); - --text-color-emphasized: hsl(0, 0%, 8%); - --text-color-normal: hsl(0, 0%, 23%); - --text-color-subdued: hsl(0, 0%, 48%); - --text-color-warning: hsl(0, 100%, 40%); - --description-link-color: #3E6397; - --button-text-color-enabled: hsl(0, 0%, 100%); - --button-text-color-disabled: hsl(0, 0%, 74%); - --popup-menu-text-color: hsl(0, 0%, 0%); - --popup-menu-background: -webkit-gradient(linear, left top, left bottom, from(hsl(0, 0%, 100%)), to(hsl(0, 0%, 92%))); - --small-btn-background: -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(166, 168, 174)), to(rgb(157, 159, 164))); - --small-btn-background-active: -webkit-gradient(linear, left top, left bottom, from( #85868d), to( #7a7bb2)); - --detail-btn-background: -webkit-gradient(linear, left top, left bottom, from(rgb(73,124,205)), to(rgb(47,79,143))); - --detail-btn-background-active: -webkit-gradient(linear, left top, left bottom, from( #0067B8), to( #003777)); - --install-btn-background: -webkit-gradient(linear, left top, left bottom, from( #85868d), color-stop(0.05, #84858c), color-stop(0.5, #888188), color-stop(0.96, #7b7c83), to( #7a7bb2)); - --install-btn-background-active: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#5e5fa0), color-stop(4%,#67686f), color-stop(50%,#736d73), color-stop(95%,#6f7077), color-stop(100%,#707178)); -} - -html[data-theme='dark'] { - --background-color: hsl(0, 0%, 14%); - --text-color-emphasized: hsl(0, 0%, 92%); - --text-color-normal: hsl(0, 0%, 77%); - --text-color-subdued: hsl(0, 0%, 62%); - --text-color-warning: hsl(0, 100%, 60%); - --description-link-color: #90B0EA; - --button-text-color-enabled: hsl(0, 0%, 0%); - --button-text-color-disabled: hsl(0, 0%, 41%); - --popup-menu-text-color: hsl(0, 0%, 100%); - --popup-menu-background: -webkit-gradient(linear, left top, left bottom, from(hsl(0, 0%, 30%)), to(hsl(0, 0%, 38%))); - --small-btn-background: -webkit-gradient(linear, 0% 0, 0% 100%, from(rgb(166, 168, 174)), to(rgb(157, 159, 164))); - --small-btn-background-active: -webkit-gradient(linear, left top, left bottom, from(#E8E6FF), to( #F2F3FC)); - --detail-btn-background: -webkit-gradient(linear, left top, left bottom, from(rgb(73,124,205)), to(rgb(47,79,143))); - --detail-btn-background-active: -webkit-gradient(linear, left top, left bottom, from(#5D99F1), to(#4A65AC)); - --install-btn-background: -webkit-gradient(linear, left top, left bottom, from( #BABBC3), color-stop(0.05, #B9BAC2), color-stop(0.5, #BDB6BD), color-stop(0.96, #AFB0B8), to(#B0AFEA)); - --install-btn-background-active: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#E8E6FF), color-stop(4%,#E7E8F0), color-stop(50%,#F5EEF5), color-stop(95%,#F1F2FA), color-stop(100%,#F2F3FC)); -} - -html.appearance-transition, -html.appearance-transition *, -html.appearance-transition *:before, -html.appearance-transition *:after { - transition: all 500ms !important; - transition-delay: 0 !important; -} - -html, -body, div, ul, ol, li, a, img, embed, h1, h2, h3, h4, h5, h6, dl, dt, dd, pre, -code, form, fieldset, legend, input, textarea, p, blockquote, table, th, -td { - margin: 0; - padding: 0 -} - -table { - border-collapse: collapse; - border-spacing: 0 -} - -h1, h2, h3, h4, h5, h6 { - font-size: 100%; - font-weight: normal; - font-style: normal -} - -li { - list-style-type: none; - disabled-list-style-position: outside; -} - -a { - text-decoration: none; - color: var(--text-color-emphasized); -} - -* { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-box-sizing: border-box; - cursor: default -} - -html, body, div, ul, ol, li, a, img, embed, h1, h2, h3, h4, h5, h6, dl, dt, dd, -pre, code, form, footer, section, header, fieldset, legend, p, blockquote, -table, th, td { - -webkit-user-select: none -} - -option { - color: initial -} - -body, button, input { - font-family: -apple-system, "Helvetica Neue", "Helvetica", "Arial"; - font-smooth: always; - -webkit-font-smoothing: antialiased; - color: var(--text-color-normal); -} - -@-webkit-keyframes generic-loading-spinner-animation { - 0% { - -webkit-transform: rotate(0deg) - } - - 8.32% { - -webkit-transform: rotate(0deg) - } - - 8.33% { - -webkit-transform: rotate(30deg) - } - - 16.65% { - -webkit-transform: rotate(30deg) - } - - 16.66% { - -webkit-transform: rotate(60deg) - } - - 24.99% { - -webkit-transform: rotate(60deg) - } - - 25% { - -webkit-transform: rotate(90deg) - } - - 33.32% { - -webkit-transform: rotate(90deg) - } - - 33.33% { - -webkit-transform: rotate(120deg) - } - - 41.65% { - -webkit-transform: rotate(120deg) - } - - 41.66% { - -webkit-transform: rotate(150deg) - } - - 49.99% { - -webkit-transform: rotate(150deg) - } - - 50% { - -webkit-transform: rotate(180deg) - } - - 58.32% { - -webkit-transform: rotate(180deg) - } - - 58.33% { - -webkit-transform: rotate(210deg) - } - - 66.65% { - -webkit-transform: rotate(210deg) - } - - 66.66% { - -webkit-transform: rotate(240deg) - } - - 74.99% { - -webkit-transform: rotate(240deg) - } - - 75% { - -webkit-transform: rotate(270deg) - } - - 83.32% { - -webkit-transform: rotate(270deg) - } - - 83.33% { - -webkit-transform: rotate(300deg) - } - - 91.65% { - -webkit-transform: rotate(300deg) - } - - 91.66% { - -webkit-transform: rotate(330deg) - } - - 100% { - -webkit-transform: rotate(330deg) - } -} - -a.anchor[name] { - height: 0; - width: 0 -} - -.single-line-clip { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -.no-display { - display: none !important -} - -.hidden { - display:none!important -} - -a * { - cursor: pointer -} - -a { - text-decoration: none; - color: var(--text-color-emphasized); - cursor: auto -} - -a:hover { - text-decoration: underline -} - -* { - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); - -webkit-box-sizing: border-box; - cursor: default -} - -html, body, div, ul, ol, li, a, img, embed, h1, h2, h3, h4, h5, h6, dl, dt, dd, -pre, code, form, footer, section, header, fieldset, legend, p, blockquote, -table, th, td { - -webkit-user-select: none -} - -div::-webkit-scrollbar-corner, div::-webkit-resizer { - display: none !important -} - -button, button *, select { - cursor: pointer -} - -body { - -webkit-font-smoothing: antialiased -} - -body:after { - content: ""; - display: block; - width: 1px; - height: 1px; -} - -a { - cursor: pointer -} - -html, body { - -webkit-background-size: 36px 635px; - background-repeat: repeat-x; - background-color: var(--background-color); - font-family: -apple-system, "Helvetica Neue", Helvetica, sans-serif; - font-size: 11px -} - -html { - height: 100%; - background-image: none -} - -#wrapper { - position: relative; - max-width: 1198px; - min-width: 984px; - height: 100%; - margin: 0 auto; - padding: 0 20px; - display: -webkit-box; - min-height: 0; - -webkit-box-orient: vertical; - -webkit-box-align: stretch -} - -#content { - min-height: 100%; - margin: 0 auto -35px; - padding-bottom: 90px -} - -div.columns { - display: -webkit-box; - -webkit-box-orient: horizontal; - -webkit-box-align: stretch; - position: relative; - z-index: 1 -} - -#content>div:first-child+div.columns, #content>div.showcase+div.columns, -#content>div:only-child { - padding-top: 20px -} - -.main { - -webkit-box-flex: 1; - min-width: 501px -} - -.main>div:not(:last-of-type) { - margin-bottom: 40px -} - -.sidebar { - width: 200px; - margin-left: 26px; -} - -.sidebar h1, .main>h1 { - -webkit-background-clip: text; - -webkit-font-smoothing: subpixel-antialiased; - background-color: #002354; - color: transparent; - font-family: -apple-system, "Helvetica Neue", Helvetica; - font-size: 24px; - font-weight: normal; - line-height: 26px; - margin-bottom: 5px; - overflow: visible; - position: relative; - top: -5px; -} - -footer { - font-size: 10px; - color: #4c4c4c; - line-height: 30px; - text-align: center; - position: relative; - z-index: 1; - max-width: 1198px; - min-width: 984px; - margin: 0 auto; - padding: 0 20px -} - -footer script { - display: none !important; -} - -footer div.bottom-links { - margin: 0 0 0 0; - border-top: 1px solid #d3d7db; - position: relative -} - -footer.no-country-nav div.bottom-links { - margin-right: 0 -} - -footer div.bottom-links * { - color: var(--text-color-normal); - display: inline-block; - font-size: 10px; - font-weight: normal; -} - -footer div.bottom-links>span { - margin-right: 5px -} - -footer div.bottom-links li a { - color: var(--text-color-normal); - font-weight: normal -} - -footer div.bottom-links>ul>li:not(:last-child):after { - content: "|"; - padding-left: 7px; - margin-right: 3px -} - -footer div.bottom-links div.intl { - position: absolute; - top: -12px; - right: -39px -} - -div.select { - position: relative; - display: inline-block -} - -div.select label { - font-size: 11px -} - -div.select select { - background: var(--popup-menu-background); - font-size: 11px; - font-weight: normal; - color: var(--popup-menu-text-color); - outline: 0; - line-height: 15px; - margin: 0; - padding: 0 25px 1px 5px; - border: 0; - -webkit-appearance: none; - -webkit-box-shadow: rgba(0, 0, 0, .35) 0 1px 2px, rgba(0, 0, 0, .8) 0 0 1px; - box-shadow: rgba(0, 0, 0, .35) 0 1px 2px, rgba(0, 0, 0, .2) 0 0 1px; - -webkit-border-radius: 3px; - -webkit-background-clip: padding; -} - -div.select select:focus { - outline: 0 transparent -} - -div.select:before { - content: ""; - position: absolute; - right: 16px; - top: 0; - width: 0; - height: 16px; - border-left: 1px solid #cecece; - border-right: 1px solid var(--background-color); - pointer-events: none -} - -div.select:after { - -webkit-mask-box-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAWCAYAAAD5Jg1dAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAl0lEQVQokWP4//8/AzIGgiggtscQR1MkBMRvgfg2ELPhUzgNiP9DcQtWhUBgg6QIhH8CsS6KQiBgBuLzaApB+DBIDllhKRZFMJwJVcMgDcSf8Sj8DFXDsBGPIhhex4AeXrjw8FIIBFuICB5QEDLIERXgUGMrCEYhVCEbjkRxDCVR4ElmBrgS7kwkhe2UZwWo4lggdkIXBwBe1apnRfWr3AAAAABJRU5ErkJggg==); - content: ""; - position: absolute; - height: 11px; - width: 5px; - right: 6px; - top: 3px; - pointer-events: none; - background-color: var(--popup-menu-text-color); -} - -div.titled-box { - margin-bottom: 15px; - padding-bottom: 10px; - -webkit-border-radius: 5px; -} - -div.titled-box h2 { - font: normal 14px -apple-system, "Helvetica Neue", 'Helvetica'; - color: var(--text-color-normal); - line-height: 24px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -div.titled-box h2 a { - font: normal 12px -apple-system, "Helvetica Neue", 'Helvetica'; - color: var(--text-color-normal); - line-height: 24px -} - -div.titled-box>div.content { - margin-top: 5px -} - -div.titled-box.quick-links>div.content { - margin-top: 3px -} - -div.titled-box h2, div.titled-box hr { - border: none; -} - -div.titled-box hr { - margin: 14px 0 -} - -div.titled-box li.popup { - position: relative; - text-align: center; - margin-bottom: 5px -} - -div.titled-box li.popup { - text-align: left; - margin: -3px 0 6px 6px -} - -div.titled-box li.popup span { - display: none -} - -div.titled-box li.popup select { - width: 188px -} - -div.titled-box li.link { - padding: 0 11px; - height: 19px; - line-height: 16px; - font-weight: normal; - border-color: transparent; - border-width: 1px 0; - border-style: solid -} - -div.titled-box li.link.selected { - border-color: #e9e9e9; - border-width: 1px 0; - border-style: solid; - background-color: #f3f3f3 -} - -div.titled-box li.link a { - display: block; - font-size: 11px; - font-weight: normal; - color: var(--text-color-normal); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -div.titled-box li.link.user-link a { - color: var(--text-color-subdued) -} - -body.screen-reader div.chart ol>li div.lockup div.lockup-info>div.multi-button { - display: block -} - -div.lockup-container, div.container, div.titled-container { - /*-webkit-border-radius: 5px;*/ -} - -div.lockup-container .title, div.titled-container .title { - height: 32px; - border-bottom: 1px solid #d7d7d7 -} - -div.lockup-container .title h2, div.titled-container .title h2 { - display: inline-block; - font: normal 14px -apple-system, "Helvetica Neue", 'Helvetica'; - color: var(--text-color-normal); - line-height: 32px -} - -div.lockup-container .title, div.titled-container .title, div.titled-box h2 { - -webkit-border-top-left-radius: 5px; - -webkit-border-top-right-radius: 5px; -} - -div.titled-box h2 { - color: var(--text-color-normal); - border-bottom: 1px solid #cacaca; - border-width: 0 0 1px !important; - margin-bottom: 6px; - margin-top: 10px; - -webkit-border-image: none; - font-size: 13px; - line-height: 22px -} - -div.lockup-container .title h1 { - display: inline-block; - font: normal 14px -apple-system, "Helvetica Neue", Helvetica; - color: var(--text-color-normal); - line-height: 32px -} - -div.lockup-container .title h1 a { - color: var(--text-color-normal); - text-decoration: none -} - -div.lockup-container .title span { - color: var(--text-color-normal); - font-size: 10px; - font-weight: normal -} - -div.lockup-container .content-and-controls, div.titled-container .content-and-controls { - position: relative -} - -div.lockup-container .content-and-controls:before, div.titled-container .content-and-controls:before { - content: ''; - display: block; - position: absolute; - width: 100%; - top: -1px; -} - -div.lockup-container .content-and-controls:after, div.titled-container .content-and-controls:before { - content: ''; - display: block; - clear: both; - height: 0 -} - -div.lockup-container .paginated-container { - -webkit-border-radius: 0 0 5px 5px -} - -div.lockup-container div.lockup { - display: block; - float: left; - width: 33.3%; - height: 98px; -} - -div.lockup-container div.lockup div.artwork { - margin-right: 10px -} - -div.lockup-container div.lockup ul { - margin-top: 4px -} - -div.lockup-container div.lockup li.name>* { - font-size: 12px; - color: var(--text-color-emphasized) -} - -div.lockup-container div.lockup li, div.lockup-container div.lockup li>a, -div.lockup-container div.lockup li>span { - color: var(--text-color-subdued); - font-size: 11px -} - -div.lockup-container[data-columns-current] { - display: block -} - -div.lockup-container[data-columns-current] div.lockup:nth-last-of-type(1) { - -webkit-border-bottom-right-radius: 5px; - padding-bottom: 11px -} - -div.lockup-container[data-columns-current] div.lockup:nth-last-of-type(1), -div.lockup-container[data-columns-current] div.lockup:nth-last-of-type(2), -div.lockup-container[data-columns-current] div.lockup:nth-last-of-type(3) { - border-bottom: 0 -} - -div.lockup-container[data-columns-current] div.lockup.backfill:last-of-type { - height: 98px !important; - display: block !important; - width: auto; - overflow: hidden -} - -div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(3n+2) { - width: 33.4% -} - -div.lockup-container[data-columns-current="3"] div.lockup:nth-of-type(3n+1) { - border-left: 0 -} - -div.lockup-container:not(.shelf)[data-columns-current="3"] div.lockup:nth-of-type(3n) { - display: table-cell; - /*float: none;*/ - /*height: auto;*/ -} - -div.lockup-container[data-columns-current="3"] div.lockup:nth-last-of-type(3) { - -webkit-border-bottom-left-radius: 5px -} - -div.lockup-container[data-columns-current="4"] div.lockup, div.lockup-container[data-columns-current="4"] div.lockup:nth-of-type(3n+2) { - width: 25% -} - -div.lockup-container:not(.shelf)[data-columns-current="4"] div.lockup:nth-of-type(4n) { - display: table-cell; - /*float: none; - height: auto;*/ - border-right: 0 -} - -div.lockup-container[data-columns-current="4"] div.lockup:nth-last-of-type(4) { - -webkit-border-bottom-left-radius: 5px -} - -div.lockup-container[data-columns-current="4"] div.lockup:nth-last-of-type(4) { - border-bottom: 0 -} - -div.lockup-container[data-columns-current="5"] div.lockup, div.lockup-container[data-columns-current="5"] div.lockup:nth-of-type(3n+2) { - width: 20% -} - -div.lockup-container:not(.shelf)[data-columns-current="5"] div.lockup:nth-of-type(5n) { - display: table-cell; - /*float: none; - height: auto;*/ -} - -div.lockup-container[data-columns-current="5"] div.lockup:nth-last-of-type(5) { - -webkit-border-bottom-left-radius: 5px -} - -div.lockup-container[data-columns-current="5"] div.lockup:nth-last-of-type(4), -div.lockup-container[data-columns-current="5"] div.lockup:nth-last-of-type(5) { - border-bottom: 0 -} - -div.lockup { - position: relative; - padding: 10px -} - -div.lockup div.artwork { - float: left; - cursor: pointer -} - -div.lockup ul { - overflow: hidden -} - -div.lockup ul li { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap -} - -div.lockup li.name { - color: var(--text-color-emphasized); - max-width: 190px -} - -div.lockup li.artist { - max-width: 190px -} - -div.lockup li.genre { - max-width: 190px -} - -div.lockup li.seller, div.lockup span.rating-count { - font-weight: normal -} - -div.lockup-container div.lockup ul li { - margin-bottom: 1px -} - -div.lockup-container div.lockup.category ul { - margin-top: 6px; - font-weight: normal; -} - -div.lockup-container div.lockup.category li.genre a { - color: var(--text-color-subdued); -} - -div.msc-button.small { - display: inline; -} - -div.msc-button-inner { - height: 13px; - -webkit-border-radius: 3px; - background: var(--small-btn-background); - border: 1px solid rgba(136, 136, 138, .746094); - text-transform: uppercase; - color: var(--button-text-color-enabled); - font: normal normal bold 9px/12px 'Helvetica'; - padding-left: 10px; - padding-right: 10px; -} - -div.msc-button-inner.installed-not-removable, -div.msc-button-inner.unavailable, -div.msc-button-inner.installing, -div.msc-button-inner.must-be-installed, -div.msc-button-inner.update-must-be-installed, -div.msc-button-inner.removing { - background: none; - cursor: default; - pointer-events: none; - border: .5px solid var(--button-text-color-disabled); - color: var(--button-text-color-disabled) -} - -div.msc-button-inner:not(.installed-not-removable):active:hover { - background: var(--small-btn-background-active); -} - -div.msc-button button { - -webkit-appearance: none; - -webkit-border-radius: 0; - -webkit-font-smoothing: antialiased; - padding: 0; - margin: 0 0 0 0; - background: 0; - border: 0; - text-align: center; - cursor: default; -} - -div.msc-button-inner.large { - height: 23px; - position: relative; - min-width: 50px; - margin-left: 5px; - font-size: 13px; - font-family: "Helvetica"; - text-transform: capitalize; - line-height: 21px; - border: 1px solid rgb(42, 73, 118); - color: White; - background: var(--detail-btn-background); - border-color: #1F4A7A; - -webkit-border-radius: 5px; -} - -div.msc-button-inner.large.installed-not-removable, -div.msc-button-inner.large.must-be-installed, -div.msc-button-inner.large.update-must-be-installed, -div.msc-button-inner.large.unavailable, -div.msc-button-inner.large.installing, -div.msc-button-inner.large.removing { - background: none; - border: .5px solid var(--button-text-color-disabled); - color: var(--button-text-color-disabled); - cursor: default; - pointer-events: none; -} - -div.msc-button-inner.large:not(.installed-not-removable):active:hover { - background: var(--detail-btn-background-active); -} - -div.msc-button.install-updates { - display: inline; - float: right; -} - -div.msc-button.install-updates.in-header { - margin-top: -2px; -} - -div.msc-button-inner.install-updates { - height: 23px; - position: relative; - min-width: 90px; - margin-left: 5px; - font-size: 11px; - text-transform: uppercase; - line-height: 24px; - border: 0; - color: var(--button-text-color-enabled); - background: var(--install-btn-background); - border-color: #1F4A7A; - -webkit-border-radius: 3px; -} - -div.msc-button-inner.install-updates.disabled, -div.msc-button-inner.install-updates.installed-not-removable, -div.msc-button-inner.install-updates.unavailable, -div.msc-button-inner.install.must-be-installed, -div.msc-button-inner.install.update-must-be-installed, -div.msc-button-inner.install-updates.installing, -div.msc-button-inner.install-updates.removing { - background: none; - border: 1px solid var(--button-text-color-disabled); - color: var(--button-text-color-disabled); - cursor: default; - pointer-events: none; -} - -div.msc-button-inner.install-updates:not(.installed-not-removable):active:hover { - background: var(--install-btn-background-active); -} - -.scrollbar::-webkit-scrollbar { - width: 15px; - height: 15px -} - -.scrollbar::-webkit-scrollbar-track { - margin: 0 -} - -.scrollbar::-webkit-scrollbar-corner { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAB0SURBVCjPndJRDsAQEEXR22YWZf8fdoV+VQeD0SZSxDGPuGKMhZ+fAIQQKOXb4+3rvzUnACmlZlHfcs5mX3S1FbLG4oE9muJdNX3+iq1FK1ixt5qGAPcu9gwO+AROY3ugGdsLm9vWsfUz3GId2wuH2CcQ4AG+YoV+rql/EQAAAABJRU5ErkJggg==); - background-position: 15px 0; - background-repeat: no-repeat; - background-position: 0 0 -} - -.scrollbar::-webkit-scrollbar:vertical { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAFCAIAAAAVLyF7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADJJREFUeNpiPHbsmIWFxf///xkYGNBIOIALsgDxv3///mMAiOA/MIALMjGQAmipGiDAAM13RLgAGwkvAAAAAElFTkSuQmCC); - background-repeat: repeat-y -} - -.scrollbar::-webkit-scrollbar-track-piece:vertical:decrement { - -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAMCAYAAAC9QufkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWFJREFUeNqckE9qwkAUxl/iRI1I/JtFk0UWLnKC5gK6Nlsv4QV6gF6gJ2m7bdcK3VUQV0IXZpGQxCBqUNHON3SkSFuhD16GmXzf730zyul0ov8WG41G5HkeAXI4HMwoivqLxaIXx/Htfr93NE37qFarb+12+8WyrCfuiaBFM0lJ07Q7mUyG8/ncbzQaxA2iuaiT53lnOp0OVqvVo+M4D6VS6VVMxidJku54PL7PssyzbZtgNgyDuEiAd7sd8X/Q+dvt9sZ13TsAGKLOZrMhp3qmaVKr1aJarSamwqwoCq5D5XJZ7MMw9IIgGPIE72yz2fQ51W82m1Sv14VJ13UqFouiVVUlfm+xAnQ8Hmm5XPrr9foZsXuIKE0QFgqFs1iuOK9UKiIFHovH7zEuHDDGRCSYIER/LwmBDmkA4e8wUEGU8eSUn+oSIPSYJmNK0W9mlAR8edSrUy+nn/ua4S/QpwADAKfohhqUznmaAAAAAElFTkSuQmCC) 11 0 0 0; - border-width: 11px 0 0 0; - margin-top: -11px -} - -.scrollbar::-webkit-scrollbar-track-piece:vertical:decrement:no-button { - margin-top: 4px -} - -.scrollbar::-webkit-scrollbar-track-piece:vertical:increment { - -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAMCAYAAAC9QufkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAVhJREFUeNqcUs1qwkAQnqybGKMnYw8VJJe8QoMP0HN9kr5AXyMv4VXPes2hPXoXinhKIiqSxESTzrewIEJbcGDYn5n5vm9n1oiiqJFSkmVZ1G63ybZt5djjrtVqEayua6qqis7nMxVFQXmek2iahh4x1Akgav8PCPHbfHm9XkkIoQ464bdCLf9yuRDqJN6BgGmaf7JrVhSWZan2ghGmaAAaATQk3QPcF2ZZhmZOBccWx+ORTqeT6iCUAESrwIoz7lF0OByUdzqdhXQcZ84+22w2E8Mw1Gi04wwHI8D3+z0lSUKu68663e6cRyxj3/dDDjzHcRxoeWDCrGGQCrbdbgfGz+FwGHKTY4lgv99fjsfjj9Vq9b5erydaHj4LpKMfeNpoNJp5nhcy6BL3Bv8wCoJAJTHjE7O/bbfb1zRNX5jd4yl893q9r8FgsGDGOXPFuqnGoz8M9iPAANAKFk0a88UQAAAAAElFTkSuQmCC) 0 0 11 0; - border-width: 0 0 11px 0; - margin-bottom: -11px -} - -.scrollbar::-webkit-scrollbar-track-piece:vertical:increment:no-button { - margin-bottom: 4px -} - -.scrollbar::-webkit-scrollbar-thumb:vertical { - -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAYCAYAAAAh8HdUAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAI7SURBVDjLY2BlZWUAYkYgZmKFAE4g5gFiXijNCRVngqpjgGlggSqSBmINIDYCYjMorQEV54WqYwRpYgNiYSDWcfSLqwlJrzkek9/5Jrao929IZusbr9iy4yYuYTUgeag6NpAmYV5Baavw9KodZR0L/rVMX/u/Z/7W/30Ld/xvnLLuf2HLwv9xJRP/OYXk7+DgFbcCqQdp0olIr93RNGXV/1mr9/6fvfbg/+kr9vzvX7zzf+fsrf+bp238X9G9/H9C2ZT/9kE5O0DqGVwDEmtq+pb8m7vu4P9Fm47+nwPUNG3F3v8TFkE1Td/4v27imv9FbYv/h+f1/jNwCKlhiM1uOj5h0db/y7ee+L9oM0TTdKCm/kUIm+omrAHatvJ/cuX0/06hhccZMiomvF6w4dD/ZUBNC4GaZqNpagJqqgXaVA7UlNO44L93fP1rhtz66X9W7Tz9f9m2k2DnzUZ3HpJNhW1L/vultP5hyGuY8X/1rjNgmxZtRmhCdx7IpuKOZf/9Ulv/QzTtPAP200KkgABp6pqzBew8sKaelf+LOpYOpCZwQKxD9RMs9CrAfkLWBAyI5dvQAgIpRdROWAvWBLcJHOQ7TiNCD2rThMWoKQIUekWwIEeOXORkhGxTHdR58MgFJ6OFiGQEdt5ytHiCakqugiYjcILthSZYLJHbNB3ip0LkBAvPGpOBWWPV3v9z1kHSHnIyKu9CyxrgTCiOyIStwEzYi5EJJ/1zDs1FyYQY2T2WiOxOVsFCchEGAFMOhUINnDofAAAAAElFTkSuQmCC) 8 0 8 0; - border-color: transparent; - border-width: 8px 1px; - min-height: 24px; - margin: 0 1px -} - -.scrollbar::-webkit-scrollbar-thumb:vertical:active { - -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAYCAYAAAAh8HdUAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAI7SURBVDjLY2BlZWUAYkYgZmKFAE4g5gFiXijNCRVngqpjgGlggSqSBmINIDYCYjMorQEV54WqYwRpYgNiYSDWcfSLqwlJrzkek9/5Jrao929IZusbr9iy4yYuYTUgeag6NpAmYV5Baavw9KodZR0L/rVMX/u/Z/7W/30Ld/xvnLLuf2HLwv9xJRP/OYXk7+DgFbcCqQdp0olIr93RNGXV/1mr9/6fvfbg/+kr9vzvX7zzf+fsrf+bp238X9G9/H9C2ZT/9kE5O0DqGVwDEmtq+pb8m7vu4P9Fm47+nwPUNG3F3v8TFkE1Td/4v27imv9FbYv/h+f1/jNwCKlhiM1uOj5h0db/y7ee+L9oM0TTdKCm/kUIm+omrAHatvJ/cuX0/06hhccZMiomvF6w4dD/ZUBNC4GaZqNpagJqqgXaVA7UlNO44L93fP1rhtz66X9W7Tz9f9m2k2DnzUZ3HpJNhW1L/vultP5hyGuY8X/1rjNgmxZtRmhCdx7IpuKOZf/9Ulv/QzTtPAP200KkgABp6pqzBew8sKaelf+LOpYOpCZwQKxD9RMs9CrAfkLWBAyI5dvQAgIpRdROWAvWBLcJHOQ7TiNCD2rThMWoKQIUekWwIEeOXORkhGxTHdR58MgFJ6OFiGQEdt5ytHiCakqugiYjcILthSZYLJHbNB3ip0LkBAvPGpOBWWPV3v9z1kHSHnIyKu9CyxrgTCiOyIStwEzYi5EJJ/1zDs1FyYQY2T2WiOxOVsFCchEGAFMOhUINnDofAAAAAElFTkSuQmCC) 8 0 8 0 -} - -.scrollbar::-webkit-scrollbar-thumb:vertical:window-inactive { - -webkit-border-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAYCAYAAAAh8HdUAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAGhSURBVDjL7ZTPasJAGMTzRH3NHLwo2k2qjRsa/4A9iSCWKgGTeHDFVCwKJkEPYvCQRzDT7JoEbeuhPfTUhT3+mNnvm1kJgPTTK/0KujyapsmU0hmt06her5+0x8eoWqvNyuWyLH0+hJA7SnWz1+vFjuNgsVhgtVphOp1iMBig3W7HiqKYhULhLod0/ckcjUbYbDbY7XbwfR/r9RrL5Tvm8zksy0bnuYMKIaYAEjtyv9+P/cDHfr/HlkOel0JLAXFF0zRhNBpxsViSJcMwZowxHA4HAQklz88hV0AMk8kE3W4X94TMpGazGQWJnSvI964hxiEHw+EQqqpGHDqFYZhAYQ55wt4qt8dSpfHYgqIqJ6nVauF4PIKD39mbuy4YO0OWbePhQcU/lEHZyDNoK0buXyUig/jIVUU9ieXyrIXhreW6yXKncJxJstxXDkV5jMI0EULpUyLYZYzukxhpaWCDIPii9J6k3E3t8cA2jEZcKhXltBr6jWqclWzbQqfzDEIq5lUJdZ1elPDtXMLkLS8vN0r4pe40q7sW1arf1P3PfqMPul2sxZkVBcYAAAAASUVORK5CYII=) 8 0 8 0 -} - -.scrollbar::-webkit-scrollbar-button:vertical { - height: 26px; - background-position: -15px 0; - background-repeat: no-repeat -} - -.scrollbar::-webkit-scrollbar-button:vertical:active { - background-position: -30px 0 -} - -.scrollbar::-webkit-scrollbar-button:vertical:window-inactive { - background-position: 0 0 -} - -.scrollbar::-webkit-scrollbar-button:vertical:start:decrement { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAaCAIAAACsB0LYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgVJREFUeNrslF1LwmAUx/dsGqRBH6Grrrrqxo9V9Cn6BEEvFxFRBEGRUvZiVBIGZVkaWUpCQqjJcmtvTebsPDO3Z23abna3Mz26s+Pz/Pzv7I8ymUwsFus6Qtf1Xu596UWpVPLeHL/hpyYiUOjo+NAg4ehn/In7Ozr+JU35GQgOK/+Wfl9WD377y2HbEnAQsl+zwmcOSwZEnJI1NIxDVVXvOwmC4L1ZkSVCI0srFw5ZlpOHR6ra9rIuy7Izs3OCIDovwajaxoRCAs+tLsx/K5IlUB/KheMhn1cUpVAoeOFYW1//bLU2Njf/vTWQz493ZUlIp+KUbVDcOGq12ttblWGY10ql2WwOh7jL5dLpy5GR8Enq9KlYdApCnlXKT8+POWgu5rPv1cofRBsHPM3Z2zuahrlGNE1ns7dQGQShadrS8kooxNA0A3lxcRkqbhhYerCPo8SW2Xx2sA3uQaLYOJ5fXiRJAgZkhCCKpXJ5EMdePNFofMC6BjRTq9f3D5I2DqqL+nNwdZHiW59mM8+x99dp8uYg0k9BA3O+TFuEWXH103A47ITjOM5s3rlipyfHsZ929NFIZCwaJS21xXE8/2V4KjbiELkKVJ1WPUiPdrs9vNn4PwjrgihJFL+MXTW9h4IB+h7S9dlPu7rdyswHmHIYu/9+6jECjoAj4Ag4Ao6Aw4/4EWAAQli1pv6ysJUAAAAASUVORK5CYII=) -} - -.scrollbar::-webkit-scrollbar-button:vertical:start:double-button:decrement { - height: 16px; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAQCAIAAAANnGG+AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAkFJREFUeNq8VW1r01AUzr1JV9Yp/gn9LH7pz5r4K/wFwpwfRIZDEHzZ2Oa0ohbpRLp1trJuLRY2kG4raZPlzZT0Xk9yk+vNmsQIq4fkpufk4d6n5+UJqtVq5XKZThkhhK3sB7NOp5Mf/OrL8PbNGyw4gWvCLmbEg5sFAwCWZmY0wYk9qICYJQ9K/8qP/gceiqJEVaMkrFpshVjwwsck83BdN/95hmHkBzu2hSQJhV70lFACD9u2t95su+44z76qqi7evWcYZlZdEDsQGbr2+MH9X47lOyhiglLq8q3ZdByn1Wrl4fFkZWU4Gj1dXc3A8BM/vn1pW0a1shaxSO/Tfr9/fHwiy/KPXm8wGGST2Gs0qtXPc3OFd5X3B+32dEJEr9c9OPzeAHC7Wf950rtEMcYDmqe+u4cxAsMY1+u7EEkj4Xnew+VHiiJjLMO6tLQMkSQafupBL7bXn3Hwh83noCEilRiPw6Mjy7KAAwrMMM1Ot5vG4/Xa+tnZOewbkJb7p6cbm1vx+aQo6oOdTxV9NORgXVP3v1bF4iBRTyEHvL+4LEKvJOppoVCYJqdpGge/2FHv3AI9pSCa86XStYUFL9RTfx1pmq5f+E4wxoq4C0SnpTotH+PxOBsc/B/k5wVJlmleBKd65I+0S+HY+IAZ6phESUwlEB9gUTnS5/aqjCWMMD0NjU4IN8ruLD29ym8dFfqW0rTvoFIsFi+1p6iGrPzc/Sfw9RKIBSYEhlYiMoKGUAgifpPiICsYupElBZbfAgwAIexg91QD5VEAAAAASUVORK5CYII=) -} - -.scrollbar::-webkit-scrollbar-button:vertical:end:decrement { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAaCAIAAACsB0LYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAi5JREFUeNrsl81O20AQx+O1g2Tg2nt77IFblcei6kP0CXqhVD1UqKgSBwQIaEqLRFqlEg2hTaoARkQKUpVAFOzUX7VrezvxxM7GscGnlINH9no9+Wv3t+PdzSxXLpcLhQKdMM/zsMQKmiRJ6cVrnztPHj9ApwuXixea58CNTl9AcvfDMo6MI+PIODKOe8/BcdzohcY8KL2Lw7Ks9P2pqno3R2CmoTO95+gtHIZh7L4vWpadBqLX6y0+faaqWhwIiQRBVZTXL57/MXUGYFiN4fhRq5mmWa/X03C8WVm5keW3q6sJ8aDYEz72i+uGrpY+bI4w6FAQ5Wi3263WJc/zF81mt9u9HaJ6fFwqfZmZye99/NQ4OYnhYIZ9ITVOf1ZB3Kh9+3XZ9AFoGJAxDshHKkdVQqAFjhBSqRyBJwnCcZyXy68EgSeEh3JpaRk8Yxw5jgYDdt2/xa13oXh/Z811ncR5enp2pus6MHC+qZomnZ8ncWxsbl1dXUO7PjTf7nS2d3bH4xF+f/r1YK8v34TivtL7fnjAzh6OzQshBv4no1iiwVyJzQvz+fwknKIooXjjUFl4NI95oSjOzs3NQz7oDBLBQV4oy3K//xuTQkgmBbYV+H0y5UyKh23bKcUwLE3TFOg1SE79wpv6PhaslzG4cI7S/7Sv0xHRaPmSKfTK7uE06qNT+Z+j7JiHK2BiTw/WiyAI7KEIF0t4IsIKOkVRTC9u2Q9REj10RZ0D+T8BBgBcvEtZt9pBOAAAAABJRU5ErkJggg==) -} - -.scrollbar::-webkit-scrollbar-button:vertical:end:increment { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAaCAIAAACsB0LYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAjlJREFUeNpiPHbsmJmZ2X8M8O/fPwgJYUDArVu3iFe8+vALE01RiOBfIPoLQRDw7w8QQwTBCpgYaAYYGRkRnP9YqP//EfI0dAdJYNQdo+4YdceoO0bdMejdgVK/MGBUNahsWoYHIxN65fYfbvF/NLfQOjz+Q2z6D69f//+HOAjmCKgCFmSdV65evXD+AqxSBgFdXR0NdXWs1qxctXrBgoUM8MD//z8mJjowwB/FHTD7Du3dunfrWmTFlo7expaO8ABBCQ9dHR0BQQEWGBAUFNTX08Pl3ciIcAVFBQ4YUFJWio2JRgkPBsb/MA+7ewdJyyIUS8kq2Ll442x/MDExmZqaMoEB0DcW5ubAlhIudwAdmp2VxczMDGQAyYK8vD9//qDGCyJd/PjxMzgqGa7YPzwRKIKcUlDcAWyvSUlKysvJAmNESUlRTEwUKILLHb9+/TIyNLCztQYqdnF20tbWAoowYHMIJJbFpeT1jS2BDCNzWz5Bsf+Q1AJzCno6BfrJwMCAi4vT0MAA3VwM8PPnz8SEBGFhocSE+C9fvuBSBrHsx8+fzl7BfPyCdi7+v37+JFx+cHFxeXp4sLOzE5MphIWFJ03o5+XlxVd+wPILAyNzdmnz7z9/EY1TWN5hwdQPDBKgI35iOBkr+PHjB9ARX79+JUYxMJa/fvsDbKBjlCL/sZcfv3//Jr6c+P79Ox7Z/wwobfO/oISPLPafLvULSpHF8P//fxzlO43dAUkYMBLJZWjF+n8GgAADACYZV6607bIjAAAAAElFTkSuQmCC) -} - -.scrollbar::-webkit-scrollbar-button:vertical:end:double-button:increment { - height: 16px; - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAQCAYAAACC/vbpAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAACxMAAAsTAQCanBgAAAKKSURBVEjHzVVNTxNRFD1vZpKpTYZQqxgglVITMVJgSiSFGDAoRkgX4EeCCkYad2iIG/+Be42/AHd+xC27rlwTWditf8LWzkzn3euipXQ+2oG6qC+5ee/Nu/e9Myf3nisODw85l8sBAJjZM/u/+c/L5TJ6jf1U+gXz6gUQMSTJxiypsZbUXBOIJKRkEBOIGEwEzbZtEFHrYr8dnxFRwO9fYn//ceDUj4ERpJSQkuBKCdm2P54bdzTAK+jnEE1r2wshQpy8o7+gexxKP0n28NjGsPAx3DgS/wHTQpwhc7w5pPmdf5bLOPpxBG5TAGbG1FQW1yYmuj70+ctX7O9/9AJixvb2Fu5vrPswCw/C76UDlA6+BWIXlguYnV8++QsOYXoqm8VgYhCaprUskUhgZno6krwnjzeRHk8jFou1LHMlg2fbWyFEe3m8V3iA0ZQ3diSVxtJKITqnFUXB3NwcFEWBoigQQmA+nwcRRYLWNA0vd3ehqio0TYOqqni9twfXdSPTw7JtPHz6whO7vlmEZdmBGgiAllJiZHgYY5dTYGZkMuMYGroIKWUkaMdxMJszsbR4E8yMlTu3MTl5HY7jBNkSwpO9DMal0THM3FgAM2M2v4iBxJCnWYluhei6LkzTRDx+DjnTDH2007BtG8WdHSST51HceY5KpdKxEIWHcAHbtrGy9ggDgwncuruBuuMAEIGa7age8Xgca6ur0HX9zMKQTCbx4f07GIbRRTxEoG8IAFBUvHrzFnVXhsid6A7adV3oun4mllv5aVkwDAPVavV0oiZOtJmkRLVmgZh8Snfi21Wn6/V6zzJcq9UiZDpEhZv/wa2iF6EdqX8d8RTNpdO+7208nG5fq/c5/wXoLm0sjsumxAAAAABJRU5ErkJggg==) -} - -.scrollbar::-webkit-scrollbar-button:vertical:start:increment { - background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAaCAIAAACsB0LYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAmZJREFUeNpiPHbsGAsLy38kwMDAACT//fsH4UIYEEFOTk7iFT/8pQBR8h8V/EMXBClnARJGRkb/MQDExH9gABe8desW8Yofnv6oq8DzFyz4FwT+/QERUDaEhOgAQiYGmgJGMIKxGRkZUeUQgMbuIBqMumNwuoMFmXPl6tUL5y/8B7MhaV5XV0dDXR2rzpWrVi9YsJABnvT+/4+JiQ4M8Icr+P7tKwMDNyM4RR7et33vtrXIii0dvY0sHEEijMBMjBoeujo6AoICLDAgKCior6eHyweREeEKigocMKCkrBQbE42SV2C2Ail37yBpWYRiKVkFOxdvnPHCxMRkamrKBAZAUyzMzYG5G2dIsrBkZ2UxMzMDGUCyIC/vz58/qJkWkTN//PwZHJUMV+wfnvjjx0/kvIviDmDJIiUpKS8nC4wRJSVFMTFRoAgud/z69cvI0MDO1hqo2MXZSVtbCyiC4iuoO0BlCDDoxaXl9U0sgYqNzG35BMUg5SjcKejpFOgnAwMDLi5OQwMDNHMxwc+fPxMTEoSFhRIT4r98+YJRiIEcAo8coGIXzxA+AUF714DfIJNRSjUs+YWLi8vTw4OdnZ2YdC4sLDxpQj8vLy+WshSUBlGLTybmnNKW33/+wiKNESGDqR8YJEBHEAwMaMT/+AF0xNevX/GV6lD3MAIrla/ffwBrE6TCnhFLvoWD379/E5/1v3//jlWclZUVWrv++w+pzoDsP9DK7T8YQcT+j5ano+4YdQf59S11wc8f3xkZeUClOxOwXGf6D6plGZjBTWQGcDsZEgr/Gf4x/mMcjZdRd+ADAAEGAE4tsrGq30G3AAAAAElFTkSuQmCC) -} - -div.progress-spinner { - background: url(progress-spinner.png) 0 0 no-repeat; - -webkit-background-size: 20px 20px; - opacity: 1; - width: 20px; - height: 20px; - -webkit-animation-name: 'generic-loading-spinner-animation'; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -webkit-animation-duration: 1s; - float: left; - position: relative; - top: 6px; - left: -14px; -} - -p[data-text-truncate-lines] { - height: auto; - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - padding: 0 !important -} - -[data-text-truncate-lines] a.text-truncate-toggle { - color: currentcolor; - padding-right: 5px; - display: inline-block -} - -[data-text-truncate-lines] a.text-truncate-toggle:after { - content: "\25BC"; - font-size: 9px; - margin-left: 5px; - color: currentcolor -} - -[data-text-truncate-lines].data-text-truncate-opened a.text-truncate-toggle { - float: right -} - -[data-text-truncate-lines].data-text-truncate-opened a.text-truncate-toggle:after { - content: "\25B2" -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='1'] { - -webkit-line-clamp: 1 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='2'] { - -webkit-line-clamp: 2 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='3'] { - -webkit-line-clamp: 3 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='4'] { - -webkit-line-clamp: 4 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='5'] { - -webkit-line-clamp: 5 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='6'] { - -webkit-line-clamp: 6 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='7'] { - -webkit-line-clamp: 7 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='8'] { - -webkit-line-clamp: 8 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='9'] { - -webkit-line-clamp: 9 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='10'] { - -webkit-line-clamp: 10 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='11'] { - -webkit-line-clamp: 11 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='12'] { - -webkit-line-clamp: 12 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='13'] { - -webkit-line-clamp: 13 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='14'] { - -webkit-line-clamp: 14 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='15'] { - -webkit-line-clamp: 15 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='16'] { - -webkit-line-clamp: 16 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='17'] { - -webkit-line-clamp: 17 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='18'] { - -webkit-line-clamp: 18 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='19'] { - -webkit-line-clamp: 19 -} - -p:not(.data-text-truncate-opened)[data-text-truncate-lines='20'] { - -webkit-line-clamp: 20 -} - -div.showcase { - height: 200px; - margin: 0; - opacity: 1; - overflow: hidden; - -webkit-transition: opacity .25s; - -webkit-border-bottom-right-radius: 8px; - position: relative; - left: 0; - margin-right: 0; - -webkit-transform: translate3d(0, 0, 0); - z-index: 5 -} - -div.showcase * { - cursor: pointer !important -} - -div.showcase, div.showcase>div.stage, div.showcase>div.stage * { - -webkit-border-bottom-left-radius: 8px; - -webkit-border-bottom-right-radius: 8px -} - -div.stage>img { - position: absolute; - height: 200px; - left: 0; - /*background-color: #000;*/ - text-align: left; - overflow: hidden; - -webkit-transition: opacity 1s ease-in-out; - opacity: 0; -} - -html { - height: 100%; - margin: 0 -} - -body { - margin: 0; - min-height: 100%; - display: -webkit-box; - -webkit-box-orient: vertical -} - -div#page { - -webkit-box-flex: 1 -} - -div.restart-needed-icon { - display: inline-block; - margin-left: 4px; - width: 11px; - height: 11px; - background:url('RestartReq.png') no-repeat; - background-size: 11px 11px; - position: relative; - top: 2px; -} - -div.logout-needed-icon { - display: inline-block; - margin-left: 4px; - width: 11px; - height: 11px; - background:url('LogOutReq.png') no-repeat; - background-size: 11px 11px; - position: relative; - top: 2px; -} - -div.title div.select { - float: right; - position: relative; - top: 8px; -} - -.warning { - color: var(--text-color-warning) !important; -} - -div.lockup li a.follow, -span a.follow { - visibility: hidden; -} - -div.lockup li.install-requested a.follow, -div.lockup li.will-be-installed a.follow, -div.lockup li.update-will-be-installed a.follow, -div.lockup li.removal-requested a.follow, -div.lockup li.will-be-removed a.follow, -span.install-requested a.follow, -span.will-be-installed a.follow, -span.update-will-be-installed a.follow, -span.removal-requested a.follow, -span.will-be-removed a.follow { - visibility: visible !important; - opacity: 0.8; - display: inline-block; - width: 12px; - height: 12px; - background: url('FollowLink.png') 0 0 no-repeat; - -webkit-background-size: 12px 12px; - position: relative; - left: 2px; - top: 3px; -} - -div.lockup li a.follow:hover, -span a.follow:hover { - opacity: 1; -} - - -div.lockup li.will-be-installed span, -div.lockup li.will-be-removed span, -div.lockup li.install-requested span, -div.lockup li.removal-requested span, -div.lockup li.downloading span, -div.lockup li.preparing-removal span, -div.lockup li.update-will-be-installed span, -div.lockup li.update-available span, -div.lockup li.install-error span, -div.lockup li.removal-error span, -span.install-requested, -span.removal-requested, -span.downloading, -span.preparing-removal, -span.install-error, -span.removal-error, -span.will-be-installed, -span.will-be-removed, -span.update-will-be-installed, -span.update-available, -span.warning { - visibility: visible !important; - color: var(--text-color-warning) !important; -} - -span.downloading>span.progress-spinner, -span.preparing-removal>span.progress-spinner, -span.installing>span.progress-spinner, -span.removing>span.progress-spinner, -div.lockup li.downloading span.progress-spinner, -div.lockup li.preparing-removal span.progress-spinner, -div.lockup li.installing span.progress-spinner, -div.lockup li.removing span.progress-spinner { - visibility: visible !important; - float: right; - background: url(progress-spinner.png) 0 0 no-repeat; - -webkit-background-size: 20px 20px; - opacity: 1; - width: 20px; - height: 20px; - -webkit-animation-name: 'generic-loading-spinner-animation'; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear; - -webkit-animation-duration: 1s; -} - -div.lockup li.downloading span.progress-spinner, -div.lockup li.preparing-removal span.progress-spinner, -div.lockup li.installing span.progress-spinner, -div.lockup li.removing span.progress-spinner { - position: absolute; - bottom: 20px; - right: 20px; -} - -div.lockup li { - max-width: 190px; -} - -div.lockup li.not-installed, -div.lockup li.installing, -div.lockup li.removing { - visibility: hidden; -} - -div.lockup li.installed, -div.lockup li.installed-not-removable { - color: var(--text-color-normal); -} - -#update-warning-text { - font-size: 12px; -} - -/* status results text*/ -div.lockup-container div.status-results { - text-align:center; - padding-top: 100px; - padding-bottom: 100px; - color: var(--text-color-subdued); -} -div.lockup-container div.status-results>h2 { - margin-bottom: 6px; - font-weight: bold; - font-size: 14px -} -div.lockup-container div.status-results>p { - font-size: 12px -} -div.lockup-container div.status-results>p a{ - font-weight: bold; - color: var(--text-color-subdued); -} - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding.png deleted file mode 100644 index fb98570d..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding1.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding1.png deleted file mode 100644 index 61dbd6b0..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding1.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding2.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding2.png deleted file mode 100644 index 8cba8e4b..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/branding2.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/delete-button-sprite.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/delete-button-sprite.png deleted file mode 100644 index 0c6fce09..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/delete-button-sprite.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/detail.css b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/detail.css deleted file mode 100644 index afebc5f0..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/detail.css +++ /dev/null @@ -1,126 +0,0 @@ -/* - ** detail.css for Managed Software Center.app - ** Based on App Store CSS © 2013 Apple Inc. - */ - -body.product.software #content { - max-width: 1397px -} -body.product div.columns { - margin-bottom: 30px -} -body.product.software .main { - margin-top: 8px -} -body.product .main>.product-detail { - margin-bottom: 23px; - min-height: 196px; - margin-right: 10px; - display: -webkit-box; - -webkit-box-orient: horizontal; - -webkit-box-align: stretch -} -body.product .product-detail .lockup { - display: inline-block; - width: 175px; - text-align: center; - padding: 0; - margin-right: 20px -} -body.product .product-detail .lockup div.artwork { - float: none; - margin-bottom: 20px -} -body.product .product-detail .lockup div.artwork img { - width: 140px; - height: 140px -} -body.product .product-detail .product-info { - -webkit-box-flex: 1 -} -body.product .product-detail .product-info .title h1 { - margin-bottom: 12px; - font-family: -apple-system, "Helvetica Neue", Helvetica; - font-size: 24px; - line-height: 22px; - /*font-weight: bold;*/ - color: var(--text-color-emphasized); - /*text-shadow: rgba(255,255,255,1) 0 1px 0*/ -} -body.product .product-detail .product-info .title a { - color: currentcolor -} -body.product .product-detail .product-info .product-review h4 { - margin-bottom: 4px; - font-family: -apple-system, "Helvetica Neue", Helvetica; - font-size: 16px; - font-weight: bold; - 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 { - font-size: 12px; - line-height: 16px; - color: var(--text-color-normal); -} -body.product .product-detail .product-info .product-review p a { - color: var(--description-link-color); -} -body.product .sidebar .app-info .content { - font-size: 11px; - color: var(--text-color-emphasized) -} -body.product .sidebar .app-info ul.list li,body.product .sidebar .app-info .app-rating { - margin-bottom: 5px -} -body.product .sidebar .app-info li .label, -body.product .sidebar .app-info p span.label { - font-weight: normal; - color: var(--text-color-subdued); -} -body.product .sidebar .more-by { - padding-bottom: 0 -} -body.product .sidebar .more-by .content { - margin-top: -6px -} -body.product .sidebar .more-by div.lockup { - display: -webkit-box; - padding: 7px 10px; - border-width: 0 0 1px 0; - height: 55px -} -body.product .sidebar .more-by div.lockup:last-child { - -webkit-border-image: none; - border-bottom-width: 0; - padding-bottom: 0; - -webkit-border-bottom-left-radius: 5px; - -webkit-border-bottom-right-radius: 5px -} -body.product .sidebar .more-by div.lockup .artwork { - width: 35px; - height: 35px; - margin-right: 5px; - display: inline-block; - float: left -} -body.product .sidebar .more-by div.lockup ul { - display: inline-block; - width: 138px -} -body.product .sidebar .more-by div.lockup li { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden -} -body.product .sidebar .more-by div.lockup li.name { - margin-bottom: 1px; - font-size: 11px; - /*font-weight: bold*/ -} -body.product .sidebar .more-by div.lockup li.genre { - color: var(--text-color-subdued); - font-size: 10px -} - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/itemlist.js b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/itemlist.js deleted file mode 100644 index cd81ac82..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/itemlist.js +++ /dev/null @@ -1,11 +0,0 @@ -/* itemlist.js - Managed Software Center - - Created by Greg Neagle on 12/28/13. - */ - -function category_select() { - var e = document.getElementById("category-selector"); - var category = e.options[e.selectedIndex].text; - window.AppController.changeSelectedCategory_(category); -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/progress-spinner.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/progress-spinner.png deleted file mode 100644 index a42843bc..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/progress-spinner.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.css b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.css deleted file mode 100644 index 9616074b..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.css +++ /dev/null @@ -1,596 +0,0 @@ -/* - ** updates.css for Managed Software Center.app - ** Based on App Store CSS © 2013 Apple Inc. - */ - -#page,#wrapper,#content { - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-box-flex: 1; - -webkit-box-align: stretch -} -#content { - padding-top: 20px -} -.main { - -webkit-box-flex: 0 -} -.hidden { - display: none!important; - opacity: 0 -} -.main>div:not(:last-of-type) { - margin-bottom: 20px -} -div.status-results { - border-bottom: 1px solid #d9d9d9 -} -div.installations { - margin: 14px 0; - position: relative -} -div.installations table { - width: 100% -} -div.installations table:not(.no-header):before { - border-width: 0 0 3px 0; - content: ''; - display: block; - left: 0; - position: absolute; - top: 32px; - width: 100%; - z-index: 1 -} -div.installations thead { - border-radius: 5px 5px 0 0; - height: 32px; - border-bottom: 1px solid #d7d7d7; -} -div.installations th { - color: var(--text-color-normal); - font-size: 14px; - font-weight: normal; - height: 32px; - line-height: 14px; - text-align: left -} -#updates div.installations th { - width: 100%; - display: block; - line-height: 32px -} - -#update-count-string { - /*margin-left: -10px*/ -} - -#updates-progress-spinner { - margin-right: 4px; - position: relative; - left: 0px; -} - -div.installations th:first-child { - -webkit-border-top-left-radius: 5px -} -div.installations th:last-child { - -webkit-border-top-right-radius: 5px -} -.installation td { - border-top: 1px solid #fff; - border-bottom: 1px solid #d9d9d9; - padding: 0 10px -} -.installation td,.installation td a { - color: var(--text-color-emphasized) -} -.installation td.install-info-cell,.installation td.install-info-cell a, li.install-info-cell { - color: var(--text-color-subdued); -} -.installation:first-child td { - border-top: transparent -} -.installation div.artwork { - position: absolute; - margin-top: 3px -} -.installation img.artwork { - height: auto; - max-width: 35px; - max-height: 35px; - margin: 0 15px 0 5px; - width: 35px -} -.installation h2 { - color: var(--text-color-emphasized); - font-size: 13px; - font-weight: normal; - overflow: hidden; - text-overflow: ellipsis; - max-width: 300px -} -.installation .error { - color: var(--text-color-warning); -} -.installation .status span { - display: block -} -.installation .install-button:not(button),.installation .install-button button,.adoption .install-button,.adoption #os-and-app-updates, { - overflow: hidden; - -webkit-border-radius: 5px -} -#other-updates #os-updates,#os-and-app-updates #os-updates, -#os-and-app-updates #app-updates, div.subscribed-items { - margin: 0; - -webkit-border-top-left-radius: 0px; - -webkit-border-top-right-radius: 0px; - -webkit-box-shadow: none -} -#other-updates #os-updates,#os-and-app-updates #os-updates { - /*border-bottom: 1px solid #d9d9d9*/ -} -#other-updates #os-updatesth:last-child,#os-and-app-updates #os-updates th:last-child { - width: 100%; - display: block; - padding-right: 0 -} -#os-and-app-updates th .scan-progress { - position: relative; - top: -1px -} -#os-and-app-updates th .scan-progress .activity-indicator { - top: 6px; - left: 0 -} - -@-webkit-keyframes fade-out-and-remove { - 0% {opacity: 1} - 100% {opacity: 0} -} - -div.purchases .installation td { - height: 41px -} -/*div.purchases .installation:last-child td:first-child { - -webkit-border-bottom-left-radius: 5px -} -div.purchases .installation:last-child td:last-child { - -webkit-border-bottom-right-radius: 5px -}*/ -div.purchases .installation td:first-child { - width: 225px -} -div.purchases .installation td.hide { - width: 1px -} -div.purchases .installation td:last-child { - width: 200px -} -div.purchases .installation ul.info { - -webkit-box-orient: vertical; - -webkit-box-pack: center; - display: -webkit-box; - height: 100%; - padding-left: 55px; - white-space: nowrap -} - -div.purchases .installation button.hide { - width:19px;height:19px;background:transparent url(delete-button-sprite.png) no-repeat; - -webkit-background-size:19px 38px; - border:0; - opacity:0; - pointer-events:none -} -div.purchases .installation button.hide:hover { - background-position:bottom -} -div.purchases .installation:hover button.hide { - opacity:1; - -webkit-transition:opacity .1s ease-out; - pointer-events:auto -} - -div.purchases .installation td.status span { - visibility: hidden; - line-height: 41px; -} - -div.purchases .installation td.status span.downloading:after, -div.purchases .installation td.status span.preparing-removal:after, -div.purchases .installation td.status span.installing:after, -div.purchases .installation td.status span.removing:after { - margin-top: 11px; -} - -div.purchases .installation td.status span.will-be-removed, -div.purchases .installation td.status span.will-be-installed, -div.purchases .installation td.status span.downloading, -div.purchases .installation td.status span.preparing-removal -{ - visibility: visible; -} - -#updates { - position: relative -} -#updates :focus { - -webkit-focus-ring-color: transparent; - outline: 0 -} -#updates #header { - margin-bottom: 15px; - padding: 40px 0 40px 0 -} -#updates #header h1 { - font-size: 15px; - font-weight: normal; - /*color: #929292;*/ - color: var(--text-color-subdued); - text-align: center -} -div.updates table { - -webkit-box-orient: vertical; - display: -webkit-box -} -div.updates thead,div.updates tbody { - -webkit-box-orient: vertical; - display: -webkit-box; - width: 100% -} -div.updates tr { - display: -webkit-box; - width: 100% -} -div.updates td { - display: block -} -div.updates .installation td { - padding-bottom: 15px; - padding-top: 15px; - white-space: nowrap -} -div.updates .installation td:nth-child(1) { - width: 360px -} -div.updates .installation td:nth-child(2) { - -webkit-box-flex: 1; - white-space: normal -} -div.updates .installation td:nth-child(3) { - vertical-align: bottom; - color: blue -} -div.updates .installation img.artwork { - min-width: 35px -} -div.updates .installation ul.info { - color: var(--text-color-subdued); - font-size: 11px; - line-height: 16px; - margin-left: 55px -} -div.updates .installation ul.info h2 { - color: var(--text-color-normal); - margin-bottom: 1px -} -div.updates .installation ul.info li:nth-child(n+2) { - overflow: hidden; - text-overflow: ellipsis -} -div.updates .installation p.description { - line-height: 16px; - padding-top: 1px -} -div.updates .installation .more-link { - display: block -} -div.updates .installation span.update { - -webkit-box-orient: horizontal; - -webkit-box-align: center; - display: -webkit-inline-box; - vertical-align: top; - min-height: 20px -} -div.updates .installation .status { - display: block; - margin-right: 10px -} -div.updates .all-os-updates td { - white-space: normal -} -@media only screen and (-webkit-min-device-pixel-ratio: 2) {div.artwork .critical-updates-icon { - background: url(da-storefront/images/-dsi-/critical-updates-icon.2x.png); - background-size: 35px 35px; - background-repeat: no-repeat -} - -} -div.updates.os-updates .installation td:last-child { - -webkit-box-pack: center; - padding-top: 15px -} -div.updates.os-updates .installation span.update { - -webkit-box-align: center -} -div.os-updates .installation h2 { - max-width: 100%; - margin-bottom: 5px -} -div.os-updates .installation td { - border: 0 -} -div.updates.os-updates .sub-installation td:last-child { - -webkit-box-pack: start -} -div.os-updates td.description p+p { - margin-top: 10px -} -div.os-updates p.note { - color: #d30 -} -.installation .description a { - color: var(--description-link-color) -} -.installation [data-text-truncate-lines] a.text-truncate-toggle { - padding: 0; - line-height: 14px -} -.installation .data-text-truncate-opened a.text-truncate-toggle { - display: none -} -.all-os-updates .toggle:before,.installation [data-text-truncate-lines] a.text-truncate-toggle:before { - content: "…"; - color: currentcolor -} -.installation [data-text-truncate-lines] a.text-truncate-toggle:after { - display: none -} -div.os-updates tbody tr.sub-installation { - display: none; - background: rgba(0,0,0,.02) -} -div.os-updates.single-update tbody tr.sub-installation { - background: transparent -} -.os-updates.expanded tbody tr.sub-installation,.os-updates.single-update tbody tr.sub-installation { - display: -webkit-box; - position: relative -} -.os-updates.expanded tbody tr.sub-installation { - padding-top: 15px -} -.os-updates.expanded tbody tr:nth-child(2) { - border-top: 1px solid rgba(0,0,0,.16) -} -div.os-updates.expanded tbody tr:nth-child(n+3):before { - position: absolute; - content: ''; - width: auto; - height: 1px; - top: 0; - right: 100px; - left: 240px; -} -div.os-updates.expanded tbody tr.sosumi:before { - display: none -} -.activity-indicator { - font: normal 20px -apple-system, "Helvetica Neue", Helvetica -} -#updates #header .scan-progress { - padding: 0 0 1px 13px -} -#updates #header .scan-progress h1::before { - display: none -} -#updates #header .scan-progress h1 { - padding-left: 0; - margin-top: -9px -} -.scan-progress.small .activity-indicator { - display: inline-block; - left: -10px; - top: 7px; - position: relative; - margin: 2px 0 1px -} -body.scanning .non-scan-progress,body.page-not-ready .non-scan-progress,body:not(.scanning):not(.page-not-ready) .scan-progress { - display: none -} - -.activity-indicator { - -webkit-animation-name: rotatingLoader; - -webkit-animation-duration: .75s; - -webkit-animation-iteration-count: infinite; - -webkit-animation-timing-function: linear -} -@-webkit-keyframes rotatingLoader {0% { - -webkit-transform: rotate(0deg) -} -8.32% { - -webkit-transform: rotate(0deg) -} -8.33% { - -webkit-transform: rotate(30deg) -} -16.65% { - -webkit-transform: rotate(30deg) -} -16.66% { - -webkit-transform: rotate(60deg) -} -24.99% { - -webkit-transform: rotate(60deg) -} -25% { - -webkit-transform: rotate(90deg) -} -33.32% { - -webkit-transform: rotate(90deg) -} -33.33% { - -webkit-transform: rotate(120deg) -} -41.65% { - -webkit-transform: rotate(120deg) -} -41.66% { - -webkit-transform: rotate(150deg) -} -49.99% { - -webkit-transform: rotate(150deg) -} -50% { - -webkit-transform: rotate(180deg) -} -58.32% { - -webkit-transform: rotate(180deg) -} -58.33% { - -webkit-transform: rotate(210deg) -} -66.65% { - -webkit-transform: rotate(210deg) -} -66.66% { - -webkit-transform: rotate(240deg) -} -74.99% { - -webkit-transform: rotate(240deg) -} -75% { - -webkit-transform: rotate(270deg) -} -83.32% { - -webkit-transform: rotate(270deg) -} -83.33% { - -webkit-transform: rotate(300deg) -} -91.65% { - -webkit-transform: rotate(300deg) -} -91.66% { - -webkit-transform: rotate(330deg) -} -100% { - -webkit-transform: rotate(330deg) -} - -} - -#os-updates .installation button.cancel-or-add { - width: 19px; - height: 19px; - background: transparent url(delete-button-sprite.png) no-repeat; - -webkit-background-size: 19px 38px; - border: 0; -} - -#other-updates .installation button.cancel-or-add { - width: 19px; - height: 19px; - background: transparent url(add-button-sprite.png) no-repeat; - -webkit-background-size: 19px 38px; - border: 0; -} - -#os-updates .installation button.cancel-or-add:hover, -#other-updates .installation button.cancel-or-add:hover { - background-position: bottom -} - - -tr.installation.added { - -webkit-animation: fade-in .5s; -} -tr.installation.deleted { - -webkit-animation: fade-out-and-remove .5s; - -webkit-animation-fill-mode: forwards; -} -@-webkit-keyframes fade-in { - 0% {opacity: 0} - 100% {opacity: 1} -} - - -div.updates { - -webkit-transition: all 1s ease-in-out; -} - -div.updates.updating .installation button { - display: none!important; - opacity: 0; -} - -div.progress { - -webkit-appearance: none; - margin-top: 8px; - display: inline-block; - border: 1px solid #a9a9a9; - width: 200px; - height: 8px; - -webkit-box-sizing: content-box; - padding: 1px 3px; - -webkit-border-radius: 8px; - background: var(--background-color); -} - -div.progress > span { - display: block; - position: relative; - height: 100%; - padding: 0 2px; - margin: 0 -2px; - -webkit-border-radius: 4px; - -webkit-box-sizing: content-box; - background: -webkit-gradient(linear,left top,left bottom,from( #4C95DF),to( #3B64C8)); - border-width: 0; - overflow: hidden; - vertical-align: top; - color: transparent; - text-shadow: none; - -webkit-transition: width 1s ease-in-out; -} - -div.progress > span.indeterminate { - -webkit-transition: none; -} - -div.progress > span[style*="width: 100%"], -div.progress > span[style*="width:100%"], -div.progress > span.indeterminate { - -webkit-border-radius: 4px; -} - -div.progress > span.indeterminate > span { - content: ""; - position: absolute; - top: 0; left: 0; bottom: 0; right: 0; - background-image: - -webkit-gradient(linear, left bottom, right top, - color-stop(.25, var(--background-color)), - color-stop(.25, transparent), color-stop(.5, transparent), - color-stop(.5, var(--background-color)), - color-stop(.75, var(--background-color)), - color-stop(.76, transparent), to(transparent) - ); - z-index: 1; - -webkit-background-size: 20px 20px; - -webkit-animation: barber-pole 2s linear infinite; - -webkit-border-radius: 4px; - overflow: hidden; -} - -@-webkit-keyframes barber-pole { - 0% { - background-position: 0 20px; - } - 100% { - background-position: 20px 0; - } -} - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.js b/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.js deleted file mode 100644 index da293291..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/WebResources/updates.js +++ /dev/null @@ -1,81 +0,0 @@ -/* updates.js - Managed Software Center - - Created by Greg Neagle on 12/28/13. -*/ - -Element.prototype.getElementHeight = function() { - if (typeof this.clip !== "undefined") { - return this.clip.height; - } else { - if (this.style.pixelHeight) { - return this.style.pixelHeight; - } else { - return this.offsetHeight; - } - } -} - -function showOrHideMoreLinks() { - var elements = document.getElementsByClassName('description'); - for (var i = 0; i < elements.length; i++) { - var truncated = (elements[i].getElementHeight() < elements[i].scrollHeight); - var more_link = elements[i].getElementsByClassName('text-truncate-toggle')[0]; - if (truncated && more_link.classList.contains('hidden')) { - more_link.classList.remove('hidden'); - } - if (!(truncated) && !(more_link.classList.contains('hidden'))) { - more_link.classList.add('hidden'); - } - } -} - -function fadeOutAndRemove(item_name) { - /* add a class to trigger a CSS transition fade-out, then - register a callback for when the transition completes so we can remove the item */ - update_table_row = document.getElementById(item_name + '_update_table_row'); - update_table_row.classList.add('deleted'); - update_table_row.addEventListener('webkitAnimationEnd', - function() { - window.AppController.updateOptionalInstallButtonFinishAction_(item_name) - }); -} - -function registerFadeInCleanup() { - /* removes the 'added' class from table rows after their - fadeIn animation completes */ - window.addEventListener('webkitAnimationEnd', - function(e) { - if (e.target.classList.contains('added')) { - /* our newly-added table row */ - e.target.classList.remove('added'); - } - }); -} - -function registerMoreLinkClick() { - /* add an event listener for the More links */ - window.addEventListener('click', - function(e) { - if (e.target.classList.contains('text-truncate-toggle')) { - description = e.target.parentNode; - description.style.webkitLineClamp = "100%" - e.target.classList.add('hidden'); - e.preventDefault(); - } - }); -} - -window.onload=function() { - showOrHideMoreLinks(); - registerFadeInCleanup(); - registerMoreLinkClick(); -} - -window.onresize=function() { - showOrHideMoreLinks(); -} - -window.onhaschange=function() { - showOrHideMoreLinks(); -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/authrestart.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/authrestart.py deleted file mode 100644 index 3ec2ee44..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/authrestart.py +++ /dev/null @@ -1,168 +0,0 @@ -# -*- coding: utf-8 -*- -# -# authrestart.py -# Managed Software Center -# -# Created by Greg Neagle on 4/17/17. -# Copyright (c) 2018-2019 The Munki Project. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the 'License'); -# you may not use this file except in compliance with the License. -# 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. -""" - authrestart.py - - Created by Greg Neagle on 2017-04-15. - - Routines for communicating with authrestartd. - Socket communications code adapted from autopkg's PkgCreator by Per Olofsson -""" - -import os -import plistlib -import select -import socket -import sys - - -AUTHRESTARTD_SOCKET = "/var/run/authrestartd" - - -class AuthRestartClientError(Exception): - '''Exception to raise for errors in AuthRestartClient''' - pass - - -class AuthRestartClient(object): - '''Handles communication with authrestartd daemon''' - def connect(self): - '''Connect to authrestartd''' - try: - #pylint: disable=attribute-defined-outside-init - self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - #pylint: enable=attribute-defined-outside-init - self.socket.connect(AUTHRESTARTD_SOCKET) - except socket.error as err: - raise AuthRestartClientError( - "Couldn't connect to authrestartd: %s" % err.strerror) - - def send_request(self, request): - '''Send a request to authrestartd''' - self.socket.send(plistlib.writePlistToString(request)) - with os.fdopen(self.socket.fileno()) as fileref: - # use select so we don't hang indefinitely if authrestartd dies - ready = select.select([fileref], [], [], 2) - if ready[0]: - reply = fileref.read() - else: - reply = '' - - if reply: - return reply.rstrip() - else: - return "ERROR:No reply" - - def disconnect(self): - '''Disconnect from authrestartd''' - self.socket.close() - - def process(self, request): - '''Send a request and return the result''' - try: - self.connect() - result = self.send_request(request) - finally: - self.disconnect() - return result - - def fv_is_active(self): - '''Returns a boolean to indicate if FileVault is active''' - result = self.process({'task': 'verify_filevault'}) - return result.startswith('OK') - - def verify_user(self, username): - '''Returns True if username can unlock the FV volume''' - request = {'task': 'verify_user', 'username': username} - result = self.process(request) - return result.startswith('OK') - - def verify_recovery_key_present(self): - '''Returns True if plist containing a FV recovery key is present''' - request = {'task': 'verify_recovery_key_present'} - result = self.process(request) - return result.startswith('OK') - - def verify_can_attempt_auth_restart(self): - '''Returns True if we are ready to attempt an auth restart''' - request = {'task': 'verify_can_attempt_auth_restart'} - result = self.process(request) - return result.startswith('OK') - - def store_password(self, password, username=None): - '''Stores a FV password with authrestartd''' - request = {'task': 'store_password', 'password': password} - if username: - request['username'] = username - result = self.process(request) - if not result.startswith('OK'): - raise AuthRestartClientError(result) - - def restart(self): - '''Returns True if restart was successful''' - result = self.process({'task': 'restart'}) - if not result.startswith('OK'): - raise AuthRestartClientError(result) - - -# Higher-level wrapper functions that swallow AuthRestartClientErrors -def fv_is_active(): - '''Returns True if FileVault can be verified to be active, - False otherwise''' - try: - return AuthRestartClient().fv_is_active() - except AuthRestartClientError: - return False - - -def verify_user(username): - '''Returns True if user can be verified to be able to perform an - authrestart, False otherwise''' - try: - return AuthRestartClient().verify_user(username) - except AuthRestartClientError: - return False - - -def verify_recovery_key_present(): - '''Returns True if we have a plist with a FileVault recovery key, - False otherwise''' - try: - return AuthRestartClient().verify_recovery_key_present() - except AuthRestartClientError: - return False - - -def verify_can_attempt_auth_restart(): - '''Returns True if we have what we need to attempt an auth restart''' - try: - return AuthRestartClient().verify_can_attempt_auth_restart() - except AuthRestartClientError: - return False - - -def store_password(password, username=None): - '''Stores a password for later authrestart usage. - Returns boolean to indicate success/failure''' - try: - AuthRestartClient().store_password(password, username=username) - return True - except AuthRestartClientError: - return False diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da-storefront-autoinstallations.css b/code/apps/pyobjc/Managed Software Center/Managed Software Center/da-storefront-autoinstallations.css deleted file mode 100644 index 70ab8f32..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da-storefront-autoinstallations.css +++ /dev/null @@ -1,236 +0,0 @@ -/* -** Copyright © 2013 Apple Inc. -** All rights reserved. -*/ - -#page,#wrapper,#content{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-flex:1;-webkit-box-align:stretch} -#content{padding-top:20px} -.main{-webkit-box-flex:0} -.hidden{display:none!important} -.main>div:not(:last-of-type){margin-bottom:20px} -div.installations{margin:14px 0;position:relative} -div.installations table{width:100%} -div.installations table:not(.no-header):before{-webkit-border-image:url(da-storefront/images/-dsi-/container_rule.png) 0 0 100% 0 stretch stretch;border-width:0 0 3px 0;content:'';display:block;left:0;position:absolute;top:32px;width:100%;z-index:1} -div.installations thead{border-radius:5px 5px 0 0;padding:0 0 0 10px;height:32px;border-bottom:1px solid #d7d7d7;text-shadow:0 1px 0 #fff;background:-webkit-gradient(linear,left bottom,left top,from( #e3e3e3),color-stop(0.49, #ededed),color-stop(0.5, #f4f4f4),to(#fff))} -div.installations .xs{opacity:0} -div.installations th{color:#53565e;font-size:14px;font-weight:bold;height:32px;line-height:14px;padding:0 10px;text-align:left} -#updates div.installations th{width:100%;display:block;line-height:32px} -div.installations th:first-child{-webkit-border-top-left-radius:5px} -div.installations th:last-child{-webkit-border-top-right-radius:5px} -div.installations tbody tr:nth-of-type(even){background:-webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))} -div.claim tr p,div.adoption tr p{padding:16px 6px;line-height:16px} -.installation td,.adoption td{border-top:1px solid #fff;border-bottom:1px solid #d9d9d9;padding:0 10px} -.installation td,.adoption td,.installation td a,.adoption td a{color:#797979} -.claim .installation td:nth-child(2){width:711px} -.installation:first-child td{border-top:transparent} -.installation td:last-child{min-width:80px;padding-left:0;text-align:right} -.installation td:last-child a{white-space:nowrap} -.installation div.artwork{position:absolute;margin-top:2px} -.installation img.artwork{height:auto;max-width:35px;max-height:35px;margin:0 15px 0 5px;width:35px} -.installation h2{color:#565656;font-size:13px;font-weight:bold;overflow:hidden;text-overflow:ellipsis;max-width:300px} -.installation h2 a{color:inherit} -.installation .status{text-align:right;font-size:10px;line-height:13px} -.installation .error{color:#f00} -.installation .status span{display:block} -.installation .install-button{display:inline-block;margin:2px 0} -.installation .install-button{min-width:80px} -.installation .install-button:not(button),.installation .install-button button,.adoption .install-button,.adoption .install-button button{min-width:80px;padding:0 0 1px} -.installation .progress{-webkit-appearance:none;display:inline-block;border:1px solid #a9a9a9;width:200px;height:4px;-webkit-box-sizing:content-box;padding:1px 3px;-webkit-border-radius:8px;background:#fff} -.installation .progress span{display:block;height:4px;padding:0 2px;margin:0 -2px;-webkit-border-radius:4px;-webkit-box-sizing:content-box;background:-webkit-gradient(linear,left top,left bottom,from( #4C95DF),to( #3B64C8));border-width:0;overflow:hidden;vertical-align:top;color:transparent;text-shadow:none} -#os-and-app-updates,#auto-update-history{overflow:hidden;-webkit-border-radius:5px} -#os-and-app-updates #os-updates,#os-and-app-updates #app-updates{margin:0;-webkit-border-radius:0;-webkit-box-shadow:none} -#os-and-app-updates #os-updates{border-bottom:1px solid #d9d9d9} -#os-and-app-updates #os-updates th:last-child,#os-and-app-updates #app-updates th:last-child{width:100%;display:block;padding-right:0} -#os-and-app-updates th .scan-progress{position:relative;top:-1px} -#os-and-app-updates th .scan-progress .activity-indicator{top:6px;left:0} -#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(even){background:0} -#os-and-app-updates #os-updates:not(.hidden)+#app-updates tbody tr:nth-of-type(odd){background:-webkit-gradient(linear,left top,left bottom,from( #f3f3f3),to( #eaeaea))} -#auto-update-history tr{min-height:97px} -#auto-update-history .installation .info h2{margin-bottom:10px;overflow:visible} -#auto-update-history .installation .description{margin-top:26px} -#purchases{text-shadow:#fff 0 1px 0} -div.purchases .installation td{height:41px} -div.purchases .installation.removing{pointer-events:none;-webkit-animation:fade-out-and-remove .5s;-webkit-animation-fill-mode:forwards} -@-webkit-keyframes fade-out-and-remove{0%{ opacity:1} -100%{opacity:0} -} -div.purchases .installation.removed{display:none} -div.purchases .installation.restored td:not(.status){opacity:.3} -div.purchases .installation.restored td:last-child button{opacity:.3} -div.purchases .installation.restored td:last-child,div.purchases .installation.restored td:last-child .update button{opacity:1} -div.purchases .installation:last-child td:first-child{-webkit-border-bottom-left-radius:5px} -div.purchases .installation:last-child td:last-child{-webkit-border-bottom-right-radius:5px} -div.purchases .installation td:first-child{width:225px} -div.purchases .installation td.hide{width:19px} -div.purchases .installation td:last-child{width:80px} -div.purchases .installation ul.info{-webkit-box-orient:vertical;-webkit-box-pack:center;display:-webkit-box;height:100%;padding-left:55px;white-space:nowrap} -div.purchases .installation button.hide{width:19px;height:19px;background:transparent url(da-storefront/images/-dsi-/elements/delete-button-sprite.png) no-repeat;-webkit-background-size:19px 38px;border:0;opacity:0;pointer-events:none} -div.purchases .installation button.hide:hover{background-position:bottom} -div.purchases .installation:hover button.hide{opacity:1;-webkit-transition:opacity .1s ease-out;pointer-events:auto} -#updates{position:relative} -#updates :focus{-webkit-focus-ring-color:transparent;outline:0} -#updates #header{margin-bottom:15px;padding:40px 0 40px 0} -#updates #header h1{font-size:15px;font-weight:bold;color:#929292;text-shadow:0 1px 0 #fff;text-align:center} -div.updates table{-webkit-box-orient:vertical;display:-webkit-box} -div.updates thead,div.updates tbody{-webkit-box-orient:vertical;display:-webkit-box;width:100%} -div.updates tr{display:-webkit-box;width:100%} -div.updates td{display:block} -div.updates .installation td{padding-bottom:15px;padding-top:15px;white-space:nowrap} -div.updates .installation td:nth-child(1){width:240px} -div.updates .installation td:nth-child(2){-webkit-box-flex:1;white-space:normal} -div.updates .installation td:last-child{-webkit-box-orient:vertical;-webkit-box-pack:start;-webkit-box-align:end;padding-top:35px;display:-webkit-box;min-width:320px;text-align:right} -div.updates .installation img.artwork{min-width:35px} -div.updates .installation ul.info{color:#62676C;font-size:11px;line-height:16px;margin-left:55px} -div.updates .installation ul.info h2{color:#565656;margin-bottom:1px} -div.updates .installation ul.info li:nth-child(n+2){overflow:hidden;text-overflow:ellipsis} -div.updates .installation p.description{line-height:16px;padding-top:1px} -div.updates .installation .more-link{display:block} -div.updates .installation span.update{-webkit-box-orient:horizontal;-webkit-box-align:center;display:-webkit-inline-box;vertical-align:top;min-height:20px} -div.updates .installation .status{display:block;margin-right:10px} -div.updates .all-os-updates td{white-space:normal} -div.artwork .os-icon{display:block;width:35px;height:35px;margin:-1px 15px 0 5px;background:url(da-storefront/images/os-icon.png);-webkit-background-size:35px 35px;background-size:35px 35px;background-repeat:no-repeat} -div.artwork .os-icon.mavericks{background:url(da-storefront/images/os-icon-mavericks.png);background-size:35px 35px;background-repeat:no-repeat} -div.artwork .critical-updates-icon{display:block;width:35px;height:35px;margin:-1px 15px 0 5px;background:url(da-storefront/images/critical-updates-icon.png);background-size:35px 35px;background-repeat:no-repeat} -@media only screen and (-webkit-min-device-pixel-ratio: 2){div.artwork .critical-updates-icon{ background:url(da-storefront/images/-dsi-/critical-updates-icon.2x.png);background-size:35px 35px;background-repeat:no-repeat} -} -body:not(.show-incompatible) .installations.incompatible,body:not(.show-incompatible) .installation.incompatible{display:none!important} -.show-incompatible-link{text-decoration:none} -.show-incompatible-link:hover{text-decoration:underline} -.show-incompatible-row td{width:100%} -.show-incompatible-row .show-incompatible-link{padding:10px;display:block;text-align:center;color:#666} -div.os-updates .os-update-action{color:rgba(0,0,0,.33)} -div.os-updates .info .os-update-action{padding-right:15px;background:url(da-storefront/images/restart_required.png) 100% 1px no-repeat;background-size:11px 11px} -div.updates.os-updates .installation td:last-child{-webkit-box-pack:center;padding-top:15px} -div.updates.os-updates .installation span.update{-webkit-box-align:center} -div.os-updates .installation .progress{width:200px} -div.os-updates .installation h2{max-width:100%;margin-bottom:5px} -div.os-updates .installation td{border:0} -div.updates.os-updates .sub-installation td:last-child{-webkit-box-pack:start} -div.os-updates td.description p+p{margin-top:10px} -div.os-updates p.note{color:#d30} -div.os-updates p.note+p{margin-top:10px} -div.os-updates p.note a.help-link{font-weight:bold;display:inline-block;height:16px;width:16px;vertical-align:middle;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAp9JREFUeNrUVzuPElEUnhnG1YFMgKxLxIBRCwM0FDaEau0NLbGk0R+gNUqvoXb/wAZKSyhcG6DZAgpDKEwUDBsIOmTCzgQS8RxzxtyFeTEgG0/yFTNz73znnvflOfciAYIAGRAA3AIc0Lc5QAfMACpgCtDc/JR3sSYEOAIcEqkbQWUmgDFA8aoAnjgKuMOcdFNBy1wAhlYW8VlsDAPukwI+zrv4yG14mAVZxlGB24AHZPpdiZ/iBpW4tFMgTOQyt3u5SZbQWUuIKz6POZHXarWnyWTycSwWO2bfdzqd026328vn82c222Xi0I2YYIPwIeCe1c5CoRAtl8tvgsHgIzsFx+PxeS6XK7VaLdVm2TfAF1YB9HfKLtoVRXnvRG7IdDrthUKhFw7Z8Rl/K9CLIzvyRqPxbJW8Wq2WeJ5/gkDzs99wbaVSObZR4IA4OYF8f2h3olQqdeVn9Xr9HevrdDp9gqdm1yQSCSdrIackUJ5aVrhMJiOvnr5YLH5as+l8fsXnkiQ5ZRJyBkU3KddsNk/YZ7MACwQCd9lnTdNUF+Eii1QgLAXJstnsqd2adrv93O/3R1fi5tyFAgHMgswGTWZN+v3+69WaMBgMzuLxeMlN0xK3aDSmqYl1wCX5n2wQvJKbpSbGSiQSebXJfzy7YLFYfBBFUWbrgkMZNnWBYNYinQTLMkuOZvdA/leB2bZtDmJh6HHrjKeJJ8Fdj3QFGiD1ayBHzqlAfXmyyc5er/dyuVx+NDAajd56UAA5NSMNx9Qi9yVz4uQMBRSaXvclF8a4zhai4aau8CgT4jK9F4RpNJP/EblKo9hPq6lYp9FZoil21+RfAT+c7gWXpMgNmud3ZfY1crubkU4a/yJr+LaI9u80Bav/3eV0L9fz3wIMABtp4f4jVHJPAAAAAElFTkSuQmCC) no-repeat;background-size:16px 16px;overflow:hidden;margin-left:3px;position:relative;top:-2px} -div.os-updates .sub-installation td.description p{font-size:11px;line-height:16px;margin-left:1.5em} -div.os-updates .sub-installation[single-update] td.description p{margin-left:0;position:relative;top:-10px} -div.os-updates .sub-installation td.description h2{font-weight:normal} -div.updates.os-updates .sub-installation td{padding-top:0} -div.updates.os-updates .sub-installation[single-update] td{padding-bottom:0} -.all-os-updates .toggle{float:right;color:#6D83A2;display:inline-block;text-decoration:none;-webkit-appearance:none;padding:0 5px 0 0;margin:0;border:0;background:transparent;font-family:"Lucida Grande",Helvetica,sans-serif} -.expanded .all-os-updates .toggle,.single-update .all-os-updates .toggle{display:none} -.installation .description a{color:#6D83A2} -.installation [data-text-truncate-lines] a.text-truncate-toggle{padding:0;line-height:14px} -.installation .data-text-truncate-opened a.text-truncate-toggle{display:none} -.all-os-updates .toggle:before,.installation [data-text-truncate-lines] a.text-truncate-toggle:before{content:"…";color:currentcolor} -.installation [data-text-truncate-lines] a.text-truncate-toggle:after{display:none} -div.os-updates tbody tr.sub-installation{display:none;background:rgba(0,0,0,.02)} -div.os-updates.single-update tbody tr.sub-installation{background:transparent} -.os-updates.expanded tbody tr.sub-installation,.os-updates.single-update tbody tr.sub-installation{display:-webkit-box;position:relative} -.os-updates.expanded tbody tr.sub-installation{padding-top:15px} -.os-updates.expanded tbody tr:nth-child(2){border-top:1px solid rgba(0,0,0,.16)} -div.os-updates.expanded tbody tr:nth-child(n+3):before{position:absolute;content:'';width:auto;height:1px;top:0;right:100px;left:240px;background:-webkit-linear-gradient(left,rgba(0,0,0,.04) 0,rgba(0,0,0,.16) 20%,rgba(0,0,0,.16) 80%,rgba(0,0,0,.04) 100%)} -div.os-updates.expanded tbody tr.sosumi:before{display:none} -.os-updates .catalog-source{font-style:italic} -.validation .os-updates{background-color:#ffed7e} -.untrusted .os-updates{background-color:#e94242} -.untrusted .os-updates h2,.untrusted .os-updates p,.untrusted .os-updates .installation ul.info h2{color:rgba(0,0,0,.85)} -.untrusted .os-updates .toggle,.untrusted .os-updates a{color:rgba(0,0,0,.5)} -.os-updates.installations tbody tr.sosumi{background:transparent;padding:0 10px 10px} -.os-updates.installations.expanded tbody tr.sosumi{background:rgba(0,0,0,.02)} -.os-updates.installations tbody tr.sosumi td{width:100%;border-top:1px solid rgba(0,0,0,.10);padding:10px 0 0 240px;color:#797979} -.os-updates.installations tbody tr.sosumi a{color:#464646} -.activity-indicator{font:bold 20px Helvetica} -#updates #header .scan-progress{padding:0 0 1px 13px} -#updates #header .scan-progress h1::before{display:none} -#updates #header .scan-progress h1{padding-left:0;margin-top:-9px} -.scan-progress.small .activity-indicator{display:inline-block;left:-10px;top:7px;position:relative;margin:2px 0 1px} -body.scanning .non-scan-progress,body.page-not-ready .non-scan-progress,body:not(.scanning):not(.page-not-ready) .scan-progress{display:none} -.centered-message{text-align:center;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-pack:center;-webkit-box-align:center;-webkit-box-flex:1;padding-top:20px;padding-bottom:20px} -.centered-message h1,.centered-message a,.centered-message span{-webkit-background-clip:text;background-color:rgba(0,0,0,.6);color:transparent;font:bold 20px Helvetica;text-shadow:rgba(255,255,255,.7) 0 1px 3px} -.centered-message span{cursor:pointer} -.centered-message .activity-indicator{display:inline-block;vertical-align:top;margin-top:1px} -.actions{text-align:right} -.centered-message .actions{margin-top:10px;text-align:center} -.centered-message .addendum{font-size:11px;text-align:center;margin:10px -50px 0 -50px;padding:22px 50px 0 50px;background-image:-webkit-linear-gradient(left,rgba(0,0,0,0) 0,rgba(0,0,0,.25) 50%,rgba(0,0,0,0) 100%),-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,.67) 50%,rgba(255,255,255,0) 100%);background-repeat:no-repeat,no-repeat;background-position:0 0,0 1px;background-size:100% 1px,100% 1px} -.centered-message .addendum span,.centered-message .addendum a{font:normal 11px "Lucida Grande";text-shadow:rgba(255,255,255,1) 0 1px 0;color:#666;background:transparent} -div.installations.adoption{margin-bottom:18px} -div.multi-update-all,button.update-all{float:right;position:relative;margin:5px 0 0 0} -div.multi-update-all button.update-all{float:left;-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button.png) 8 30 8 8 stretch stretch;margin-top:0} -div.multi-update-all button.update-all:hover{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-hover.png) 8 30 8 8 stretch stretch} -div.multi-update-all button.update-all:active{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-active.png) 8 30 8 8 stretch stretch} -div.multi-update-all button.update-all.disabled,div.multi-update-all button.update-all[disabled]{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-inactive.png) 8 30 8 8 stretch stretch} -div.multi-update-all select{-webkit-appearance:none;-webkit-focus-ring-color:transparent;-webkit-border-radius:0;-webkit-font-smoothing:antialiased;-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button.png) 8 8 8 0 stretch stretch;border-width:4px 4px 4px 0;width:13px;height:22px;margin-left:1px;cursor:pointer;text-shadow:none;text-indent:-9999px;overflow:hidden;float:left} -div.multi-update-all select:hover{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-hover.png) 8 8 8 0 stretch stretch} -div.multi-update-all select:active{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-active.png) 8 8 8 0 stretch stretch} -div.multi-update-all.disabled select,div.multi-update-all button.update-all[disabled]+select{-webkit-border-image:url(da-storefront/images/-dsi-/elements/install-button-inactive.png) 8 8 8 0 stretch stretch} -div.multi-update-all select:focus{-webkit-focus-ring-color:transparent} -div.multi-update-all:after{content:" ";-webkit-mask-box-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAYAAABr5z2BAAAAIGNIUk0AAHolAACAgwAA9CUAAITRAABtXwAA6GwAADyLAAAbWIPnB3gAAAAJcEhZcwAAFiUAABYlAUlSJPAAAADgSURBVCjPY/j//z8DJZiBWgaw8vHxtTAwMBwE4v0E8EGQWpAeZAO4b9++bSgqKjoXqGAZPgxSc+nSJWOQHmQD2IBYaevWrYns7OxzgApnYcMgOZAakFqoHrgBjEDMBcS61dXVVUDFIJfMRsNzQXJANXpQtYzogcgExIJAbOXi4tIPNWQOFM8FiYHkoGqYcMUCMxBLvnr1yktOTg7k7HkgDGI/efLEByQHVYM3GsHhsWXLliQ2Nrb5IAxiI/ubkAHw8CgtLa0AYXR/E5OQYOGhDcUo/iY2JYL8ygnFzLjUAQAJWMyHkOvUCwAAAABJRU5ErkJggg==);background-color:#fff;width:8px;height:6px;position:absolute;top:8px;right:3px;pointer-events:none;display:block} -#utd-os-updates{background-image:-webkit-linear-gradient(bottom, #e9ede7, #c3d9db);margin-bottom:30px;padding:30px 12px 30px 30px;margin-top:0} -#utd-os-updates .installation td{padding:0;border:0} -#utd-os-updates .installation td:first-child{width:130px} -#utd-os-updates .artwork{position:static;width:101px;height:170px;margin:0;background-image:url(data:image/png;base64,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);background-position:center center;background-repeat:no-repeat;-webkit-background-size:101px 103px} -#utd-os-updates h2{width:207px;height:26px;margin-bottom:20px;text-indent:-210px;background-image:url(data:image/png;base64,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8vdREeuB7xW4SdxZ7zTCP59zzjmtno6pvuIVr/hpCDc86SvKD9UX+aV4vA120aJF18LTlo80VU9/+ctfDgzhtuNIrqnQll2Uvecf//jH3jYO3/NOO+00p2WIpwFtNxzwtQcTzprKlGoXhi7ffbthfILQoOJ5z3ve002BJcxlKXgFWQviuhGWG4UvQV+u19NPP13TagMtoKOujI973OM2QcC8XETkTIQyq+67777lZz7zmaUQvZQnNCXgxQbM4Ih0n332qRxyyCEl6GiLs0eoTMk4DIUjvAUpEAHPNAVcV4eh9wIoprNsxxw47rjj7g21B8Xtscced02lIobo/Y8c8Zx66qkagWikI+WT2ZmZvplAoFYkUM2PKaUqa0Jb8+QnjaZd1jeqfFhc07pMLqUjWkST/QvRh5C+CQE92oaGWDr88MMrIdzwo1dlawR3bbSz3Mdv4Tv22GM1gis3AreRtLNmzZrjw58MZ3SyRDQ3Aj8j7bFJHL73e++996AMWNPR/2EcePrTn37/k5/85BFfm1D/eMELXrBhKhVbysVHL3JvKpHaEC0zXaklDHAtXEOBg0IBz7atVGb5yCOPrHR2dm51wBpRWLlc7uK38Pe//33WdJrwrQH/QXk8S2p0+jxUInIMjc997nPHUIzedQ3ReNttt839zW9+s+APf/jDXOV7yUtecnEEpMV/KLNJGtTws5/97AVXXXXV4gB4rfXknuqjzjRK2s0H76lPfWqZehrRqMuXptlwaNhFHSXvRXpNt63Km96X7t3vfncvimxXX3wyvBEak3mn37dbDky86U1vKn3+85+fRT+vm11Rf2dENDh//vxfTKXSGaXjoml7HvE4FQ9+4wMUqq5ikgV/whOeUHnNa15TYvRgFM44aSb42cJdimYuawpV/RCyXZdeemnv3//+91lJeNZ7l/DznunqSzqt63iV2aMf/ejqsmXLhiwatwBXNNrXXF5miz7u+o1s3ry58/rrrxfctgloYFde+cpXlsHTLUXHu+vqeec739lz/vnnZyofjVCxgOQM4bx22223CdUXkZmwnAByBt5///37uyyxnXfeeZL1n5QhgyOFptuaVjwoHefo0IcXl/7ZOYs0new/hAMYXFuQF+W3ve1tHfS7yujoaCQ7kA+Tz3rWszQTItnVlinooiyU4nH1J8Hbno2nlOJ5xzveofWS4KIv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);background-repeat:no-repeat;-webkit-background-size:207px 26px} -#utd-os-updates p,#utd-os-updates a{margin-bottom:15px;font-size:14px;font-family:'Lucida Grande';text-shadow:0 1px 0 rgba(255,255,255,.5)} -#utd-os-updates p.tagline{font-weight:bold;color:#5e5e5e;line-height:18px} -#utd-os-updates p.blurb{font-size:12px;color:#767676;line-height:17px} -#utd-os-updates a.learn-more{font-size:14px;font-weight:bold;color:#577fa5} -#utd-os-updates .installation td:last-child{-webkit-box-pack:center} -.activity-indicator{height:1em;width:1em;-webkit-transform-origin:.5em .5em;-moz-transform-origin:.5em .5em} -.activity-indicator *,.activity-indicator ::before,.activity-indicator ::after{display:block;position:absolute;width:.107em;height:.3em;top:0;-webkit-transform-origin:.05em .5em;-moz-transform-origin:.05em .5em;border-radius:.1em;-moz-border-radius:.1em;-webkit-border-radius:.2em} -.activity-indicator *{left:50%;margin-left:-.05em} -.activity-indicator>::before,.activity-indicator>::after{content:" "} -.activity-indicator>.top{-webkit-transform:rotate(0deg);-moz-transform:rotate(0deg)} -.activity-indicator>.right{-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg)} -.activity-indicator>.bottom{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg)} -.activity-indicator>.left{-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg)} -.activity-indicator>::before{-webkit-transform:rotate(30deg);-moz-transform:rotate(30deg)} -.activity-indicator>::after{-webkit-transform:rotate(-30deg);-moz-transform:rotate(-30deg)} -.activity-indicator>.top{background-color:rgba(0,0,0,.68)} -.activity-indicator>.top::after{background-color:rgba(0,0,0,.625)} -.activity-indicator>.left::before{background-color:rgba(0,0,0,.57)} -.activity-indicator>.left{background-color:rgba(0,0,0,.515)} -.activity-indicator>.left::after{background-color:rgba(0,0,0,.46)} -.activity-indicator>.bottom::before{background-color:rgba(0,0,0,.405)} -.activity-indicator>.bottom{background-color:rgba(0,0,0,.35)} -.activity-indicator>.bottom::after{background-color:rgba(0,0,0,.295)} -.activity-indicator>.right::before{background-color:rgba(0,0,0,.24)} -.activity-indicator>.right{background-color:rgba(0,0,0,.185)} -.activity-indicator>.right::after{background-color:rgba(0,0,0,.13)} -.activity-indicator>.top::before{background-color:rgba(0,0,0,.075)} -.dark .activity-indicator>.top{background-color:rgba(255,255,255,.68)} -.dark .activity-indicator>.top::after{background-color:rgba(255,255,255,.625)} -.dark .activity-indicator>.left::before{background-color:rgba(255,255,255,.57)} -.dark .activity-indicator>.left{background-color:rgba(255,255,255,.515)} -.dark .activity-indicator>.left::after{background-color:rgba(255,255,255,.46)} -.dark .activity-indicator>.bottom::before{background-color:rgba(255,255,255,.405)} -.dark .activity-indicator>.bottom{background-color:rgba(255,255,255,.35)} -.dark .activity-indicator>.bottom::after{background-color:rgba(255,255,255,.295)} -.dark .activity-indicator>.right::before{background-color:rgba(255,255,255,.24)} -.dark .activity-indicator>.right{background-color:rgba(255,255,255,.185)} -.dark .activity-indicator>.right::after{background-color:rgba(255,255,255,.13)} -.dark .activity-indicator>.top::before{background-color:rgba(255,255,255,.075)} -.activity-indicator{-webkit-animation-name:rotatingLoader;-webkit-animation-duration:.75s;-webkit-animation-iteration-count:infinite;-webkit-animation-timing-function:linear} -@-webkit-keyframes rotatingLoader{0%{ -webkit-transform:rotate(0deg)} -8.32%{-webkit-transform:rotate(0deg)} -8.33%{-webkit-transform:rotate(30deg)} -16.65%{-webkit-transform:rotate(30deg)} -16.66%{-webkit-transform:rotate(60deg)} -24.99%{-webkit-transform:rotate(60deg)} -25%{-webkit-transform:rotate(90deg)} -33.32%{-webkit-transform:rotate(90deg)} -33.33%{-webkit-transform:rotate(120deg)} -41.65%{-webkit-transform:rotate(120deg)} -41.66%{-webkit-transform:rotate(150deg)} -49.99%{-webkit-transform:rotate(150deg)} -50%{-webkit-transform:rotate(180deg)} -58.32%{-webkit-transform:rotate(180deg)} -58.33%{-webkit-transform:rotate(210deg)} -66.65%{-webkit-transform:rotate(210deg)} -66.66%{-webkit-transform:rotate(240deg)} -74.99%{-webkit-transform:rotate(240deg)} -75%{-webkit-transform:rotate(270deg)} -83.32%{-webkit-transform:rotate(270deg)} -83.33%{-webkit-transform:rotate(300deg)} -91.65%{-webkit-transform:rotate(300deg)} -91.66%{-webkit-transform:rotate(330deg)} -100%{-webkit-transform:rotate(330deg)} -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/Localizable.strings deleted file mode 100644 index f198bb84..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s ventende opdateringer"; - -/* One Update message */ -"1 pending update" = "1 ventende opdatering"; - -/* Logout Required detail */ -"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?" = "Du skal logge ud før opdatering. Vent et øjeblik da der kan være en kort forsinkelse ved log ind-vinduet. Log ud og opdater nu?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "En tvungen log ud vil ske ca. %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "En tvungen log ud vil ske om mindre end %s minutter."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "En tvungen log ud vil ske om mindre end et minut.\nAlle ventende opdateringer bliver installeret. Alle uarkiverede oplysninger vil gå tabt."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Forsøg på fjernelse mislykkedes. Fjernelse bliver forsøgt igen.\nHvis problemet består, skal du kontakte systemadministratoren."; - -/* Restart Required detail */ -"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?" = "Computeren skal genstartes efter opdatering. Vent et øjeblik da der kan være en kort forsinkelse ved log ind-vinduet. Log ud og opdater nu?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "En fejl i systemkonfigurationen forhindrer Managed Software Center i at fungere korrekt. Den rapporterede fejl er: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Flere ventende opdateringer"; - -/* AllCategoriesLabel */ -"All Categories" = "Alle kategorier"; - -/* AllItemsHeaderText */ -"All items" = "Alle emner"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Alle ventende opdateringer bliver installeret. Uarkiverede oplysninger vil gå tabt.\nDu kan undgå den tvungne log ud, hvis du selv vælger at logge ud nu."; - -/* Allow button text */ -"Allow" = "Tillad"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Forsøg på installering mislykkedes. Installering bliver forsøgt igen.\nHvis problemet består, skal du kontakte systemadministratoren."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "En ældre version er installeret. Der er ikke nok ledig plads på disken til at downloade og installere denne opdatering."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "En ældre version er installeret. Du skal opgradere til macOS version %s eller nyere for at installere denne opdatering."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Programmer i brug af andre"; - -/* Cancel button title/short action text */ -"Cancel" = "Annuller"; - -/* Cancel Install long action text */ -"Cancel install" = "Annuller installering"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Annuller fjernelse"; - -/* Cancel Update long action text */ -"Cancel update" = "Annuler opdatering"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Kan ikke vise det ønskede emne."; - -/* Categories label */ -"Categories" = "Kategorier"; - -/* Sidebar Category label */ -"Category:" = "Kategori:"; - -/* Check Again button title */ -"Check Again" = "Kontroller igen"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerer kataloget til Apple-softwareopdatering..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Søger efter yderligere ændringer..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Søger efter tilgængelige Apple-softwareopdateringer..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Søger efter tilgængelige opdateringer..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Søger efter opdateringer..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Der er åbne programmer, som giver konflikt med installeringen"; - -/* Continue button text */ -"Continue" = "Fortsæt"; - -/* Critical Update type */ -"Critical Update" = "Kritisk opdatering"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Ikke tilgængelig"; - -/* Deny button text */ -"Deny" = "Afvis"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Undersøger hvilke elementer, der skal fjernes"; - -/* Sidebar Developer label */ -"Developer:" = "Udvikler:"; - -/* managedsoftwareupdate message */ -"Done." = "Færdig."; - -/* Downloading status text */ -"Downloading" = "Henter"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Henter tilgængelige Apple-softwareopdateringer..."; - -/* Sidebar Due label */ -"Due:" = "Forfalder:"; - -/* FeaturedLabel */ -"Featured" = "Udvalgte"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Udvalgt software"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Afslutter..."; - -/* Firmware Alert Default detail */ -"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." = "Firmware bliver opdateret på din computer. Sørg for at din computer er sluttet til strømforsyningen og at denne er sat i stikkontakten. Opdateringen kan være flere minutter om at blive færdig. Lad være med at tage stikket ud under denne opdatering."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Du bør slutte computeren til strømforsyningen før installeringen. Er du sikker på, at du vil forsætte opdateringen?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Tvungen log ud for obligatorisk installering"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samler oplysninger om installeret software"; - -/* No help alert title */ -"Help" = "Hjælp"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Hjælp er ikke tilgængelig for Managed Software Center."; - -/* Sidebar Information label */ -"Information" = "Information"; - -/* Install action text */ -"Install" = "Installer"; - -/* Install Required action text */ -"Install Required" = "Installering krævet"; - -/* Install Requested status text */ -"Install requested" = "Installering ønsket"; - -/* Install Session Failed title */ -"Install session failed" = "Installeringen mislykkedes"; - -/* Install Error status text */ -"Installation Error" = "Fejl under installering"; - -/* Installed status text */ -"Installed" = "Installeret"; - -/* Installing status text */ -"Installing" = "Installerer"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerer tilgængelige Apple-softwareopdateringer..."; - -/* Log out and Update button text */ -"Log out and update" = "Log ud og opdater"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Log ud og opdater nu"; - -/* Logout Required title */ -"Logout Required" = "Log ud krævet"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke søge efter opdateringer.\nPrøv igen senere. Hvis problemet består, skal du kontakte systemadministratoren."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke kontakte opdateringsserveren.\nPrøv igen senere. Hvis problemet består, skal du kontakte systemadministratoren."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Center skal låse startdisken op efter genstart for at færdiggøre alle ventende opdateringer."; - -/* Managed Update type */ -"Managed Update" = "Administreret opdatering"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Obligatoriske opdateringer venter"; - -/* More link text */ -"More" = "Mere"; - -/* Sidebar More By Developer label */ -"More by %s" = "Mere af %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Mere i %s"; - -/* My Items label */ -"My Items" = "Mine emner"; - -/* No Licenses Available display text */ -"No licenses available" = "Ingen tilgængelige licenser"; - -/* No Updates message */ -"No pending updates" = "Ingen ventende opdateringer"; - -/* Item Not Found title */ -"Not Found" = "Findes ikke"; - -/* Not Currently Available display text */ -"Not currently available" = "Ikke tilgængeligt i øjeblikket"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Ikke nok plads på disken"; - -/* Not Installed status text */ -"Not installed" = "Ikke installeret"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Intet at fjerne."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Et eller flere emner skal installeres senest %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "En eller flere obligatoriske opdateringer burde have været installeret. Der vil inden længe ske en tvungen log ud."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "En eller flere opdateringer skal installeres inden %s. Hvis du venter for længe, vil der ske en tvungen log ud."; - -/* Other Available Updates label */ -"Other available updates" = "Andre tilgængelige opdateringer"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Der er andre brugere som anvender følgende programmer. Prøv at opdatere senere når de ikke længere er i brug:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Andre brugere er logget ind"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Fjernelse af software er gennemført."; - -/* Password label */ -"Password:" = "Adgangskode:"; - -/* Pending Updates alert title */ -"Pending updates" = "Ventende opdateringer"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Udfører postflight-opgaver..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Udfører preflight-opgaver..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Forbereder fjernelse"; - -/* Problem Updates label */ -"Problem updates" = "Problematiske opdateringer"; - -/* Quit button title */ -"Quit" = "Slut"; - -/* Removal Error status text */ -"Removal Error" = "Fejl ved fjernelse"; - -/* Removal Requested status text */ -"Removal requested" = "Fjernelse krævet"; - -/* Remove action text */ -"Remove" = "Fjern"; - -/* Removing status text */ -"Removing" = "Fjerner"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Fjerner oplysninger om kvittering"; - -/* Install Required action text */ -"Required" = "Påkrævet"; - -/* Restart button title */ -"Restart" = "Genstart"; - -/* Restart Required title */ -"Restart Required" = "Kræver genstart"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Henter en liste over tilgænglig software til denne computer..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kører Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kører Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kører Adobe Uninstall"; - -/* No Installed Software secondary text */ -"Select software to install." = "Vælg software der skal installeres."; - -/* Sidebar Size label */ -"Size:" = "Str.:"; - -/* Software label */ -"Software" = "Software"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Installeret eller fjernet software kræver genstart."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Den software, der blev installeret eller fjernet, kræver en genstart. Du får mulighed for at gemme alle åbne dokumenter."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starter Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starter..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problem med systemkonfigurationen"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Installation af software er gennemført."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Der er flere ventende opdateringer, som skal installeres eller fjernes."; - -/* No Items primary text */ -"There are no available software items." = "Der ingen software-emner."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Der er ingen emner fra udvikleren."; - -/* No Category Results primary text */ -"There are no items in this category." = "Der er ingen emner i kategorien."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Der er andre brugere logget ind på computeren.\nHvis du opdaterer nu, risikerer du, at de mister deres åbne dokumenter.\n\nPrøv igen senere når de andre brugere er logget ud."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Der er ventende opdateringer for computeren."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Der er et konfigurationsproblem med softwareværktøjet. Kan ikke starte processen. Kontakt systemadministratoren."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Der er et konfigurationsproblem med softwareværktøjet. Processes sluttede uventet. Kontakt systemadministratoren."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Der er ingen nye opdateringer lige nu."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Der er ikke nok ledig plads på disken til at downloade og installere dette emne."; - -/* Dependency List prologue text */ -"This item is required by:" = "Dette emne kræves af:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Dette emne skal installeres senest %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "For at tillade dette, skal du skrive din log ind-adgangskode."; - -/* No Items secondary text */ -"Try again later." = "Prøv igen senere."; - -/* No Search Results secondary text */ -"Try searching again." = "Prøv at søge igen."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Prøv at vælge en anden kategori."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Prøv at vælge en anden udvikler."; - -/* Sidebar Type label */ -"Type:" = "Type:"; - -/* Unavailable status text */ -"Unavailable" = "Ikke tilgængelig"; - -/* No Category name */ -"Uncategorized" = "Ikke kategoriseret"; - -/* Update button title/action text */ -"Update" = "Opdater"; - -/* Update All button title */ -"Update All" = "Opdater alle"; - -/* Update Required long action text */ -"Update Required" = "Opdatering kræves"; - -/* No comment provided by engineer. */ -"Update available" = "Kan opdateres"; - -/* Update Check Failed title */ -"Update check failed" = "Søgning efter opdateringer mislykkedes"; - -/* Update In Progress primary text */ -"Update in progress." = "Opdatering i gang."; - -/* Update Now button title */ -"Update now" = "Opdater nu"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Opdatering bliver installeret"; - -/* Updates label */ -"Updates" = "Opdateringer"; - -/* Updating message */ -"Updating..." = "Opdaterer..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Kontrollerer integritet af software..."; - -/* Sidebar Version label */ -"Version" = "Version"; - -/* Sidebar Version label */ -"Version:" = "Version:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Venter på netværk..."; - -/* Will Be Installed status text */ -"Will be installed" = "Bliver installeret"; - -/* Will Be Removed status text */ -"Will be removed" = "Bliver fjernet"; - -/* No Installed Software primary text */ -"You have no selected software." = "Du har ikke valgt nogen software."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Du skal slutte følgende programmer for du forsætter med installering eller fjernelse:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Du skal opgradere til macOS version %s for at installere dette emne."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Computeren er ikke sluttet til strømforsyningen."; - -/* No Search Results primary text */ -"Your search had no results." = "Søgningen gav ingen resultater."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Softwaren er opdateret."; - -/* macOS update required text */ -"macOS update required" = "Kræver macOS-opdatering"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/MainMenu.strings deleted file mode 100644 index e073720e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/da.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjælp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjælp"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Hjælp til Managed Software Center"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Skjul Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Slut Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Skjul andre"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Vis alle"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Vindue"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Vælg alt"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klip"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Søg"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mine emner"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Slet"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Sæt ind"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Fortryd"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Gentag"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimer"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Vindue"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Naviger"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Naviger"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Om Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Kategorier"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Genindlæs side"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Opdateringer"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Opdateringer"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Kategorier"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Kategorier"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mine emner"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mine emner"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Software"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Luk"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Tilbage"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Søg"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Start fuld skærm"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Opdateringer"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Vis log"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Frem"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings deleted file mode 100644 index fe8eea53..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Geführte Softwareaktualisierung"; -"CFBundleDisplayName" = "Geführte Softwareaktualisierung"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/Localizable.strings deleted file mode 100644 index a841317b..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s anstehende Updates"; - -/* One Update message */ -"1 pending update" = "1 anstehendes Update"; - -/* Logout Required detail */ -"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?" = "Für die Updates ist ein Abmelden erforderlich. Es kann zu kurzen Verzögerungen am Anmeldebildschirm kommen, bitte gedulden sie sich einen Moment. Jetzt abmelden und aktualisieren?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Eine Abmeldung wird in ca. %s erzwungen."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Eine Abmeldung wird in weniger als %s Minuten erzwungen."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "Eine Abmeldung wird in weniger als einer Minute erzwungen.\nAlle anstehenden Updates werden installiert. Alle ungespeicherte Arbeit geht verloren."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Entfernen fehlgeschlagen. Entfernen wird erneut versucht.\nFalls dieses Verhalten anhält, verständigen Sie bitte ihren Systemadministrator."; - -/* Restart Required detail */ -"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?" = "Für das Update ist ein Neustart erforderlich. Es kann zu kurzen Verzögerungen am Anmeldebildschirm kommen, bitte gedulden sie sich einen Moment. Jetzt abmelden und aktualisieren?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Ein Problem mit der Systemkonfiguration verhindert das korrekte funktionieren der Geführten Softwareaktualisierung. Das aufgetretene Problem ist: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Weitere anstehende Updates"; - -/* AllCategoriesLabel */ -"All Categories" = "Alle Kategorien"; - -/* AllItemsHeaderText */ -"All items" = "Alle Objekte"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Alle anstehenden Updates werden installiert. Alle ungespeicherte Arbeit geht verloren.\nDurch jetziges Abmelden können sie eine erzwungene Abmeldung verhindern"; - -/* Allow button text */ -"Allow" = "Erlauben"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Installation fehlgeschlagen. Installation wird wieder versucht.\nFalls dieses Verhalten anhält, verständigen Sie bitte ihren Systemadministrator."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Eine ältere Version ist derzeit installiert. Es ist nicht genügend Speicherplatz vorhanden, um dieses Update herunterzuladen und zu installieren."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Eine ältere Version ist derzeit installiert. Sie müssen auf macOS version %s oder höher aktualisieren, um dieses Update installieren zu können."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Applikationen, die von anderen benutzt werden"; - -/* Cancel button title/short action text */ -"Cancel" = "Abbrechen"; - -/* Cancel Install long action text */ -"Cancel install" = "Installation abbrechen"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Entfernen abbrechen"; - -/* Cancel Update long action text */ -"Cancel update" = "Update abbrechen"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Kann das angeforderte Objekt nicht anzeigen"; - -/* Categories label */ -"Categories" = "Kategorien"; - -/* Sidebar Category label */ -"Category:" = "Kategorie:"; - -/* Check Again button title */ -"Check Again" = "Erneut prüfen"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Prüfe Apple Software Update Katalog..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Suche nach weiteren Änderungen..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Prüfe verfügbare Apple Software Updates..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Prüfe auf verfügbare Updates..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Prüfe auf Updates..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Es gibt einen Konflikt mit laufenden Programmen"; - -/* Continue button text */ -"Continue" = "Fortsetzen"; - -/* Critical Update type */ -"Critical Update" = "Kritisches Update"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Derzeit nicht verfügbar"; - -/* Deny button text */ -"Deny" = "Nicht erlauben"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Stelle fest, welche Dateien zu entfernen sind"; - -/* Sidebar Developer label */ -"Developer:" = "Entwickler:"; - -/* managedsoftwareupdate message */ -"Done." = "Fertig"; - -/* Downloading status text */ -"Downloading" = "Lädt gerade herunter..."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Lade verfügbare Apple Software Updates herunter..."; - -/* Sidebar Due label */ -"Due:" = "Fällig:"; - -/* FeaturedLabel */ -"Featured" = "Highlights"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "App-Highlights"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Abschließen..."; - -/* Firmware Alert Default detail */ -"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." = "Firmware auf Ihrem Computer wird aktualisiert. Das Netzteil Ihres Computers muss eingesteckt und mit einer Stromquelle verbunden sein. Das Update kann mehrere Minuten dauern. Benutzen Sie Ihren Computer nicht und schalten Sie Ihn während dieses Updates nicht aus."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Sie sollten den Computer an eine Stromquelle anschliessen bevor Sie das Update durchführen. Sind Sie sicher, dass Sie das Update fortsetzen möchten?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Erzwungene Abmeldung für eine zwingend notwendige Installation"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Sammle Informationen zu installierten Paketen"; - -/* No help alert title */ -"Help" = "Hilfe"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Hilfe ist für Geführte Softwareaktualisierung nicht verfügbar."; - -/* Sidebar Information label */ -"Information" = "Informationen"; - -/* Install action text */ -"Install" = "Installieren"; - -/* Install Required action text */ -"Install Required" = "Installation notwendig "; - -/* Install Requested status text */ -"Install requested" = "Installation angefordert"; - -/* Install Session Failed title */ -"Install session failed" = "Installation fehlgeschlagen"; - -/* Install Error status text */ -"Installation Error" = "Fehler bei der Installation"; - -/* Installed status text */ -"Installed" = "Installiert"; - -/* Installing status text */ -"Installing" = "Installiere..."; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installiere verfügbare Apple Software Updates..."; - -/* Log out and Update button text */ -"Log out and update" = "Abmelden und aktualisieren"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Jetzt abmelden und aktualisieren"; - -/* Logout Required title */ -"Logout Required" = "Abmelden erforderlich"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kann gerade nicht auf anstehende Update prüfen.\nVersuchen Sie es später noch einmal. Falls dieses Problem weiter besteht, kontaktieren Sie Ihren Systemadministrator."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kann den Updateserver derzeit nicht erreichen.\nVersuchen Sie es später noch einmal. Falls dieses Problem weiter besteht, kontaktieren Sie Ihren Systemadministrator."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Die geführte Softwareaktualisierung möchte nach einem Neustart das Startvolume entsperren, um alle anstehenden Aktualisierungen abzuschließen."; - -/* Managed Update type */ -"Managed Update" = "Verwaltetes Update"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Zwingend notwendige Updates anstehend"; - -/* More link text */ -"More" = "Mehr"; - -/* Sidebar More By Developer label */ -"More by %s" = "Mehr von %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Mehr aus %s"; - -/* My Items label */ -"My Items" = "Meine Objekte"; - -/* No Licenses Available display text */ -"No licenses available" = "Keine Lizenzen verfügbar"; - -/* No Updates message */ -"No pending updates" = "Keine anstehenden Updates"; - -/* Item Not Found title */ -"Not Found" = "Nicht gefunden"; - -/* Not Currently Available display text */ -"Not currently available" = "Derzeit nicht verfügbar"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Nicht genügend Festplattenplatz"; - -/* Not Installed status text */ -"Not installed" = "Nicht installiert"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Nichts zu entfernen."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Eines oder mehrere Objekte müssen bis %s installiert werden."; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Eines oder mehrere zwingend notwendige Updates sind überfällig. Eine Abmeldung wird in Kürze erzwungen."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Eines oder mehrere Updates müssen unbedingt bis %s installiert werden. Eine Abmeldung wird möglicherweise erzwungen, wenn Sie zu lange mit dem Update warten."; - -/* Other Available Updates label */ -"Other available updates" = "Andere verfügbare Updates"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Andere angemeldete User verwenden die folgenden Applikationen. Versuchen Sie das Update später noch einmal, wenn diese nicht mehr in Verwendung sind:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Andere User sind eingeloggt"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Entfernen des Paketes abgeschlossen."; - -/* Password label */ -"Password:" = "Passwort:"; - -/* Pending Updates alert title */ -"Pending updates" = "Anstehende updates"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Führe postflight Aufgaben aus..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Führe preflight Aufgaben aus..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Bereite das Entfernen vor"; - -/* Problem Updates label */ -"Problem updates" = "Problematische updates"; - -/* Quit button title */ -"Quit" = "Beenden"; - -/* Removal Error status text */ -"Removal Error" = "Fehler beim Entfernen"; - -/* Removal Requested status text */ -"Removal requested" = "Entfernung angefordert"; - -/* Remove action text */ -"Remove" = "Entfernen"; - -/* Removing status text */ -"Removing" = "Deinstallation"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Entferne Paketquittung"; - -/* Install Required action text */ -"Required" = "Erforderlich"; - -/* Restart button title */ -"Restart" = "Neustart"; - -/* Restart Required title */ -"Restart Required" = "Neustart erforderlich"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Hole Liste der Software für diesen Computer..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Starte Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Starte Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Starte Adobe Uninstall"; - -/* No Installed Software secondary text */ -"Select software to install." = "Wählen Sie die zu installierende Software aus."; - -/* Sidebar Size label */ -"Size:" = "Größe:"; - -/* Software label */ -"Software" = "Software"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Installierte oder entfernte Software benötigt einen Neustart."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Softwareaktualisierung oder -entfernung benötigt einen Neustart. Sie können offene Dokumente noch sichern."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starte Adobe Installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starte..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problem mit der Systemkonfiguration"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Die Software wurde erfolgreich installiert."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Es stehen weitere Updates zur Installation oder Entfernung an."; - -/* No Items primary text */ -"There are no available software items." = "Es ist keine Software verfügbar."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Es gibt keine Objekte von diesem Entwickler"; - -/* No Category Results primary text */ -"There are no items in this category." = "Es gibt keine Objekte in dieser Kategorie."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Es sind andere Benutzer angemeldet\nEin Update könnte zu Datenverlust führen.\n\nBitte probieren Sie es noch einmal, sobald die anderen abgemeldet sind."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Für diesen Computer gibt es anstehende Updates."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Managed Software Installer hat ein Konfigurationsproblem (Could not start the process). Informieren Sie Ihren Administrator."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Managed Software Installer hat ein Konfigurationsproblem (The process ended unexpectedly). Informieren Sie Ihren Administrator."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Zurzeit sind keine Softwareupdates für Ihren Computer verfügbar."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Es ist nicht genügend Speicherplatz vorhanden, um dieses Update herunterzuladen und zu installieren."; - -/* Dependency List prologue text */ -"This item is required by:" = "Dieses Objekt wird benötigt von:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Dieses Objekt muss bis %s installiert sein"; - -/* Password explanation */ -"To allow this, enter your login password." = "To allow this, enter your login password."; - -/* No Items secondary text */ -"Try again later." = "Später noch einmal versuchen."; - -/* No Search Results secondary text */ -"Try searching again." = "Versuchen Sie erneut zu suchen."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Versuchen Sie eine andere Kategorie."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Versuchen Sie einen anderen Entwickler auszuwählen."; - -/* Sidebar Type label */ -"Type:" = "Typ:"; - -/* Unavailable status text */ -"Unavailable" = "Nicht verfügbar"; - -/* No Category name */ -"Uncategorized" = "Unkategorisiert"; - -/* Update button title/action text */ -"Update" = "Aktualisieren"; - -/* Update All button title */ -"Update All" = "Alle aktualisieren"; - -/* Update Required long action text */ -"Update Required" = "Update erforderlich"; - -/* No comment provided by engineer. */ -"Update available" = "Updates verfügbar"; - -/* Update Check Failed title */ -"Update check failed" = "Prüfung auf Updates fehlgeschlagen"; - -/* Update In Progress primary text */ -"Update in progress." = "Update läuft."; - -/* Update Now button title */ -"Update now" = "Jetzt aktualisieren"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Update wird installiert"; - -/* Updates label */ -"Updates" = "Updates"; - -/* Updating message */ -"Updating..." = "Aktualisiere..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Prüfe Paketintegrität..."; - -/* Sidebar Version label */ -"Version" = "Version"; - -/* Sidebar Version label */ -"Version:" = "Version:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Warte auf Netzwerk..."; - -/* Will Be Installed status text */ -"Will be installed" = "wird installiert"; - -/* Will Be Removed status text */ -"Will be removed" = "wird entfernt"; - -/* No Installed Software primary text */ -"You have no selected software." = "Sie haben keine Software ausgewählt."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Sie müssen die folgenden Programme beenden, um mit dem Update fortfahren zu können:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Sie müssen auf macOS version %s oder höher aktualisieren, um dieses Update installieren zu können."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Ihr Computer ist nicht an eine Stromquelle angeschlossen."; - -/* No Search Results primary text */ -"Your search had no results." = "Ihre Suche lieferte keine Ergebnisse."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Ihre Software ist auf dem neuesten Stand."; - -/* macOS update required text */ -"macOS update required" = "macOS update erforderlich"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/MainMenu.strings deleted file mode 100644 index 01b2a25e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/de.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hilfe"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hilfe"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Softwareaktualisierung-Hilfe"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Dienste"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Dienste"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Softwareaktualisierung ausblenden"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Softwareaktualisierung beenden"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Andere ausblenden"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Alle einblenden"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fenster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopieren"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Alles auswählen"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Ausschneiden"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Suche"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Installiert"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Löschen"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Einfügen"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Bearbeiten"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Widerrufen"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Wiederholen"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Bearbeiten"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimieren"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoomen"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fenster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Navigation"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Navigation"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Über Geführte Softwareaktualisierung"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Kategorien"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Seite neu laden"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Updates"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Updates"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Kategorien"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Kategorien"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Installiert"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Installiert"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Software"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Schließen"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Zurück"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Suche"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Vollbild"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Updates"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Protokoll einblenden"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Weiter"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings deleted file mode 100644 index 4a7411be..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Centre"; -"CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/Localizable.strings deleted file mode 100644 index 8c0c2c0d..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/Localizable.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator."; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/MainMenu.strings deleted file mode 100644 index 2c9ffbfe..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_AU.lproj/MainMenu.strings +++ /dev/null @@ -1,21 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Centre"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "About Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Centre Help"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Hide Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Quit Managed Software Centre"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Centre"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings deleted file mode 100644 index 4a7411be..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Centre"; -"CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/Localizable.strings deleted file mode 100644 index 8c0c2c0d..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/Localizable.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator."; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/MainMenu.strings deleted file mode 100644 index 2c9ffbfe..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_CA.lproj/MainMenu.strings +++ /dev/null @@ -1,21 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Centre"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "About Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Centre Help"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Hide Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Quit Managed Software Centre"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Centre"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings deleted file mode 100644 index 4a7411be..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Centre"; -"CFBundleDisplayName" = "Managed Software Centre"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/Localizable.strings deleted file mode 100644 index 8c0c2c0d..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/Localizable.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centre cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator."; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/MainMenu.strings deleted file mode 100644 index 2c9ffbfe..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/en_GB.lproj/MainMenu.strings +++ /dev/null @@ -1,21 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Centre"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "About Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Centre Help"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Hide Managed Software Centre"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Quit Managed Software Centre"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Centre"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings deleted file mode 100644 index 0fafd7da..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Centro de aplicaciones"; -"CFBundleDisplayName" = "Centro de aplicaciones"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/Localizable.strings deleted file mode 100644 index c2c911bd..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s actualizaciones pendiente"; - -/* One Update message */ -"1 pending update" = "1 actualización pendiente"; - -/* Logout Required detail */ -"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?" = "Es necesario cerrar la sesión antes de actualizar. Por favor se paciente ya que puede tardar en aparecer la ventana de inicio. ¿Cerrar sesión y actualizar ahora?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Se va a forzar el cierre de sesión en aproximadamente %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Se va a forzar el cierre de sesión en menos de %s minutos."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "Se va a forzar el cierre de sesión en menos de un minuto.\nTodas las actualizaciones pendientes serán instaladas. Se perderá cualquier cambio no guardado."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Una desinstalación ha fallado. Se volverá a intentar.\nSi esta situación continua, contacta con el administrador de sistemas."; - -/* Restart Required detail */ -"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?" = "Es necesario reiniciar después de instalar las actualizaciones. Por favor se paciente ya que puede tardar en aparecer la ventana de inicio. ¿Cerrar sesión y actualizar ahora?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Un fallo en la configuración del sistema ha provocado que Centro de Aplicaciones no funcione de forma correcta. El fallo reportado es: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Actualizaciones adicionales pendientes"; - -/* AllCategoriesLabel */ -"All Categories" = "Todas las categorías"; - -/* AllItemsHeaderText */ -"All items" = "Todos"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Todas las actualizaciones pendientes serán instaladas. Cualquier cambio no guardado se perderá.\nPuedes evitar el cierre forzado de la sesión cerrándola ahora."; - -/* Allow button text */ -"Allow" = "Permitir"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Una instalación ha fallado. Se volverá a intentar.\nSi esta situación continua, contacta con el administrador de sistemas."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Una versión anterior está instalada. No hay espacio suficiente en el disco para descargar e instalar esta actualización."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Una versión anterior está instalada. Tienes que actualizar a macOS %s o superior para poder instalar esta actualización."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Applicaciones en uso por otros"; - -/* Cancel button title/short action text */ -"Cancel" = "Cancelar"; - -/* Cancel Install long action text */ -"Cancel install" = "Cancelar instalación"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Cancelar desinstalación"; - -/* Cancel Update long action text */ -"Cancel update" = "Cancelar actualización"; - -/* Item Not Found message */ -"Cannot display the requested item." = "No se puede mostar el ítem solicitado."; - -/* Categories label */ -"Categories" = "Categorías"; - -/* Sidebar Category label */ -"Category:" = "Categoría:"; - -/* Check Again button title */ -"Check Again" = "Volver a comprobar"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Comprobando actualizaciones del catálogo de Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Comprobando cambios adicionales..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Comprobando actualizaciones de Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Comprobando actualizaciones disponibles..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Comprobando actualizaciones..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Existen aplicaciones conflictivas abiertas"; - -/* Continue button text */ -"Continue" = "Continuar"; - -/* Critical Update type */ -"Critical Update" = "Actualización crítica"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Actualmente no disponible"; - -/* Deny button text */ -"Deny" = "Denegar"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Determinando que archivos hay que quitar"; - -/* Sidebar Developer label */ -"Developer:" = "Desarrollador:"; - -/* managedsoftwareupdate message */ -"Done." = "Hecho."; - -/* Downloading status text */ -"Downloading" = "Descargando"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Descargando actualizaciones de Apple disponibles..."; - -/* Sidebar Due label */ -"Due:" = "Requerida:"; - -/* FeaturedLabel */ -"Featured" = "Destacados"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Ítems destacados"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Finalizando..."; - -/* Firmware Alert Default detail */ -"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." = "El firmware de tu equipo se va a actualizar. El equipo tiene que estar conectado a una fuente de alimentación. Puede tardar varios minutos en completarse. No interrumpas o apagues el equipo durante esta instalación."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Para mejores resultados se recomienda que conectes el equipo a una fuente de alimentación antes de iniciar la instalación. ¿Seguro que quieres continuar con la actualización?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Cierre de sesión forzado para una instalación requerida"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Recopilando información sobre los paquetes instalados"; - -/* No help alert title */ -"Help" = "Ayuda"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "No hay ayuda disponible para el Centro de aplicaciones."; - -/* Sidebar Information label */ -"Information" = "Información"; - -/* Install action text */ -"Install" = "Instalar"; - -/* Install Required action text */ -"Install Required" = "Instalación requerida"; - -/* Install Requested status text */ -"Install requested" = "Instalación solicitada"; - -/* Install Session Failed title */ -"Install session failed" = "Error en la sesión de instalación"; - -/* Install Error status text */ -"Installation Error" = "Error durante la instalación"; - -/* Installed status text */ -"Installed" = "Instalado"; - -/* Installing status text */ -"Installing" = "Instalando"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Instalando actualizaciones de Apple disponibles..."; - -/* Log out and Update button text */ -"Log out and update" = "Cerrar sesión y actualizar"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Cerrar sesión y actualizar ahora"; - -/* Logout Required title */ -"Logout Required" = "Es necesario cerrar sesión"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Centro de aplicaciones no puede comprobar las actualizaciones.\nPrueba más tarde. Si esta situación continua, contacta con el administrador de sistemas."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Centro de aplicaciones no puede contactar con el servidor de actualizaciones.\nPrueba más tarde. Si esta situación continua, contacta con el administrador de sistemas."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Centro de Aplicaciones quiere desbloquear el disco de inicio tras el reinicio para completar todas las actualizaciones pendientes."; - -/* Managed Update type */ -"Managed Update" = "Actualización controlada"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Actualizaciones requeridas pendientes"; - -/* More link text */ -"More" = "Más"; - -/* Sidebar More By Developer label */ -"More by %s" = "Más de %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Otros en %s"; - -/* My Items label */ -"My Items" = "Mis ítems"; - -/* No Licenses Available display text */ -"No licenses available" = "No hay licencias disponibles"; - -/* No Updates message */ -"No pending updates" = "No hay actualizaciones pendientes"; - -/* Item Not Found title */ -"Not Found" = "No encontrado"; - -/* Not Currently Available display text */ -"Not currently available" = "Actualmente no disponible"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "No hay suficiente espacion en el disco"; - -/* Not Installed status text */ -"Not installed" = "No instalado"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Nada que quitar."; - -/* OK button title */ -"OK" = "Aceptar"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Uno o mas ítems tienen que ser instalados para el %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "El periodo de instalación de una o más actualizaciones requeridas ha expirado. En breve se forzará el cierre de sesión"; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Una o más actualizaciones tienen ser instaladas para el %s. Se forzará el cierre de sesión si esperas demasiado en actualizar."; - -/* Other Available Updates label */ -"Other available updates" = "Otras actualizaciones disponibles"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Otros usuarios estan utilizando las siguientes aplicaciones. Prueba de actualizar más tarde cuando no esten en uso:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Hay otros usuarios con sesiones abiertas"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Desinstalación del paquete completada."; - -/* Password label */ -"Password:" = "Contraseña:"; - -/* Pending Updates alert title */ -"Pending updates" = "Actualizaciones pendientes"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Ejecutando los scripts de postinstalación..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Ejecutando los scripts de preinstalación..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Preparando desinstalación"; - -/* Problem Updates label */ -"Problem updates" = "Actualización problematica"; - -/* Quit button title */ -"Quit" = "Salir"; - -/* Removal Error status text */ -"Removal Error" = "Error al desinstalar"; - -/* Removal Requested status text */ -"Removal requested" = "Desinstalación solicitada"; - -/* Remove action text */ -"Remove" = "Desinstalar"; - -/* Removing status text */ -"Removing" = "Desinstalando"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Quitando la información del recibo"; - -/* Install Required action text */ -"Required" = "Requerida"; - -/* Restart button title */ -"Restart" = "Reiniciar"; - -/* Restart Required title */ -"Restart Required" = "Es necesario reiniciar"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Obteniendo la lista de software para este equipo..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Instalación de un parche de Adobe en marcha"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Instalación de Adobe en marcha"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Desinstalación de Adobe en marcha"; - -/* No Installed Software secondary text */ -"Select software to install." = "Selecciona la aplicaciones a instalar."; - -/* Sidebar Size label */ -"Size:" = "Tamaño:"; - -/* Software label */ -"Software" = "Aplicaciones"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "El software instalado o desinstalado requiere reiniciar el equipo."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "El software instalado necesita reiniciar el equipo. Tendrás oportunidad de guardar tus documentos"; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Empezando el instalador de Adobe..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Empezando..."; - -/* Sidebar Status label */ -"Status:" = "Estado:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problema en la configuración del sistema"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "El software se instaló correctamente."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Hay actualizaciones adicionales pendientes de instalar o desinstalar."; - -/* No Items primary text */ -"There are no available software items." = "No hay aplicaciones disponibles."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "No hay aplicaciones de este desarollador."; - -/* No Category Results primary text */ -"There are no items in this category." = "No hay ítems en esta categoria."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Hay otros usuarios con sesiones abiertas en este equipo.\nActualizar ahora puede causar perdidas en su trabajo.\n\nPor favor prueba más tarde cuando los otros usuarios hayan cerrado sus sesiones."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Hay actualizaciones pendientes para este equipo."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Existe un problema de configuración con el Centro de aplicaciones. No se ha podido iniciar el proceso. Contacta con el administrador de sistemas."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Existe un problema de configuración con el Centro de aplicaciones. El proceso se ha cerrado inesperadamente. Contacta con el administrador de sistemas."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "No hay actualizaciones de software disponibles."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "No hay espacio suficiente en el disco para descargar e instalar esta actualización."; - -/* Dependency List prologue text */ -"This item is required by:" = "Requerido por:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Tiene que estar instalado para el %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Para permitir esto, introduzca su contraseña de usuario."; - -/* No Items secondary text */ -"Try again later." = "Probar de nuevo más tarde."; - -/* No Search Results secondary text */ -"Try searching again." = "Intentar buscar otra vez."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Intentar seleccionar otra categoría."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Intentar seleccionar otro desarollador."; - -/* Sidebar Type label */ -"Type:" = "Tipo:"; - -/* Unavailable status text */ -"Unavailable" = "No disponible"; - -/* No Category name */ -"Uncategorized" = "Sin categoría"; - -/* Update button title/action text */ -"Update" = "Actualizar"; - -/* Update All button title */ -"Update All" = "Actualizar todo"; - -/* Update Required long action text */ -"Update Required" = "Requiere una actualización"; - -/* No comment provided by engineer. */ -"Update available" = "Actualización disponible"; - -/* Update Check Failed title */ -"Update check failed" = "Comprobación fallida"; - -/* Update In Progress primary text */ -"Update in progress." = "Actualización en proceso."; - -/* Update Now button title */ -"Update now" = "Actualizar ahora"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Se instalará la actualización"; - -/* Updates label */ -"Updates" = "Actualizaciones"; - -/* Updating message */ -"Updating..." = "Actualizando..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Comprobando la integridad del paquete..."; - -/* Sidebar Version label */ -"Version" = "Versión"; - -/* Sidebar Version label */ -"Version:" = "Versión:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Esperando red..."; - -/* Will Be Installed status text */ -"Will be installed" = "Será instalado"; - -/* Will Be Removed status text */ -"Will be removed" = "Será desinstalado"; - -/* No Installed Software primary text */ -"You have no selected software." = "No has seleccionado ninguna aplicación."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Debes cerrar las siguientes aplicaciones antes de instalar o desinstalar:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Tienes que actualizar a macOS %s o superior para poder instalar esta actualización."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "El equipo no está conectado a una fuente de alimentación."; - -/* No Search Results primary text */ -"Your search had no results." = "Tu busqueda no ha obtenido ningún resultado."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Tus aplicaciones están actualizadas."; - -/* macOS update required text */ -"macOS update required" = "Actualización de macOS requerida"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/MainMenu.strings deleted file mode 100644 index 9f847912..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/es.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Ayuda"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Ayuda"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Ayuda del Centro de aplicaciones"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Servicios"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Servicios"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Ocultar Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Salir de Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Ocultar otros"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Mostrar todos"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Ventana"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copiar"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Seleccionar todo"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cortar"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Buscar"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mis aplicaciones"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Eliminar"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Pegar"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edición"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Deshacer"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Rehacer"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Editar"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimizar"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Ventana"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMenuPrincipal"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Navegar"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Navegar"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Centro de aplicaciones"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Acerca del Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Categorías"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Recargar página"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Actualizaciones"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Actualizaciones"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Categorías"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Categorías"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mis aplicaciones"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mis aplicaciones"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Aplicaciones"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Cerrar"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Anterior"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Buscar"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Usar pantalla completa"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Aplicaciones"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Aplicaciones"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Actualizaciones"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Mostrar registro"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Siguiente"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/Localizable.strings deleted file mode 100644 index 44b69e16..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s päivitystä saatavilla"; - -/* One Update message */ -"1 pending update" = "1 päivitys saatavilla"; - -/* Logout Required detail */ -"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?" = "Päivitysten asentaminen vaatii kaikkien ohjelmien sulkemista ja käyttäjien uloskirjautumista järjestelmästä. Kirjautumisikkunan avauduttua saattaa kestää hetki ennen kuin asennus alkaa. Kirjaudu ulos ja päivitä nyt?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Uloskirjautuminen suoritetaan pakotetusti noin %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Uloskirjautuminen suoritetaan pakotetusti alle %s minuutin kuluttua."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "Uloskirjautuminen suoritetaan alle minuutin kuluttua pakotetusti.\nKaikki odottamassa olevat päivitykset asennetaan. Tallentamattomat muutokset menetetään avoimista ohjelmista."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Poistaminen epäonnistui mutta sitä yritetään uudelleen.\nJos ongelma jatkuu, ota yhteyttä järjestelmän ylläpitäjään."; - -/* Restart Required detail */ -"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?" = "Päivitysten asentaminen vaatii tietokoneen uudelleenkäynnistystä. Kirjautumisikkunan avauduttua saattaa kestää hetki ennen kuin asennus alkaa. Kirjaudu ulos ja päivitä nyt?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Järjestelmän määritysvirhe estää Managed Software Center -ohjelman toiminnan. Raportoitu ongelma on: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Muut odottavat päivitykset"; - -/* AllCategoriesLabel */ -"All Categories" = "Kaikki kategoriat"; - -/* AllItemsHeaderText */ -"All items" = "Kaikki ohjelmistot"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Kaikki odottamassa olevat päivitykset asennetaan. Tallentamattomat muutokset menetetään avoimista ohjelmista.\nVoit ohittaa pakotetun uloskirjaamisen kirjautumalla ulos nyt."; - -/* Allow button text */ -"Allow" = "Salli"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Asennus epäonnistui mutta sitä yritetään uudelleen.\nJos ongelma jatkuu, ota yhteyttä järjestelmän ylläpitäjään."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Vanhempi versio on asennettu. Päivityksen lataamiseen ja asentamiseen ei ole riittävästi levytilaa."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Vanhempi versio on asennettu. Päivityksen asentamiseen tarvitaan macOS %s tai uudempi."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Muiden käyttäjien käytössä olevat ohjelmat"; - -/* Cancel button title/short action text */ -"Cancel" = "Kumoa"; - -/* Cancel Install long action text */ -"Cancel install" = "Kumoa asennus"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Kumoa poisto"; - -/* Cancel Update long action text */ -"Cancel update" = "Keskeytä päivitys"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Pyydettyä kohdetta ei voida näyttää."; - -/* Categories label */ -"Categories" = "Kategoriat"; - -/* Sidebar Category label */ -"Category:" = "Kategoria:"; - -/* Check Again button title */ -"Check Again" = "Tarkista uudelleen"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Tarkistetaan Applen ohjelmistopäivitysten luetteloa..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Tarkistetaan muutoksia..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Tarkistetaan Applen ohjelmistopäivityksiä..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Tarkistetaan päivityksiä..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Tarkistetaan päivityksiä..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Käynnissä on ohjelmia jotka estävät päivityksen"; - -/* Continue button text */ -"Continue" = "Jatka"; - -/* Critical Update type */ -"Critical Update" = "Kriittinen päivitys"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Ei saatavilla tällä hetkellä"; - -/* Deny button text */ -"Deny" = "Kiellä"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Määritetään poistettavia kohteita"; - -/* Sidebar Developer label */ -"Developer:" = "Kehittäjä:"; - -/* managedsoftwareupdate message */ -"Done." = "Valmis."; - -/* Downloading status text */ -"Downloading" = "Ladataan"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Haetaan Applen ohjelmistopäivityksiä..."; - -/* Sidebar Due label */ -"Due:" = "Asennettava:"; - -/* FeaturedLabel */ -"Featured" = "Esittelyssä"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Esittelyssä"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Viimeistellään..."; - -/* Firmware Alert Default detail */ -"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." = "Tietokoneellesi asennetaan laiteohjelmistopäivitys. Tietokoneen virtajohdon pitää olla liitettynä toimivaan virtalähteeseen. Päivityksen suorittaminen kestää useita minuutteja. Älä häiritse tai sammuta tietokonetta päivityksen aikana."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Tietokone tulisi varmuuden vuoksi liittää virtalähteeseen ennen päivittämistä. Oletko varma että haluat jatkaa?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Pakotettu uloskirjautuminen pakolliselle asennukselle"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Kootaan tietoja asennetuista paketeista"; - -/* No help alert title */ -"Help" = "Ohje"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Managed Software Center -ohjelmalle ei ole ohjetta."; - -/* Sidebar Information label */ -"Information" = "Tietoja"; - -/* Install action text */ -"Install" = "Asenna"; - -/* Install Required action text */ -"Install Required" = "Pakollinen asennus"; - -/* Install Requested status text */ -"Install requested" = "Asennusta pyydetty"; - -/* Install Session Failed title */ -"Install session failed" = "Asennus epäonnistui"; - -/* Install Error status text */ -"Installation Error" = "Virhe asennettaessa"; - -/* Installed status text */ -"Installed" = "Asennettu"; - -/* Installing status text */ -"Installing" = "Asennetaan"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Asennetaan Applen ohjelmistopäivityksiä..."; - -/* Log out and Update button text */ -"Log out and update" = "Kirjaudu ulos ja päivitä"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Kirjaudu ulos ja päivitä nyt"; - -/* Logout Required title */ -"Logout Required" = "Vaatii uloskirjautumisen"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center ei voi tarkistaa päivityksiä juuri nyt. Yritä myöhemmin uudelleen. Jos ongelma jatkuu, ota yhteyttä järjestelmän ylläpitäjään."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center ei saa yhteyttä palvelimeen juuri nyt. Yritä myöhemmin uudelleen. Jos ongelma jatkuu, ota yhteyttä järjestelmän ylläpitäjään."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Center haluaa avata käynnistyslevyn lukituksen seuraavalla käynnistyskerralla jatkaakseen päivitysten asentamista."; - -/* Managed Update type */ -"Managed Update" = "Hallittu päivitys"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Pakollisia päivityksiä odottamassa"; - -/* More link text */ -"More" = "Lisää"; - -/* Sidebar More By Developer label */ -"More by %s" = "Lisää kehittäjältä %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Lisää kategoriassa %s"; - -/* My Items label */ -"My Items" = "Omat ohjelmistot"; - -/* No Licenses Available display text */ -"No licenses available" = "Ei lisenssejä saatavilla"; - -/* No Updates message */ -"No pending updates" = "Ei päivityksiä"; - -/* Item Not Found title */ -"Not Found" = "Ei tuloksia"; - -/* Not Currently Available display text */ -"Not currently available" = "Ei saatavilla tällä hetkellä"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Kovalevyllä ei ole tarpeeksi tilaa"; - -/* Not Installed status text */ -"Not installed" = "Ei asennettu"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Ei poistettavia kohteita."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Yksi tai useampi kohde täytyy asentaa ennen %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Yksi tai useampi pakollinen päivitys on myöhässä. Uloskirjautuminen tullaan suorittamaan pakotetusti pian."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Yksi tai useampi päivitys täytyy asentaa ennen %s. Uloskirjautuminen saatetaan pakottaa jos odotat liian pitkään ennen päivittämistä."; - -/* Other Available Updates label */ -"Other available updates" = "Muut saatavilla olevat päivitykset"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Muut kirjautuneet käyttäjät käyttävät seuraavia ohjelmia. Yritä myöhemmin uudelleen kun ohjelmat eivät ole enää käytössä:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Muut kirjautuneet käyttäjät"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paketin poisto valmis."; - -/* Password label */ -"Password:" = "Salasana:"; - -/* Pending Updates alert title */ -"Pending updates" = "Odottavat päivitykset"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Suoritetaan viimeisteleviä tehtäviä..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Suoritetaan valmistavia tehtäviä..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Valmistellaan poistoa"; - -/* Problem Updates label */ -"Problem updates" = "Epäonnistuneet päivitykset"; - -/* Quit button title */ -"Quit" = "Lopeta"; - -/* Removal Error status text */ -"Removal Error" = "Virhe poistettaessa"; - -/* Removal Requested status text */ -"Removal requested" = "Poistoa pyydetty"; - -/* Remove action text */ -"Remove" = "Poista"; - -/* Removing status text */ -"Removing" = "Poistetaan"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Poistetaan kuitteja"; - -/* Install Required action text */ -"Required" = "Pakollinen"; - -/* Restart button title */ -"Restart" = "Käynnistä uudelleen"; - -/* Restart Required title */ -"Restart Required" = "Vaatii uudelleenkäynnistyksen"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Haetaan ohjelmistolistaa tälle tietokoneelle..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Ajetaan Adobe Patch -asentajaa"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Ajetaan Adobe Setup -ohjelmistoa"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Ajetaan Adobe Uninstall -ohjelmistoa"; - -/* No Installed Software secondary text */ -"Select software to install." = "Valitse asennettavat ohjelmistot."; - -/* Sidebar Size label */ -"Size:" = "Koko:"; - -/* Software label */ -"Software" = "Ohjelmistot"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Asennettu ohjelmisto tai poisto vaatii tietokoneen uudelleenkäynnistyksen."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Asennettu ohjelmisto tai poisto vaatii tietokoneen uudelleenkäynnistyksen. Avoinna olevien dokumenttien tallentaminen on mahdollista."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Käynnistetään Adobe-asentajaa..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Aloitetaan..."; - -/* Sidebar Status label */ -"Status:" = "Tila:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Järjestelmän määritysvirhe"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Ohjelmiston asennus onnistui."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Muita ohjelmistopäivityksiä tai poistoja odottamassa."; - -/* No Items primary text */ -"There are no available software items." = "Ei saatavilla olevia ohjelmistoja."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Ei kohteita tältä kehittäjältä."; - -/* No Category Results primary text */ -"There are no items in this category." = "Ei kohteita tässä kategoriassa."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Tietokoneelle on kirjautuneena muita käyttäjiä.\nMuut käyttäjät saattavat menettää tallentamattomia muutoksia jos päivitys tehdään nyt.\n\nYritä uudelleen kun muut käyttäjät ovat kirjautuneet ulos."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Tietokoneelle on saatavilla ohjelmistopäivityksiä."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Asennusohjelmassa on konfiguraatiovirhe. Prosessia ei voitu käynnistää. Ota yhteyttä järjestelmän ylläpitäjään."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Asennusohjelmassa on konfiguraatiovirhe. Prosessi päättyi odottamatta. Ota yhteyttä järjestelmän ylläpitäjään."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Tietokoneelle ei ole tällä hetkellä saatavilla uusia ohjelmistopäivityksiä."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Päivityksen lataamiseen ja asentamiseen ei ole riittävästi levytilaa."; - -/* Dependency List prologue text */ -"This item is required by:" = "Tämän kohteen vaatii:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Asennettava ennen %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Salli lukituksen avaaminen syöttämällä salasana."; - -/* No Items secondary text */ -"Try again later." = "Yritä myöhemmin uudelleen."; - -/* No Search Results secondary text */ -"Try searching again." = "Yritä etsimistä uudelleen."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Valitse toinen kategoria."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Valitse toinen kehittäjä."; - -/* Sidebar Type label */ -"Type:" = "Tyyppi:"; - -/* Unavailable status text */ -"Unavailable" = "Ei saatavilla"; - -/* No Category name */ -"Uncategorized" = "Ei kategoriaa"; - -/* Update button title/action text */ -"Update" = "Päivitä"; - -/* Update All button title */ -"Update All" = "Päivitä kaikki"; - -/* Update Required long action text */ -"Update Required" = "Pakollinen päivitys"; - -/* No comment provided by engineer. */ -"Update available" = "Päivitys saatavilla"; - -/* Update Check Failed title */ -"Update check failed" = "Päivitysten tarkistaminen epäonnistui"; - -/* Update In Progress primary text */ -"Update in progress." = "Päivitys käynnissä."; - -/* Update Now button title */ -"Update now" = "Päivitä nyt"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Päivitys asennetaan"; - -/* Updates label */ -"Updates" = "Päivitykset"; - -/* Updating message */ -"Updating..." = "Päivitetään..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Tarkistetaan paketin eheyttä..."; - -/* Sidebar Version label */ -"Version" = "Versio"; - -/* Sidebar Version label */ -"Version:" = "Versio:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Odotetaan verkkoyhteyttä..."; - -/* Will Be Installed status text */ -"Will be installed" = "Asennetaan"; - -/* Will Be Removed status text */ -"Will be removed" = "Poistetaan"; - -/* No Installed Software primary text */ -"You have no selected software." = "Sinulla ei ole valittuja ohjelmistoja."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Jotta voit jatkaa asennusta tai poistoa, sulje seuraavat ohjelmat:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Päivityksen asentamiseen tarvitaan macOS %@."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Tietokonetta ei ole liitetty virtalähteeseen."; - -/* No Search Results primary text */ -"Your search had no results." = "Ei tuloksia."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Ohjelmisto on ajan tasalla."; - -/* macOS update required text */ -"macOS update required" = "macOS-päivitys vaaditaan"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/MainMenu.strings deleted file mode 100644 index f361b8ee..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fi.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Ohje"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Ohje"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Center -ohje"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Palvelut"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Palvelut"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Kätke Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Lopeta Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Kätke muut"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Näytä kaikki"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Ikkuna"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopioi"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Valitse kaikki"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Leikkaa"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Etsi"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Omat"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Poista"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Liitä"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Muokkaa"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Peru"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Tee sittenkin"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Muokkaa"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Pienennä"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoomaa"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Ikkuna"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Näytä"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Näytä"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Tietoja: Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Kategoriat"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Lataa sivu uudelleen"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Päivitykset"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Päivitykset"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Kategoriat"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Kategoriat"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Omat"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Omat"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Ohjelmistot"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Sulje"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Taaksepäin"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Etsi"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Siirry koko näytön tilaan"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Ohjelmistot"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Ohjelmistot"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Päivitykset"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Näytä loki"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Eteenpäin"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings deleted file mode 100644 index 581ce409..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Centre de gestion des logiciels"; -"CFBundleDisplayName" = "Centre de gestion des logiciels"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/Localizable.strings deleted file mode 100644 index c6e59022..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/Localizable.strings +++ /dev/null @@ -1,477 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s mises à jour en attente"; - -/* One Update message */ -"1 pending update" = "1 mise à jour en attente"; - -/* Logout Required detail */ -"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?" = "Il est nécessaire de fermer la session avant de mettre à jour. Soyez patient car il peut y avoir un délai à la reconnexion. Fermer et mettre à jour maintenant ?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Une fermeture de session sera forcée à environ %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Une fermeture de session sera forcée dans moins de %s minutes."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "La fermeture de session sera forcée dans moins d'une minute.\nToutes les mises à jour en attente seront installées. Le travail non sauvegardé sera perdu."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Une tentative de suppression a échoué. La suppression sera tentée à nouveau.\nSi cette situation perdure, contactez votre administrateur système."; - -/* Restart Required detail */ -"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?" = "Un redémarrage est nécessaire après la mise à jour. Soyez patient car il peut y avoir un petit délai lors de la reconnexion. Déconnecter et mettre à jour maintenant ?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Un problème de configuration du système empêche le Centre de gestion des logiciels de fonctionner correctement. Le problème signalé est: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Mises à jour supplémentaires en attente"; - -/* AllCategoriesLabel */ -"All Categories" = "Toutes les catégories"; - -/* AllItemsHeaderText */ -"All items" = "Tous les articles"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Toutes les mises à jour en attente vont être installées. Les travaux non sauvegardés seront perdus.\nPour éviter cela fermer maintenant votre session."; - -/* Allow button text */ -"Allow" = "Autoriser"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Une tentative d'installation a échoué. L'installation sera tentée à nouveau.\nSi cette situation perdure, contactez votre administrateur système."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Une ancienne version est actuellement installée. Il n'y a pas assez d'espace disque pour télécharger et installer cette mise à jour."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Une ancienne version est actuellement installée. Vous devez mettre à jour vers une version macOS -%s ou plus pour pouvoir installer cette mise à jour."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Application utilisée par d'autres"; - -/* Cancel button title/short action text */ -"Cancel" = "Annuler"; - -/* Cancel Install long action text */ -"Cancel install" = "Annuler l'installation"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Annuler la suppression"; - -/* Cancel Update long action text */ -"Cancel update" = "Annuler la mise à jour"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Ne peut afficher l'article demandé."; - -/* Categories label */ -"Categories" = "Catégories"; - -/* Sidebar Category label */ -"Category:" = "Catégorie:"; - -/* Check Again button title */ -"Check Again" = "Vérifier à nouveau"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Vérification des mises à jour Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Vérification des modifications supplémentaires..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Vérification des mises à jour Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Vérification des mises à jour disponibles..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Vérification des mises à jour..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Conflit avec des applications actives"; - -/* Continue button text */ -"Continue" = "Continuer"; - -/* Critical Update type */ -"Critical Update" = "Mise à jour critique"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Actuellement non disponible"; - -/* Deny button text */ -"Deny" = "Refuser"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Vérification des éléments à supprimer"; - -/* Sidebar Developer label */ -"Developer:" = "Développeur:"; - -/* managedsoftwareupdate message */ -"Done." = "Terminé."; - -/* Downloading status text */ -"Downloading" = "Téléchargement"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Téléchargement des mises à jour Apple disponibles..."; - -/* Sidebar Due label */ -"Due:" = "Dû:"; - -/* FeaturedLabel */ -"Featured" = "En vedette"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Articles en vedette"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Finalisation..."; - -/* Firmware Alert Default detail */ -"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." = "Un microprogramme sera mis à jour sur votre ordinateur. Le cordon d'alimentation de votre ordinateur doit être branché à une prise de courant fonctionnelle. La mise à jour pourrait prendre plusieurs minutes pour se terminer. Veuillez ne pas éteindre ou mettre votre ordinateur en veille, et ne pas débrancher le cordon d'alimentation durant cette mise à jour."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Pour un meilleur résultat, vous devriez brancher votre ordinateur à une alimentation externe avant de mettre à jour. Êtes-vous certain de vouloir poursuivre la mise à jour ?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Fermeture de session requise pour mise à jour obligatoire"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Vérification de l'information sur les articles installés"; - -/* No help alert title */ -"Help" = "Aide"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "L'aide n'est pas disponible pour le Centre de gestion des logiciels."; - -/* Sidebar Information label */ -"Information" = "Information"; - -/* Install action text */ -"Install" = "Installer"; - -/* Install Required action text */ -"Install Required" = "Installation obligatoire"; - -/* Install Requested status text */ -"Install requested" = "Installation demandée"; - -/* Install Session Failed title */ -"Install session failed" = "Échec de l'installation"; - -/* Install Error status text */ -"Installation Error" = "Erreur lors de l'installation"; - -/* Installed status text */ -"Installed" = "Installé"; - -/* Installing status text */ -"Installing" = "Installation en cours"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installation des mises à jour Apple disponibles..."; - -/* Log out and Update button text */ -"Log out and update" = "Déconnecter et mettre à jour"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Déconnecter et mettre à jour maintenant"; - -/* Logout Required title */ -"Logout Required" = "Fermeture de session obligatoire"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Le Centre de gestion des logiciels ne peut vérifier les mises à jour maintenant.\nEssayez plus tard. Si cette situation perdure, contactez votre administrateur système."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Le Centre de gestion des logiciels ne peut contacter le serveur de mise à jour.\nEssayez plus tard. Si cette situation perdure, contactez votre administrateur système."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Le Centre de gestion des logiciels doit déverrouiller votre disque de démarrage au prochain redémarrage afin de terminer les mises à jour en attente."; - -/* Managed Update type */ -"Managed Update" = "Mise à jour gérée"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Mises à jour obligatoires en attente"; - -/* More link text */ -"More" = "Plus"; - -/* Sidebar More By Developer label */ -"More by %s" = "Plus par %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Plus dans %s"; - -/* My Items label */ -"My Items" = "Mes articles"; - -/* No Licenses Available display text */ -"No licenses available" = "Aucune licence disponible"; - -/* No Updates message */ -"No pending updates" = "Aucune mise à jour en attente"; - -/* Item Not Found title */ -"Not Found" = "Aucune correspondance"; - -/* Not Currently Available display text */ -"Not currently available" = "Non disponible actuellement"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Pas assez d'espace disque"; - -/* Not Installed status text */ -"Not installed" = "Non installé"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Rien à supprimer."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Un article doit être installé avant le %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Une ou plusieurs mises à jour obligatoires sont en retard. Une fermeture de session va être forcée très bientôt."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Une ou plusieurs mises à jour doivent être installées avant le %s. Une fermeture de session peut être forcée si vous attendez trop longtemps avant de mettre à jour."; - -/* Other Available Updates label */ -"Other available updates" = "Autres mises à jour disponibles"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "D'autres utilisateurs connectés utilisent les applications suivantes. Essayez de les mettre à jour lorsqu'elles ne seront plus utilisées:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "D'autres utilisateurs sont connectés"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paquet supprimé avec succès."; - -/* Password label */ -"Password:" = "Mot de passe :"; - -/* Pending Updates alert title */ -"Pending updates" = "Mises à jour en attente"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Exécution des tâches de finition..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Exécution des tâches d'initialisation..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Préparation de la suppression"; - -/* Problem Updates label */ -"Problem updates" = "Mises à jour problématiques"; - -/* Quit button title */ -"Quit" = "Quitter"; - -/* Removal Error status text */ -"Removal Error" = "Erreur de suppression"; - -/* Removal Requested status text */ -"Removal requested" = "Suppression demandée"; - -/* Remove action text */ -"Remove" = "Supprimer"; - -/* Removing status text */ -"Removing" = "Suppression en cours"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Suppression de l'information du reçu"; - -/* Install Required action text */ -"Required" = "obligatoire"; - -/* Restart button title */ -"Restart" = "Redémarrer"; - -/* Restart Required title */ -"Restart Required" = "Redémarrage requis"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Obtention de la liste des logiciels pour cet ordinateur..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Démarrage du programme de mise à niveau d'Adobe"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Démarrage du programme d'installation d'Adobe"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Désinstallation d'Adobe"; - -/* No Installed Software secondary text */ -"Select software to install." = "Choisir les logiciels à installer."; - -/* Sidebar Size label */ -"Size:" = "Taille:"; - -/* Software label */ -"Software" = "Logiciel"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Un logiciel installé ou supprimé nécessite un redémarrage."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Les logiciels installés ou supprimés nécessitent un redémarrage. Vous aurez la possibilité de sauvegarder les documents ouverts."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Démarrage du programme d'installation d'Adobe..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Démarrage..."; - -/* Sidebar Status label */ -"Status:" = "État:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problème de configuration du système"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Le logiciel a été installé avec succès."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Il y a des mises à jour additionnelles à installer ou supprimer."; - -/* No Items primary text */ -"There are no available software items." = "Il n'y a pas de logiciel disponible."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Il n'y a pas d'articles par ce développeur."; - -/* No Category Results primary text */ -"There are no items in this category." = "Il n'y a pas d'articles dans cette catégorie."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Il y a d'autres utilisateurs connectés sur l'ordinateur.\nMettre à jour peut entrainer la perte du travail des autres utilisateurs.\n\nMerci d'essayer plus tard après que les autres utilisateurs aient quitté leur session."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Des mises à jour sont disponibles pour cet ordinateur."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "L'utilitaire de mise à jour a rencontré un problème de configuration. Impossible de démarrer le processus. Contactez votre administrateur système."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "L'utilitaire de mise à jour a rencontré un problème de configuration. Le processus s'est arrêté de manière inattendue. Contactez votre administrateur système."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Il n'y a pas de nouveau logiciel pour votre ordinateur."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Il n'y a pas assez d'espace disque pour télécharger et installer cet élément."; - -/* Dependency List prologue text */ -"This item is required by:" = "Cet article est requis avant:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Cet article doit être installé avant le %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Pour autoriser cette opération, veuillez entrer votre mot de passe."; - -/* No Items secondary text */ -"Try again later." = "Essayez plus tard."; - -/* No Search Results secondary text */ -"Try searching again." = "Tentez la recherche à nouveau."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Essayez de sélectionner une autre catégorie."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Essayez de sélectionner un autre développeur."; - -/* Sidebar Type label */ -"Type:" = "Type:"; - -/* Unavailable status text */ -"Unavailable" = "Non disponible"; - -/* No Category name */ -"Uncategorized" = "Non catégorisé"; - -/* Update button title/action text */ -"Update" = "Mettre à jour"; - -/* Update All button title */ -"Update All" = "Tout mettre à jour"; - -/* Update Required long action text */ -"Update Required" = "Mise à jour obligatoire"; - -/* No comment provided by engineer. */ -"Update available" = "Mise à jour disponible"; - -/* Update Check Failed title */ -"Update check failed" = "Échec de la recherche de mises à jour"; - -/* Update In Progress primary text */ -"Update in progress." = "Mise à jour en cours."; - -/* Update Now button title */ -"Update now" = "Mettre à jour maintenant"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "La mise à jour sera installée"; - -/* Updates label */ -"Updates" = "Mises à jour"; - -/* Updating message */ -"Updating..." = "Mise à jour en cours..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Vérification de l'intégrité du paquet..."; - -/* Sidebar Version label */ -"Version" = "Version"; - -/* Sidebar Version label */ -"Version:" = "Version:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "En attente du réseau..."; - -/* Will Be Installed status text */ -"Will be installed" = "Sera installé"; - -/* Will Be Removed status text */ -"Will be removed" = "Sera supprimé"; - -/* No Installed Software primary text */ -"You have no selected software." = "Vous n'avez pas sélectionné de logiciel."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Vous devez quitter les logiciels suivants avant de les installer ou de les supprimer:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Vous devez mettre à jour vers une version macOS %s pour pouvoir installer cet élément."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Votre ordinateur n'est pas branché à une prise de courant."; - -/* No Search Results primary text */ -"Your search had no results." = "Votre recherche n'a retourné aucun résultat."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Votre logiciel est à jour."; - -/* macOS update required text */ -"macOS update required" = "Mise à jour MacOS requise"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/MainMenu.strings deleted file mode 100644 index 193389f0..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/fr.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Aide"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Aide"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Aide du Centre de gestion des logiciels"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Masquer le Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Quitter le Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Masquer les autres"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Tout afficher"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fenêtre"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Tout sélectionner"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Couper"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Rechercher"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mes articles"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Supprimer"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Coller"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Édition"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Annuler"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Rétablir"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Modifier"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimiser"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fenêtre"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMenuPrincipal"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Naviguer"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Naviguer"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Centre de gestion des logiciels"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "À propos de Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Catégories"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Actualiser la page"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Mises à jour"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Mises à jour"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Catégories"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Catégories"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mes articles"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mes articles"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Logiciel"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Fermer"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Précédent"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Rechercher"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Plein écran"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Logiciel"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Logiciel"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Mises à jour"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Afficher l’historique"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Suivant"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings deleted file mode 100644 index e2dc43ac..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Centro Gestione Applicazioni"; -"CFBundleDisplayName" = "Centro Gestione Applicazioni"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/Localizable.strings deleted file mode 100644 index 4bd636bd..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s aggiornamenti in sospeso"; - -/* One Update message */ -"1 pending update" = "1 aggiornamento in sospeso"; - -/* Logout Required detail */ -"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?" = "È necessario un logout prima dell'aggiornamento. Potrebbe esserci un leggero ritardo prima della finestra di login. Eseguire il logout e l'aggiornamento adesso?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Un logout verrà forzato tra circa %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Un logout verrà forzato in meno di %s minuti."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "Un logout verrà forzato in meno di un minuto.\nTutti gli aggiornamenti in sospeso verranno installati. I documenti non salvati verranno persi."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Un tentativo di rimozione è fallito. La rimozione verrà riprovata ancora.\nSe questa situazione persiste, contattare l'amministratore di sistema."; - -/* Restart Required detail */ -"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?" = "È necessario un riavvio dopo dell'aggiornamento. Potrebbe esserci un leggero ritardo prima della finestra di login. Eseguire il logout e l'aggiornamento adesso?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Un problema alla configurazione di sistema sta impedendo al Centro Gestione Applicazioni di operare correttamente. Il problema segnalato è: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Ulteriori Aggiornamenti in Sospeso"; - -/* AllCategoriesLabel */ -"All Categories" = "Tutte le Categorie"; - -/* AllItemsHeaderText */ -"All items" = "Tutti gli elementi"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Tutti gli aggiornamenti in sospeso verrano installati. I documenti non salvati verranno persi.\nSi può evitare un logout forzato facendo un logout adesso."; - -/* Allow button text */ -"Allow" = "Consenti"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Un tentativo di installazione è fallito. L'installazione verrà riprovata ancora.\nSe questa situazione persiste, contattare l'amministratore di sistema."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Una versione precedente è attualmente installata. Non c'è abbastanza spazio su disco per scaricare e installare questo aggiornamento."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Una versione precedente è attualmente installata. Per installare questo update bisogna aggiornare il sistema a Mac OS %s o superiore."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Applicazioni in uso da altri"; - -/* Cancel button title/short action text */ -"Cancel" = "Cancella"; - -/* Cancel Install long action text */ -"Cancel install" = "Cancella installazione"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Cancella rimozione"; - -/* Cancel Update long action text */ -"Cancel update" = "Cancella aggiornamento"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Impossibile visualizzare l'elemento richiesto."; - -/* Categories label */ -"Categories" = "Categorie"; - -/* Sidebar Category label */ -"Category:" = "Categoria:"; - -/* Check Again button title */ -"Check Again" = "Controlla ancora"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Controllo catalogo Aggiornamenti Software Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Controllo per ulteriori cambiamenti..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Controllo Aggiornamenti Software Apple disponibili..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Controllo aggiornamenti disponibili..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Controllo aggiornamenti..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Applicazioni in conflitto in esecuzione"; - -/* Continue button text */ -"Continue" = "Continua"; - -/* Critical Update type */ -"Critical Update" = "Aggiornamento critico"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Attualmente non disponibile"; - -/* Deny button text */ -"Deny" = "Rifiuta"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Determino elementi del filesystem da rimuovere"; - -/* Sidebar Developer label */ -"Developer:" = "Developer:"; - -/* managedsoftwareupdate message */ -"Done." = "Fatto."; - -/* Downloading status text */ -"Downloading" = "Download in corso"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Download Aggiornamenti Apple Disponibili in corso..."; - -/* Sidebar Due label */ -"Due:" = "Da fare:"; - -/* FeaturedLabel */ -"Featured" = "In primo piano"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Elementi in primo piano"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Conclusione..."; - -/* Firmware Alert Default detail */ -"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." = "Il Firmware verrà aggiornato sul computer. L'alimentatore deve essere collegato al computer ed inserito in una presa di corrente. L'aggiornamento potrebbe richiedere diversi minuti. Non interferire o spegnere il computer durante questo aggiornamento."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Per migliori risultati, connettere il computer ad un alimentatore di corrente prima dell'aggiornamento. Sicuro di continuare l'aggiornamento?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Logout forzato per installazioni obbligatorie"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Raccolta informazioni sui pacchetti installati in corso"; - -/* No help alert title */ -"Help" = "Aiuto"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Aiuto non è disponibile per Centro Gestione Applicazioni."; - -/* Sidebar Information label */ -"Information" = "Informazioni"; - -/* Install action text */ -"Install" = "Installazione"; - -/* Install Required action text */ -"Install Required" = "Installazione Necessaria"; - -/* Install Requested status text */ -"Install requested" = "Installazione richiesta"; - -/* Install Session Failed title */ -"Install session failed" = "Sessione di installazione fallita"; - -/* Install Error status text */ -"Installation Error" = "Errore di installazione"; - -/* Installed status text */ -"Installed" = "Installato"; - -/* Installing status text */ -"Installing" = "Installazione in corso"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installazione Aggiornamenti Software Apple in corso..."; - -/* Log out and Update button text */ -"Log out and update" = "Log out e aggiornamento"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Log out e aggiornamento ora"; - -/* Logout Required title */ -"Logout Required" = "Logout Necessario"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "In questo momento il Centro Gestione Applicazioni non riesce a controllare gli aggiornamenti.\nRiprovare più tardi. Se questa situazione persiste, contattare l'amministratore di sistema."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "In questo momento il Centro Gestione Applicazioni non riesce a contattare il server.\nRiprovare più tardi. Se questa situazione persiste, contattare l'amministratore di sistema."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Centro Gestione Applicazioni vuole sbloccare il disco di avvio dopo il riavvio per completare gli aggiornamenti in sospeso."; - -/* Managed Update type */ -"Managed Update" = "Aggiornamento Gestito"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Aggiornamenti obbligatori in Sospeso"; - -/* More link text */ -"More" = "Altri"; - -/* Sidebar More By Developer label */ -"More by %s" = "Altri da %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Altri in %s"; - -/* My Items label */ -"My Items" = "I Miei Elementi"; - -/* No Licenses Available display text */ -"No licenses available" = "Nessuna licenza disponibile"; - -/* No Updates message */ -"No pending updates" = "Nessun aggiornamento in sospeso"; - -/* Item Not Found title */ -"Not Found" = "Non Trovato"; - -/* Not Currently Available display text */ -"Not currently available" = "Attualmente non disponibile"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Non c'è abbastanza spazio su disco"; - -/* Not Installed status text */ -"Not installed" = "Non Installato"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Niente da Rimuovere."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Uno o più elementi devono essere installati da %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Uno o più aggiornamenti obbligatori sono in ritardo per l'installazione. Un logout verrà forzato a breve."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Uno o più elementi devono essere installati da %s. Un logout potrebbe essere forzato in caso si aspetti troppo per l'aggiornamento."; - -/* Other Available Updates label */ -"Other available updates" = "Altri aggiornamenti disponibili"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Altri utenti collegati stanno usando le seguenti applicazioni. Provare l'aggiornamento quando non saranno in uso:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Altri utenti collegati"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Rimozione pacchetto riuscita."; - -/* Password label */ -"Password:" = "Password:"; - -/* Pending Updates alert title */ -"Pending updates" = "Aggiornamenti in sospeso"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Controllo post-installazione in corso..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Controllo pre-installazione in corso..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Rimozione in preparazione"; - -/* Problem Updates label */ -"Problem updates" = "Aggiornamenti che richiedono attenzione"; - -/* Quit button title */ -"Quit" = "Chiudi"; - -/* Removal Error status text */ -"Removal Error" = "Errore di rimozione"; - -/* Removal Requested status text */ -"Removal requested" = "Rimozione richiesta"; - -/* Remove action text */ -"Remove" = "Rimuovi"; - -/* Removing status text */ -"Removing" = "Rimozione in corso"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Rimozione info in corso"; - -/* Install Required action text */ -"Required" = "Necessario"; - -/* Restart button title */ -"Restart" = "Riavvio"; - -/* Restart Required title */ -"Restart Required" = "Riavvio Necessario"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Recupero la lista delle applicazioni per questo mac..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobe Patch Installer in esecuzione"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobe Setup in esecuzione"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobe Uninstall in esecuzione"; - -/* No Installed Software secondary text */ -"Select software to install." = "Seleziona applicazioni da installare."; - -/* Sidebar Size label */ -"Size:" = "Dimensione:"; - -/* Software label */ -"Software" = "Applicazione"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "L'applicazione installata o rimossa richiede il riavvio."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "L'applicazione installata o rimossa richiede il riavvio. Avrai la possibilità di salvare i documenti aperti."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Avvio Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Avvio..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problema di configurazione di sistema"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "L'applicazione è stata installata con successo."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Ci sono ulteriori aggiornamenti in sospeso da installare o rimuovere."; - -/* No Items primary text */ -"There are no available software items." = "Non ci sono elementi disponibili."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Non ci sono elementi di questo developer."; - -/* No Category Results primary text */ -"There are no items in this category." = "Non ci sono elementi in questa categoria."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Ci sono altri utenti collegati a questo computer.\nAggiornare ora può causare la perdita dei loro dati.\n\nRiprovare quando tutti gli utenti saranno scollegati."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Ci sono aggiornamenti in sospeso per questo computer."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "C'è un problema di configurazione con l'installer del Centro Gestione Aggiornamenti. Impossibile avviare il processo. Contattare l'amministratore di sistema."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "C'è un problema di configurazione con l'installer del Centro Gestione Aggiornamenti. Il processo si è chiuso inaspettatamente. Contattare l'amministratore di sistema."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Al momento non ci sono nuove applicazioni per questo computer."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Non c'è abbastanza spazio su disco per scaricare e installare questo elemento."; - -/* Dependency List prologue text */ -"This item is required by:" = "Questo elemento è richiesto da:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Questo elemento deve essere installato da %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Per permettere questo, inserire la password di login."; - -/* No Items secondary text */ -"Try again later." = "Prova ancora più tardi."; - -/* No Search Results secondary text */ -"Try searching again." = "Prova a cercare ancora."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Prova selezionando un'altra categoria."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Prova selezionando un altro developer."; - -/* Sidebar Type label */ -"Type:" = "Tipo:"; - -/* Unavailable status text */ -"Unavailable" = "Non disponibile"; - -/* No Category name */ -"Uncategorized" = "Senza Categoria"; - -/* Update button title/action text */ -"Update" = "Aggiorna"; - -/* Update All button title */ -"Update All" = "Aggiorna Tutto"; - -/* Update Required long action text */ -"Update Required" = "Aggiornamento richiesto"; - -/* No comment provided by engineer. */ -"Update available" = "Aggiornamento disponibile"; - -/* Update Check Failed title */ -"Update check failed" = "Controllo aggiornamento fallito"; - -/* Update In Progress primary text */ -"Update in progress." = "Aggiornamento in corso."; - -/* Update Now button title */ -"Update now" = "Aggiorna adesso"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "L'aggiornamento verrà installato"; - -/* Updates label */ -"Updates" = "Aggiornamenti"; - -/* Updating message */ -"Updating..." = "Aggiornamento in corso..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Controllo l'integrità del pacchetto..."; - -/* Sidebar Version label */ -"Version" = "Versione"; - -/* Sidebar Version label */ -"Version:" = "Versione:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "In attesa del network..."; - -/* Will Be Installed status text */ -"Will be installed" = "Verrà installato"; - -/* Will Be Removed status text */ -"Will be removed" = "Verrà rimosso"; - -/* No Installed Software primary text */ -"You have no selected software." = "Non ci sono applicazioni selezionate."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Chiudere le seguenti applicazioni prima di procedere all'installazione o rimozione:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Per installare questo elemento bisogna aggiornare il sistema a Mac OS %s"; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Il computer non è collegato ad una presa di corrente."; - -/* No Search Results primary text */ -"Your search had no results." = "La ricerca non ha prodotto risultati."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Le applicazioni sono aggiornate."; - -/* macOS update required text */ -"macOS update required" = "È richiesto un aggiornamento di Mac OS"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/MainMenu.strings deleted file mode 100644 index 9a7afb2d..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/it.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Aiuto"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Aiuto"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Aiuto su Centro Gestione Applicazioni"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Servizi"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Servizi"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Nascondi Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Chiudi Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Nascondi Altri"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Mostra Tutto"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Finestra"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copia"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Seleziona Tutto"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Taglia"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Cerca"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "I Miei Elementi"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Elimina"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Incolla"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Modifica"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Annulla"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Ripristina Originale"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Modifica"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Riduci"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Ingrandisci"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Finestra"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMenu Principale"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Naviga"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Naviga"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Centro Gestione Applicazioni"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Info su Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Categorie"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Ricarica Pagina"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Aggiornamenti"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Aggiornamenti"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigazione"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Categorie"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Categorie"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "I Miei Elementi"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "I Miei Elementi"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Applicazioni"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Chiudi"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Indietro"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Cerca"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Schermo Pieno"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Applicazioni"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Applicazioni"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Spazio Ristretto"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Aggiornamenti"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Mostra log"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Avanti"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/InfoPlist.strings deleted file mode 100644 index 0ac157de..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2015 The Munki Project\nhttps://github.com/munki/munki"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/Localizable.strings deleted file mode 100644 index 248ec52e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "アップデートを%s個保留しています"; - -/* One Update message */ -"1 pending update" = "アップデートを1つ保留しています"; - -/* Logout Required detail */ -"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?" = "アップデートの前にログアウトする必要があります。ログインウィンドウで若干の時間がかかります。今からログアウトしアップデートを実行しますか?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "%sに、強制的にログアウトさせられます"; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "%s分以内に、強制的にログアウトさせられます"; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "1分以内に強制的にログアウトさせられます。\n保留中のアップデートはすべてインストールされます。保存されていない作業内容は消失します。"; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "削除に失敗しました。次回削除を試みます。\nそれでも削除されない場合は、システムの管理者に連絡してください。"; - -/* Restart Required detail */ -"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?" = "アップデート後に再起動する必要があります。ログインウィンドウで若干の時間がかかります。今からログアウトしアップデートを実行しますか?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "システムコンフィギュレーションの問題が、Managed Software Centerの正常なオペレーションを妨げています。報告された問題は:"; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "その他保留中のアップデート"; - -/* AllCategoriesLabel */ -"All Categories" = "すべてのカテゴリ"; - -/* AllItemsHeaderText */ -"All items" = "すべてのアイテム"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "保留中のすべてのアップデートをインストールします。保存されていない作業内容は消失します。\n今ログアウトをすれば、強制ログアウトを避ける事ができます。"; - -/* Allow button text */ -"Allow" = "許可"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "インストールに失敗しました。再度インストールを試みます。\nインストールの失敗が続くようであれば、システム管理者に連絡してください。"; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "旧バージョンがインストールされています。このアップデートをダウンロードしてインストールするには空き領域が不足しています。"; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "旧バージョンがインストールされています。このアップデートをインストールするにはmacOS %s 以降にアップグレードする必要があります。"; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "他のユーザーが使用しているアプリケーションです"; - -/* Cancel button title/short action text */ -"Cancel" = "キャンセル"; - -/* Cancel Install long action text */ -"Cancel install" = "インストールをキャンセル"; - -/* Cancel Removal long action text */ -"Cancel removal" = "削除をキャンセル"; - -/* Cancel Update long action text */ -"Cancel update" = "アップデートをキャンセル"; - -/* Item Not Found message */ -"Cannot display the requested item." = "リクエストされたアイテムを表示できません"; - -/* Categories label */ -"Categories" = "カテゴリ"; - -/* Sidebar Category label */ -"Category:" = "カテゴリ:"; - -/* Check Again button title */ -"Check Again" = "もう一度確認する"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "アップルソフトウェアアップデートカタログを確認中…"; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "追加変更を確認中…"; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "入手可能なアップルソフトウェアアップデートを確認中…"; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "入手可能なアップデートを確認中…"; - -/* Checking For Updates message */ -"Checking for updates..." = "アップデートを確認中…"; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "下記のプログラムを閉じる必要があります"; - -/* Continue button text */ -"Continue" = "続ける"; - -/* Critical Update type */ -"Critical Update" = "重要なアップデート"; - -/* Unavailable long action text */ -"Currently Unavailable" = "現在利用できません"; - -/* Deny button text */ -"Deny" = "不許可"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "どのファイル項目を削除するか特定中"; - -/* Sidebar Developer label */ -"Developer:" = "デベロッパ:"; - -/* managedsoftwareupdate message */ -"Done." = "完了"; - -/* Downloading status text */ -"Downloading" = "ダウンロード中"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "入手可能なアップルソフトウェアアップデートをダウンロード中…"; - -/* Sidebar Due label */ -"Due:" = "アップデート期限:"; - -/* FeaturedLabel */ -"Featured" = "おすすめ"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "おすすめのアイテム"; - -/* managedsoftwareupdate message */ -"Finishing..." = "仕上げ中…"; - -/* Firmware Alert Default detail */ -"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." = "ファームウェアがアップデートされます。お使いのコンピューターの電源コードを電源に繋いでください。アップデート完了までには数分がかかります。アップデート処理の進行中はアップデート処理を妨げたり、コンピューターの電源を切らないでください。"; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "支障の発生を避けるためにアップデートを実行する前にコンピューターを電源に接続してください。アップデートを続けますか?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "必須インストールのため、強制ログアウトします"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "インストールされたパッケージの情報を収集中"; - -/* No help alert title */ -"Help" = "ヘルプ"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Managed Software Centerにヘルプは付属していません"; - -/* Sidebar Information label */ -"Information" = "情報"; - -/* Install action text */ -"Install" = "インストール"; - -/* Install Required action text */ -"Install Required" = "要請されたインストール"; - -/* Install Requested status text */ -"Install requested" = "要請されたインストール"; - -/* Install Session Failed title */ -"Install session failed" = "インストールセッションに失敗しました"; - -/* Install Error status text */ -"Installation Error" = "インストールエラー"; - -/* Installed status text */ -"Installed" = "インストール済み"; - -/* Installing status text */ -"Installing" = "インストール中"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "入手可能なアップルソフトウェアアップデートをインストール中…"; - -/* Log out and Update button text */ -"Log out and update" = "ログアウトし、アップデートする"; - -/* Logout and Update Now button text */ -"Log out and update now" = "ログアウトし、今すぐアップデートする"; - -/* Logout Required title */ -"Logout Required" = "ログアウトが必要です"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centerは現在アップーデートのチェックができません。\n後でもう一度実行してください。もしこの状況が改善されない場合は、システム管理者に連絡してください。"; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Centerは現在アップーデートサーバーと接続できません。\n後でもう一度実行してください。もしこの状況が改善されない場合は、システム管理者に連絡してください。"; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Centerは、すべての保留中のアップデートを完了させるため、再起動後、スタートアップディスクをアンロックします。"; - -/* Managed Update type */ -"Managed Update" = "必須のアップデート"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "必須のアップデートを保留中"; - -/* More link text */ -"More" = "さらに見る"; - -/* Sidebar More By Developer label */ -"More by %s" = "%sをもっと見る"; - -/* Sidebar More In Category label */ -"More in %s" = "%sでもっと見る"; - -/* My Items label */ -"My Items" = "マイアイテム"; - -/* No Licenses Available display text */ -"No licenses available" = "有効なライセンスがありません"; - -/* No Updates message */ -"No pending updates" = "保留中のアップデートはありません"; - -/* Item Not Found title */ -"Not Found" = "見つかりません"; - -/* Not Currently Available display text */ -"Not currently available" = "現在使用ができません"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "ディスクの空きが足りません"; - -/* Not Installed status text */ -"Not installed" = "インストールされていません"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "削除する項目がありません"; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "%sまでに、1つまたそれ以上のアイテムのインストールが必要です"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "一つまたそれ以上の必須アップデートが期日までに行われていないため、強制的にログアウトをします"; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "%sまでに、一つまたそれ以上のアイテムをインストールしなければいけません。アップーデートを実行しなければ、強制的にログアウトさせられます。"; - -/* Other Available Updates label */ -"Other available updates" = "その他入手可能なアップデート"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "別のユーザーが、下記のアプリケーションを使用しています。別のユーザーが\n%s\nの使用を終えてから、アップーデートをもう一度実行してください。"; - -/* Other Users Logged In title */ -"Other users logged in" = "別のユーザーがログイン中です"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "パッケージの削除が完了しました"; - -/* Password label */ -"Password:" = "パスワード:"; - -/* Pending Updates alert title */ -"Pending updates" = "アップデートを保留中"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "postflightタスクを実行中…"; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "preflightタスクを実行中…"; - -/* Preparing Removal status text */ -"Preparing removal" = "削除準備中です"; - -/* Problem Updates label */ -"Problem updates" = "問題のアップデート"; - -/* Quit button title */ -"Quit" = "終了"; - -/* Removal Error status text */ -"Removal Error" = "削除エラー"; - -/* Removal Requested status text */ -"Removal requested" = "削除リクエスト"; - -/* Remove action text */ -"Remove" = "削除"; - -/* Removing status text */ -"Removing" = "削除中"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "receipt情報を削除中です"; - -/* Install Required action text */ -"Required" = "必須"; - -/* Restart button title */ -"Restart" = "再起動"; - -/* Restart Required title */ -"Restart Required" = "再起動が必要です"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "このコンピューターのソフトウェアリストを取得中…"; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobeアップデートをインストール中"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobeセットアップ実行中"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobeアンインストール実行中"; - -/* No Installed Software secondary text */ -"Select software to install." = "インストールするソフトウェアを選択してください"; - -/* Sidebar Size label */ -"Size:" = "サイズ:"; - -/* Software label */ -"Software" = "ソフトウェア"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "インストールまた削除したソフトウェアがあるため再起動が必要です"; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "インストールまた削除したソフトウェアがあるため再起動が必要です。現在使用中のドキュメントの保存ができます。"; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Adobeインストーラーを開始…"; - -/* managedsoftwareupdate message */ -"Starting..." = "開始…"; - -/* Sidebar Status label */ -"Status:" = "ステータス:"; - -/* System configuration problem alert title */ -"System configuration problem" = "システムコンフィギュレーションに問題があります"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "ソフトウェアのインストールが完了しました"; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "インストールまた削除すべきアップデートが保留中になっています"; - -/* No Items primary text */ -"There are no available software items." = "使用可能なソフトウェアアイテムはありません"; - -/* No Developer Results primary text */ -"There are no items from this developer." = "このディベロッパのアイテムはありません"; - -/* No Category Results primary text */ -"There are no items in this category." = "このカテゴリにアイテムはありません"; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "他のユーザーがこのコンピューターにログインしています。\n今アップデートを実行すると、他のユーザーのワークが失われる原因になります。\n\n他のユーザーがログアウトしたのち、もう一度アップデートを実行してください。"; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "アップデートが保留されています。"; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "managed softwareインストラーのコンフィギュレーションに問題があります。インストールプロセスを開始することができません。システム管理者に連絡してください。"; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "managed softwareインストラーのコンフィギュレーションに問題があります。インストールプロセスが予期せず中断されました。システム管理者に連絡してください。"; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "このコンピューターのソフトウェアを更新する必要はありません"; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "このアイテムをダウンロードしてインストールするには空き領域が不足しています。"; - -/* Dependency List prologue text */ -"This item is required by:" = "関連アイテム:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "このアイテムは%sまでにインストールしなければいけません"; - -/* Password explanation */ -"To allow this, enter your login password." = "これを許可する為、ログインパスワードを入力して下さい。"; - -/* No Items secondary text */ -"Try again later." = "後でもう一度実行してください"; - -/* No Search Results secondary text */ -"Try searching again." = "もう一度検索してください"; - -/* No Category Results secondary text */ -"Try selecting another category." = "ほかのカテゴリを選択してしてください"; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "ほかのディベロッパを選択してしてください"; - -/* Sidebar Type label */ -"Type:" = "種類:"; - -/* Unavailable status text */ -"Unavailable" = "入手できません"; - -/* No Category name */ -"Uncategorized" = "未分類"; - -/* Update button title/action text */ -"Update" = "アップデート"; - -/* Update All button title */ -"Update All" = "すべてをアップデート"; - -/* Update Required long action text */ -"Update Required" = "アップデートが必要です"; - -/* No comment provided by engineer. */ -"Update available" = "アップデートがあります"; - -/* Update Check Failed title */ -"Update check failed" = "アップデートの確認に失敗しました"; - -/* Update In Progress primary text */ -"Update in progress." = "アップデートを実行中です"; - -/* Update Now button title */ -"Update now" = "今すぐアップデート"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "アップデートをインストールします"; - -/* Updates label */ -"Updates" = "アップデート"; - -/* Updating message */ -"Updating..." = "アップデート中…"; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "パッケージの完全性を検証中…"; - -/* Sidebar Version label */ -"Version" = "バージョン"; - -/* Sidebar Version label */ -"Version:" = "バージョン:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "ネットーワーク接続を待っています…"; - -/* Will Be Installed status text */ -"Will be installed" = "インストールします"; - -/* Will Be Removed status text */ -"Will be removed" = "削除します"; - -/* No Installed Software primary text */ -"You have no selected software." = "ソフトウェアが選択されていません"; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "インストールまた削除を進めるには、以下のアプリケーションを終了しなければいけません:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "このアイテムをインストールするにはmacOS %s 以降にアップグレードする必要があります。"; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "このコンピューターは電源に接続されていません"; - -/* No Search Results primary text */ -"Your search had no results." = "検索結果がありません"; - -/* No Pending Updates primary text */ -"Your software is up to date." = "ソフトウェアは最新です"; - -/* macOS update required text */ -"macOS update required" = "macOSアップデートが必要"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/MainMenu.strings deleted file mode 100644 index cdea7101..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ja.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "ヘルプ"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "ヘルプ"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Center ヘルプ"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "サービス"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "サービス"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Managed Software Centerを隠す"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Managed Software Centerを終了"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "ほかを隠す"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "すべてを表示"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "ウィンドウ"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "コピー"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "すべてを選択"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "カット"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "検索"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "マイアイテム"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "削除"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "ペースト"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "編集"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "編集"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "しまう"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "ズーム"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "ウィンドウ"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "ナビゲート"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "ナビゲート"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Managed Software Centerについて"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "カテゴリ"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "再読み込み"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "アップデート"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "アップデート"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "ナビゲーション"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "カテゴリ"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "カテゴリ"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "マイアイテム"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "マイアイテム"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "ソフトウェア"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "閉じる"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "戻る"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "検索"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Enter Full Screen"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "ソフトウェア"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "ソフトウェア"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "アップデート"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "ログを表示"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "次へ"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.m b/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.m deleted file mode 100644 index fbb76d50..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.m +++ /dev/null @@ -1,66 +0,0 @@ -// -// main.m -// 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. - -#import - -#if __clang_major__ >= 9 -#import -#else -#import -#endif - -int main(int argc, char *argv[]) -{ - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *resourcePath = [mainBundle resourcePath]; - NSArray *pythonPathArray = [NSArray arrayWithObjects: resourcePath, [resourcePath stringByAppendingPathComponent:@"PyObjC"], nil]; - - setenv("PYTHONPATH", [[pythonPathArray componentsJoinedByString:@":"] UTF8String], 1); - - NSArray *possibleMainExtensions = [NSArray arrayWithObjects: @"py", @"pyc", @"pyo", nil]; - NSString *mainFilePath = nil; - - for (NSString *possibleMainExtension in possibleMainExtensions) { - mainFilePath = [mainBundle pathForResource: @"main" ofType: possibleMainExtension]; - if ( mainFilePath != nil ) break; - } - - if ( !mainFilePath ) { - [NSException raise: NSInternalInconsistencyException format: @"%s:%d main() Failed to find the Main.{py,pyc,pyo} file in the application wrapper's Resources directory.", __FILE__, __LINE__]; - } - - Py_SetProgramName("/usr/bin/python"); - Py_Initialize(); - PySys_SetArgv(argc, (char **)argv); - - const char *mainFilePathPtr = [mainFilePath UTF8String]; - FILE *mainFile = fopen(mainFilePathPtr, "r"); - int result = PyRun_SimpleFile(mainFile, (char *)[[mainFilePath lastPathComponent] UTF8String]); - - if ( result != 0 ) - [NSException raise: NSInternalInconsistencyException - format: @"%s:%d main() PyRun_SimpleFile failed with file '%@'. See console for errors.", __FILE__, __LINE__, mainFilePath]; - - [pool drain]; - - return result; - -} diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.py deleted file mode 100644 index fb18e2a6..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/main.py +++ /dev/null @@ -1,28 +0,0 @@ -# encoding: utf-8 -# -# main.py -# -# Created by Greg Neagle on 2/14/13. -# - -#import modules required by application -import objc -import Foundation -import AppKit - -from PyObjCTools import AppHelper - -# import modules containing classes required to start application and -# MainMenu.nib -import MSCAppDelegate -import MSCMainWindowController -import MSCPasswordAlertController -import MSCStatusController -import MSCLogWindowController -import MSCToolbar - -# get more debugging info on exceptions -objc.setVerbose(1) - -# pass control to AppKit -AppHelper.runEventLoop() diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/mschtml.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/mschtml.py deleted file mode 100644 index e801d93d..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/mschtml.py +++ /dev/null @@ -1,745 +0,0 @@ -# encoding: utf-8 -# -# mschtml.py -# Managed Software Center -# -# Created by Greg Neagle on 2/24/14. -# - -import os - -from operator import itemgetter -from random import shuffle -from string import Template -from unicodedata import normalize - -import MunkiItems -import msclib -import msclog -import munki - -from AppKit import NSApp, NSUserDefaults -from Foundation import NSBundle -from Foundation import NSString, NSLocalizedString, NSUTF8StringEncoding - -def interfaceStyle(): - '''Returns "dark" if using Dark Mode, otherwise "light"''' - # running under Mojave or later or undocumented pref is set - if (int(os.uname()[2].split('.')[0]) >= 18 or - NSUserDefaults.standardUserDefaults().boolForKey_("AllowDarkModeOnUnsupportedOSes")): - interfaceType = NSUserDefaults.standardUserDefaults().stringForKey_("AppleInterfaceStyle") - if interfaceType == "Dark": - return "dark" - return "light" - -def quote(a_string): - '''Replacement for urllib.quote that handles Unicode strings''' - return str(NSString.stringWithString_( - a_string).stringByAddingPercentEscapesUsingEncoding_( - NSUTF8StringEncoding)) - -def unquote(a_string): - '''Replacement for urllib.unquote that handles Unicode strings''' - return str(NSString.stringWithString_( - a_string).stringByReplacingPercentEscapesUsingEncoding_( - NSUTF8StringEncoding)) - - -def get_template(template_name, raw=False): - '''return an html template. If raw is True, just return the string; otherwise - return a string Template object''' - customTemplatesPath = os.path.join(msclib.html_dir(), 'custom/templates') - resourcesPath = NSBundle.mainBundle().resourcePath() - defaultTemplatesPath = os.path.join(resourcesPath, 'templates') - for directory in [customTemplatesPath, defaultTemplatesPath]: - templatePath = os.path.join(directory, template_name) - if os.path.exists(templatePath): - try: - file_ref = open(templatePath) - template_html = file_ref.read() - file_ref.close() - if raw: - return template_html.decode('utf-8') - else: - return Template(template_html.decode('utf-8')) - except (IOError, OSError): - return None - return None - -def build_page(filename): - '''Dispatch request to build a page to the appropriate function''' - msclog.debug_log(u'build_page for %s' % filename) - name = os.path.splitext(filename)[0] - key, p, value = name.partition('-') - if key == 'detail': - build_detail_page(value) - elif key == 'category': - build_list_page(category=value) - elif key == 'categories': - build_categories_page() - elif key == 'filter': - build_list_page(filter=value) - elif key == 'developer': - build_list_page(developer=value) - elif key == 'myitems': - build_myitems_page() - elif key == 'updates': - build_updates_page() - elif key == 'updatedetail': - build_updatedetail_page(value) - else: - build_item_not_found_page(filename) - - -def write_page(page_name, html): - '''write html to page_name in our local html directory''' - html_file = os.path.join(msclib.html_dir(), page_name) - try: - f = open(html_file, 'w') - f.write(html.encode('utf-8')) - f.close() - except (OSError, IOError), err: - msclog.debug_log('write_page error: %s', str(err)) - raise - - -def assemble_page(main_page_template_name, page_dict, **kwargs): - '''Returns HTML for our page from one or more templates - and a dictionary of keys and values''' - # add current appearance style/theme - page_dict["data_theme"] = interfaceStyle() - # make sure our general labels are present - addGeneralLabels(page_dict) - # get our main template - main_page = get_template(main_page_template_name) - # incorporate any sub-templates - html_template = Template(main_page.safe_substitute(**kwargs)) - # substitute page variables - html = html_template.safe_substitute(page_dict) - return html - - -def generate_page(page_name, main_page_template_name, page_dict, **kwargs): - '''Assembles HTML and writes the page to page_name in our local html directory''' - msclog.debug_log('generate_page for %s' % page_name) - html = assemble_page(main_page_template_name, page_dict, **kwargs) - write_page(page_name, html) - - -def escape_quotes(text): - """Escape single and double-quotes for JavaScript""" - return text.replace("'", r"\'").replace('"', r'\"') - - -def escape_html(text): - """Convert some problematic characters to entities""" - html_escape_table = { - "&": "&", - '"': """, - "'": "'", - ">": ">", - "<": "<", - } - return "".join(html_escape_table.get(c, c) for c in text) - - -def escapeAndQuoteCommonFields(item): - '''Adds _escaped and _quoted versions of several commonly-used fields''' - item['name_escaped'] = escape_html(item['name']) - item['name_quoted'] = escape_html(escape_quotes(item['name'])) - item['display_name_escaped'] = escape_html(item['display_name']) - item['developer_escaped'] = escape_html(item['developer']) - item['display_version_escaped'] = escape_html(item['display_version']) - - -def addGeneralLabels(page): - '''adds localized labels for Software, Categories, My Items and Updates to html pages''' - page['SoftwareLabel'] = NSLocalizedString(u"Software", u"Software label") - page['CategoriesLabel'] = NSLocalizedString(u"Categories", u"Categories label") - page['MyItemsLabel'] = NSLocalizedString(u"My Items", u"My Items label") - page['UpdatesLabel'] = NSLocalizedString(u"Updates", u"Updates label") - - -def addDetailSidebarLabels(page): - '''adds localized labels for the detail view sidebars''' - page['informationLabel'] = NSLocalizedString( - u"Information", - u"Sidebar Information label") - page['categoryLabel'] = NSLocalizedString( - u"Category:", - u"Sidebar Category label") - page['versionLabel'] = NSLocalizedString( - u"Version:", - u"Sidebar Version label") - page['sizeLabel'] = NSLocalizedString( - u"Size:", - u"Sidebar Size label") - page['developerLabel'] = NSLocalizedString( - u"Developer:", - u"Sidebar Developer label") - page['statusLabel'] = NSLocalizedString( - u"Status:", u"Sidebar Status label") - page['moreByDeveloperLabel'] = NSLocalizedString( - u"More by %s", - u"Sidebar More By Developer label") - page['moreInCategoryLabel'] = NSLocalizedString( - u"More in %s", - u"Sidebar More In Category label") - page['typeLabel'] = NSLocalizedString( - u"Type:", u"Sidebar Type label") - page['dueLabel'] = NSLocalizedString( - u"Due:", u"Sidebar Due label") - - -def build_item_not_found_page(page_name): - '''Build item not found page''' - page = {} - page['item_not_found_title'] = NSLocalizedString( - u"Not Found", u"Item Not Found title") - page['item_not_found_message'] = NSLocalizedString( - u"Cannot display the requested item.", u"Item Not Found message") - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'page_not_found_template.html', page, footer=footer) - - -def build_detail_page(item_name): - '''Build page showing detail for a single optional item''' - msclog.debug_log('build_detail_page for %s' % item_name) - items = MunkiItems.getOptionalInstallItems() - page_name = u'detail-%s.html' % item_name - for item in items: - if item['name'] == item_name: - # make a copy of the item to use to build our page - page = MunkiItems.OptionalItem(item) - escapeAndQuoteCommonFields(page) - addDetailSidebarLabels(page) - # make "More in CategoryFoo" list - page['hide_more_in_category'] = u'hidden' - more_in_category_html = u'' - more_in_category = [] - if item.get('category'): - category = item['category'] - page['category_link'] = u'category-%s.html' % quote(category) - more_in_category = [a for a in items - if a.get('category') == category - and a != item - and a.get('status') != 'installed'] - if more_in_category: - page['hide_more_in_category'] = u'' - page['moreInCategoryLabel'] = page['moreInCategoryLabel'] % page['category'] - shuffle(more_in_category) - more_template = get_template('detail_more_items_template.html') - for more_item in more_in_category[:4]: - more_item['display_name_escaped'] = escape_html(more_item['display_name']) - more_item['second_line'] = more_item.get('developer', '') - more_in_category_html += more_template.safe_substitute(more_item) - page['more_in_category'] = more_in_category_html - # make "More by DeveloperFoo" list - page['hide_more_by_developer'] = u'hidden' - more_by_developer_html = u'' - more_by_developer = [] - if item.get('developer'): - developer = item['developer'] - page['developer_link'] = u'developer-%s.html' % quote(developer) - more_by_developer = [a for a in items - if a.get('developer') == developer - and a != item - and a not in more_in_category - and a.get('status') != 'installed'] - if more_by_developer: - page['hide_more_by_developer'] = u'' - page['moreByDeveloperLabel'] = ( - page['moreByDeveloperLabel'] % developer) - shuffle(more_by_developer) - more_template = get_template( - 'detail_more_items_template.html') - for more_item in more_by_developer[:4]: - escapeAndQuoteCommonFields(more_item) - more_item['second_line'] = more_item.get('category', '') - more_by_developer_html += more_template.safe_substitute(more_item) - page['more_by_developer'] = more_by_developer_html - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'detail_template.html', page, footer=footer) - return - msclog.debug_log('No detail found for %s' % item_name) - build_item_not_found_page(page_name) - - -def build_list_page(category=None, developer=None, filter=None): - '''Build page listing available optional items''' - items = MunkiItems.getOptionalInstallItems() - - header = NSLocalizedString(u"All items", u"AllItemsHeaderText") - page_name = u'category-all.html' - if category == 'all': - category = None - if category: - header = category - page_name = u'category-%s.html' % category - if developer: - header = developer - page_name = u'developer-%s.html' % developer - if filter: - header = u'Search results for %s' % filter - page_name = u'filter-%s.html' % filter - - featured_items = [item for item in items if item.get('featured')] - if (featured_items and category is None and developer is None and - filter is None): - header = NSLocalizedString(u"Featured items", - u"FeaturedItemsHeaderText") - - category_list = [] - for item in items: - if 'category' in item and item['category'] not in category_list: - category_list.append(item['category']) - - item_html = build_list_page_items_html( - category=category, developer=developer, filter=filter) - - # make HTML for Categories pop-up menu - if featured_items: - all_categories_label = NSLocalizedString(u"Featured", u"FeaturedLabel") - else: - all_categories_label = NSLocalizedString(u"All Categories", - u"AllCategoriesLabel") - if category: - categories_html = u'%s\n' % all_categories_label - else: - categories_html = u'%s\n' % all_categories_label - - for item in sorted(category_list): - if item == category: - categories_html += u'%s\n' % item - else: - categories_html += u'%s\n' % item - - categories_html_list = '' - # make HTML for list of categories - for item in sorted(category_list): - categories_html_list += (u'%s\n' - % (quote(item), item)) - - page = {} - page['list_items'] = item_html - page['category_items'] = categories_html - page['category_list'] = categories_html_list - page['header_text'] = header - if category or filter or developer: - showcase = '' - else: - showcase = get_template('showcase_template.html', raw=True) - sidebar = get_template('sidebar_template.html', raw=True) - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'list_template.html', page, - showcase=showcase, sidebar=sidebar, footer=footer) - - -def build_list_page_items_html(category=None, developer=None, filter=None): - '''Returns HTML for the items on the list page''' - items = MunkiItems.getOptionalInstallItems() - item_html = u'' - if filter: - # since the filter term came through the filesystem, - # HFS+ does some unicode character decomposition which can cause issue - # with comparisons - # so before we do our comparison, we normalize the unicode string - # using unicodedata.normalize - filter = normalize('NFC', filter) - msclog.debug_log(u'Filtering on %s' % filter) - items = [item for item in items - if filter in item['display_name'].lower() - or filter in item['description'].lower() - or filter in item['developer'].lower() - or filter in item['category'].lower()] - if category: - items = [item for item in items - if category.lower() == item.get('category', '').lower()] - if developer: - items = [item for item in items - if developer.lower() == item.get('developer', '').lower()] - - if category is None and developer is None and filter is None: - # this is the default (formerly) "all items" view - # look for featured items and display those if we have them - featured_items = [item for item in items if item.get('featured')] - if featured_items: - items = featured_items - - if items: - item_template = get_template('list_item_template.html') - for item in sorted(items, key=itemgetter('display_name_lower')): - escapeAndQuoteCommonFields(item) - item['category_and_developer_escaped'] = escape_html(item['category_and_developer']) - item_html += item_template.safe_substitute(item) - # pad with extra empty items so we have a multiple of 3 - if len(items) % 3: - for x in range(3 - (len(items) % 3)): - item_html += u'\n' - else: - # no items; build appropriate alert messages - status_results_template = get_template('status_results_template.html') - alert = {} - if filter: - alert['primary_status_text'] = NSLocalizedString( - u"Your search had no results.", - u"No Search Results primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"Try searching again.", u"No Search Results secondary text") - elif category: - alert['primary_status_text'] = NSLocalizedString( - u"There are no items in this category.", - u"No Category Results primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"Try selecting another category.", - u"No Category Results secondary text") - elif developer: - alert['primary_status_text'] = NSLocalizedString( - u"There are no items from this developer.", - u"No Developer Results primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"Try selecting another developer.", - u"No Developer Results secondary text") - else: - alert['primary_status_text'] = NSLocalizedString( - u"There are no available software items.", - u"No Items primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"Try again later.", - u"No Items secondary text") - alert['hide_progress_bar'] = u'hidden' - alert['progress_bar_value'] = u'' - item_html = status_results_template.safe_substitute(alert) - return item_html - - -def build_categories_page(): - '''Build page showing available categories and some items in each one''' - all_items = MunkiItems.getOptionalInstallItems() - header = NSLocalizedString(u"Categories", u"Categories label") - page_name = u'categories.html' - category_list = [] - for item in all_items: - if 'category' in item and item['category'] not in category_list: - category_list.append(item['category']) - - item_html = build_category_items_html() - - all_categories_label = NSLocalizedString(u"All Categories", u"AllCategoriesLabel") - categories_html = u'%s\n' % all_categories_label - for item in sorted(category_list): - categories_html += u'%s\n' % item - - page = {} - page['list_items'] = item_html - page['category_items'] = categories_html - page['header_text'] = header - - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'list_template.html', page, showcase=u'', sidebar=u'', footer=footer) - - -def build_category_items_html(): - '''Returns HTML for the items on the Categories page''' - all_items = MunkiItems.getOptionalInstallItems() - if all_items: - category_list = [] - for item in all_items: - if 'category' in item and item['category'] not in category_list: - category_list.append(item['category']) - - item_template = get_template('category_item_template.html') - item_html = u'' - for category in sorted(category_list): - category_data = {} - category_data['category_name_escaped'] = escape_html(category) - category_data['category_link'] = u'category-%s.html' % quote(category) - category_items = [item for item in all_items if item.get('category') == category] - shuffle(category_items) - category_data['item1_icon'] = category_items[0]['icon'] - category_data['item1_display_name_escaped'] = escape_html( - category_items[0]['display_name']) - category_data['item1_detail_link'] = category_items[0]['detail_link'] - if len(category_items) > 1: - category_data['item2_display_name_escaped'] = escape_html( - category_items[1]['display_name']) - category_data['item2_detail_link'] = category_items[1]['detail_link'] - else: - category_data['item2_display_name_escaped'] = u'' - category_data['item2_detail_link'] = u'#' - if len(category_items) > 2: - category_data['item3_display_name_escaped'] = escape_html( - category_items[2]['display_name']) - category_data['item3_detail_link'] = category_items[2]['detail_link'] - else: - category_data['item3_display_name_escaped'] = u'' - category_data['item3_detail_link'] = u'#' - - item_html += item_template.safe_substitute(category_data) - - # pad with extra empty items so we have a multiple of 3 - if len(category_list) % 3: - for x in range(3 - (len(category_list) % 3)): - item_html += u'\n' - - else: - # no items - status_results_template = get_template('status_results_template.html') - alert = {} - alert['primary_status_text'] = NSLocalizedString( - u"There are no available software items.", - u"No Items primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"Try again later.", - u"No Items secondary text") - alert['hide_progress_bar'] = u'hidden' - alert['progress_bar_value'] = u'' - item_html = status_results_template.safe_substitute(alert) - return item_html - - -def build_myitems_page(): - '''Builds "My Items" page, which shows all current optional choices''' - page_name = u'myitems.html' - - page = {} - page['my_items_header_label'] = NSLocalizedString( - u"My Items", u"My Items label") - page['myitems_rows'] = build_myitems_rows() - - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'myitems_template.html', page, footer=footer) - - -def build_myitems_rows(): - '''Returns HTML for the items on the 'My Items' page''' - item_list = MunkiItems.getMyItemsList() - if item_list: - item_template = get_template('myitems_row_template.html') - myitems_rows = u'' - for item in sorted(item_list, key=itemgetter('display_name_lower')): - escapeAndQuoteCommonFields(item) - myitems_rows += item_template.safe_substitute(item) - else: - status_results_template = get_template('status_results_template.html') - alert = {} - alert['primary_status_text'] = NSLocalizedString( - u"You have no selected software.", - u"No Installed Software primary text") - alert['secondary_status_text'] = ( - u'%s' % NSLocalizedString( - u"Select software to install.", - u"No Installed Software secondary text")) - alert['hide_progress_bar'] = u'hidden' - myitems_rows = status_results_template.safe_substitute(alert) - return myitems_rows - - -def build_updates_page(): - '''available/pending updates''' - page_name = u'updates.html' - - # need to consolidate/centralize this flag. Accessing it this way is ugly. - if NSApp.delegate().mainWindowController._update_in_progress: - return build_update_status_page() - - item_list = MunkiItems.getEffectiveUpdateList() - - problem_updates = MunkiItems.getProblemItems() - for item in problem_updates: - item['hide_cancel_button'] = u'hidden' - - # find any optional installs with update available - other_updates = [ - item for item in MunkiItems.getOptionalInstallItems() - if item['status'] == 'update-available'] - - # find any listed optional install updates that require a higher OS - # or have insufficient disk space or other blockers (because they have a - # note) - blocked_optional_updates = [ - item for item in MunkiItems.getOptionalInstallItems() - if item['status'] == 'installed' and item.get('note')] - for item in blocked_optional_updates: - item['hide_cancel_button'] = u'hidden' - - other_updates.extend(blocked_optional_updates) - - page = {} - page['update_rows'] = u'' - page['hide_progress_spinner'] = u'hidden' - page['hide_problem_updates'] = u'hidden' - page['hide_other_updates'] = u'hidden' - page['install_all_button_classes'] = u'' - - item_template = get_template('update_row_template.html') - - # build pending updates table - if item_list: - for item in item_list: - escapeAndQuoteCommonFields(item) - page['update_rows'] += item_template.safe_substitute(item) - elif not other_updates and not problem_updates: - status_results_template = get_template('status_results_template.html') - alert = {} - alert['primary_status_text'] = NSLocalizedString( - u"Your software is up to date.", u"No Pending Updates primary text") - alert['secondary_status_text'] = NSLocalizedString( - u"There is no new software for your computer at this time.", - u"No Pending Updates secondary text") - alert['hide_progress_bar'] = u'hidden' - alert['progress_bar_value'] = u'' - page['update_rows'] = status_results_template.safe_substitute(alert) - - count = len([item for item in item_list if item['status'] != 'problem-item']) - page['update_count'] = msclib.updateCountMessage(count) - page['install_btn_label'] = msclib.getInstallAllButtonTextForCount(count) - page['warning_text'] = get_warning_text() - - # build problem updates table - page['problem_updates_header_message'] = NSLocalizedString( - u"Problem updates", - u"Problem Updates label") - page['problem_update_rows'] = u'' - - if problem_updates: - page['hide_problem_updates'] = u'' - for item in problem_updates: - escapeAndQuoteCommonFields(item) - page['problem_update_rows'] += item_template.safe_substitute(item) - - # build other available updates table - page['other_updates_header_message'] = NSLocalizedString( - u"Other available updates", - u"Other Available Updates label") - page['other_update_rows'] = u'' - - if other_updates: - page['hide_other_updates'] = u'' - for item in other_updates: - escapeAndQuoteCommonFields(item) - page['other_update_rows'] += item_template.safe_substitute(item) - - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'updates_template.html', page, footer=footer) - - -def build_update_status_page(): - '''returns our update status page''' - page_name = u'updates.html' - item_list = [] - other_updates = [] - - status_title_default = NSLocalizedString(u"Checking for updates...", - u"Checking For Updates message") - page = {} - page['update_rows'] = u'' - page['hide_progress_spinner'] = u'' - page['hide_problem_updates'] = u'hidden' - page['hide_other_updates'] = u'hidden' - page['other_updates_header_message'] = u'' - page['other_update_rows'] = u'' - - # don't like this bit as it ties us to a different object - status_controller = NSApp.delegate().statusController - status_results_template = get_template('status_results_template.html') - alert = {} - alert['primary_status_text'] = ( - status_controller._status_message - or NSLocalizedString(u"Update in progress.", u"Update In Progress primary text")) - alert['secondary_status_text'] = (status_controller._status_detail or ' ') - alert['hide_progress_bar'] = u'' - if status_controller._status_percent < 0: - alert['progress_bar_attributes'] = u'class="indeterminate"' - else: - alert['progress_bar_attributes'] = (u'style="width: %s%%"' - % status_controller._status_percent) - page['update_rows'] = status_results_template.safe_substitute(alert) - - install_all_button_classes = [] - if status_controller._status_stopBtnHidden: - install_all_button_classes.append(u'hidden') - if status_controller._status_stopBtnDisabled: - install_all_button_classes.append(u'disabled') - page['install_all_button_classes'] = u' '.join(install_all_button_classes) - - # don't like this bit as it ties us yet another object - page['update_count'] = NSApp.delegate().mainWindowController._status_title or status_title_default - page['install_btn_label'] = NSLocalizedString(u"Cancel", u"Cancel button title/short action text") - page['warning_text'] = u'' - - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'updates_template.html', page, footer=footer) - - -def getRestartActionForUpdateList(update_list): - '''Returns a localized overall restart action message for the list of updates''' - if not update_list: - return '' - if [item for item in update_list if 'Restart' in item.get('RestartAction', '')]: - # found at least one item containing 'Restart' in its RestartAction - return NSLocalizedString(u"Restart Required", u"Restart Required title") - if ([item for item in update_list if 'Logout' in item.get('RestartAction', '')] - or munki.installRequiresLogout()): - # found at least one item containing 'Logout' in its RestartAction - return NSLocalizedString(u"Logout Required", u"Logout Required title") - else: - return '' - - -def get_warning_text(): - '''Return localized text warning about forced installs and/or - logouts and/or restarts''' - item_list = MunkiItems.getEffectiveUpdateList() - warning_text = u'' - forced_install_date = munki.earliestForceInstallDate(item_list) - if forced_install_date: - date_str = munki.stringFromDate(forced_install_date) - forced_date_text = NSLocalizedString( - u"One or more items must be installed by %s", - u"Forced Install Date summary") - warning_text = forced_date_text % date_str - restart_text = getRestartActionForUpdateList(item_list) - if restart_text: - if warning_text: - warning_text += u' • ' + restart_text - else: - warning_text = restart_text - return warning_text - - -def build_updatedetail_page(identifier): - '''Build detail page for a non-optional update''' - items = MunkiItems.getUpdateList() + MunkiItems.getProblemItems() - page_name = u'updatedetail-%s.html' % identifier - name, sep, version = identifier.partition('--version-') - for item in items: - if item['name'] == name and item.get('version_to_install', '') == version: - page = MunkiItems.UpdateItem(item) - escapeAndQuoteCommonFields(page) - addDetailSidebarLabels(page) - force_install_after_date = item.get('force_install_after_date') - if force_install_after_date: - try: - local_date = munki.discardTimeZoneFromDate( - force_install_after_date) - date_str = munki.shortRelativeStringFromDate( - local_date) - page['dueLabel'] += u' ' - page['short_due_date'] = date_str - except munki.BadDateError: - # some issue with the stored date - msclog.debug_log('Problem with force_install_after_date for %s' % identifier) - page['dueLabel'] = u'' - page['short_due_date'] = u'' - else: - page['dueLabel'] = u'' - page['short_due_date'] = u'' - - footer = get_template('footer_template.html', raw=True) - generate_page(page_name, 'updatedetail_template.html', page, footer=footer) - return - # if we get here we didn't find any item matching identifier - msclog.debug_log('No update detail found for %s' % identifier) - build_item_not_found_page(page_name) - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclib.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclib.py deleted file mode 100644 index 78d76f69..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclib.py +++ /dev/null @@ -1,136 +0,0 @@ -# encoding: utf-8 -# -# msclib.py -# -# Created by Greg Neagle on 12/10/13. -# Copyright 2010-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. -'''Some functions used a few places that don't (yet) have an obvious home''' - - -import os -import sys - -import shutil - -from zipfile import ZipFile, BadZipfile - -from Foundation import * -from AppKit import * - -import msclog -import munki - -_html_dir = None - - -def updateCountMessage(count): - '''Return a localized message describing the count of updates to install''' - if count == 0: - return NSLocalizedString(u"No pending updates", u"No Updates message") - if count == 1: - return NSLocalizedString(u"1 pending update", u"One Update message") - else: - return (NSLocalizedString(u"%s pending updates", u"Multiple Updates message") % count) - - -def getInstallAllButtonTextForCount(count): - '''Return localized display text for action button in Updates view''' - if count == 0: - return NSLocalizedString(u"Check Again", u"Check Again button title") - elif count == 1: - return NSLocalizedString(u"Update", u"Update button title/action text") - else: - return NSLocalizedString(u"Update All", u"Update All button title") - - -def get_custom_resources(): - '''copies custom resources into our html dir''' - if not _html_dir: - return - managed_install_dir = munki.pref('ManagedInstallDir') - source_path = os.path.join(managed_install_dir, 'client_resources/custom.zip') - if os.path.exists(source_path): - dest_path = os.path.join(_html_dir, 'custom') - if os.path.exists(dest_path): - try: - shutil.rmtree(dest_path, ignore_errors=True) - except (OSError, IOError), err: - msclog.debug_log('Error clearing %s: %s' % (dest_path, err)) - if not os.path.exists(dest_path): - try: - os.mkdir(dest_path) - except (OSError, IOError), err: - msclog.debug_log('Error creating %s: %s' % (dest_path, err)) - try: - archive = ZipFile(source_path) - except BadZipfile: - # ignore it - return - archive_files = archive.namelist() - # sanity checking in case the archive is not built correctly - files_to_extract = [filename for filename in archive_files - if filename.startswith('resources/') - or filename.startswith('templates/')] - if not files_to_extract: - msclog.debug_log('Invalid client resources archive.') - for filename in files_to_extract: - try: - if filename.endswith('/'): - # it's a directory. The extract method in Python 2.6 handles this wrong - # do we'll do it ourselves - os.makedirs(os.path.join(dest_path, filename)) - else: - archive.extract(filename, dest_path) - except (OSError, IOError), err: - msclog.debug_log('Error expanding %s from archive: %s' % (filename, err)) - - -def html_dir(): - '''sets up our local html cache directory''' - global _html_dir - if _html_dir: - return _html_dir - bundle_id = NSBundle.mainBundle().bundleIdentifier() - cache_dir_urls = NSFileManager.defaultManager().URLsForDirectory_inDomains_( - NSCachesDirectory, NSUserDomainMask) - if cache_dir_urls: - cache_dir = cache_dir_urls[0].path() - else: - cache_dir = u'/private/tmp' - our_cache_dir = os.path.join(cache_dir, bundle_id) - if not os.path.exists(our_cache_dir): - os.makedirs(our_cache_dir) - _html_dir = os.path.join(our_cache_dir, 'html') - if os.path.exists(_html_dir): - # empty it - shutil.rmtree(_html_dir) - os.mkdir(_html_dir) - - # symlink our static files dir - resourcesPath = NSBundle.mainBundle().resourcePath() - source_path = os.path.join(resourcesPath, 'WebResources') - link_path = os.path.join(_html_dir, 'static') - os.symlink(source_path, link_path) - - # symlink the Managed Installs icons dir - managed_install_dir = munki.pref('ManagedInstallDir') - source_path = os.path.join(managed_install_dir, 'icons') - link_path = os.path.join(_html_dir, 'icons') - os.symlink(source_path, link_path) - - # unzip any custom client resources - get_custom_resources() - - return _html_dir diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclog.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclog.py deleted file mode 100644 index 879a15e2..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/msclog.py +++ /dev/null @@ -1,207 +0,0 @@ -# encoding: utf-8 -# -# msclog.py -# Managed Software Center -# -# Created by Greg Neagle on 2/23/14. -# Original by John Randolph -# -# 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. - -'''Implements client logging for Managed Software Center.app''' - -import logging -import os -import random -import stat - -import munki - -from Foundation import NSLog - -MSULOGDIR = \ - "/Users/Shared/.com.googlecode.munki.ManagedSoftwareUpdate.logs" -MSULOGFILE = "%s.log" -MSULOGENABLED = False -MSUDEBUGLOGENABLED = False - -class FleetingFileHandler(logging.FileHandler): - """File handler which opens/closes the log file only during log writes.""" - - def __init__(self, filename, mode='a', encoding=None, delay=True): - if hasattr(self, '_open'): # if py2.6+ ... - logging.FileHandler.__init__(self, filename, mode, encoding, delay) - else: - logging.FileHandler.__init__(self, filename, mode, encoding) - # lots of py <=2.5 fixes to support delayed open and immediate - # close. - self.encoding = encoding - self._open = self.__open - self.flush = self.__flush - self._close() - - def __open(self): - """Open the log file.""" - if self.encoding is None: - stream = open(self.baseFilename, self.mode) - else: - stream = logging.codecs.open( - self.baseFilename, self.mode, self.encoding) - return stream - - def __flush(self): - """Flush the stream if it is open.""" - if self.stream: - self.stream.flush() - - def _close(self): - """Close the log file if it is open.""" - if self.stream: - self.flush() - if hasattr(self.stream, 'close'): - self.stream.close() - self.stream = None - - def close(self): - """Close the entire handler if it is open.""" - if self.stream: - return logging.FileHandler.close(self) - - def emit(self, record): - """Open the log, emit a record and close the log.""" - if self.stream is None: - self.stream = self._open() - logging.FileHandler.emit(self, record) - self._close() - - -def setup_logging(username=None): - """Setup logging module. - - Args: - username: str, optional, current login name - """ - global MSULOGENABLED - global MSUDEBUGLOGENABLED - - if (logging.root.handlers and - logging.root.handlers[0].__class__ is FleetingFileHandler): - return - - if munki.pref('MSUDebugLogEnabled'): - MSUDEBUGLOGENABLED = True - - if munki.pref('MSULogEnabled'): - MSULOGENABLED = True - - if not MSULOGENABLED: - return - - if username is None: - username = os.getlogin() or 'UID%d' % os.getuid() - - if not os.path.exists(MSULOGDIR): - try: - os.mkdir(MSULOGDIR, 01777) - except OSError, err: - logging.error('mkdir(%s): %s', MSULOGDIR, str(err)) - return - - if not os.path.isdir(MSULOGDIR): - logging.error('%s is not a directory', MSULOGDIR) - return - - # freshen permissions, if possible. - try: - os.chmod(MSULOGDIR, 01777) - except OSError: - pass - - # find a safe log file to write to for this user - filename = os.path.join(MSULOGDIR, MSULOGFILE % username) - attempt = 0 - ours = False - - while attempt < 10: - try: - fref = os.open(filename, os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW, 0600) - st = os.fstat(fref) - ours = stat.S_ISREG(st.st_mode) and st.st_uid == os.getuid() - os.close(fref) - if ours: - break - except (OSError, IOError): - pass # permission denied, symlink, ... - - # avoid creating many separate log files by using one static suffix - # as the first alternative. if unsuccessful, switch to totally - # randomly suffixed files. - if attempt == 0: - random.seed(hash(username)) - elif attempt == 1: - random.seed() - - filename = os.path.join( - MSULOGDIR, MSULOGFILE % ( - '%s_%d' % (username, random.randint(0, 2**32)))) - - attempt += 1 - - if not ours: - logging.error('No logging is possible') - return - - # setup log handler - - log_format = '%(created)f %(levelname)s ' + username + ' : %(message)s' - ffh = None - - try: - ffh = FleetingFileHandler(filename) - except IOError, err: - logging.error('Error opening log file %s: %s', filename, str(err)) - - ffh.setFormatter(logging.Formatter(log_format, None)) - logging.root.addHandler(ffh) - logging.getLogger().setLevel(logging.INFO) - - -def log(source, event, msg=None, *args): - """Log an event from a source. - - Args: - source: str, like "MSU" or "user" - event: str, like "exit" - msg: str, optional, additional log output - args: list, optional, arguments supplied to msg as format args - """ - if not MSULOGENABLED: - return - - if msg: - if args: - logging.info('@@%s:%s@@ ' + msg, source, event, *args) - else: - logging.info('@@%s:%s@@ %s', source, event, msg) - else: - logging.info('@@%s:%s@@', source, event) - - -def debug_log(msg): - """Log to Apple System Log facility and also to MSU log if configured""" - if MSUDEBUGLOGENABLED: - NSLog('%@', msg) - log('MSC', 'debug', msg) - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/munki.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/munki.py deleted file mode 100644 index a4f935b7..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/munki.py +++ /dev/null @@ -1,595 +0,0 @@ -# encoding: utf-8 -# -# munki.py -# Managed Software Center -# -# Created by Greg Neagle on 2/11/10. -# Copyright 2010-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. - -'''munki-specific code for use with Managed Software Center''' - -import os -import subprocess -import FoundationPlist - -import objc - -# PyLint cannot properly find names inside Cocoa libraries, so issues bogus -# No name 'Foo' in module 'Bar' warnings. Disable them. -# pylint: disable=E0611 -from Foundation import NSTimeZone -from Foundation import NSBundle -from Foundation import NSDate -from Foundation import NSFileManager -from Foundation import CFPreferencesCopyAppValue -from Foundation import CFPreferencesAppSynchronize -from Foundation import NSDateFormatter -from Foundation import NSDateFormatterBehavior10_4 -from Foundation import kCFDateFormatterLongStyle -from Foundation import kCFDateFormatterShortStyle -from SystemConfiguration import SCDynamicStoreCopyConsoleUser -# pylint: enable=E0611 - -# See http://michaellynn.github.io/2015/08/08/learn-you-a-better-pyobjc-bridgesupport-signature/ -# for a primer on the bridging techniques used here -# - -# https://developer.apple.com/documentation/iokit/iopowersources.h?language=objc -IOKit = NSBundle.bundleWithIdentifier_('com.apple.framework.IOKit') - -functions = [("IOPSGetPowerSourceDescription", b"@@@"), - ("IOPSCopyPowerSourcesInfo", b"@"), - ("IOPSCopyPowerSourcesList", b"@@"), - ("IOPSGetProvidingPowerSourceType", b"@@"), - ] - -# No idea why PyLint complains about objc.loadBundleFunctions -# pylint: disable=no-member -objc.loadBundleFunctions(IOKit, globals(), functions) -# pylint: enable=no-member - -# Disable PyLint complaining about 'invalid' camelCase names -# pylint: disable=C0103 - - -INSTALLATLOGOUTFILE = "/private/tmp/com.googlecode.munki.installatlogout" -UPDATECHECKLAUNCHFILE = \ - "/private/tmp/.com.googlecode.munki.updatecheck.launchd" -INSTALLWITHOUTLOGOUTFILE = \ - "/private/tmp/.com.googlecode.munki.managedinstall.launchd" - -def call(cmd): - '''Convenience function; works around an issue with subprocess.call - in PyObjC in Snow Leopard''' - proc = subprocess.Popen(cmd, bufsize=-1, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - _ = proc.communicate() - return proc.returncode - - -def osascript(osastring): - """Wrapper to run AppleScript commands""" - cmd = ['/usr/bin/osascript', '-e', osastring] - proc = subprocess.Popen(cmd, shell=False, bufsize=-1, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, _) = proc.communicate() - if proc.returncode != 0: - return '' - if out: - return str(out).decode('UTF-8').rstrip('\n') - - -def restartNow(): - '''Trigger a restart''' - osascript('tell application "System Events" to restart') - - -BUNDLE_ID = u'ManagedInstalls' - -def reload_prefs(): - """Uses CFPreferencesAppSynchronize(BUNDLE_ID) - to make sure we have the latest prefs. Call this - if another process may have modified ManagedInstalls.plist, - this needs to be run after returning from MunkiStatus""" - CFPreferencesAppSynchronize(BUNDLE_ID) - -DEFAULT_GUI_CACHE_AGE_SECS = 600 - -def pref(pref_name): - """Return a preference. Since this uses CFPreferencesCopyAppValue, - Preferences can be defined several places. Precedence is: - - MCX - - ~/Library/Preferences/ManagedInstalls.plist - - /Library/Preferences/ManagedInstalls.plist - - default_prefs defined here. - """ - default_prefs = { - 'ManagedInstallDir': '/Library/Managed Installs', - 'InstallAppleSoftwareUpdates': False, - 'AppleSoftwareUpdatesOnly': False, - 'ShowRemovalDetail': False, - 'InstallRequiresLogout': False, - 'CheckResultsCacheSeconds': DEFAULT_GUI_CACHE_AGE_SECS, - } - pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID) - if pref_value is None: - pref_value = default_prefs.get(pref_name) - #if type(pref_value).__name__ in ['__NSCFDate', '__NSDate', '__CFDate']: - # convert NSDate/CFDates to strings - #pref_value = str(pref_value) - return pref_value - - -WRITEABLE_SELF_SERVICE_MANIFEST_PATH = "/Users/Shared/.SelfServeManifest" - -def readSelfServiceManifest(): - '''Read the SelfServeManifest if it exists''' - # read our working copy if it exists - SelfServeManifest = WRITEABLE_SELF_SERVICE_MANIFEST_PATH - if not os.path.exists(SelfServeManifest): - # no working copy, look for system copy - managedinstallbase = pref('ManagedInstallDir') - SelfServeManifest = os.path.join(managedinstallbase, "manifests", - "SelfServeManifest") - if os.path.exists(SelfServeManifest): - try: - return FoundationPlist.readPlist(SelfServeManifest) - except FoundationPlist.NSPropertyListSerializationException: - return {} - else: - return {} - - -def writeSelfServiceManifest(optional_install_choices): - '''Write out our self-serve manifest - so managedsoftwareupdate can use it. Returns True on success, - False otherwise.''' - usermanifest = WRITEABLE_SELF_SERVICE_MANIFEST_PATH - try: - FoundationPlist.writePlist(optional_install_choices, usermanifest) - return True - except FoundationPlist.FoundationPlistException: - return False - - -def userSelfServiceChoicesChanged(): - '''Is WRITEABLE_SELF_SERVICE_MANIFEST_PATH different from - the 'system' version of this file?''' - if not os.path.exists(WRITEABLE_SELF_SERVICE_MANIFEST_PATH): - return False - user_choices = FoundationPlist.readPlist( - WRITEABLE_SELF_SERVICE_MANIFEST_PATH) - managedinstallbase = pref('ManagedInstallDir') - system_path = os.path.join(managedinstallbase, "manifests", - "SelfServeManifest") - if not os.path.exists(system_path): - return True - system_choices = FoundationPlist.readPlist(system_path) - return user_choices != system_choices - - -def getRemovalDetailPrefs(): - '''Returns preference to control display of removal detail''' - return pref('ShowRemovalDetail') - - -def installRequiresLogout(): - '''Returns preference to force logout for all installs''' - return pref('InstallRequiresLogout') - - -def getInstallInfo(): - '''Returns the dictionary describing the managed installs and removals''' - managedinstallbase = pref('ManagedInstallDir') - plist = {} - installinfo = os.path.join(managedinstallbase, 'InstallInfo.plist') - if os.path.exists(installinfo): - try: - plist = FoundationPlist.readPlist(installinfo) - except FoundationPlist.NSPropertyListSerializationException: - pass - return plist - - -def munkiUpdatesContainAppleItems(): - """Return True if there are any Apple items in the list of updates""" - installinfo = getInstallInfo() - # check managed_installs - for item in installinfo.get('managed_installs', []): - if item.get('apple_item'): - return True - # check removals - for item in installinfo.get('removals', []): - if item.get('apple_item'): - return True - return False - - -def thereAreUpdatesToBeForcedSoon(hours=72): - '''Return True if any updates need to be installed within the next - X hours, false otherwise''' - installinfo = getInstallInfo().get('managed_installs', []) - installinfo.extend(getAppleUpdates().get('AppleUpdates', [])) - - if installinfo: - now_xhours = NSDate.dateWithTimeIntervalSinceNow_(hours * 3600) - for item in installinfo: - force_install_after_date = item.get('force_install_after_date') - if force_install_after_date: - try: - force_install_after_date = discardTimeZoneFromDate( - force_install_after_date) - if now_xhours >= force_install_after_date: - return True - except BadDateError: - # some issue with the stored date - pass - return False - - -def earliestForceInstallDate(installinfo=None): - """Check installable packages for force_install_after_dates - Returns None or earliest force_install_after_date converted to local time - """ - earliest_date = None - - if not installinfo: - installinfo = getInstallInfo().get('managed_installs', []) - installinfo.extend(getAppleUpdates().get('AppleUpdates', [])) - - for install in installinfo: - this_force_install_date = install.get('force_install_after_date') - - if this_force_install_date: - try: - this_force_install_date = discardTimeZoneFromDate( - this_force_install_date) - if not earliest_date or this_force_install_date < earliest_date: - earliest_date = this_force_install_date - except BadDateError: - # some issue with the stored date - pass - return earliest_date - - -class BadDateError(Exception): - '''Exception when transforming dates''' - pass - -def discardTimeZoneFromDate(the_date): - """Input: NSDate object - Output: NSDate object with same date and time as the UTC. - In Los Angeles (PDT), '2011-06-20T12:00:00Z' becomes - '2011-06-20 12:00:00 -0700'. - In New York (EDT), it becomes '2011-06-20 12:00:00 -0400'. - """ - # get local offset - offset = NSTimeZone.localTimeZone().secondsFromGMT() - try: - # return new NSDate minus local_offset - return the_date.dateByAddingTimeInterval_(-offset) - except: - raise BadDateError() - - -def stringFromDate(nsdate): - """Input: NSDate object - Output: unicode object, date and time formatted per system locale. - """ - df = NSDateFormatter.alloc().init() - df.setFormatterBehavior_(NSDateFormatterBehavior10_4) - df.setDateStyle_(kCFDateFormatterLongStyle) - df.setTimeStyle_(kCFDateFormatterShortStyle) - return unicode(df.stringForObjectValue_(nsdate)) - - -def shortRelativeStringFromDate(nsdate): - """Input: NSDate object - Output: unicode object, date and time formatted per system locale. - """ - df = NSDateFormatter.alloc().init() - df.setDateStyle_(kCFDateFormatterShortStyle) - df.setTimeStyle_(kCFDateFormatterShortStyle) - df.setDoesRelativeDateFormatting_(True) - return unicode(df.stringFromDate_(nsdate)) - - -def getAppleUpdates(): - '''Returns any available Apple updates''' - managedinstallbase = pref('ManagedInstallDir') - plist = {} - appleUpdatesFile = os.path.join(managedinstallbase, 'AppleUpdates.plist') - if (os.path.exists(appleUpdatesFile) and - (pref('InstallAppleSoftwareUpdates') or - pref('AppleSoftwareUpdatesOnly'))): - try: - plist = FoundationPlist.readPlist(appleUpdatesFile) - except FoundationPlist.NSPropertyListSerializationException: - pass - return plist - - -def humanReadable(kbytes): - """Returns sizes in human-readable units.""" - units = [(" KB", 2**10), (" MB", 2**20), (" GB", 2**30), (" TB", 2**40)] - for suffix, limit in units: - if kbytes > limit: - continue - else: - return str(round(kbytes/float(limit/2**10), 1)) + suffix - - -def trimVersionString(version_string): - """Trims all lone trailing zeros in the version string after major/minor. - - Examples: - 10.0.0.0 -> 10.0 - 10.0.0.1 -> 10.0.0.1 - 10.0.0-abc1 -> 10.0.0-abc1 - 10.0.0-abc1.0 -> 10.0.0-abc1 - """ - if version_string is None or version_string == '': - return '' - version_parts = version_string.split('.') - # strip off all trailing 0's in the version, while over 2 parts. - while len(version_parts) > 2 and version_parts[-1] == '0': - del version_parts[-1] - return '.'.join(version_parts) - - -def getconsoleuser(): - '''Get current GUI user''' - cfuser = SCDynamicStoreCopyConsoleUser(None, None, None) - return cfuser[0] - - -def currentGUIusers(): - '''Gets a list of GUI users by parsing the output of /usr/bin/who''' - gui_users = [] - proc = subprocess.Popen("/usr/bin/who", shell=False, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, _) = proc.communicate() - lines = str(output).splitlines() - for line in lines: - if "console" in line: - parts = line.split() - gui_users.append(parts[0]) - - # 10.11 sometimes has a phantom '_mbsetupuser' user. Filter it out. - users_to_ignore = ['_mbsetupuser'] - gui_users = [user for user in gui_users if user not in users_to_ignore] - - return gui_users - - -class ProcessStartError(Exception): - '''An exception to raise when we can't start managedsoftwareupdate''' - pass - - -def startUpdateCheck(suppress_apple_update_check=False): - '''Does launchd magic to run managedsoftwareupdate as root.''' - try: - if not os.path.exists(UPDATECHECKLAUNCHFILE): - plist = {} - plist['SuppressAppleUpdateCheck'] = suppress_apple_update_check - try: - FoundationPlist.writePlist(plist, UPDATECHECKLAUNCHFILE) - except FoundationPlist.FoundationPlistException, err: - # problem creating the trigger file - raise ProcessStartError(err) - except (OSError, IOError), err: - raise ProcessStartError(err) - - -def logoutNow(): - '''Uses osascript to run an AppleScript - to tell loginwindow to logout. - Ugly, but it works.''' - - script = """ -ignoring application responses - tell application "loginwindow" - «event aevtrlgo» - end tell -end ignoring -""" - cmd = ['/usr/bin/osascript', '-e', script] - _ = call(cmd) - - -def logoutAndUpdate(): - '''Touch a flag so the process that runs after - logout knows it's OK to install everything''' - try: - if not os.path.exists(INSTALLATLOGOUTFILE): - open(INSTALLATLOGOUTFILE, 'w').close() - logoutNow() - except (OSError, IOError), err: - raise ProcessStartError(err) - - -def clearLaunchTrigger(): - '''Clear the trigger file that fast-launches us at loginwindow. - typically because we have been launched in statusmode at the - loginwindow to perform a logout-install.''' - try: - if os.path.exists(INSTALLATLOGOUTFILE): - os.unlink(INSTALLATLOGOUTFILE) - except (OSError, IOError), err: - raise ProcessStartError(err) - - -def justUpdate(): - '''Trigger managedinstaller via launchd KeepAlive path trigger - We touch a file that launchd is is watching - launchd, in turn, - launches managedsoftwareupdate --installwithnologout as root''' - try: - if not os.path.exists(INSTALLWITHOUTLOGOUTFILE): - open(INSTALLWITHOUTLOGOUTFILE, 'w').close() - except (OSError, IOError), err: - raise ProcessStartError(err) - - -def pythonScriptRunning(scriptname): - """Returns Process ID for a running python script""" - cmd = ['/bin/ps', '-eo', 'pid=,command='] - proc = subprocess.Popen(cmd, shell=False, bufsize=1, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() - mypid = os.getpid() - lines = str(out).splitlines() - for line in lines: - try: - (pid, process) = line.split(None, 1) - except ValueError: - # funky process line, so we'll skip it - pass - else: - args = process.split() - try: - # first look for Python processes - if (args[0].find('MacOS/Python') != -1 or - args[0].find('python') != -1): - # look for first argument being scriptname - if args[1].find(scriptname) != -1: - try: - if int(pid) != int(mypid): - return pid - except ValueError: - # pid must have some funky characters - pass - except IndexError: - pass - # if we get here we didn't find a Python script with scriptname - # (other than ourselves) - return 0 - - -def getRunningProcessesWithUsers(): - """Returns a list of usernames and paths of running processes""" - proc_list = [] - proc = subprocess.Popen(['/bin/ps', '-axo' 'user=,comm='], - shell=False, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() - if proc.returncode == 0: - proc_lines = [item for item in output.splitlines()] - LaunchCFMApp = ('/System/Library/Frameworks/Carbon.framework' - '/Versions/A/Support/LaunchCFMApp') - saw_launch_cfmapp = False - for line in proc_lines: - # split into max two parts on whitespace - parts = line.split(None, 1) - if len(parts) > 1 and parts[1] == LaunchCFMApp: - saw_launch_cfmapp = True - elif len(parts) > 1: - info = {'user': parts[0], - 'pathname': parts[1]} - proc_list.append(info) - if saw_launch_cfmapp: - # look at the process table again with different options - # and get the arguments for LaunchCFMApp instances - proc = subprocess.Popen(['/bin/ps', '-axo' 'user=,command='], - shell=False, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() - if proc.returncode == 0: - proc_lines = [item for item in output.splitlines()] - for line in proc_lines: - # split into max three parts on whitespace - parts = line.split(None, 2) - if len(parts) > 2 and parts[1] == LaunchCFMApp: - info = {'user': parts[0], - 'pathname': parts[2]} - proc_list.append(info) - return proc_list - else: - return [] - - -def getRunningBlockingApps(appnames): - """Given a list of app names, return a list of dicts for apps in the list - that are running. Each dict contains username, pathname, display_name""" - proc_list = getRunningProcessesWithUsers() - running_apps = [] - filemanager = NSFileManager.alloc().init() - for appname in appnames: - matching_items = [] - if appname.startswith('/'): - # search by exact path - matching_items = [item for item in proc_list - if item['pathname'] == appname] - elif appname.endswith('.app'): - # search by filename - matching_items = [ - item for item in proc_list - if '/'+ appname + '/Contents/MacOS/' in item['pathname']] - else: - # check executable name - matching_items = [item for item in proc_list - if item['pathname'].endswith('/' + appname)] - - if not matching_items: - # try adding '.app' to the name and check again - matching_items = [ - item for item in proc_list - if '/' + appname + '.app/Contents/MacOS/' in item['pathname']] - - #matching_items = set(matching_items) - for item in matching_items: - path = item['pathname'] - while '/Contents/' in path or path.endswith('/Contents'): - path = os.path.dirname(path) - # ask NSFileManager for localized name since end-users - # will see this name - item['display_name'] = filemanager.displayNameAtPath_(path) - running_apps.append(item) - - return running_apps - - -def onACPower(): - """Returns a boolean to indicate if the machine is on AC power""" - # pylint: disable=undefined-variable - power_source = IOPSGetProvidingPowerSourceType(IOPSCopyPowerSourcesInfo()) - # pylint: enable=undefined-variable - return power_source == 'AC Power' - - -def onBatteryPower(): - """Returns a boolean to indicate if the machine is on battery power""" - # pylint: disable=undefined-variable - power_source = IOPSGetProvidingPowerSourceType(IOPSCopyPowerSourcesInfo()) - # pylint: enable=undefined-variable - return power_source == 'Battery Power' - - -def getBatteryPercentage(): - """Returns battery charge percentage""" - # pylint: disable=undefined-variable - ps_blob = IOPSCopyPowerSourcesInfo() - power_sources = IOPSCopyPowerSourcesList(ps_blob) - for source in power_sources: - description = IOPSGetPowerSourceDescription(ps_blob, source) - if description.get('Type') == 'InternalBattery': - return description.get('Current Capacity', 0) - return 0 diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/Localizable.strings deleted file mode 100644 index 80fe9965..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s ventende oppdateringer"; - -/* One Update message */ -"1 pending update" = "1 ventende oppdatering"; - -/* Logout Required detail */ -"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?" = "Du må logge ut før opdatering. Vent et øyeblikk da det kan være en kort forsinkelse ved påloggingsvinduet. Logg ut og oppdater nå?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "En tvungen utlogging vil skje ca. %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "En tvungen utlogging vil skje om mindre enn %s minutter."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "En tvungen utlogging vil skje om mindre enn et minutt.\nAlle ventende oppdateringer vil da bli installert. Alt arbeid som ikke er lagret vil gå tapt."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Et forsøk på å fjerne feilet. Fjerning vil bli forsøkt igjen.\nHvis problemet fortsetter, kontakt din systemadministrator."; - -/* Restart Required detail */ -"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?" = "En omstart er påkrevd etter oppdatering. Vent et øyeblikk da det kan oppstå en kort forsinkelse ved påloggingsvinduet. Logg ut og oppdater nå?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Et systemkonfigurasjonsproblem hindrer Managed Software Center fra å virker som det skal. Det rapporterte problemet er: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Flere ventende oppdateringer"; - -/* AllCategoriesLabel */ -"All Categories" = "Alle kategorier"; - -/* AllItemsHeaderText */ -"All items" = "Alle objekter"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Alle ventende oppdateringer vil bli installeret. Arbeid som ikke er lagret vil gå tapt.\nDu kan unngå den tvungne utloggingen, hvis du selv velger å logge ut nå."; - -/* Allow button text */ -"Allow" = "Tillat"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Forsøket på installering feilet. installeringen vil bli forsøkt igjen.\nHvis problemet fortsetter, kontakt din systemadministrator."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "En eldre versjon er installert, og det er ikke tilstrekkelig diskplass tilgjengelig for å laste ned og installere denne oppdateringen."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "En eldre versjon er installert. Du må oppgradere macOS til versjon %s eller nyere for å kunne installere denne oppdateringen."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Programmer som er i bruk av andre"; - -/* Cancel button title/short action text */ -"Cancel" = "Avbryt"; - -/* Cancel Install long action text */ -"Cancel install" = "Avbryt installering"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Avbryt fjerning"; - -/* Cancel Update long action text */ -"Cancel update" = "Avbryt oppdatering"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Kan ikke vise ønsket objekt."; - -/* Categories label */ -"Categories" = "Kategorier"; - -/* Sidebar Category label */ -"Category:" = "Kategori:"; - -/* Check Again button title */ -"Check Again" = "Kontroller igjen"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerer katalogen til Apple-programvareoppdatering..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Søker etter flere endringer..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Søker etter tilgjengelige Apple-programvareoppdateringer..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Søker etter tilgjengelige oppdateringer..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Søker etter oppdateringer..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Du har programmer åpne som hindrer installasjonen i å fullføre"; - -/* Continue button text */ -"Continue" = "Fortsett"; - -/* Critical Update type */ -"Critical Update" = "Kritisk oppdatering"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Ikke tilgjengelig"; - -/* Deny button text */ -"Deny" = "Avslå"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Undersøker hvilke elementer som kan fjernes"; - -/* Sidebar Developer label */ -"Developer:" = "Utvikler:"; - -/* managedsoftwareupdate message */ -"Done." = "Ferdig."; - -/* Downloading status text */ -"Downloading" = "Henter"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Henter tilgjengelige Apple-programvareoppdateringer..."; - -/* Sidebar Due label */ -"Due:" = "Forfall:"; - -/* FeaturedLabel */ -"Featured" = "Valgt"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Valgte elementer"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Avslutter..."; - -/* Firmware Alert Default detail */ -"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." = "Firmwaren på din datamaskin vil bli oppdatert. Påse at din datamaskin tilkoblet strømforsyningen og at denne er satt i stikkontakten. Oppdateringen kan ta flere minutter. Ikke avbryt oppdateringen eller koble datamaskinen fra strømnettet."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Du bør koble datamaskinen til en strømforsyning før du oppdaterer. Er du sikker på at du vil forsette med oppdateringen?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Tvungen utlogging for obligatorisk installasjon"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samler informasjon om installert programvare"; - -/* No help alert title */ -"Help" = "Hjælp"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Hjelp er ikke tilgjengelig for Managed Software Center."; - -/* Sidebar Information label */ -"Information" = "Informasjon"; - -/* Install action text */ -"Install" = "Installer"; - -/* Install Required action text */ -"Install Required" = "Installasjon påkrevd"; - -/* Install Requested status text */ -"Install requested" = "Installasjon forespurt"; - -/* Install Session Failed title */ -"Install session failed" = "Installasjonen feilet"; - -/* Install Error status text */ -"Installation Error" = "Feil oppsto under installering"; - -/* Installed status text */ -"Installed" = "Installert"; - -/* Installing status text */ -"Installing" = "Installerer"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerer tilgjengelige Apple-programvareopdateringer..."; - -/* Log out and Update button text */ -"Log out and update" = "Logg ut og oppdater"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Logg ut og oppdater nå"; - -/* Logout Required title */ -"Logout Required" = "Utlogging påkrevd"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke søke etter oppdateringer.\nPrøv igjen senere. Hvis problemet fortsetter, kontakt din systemadministrator."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan ikke kontakte oppdateringsserveren.\nPrøv igjen senere. Hvis problemet fortsetter, kontakt din systemadministrator."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Center vil låse startdisken opp etter omstart får å ferdigjøre alle ventende oppdateringer."; - -/* Managed Update type */ -"Managed Update" = "Administrert oppdatering"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Påkrevde oppdateringer venter"; - -/* More link text */ -"More" = "Mer"; - -/* Sidebar More By Developer label */ -"More by %s" = "Mer av %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Mere i %s"; - -/* My Items label */ -"My Items" = "Mine objekter"; - -/* No Licenses Available display text */ -"No licenses available" = "Ingen tilgjengelige lisenser"; - -/* No Updates message */ -"No pending updates" = "Ingen ventende oppdateringer"; - -/* Item Not Found title */ -"Not Found" = "Ikke funnet"; - -/* Not Currently Available display text */ -"Not currently available" = "Ikke tilgjengelig for øyeblikket"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Ikke nok plass på disken"; - -/* Not Installed status text */ -"Not installed" = "Ikke installert"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Ingenting å fjerne."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Et eller flere objekter må installeres senest %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "En eller flere påkrevde oppdateringer burde vært installert. En tvungen utlogging vil skje snart."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "En eller flere oppdateringer må installeres innen %s. Hvis du venter forlenge med å oppdatere, vil en tvungen utlogging skje."; - -/* Other Available Updates label */ -"Other available updates" = "Andre tilgjengelige oppdateringer"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Andre påloggede brukere anvender følgende programmer. Prøv å oppdatere senere når de ikke lengere er i bruk:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Andre brukere er pålogget"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Avinstallasjon av programvare er gjennomført."; - -/* Password label */ -"Password:" = "Passord:"; - -/* Pending Updates alert title */ -"Pending updates" = "Ventende oppdateringer"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Utfører postflight-oppgaver..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Utfører preflight-oppgaver..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Forbereder avinstallasjon"; - -/* Problem Updates label */ -"Problem updates" = "Problematiske oppdateringer"; - -/* Quit button title */ -"Quit" = "Avslutt"; - -/* Removal Error status text */ -"Removal Error" = "Feil ved avinstallasjon"; - -/* Removal Requested status text */ -"Removal requested" = "Avinstallasjon forespurt"; - -/* Remove action text */ -"Remove" = "Avinstaller"; - -/* Removing status text */ -"Removing" = "Avinstallerer"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Fjerner opplysninger om kvittering"; - -/* Install Required action text */ -"Required" = "Påkrevd"; - -/* Restart button title */ -"Restart" = "Omstart"; - -/* Restart Required title */ -"Restart Required" = "Omstart påkrevd"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Henter en liste over tilgjenglig programvare for denne datamaskinen..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kjører Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kjører Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kjører Adobe Uninstall"; - -/* No Installed Software secondary text */ -"Select software to install." = "Velg programvare som skal installeres."; - -/* Sidebar Size label */ -"Size:" = "Størrelse:"; - -/* Software label */ -"Software" = "Programvare"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Programvaren som er installert eller fjernet krever en omstart."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Programvaren som er blitt installert eller fjernet krever en omstart. Du får muligheten til å lagre åpne dokumenter."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starter Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starter..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problem med systemkonfigurasjon"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Installasjon av programvaren er fullført."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Det er flere ventende oppdateringer som skal installeres eller fjernes."; - -/* No Items primary text */ -"There are no available software items." = "Det er ingen programvareobjekter."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Det er ingen objekter fra utvikleren."; - -/* No Category Results primary text */ -"There are no items in this category." = "Det finnes ingen objekter i kategorien."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Det er andre påloggede brukere på datamaskinen.\nHvis du oppdaterer nå, risikerer du at de mister deres ulagrede arbeid.\n\nPrøv igjen senere når de andre brukerne har logget ut."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Det er ventende oppdateringer for denne datamaskinen."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Det er et konfigurasjonsproblem med programvarerammeverket. Kan ikke starte prosessen. Kontakt din systemadministrator."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Det er et konfigurasjonsproblem med programvarerammeverket. Prosessen avsluttet uventet. Kontakt din systemadministrator."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Det er ingen nye oppdateringer for din datamaskin på det nåværende tidspunkt."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Det er ikke tilstrekkelig diskplass tilgjengelig til laste ned og installere denne pakken."; - -/* Dependency List prologue text */ -"This item is required by:" = "Dette objektet kreves av:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Dette objektet må installeres senest %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "For å tillate dette, må du skrive inn ditt påloggingspassord."; - -/* No Items secondary text */ -"Try again later." = "Prøv igjen senere."; - -/* No Search Results secondary text */ -"Try searching again." = "Prøv å søke igjen."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Prøv å velge en annen kategori."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Prøv å velge en annen utvikler."; - -/* Sidebar Type label */ -"Type:" = "Type:"; - -/* Unavailable status text */ -"Unavailable" = "Ikke tilgjengelig"; - -/* No Category name */ -"Uncategorized" = "Ikke kategorisert"; - -/* Update button title/action text */ -"Update" = "Oppdater"; - -/* Update All button title */ -"Update All" = "Oppdater alle"; - -/* Update Required long action text */ -"Update Required" = "Oppdatering påkrevd"; - -/* No comment provided by engineer. */ -"Update available" = "Kan oppdateres"; - -/* Update Check Failed title */ -"Update check failed" = "Søk etter oppdateringer mislyktes"; - -/* Update In Progress primary text */ -"Update in progress." = "Oppdatering i gang."; - -/* Update Now button title */ -"Update now" = "Oppdater nå"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Oppdatering vil bli installert"; - -/* Updates label */ -"Updates" = "Oppdateringer"; - -/* Updating message */ -"Updating..." = "Oppdaterer..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Verifiserer integriteten til programvarepakken..."; - -/* Sidebar Version label */ -"Version" = "Versjon"; - -/* Sidebar Version label */ -"Version:" = "Versjon:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Venter på nettverk..."; - -/* Will Be Installed status text */ -"Will be installed" = "Vil bli installert"; - -/* Will Be Removed status text */ -"Will be removed" = "Vil bli fjernet"; - -/* No Installed Software primary text */ -"You have no selected software." = "Du har ikke valgt noe program."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Du må avslutte følgende programmer før du forsetter med installasjon eller fjerning:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Du må oppgradere macOS til versjon %s for å kunne installere denne pakken."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Datamaskinen er ikke tilkoblet en strømforsyning."; - -/* No Search Results primary text */ -"Your search had no results." = "Ditt søk ga ingen resultater."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Din programvare er oppdatert."; - -/* macOS update required text */ -"macOS update required" = "macOS-oppdatering nødvendig"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/MainMenu.strings deleted file mode 100644 index 3e34f7de..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nb.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjelp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjelp"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Hjelp til Managed Software Center"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Skjul Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Avslutt Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Skjul andre"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Vis alle"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Vindu"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Velg alt"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klipp ut"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Søk"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mine objekter"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Slett"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Lim inn"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Angre"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Gjenta"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimer"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Skalere"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Vindu"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Naviger"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Naviger"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Om Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Kategorier"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Last side på nytt"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Oppdateringer"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Oppdateringer"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigasjon"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Kategorier"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Kategorier"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mine objekter"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mine okjekter"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Programvare"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Lukk"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Tilbake"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Søk"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Start full skjerm"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Programvare"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Programvare"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Oppdateringer"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Vis logg"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Fram"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/Localizable.strings deleted file mode 100644 index b95a1926..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s wachtende updates"; - -/* One Update message */ -"1 pending update" = "1 wachtende update"; - -/* Logout Required detail */ -"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 moet uitloggen voordat het bijwerken kan beginnen. Nadat het login window verschijnt kan het een ogenblik duren voordat bijwerken begint. Wilt U nu uitloggen en bijwerken?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "U wordt automatisch uitgelogd om ongeveer %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "U wordt automatisch uitgelogd binnen %s minuten."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "U wordt binnen een minuut automatisch uitgelogd.\nAlle wachtende updates zullen worden geïnstalleerd. Niet-opgeslagen werk zal verloren gaan."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Een verwijderingspoging is niet geslaagd. Verwijderen wordt opnieuw geprobeerd.\nAls deze situatie niet verandert, neem contact op met uw systeembeheerder."; - -/* Restart Required detail */ -"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?" = "Het is nodig te herstarten na het bijwerken. Nadat het login window verschijnt kan het een ogenblik duren voordat bijwerken begint. Wilt U nu uitloggen en bijwerken?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Een probleem met de systeemconfiguratie verhindert het correct functioneren van Managed Software Center. Het probleem is: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Additionele Wachtende Updates"; - -/* AllCategoriesLabel */ -"All Categories" = "Alle Categorieën"; - -/* AllItemsHeaderText */ -"All items" = "Alle items"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Alle wachtende updates zullen worden geïnstalleerd. Niet-opgeslagen werk zal verloren gaan.\nU kunt dit voorkomen door nu zelf uit te loggen."; - -/* Allow button text */ -"Allow" = "Sta toe"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Een installatiepoging is niet geslaagd. Installatie wordt opnieuw geprobeerd.\nAls deze situatie niet verandert, neem contact op met uw systeembeheerder"; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "Een oudere versie is op dit moment geïnstalleerd. Er is onvoldoende schijfruimte om deze update te downloaden en installeren."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "Een oudere versie is op dit moment geïnstalleerd. Upgrade eerst naar macOS versie %s of hoger zodat de update geïnstalleerd kan worden."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Applicaties in gebruik door anderen"; - -/* Cancel button title/short action text */ -"Cancel" = "Annuleren"; - -/* Cancel Install long action text */ -"Cancel install" = "Annuleer installatie"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Annuleer verwijdering"; - -/* Cancel Update long action text */ -"Cancel update" = "Annuleer update"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Kan het gevraagde item niet tonen."; - -/* Categories label */ -"Categories" = "Categorieën"; - -/* Sidebar Category label */ -"Category:" = "Categorie:"; - -/* Check Again button title */ -"Check Again" = "Check Opnieuw"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Apple Software-update catalogus checken..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Additionele wijzigingen checken..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Naar beschikbare Apple Software-updates zoeken..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Naar beschikbare updates zoeken..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Updates opvragen..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Conflicterende applicaties actief"; - -/* Continue button text */ -"Continue" = "Ga door"; - -/* Critical Update type */ -"Critical Update" = "Kritieke Update"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Op dit moment niet beschikbaar"; - -/* Deny button text */ -"Deny" = "Nee"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Bepalen welke bestandssysteem onderdelen te verwijderen"; - -/* Sidebar Developer label */ -"Developer:" = "Ontwikkelaar:"; - -/* managedsoftwareupdate message */ -"Done." = "Klaar."; - -/* Downloading status text */ -"Downloading" = "Aan het downloaden"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Beschikbare Apple Software-updates downloaden..."; - -/* Sidebar Due label */ -"Due:" = "Vervaldatum:"; - -/* FeaturedLabel */ -"Featured" = "Uitgelicht"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Uitgelicht"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Afronden..."; - -/* Firmware Alert Default detail */ -"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." = "Firmware zal worden bijgewerkt computer. Uw computer moet aangesloten zijn op het lichtnet. Het kan enkele minuten duren voordat deze update voltooid is. Onderbreek deze update niet en zorg dat de stroom aangesloten blijft."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Het is aan te raden Uw computer aan te sluiten op de netvoeding. Weet U zeker dat U wilt doorgaan met bijwerken?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Gedwongen uitloggen voor verplichte installatie"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Informatie over geïnstalleerde pakketten verzamelen"; - -/* No help alert title */ -"Help" = "Help"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Help is niet beschikbaar voor Managed Software Center."; - -/* Sidebar Information label */ -"Information" = "Informatie"; - -/* Install action text */ -"Install" = "Installeer"; - -/* Install Required action text */ -"Install Required" = "Installatie Benodigd"; - -/* Install Requested status text */ -"Install requested" = "Installatie Verzocht"; - -/* Install Session Failed title */ -"Install session failed" = "Installatie-sessie niet succesvol"; - -/* Install Error status text */ -"Installation Error" = "Installatie Fout"; - -/* Installed status text */ -"Installed" = "Geïnstalleerd"; - -/* Installing status text */ -"Installing" = "Aan het installeren"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Beschikbare Apple Software-updates installeren..."; - -/* Log out and Update button text */ -"Log out and update" = "Uitloggen en bijwerken"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Nu uitloggen en bijwerken"; - -/* Logout Required title */ -"Logout Required" = "Uitloggen Benodigd"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan geen updates checken.\nProbeer later nog eens. Als deze situatie niet verandert, neem contact op met uw systeembeheerder."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan de update server op dit moment niet bereiken.\nProbeer later nog eens. Als deze situatie niet verandert, neem contact op met uw systeembeheerder."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Center wil de opstartschijf ontgrendelen na herstart zodat alle nog wachtende updates uitgevoerd kunnen worden."; - -/* Managed Update type */ -"Managed Update" = "Beheerde Update"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Verplichte Updates Wachtend"; - -/* More link text */ -"More" = "Meer"; - -/* Sidebar More By Developer label */ -"More by %s" = "Meer van %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Meer in %s"; - -/* My Items label */ -"My Items" = "Mijn Items"; - -/* No Licenses Available display text */ -"No licenses available" = "Geen licenties beschikbaar"; - -/* No Updates message */ -"No pending updates" = "Geen updates"; - -/* Item Not Found title */ -"Not Found" = "Niet Gevonden"; - -/* Not Currently Available display text */ -"Not currently available" = "Op dit moment niet beschikbaar"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Onvoldoende ruimte op de harde schijf"; - -/* Not Installed status text */ -"Not installed" = "Niet geïnstalleerd"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Niets te verwijderen."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Eén of meer items moeten voor %s worden geïnstalleerd"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Er zijn één of meerdere achterstallige updates. Verplicht uitloggen zal spoedig nodig zijn."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Één of meerdere updates zullen worden geïnstalleerd op of voor %s. Geforceerd uitloggen kan mogelijk zijn als U te lang wacht met bijwerken."; - -/* Other Available Updates label */ -"Other available updates" = "Andere beschikbare updates"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Andere ingelogde gebruikers hebben de volgende applicaties actief. Probeer later nog eens te updaten als ze niet langer in gebruik zijn:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Andere gebruikers zijn nog ingelogd"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Verwijderen van pakket voltooid."; - -/* Password label */ -"Password:" = "Wachtwoord:"; - -/* Pending Updates alert title */ -"Pending updates" = "Wachtende updates"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Postflight taken afhandelen..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Preflight taken afhandelen..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Verwijderen voorbereiden"; - -/* Problem Updates label */ -"Problem updates" = "Problematische updates"; - -/* Quit button title */ -"Quit" = "Afsluiten"; - -/* Removal Error status text */ -"Removal Error" = "Verwijderfout"; - -/* Removal Requested status text */ -"Removal requested" = "Verwijdering verzocht"; - -/* Remove action text */ -"Remove" = "Verwijder"; - -/* Removing status text */ -"Removing" = "Verwijderen"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Receipt informatie verwijderen"; - -/* Install Required action text */ -"Required" = "Benodigd"; - -/* Restart button title */ -"Restart" = "Herstarten"; - -/* Restart Required title */ -"Restart Required" = "Herstart Benodigd"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Software lijst voor deze machine ophalen..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobe Patch Installer uitvoeren"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobe Setup uitvoeren"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobe Uninstall uitvoeren"; - -/* No Installed Software secondary text */ -"Select software to install." = "Selecteer software om te installeren."; - -/* Sidebar Size label */ -"Size:" = "Grootte:"; - -/* Software label */ -"Software" = "Software"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Geïnstalleerde of verwijderde software maakt een herstart nodig."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Geïnstalleerde of verwijderde software maakt een herstart nodig. U krijgt de kans om geopende documenten op te slaan."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Adobe installer opstarten..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Beginnen..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Probleem met de systeemconfiguratie"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "De software is succesvol geïnstalleerd."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Er zijn additionele wachtende updates om te installeren of verwijderen."; - -/* No Items primary text */ -"There are no available software items." = "Geen software items beschikbaar."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Er zijn geen items van deze ontwikkelaar."; - -/* No Category Results primary text */ -"There are no items in this category." = "Er zijn geen items in deze categorie."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Er zijn andere gebruikers ingelogd op deze computer.\nAls U nu bijwerkt kunnen de andere gebruikers hun werk verliezen.\n\nProbeer opnieuw nadat de andere gebruikers zijn uitgelogd."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Er zijn updates voor deze computer."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Er is een probleem met de configuratie van de beheerde software installatie. Het proces kon niet gestart worden. Neem contact op met uw systeembeheerder."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Er is een probleem met de configuratie van de beheerde software installatie. De bewerking werd onverwacht beëindigd. Neem contact op met uw systeembeheerder."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Er is geen nieuwe software beschikbaar voor uw computer."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Er is onvoldoende schijfruimte om dit onderdeel te downloaden en installeren."; - -/* Dependency List prologue text */ -"This item is required by:" = "Dit item is benodigd voor:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Dit item moet geïnstalleerd zijn voor %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Om dit toe te staan, vul je loginwachtwoord in."; - -/* No Items secondary text */ -"Try again later." = "Probeer later nog eens."; - -/* No Search Results secondary text */ -"Try searching again." = "Probeer nog eens te zoeken."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Selecteer een andere categorie."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Selecteer een andere ontwikkelaar."; - -/* Sidebar Type label */ -"Type:" = "Type:"; - -/* Unavailable status text */ -"Unavailable" = "Niet beschikbaar"; - -/* No Category name */ -"Uncategorized" = "Niet gecategoriseerd"; - -/* Update button title/action text */ -"Update" = "Update"; - -/* Update All button title */ -"Update All" = "Update Alles"; - -/* Update Required long action text */ -"Update Required" = "Update benodigd"; - -/* No comment provided by engineer. */ -"Update available" = "Update beschikbaar"; - -/* Update Check Failed title */ -"Update check failed" = "Update check mislukt"; - -/* Update In Progress primary text */ -"Update in progress." = "Update in behandeling."; - -/* Update Now button title */ -"Update now" = "Update nu"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Update wordt geïnstalleerd"; - -/* Updates label */ -"Updates" = "Updates"; - -/* Updating message */ -"Updating..." = "Aan het updaten..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Integriteit van het pakket controleren..."; - -/* Sidebar Version label */ -"Version" = "Versie"; - -/* Sidebar Version label */ -"Version:" = "Versie:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Op netwerk wachten..."; - -/* Will Be Installed status text */ -"Will be installed" = "Zal geïnstalleerd worden"; - -/* Will Be Removed status text */ -"Will be removed" = "Zal verwijderd worden"; - -/* No Installed Software primary text */ -"You have no selected software." = "U heeft geen software geselecteerd."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "De volgende applicaties dienen afgesloten te worden voordat de installatie of verwijdering plaats kan vinden:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Upgrade eerst naar macOS versie %s of hoger zodat dit onderdeel geïnstalleerd kan worden."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Uw computer is niet aangesloten op het lichtnet."; - -/* No Search Results primary text */ -"Your search had no results." = "Uw zoekopdracht geeft geen resultaat."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Uw software is up to date."; - -/* macOS update required text */ -"macOS update required" = "macOS update vereist"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/MainMenu.strings deleted file mode 100644 index 9c4fdf45..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/nl.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Center Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Voorzieningen"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Voorzieningen"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Verberg Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Stop Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Verberg Andere"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Toon Alles"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Venster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopieer"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Selecteer alles"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Knip"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Zoek"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mijn Items"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Verwijder"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Plak"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Bewerken"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Herstel"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Opnieuw"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Bewerken"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimaliseren"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Vergroot/verklein"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Venster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Navigeer"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Navigeer"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Over Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Categorieën"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Laad pagina opnieuw"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Updates"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Updates"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigation"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Categorieën"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Categorieën"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mijn Items"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mijn Items"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Software"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Sluit"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Vorige"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Zoek"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Schakel schermvullende weergave in"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Software"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Updates"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Toon logbestand"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Volgende"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/passwdutil.py b/code/apps/pyobjc/Managed Software Center/Managed Software Center/passwdutil.py deleted file mode 100644 index 86d75a53..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/passwdutil.py +++ /dev/null @@ -1,57 +0,0 @@ -# -*- coding: utf-8 -*- -# -# passwdutil.py -# Managed Software Center -# -# Created by Greg Neagle on 4/18/17. -# Copyright (c) 2018-2019 The Munki Project. All rights reserved. -# -'''Code to interact with the OpenDirectory framework''' - - -import OpenDirectory as OD -from Foundation import NSLog - -def findODuserRecords(username, nodename='/Search'): - '''Uses OpenDirectory methods to find user records for username''' - mySession = OD.ODSession.defaultSession() - if not mySession: - NSLog('DS session error: no default session') - return None - searchNode, err = OD.ODNode.nodeWithSession_name_error_( - mySession, nodename, None) - if not searchNode: - NSLog('DS node error: %s' % err) - return None - myQuery, err = OD.ODQuery.queryWithNode_forRecordTypes_attribute_matchType_queryValues_returnAttributes_maximumResults_error_( - searchNode, - OD.kODRecordTypeUsers, - OD.kODAttributeTypeRecordName, - OD.kODMatchEqualTo, - username, - OD.kODAttributeTypeAllAttributes, - 0, - None) - results, err = myQuery.resultsAllowingPartial_error_(False, None) - if not results: - return None - return results - - -def findODuserRecord(username, nodename='/Search'): - '''Returns first record found for username.''' - records = findODuserRecords(username, nodename) - if records: - return records[0] - else: - return None - - -def verifyPassword(username, password): - '''Uses OpenDirectory methods to verify password for username''' - result = False - userRecord = findODuserRecord(username) - if userRecord: - result, unused_err = userRecord.verifyPassword_error_( - password, None) - return result \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings deleted file mode 100644 index 9fa0a5c0..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Центр Управления ПО"; -"CFBundleDisplayName" = "Центр Управления ПО"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/Localizable.strings deleted file mode 100644 index 6d72b7ae..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s отложенных обновлений"; - -/* One Update message */ -"1 pending update" = "1 отложенное обновление"; - -/* Logout Required detail */ -"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?" = "Перед установкой обновлений необходимо выйти из системы. После выхода из системы дождитесь появления окна входа. Выйти из системы и обновить?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Работа будет принудительно завершена в %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Работа будет принудительно завершена менее чем через %s минут."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "Работа будет принудительно завершена менее чем через минуту.\nБудут установлены все отложенные обновления. Все несохраненные данные будут потеряны."; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Попытка удаления не удалась. Попытка будет предпринята снова.\nЕсли эта ситуация продолжится, обратитесь к системному администратору."; - -/* Restart Required detail */ -"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?" = "После установки обновлений необходимо перезагрузить компьютер. После перезагрузки дождитесь появления окна входа. Выйти из системы и обновить?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Проблема с конфигурацией системы не позволяет правильному функционированию Центра Управления ПО. Сообщаемая проблема: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Дополнительные отложенные обновления"; - -/* AllCategoriesLabel */ -"All Categories" = "Все категории"; - -/* AllItemsHeaderText */ -"All items" = "Все элементы"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Будут установлены все отложенные обновления. Все несохраненные данные будут потеряны.\nВы можете избежать принудительного завершения работы, выполнив выход из системы."; - -/* Allow button text */ -"Allow" = "Разрешить"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Попытка установки не удалась. Попытка будет предпринята снова.\nЕсли эта ситуация продолжится, обратитесь к системному администратору."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "В настоящее время установлена старая версия. Недостаточно места на диске для загрузки и установки этого обновления."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "В настоящее время установлена старая версия. Для установки этого обновления необходимо перейти на версию macOS %s или выше."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Приложения, которые используются другими"; - -/* Cancel button title/short action text */ -"Cancel" = "Отменить"; - -/* Cancel Install long action text */ -"Cancel install" = "Отменить установку"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Отменить удаление"; - -/* Cancel Update long action text */ -"Cancel update" = "Отменить обновление"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Невозможно отобразить запрашиваемый элемент."; - -/* Categories label */ -"Categories" = "Категории"; - -/* Sidebar Category label */ -"Category:" = "Категория:"; - -/* Check Again button title */ -"Check Again" = "Проверить снова"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Проверка каталога Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Проверка дополнительных изменений..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Проверка доступных Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Проверка доступных обновлений..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Проверка обновлений..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Конфликт запущенных приложений"; - -/* Continue button text */ -"Continue" = "Продолжить"; - -/* Critical Update type */ -"Critical Update" = "Критическое Обновление"; - -/* Unavailable long action text */ -"Currently Unavailable" = "В настоящее время Недоступно"; - -/* Deny button text */ -"Deny" = "Не разрешать"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Определение элементов файловой системы для удаления"; - -/* Sidebar Developer label */ -"Developer:" = "Разработчик:"; - -/* managedsoftwareupdate message */ -"Done." = "Готово."; - -/* Downloading status text */ -"Downloading" = "Загрузка"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Загрузка доступных Обновлений ПО Apple..."; - -/* Sidebar Due label */ -"Due:" = "До:"; - -/* FeaturedLabel */ -"Featured" = "Подборка"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "Подборка элементов"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Завершение..."; - -/* Firmware Alert Default detail */ -"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." = "Прошивка будет обновлена на Вашем компьютере. Шнур питания компьютера должен быть подключен и подключен к исправному источнику питания. Обновление может занять несколько минут. Не мешайте или не отключайте питание на вашем компьютере во время этого обновления."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Для продолжения установки рекомендуется подключить компьютер к постоянному источнику питания. Вы действительно хотите продолжить установку обновлений?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Принудительное завершение работы для установки Обязательного Обновления"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Сбор информации об установленных пакетах"; - -/* No help alert title */ -"Help" = "Помощь"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Помощь недоступна для Центра Управления ПО."; - -/* Sidebar Information label */ -"Information" = "Информация"; - -/* Install action text */ -"Install" = "Установить"; - -/* Install Required action text */ -"Install Required" = "Установить Обязательные"; - -/* Install Requested status text */ -"Install requested" = "Установить Требуемые"; - -/* Install Session Failed title */ -"Install session failed" = "Сбой процесса установки"; - -/* Install Error status text */ -"Installation Error" = "Ошибка установки"; - -/* Installed status text */ -"Installed" = "Установлено"; - -/* Installing status text */ -"Installing" = "Установка"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Установка доступных Обновлений ПО Apple..."; - -/* Log out and Update button text */ -"Log out and update" = "Выйти из системы и обновить"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Выйти из системы и обновить сейчас"; - -/* Logout Required title */ -"Logout Required" = "Необходимо выйти из системы"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Центр Управления ПО не может проверить наличие обновлений в данный момент.\nПопробуйте еще раз позже. Если эта ситуация продолжится, обратитесь к системному администратору."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Центр Управления ПО не может связаться с сервером обновлений в данный момент.\nПопробуйте еще раз позже. Если эта ситуация продолжится, обратитесь к системному администратору."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Центр Управления ПО хочет разблокировать загрузочный диск после перезапуска, чтобы завершить все ожидающие обновления."; - -/* Managed Update type */ -"Managed Update" = "Управляемое Обновление"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Отложенные обязательные обновления"; - -/* More link text */ -"More" = "Дополнительно"; - -/* Sidebar More By Developer label */ -"More by %s" = "Еще от %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Еще в %s"; - -/* My Items label */ -"My Items" = "Мои элементы"; - -/* No Licenses Available display text */ -"No licenses available" = "Нет доступных лицензий"; - -/* No Updates message */ -"No pending updates" = "Нет отложенных обновлений"; - -/* Item Not Found title */ -"Not Found" = "Не найдено"; - -/* Not Currently Available display text */ -"Not currently available" = "В настоящее время не доступно"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Недостаточно места на диске"; - -/* Not Installed status text */ -"Not installed" = "Не установлено"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Нечего удалять."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Один или несколько элементов должны быть установлены до %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "Одно или несколько обязательных обновлений просрочены для установки. Сеанс будет принудительно завершен."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "Один или несколько обвнолений должны быть установлены до %s. Сеанс может быть принудительно завершен."; - -/* Other Available Updates label */ -"Other available updates" = "Другие доступные обновления"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Другие авторизированные пользователи используют следующие приложения. Попробуйте обновление позже, когда они больше не используются:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Другие пользователи сейчас в системе"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Удаление пакета завершено."; - -/* Password label */ -"Password:" = "Пароль:"; - -/* Pending Updates alert title */ -"Pending updates" = "Отложенные обновления"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Выполнение послеполетных задач..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Выполнение предварительных задач..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Подготовка удаления"; - -/* Problem Updates label */ -"Problem updates" = "проблемные обновления"; - -/* Quit button title */ -"Quit" = "Завершить"; - -/* Removal Error status text */ -"Removal Error" = "Ошибка Удаления"; - -/* Removal Requested status text */ -"Removal requested" = "Запрошено удаление"; - -/* Remove action text */ -"Remove" = "Удалить"; - -/* Removing status text */ -"Removing" = "Удаление"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Удаление квитанции"; - -/* Install Required action text */ -"Required" = "Обязательное"; - -/* Restart button title */ -"Restart" = "Перезагрузить"; - -/* Restart Required title */ -"Restart Required" = "Требуется перезагрузка"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Получение списка ПО для этой машины..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Выполнение установщика Adobe Patch"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Выполнение Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Выполнение Adobe Uninstall"; - -/* No Installed Software secondary text */ -"Select software to install." = "Выберите ПО для установки."; - -/* Sidebar Size label */ -"Size:" = "Размер:"; - -/* Software label */ -"Software" = "Программы"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Установленное или удаленное ПО требует перезагрузки."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Установленное или удаленное ПО требует перезагрузки. У Вас будет возможность сохранить открытые документы."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Запуск Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Запуск..."; - -/* Sidebar Status label */ -"Status:" = "Статус:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Проблема с конфигурацией системы"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Программное обеспечение было успешно установлено."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Есть дополнительные отложенные обновления для установки или удаления."; - -/* No Items primary text */ -"There are no available software items." = "Нет доступного ПО."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Нет элементов от этого разработчика."; - -/* No Category Results primary text */ -"There are no items in this category." = "Нет элементов в этой категории."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Другие пользователи сейчас в системе.\nОбновление может привести к потере их несохраненных документов.\n\nПовторите попытку после выхода других пользователей из системы."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Есть отложенные обновления для этого компьютера."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Ошибка в конфигурации managed software installer. Не удалось запустить процесс. Обратитесь к системному администратору."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Ошибка в конфигурации managed software installer. Процесс завершился неожиданно. Обратитесь к системному администратору."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "В данный момент для Вашего компьютера нет обновлений."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "Недостаточно места на диске для загрузки и установки этого элемента."; - -/* Dependency List prologue text */ -"This item is required by:" = "Этот элемент необходим для:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Этот элемент должен быть установлен до %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Для разрешения введите свой пароль для входа в систему."; - -/* No Items secondary text */ -"Try again later." = "Попробуйте еще раз позже."; - -/* No Search Results secondary text */ -"Try searching again." = "Попробуйте поискать снова."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Попробуйте выбрать другую категорию."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Попробуйте выбрать другой разработчика."; - -/* Sidebar Type label */ -"Type:" = "Тип:"; - -/* Unavailable status text */ -"Unavailable" = "Недоступно"; - -/* No Category name */ -"Uncategorized" = "Без категории"; - -/* Update button title/action text */ -"Update" = "Обновить"; - -/* Update All button title */ -"Update All" = "Обновить Все"; - -/* Update Required long action text */ -"Update Required" = "Обновить Обязательное"; - -/* No comment provided by engineer. */ -"Update available" = "Доступно обновление"; - -/* Update Check Failed title */ -"Update check failed" = "Не удалось проверить наличие обновлений"; - -/* Update In Progress primary text */ -"Update in progress." = "Обновление выполняется."; - -/* Update Now button title */ -"Update now" = "Обновить сейчас"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Обновление будет установлено"; - -/* Updates label */ -"Updates" = "Обновления"; - -/* Updating message */ -"Updating..." = "Обновление..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Проверка целостности пакета..."; - -/* Sidebar Version label */ -"Version" = "Версия"; - -/* Sidebar Version label */ -"Version:" = "Версия:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Ожидание сети..."; - -/* Will Be Installed status text */ -"Will be installed" = "Будет установлено"; - -/* Will Be Removed status text */ -"Will be removed" = "Будет удалено"; - -/* No Installed Software primary text */ -"You have no selected software." = "У вас нет выбранного ПО."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Вы должны закрыть следующие приложения перед установкой или удалением:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "Вы должны перейти на версию macOS %s чтобы иметь возможность установить этот элемент."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Ваш компьютер не подключен к постоянному источнику питания."; - -/* No Search Results primary text */ -"Your search had no results." = "По Вашему запросу ничего не найдено."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "У Вас самое новое ПО."; - -/* macOS update required text */ -"macOS update required" = "требуется обновление macOS"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/MainMenu.strings deleted file mode 100644 index 53a61d0e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/ru.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Справка"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Справка"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Справка Центр Управления ПО"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Службы"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Службы"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Скрыть Центр Управления ПО"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Завершить Центр Управления ПО"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Скрыть остальные"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Показать все"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Окно"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Скопировать"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Выбрать все"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Вырезать"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Поиск"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Мои эелементы"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Удалить"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Вставить"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Правка"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Отменить"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Повторить"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Правка"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Свернуть"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Изменить масштаб"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Окно"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Центр Управления ПО"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Перейти"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Перейти"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Центр Управления ПО"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Центр Управления ПО"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "О Центре Управления ПО"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Категории"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Обновить страницу"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Обновления"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Обновления"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Навигация"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Категории"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Категории"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Мои элементы"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Мои элементы"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Программы"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Закрыть"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Назад"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Поиск"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Перейти в полноэкранный режим"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Программы"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Программы"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Обновления"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Показать журнал"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Вперед"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings deleted file mode 100644 index 8a1ea041..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Managed Software Center"; -"CFBundleDisplayName" = "Managed Software Center"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/Localizable.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/Localizable.strings deleted file mode 100644 index 2f39cb76..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/Localizable.strings +++ /dev/null @@ -1,476 +0,0 @@ -/* Multiple Updates message */ -"%s pending updates" = "%s uppdateringar väntar"; - -/* One Update message */ -"1 pending update" = "1 uppdatering väntar"; - -/* Logout Required detail */ -"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?" = "Det krävs en utloggning före uppdateringen. Det kan bli en kort fördröjning vid inloggningsfönstret. Logga ut och uppdatera nu?"; - -/* Logout warning string when logout is an hour or more away */ -"A logout will be forced at approximately %s." = "Du kommer tvingas att loggas ut om cirka %s."; - -/* Logout warning string when logout is in < 60 minutes */ -"A logout will be forced in less than %s minutes." = "Du kommer tvingas att loggas ut om cirka %s minuter."; - -/* Logout warning string when logout is in less than a minute */ -"A logout will be forced in less than a minute.\nAll pending updates will be installed. Unsaved work will be lost." = "En automatisk utloggning kommer att ske om mindre än en minut.\nAlla väntande uppdateringar kommer att installereras. Ej sparat arbete kommer att förloras!"; - -/* Removal Error message */ -"A removal attempt failed. Removal will be attempted again.\nIf this situation continues, contact your systems administrator." = "Avinstallationen misslyckades. Ytterligare försök att avinstallera kommer att göras.\nKontakta din datoradministratör om problemet återkommer."; - -/* Restart Required detail */ -"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?" = "En omstart krävs efter uppdateringen. Det kan bli en kort fördröjning vid inloggningsfönstret. Logga ut och uppdatera nu?"; - -/* System configuration problem alert detail */ -"A systems configuration issue is preventing Managed Software Center from operating correctly. The reported issue is: " = "Ett problem med systemkonfigurationen hindrar Managed Software Center från att fungera korrekt. Det rapporterade felet är: "; - -/* Additional Pending Updates title */ -"Additional Pending Updates" = "Ytterligare uppdateringar"; - -/* AllCategoriesLabel */ -"All Categories" = "Alla kategorier"; - -/* AllItemsHeaderText */ -"All items" = "Alla objekt"; - -/* Forced Logout warning detail */ -"All pending updates will be installed. Unsaved work will be lost.\nYou may avoid the forced logout by logging out now." = "Alla väntande uppdateringar kommer att installeras. Ej sparat arbete kommer att förloras!\nDu kan undgå detta genom att logga ut nu."; - -/* Allow button text */ -"Allow" = "Tillåt"; - -/* Install Error message */ -"An installation attempt failed. Installation will be attempted again.\nIf this situation continues, contact your systems administrator." = "Installationen misslyckades. Ytterligare installationsförsök kommer att göras.\nKontakta din datoradministratör om problemet återkommer."; - -/* Long Not Enough Disk Space For Update display text */ -"An older version is currently installed. There is not enough disk space to download and install this update." = "An older version is currently installed. There is not enough disk space to download and install this update."; - -/* Long update requires a higher OS version text */ -"An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update." = "An older version is currently installed. You must upgrade to macOS version %s or higher to be able to install this update."; - -/* Other Users Blocking Apps Running title */ -"Applications in use by others" = "Andra användare på datorn har programmet öppet"; - -/* Cancel button title/short action text */ -"Cancel" = "Avbryt"; - -/* Cancel Install long action text */ -"Cancel install" = "Avbryt installation"; - -/* Cancel Removal long action text */ -"Cancel removal" = "Avbryt avinstallation"; - -/* Cancel Update long action text */ -"Cancel update" = "Avbryt uppdatering"; - -/* Item Not Found message */ -"Cannot display the requested item." = "Kan inte visa de objekt du begärt."; - -/* Categories label */ -"Categories" = "Kategorier"; - -/* Sidebar Category label */ -"Category:" = "Kategori:"; - -/* Check Again button title */ -"Check Again" = "Kontrollera igen"; - -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerar Apples lista med programuppdateringar..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Kontrollerar ytterligare ändringar..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Letar efter Apple-uppdateringar..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Letar efter uppdateringar..."; - -/* Checking For Updates message */ -"Checking for updates..." = "Letar efter uppdateringar..."; - -/* Blocking Apps Running title */ -"Conflicting applications running" = "Det finns öppna program som skapar en konflikt med installationen"; - -/* Continue button text */ -"Continue" = "Fortsätt"; - -/* Critical Update type */ -"Critical Update" = "Kritisk uppdatering"; - -/* Unavailable long action text */ -"Currently Unavailable" = "Inte tillgänglig just nu"; - -/* Deny button text */ -"Deny" = "Avbryt"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Bestämmer vad som ska tas bort"; - -/* Sidebar Developer label */ -"Developer:" = "Utvecklare:"; - -/* managedsoftwareupdate message */ -"Done." = "Klar."; - -/* Downloading status text */ -"Downloading" = "Hämtar"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Hämtar Apple-uppdateringar..."; - -/* Sidebar Due label */ -"Due:" = "Dags:"; - -/* FeaturedLabel */ -"Featured" = "I blickfånget"; - -/* FeaturedItemsHeaderText */ -"Featured items" = "I blickfånget"; - -/* managedsoftwareupdate message */ -"Finishing..." = "Avslutar..."; - -/* Firmware Alert Default detail */ -"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." = "Den fasta programvaran i din dator kommer att uppdateras. Strömsladden måste vara ansluten till en fungerande strömkälla. Det kan ta flera minuter att installera uppdateringen. Försök inte avbryta uppdateringen eller stänga av datorn under tiden."; - -/* No Power Source Warning detail */ -"For best results, you should connect your computer to a power source before updating. Are you sure you want to continue the update?" = "Du bör ansluta datorn till en strömkälla innan du uppdaterar. Är du säker på att du vill fortsätta?"; - -/* Forced Logout title text */ -"Forced Logout for Mandatory Install" = "Tvingad utloggning för en obligatorisk installation"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samlar information om installerade paket"; - -/* No help alert title */ -"Help" = "Hjälp"; - -/* No help alert detail */ -"Help isn't available for Managed Software Center." = "Det finns ingen hjälp för Managed Software Center."; - -/* Sidebar Information label */ -"Information" = "Information"; - -/* Install action text */ -"Install" = "Installera"; - -/* Install Required action text */ -"Install Required" = "Installation krävs"; - -/* Install Requested status text */ -"Install requested" = "Installation begärd"; - -/* Install Session Failed title */ -"Install session failed" = "Installationen misslyckades"; - -/* Install Error status text */ -"Installation Error" = "Installationsfel"; - -/* Installed status text */ -"Installed" = "Installerad"; - -/* Installing status text */ -"Installing" = "Installerar"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerar tillgängliga Apple-uppdateringar..."; - -/* Log out and Update button text */ -"Log out and update" = "Logga ut och uppdatera"; - -/* Logout and Update Now button text */ -"Log out and update now" = "Logga ut och uppdatera nu"; - -/* Logout Required title */ -"Logout Required" = "Utloggning krävs"; - -/* Failed Preflight Check detail */ -"Managed Software Center cannot check for updates now.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan inte leta efter uppdateringar nu.\nFörsök igen senare. Kontakta din datoradministratör om problemet återkommer."; - -/* Cannot Contact Server detail */ -"Managed Software Center cannot contact the update server at this time.\nTry again later. If this situation continues, contact your systems administrator." = "Managed Software Center kan inte kontakta den uppdateringsservern just nu.\nFörsök igen senare. Kontakta din datoradministratör om problemet återkommer."; - -/* Password prompt title */ -"Managed Software Center wants to unlock the startup disk after restarting to complete all pending updates." = "Managed Software Center behöver låsa upp startskivan efter omstart för att slutföra alla uppdateringar."; - -/* Managed Update type */ -"Managed Update" = "Hanterad uppdatering"; - -/* Mandatory Updates Pending text */ -"Mandatory Updates Pending" = "Obligatoriska uppdateringar väntar"; - -/* More link text */ -"More" = "Mer"; - -/* Sidebar More By Developer label */ -"More by %s" = "Mer av %s"; - -/* Sidebar More In Category label */ -"More in %s" = "Mer i %s"; - -/* My Items label */ -"My Items" = "Mina objekt"; - -/* No Licenses Available display text */ -"No licenses available" = "Inga licenser tillgängliga"; - -/* No Updates message */ -"No pending updates" = "Inga uppdateringar väntar."; - -/* Item Not Found title */ -"Not Found" = "Kunde inte hittas"; - -/* Not Currently Available display text */ -"Not currently available" = "Inte tillgänglig just nu"; - -/* Not Enough Disk Space display text */ -"Not enough disk space" = "Inte tillräckligt med utrymme på skivan"; - -/* Not Installed status text */ -"Not installed" = "Ej installerat"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Inget att ta bort."; - -/* OK button title */ -"OK" = "OK"; - -/* Forced Install Date summary */ -"One or more items must be installed by %s" = "Ett eller flera objekt måste installeras senast %s"; - -/* Mandatory Updates Imminent detail */ -"One or more mandatory updates are overdue for installation. A logout will be forced soon." = "En eller flera obligatoriska uppdateringar har förfallit för installation. En utloggning kommer att tvingas snart."; - -/* Mandatory Updates Pending detail */ -"One or more updates must be installed by %s. A logout may be forced if you wait too long to update." = "En eller flera uppdateringar måste installeras senast %s. Om du väntar för länge, kommer det att ske en automatisk utloggning."; - -/* Other Available Updates label */ -"Other available updates" = "Andra tillgängliga uppdateringar"; - -/* Other Users Blocking Apps Running detail */ -"Other logged in users are using the following applications. Try updating later when they are no longer in use:\n\n%s" = "Andra användare i datorn har följande program öppna Försök uppdatera senare när de inte längre används:\n\n%s"; - -/* Other Users Logged In title */ -"Other users logged in" = "Andra användare är inloggade"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paketet borttaget."; - -/* Password label */ -"Password:" = "Lösenord:"; - -/* Pending Updates alert title */ -"Pending updates" = "Väntande uppdateringar"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Städar upp..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Förbereder..."; - -/* Preparing Removal status text */ -"Preparing removal" = "Förbereder borttagning"; - -/* Problem Updates label */ -"Problem updates" = "Problem updates"; - -/* Quit button title */ -"Quit" = "Avsluta"; - -/* Removal Error status text */ -"Removal Error" = "Borttagning misslyckades"; - -/* Removal Requested status text */ -"Removal requested" = "Borttagning begärd"; - -/* Remove action text */ -"Remove" = "Ta bort"; - -/* Removing status text */ -"Removing" = "Tar bort"; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Tar bort kvittot"; - -/* Install Required action text */ -"Required" = "Required"; - -/* Restart button title */ -"Restart" = "Omstart"; - -/* Restart Required title */ -"Restart Required" = "Omstart krävs"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Hämtar en lista med programvara för den här datorn..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kör Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kör Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kör Adobe Uninstall"; - -/* No Installed Software secondary text */ -"Select software to install." = "Välj programvara att installera."; - -/* Sidebar Size label */ -"Size:" = "Storlek:"; - -/* Software label */ -"Software" = "Programvara"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Programvara som installerades eller togs bort kräver en omstart.."; - -/* Restart Required alert detail */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Program som lades till eller togs bort kräver en omstart. Du kommer att få möjlighet att spara ditt arbete."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Startar Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Startar..."; - -/* Sidebar Status label */ -"Status:" = "Status:"; - -/* System configuration problem alert title */ -"System configuration problem" = "Problem med systemkonfiguration"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Programvaran installerades."; - -/* Additional Pending Updates detail */ -"There are additional pending updates to install or remove." = "Det fins fler uppdateringar att installera eller ta bort."; - -/* No Items primary text */ -"There are no available software items." = "Inga programvaror tillgängliga."; - -/* No Developer Results primary text */ -"There are no items from this developer." = "Det finns inga programvaror från den här utvecklaren."; - -/* No Category Results primary text */ -"There are no items in this category." = "Det finns inget i den här kategorin."; - -/* Other Users Logged In detail */ -"There are other users logged into this computer.\nUpdating now could cause other users to lose their work.\n\nPlease try again later after the other users have logged out." = "Det finns andra användare inloggade på datorn.\nOm du uppdaterar nu riskerar dom att förlora ej sparat arbete.\n\nProva igen när de andra användarna har loggat ut."; - -/* Pending Updates alert detail text */ -"There are pending updates for this computer." = "Uppdateringar väntar."; - -/* Could Not Start Session message */ -"There is a configuration problem with the managed software installer. Could not start the process. Contact your systems administrator." = "Ett fel i inställningarna för Managed Software Installer har upptäckts, kontakta din datoradministratör."; - -/* Unexpected Session End message */ -"There is a configuration problem with the managed software installer. The process ended unexpectedly. Contact your systems administrator." = "Ett fel i inställningarna för Managed Software Update upptäcktes. Processen avslutades oväntat. Kontakta din datoradministratör."; - -/* No Pending Updates secondary text */ -"There is no new software for your computer at this time." = "Det finns inga uppdateringar för närvarande."; - -/* Long Not Enough Disk Space display text */ -"There is not enough disk space to download and install this item." = "There is not enough disk space to download and install this item."; - -/* Dependency List prologue text */ -"This item is required by:" = "Krävs av:"; - -/* Forced Date warning */ -"This item must be installed by %s" = "Måste installeras senast %s"; - -/* Password explanation */ -"To allow this, enter your login password." = "Ange ditt inloggningslösenord för att tillåta detta."; - -/* No Items secondary text */ -"Try again later." = "Försök igen senare."; - -/* No Search Results secondary text */ -"Try searching again." = "Försök söka igen."; - -/* No Category Results secondary text */ -"Try selecting another category." = "Försök med en annan kategori."; - -/* No Developer Results secondary text */ -"Try selecting another developer." = "Försök med en annan utvecklare."; - -/* Sidebar Type label */ -"Type:" = "Typ:"; - -/* Unavailable status text */ -"Unavailable" = "Ej tillgänglig"; - -/* No Category name */ -"Uncategorized" = "Saknar kategori"; - -/* Update button title/action text */ -"Update" = "Uppdatera"; - -/* Update All button title */ -"Update All" = "Uppdatera alla"; - -/* Update Required long action text */ -"Update Required" = "Uppdatering krävs"; - -/* No comment provided by engineer. */ -"Update available" = "Uppdatering tillgänglig"; - -/* Update Check Failed title */ -"Update check failed" = "Uppdateringskontrollen misslyckades"; - -/* Update In Progress primary text */ -"Update in progress." = "Uppdatering pågår."; - -/* Update Now button title */ -"Update now" = "Uppdatera nu"; - -/* Update Will Be Installed status text */ -"Update will be installed" = "Uppdateringen kommer att installeras"; - -/* Updates label */ -"Updates" = "Uppdateringar"; - -/* Updating message */ -"Updating..." = "Uppdateringar..."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Verifierar paket..."; - -/* Sidebar Version label */ -"Version" = "Version"; - -/* Sidebar Version label */ -"Version:" = "Version:"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Väntar på nätverket..."; - -/* Will Be Installed status text */ -"Will be installed" = "Kommer att installeras"; - -/* Will Be Removed status text */ -"Will be removed" = "Kommer att tas bort"; - -/* No Installed Software primary text */ -"You have no selected software." = "Du har inte valt någon programvara."; - -/* Blocking Apps Running detail */ -"You must quit the following applications before proceeding with installation or removal:\n\n%s" = "Du måste avsluta följande program innan installationen eller avinstallationen kan fortsätta:\n\n%s"; - -/* Long item requires a higher OS version text */ -"You must upgrade to macOS version %s to be able to install this item." = "You must upgrade to macOS version %s to be able to install this item."; - -/* No Power Source Warning text */ -"Your computer is not connected to a power source." = "Datorn är inte ansluten till en strömkälla."; - -/* No Search Results primary text */ -"Your search had no results." = "Din sökning gav inga resultat."; - -/* No Pending Updates primary text */ -"Your software is up to date." = "Programvaran är uppdaterad."; - -/* macOS update required text */ -"macOS update required" = "macOS update required"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/MainMenu.strings b/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/MainMenu.strings deleted file mode 100644 index 377de523..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/sv.lproj/MainMenu.strings +++ /dev/null @@ -1,164 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjälp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjälp"; - -/* Class = "NSMenuItem"; title = "Managed Software Center Help"; ObjectID = "111"; */ -"111.title" = "Managed Software Center Hjälp"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjänster"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjänster"; - -/* Class = "NSMenuItem"; title = "Hide Managed Software Center"; ObjectID = "134"; */ -"134.title" = "Göm Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Quit Managed Software Center"; ObjectID = "136"; */ -"136.title" = "Avsluta Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Göm övriga"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Visa alla"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fönster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopiera"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Markera alla"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klipp ut"; - -/* Class = "NSMenuItem"; title = "Search"; ObjectID = "1XY-pX-Pwv"; */ -"1XY-pX-Pwv.title" = "Sök"; - -/* Class = "NSMenuItem"; title = "My Items"; ObjectID = "1uK-HI-wGR"; */ -"1uK-HI-wGR.title" = "Mina objekt"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Radera"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Klistra in"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Redigera"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Ångra"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Ångra"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Redigera"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimera"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zooma"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fönster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AHuvudmeny"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "371"; */ -"371.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Navigate"; ObjectID = "375"; */ -"375.title" = "Navigera"; - -/* Class = "NSMenu"; title = "Navigate"; ObjectID = "376"; */ -"376.title" = "Navigera"; - -/* Class = "NSMenuItem"; title = "Managed Software Center"; ObjectID = "56"; */ -"56.title" = "Managed Software Center"; - -/* Class = "NSMenu"; title = "Managed Software Center"; ObjectID = "57"; */ -"57.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "About Managed Software Center"; ObjectID = "58"; */ -"58.title" = "Om Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Categories"; ObjectID = "9S1-fB-bKZ"; */ -"9S1-fB-bKZ.title" = "Kategorier"; - -/* Class = "NSMenuItem"; title = "Reload Page"; ObjectID = "Afg-mB-WlJ"; */ -"Afg-mB-WlJ.title" = "Ladda om sidan"; - -/* Class = "NSToolbarItem"; label = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.label" = "Uppdateringar"; - -/* Class = "NSToolbarItem"; paletteLabel = "Updates"; ObjectID = "AjB-m1-mu8"; */ -"AjB-m1-mu8.paletteLabel" = "Uppdateringar"; - -/* Class = "NSToolbarItem"; paletteLabel = "Navigation"; ObjectID = "CEb-sx-e3H"; */ -"CEb-sx-e3H.paletteLabel" = "Navigering"; - -/* Class = "NSTextFieldCell"; title = "Password:"; ObjectID = "Ef8-fO-Dm6"; */ -"Ef8-fO-Dm6.title" = "Password:"; - -/* Class = "NSToolbarItem"; label = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.label" = "Kategorier"; - -/* Class = "NSToolbarItem"; paletteLabel = "Categories"; ObjectID = "G2X-M7-NVB"; */ -"G2X-M7-NVB.paletteLabel" = "Kategorier"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "Rl8-0j-Dpu"; */ -"Rl8-0j-Dpu.title" = "Text Cell"; - -/* Class = "NSToolbarItem"; label = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.label" = "Mina objekt"; - -/* Class = "NSToolbarItem"; paletteLabel = "My Items"; ObjectID = "Uby-CQ-lvU"; */ -"Uby-CQ-lvU.paletteLabel" = "Mina objekt"; - -/* Class = "NSMenuItem"; title = "Software"; ObjectID = "VYO-og-DRc"; */ -"VYO-og-DRc.title" = "Programvara"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "X5I-7f-aJs"; */ -"X5I-7f-aJs.title" = "Log"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "a1e-6Q-iNS"; */ -"a1e-6Q-iNS.title" = "Text Cell"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Stäng"; - -/* Class = "NSMenuItem"; title = "Back"; ObjectID = "fEq-6D-Ce0"; */ -"fEq-6D-Ce0.title" = "Tillbaka"; - -/* Class = "NSToolbarItem"; paletteLabel = "Search"; ObjectID = "fbJ-cF-weR"; */ -"fbJ-cF-weR.paletteLabel" = "Sök"; - -/* Class = "NSMenuItem"; title = "Enter Full Screen"; ObjectID = "jQt-Mr-wFT"; */ -"jQt-Mr-wFT.title" = "Fullskärm"; - -/* Class = "NSToolbarItem"; label = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.label" = "Programvara"; - -/* Class = "NSToolbarItem"; paletteLabel = "Software"; ObjectID = "jTu-Wf-fi2"; */ -"jTu-Wf-fi2.paletteLabel" = "Programvara"; - -/* Class = "NSToolbarItem"; paletteLabel = "Narrow Space"; ObjectID = "sF1-NQ-sSb"; */ -"sF1-NQ-sSb.paletteLabel" = "Narrow Space"; - -/* Class = "NSMenuItem"; title = "Updates"; ObjectID = "tv9-wZ-XWN"; */ -"tv9-wZ-XWN.title" = "Uppdateringar"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "vPs-dO-LDa"; */ -"vPs-dO-LDa.title" = "Visa logg"; - -/* Class = "NSMenuItem"; title = "Forward"; ObjectID = "z4Z-vu-XGX"; */ -"z4Z-vu-XGX.title" = "Framåt"; diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/category_item_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/category_item_template.html deleted file mode 100644 index 56597f29..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/category_item_template.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - ${category_name_escaped} - ${item1_display_name_escaped} - ${item2_display_name_escaped} - ${item3_display_name_escaped} - - \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_more_items_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_more_items_template.html deleted file mode 100644 index 44a44b18..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_more_items_template.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - ${display_name_escaped} - - ${second_line} - - \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_template.html deleted file mode 100644 index a1178d02..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/detail_template.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - ${display_name_escaped} - - - - - - - - - - - - - - - - - - - - - - - - ${long_action_text} - - - - - - ${restart_action_text} - - - - - - - ${display_name_escaped} - - - ${description} - - - - - - - - - - ${informationLabel} - - - - ${categoryLabel} - ${category} - - ${versionLabel} ${version_to_install} - ${sizeLabel} ${size} - - ${developerLabel} - ${developer_escaped} - - - - - - ${statusLabel} - - - ${status_text} - - - - - - ${restart_action_text} - - - - - - - ${moreInCategoryLabel} - - ${more_in_category} - - - - - ${moreByDeveloperLabel} - - ${more_by_developer} - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/footer_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/footer_template.html deleted file mode 100644 index ac74a0ea..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/footer_template.html +++ /dev/null @@ -1,7 +0,0 @@ - - - ${SoftwareLabel} - ${CategoriesLabel} - ${UpdatesLabel} - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_item_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_item_template.html deleted file mode 100644 index 79eb6e71..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_item_template.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - ${display_name_escaped} - ${category_and_developer_escaped} - - ${status_text} - - - - - - - - ${short_action_text} - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_template.html deleted file mode 100644 index 6a3bc746..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/list_template.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - Managed Software Center - - - - - - - - - - - - - - ${showcase} - - - - - - - ${header_text} - - - - ${list_items} - - - - - - ${sidebar} - - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_row_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_row_template.html deleted file mode 100644 index 4d9c42c0..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_row_template.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - ${display_name_escaped} - ${developer_escaped} - - - ${version_to_install} - ${size} - - - ${status_text} - - - - - - - - ${myitem_action_text} - - - - - \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_template.html deleted file mode 100644 index b830b31e..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/myitems_template.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - My Items - - - - - - - - - - - - - - - - - - ${my_items_header_label} - - - - - - - - - - - - ${myitems_rows} - - - - - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/page_not_found_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/page_not_found_template.html deleted file mode 100644 index fe2130ea..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/page_not_found_template.html +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - Item Not Found - - - - - - - - - - - - - - - - - ${item_not_found_title} - - - - - ${item_not_found_message} - - - - - - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/showcase_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/showcase_template.html deleted file mode 100644 index a6634e72..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/showcase_template.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/sidebar_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/sidebar_template.html deleted file mode 100644 index 3cced693..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/sidebar_template.html +++ /dev/null @@ -1,10 +0,0 @@ - - - ${CategoriesLabel} - - - ${category_list} - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/status_results_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/status_results_template.html deleted file mode 100644 index 7b34aa02..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/status_results_template.html +++ /dev/null @@ -1,7 +0,0 @@ - - ${primary_status_text} - ${secondary_status_text} - - - - \ No newline at end of file diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/update_row_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/update_row_template.html deleted file mode 100644 index 5b91c100..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/update_row_template.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - ${display_name_escaped} - ${developer_escaped} - ${version_label} ${display_version_escaped} - ${restart_action_text} - - - - - ${description_without_images} - ${more_link_text} - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updatedetail_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updatedetail_template.html deleted file mode 100644 index 1254f1c8..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updatedetail_template.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - ${display_name_escaped} - - - - - - - - - - - - - - - - - - - - - - - - - ${restart_action_text} - - - - - - - ${display_name_escaped} - - - ${description} - - - - - - - - - ${informationLabel} - - - ${typeLabel} ${type} - ${versionLabel} ${display_version_escaped} - ${sizeLabel} ${size} - ${developerLabel} ${developer_escaped} - - - - ${dueLabel}${short_due_date} - ${restart_action_text} - - - - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updates_template.html b/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updates_template.html deleted file mode 100644 index d0579786..00000000 --- a/code/apps/pyobjc/Managed Software Center/Managed Software Center/templates/updates_template.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - Updates - - - - - - - - - - - - - - - - - - - - - - ${update_count} - - - ${warning_text} - - - - - - ${install_btn_label} - - - - - - - - - - - - - ${update_rows} - - - - - - - - - - - ${problem_updates_header_message} - - - - - - ${problem_update_rows} - - - - - - - - - - ${other_updates_header_message} - - - - - - ${other_update_rows} - - - - - - - - - - - - - diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/toolbarCategoriesTemplate.pdf b/code/apps/pyobjc/Managed Software Center/Managed Software Center/toolbarCategoriesTemplate.pdf deleted file mode 100644 index cb3c08ae..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/toolbarCategoriesTemplate.pdf and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Managed Software Center/updatesTemplate.png b/code/apps/pyobjc/Managed Software Center/Managed Software Center/updatesTemplate.png deleted file mode 100644 index dd645692..00000000 Binary files a/code/apps/pyobjc/Managed Software Center/Managed Software Center/updatesTemplate.png and /dev/null differ diff --git a/code/apps/pyobjc/Managed Software Center/Yet more notes.txt b/code/apps/pyobjc/Managed Software Center/Yet more notes.txt deleted file mode 100644 index ffe91aab..00000000 --- a/code/apps/pyobjc/Managed Software Center/Yet more notes.txt +++ /dev/null @@ -1,34 +0,0 @@ -desired_state = 'installed' or 'not-installed' or 'dont-care' -maps to managed_install, managed_uninstall, or not in SelfServeManifest - -removal-error (implies desired_state is not-installed) - click -> cancel -> desired_state = dont-care -will-be-removed - click -> cancel -> desired_state = dont-care -installed-not-removable - no action possible -removal-requested - click -> cancel -> desired_state = dont-care (No updatecheck needed) -installed - click -> remove -> desired_state = not-installed (immediate state is removal-requested) -update-must-be-installed - no action possible -update-will-be-installed - click -> cancel -> desired_state = dont-care -update-available - click -> install -> desired_state = installed (immediate state is install-requested) - -install-error (implies desired_state is installed) - click -> cancel -> desired_state = dont-care -unavailable - no action possible -must-be-installed - no action possible -will-be-installed - click -> cancel -> desired_state = dont-care -install-requested - click -> cancel -> desired_state = dont-care (No updatecheck needed) -not-installed - click -> install -> desired_state = installed (immediate state is install-requested) - - diff --git a/code/apps/pyobjc/MunkiStatus/.gitignore b/code/apps/pyobjc/MunkiStatus/.gitignore deleted file mode 100644 index 447a2713..00000000 --- a/code/apps/pyobjc/MunkiStatus/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# .DS_Store files! -.DS_Store - -# don't track .pyc files -*.pyc - -# Xcode 5 user data -*.xcodeproj/project.xcworkspace/ -*.xcodeproj/xcuserdata/ - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj b/code/apps/pyobjc/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj deleted file mode 100644 index 60305f86..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus.xcodeproj/project.pbxproj +++ /dev/null @@ -1,570 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - C002ECE51913F6D6003DD155 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C002ECE21913F6D6003DD155 /* Localizable.strings */; }; - C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */ = {isa = PBXBuildFile; fileRef = C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */; }; - C00F67551F01680A00D9007D /* CocoaWrapper.py in Resources */ = {isa = PBXBuildFile; fileRef = C00F67541F01680A00D9007D /* CocoaWrapper.py */; }; - C046261E1A00019800AF1E48 /* MainMenu.strings in Resources */ = {isa = PBXBuildFile; fileRef = C002ECE01913F6D6003DD155 /* MainMenu.strings */; }; - C05C3CEF188391F200095E65 /* munki.py in Resources */ = {isa = PBXBuildFile; fileRef = C05C3CEE188391F200095E65 /* munki.py */; }; - C09004F216CDD84E00BE34CE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C09004F116CDD84E00BE34CE /* Cocoa.framework */; }; - C09004FE16CDD84E00BE34CE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C09004FC16CDD84E00BE34CE /* InfoPlist.strings */; }; - C090050016CDD84E00BE34CE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C09004FF16CDD84E00BE34CE /* main.m */; }; - C090050616CDD84E00BE34CE /* main.py in Resources */ = {isa = PBXBuildFile; fileRef = C090050516CDD84E00BE34CE /* main.py */; }; - C090050816CDD84E00BE34CE /* MSUAppDelegate.py in Resources */ = {isa = PBXBuildFile; fileRef = C090050716CDD84E00BE34CE /* MSUAppDelegate.py */; }; - C090050B16CDD84E00BE34CE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = C090050916CDD84E00BE34CE /* MainMenu.xib */; }; - C094B6D1188F7CE100E06897 /* MSUStatusWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C094B6D0188F7CE100E06897 /* MSUStatusWindowController.py */; }; - C094B6D61891826700E06897 /* BorderlessWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = C094B6D31891826700E06897 /* BorderlessWindow.m */; }; - C094B6D71891826700E06897 /* ScaledImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = C094B6D51891826700E06897 /* ScaledImageView.m */; }; - C0AE8658186D2DF900C87AE7 /* MunkiStatus.icns in Resources */ = {isa = PBXBuildFile; fileRef = C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */; }; - C0CC54681EEADDB40064A100 /* libpython2.7.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = C0CC54671EEADDB40064A100 /* libpython2.7.dylib */; }; - C0D67B581CC55BFD009E8C2F /* MSULogWindowController.py in Resources */ = {isa = PBXBuildFile; fileRef = C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - C002ECE11913F6D6003DD155 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/MainMenu.strings; sourceTree = ""; }; - C002ECE31913F6D6003DD155 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - C005C5AF1913F7410074AF82 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B01913F7440074AF82 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B11913F7460074AF82 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B21913F7490074AF82 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B31913F74C0074AF82 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B41913F74E0074AF82 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B51913F7500074AF82 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B61913F7530074AF82 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B71913F7550074AF82 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/MainMenu.strings; sourceTree = ""; }; - C005C5B81913F75C0074AF82 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; - C005C5B91913F75E0074AF82 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BA1913F75F0074AF82 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BB1913F7610074AF82 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BC1913F7620074AF82 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BD1913F7630074AF82 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BE1913F7640074AF82 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; - C005C5BF1913F7660074AF82 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; - C005C5C01913F7660074AF82 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; - C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = FoundationPlist.py; sourceTree = ""; }; - C00F67541F01680A00D9007D /* CocoaWrapper.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = CocoaWrapper.py; sourceTree = ""; }; - C02A5D4D1B4DAF8800A929F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ja; path = ja.lproj/MainMenu.xib; sourceTree = ""; }; - C02A5D4E1B4DAF8F00A929F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/MainMenu.strings; sourceTree = ""; }; - C02A5D4F1B4DAF9400A929F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; - C02A5D501B4DAF9900A929F4 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/InfoPlist.strings; sourceTree = ""; }; - C04626191A00014800AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/MainMenu.strings; sourceTree = ""; }; - C046261A1A00015600AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; - C046261C1A00016200AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; - C046261D1A00019800AF1E48 /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = it.lproj/MainMenu.xib; sourceTree = ""; }; - C05C3CEE188391F200095E65 /* munki.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = munki.py; path = MunkiStatus/munki.py; sourceTree = SOURCE_ROOT; }; - C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BorderlessWindow.h; sourceTree = ""; }; - C07E956C1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fr; path = fr.lproj/MainMenu.xib; sourceTree = ""; }; - C07E956D1913ECEF00B40B9A /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E956E1913ECF400B40B9A /* de */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = de; path = de.lproj/MainMenu.xib; sourceTree = ""; }; - C07E956F1913ECF400B40B9A /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E95701913ECFB00B40B9A /* es */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = es; path = es.lproj/MainMenu.xib; sourceTree = ""; }; - C07E95711913ECFB00B40B9A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E95721913ED1700B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nl; path = nl.lproj/MainMenu.xib; sourceTree = ""; }; - C07E95731913ED1700B40B9A /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E95741913ED2300B40B9A /* da */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = da; path = da.lproj/MainMenu.xib; sourceTree = ""; }; - C07E95751913ED2300B40B9A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E95761913ED2A00B40B9A /* fi */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = fi; path = fi.lproj/MainMenu.xib; sourceTree = ""; }; - C07E95771913ED2A00B40B9A /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E95781913ED3600B40B9A /* nb */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = nb; path = nb.lproj/MainMenu.xib; sourceTree = ""; }; - C07E95791913ED3600B40B9A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E957A1913ED3D00B40B9A /* sv */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = sv; path = sv.lproj/MainMenu.xib; sourceTree = ""; }; - C07E957B1913ED3D00B40B9A /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; - C07E957C1913ED4C00B40B9A /* ru */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = ru; path = ru.lproj/MainMenu.xib; sourceTree = ""; }; - C07E957D1913ED4C00B40B9A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; - C09004ED16CDD84E00BE34CE /* MunkiStatus.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MunkiStatus.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; }; - C09004F816CDD84E00BE34CE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - C09004FB16CDD84E00BE34CE /* MunkiStatus-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "MunkiStatus-Info.plist"; sourceTree = ""; }; - C09004FD16CDD84E00BE34CE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - C09004FF16CDD84E00BE34CE /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - C090050116CDD84E00BE34CE /* MunkiStatus-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MunkiStatus-Prefix.pch"; sourceTree = ""; }; - C090050516CDD84E00BE34CE /* main.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = main.py; sourceTree = ""; }; - C090050716CDD84E00BE34CE /* MSUAppDelegate.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = MSUAppDelegate.py; sourceTree = ""; }; - C090050A16CDD84E00BE34CE /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; }; - C094B6D0188F7CE100E06897 /* MSUStatusWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSUStatusWindowController.py; sourceTree = ""; }; - C094B6D31891826700E06897 /* BorderlessWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BorderlessWindow.m; sourceTree = ""; }; - C094B6D41891826700E06897 /* ScaledImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScaledImageView.h; sourceTree = ""; }; - C094B6D51891826700E06897 /* ScaledImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScaledImageView.m; sourceTree = ""; }; - C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = MunkiStatus.icns; sourceTree = ""; }; - C0CC54671EEADDB40064A100 /* 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 = ""; }; - C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = MSULogWindowController.py; sourceTree = ""; }; - E65810B31993C96E00E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/Localizable.strings; sourceTree = ""; }; - E65810B51993C97500E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/MainMenu.strings; sourceTree = ""; }; - E65810B61993C97C00E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_CA; path = en_CA.lproj/InfoPlist.strings; sourceTree = ""; }; - E65810B71993C9B400E53A48 /* en_CA */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_CA; path = en_CA.lproj/MainMenu.xib; sourceTree = ""; }; - E6B4BCDA1993D7D0004D1B81 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/MainMenu.strings; sourceTree = ""; }; - E6B4BCDB1993D7D3004D1B81 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/Localizable.strings; sourceTree = ""; }; - E6B4BCDD1993D7D8004D1B81 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_GB; path = en_GB.lproj/InfoPlist.strings; sourceTree = ""; }; - E6B4BCDE1993D7E0004D1B81 /* en_GB */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_GB; path = en_GB.lproj/MainMenu.xib; sourceTree = ""; }; - E6B4BCDF1993D7E5004D1B81 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en_AU; path = en_AU.lproj/MainMenu.xib; sourceTree = ""; }; - E6B4BCE01993D7E8004D1B81 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/MainMenu.strings; sourceTree = ""; }; - E6B4BCE11993D7EC004D1B81 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/Localizable.strings; sourceTree = ""; }; - E6B4BCE21993D7F1004D1B81 /* en_AU */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en_AU; path = en_AU.lproj/InfoPlist.strings; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - C09004EA16CDD84E00BE34CE /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - C09004F216CDD84E00BE34CE /* Cocoa.framework in Frameworks */, - C0CC54681EEADDB40064A100 /* libpython2.7.dylib in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - C09004E216CDD84E00BE34CE = { - isa = PBXGroup; - children = ( - C09004F916CDD84E00BE34CE /* MunkiStatus */, - C09004F016CDD84E00BE34CE /* Frameworks */, - C09004EE16CDD84E00BE34CE /* Products */, - C0CC54671EEADDB40064A100 /* libpython2.7.dylib */, - ); - sourceTree = ""; - }; - C09004EE16CDD84E00BE34CE /* Products */ = { - isa = PBXGroup; - children = ( - C09004ED16CDD84E00BE34CE /* MunkiStatus.app */, - ); - name = Products; - sourceTree = ""; - }; - C09004F016CDD84E00BE34CE /* Frameworks */ = { - isa = PBXGroup; - children = ( - C09004F116CDD84E00BE34CE /* Cocoa.framework */, - C09004F516CDD84E00BE34CE /* Other Frameworks */, - ); - name = Frameworks; - sourceTree = ""; - }; - C09004F516CDD84E00BE34CE /* Other Frameworks */ = { - isa = PBXGroup; - children = ( - C09004F616CDD84E00BE34CE /* AppKit.framework */, - C09004F816CDD84E00BE34CE /* Foundation.framework */, - ); - name = "Other Frameworks"; - sourceTree = ""; - }; - C09004F916CDD84E00BE34CE /* MunkiStatus */ = { - isa = PBXGroup; - children = ( - C06137A51C9CB3BD00EC298E /* BorderlessWindow.h */, - C094B6D31891826700E06897 /* BorderlessWindow.m */, - C094B6D41891826700E06897 /* ScaledImageView.h */, - C094B6D51891826700E06897 /* ScaledImageView.m */, - C094B6D0188F7CE100E06897 /* MSUStatusWindowController.py */, - C05C3CEE188391F200095E65 /* munki.py */, - C0B37439187089B900B6204E /* Resources */, - C00A4C56185FCEC9004EB3B7 /* FoundationPlist.py */, - C090050516CDD84E00BE34CE /* main.py */, - C090050716CDD84E00BE34CE /* MSUAppDelegate.py */, - C0AE8657186D2DF900C87AE7 /* MunkiStatus.icns */, - C090050916CDD84E00BE34CE /* MainMenu.xib */, - C09004FA16CDD84E00BE34CE /* Supporting Files */, - C0D67B571CC55BFD009E8C2F /* MSULogWindowController.py */, - C00F67541F01680A00D9007D /* CocoaWrapper.py */, - ); - path = MunkiStatus; - sourceTree = ""; - }; - C09004FA16CDD84E00BE34CE /* Supporting Files */ = { - isa = PBXGroup; - children = ( - C002ECE01913F6D6003DD155 /* MainMenu.strings */, - C002ECE21913F6D6003DD155 /* Localizable.strings */, - C09004FB16CDD84E00BE34CE /* MunkiStatus-Info.plist */, - C09004FC16CDD84E00BE34CE /* InfoPlist.strings */, - C09004FF16CDD84E00BE34CE /* main.m */, - C090050116CDD84E00BE34CE /* MunkiStatus-Prefix.pch */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - C0B37439187089B900B6204E /* Resources */ = { - isa = PBXGroup; - children = ( - ); - name = Resources; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - C09004EC16CDD84E00BE34CE /* MunkiStatus */ = { - isa = PBXNativeTarget; - buildConfigurationList = C090050E16CDD84E00BE34CE /* Build configuration list for PBXNativeTarget "MunkiStatus" */; - buildPhases = ( - C09CE1DB18BEA77500B9724A /* Localize */, - C09004E916CDD84E00BE34CE /* Sources */, - C09004EA16CDD84E00BE34CE /* Frameworks */, - C09004EB16CDD84E00BE34CE /* Resources */, - C09CE1DC18BEA78300B9724A /* Embed version info */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = MunkiStatus; - productName = SomeDumbTest; - productReference = C09004ED16CDD84E00BE34CE /* MunkiStatus.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - C09004E416CDD84E00BE34CE /* Project object */ = { - isa = PBXProject; - attributes = { - CLASSPREFIX = MSC; - LastUpgradeCheck = 0450; - ORGANIZATIONNAME = "The Munki Project"; - }; - buildConfigurationList = C09004E716CDD84E00BE34CE /* Build configuration list for PBXProject "MunkiStatus" */; - 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, - en_CA, - en_GB, - en_AU, - it, - ja, - ); - mainGroup = C09004E216CDD84E00BE34CE; - productRefGroup = C09004EE16CDD84E00BE34CE /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - C09004EC16CDD84E00BE34CE /* MunkiStatus */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - C09004EB16CDD84E00BE34CE /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C09004FE16CDD84E00BE34CE /* InfoPlist.strings in Resources */, - C090050616CDD84E00BE34CE /* main.py in Resources */, - C090050816CDD84E00BE34CE /* MSUAppDelegate.py in Resources */, - C094B6D1188F7CE100E06897 /* MSUStatusWindowController.py in Resources */, - C00F67551F01680A00D9007D /* CocoaWrapper.py in Resources */, - C002ECE51913F6D6003DD155 /* Localizable.strings in Resources */, - C046261E1A00019800AF1E48 /* MainMenu.strings in Resources */, - C05C3CEF188391F200095E65 /* munki.py in Resources */, - C090050B16CDD84E00BE34CE /* MainMenu.xib in Resources */, - C00A4C57185FCEC9004EB3B7 /* FoundationPlist.py in Resources */, - C0AE8658186D2DF900C87AE7 /* MunkiStatus.icns in Resources */, - C0D67B581CC55BFD009E8C2F /* MSULogWindowController.py in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - C09CE1DB18BEA77500B9724A /* Localize */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = Localize; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "cd MunkiStatus\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 nl --utf8\n./Localize.py --from en --to fr --utf8\n./Localize.py --from en --to de --utf8\n./Localize.py --from en --to es --utf8\n./Localize.py --from en --to da --utf8\n./Localize.py --from en --to fi --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 ru --utf8\n./Localize.py --from en --to sv --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"; - }; - C09CE1DC18BEA78300B9724A /* 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 = ( - C094B6D61891826700E06897 /* BorderlessWindow.m in Sources */, - C090050016CDD84E00BE34CE /* main.m in Sources */, - C094B6D71891826700E06897 /* ScaledImageView.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - C002ECE01913F6D6003DD155 /* MainMenu.strings */ = { - isa = PBXVariantGroup; - children = ( - C002ECE11913F6D6003DD155 /* en */, - C005C5AF1913F7410074AF82 /* fr */, - C005C5B01913F7440074AF82 /* de */, - C005C5B11913F7460074AF82 /* es */, - C005C5B21913F7490074AF82 /* nl */, - C005C5B31913F74C0074AF82 /* da */, - C005C5B41913F74E0074AF82 /* fi */, - C005C5B51913F7500074AF82 /* nb */, - C005C5B61913F7530074AF82 /* sv */, - C005C5B71913F7550074AF82 /* ru */, - E65810B51993C97500E53A48 /* en_CA */, - E6B4BCDA1993D7D0004D1B81 /* en_GB */, - E6B4BCE01993D7E8004D1B81 /* en_AU */, - C04626191A00014800AF1E48 /* it */, - C02A5D4E1B4DAF8F00A929F4 /* ja */, - ); - name = MainMenu.strings; - sourceTree = ""; - }; - C002ECE21913F6D6003DD155 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - C002ECE31913F6D6003DD155 /* en */, - C005C5B81913F75C0074AF82 /* fr */, - C005C5B91913F75E0074AF82 /* de */, - C005C5BA1913F75F0074AF82 /* es */, - C005C5BB1913F7610074AF82 /* nl */, - C005C5BC1913F7620074AF82 /* da */, - C005C5BD1913F7630074AF82 /* fi */, - C005C5BE1913F7640074AF82 /* nb */, - C005C5BF1913F7660074AF82 /* sv */, - C005C5C01913F7660074AF82 /* ru */, - E65810B31993C96E00E53A48 /* en_CA */, - E6B4BCDB1993D7D3004D1B81 /* en_GB */, - E6B4BCE11993D7EC004D1B81 /* en_AU */, - C046261A1A00015600AF1E48 /* it */, - C02A5D4F1B4DAF9400A929F4 /* ja */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - C09004FC16CDD84E00BE34CE /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - C09004FD16CDD84E00BE34CE /* en */, - C07E956D1913ECEF00B40B9A /* fr */, - C07E956F1913ECF400B40B9A /* de */, - C07E95711913ECFB00B40B9A /* es */, - C07E95731913ED1700B40B9A /* nl */, - C07E95751913ED2300B40B9A /* da */, - C07E95771913ED2A00B40B9A /* fi */, - C07E95791913ED3600B40B9A /* nb */, - C07E957B1913ED3D00B40B9A /* sv */, - C07E957D1913ED4C00B40B9A /* ru */, - E65810B61993C97C00E53A48 /* en_CA */, - E6B4BCDD1993D7D8004D1B81 /* en_GB */, - E6B4BCE21993D7F1004D1B81 /* en_AU */, - C046261C1A00016200AF1E48 /* it */, - C02A5D501B4DAF9900A929F4 /* ja */, - ); - name = InfoPlist.strings; - sourceTree = ""; - }; - C090050916CDD84E00BE34CE /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - C090050A16CDD84E00BE34CE /* en */, - C07E956C1913ECEF00B40B9A /* fr */, - C07E956E1913ECF400B40B9A /* de */, - C07E95701913ECFB00B40B9A /* es */, - C07E95721913ED1700B40B9A /* nl */, - C07E95741913ED2300B40B9A /* da */, - C07E95761913ED2A00B40B9A /* fi */, - C07E95781913ED3600B40B9A /* nb */, - C07E957A1913ED3D00B40B9A /* sv */, - C07E957C1913ED4C00B40B9A /* ru */, - E65810B71993C9B400E53A48 /* en_CA */, - E6B4BCDE1993D7E0004D1B81 /* en_GB */, - E6B4BCDF1993D7E5004D1B81 /* en_AU */, - C046261D1A00019800AF1E48 /* it */, - C02A5D4D1B4DAF8800A929F4 /* ja */, - ); - name = MainMenu.xib; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - C090050C16CDD84E00BE34CE /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; - 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; - 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.6; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - }; - name = Debug; - }; - C090050D16CDD84E00BE34CE /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; - 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.6; - SDKROOT = macosx; - }; - name = Release; - }; - C090050F16CDD84E00BE34CE /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "MunkiStatus/MunkiStatus-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 = "MunkiStatus/MunkiStatus-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_NAME = MunkiStatus; - WRAPPER_EXTENSION = app; - }; - name = Debug; - }; - C090051016CDD84E00BE34CE /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ARCHS = "$(ARCHS_STANDARD)"; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "MunkiStatus/MunkiStatus-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 = "MunkiStatus/MunkiStatus-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_NAME = MunkiStatus; - WRAPPER_EXTENSION = app; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - C09004E716CDD84E00BE34CE /* Build configuration list for PBXProject "MunkiStatus" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C090050C16CDD84E00BE34CE /* Debug */, - C090050D16CDD84E00BE34CE /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - C090050E16CDD84E00BE34CE /* Build configuration list for PBXNativeTarget "MunkiStatus" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - C090050F16CDD84E00BE34CE /* Debug */, - C090051016CDD84E00BE34CE /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = C09004E416CDD84E00BE34CE /* Project object */; -} diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.h b/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.h deleted file mode 100644 index 897f755e..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.h +++ /dev/null @@ -1,31 +0,0 @@ -// -// BorderlessWindow.h -// -// Created by Greg Neagle on 5/16/09. -// 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. -// - - -#import - - -@interface BorderlessWindow : NSWindow - -- (id) initWithContentRect: (NSRect) contentRect - styleMask: (unsigned int) aStyle - backing: (NSBackingStoreType) bufferingType - defer: (BOOL) flag; - -@end diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.m b/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.m deleted file mode 100644 index cf230005..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/BorderlessWindow.m +++ /dev/null @@ -1,41 +0,0 @@ -// -// BorderlessWindow.m -// -// Created by Greg Neagle on 5/16/09. -// 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. -// - -#import "BorderlessWindow.h" - - -@implementation BorderlessWindow - -- (id) initWithContentRect: (NSRect) contentRect - styleMask: (unsigned int) aStyle - backing: (NSBackingStoreType) bufferingType - defer: (BOOL) flag -{ - if (self = [super initWithContentRect: contentRect - styleMask: NSBorderlessWindowMask - backing: bufferingType - defer: flag]) - - { - // other initialization - } - - return self; -} -@end diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/CocoaWrapper.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/CocoaWrapper.py deleted file mode 100644 index 3932065e..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/CocoaWrapper.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# CocoaWrapper.py -# MunkiStatus -# -# 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 ( - NSBundle, - NSData, - NSFileHandle, - NSLocalizedString, - NSLog, - NSMutableArray, - NSObject, - NSPoint, - NSPredicate, - NSString, - NSTimer, - NSURL, - NSUTF8StringEncoding, -) - -# put all AppKit imports used by the project here -from AppKit import ( - NSAlert, - NSApp, - NSBackingStoreBuffered, - NSBorderlessWindowMask, - NSColor, - NSDistributedNotificationCenter, - NSDragOperationAll, - NSImage, - NSMenu, - NSNotFound, - NSNotificationSuspensionBehaviorDeliverImmediately, - NSPasteboard, - NSScreen, - NSScreenSaverWindowLevel, - NSWindow, - NSWindowAbove, -) diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/FoundationPlist.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/FoundationPlist.py deleted file mode 100644 index 96a901e3..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/FoundationPlist.py +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/python -# 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) - - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/Localize.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/Localize.py deleted file mode 100755 index 12945a48..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/Localize.py +++ /dev/null @@ -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.+)=(?P.+)$', 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) diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUAppDelegate.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUAppDelegate.py deleted file mode 100644 index b9087c7f..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUAppDelegate.py +++ /dev/null @@ -1,81 +0,0 @@ -# encoding: utf-8 -# -# MSUAppDelegate.py -# MunkiStatus -# -# 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. -'''Following Cocoa application design pattern - defines our app delegate -class''' - -from objc import YES, NO, IBOutlet -import PyObjCTools -## 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 - -# lots of camelCase names following Cocoa style -# pylint: disable=C0103 - - -class MSUAppDelegate(NSObject): - '''Implements some NSApplicationDelegateProtocol methods''' - - # since this subclasses NSObject, - # it doesn't have a Python __init__method - # pylint: disable=no-init - - # several delegate methods pass a 'sender' object that we don't use - # pylint: disable=unused-argument - - statusWindowController = IBOutlet() - logWindowController = IBOutlet() - - def applicationWillFinishLaunching_(self, sender): - '''NSApplicationDelegate method - Sent by the default notification center immediately before the - application object is initialized.''' - - # pylint: disable=no-self-use - - consoleuser = munki.getconsoleuser() - if consoleuser == None or consoleuser == u"loginwindow": - # don't show menu bar - NSMenu.setMenuBarVisible_(NO) - # make sure we're active - NSApp.activateIgnoringOtherApps_(YES) - - def applicationDidFinishLaunching_(self, sender): - '''NSApplicationDelegate method - Sent by the default notification center after the application has - been launched and initialized but before it has received its first - event.''' - - # Prevent automatic relaunching at login on Lion+ - if NSApp.respondsToSelector_('disableRelaunchOnLogin'): - NSApp.disableRelaunchOnLogin() - - # show the default initial view - self.statusWindowController.initStatusSession() - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSULogWindowController.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSULogWindowController.py deleted file mode 100644 index 7e5b4e8f..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSULogWindowController.py +++ /dev/null @@ -1,233 +0,0 @@ -# -*- coding: utf-8 -*- -# -# MSULogWindowController.py -# MunkiStatus -# -# Created by Greg Neagle on 4/18/16. -# Copyright (c) 2016-2019 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 MSULogViewDataSource(NSObject): - """Data source for an NSTableView that displays an array of text lines. - Line breaks are assumed to be LF, and partial lines from incremental - reading is handled.""" - - # since this subclasses NSObject, - # it doesn't have a Python __init__method - # pylint: disable=no-init - - logFileData = NSMutableArray.alloc().init() - filteredData = logFileData - - lastLineIsPartial = False - filterText = '' - - def tableView_writeRowsWithIndexes_toPasteboard_( - self, aTableView, rowIndexes, pasteboard): - '''Implements drag-n-drop of text rows to external apps''' - text_to_copy = '' - index_set = aTableView.selectedRowIndexes() - index = index_set.firstIndex() - while index != NSNotFound: - line = self.filteredData.objectAtIndex_(index) - text_to_copy += line + '\n' - index = index_set.indexGreaterThanIndex_(index) - #changeCount = pasteboard.clearContents() - result = pasteboard.writeObjects_([text_to_copy]) - return YES - - def applyFilterToData(self): - '''Filter our log data''' - if len(self.filterText): - filterPredicate = NSPredicate.predicateWithFormat_( - 'self CONTAINS[cd] %@', self.filterText) - self.filteredData = ( - self.logFileData.filteredArrayUsingPredicate_(filterPredicate)) - else: - self.filteredData = self.logFileData - - def addLine_partial_(self, line, isPartial): - '''Add a line to our datasource''' - if self.lastLineIsPartial: - joinedLine = self.logFileData.lastObject() + line - self.logFileData.removeLastObject() - self.logFileData.addObject_(joinedLine) - else: - self.logFileData.addObject_(line) - self.lastLineIsPartial = isPartial - self.applyFilterToData() - - def removeAllLines(self): - '''Remove all data from our datasource''' - self.logFileData.removeAllObjects() - - def lineCount(self): - '''Return the number of lines in our filtered data''' - return self.filteredData.count() - - def numberOfRowsInTableView_(self, tableView): - '''Required datasource method''' - return self.lineCount() - - def tableView_objectValueForTableColumn_row_(self, tableView, column, row): - '''Required datasource method -- returns the text data for the - given row and column''' - if column.identifier() == 'data': - return self.filteredData.objectAtIndex_(row) - else: - return '' - - -class MSULogWindowController(NSObject): - '''Controller object for our log window''' - - # since this subclasses NSObject, - # it doesn't have a Python __init__method - # pylint: disable=no-init - - window = IBOutlet() - logView = IBOutlet() - searchField = IBOutlet() - pathControl = IBOutlet() - - logFileData = MSULogViewDataSource.alloc().init() - - fileHandle = None - updateTimer = None - - def copy_(self, sender): - '''Implements copy operation so we can copy data from table view''' - text_to_copy = '' - index_set = self.logView.selectedRowIndexes() - index = index_set.firstIndex() - while index != NSNotFound: - line = self.logFileData.filteredData.objectAtIndex_(index) - text_to_copy += line + '\n' - index = index_set.indexGreaterThanIndex_(index) - pasteboard = NSPasteboard.generalPasteboard() - changeCount = pasteboard.clearContents() - result = pasteboard.writeObjects_([text_to_copy]) - - @IBAction - def searchFilterChanged_(self, sender): - '''User changed the search field''' - filterString = self.searchField.stringValue().lower() - self.logFileData.filterText = filterString - self.logFileData.applyFilterToData() - self.logView.reloadData() - - def getWindowLevel(self): - '''Gets our NSWindowLevel. Works around issues with the loginwindow - PolicyBanner in 10.11+ Some code based on earlier work by Pepijn - Bruienne''' - window_level = NSScreenSaverWindowLevel - 1 - # Get our Darwin major version - darwin_vers = int(os.uname()[2].split('.')[0]) - have_policy_banner = False - for test_file in ['/Library/Security/PolicyBanner.txt', - '/Library/Security/PolicyBanner.rtf', - '/Library/Security/PolicyBanner.rtfd']: - if os.path.exists(test_file): - have_policy_banner = True - break - # bump our NSWindowLevel if we have a PolicyBanner in ElCap+ - if have_policy_banner and darwin_vers > 14: - window_level = NSScreenSaverWindowLevel - return window_level - - @IBAction - def showLogWindow_(self, notification): - '''Show the log window.''' - - if self.window.isVisible(): - # It's already open, just move it to front - self.window.makeKeyAndOrderFront_(self) - return - - consoleuser = munki.getconsoleuser() - if consoleuser == None or consoleuser == u"loginwindow": - self.window.setCanBecomeVisibleWithoutLogin_(True) - self.window.setLevel_(self.getWindowLevel()) - - screenRect = NSScreen.mainScreen().frame() - windowRect = screenRect.copy() - windowRect.origin.x = 100.0 - windowRect.origin.y = 200.0 - windowRect.size.width -= 200.0 - windowRect.size.height -= 300.0 - - logfile = munki.pref('LogFile') - self.pathControl.setURL_(NSURL.fileURLWithPath_(logfile)) - self.window.setTitle_(os.path.basename(logfile)) - self.window.setFrame_display_(windowRect, NO) - self.window.makeKeyAndOrderFront_(self) - self.watchLogFile_(logfile) - - # allow dragging from table view to outside of the app - self.logView.setDraggingSourceOperationMask_forLocal_( - NSDragOperationAll, NO) - - def watchLogFile_(self, logFile): - '''Display and continuously update a log file in the main window.''' - self.stopWatching() - self.logFileData.removeAllLines() - self.logView.setDataSource_(self.logFileData) - self.logView.reloadData() - self.fileHandle = NSFileHandle.fileHandleForReadingAtPath_(logFile) - self.refreshLog() - # Kick off a timer that updates the log view periodically. - self.updateTimer = ( - NSTimer. - scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( - 0.25, self, self.refreshLog, None, YES)) - - def stopWatching(self): - '''Release the file handle and stop the update timer.''' - if self.fileHandle is not None: - self.fileHandle.closeFile() - self.fileHandle = None - if self.updateTimer is not None: - self.updateTimer.invalidate() - self.updateTimer = None - - def refreshLog(self): - '''Check for new available data, read it, and scroll to the bottom.''' - data = self.fileHandle.availableData() - if data.length(): - utf8string = NSString.alloc().initWithData_encoding_( - data, NSUTF8StringEncoding) - for line in utf8string.splitlines(True): - if line.endswith(u"\n"): - self.logFileData.addLine_partial_(line.rstrip(u"\n"), False) - else: - self.logFileData.addLine_partial_(line, True) - self.logView.reloadData() - self.logView.scrollRowToVisible_(self.logFileData.lineCount() - 1) - - def windowWillClose_(self, notification): - '''NSWindow delegate method -- if our window is closing, - stop watching the log file.''' - self.stopWatching() diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUStatusWindowController.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUStatusWindowController.py deleted file mode 100644 index 688c44f6..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MSUStatusWindowController.py +++ /dev/null @@ -1,531 +0,0 @@ -# encoding: utf-8 -# -# MSUStatusWindowController.py -# MunkiStatus -# -# 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. -'''Controller for the main status window''' - -from objc import YES, NO, IBAction, IBOutlet, nil -from PyObjCTools import AppHelper - -import os - -import munki -import FoundationPlist - -## 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 - - -# lots of camelCase names, following Cocoa convention -# pylint: disable=invalid-name - -debug = False - -def getLoginwindowPicture(): - '''Returns the image behind the loginwindow (in < 10.7)''' - desktopPicturePath = '' - loginwindowPrefsPath = "/Library/Preferences/com.apple.loginwindow.plist" - if os.path.exists(loginwindowPrefsPath): - loginwindowPrefs = FoundationPlist.readPlist(loginwindowPrefsPath) - if loginwindowPrefs: - desktopPicturePath = loginwindowPrefs.get('DesktopPicture', '') - if desktopPicturePath: - if os.path.exists(desktopPicturePath): - theImage = NSImage.alloc().initWithContentsOfFile_( - desktopPicturePath) - if theImage: - return theImage - return NSImage.imageNamed_("Solid Aqua Blue") - theImage = NSImage.alloc().initWithContentsOfFile_( - "/System/Library/CoreServices/DefaultDesktop.jpg") - if theImage: - return theImage - else: - return NSImage.imageNamed_("Solid Aqua Blue") - - -class MSUStatusWindowController(NSObject): - '''Controls the status window.''' - - # since this subclasses NSObject, - # it doesn't have a Python __init__method - # pylint: disable=no-init - - window = IBOutlet() - logWindow = IBOutlet() - messageFld = IBOutlet() - detailFld = IBOutlet() - progressIndicator = IBOutlet() - stopBtn = IBOutlet() - imageFld = IBOutlet() - - backdropWindow = IBOutlet() - backdropImageFld = IBOutlet() - - stopBtnState = 0 - restartAlertDismissed = 0 - got_status_update = False - receiving_notifications = False - timer = None - timeout_counter = 0 - saw_process = False - managedsoftwareupdate_pid = None - window_level = NSScreenSaverWindowLevel - 1 - - - @IBAction - def stopBtnClicked_(self, sender): - '''Called when stop button is clicked in the status window''' - if debug: - NSLog(u"Stop button was clicked.") - sender.setState_(1) - self.stopBtnState = 1 - sender.setEnabled_(False) - # send a notification that stop button was clicked - STOP_REQUEST_FLAG = ('/private/tmp/com.googlecode.munki.' - 'managedsoftwareupdate.stop_requested') - if not os.path.exists(STOP_REQUEST_FLAG): - open(STOP_REQUEST_FLAG, 'w').close() - - def registerForNotifications(self): - '''Register for notification messages''' - dnc = NSDistributedNotificationCenter.defaultCenter() - dnc.addObserver_selector_name_object_suspensionBehavior_( - self, - self.updateStatus_, - 'com.googlecode.munki.managedsoftwareupdate.statusUpdate', - None, - NSNotificationSuspensionBehaviorDeliverImmediately) - dnc.addObserver_selector_name_object_suspensionBehavior_( - self, - self.managedsoftwareupdateStarted_, - 'com.googlecode.munki.managedsoftwareupdate.started', - None, - NSNotificationSuspensionBehaviorDeliverImmediately) - dnc.addObserver_selector_name_object_suspensionBehavior_( - self, - self.managedsoftwareupdateEnded_, - 'com.googlecode.munki.managedsoftwareupdate.ended', - 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 managedsoftwareupdateStarted_(self, notification): - '''Called when we get a - com.googlecode.munki.managedsoftwareupdate.started notification''' - if 'pid' in notification.userInfo(): - self.managedsoftwareupdate_pid = notification.userInfo()['pid'] - NSLog('managedsoftwareupdate pid %s started' - % self.managedsoftwareupdate_pid) - - def managedsoftwareupdateEnded_(self, notification): - '''Called when we get a - com.googlecode.munki.managedsoftwareupdate.ended notification''' - NSLog('managedsoftwareupdate pid %s ended' - % notification.userInfo().get('pid')) - - def haveElCapPolicyBanner(self): - '''Returns True if we are running El Cap or later and there is - a loginwindow PolicyBanner in place''' - # Get our Darwin major version - darwin_vers = int(os.uname()[2].split('.')[0]) - if darwin_vers > 14: - for test_file in ['/Library/Security/PolicyBanner.txt', - '/Library/Security/PolicyBanner.rtf', - '/Library/Security/PolicyBanner.rtfd']: - if os.path.exists(test_file): - return True - return False - - def setWindowLevel(self): - '''Sets our NSWindowLevel. Works around issues with the loginwindow - PolicyBanner in 10.11+ Some code based on earlier work by Pepijn - Bruienne''' - # bump our NSWindowLevel if we have a PolicyBanner in ElCap+ - if self.haveElCapPolicyBanner(): - NSLog('El Capitan+ loginwindow PolicyBanner found') - self.window_level = NSScreenSaverWindowLevel - - def initStatusSession(self): - '''Initialize our status session''' - self.setWindowLevel() - consoleuser = munki.getconsoleuser() - if consoleuser == None or consoleuser == u"loginwindow": - self.displayBackdropWindow() - # needed so the window can show over the loginwindow - self.window.setCanBecomeVisibleWithoutLogin_(True) - self.window.setLevel_(self.window_level) - - self.window.center() - self.messageFld.setStringValue_( - NSLocalizedString(u"Starting…", None)) - self.detailFld.setStringValue_(u"") - self.stopBtn.setHidden_(False) - self.stopBtn.setEnabled_(True) - self.stopBtnState = 0 - if self.imageFld: - theImage = NSImage.imageNamed_("MunkiStatus") - self.imageFld.setImage_(theImage) - if self.progressIndicator: - self.progressIndicator.setMinValue_(0.0) - self.progressIndicator.setMaxValue_(100.0) - self.progressIndicator.setIndeterminate_(True) - self.progressIndicator.setUsesThreadedAnimation_(True) - self.progressIndicator.startAnimation_(self) - self.window.orderFrontRegardless() - self.registerForNotifications() - # start our process monitor timer so we can be notified about - # process failure - self.timeout_counter = 6 - self.saw_process = False - self.timer = (NSTimer. - scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( - 5.0, self, self.checkProcess, None, YES)) - - def checkProcess(self): - '''Monitors managedsoftwareupdate process for failure to start - or unexpected exit, so we're not waiting around forever if - managedsoftwareupdate isn't running.''' - PYTHON_SCRIPT_NAME = 'managedsoftwareupdate' - NEVER_STARTED = -2 - UNEXPECTEDLY_QUIT = -1 - - NSLog('checkProcess timer fired') - - if self.window_level == NSScreenSaverWindowLevel: - # we're at the loginwindow, there is a PolicyBanner, and we're - # running under 10.11+. Make sure we're in the front. - NSApp.activateIgnoringOtherApps_(YES) - if not self.logWindow.isVisible(): - self.window.makeKeyAndOrderFront_(self) - - if self.got_status_update: - # we got a status update since we last checked; no need to - # check the process table - 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: - NSLog('managedsoftwareupdate not running...') - self.timeout_counter -= 1 - if self.timeout_counter == 0: - NSLog('Timed out waiting for managedsoftwareupdate.') - if self.saw_process: - self.statusSessionFailed_(UNEXPECTEDLY_QUIT) - else: - self.statusSessionFailed_(NEVER_STARTED) - - def statusSessionFailed_(self, sessionResult): - '''Called if the status session fails''' - NSLog('statusSessionFailed: %s' % sessionResult) - self.cleanUpStatusSession() - NSApp.terminate_(self) - - def cleanUpStatusSession(self): - '''Clean things up before we exit''' - self.unregisterForNotifications() - if self.backdropWindow and self.backdropWindow.isVisible(): - self.backdropWindow.orderOut_(self) - self.window.orderOut_(self) - # clean up timer - if self.timer: - self.timer.invalidate() - self.timer = None - - def configureAndDisplayBackdropWindow_(self, window): - '''Sets all our configuration options for our masking windows''' - window.setCanBecomeVisibleWithoutLogin_(True) - if self.haveElCapPolicyBanner(): - self.backdropWindow.setLevel_(self.window_level) - else: - self.backdropWindow.setLevel_(self.window_level - 1) - translucentColor = NSColor.blackColor().colorWithAlphaComponent_(0.35) - window.setBackgroundColor_(translucentColor) - window.setOpaque_(False) - window.setIgnoresMouseEvents_(False) - window.setAlphaValue_(0.0) - window.orderFrontRegardless() - window.animator().setAlphaValue_(1.0) - - def displayBackdropWindow(self): - '''Draw a window that covers the login UI''' - self.backdropWindow.setCanBecomeVisibleWithoutLogin_(True) - if self.haveElCapPolicyBanner(): - self.backdropWindow.setLevel_(self.window_level) - else: - self.backdropWindow.setLevel_(self.window_level - 1) - screenRect = NSScreen.mainScreen().frame() - self.backdropWindow.setFrame_display_(screenRect, True) - - darwin_vers = int(os.uname()[2].split('.')[0]) - if darwin_vers < 11: - if self.backdropImageFld: - bgImage = getLoginwindowPicture() - self.backdropImageFld.setImage_(bgImage) - self.backdropWindow.orderFrontRegardless() - else: - # Lion+ - # draw transparent/translucent windows to prevent interaction - # with the login UI - self.backdropImageFld.setHidden_(True) - self.configureAndDisplayBackdropWindow_(self.backdropWindow) - # are there any other screens? - for screen in NSScreen.screens(): - if screen != NSScreen.mainScreen(): - # create another masking window for this secondary screen - window_rect = screen.frame() - window_rect.origin = NSPoint(0.0, 0.0) - child_window = NSWindow.alloc( - ).initWithContentRect_styleMask_backing_defer_screen_( - window_rect, - NSBorderlessWindowMask, NSBackingStoreBuffered, - NO, screen) - self.configureAndDisplayBackdropWindow_(child_window) - if self.haveElCapPolicyBanner(): - self.backdropWindow.addChildWindow_ordered_( - child_window, NSWindowAbove) - - if self.haveElCapPolicyBanner(): - # preserve the relative ordering of the backdrop window and the - # status window IOW, clicking the backdrop window will not bring it - # in front of the status window - self.backdropWindow.addChildWindow_ordered_( - self.window, NSWindowAbove) - - - def updateStatus_(self, notification): - '''Called when we get a - com.googlecode.munki.managedsoftwareupdate.statusUpdate notification; - update our status display with information from the notification''' - - if self.window_level == NSScreenSaverWindowLevel: - # we're at the loginwindow, there is a PolicyBanner, and we're - # running under 10.11+. Make sure we're in the front. - NSApp.activateIgnoringOtherApps_(YES) - if not self.logWindow.isVisible(): - self.window.makeKeyAndOrderFront_(self) - - self.got_status_update = True - info = notification.userInfo() - # 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 self.stopBtnState == 0 and 'stop_button_visible' in info_keys: - if info['stop_button_visible']: - self.showStopButton() - else: - self.hideStopButton() - if self.stopBtnState == 0 and 'stop_button_enabled' in info_keys: - if info['stop_button_enabled']: - self.enableStopButton() - else: - self.disableStopButton() - - command = info.get('command') - if command == 'activate': - NSApp.activateIgnoringOtherApps_(YES) - self.window.orderFrontRegardless() - elif command == 'showRestartAlert': - # clean up timer - if self.timer: - self.timer.invalidate() - self.timer = None - self.doRestartAlert() - elif command == 'quit': - self.cleanUpStatusSession() - NSApp.terminate_(self) - - def setPercentageDone_(self, percent): - '''Set progress indicator to display percent done''' - if float(percent) < 0: - if not self.progressIndicator.isIndeterminate(): - self.progressIndicator.setIndeterminate_(True) - self.progressIndicator.startAnimation_(self) - else: - if self.progressIndicator.isIndeterminate(): - self.progressIndicator.stopAnimation_(self) - self.progressIndicator.setIndeterminate_(False) - self.progressIndicator.setDoubleValue_(float(percent)) - - @AppHelper.endSheetMethod - def restartAlertDidEnd_returnCode_contextInfo_( - self, alert, returncode, contextinfo): - '''Called when restart alert is dismissed''' - # we don't use the returncode or contextinfo arguments - # pylint: disable=unused-argument - self.restartAlertDismissed = 1 - munki.restartNow() - - def doRestartAlert(self): - '''Display a restart alert''' - self.restartAlertDismissed = 0 - # pylint: disable=line-too-long - nsa = NSAlert.alertWithMessageText_defaultButton_alternateButton_otherButton_informativeTextWithFormat_( - NSLocalizedString(u"Restart Required", None), - NSLocalizedString(u"Restart", None), - nil, - nil, - NSLocalizedString( - u"Software installed or removed requires a restart. " - "You will have a chance to save open documents.", None)) - # pylint: enable=line-too-long - nsa.beginSheetModalForWindow_modalDelegate_didEndSelector_contextInfo_( - self.window, self, self.restartAlertDidEnd_returnCode_contextInfo_, - nil) - - def setMessage_(self, messageText): - '''Set the main status message''' - messageText = NSBundle.mainBundle().localizedStringForKey_value_table_( - messageText, messageText, None) - self.messageFld.setStringValue_(messageText) - - def setDetail_(self, detailText): - '''Set the status detail text''' - detailText = NSBundle.mainBundle().localizedStringForKey_value_table_( - detailText, detailText, None) - self.detailFld.setStringValue_(detailText) - - def getStopBtnState(self): - '''Return True if the stop button was clicked; False otherwise''' - return self.stopBtnState - - def hideStopButton(self): - '''Hide the stop button''' - self.stopBtn.setHidden_(True) - - def showStopButton(self): - '''Show the stop button''' - self.stopBtn.setHidden_(False) - - def enableStopButton(self): - '''Enable the stop button''' - self.stopBtn.setEnabled_(True) - - def disableStopButton(self): - '''Disable the stop button''' - self.stopBtn.setEnabled_(False) - - def getRestartAlertDismissed(self): - '''Return True if the restart alert was dismissed; False otherwise''' - return self.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''' - dummy = NSLocalizedString(u"Starting...", "managedsoftwareupdate message") - dummy = NSLocalizedString(u"Finishing...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Performing preflight tasks...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Performing postflight tasks...", "managedsoftwareupdate message") - - dummy = NSLocalizedString( - u"Checking for available updates...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Checking for additional changes...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Software installed or removed requires a restart.", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Waiting for network...", "managedsoftwareupdate message") - dummy = NSLocalizedString(u"Done.", "managedsoftwareupdate message") - - dummy = NSLocalizedString( - u"Retrieving list of software for this machine...", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Verifying package integrity...", "managedsoftwareupdate message") - - dummy = NSLocalizedString( - u"The software was successfully installed.", - "managedsoftwareupdate message") - - dummy = NSLocalizedString( - u"Gathering information on installed packages", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Determining which filesystem items to remove", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Removing receipt info", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Nothing to remove.", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Package removal complete.", "managedsoftwareupdate message") - - # apple update messages - dummy = NSLocalizedString( - u"Checking for available Apple Software Updates...", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Checking Apple Software Update catalog...", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Downloading available Apple Software Updates...", - "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Installing available Apple Software Updates...", - "managedsoftwareupdate message") - - # Adobe install/uninstall messages - dummy = NSLocalizedString( - u"Running Adobe Setup", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Running Adobe Uninstall", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Starting Adobe installer...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Running Adobe Patch Installer", "managedsoftwareupdate message") - - # macOS install/upgrade messages - dummy = NSLocalizedString( - u"Starting macOS upgrade...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"Preparing to run macOS Installer...", "managedsoftwareupdate message") - dummy = NSLocalizedString( - u"System will restart and begin upgrade of macOS.", - "managedsoftwareupdate message") diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist deleted file mode 100644 index 6d49d6cd..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Info.plist +++ /dev/null @@ -1,40 +0,0 @@ - - - - - NSRequiresAquaSystemAppearance - - LSUIElement - - CFBundleDevelopmentRegion - en - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - ${EXECUTABLE_NAME} - CFBundleIdentifier - com.googlecode.munki.MunkiStatus - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 4.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - GitRevision - - LSMinimumSystemVersion - ${MACOSX_DEPLOYMENT_TARGET} - NSHumanReadableCopyright - Copyright © 2019 The Munki Project https://github.com/munki/munki - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Prefix.pch b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Prefix.pch deleted file mode 100644 index 4ede094d..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus-Prefix.pch +++ /dev/null @@ -1,7 +0,0 @@ -// -// Prefix header for all source files of the 'MunkiStatus' target in the 'MunkiStatus' project -// - -#ifdef __OBJC__ - #import -#endif diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus.icns b/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus.icns deleted file mode 100644 index c6f61d81..00000000 Binary files a/code/apps/pyobjc/MunkiStatus/MunkiStatus/MunkiStatus.icns and /dev/null differ diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.h b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.h deleted file mode 100644 index 85f31ccf..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// ScaledImageViewView.h -// -// Created by Greg Neagle on 5/27/09. -// -// 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. -// - - -#import - - -@interface ScaledImageView : NSImageView - -@end diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.m b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.m deleted file mode 100644 index b9b3bb6a..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ScaledImageView.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// ScaledImageView.m -// -// Created by Greg Neagle on 5/27/09. -// -// 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. -// - - -#import "ScaledImageView.h" - - -@implementation ScaledImageView - --(void)drawRect:(NSRect)rect { - - NSRect dstRect = [self bounds]; - - float sourceWidth = [[self image] size].width; - float sourceHeight = [[self image] size].height; - float targetWidth = dstRect.size.width; - float targetHeight = dstRect.size.height; - - // Calculate aspect ratios - float sourceRatio = sourceWidth / sourceHeight; - float targetRatio = targetWidth / targetHeight; - - // Determine what side of the source image to use for proportional scaling - BOOL scaleWidth = (sourceRatio <= targetRatio); - - // Proportionally scale source image - float scalingFactor, scaledWidth, scaledHeight; - if (scaleWidth) { - scalingFactor = 1.0 / sourceRatio; - scaledWidth = targetWidth; - scaledHeight = round(targetWidth * scalingFactor); - } else { - scalingFactor = sourceRatio; - scaledWidth = round(targetHeight * scalingFactor); - scaledHeight = targetHeight; - } - float scaleFactor = scaledHeight / sourceHeight; - - // Calculate compositing rectangles - NSRect sourceRect; - float destX, destY; - - // Crop from center - destX = round((scaledWidth - targetWidth) / 2.0); - destY = round((scaledHeight - targetHeight) / 2.0); - - sourceRect = NSMakeRect(destX / scaleFactor, destY / scaleFactor, - targetWidth / scaleFactor, targetHeight / scaleFactor); - - - [[self image] drawInRect:dstRect - fromRect:sourceRect - operation:NSCompositeSourceOver - fraction:1.0]; - -} - - -@end diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/Localizable.strings deleted file mode 100644 index 3e5e2f03..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerer kataloget til Apple-softwareopdatering..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Søger efter yderligere ændringer..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Søger efter tilgængelige Apple-softwareopdateringer..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Søger efter tilgængelige opdateringer..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Undersøger hvilke elementer, der skal fjernes"; - -/* managedsoftwareupdate message */ -"Done." = "Færdig."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Henter tilgængelige Apple-softwareopdateringer..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Afslutter..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samler oplysninger om installeret software"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerer tilgængelige Apple-softwareopdateringer..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Intet at fjerne."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Fjernelse af software er gennemført."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Udfører postflight-opgaver..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Udfører preflight-opgaver..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Forbereder afvikling af macOS-installeringsprogrammet..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Fjerner oplysninger om kvittering"; - -/* No comment provided by engineer. */ -"Restart" = "Genstart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Kræver genstart"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Henter en liste over tilgænglig software til denne computer..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kører Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kører Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kører Adobe Uninstall"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Den software, der blev installeret eller fjernet, kræver en genstart."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Den software, der blev installeret eller fjernet, kræver en genstart. Du får mulighed for at gemme alle åbne dokumenter."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starter Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Starter macOS-opgradering..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starter..."; - -/* No comment provided by engineer. */ -"Starting…" = "Starter…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Systemet vil genstarte og begynde opgradering af macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Installation af software er gennemført."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Kontrollerer integritet af software..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Venter på netværk..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/MainMenu.strings deleted file mode 100644 index d5000f22..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/da.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjælp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjælp"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Hjælp til Managed Software Update"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Skjul Managed Software Update"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Slut Managed Software Update"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Skjul andre"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Vis alle"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Vindue"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Vælg alt"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klip"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Vis log"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Slet"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Sæt ind"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Fortryd"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Gentag"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimer"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Vindue"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "Managed Software Update"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "Managed Software Update"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Om Managed Software Update"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/Localizable.strings deleted file mode 100644 index d5d6790f..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Prüfe Apple Software Update Katalog..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Prüfe auf weitere Änderungen..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Prüfe verfügbare Apple Software Updates..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Prüfe auf verfügbare Updates..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Stelle fest, welche Dateien zu entfernen sind"; - -/* managedsoftwareupdate message */ -"Done." = "Fertig"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Lade verfügbare Apple Software Updates herunter..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Abschließen..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Sammle Informationen zu installierten Paketen"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installiere verfügbare Apple Software Updates..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Nichts zu entfernen."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Entfernen des Paketes abgeschlossen."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Führe postflight Aufgaben aus..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Führe preflight Aufgaben aus..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Vorbereitungen zum Ausführen des macOS Installer..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Entferne Paketquittung"; - -/* No comment provided by engineer. */ -"Restart" = "Neustart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Neustart erforderlich"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Hole Liste von Software für diesen Computer..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Starte Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Starte Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Starte Adobe Uninstall"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Installierte oder entfernte Software benötigt einen Neustart."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Softwareaktualisierung oder -entfernung benötigt einen Neustart. Sie können offene Dokumente noch sichern."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starte Adobe Installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "macOS Upgrade wird gestartet..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starte..."; - -/* No comment provided by engineer. */ -"Starting…" = "Starten…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Das System startet neu und beginnt dann mit dem Upgrade von macOS"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Die Software wurde erfolgreich installiert."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Prüfe Paketintegrität..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Warte auf Netzwerk..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/MainMenu.strings deleted file mode 100644 index d57305ea..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/de.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hilfe"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hilfe"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Softwareaktualiserung-Hilfe"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Dienste"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Dienste"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Softwareaktualiserung ausblenden"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Softwareaktualisierung beenden"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Andere ausblenden"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Alle einblenden"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fenster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopieren"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Alles auswählen"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Ausschneiden"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Protokoll einblenden"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Löschen"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Einfügen"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Bearbeiten"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Widerrufen"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Wiederholen"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Bearbeiten"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimieren"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoomen"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fenster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "Geführte Softwareaktualisierung"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Über Geführte Softwareaktualisierung"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stoppen"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Schließen"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings deleted file mode 100644 index 06da609f..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "MunkiStatus"; -"CFBundleDisplayName" = "MunkiStatus"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/Localizable.strings deleted file mode 100644 index 79cb3c0b..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/Localizable.strings +++ /dev/null @@ -1,12 +0,0 @@ -/* No comment provided by engineer. */ -"Restart" = "Restart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Restart Required"; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Software installed or removed requires a restart. You will have a chance to save open documents."; - -/* No comment provided by engineer. */ -"Starting…" = "Starting…"; - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/MainMenu.strings deleted file mode 100644 index 0ae3b085..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_AU.lproj/MainMenu.strings +++ /dev/null @@ -1,90 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Window"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimize"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Window"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "About MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Hide MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Quit MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Hide Others"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Show All"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copy"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Select All"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cut"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Delete"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Paste"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Centre"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "BorderlessWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/Localizable.strings deleted file mode 100644 index 79cb3c0b..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/Localizable.strings +++ /dev/null @@ -1,12 +0,0 @@ -/* No comment provided by engineer. */ -"Restart" = "Restart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Restart Required"; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Software installed or removed requires a restart. You will have a chance to save open documents."; - -/* No comment provided by engineer. */ -"Starting…" = "Starting…"; - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/MainMenu.strings deleted file mode 100644 index 0ae3b085..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_CA.lproj/MainMenu.strings +++ /dev/null @@ -1,90 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Window"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimize"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Window"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "About MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Hide MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Quit MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Hide Others"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Show All"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copy"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Select All"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cut"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Delete"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Paste"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Centre"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "BorderlessWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/Localizable.strings deleted file mode 100644 index 79cb3c0b..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/Localizable.strings +++ /dev/null @@ -1,12 +0,0 @@ -/* No comment provided by engineer. */ -"Restart" = "Restart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Restart Required"; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Software installed or removed requires a restart. You will have a chance to save open documents."; - -/* No comment provided by engineer. */ -"Starting…" = "Starting…"; - diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/MainMenu.strings deleted file mode 100644 index 0ae3b085..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/en_GB.lproj/MainMenu.strings +++ /dev/null @@ -1,90 +0,0 @@ - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Window"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimize"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Window"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "About MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Hide MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Quit MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Hide Others"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Show All"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copy"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Select All"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cut"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Delete"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Paste"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Centre"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "BorderlessWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/Localizable.strings deleted file mode 100644 index d281250c..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Comprobando actualizaciones del catálogo de Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Comprobando cambios adicionales..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Comprobando actualizaciones de Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Comprobando actualizaciones disponibles..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Determinando que archivos hay que quitar"; - -/* managedsoftwareupdate message */ -"Done." = "Hecho."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Descargando actualizaciones de Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Finalizando..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Recopilando información sobre los paquetes instalados"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Instalando actualizaciones de Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Nada que quitar."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Desinstalación del paquete completada."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Ejecutando los scripts de postinstalación..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Ejecutando los scripts de preinstalación..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Preparando la ejecución del instalador de macOS..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Quitando la información del recibo"; - -/* No comment provided by engineer. */ -"Restart" = "Reiniciar"; - -/* No comment provided by engineer. */ -"Restart Required" = "Es necesario reiniciar"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Obteniendo la lista de software para este equipo..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Instalación de un parche de Adobe en marcha"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Instalación de Adobe en marcha"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Desinstalación de Adobe en marcha"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "El software instalado o desinstalado requiere reiniciar el equipo."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "El software instalado necesita reiniciar el ordenador. Podrás guardar tus documentos"; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Empezando el instalador de Adobe..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Comenzando actualización de macOS..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Empezando..."; - -/* No comment provided by engineer. */ -"Starting…" = "Empezando…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "El sistema se reiniciará y comenzará la actualización de macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "El software se instaló correctamente."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Comprobando la integridad del paquete..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Esperando red..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/MainMenu.strings deleted file mode 100644 index f1af63ac..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/es.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Ayuda"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Ayuda"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Ayuda de Centro de aplicaciones"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Servicios"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Servicios"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Ocultar Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Salir de Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Ocultar otros"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Mostrar todos"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Ventana"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copiar"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Seleccionar todo"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cortar"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Mostrar registro"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Eliminar"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Pegar"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edición"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Deshacer"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Rehacer"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Editar"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimizar"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Ventana"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "Centro de aplicaciones"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "Centro de aplicaciones"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Acerca de Centro de aplicaciones"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Detener"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Cerrar"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/Localizable.strings deleted file mode 100644 index 6a10e05a..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Tarkistetaan Applen ohjelmistopäivitysten luetteloa..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Tarkistetaan muutoksia..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Tarkistetaan Applen ohjelmistopäivityksiä..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Tarkistetaan päivityksiä..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Määritetään poistettavia kohteita"; - -/* managedsoftwareupdate message */ -"Done." = "Valmis."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Haetaan Applen ohjelmistopäivityksiä..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Viimeistellään..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Kootaan tietoja asennetuista paketeista"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Asennetaan Applen ohjelmistopäivityksiä..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Ei poistettavia kohteita."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paketin poisto valmis."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Suoritetaan viimeisteleviä tehtäviä..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Suoritetaan valmistavia tehtäviä..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Valmistaudutaan käynnistämään macOS-asentaja..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Poistetaan kuitteja"; - -/* No comment provided by engineer. */ -"Restart" = "Käynnistä uudelleen"; - -/* No comment provided by engineer. */ -"Restart Required" = "Vaatii uudelleenkäynnistyksen"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Haetaan ohjelmistolistaa tälle tietokoneelle..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Ajetaan Adobe Patch -asentajaa"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Ajetaan Adobe Setup -ohjelmistoa"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Ajetaan Adobe Uninstall -ohjelmistoa"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Asennettu ohjelmisto tai poisto vaatii tietokoneen uudelleenkäynnistyksen."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Tietokone on käynnistettävä uudelleen kun asennus tai ohjelmiston poisto on suoritettu. Avoinna olevien dokumenttien tallentaminen on mahdollista."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Käynnistetään Adobe-asentajaa..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Aloitetaan macOS-päivitystä..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Aloitetaan..."; - -/* No comment provided by engineer. */ -"Starting…" = "Aloitetaan…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Tietokone käynnistyy uudelleen ja aloittaa macOS-päivityksen."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Ohjelmiston asennus onnistui."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Tarkistetaan paketin eheyttä..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Odotetaan verkkoyhteyttä..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/MainMenu.strings deleted file mode 100644 index 2bcc0e45..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fi.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Ohje"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Ohje"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus-ohje"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Palvelut"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Palvelut"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Kätke MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Lopeta MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Kätke muut"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Näytä kaikki"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Ikkuna"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopioi"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Valitse kaikki"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Leikkaa"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Näytä loki"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Poista"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Liitä"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Muokkaa"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Peru"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Tee sittenkin"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Muokkaa"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Pienennä"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoomaa"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Ikkuna"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Tietoja: MunkiStatus"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Pysäytä"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Sulje"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings deleted file mode 100644 index 581ce409..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "Centre de gestion des logiciels"; -"CFBundleDisplayName" = "Centre de gestion des logiciels"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/Localizable.strings deleted file mode 100644 index 260e2707..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Vérification des mises à jour Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Vérification des modifications supplémentaires..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Vérification des mises à jour Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Vérification des mises à jour disponibles..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Vérification des éléments à supprimer"; - -/* managedsoftwareupdate message */ -"Done." = "Terminé."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Téléchargement des mises à jour Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Finalisation..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Vérification de l'information sur les articles installés"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installation des mises à jour Apple disponibles..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Rien à supprimer."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paquet supprimé avec succès."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Exécution des tâches de finition..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Exécution des tâches d'initialisation..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Installation de macOS en cours de préparation..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Suppression de l'information du reçu"; - -/* No comment provided by engineer. */ -"Restart" = "Redémarrer"; - -/* No comment provided by engineer. */ -"Restart Required" = "Redémarrage requis"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Obtention de la liste des logiciels pour cet ordinateur..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Démarrage du programme de mise à niveau d'Adobe"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Démarrage du programme d'installation d'Adobe"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Désinstallation d'Adobe"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Un logiciel installé ou supprimé nécessite un redémarrage."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Les logiciels installés ou supprimés nécessitent un redémarrage. Vous aurez la possibilité de sauvegarder les documents ouverts."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Démarrage du programme d'installation d'Adobe..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "La mise à jour de macOS démarre..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Démarrage..."; - -/* No comment provided by engineer. */ -"Starting…" = "Démarrage…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Le système va redémarrer et commencer la mise à jour de macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Le logiciel a été installé avec succès."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Vérification de l'intégrité du paquet..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "En attente du réseau..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/MainMenu.strings deleted file mode 100644 index 9d0a35f2..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/fr.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Aide"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Aide"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Aide Mise à jour de logiciels"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Masquer Mise à jour de logiciels"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Quitter Mise à jour de logiciels"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Masquer les autres"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Tout afficher"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fenêtre"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Tout sélectionner"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Couper"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Afficher l’historique"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Centre de gestion des logiciels"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Supprimer"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Coller"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Édition"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Annuler"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Rétablir"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Modifier"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimiser"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fenêtre"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMenuPrincipal"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "Mise à jour de logiciels"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "Mise à jour de logiciels"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "À propos de Mise à jour de logiciels"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Arrêter"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "FenetreFond"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Fermer"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings deleted file mode 100644 index 06da609f..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "MunkiStatus"; -"CFBundleDisplayName" = "MunkiStatus"; -NSHumanReadableCopyright = "Copyright © 2010-2019 The Munki Project\nhttps://github.com/munki/munki"; \ No newline at end of file diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/Localizable.strings deleted file mode 100644 index b83610a0..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Controllo catalogo Aggiornamenti Software Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Controllo per ulteriori cambiamenti..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Controllo Aggiornamenti Software Apple disponibili..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Controllo aggiornamenti disponibili..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Determino elementi del filesystem da rimuovere"; - -/* managedsoftwareupdate message */ -"Done." = "Fatto."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Download Aggiornamenti Apple Disponibili in corso..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Conclusione..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Raccolta informazioni sui pacchetti installati in corso"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installazione Aggiornamenti Software Apple in corso..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Niente da Rimuovere."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Rimozione pacchetto riuscita."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Controllo post-installazione in corso..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Controllo pre-installazione in corso..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Preparazione all'avvio di macOS Installer..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Rimozione info in corso"; - -/* No comment provided by engineer. */ -"Restart" = "Riavvio"; - -/* No comment provided by engineer. */ -"Restart Required" = "Riavvio Necessario"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Recupero la lista delle applicazioni per questo mac..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobe Patch Installer in esecuzione"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobe Setup in esecuzione"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobe Uninstall in esecuzione"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "L'applicazione installata o rimossa richiede il riavvio."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "L'applicazione installata o rimossa richiede il riavvio. Avrai la possibilità di salvare i documenti aperti."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Avvio Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Inizio aggiornamento macOS..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Avvio..."; - -/* No comment provided by engineer. */ -"Starting…" = "Avvio…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Il sistema si riavvierà e inizierà l'aggiornamento di macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "L'applicazione è stata installata con successo."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Controllo l'integrità del pacchetto..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "In attesa del network..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/MainMenu.strings deleted file mode 100644 index a937525b..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/it.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Services"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Hide MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Quit MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Hide Others"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Show All"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Window"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Copy"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Select All"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Cut"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Mostra log"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Centro Gestione Applicazioni"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Delete"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Paste"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Edit"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimize"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Window"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "About MunkiStatus"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/InfoPlist.strings deleted file mode 100644 index ef0f0445..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* Localized versions of Info.plist keys */ - -"CFBundleName" = "MunkiStatus"; -"CFBundleDisplayName" = "MunkiStatus"; -NSHumanReadableCopyright = "Copyright © 2010-2015 The Munki Project\nhttps://github.com/munki/munki"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/Localizable.strings deleted file mode 100644 index 7ff1b230..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "アップルソフトウェアアップデートカタログを確認中…"; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "追加で変更すべき項目の有無を確認中…"; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "入手可能なアップルソフトウェアアップデートの有無を確認中…"; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "入手可能なアップデートの有無を確認中…"; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "削除対象となるファイル項目を特定しています"; - -/* managedsoftwareupdate message */ -"Done." = "完了"; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "入手可能なアップルソフトウェアアップデートをダウンロード中…"; - -/* managedsoftwareupdate message */ -"Finishing..." = "仕上げ中…"; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "インストールされたパッケージの情報を収集中"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "アップルソフトウェアアップデートをインストール中…"; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "削除対象項目はありません"; - -/* managedsoftwareupdate message */ -"Package removal complete." = "パッケージの削除が完了しました"; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "postflightタスクを実行中…"; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "preflightタスクを実行中…"; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "macOSインストーラーの実行を準備中..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Receipt情報を削除中です"; - -/* No comment provided by engineer. */ -"Restart" = "再起動"; - -/* No comment provided by engineer. */ -"Restart Required" = "再起動が必要です"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "この端末用のソフトウェアリストを取得中…"; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobeアップデートを実行中"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobeセットアップを実行中"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobeアンインストールを実行中"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "再起動を必要とするソフトウエアをインストールまたは削除しました"; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "再起動を必要とするソフトウエアをインストールまたは削除しました。現在使用中のドキュメントの保存ができます。"; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Adobeインストーラーを開始…"; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "macOSアップグレードの開始..."; - -/* managedsoftwareupdate message */ -"Starting..." = "開始…"; - -/* No comment provided by engineer. */ -"Starting…" = "開始…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "システムを再起動し、macOSアップグレードを開始します。"; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "ソフトウェアのインストールが完了しました"; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "パッケージの完全性を検証中…"; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "ネットーワークとの接続を待っています…"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/MainMenu.strings deleted file mode 100644 index e52ab79e..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ja.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "ヘルプ"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "ヘルプ"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus ヘルプ"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "サービス"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "サービス"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "MunkiStatusを隠す"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "MunkiStatusを終了"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "ほかを隠す"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "すべてを表示"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "ウィンドウ"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "コピー"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "すべてを選択"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "カット"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "ログを表示"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "削除"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "ペースト"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "編集"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Undo"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Redo"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "編集"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "しまう"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "ズーム"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "ウィンドウ"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "MunkiStatusについて"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "停止"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "閉じる"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.m b/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.m deleted file mode 100644 index f60fe0bc..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.m +++ /dev/null @@ -1,67 +0,0 @@ -// -// main.m -// MunkiStatus -// -// 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. -// - -#import - -#if __clang_major__ >= 9 -#import -#else -#import -#endif - -int main(int argc, char *argv[]) -{ - - NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - NSBundle *mainBundle = [NSBundle mainBundle]; - NSString *resourcePath = [mainBundle resourcePath]; - NSArray *pythonPathArray = [NSArray arrayWithObjects: resourcePath, [resourcePath stringByAppendingPathComponent:@"PyObjC"], nil]; - - setenv("PYTHONPATH", [[pythonPathArray componentsJoinedByString:@":"] UTF8String], 1); - - NSArray *possibleMainExtensions = [NSArray arrayWithObjects: @"py", @"pyc", @"pyo", nil]; - NSString *mainFilePath = nil; - - for (NSString *possibleMainExtension in possibleMainExtensions) { - mainFilePath = [mainBundle pathForResource: @"main" ofType: possibleMainExtension]; - if ( mainFilePath != nil ) break; - } - - if ( !mainFilePath ) { - [NSException raise: NSInternalInconsistencyException format: @"%s:%d main() Failed to find the Main.{py,pyc,pyo} file in the application wrapper's Resources directory.", __FILE__, __LINE__]; - } - - Py_SetProgramName("/usr/bin/python"); - Py_Initialize(); - PySys_SetArgv(argc, (char **)argv); - - const char *mainFilePathPtr = [mainFilePath UTF8String]; - FILE *mainFile = fopen(mainFilePathPtr, "r"); - int result = PyRun_SimpleFile(mainFile, (char *)[[mainFilePath lastPathComponent] UTF8String]); - - if ( result != 0 ) - [NSException raise: NSInternalInconsistencyException - format: @"%s:%d main() PyRun_SimpleFile failed with file '%@'. See console for errors.", __FILE__, __LINE__, mainFilePath]; - - [pool drain]; - - return result; - -} diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.py deleted file mode 100644 index 34050ee3..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/main.py +++ /dev/null @@ -1,35 +0,0 @@ -# -# main.py -# MunkiStatus -# -# 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. - -#import modules required by application -import objc -import Foundation -import AppKit - -from PyObjCTools import AppHelper - -# import modules containing classes required to start application and load MainMenu.nib -import MSUAppDelegate -import MSUStatusWindowController -import MSULogWindowController - -# get more debugging info on exceptions -objc.setVerbose(1) - -# pass control to AppKit -AppHelper.runEventLoop() diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/munki.py b/code/apps/pyobjc/MunkiStatus/MunkiStatus/munki.py deleted file mode 100644 index be19f9cc..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/munki.py +++ /dev/null @@ -1,126 +0,0 @@ -# encoding: utf-8 -# -# munki.py -# MunkiStatus -# -# Created by Greg Neagle on 2/11/10. -# Copyright 2010-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. - -'''munki-specific code for use with MunkiStatus''' - -import os -import stat -import subprocess - -from Foundation import CFPreferencesCopyAppValue -from SystemConfiguration import SCDynamicStoreCopyConsoleUser - -INSTALLATLOGOUTFILE = "/private/tmp/com.googlecode.munki.installatlogout" - -BUNDLE_ID = u'ManagedInstalls' - -def pref(pref_name): - """Return a preference. Since this uses CFPreferencesCopyAppValue, - Preferences can be defined several places. Precedence is: - - MCX - - ~/Library/Preferences/ManagedInstalls.plist - - /Library/Preferences/ManagedInstalls.plist - - default_prefs defined here. - """ - default_prefs = { - 'LogFile': '/Library/Managed Installs/Logs/ManagedSoftwareUpdate.log' - } - pref_value = CFPreferencesCopyAppValue(pref_name, BUNDLE_ID) - if pref_value == None: - pref_value = default_prefs.get(pref_name) - return pref_value - -def call(cmd): - '''Convenience function; works around an issue with subprocess.call - in PyObjC in Snow Leopard''' - proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (output, err) = proc.communicate() - return proc.returncode - - -def getconsoleuser(): - cfuser = SCDynamicStoreCopyConsoleUser( None, None, None ) - return cfuser[0] - - -def osascript(osastring): - """Wrapper to run AppleScript commands""" - cmd = ['/usr/bin/osascript', '-e', osastring] - proc = subprocess.Popen(cmd, shell=False, bufsize=1, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, err) = proc.communicate() - if proc.returncode != 0: - print >> sys.stderr, 'Error: ', err - if out: - return str(out).decode('UTF-8').rstrip('\n') - - -def restartNow(): - '''Trigger a restart''' - osascript('tell application "System Events" to restart') - - -def clearLaunchTrigger(): - '''Clear the trigger file that fast-launches us at loginwindow. - typically because we have been launched in statusmode at the - loginwindow to perform a logout-install.''' - try: - if os.path.exists(INSTALLATLOGOUTFILE): - os.unlink(INSTALLATLOGOUTFILE) - except (OSError, IOError): - return 1 - - -def pythonScriptRunning(scriptname): - """Returns Process ID for a running python script""" - cmd = ['/bin/ps', '-eo', 'pid=,command='] - proc = subprocess.Popen(cmd, shell=False, bufsize=1, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() - mypid = os.getpid() - lines = str(out).splitlines() - for line in lines: - try: - (pid, process) = line.split(None, 1) - except ValueError: - # funky process line, so we'll skip it - pass - else: - args = process.split() - try: - # first look for Python processes - if (args[0].find('MacOS/Python') != -1 or - args[0].find('python') != -1): - # look for first argument being scriptname - if args[1].find(scriptname) != -1: - try: - if int(pid) != int(mypid): - return pid - except ValueError: - # pid must have some funky characters - pass - except IndexError: - pass - # if we get here we didn't find a Python script with scriptname - # (other than ourselves) - return 0 \ No newline at end of file diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/Localizable.strings deleted file mode 100644 index b6c3d19a..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerer katalogen til Apple-programvareoppdatering..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Søker etter flere endringer..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Søker etter tilgjengelige Apple-programvareoppdateringer..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Søker etter tilgjengelige oppdateringer..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Undersøker hvilke elementer som kan fjernes"; - -/* managedsoftwareupdate message */ -"Done." = "Ferdig."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Henter tilgjengelige Apple-programvareoppdateringer..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Avslutter..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samler informasjon om installert programvare"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerer tilgjengelige Apple-programvareopdateringer..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Ingenting å fjerne."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Avinstallasjon av programvare er gjennomført."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Utfører postflight-oppgaver..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Utfører preflight-oppgaver..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Forbereder å kjøre macOS-installeringsprogrammet..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Fjerner opplysninger om kvittering"; - -/* No comment provided by engineer. */ -"Restart" = "Omstart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Krever omstart"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Henter en liste over tilgjenglig programvare for denne datamaskinen..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kjører Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kjører Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kjører Adobe Uninstall"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Programvaren som er installert eller fjernet krever en omstart."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Programvare som er installert eller fjernet krever en omstart. Du vil få anledning til å lagre alle åpne dokumenter."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Starter Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Starter macOS-oppgradering..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Starter..."; - -/* No comment provided by engineer. */ -"Starting…" = "Starter…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Systemet vil gjøre en omstart og begynne oppgraderingen av macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Installasjon av programvaren er fullført."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Verifiserer integriteten til programvarepakken..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Venter på nettverk..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/MainMenu.strings deleted file mode 100644 index 16ab54e9..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nb.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjelp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjelp"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Hjelp til MunkiStatus"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjenester"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Skjul MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Avslutt MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Skjul andre"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Vis alle"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Vindu"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopier"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Velg alt"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klipp ut"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Vis logg"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Slett"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Lim inn"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Angre"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Gjenta"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Rediger"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimer"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zoom"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Vindu"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Om MunkiStatus"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/Localizable.strings deleted file mode 100644 index 852d980d..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Apple Software-update catalogus checken..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Additionele wijzigingen checken..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Naar beschikbare Apple Software-updates zoeken..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Naar beschikbare Apple Software-updates zoeken..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Bepalen welke bestandssysteem onderdelen te verwijderen"; - -/* managedsoftwareupdate message */ -"Done." = "Klaar."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Beschikbare Apple Software-updates downloaden..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Afronden..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Informatie over geïnstalleerde pakketten verzamelen"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Beschikbare Apple Software-updates installeren..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Niets te verwijderen."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Verwijderen van pakket voltooid."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Postflight taken afhandelen..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Preflight taken afhandelen..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "MacOS installatieprogramma voorbereiden..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Receipt informatie verwijderen"; - -/* No comment provided by engineer. */ -"Restart" = "Herstarten"; - -/* No comment provided by engineer. */ -"Restart Required" = "Herstart Benodigd"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Software lijst voor deze machine ophalen..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Adobe Patch Installer uitvoeren"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Adobe Setup uitvoeren"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Adobe Uninstall uitvoeren"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Geïnstalleerde of verwijderde software maakt een herstart nodig."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Geïnstalleerde of verwijderde software maakt een herstart nodig. U krijgt de kans om geopende documenten op te slaan."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Adobe installer opstarten..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Beginnen met macOS upgrade..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Beginnen..."; - -/* No comment provided by engineer. */ -"Starting…" = "Beginnen…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Systeem zal herstarten en de upgrade van macOS starten."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "De software is succesvol geïnstalleerd."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Integriteit van het pakket controleren..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Op netwerk wachten..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/MainMenu.strings deleted file mode 100644 index 9223c760..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/nl.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Help"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Help"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "MunkiStatus Help"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Voorzieningen"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Voorzieningen"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Verberg MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Stop MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Verberg Andere"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Toon Alles"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Venster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopieer"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Alles Selecteren"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Knip"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Toon logbestand"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Verwijder"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Plak"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Bewerken"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Herstel"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Opnieuw"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Bewerken"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimaliseren"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Vergroot/verklein"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Venster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Over MunkiStatus"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Sluit"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/Localizable.strings deleted file mode 100644 index 7829185a..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Проверка каталога Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Проверка дополнительных изменений..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Проверка доступных Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Проверка доступных обновлений..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Определение элементов файловой системы для удаления"; - -/* managedsoftwareupdate message */ -"Done." = "Готово."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Загрузка доступных Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Завершение..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Сбор информации об установленных пакетах"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Установка доступных Обновлений ПО Apple..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Нечего удалять."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Удаление пакета завершено."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Выполнение послеполетных задач..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Выполнение предварительных задач..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Пoдготовка запуска Программы Установки macOS..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Удаление квитанции"; - -/* No comment provided by engineer. */ -"Restart" = "Перезагрузить"; - -/* No comment provided by engineer. */ -"Restart Required" = "Требуется перезагрузка"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Получение списка ПО для этой машины..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Выполнение установщика Adobe Patch"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Выполнение Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Выполнение Adobe Uninstall"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Установленное или удаленное ПО требует перезагрузки."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Установка/удаление программ требует перезагрузки. У Вас будет возможность сохранить открытые документы."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Запуск Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Запуск обновление для macOS..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Запуск..."; - -/* No comment provided by engineer. */ -"Starting…" = "Запуск…"; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Система перегрузится и начнет обновление macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Программное обеспечение было успешно установлено."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Проверка целостности пакета..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Ожидание сети..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/MainMenu.strings deleted file mode 100644 index 448beacb..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/ru.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Справка"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Справка"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Справка Управление обновлением ПО"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Службы"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Службы"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Скрыть Управлением обновлением ПО"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Завершить Управление обновлением ПО"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Скрыть остальные"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Показать все"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Окно"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Скопировать"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Выбрать все"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Вырезать"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Показать журнал"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Управление обновлением ПО"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Удалить"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Вставить"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Правка"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Отменить"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Повторить"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Правка"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Свернуть"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Изменить масштаб"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Окно"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AMainMenu"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "Управление обновлением ПО"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "Управление обновлением ПО"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Об Управением обновлением ПО"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stop"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Close"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/InfoPlist.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/InfoPlist.strings deleted file mode 100644 index a5d50307..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/InfoPlist.strings +++ /dev/null @@ -1,5 +0,0 @@ -/* No comment provided by engineer. */ -"CFBundleDisplayName" = "MunkiStatus"; - -/* Localized versions of Info.plist keys */ -"CFBundleName" = "MunkiStatus"; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/Localizable.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/Localizable.strings deleted file mode 100644 index 3c7aee0a..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/Localizable.strings +++ /dev/null @@ -1,95 +0,0 @@ -/* managedsoftwareupdate message */ -"Checking Apple Software Update catalog..." = "Kontrollerar Apples lista med programuppdateringar..."; - -/* managedsoftwareupdate message */ -"Checking for additional changes..." = "Kontrollerar ytterligare ändringar..."; - -/* managedsoftwareupdate message */ -"Checking for available Apple Software Updates..." = "Letar efter Apple-uppdateringar..."; - -/* managedsoftwareupdate message */ -"Checking for available updates..." = "Letar efter uppdateringar..."; - -/* managedsoftwareupdate message */ -"Determining which filesystem items to remove" = "Bestämmer vad som ska tas bort"; - -/* managedsoftwareupdate message */ -"Done." = "Klar."; - -/* managedsoftwareupdate message */ -"Downloading available Apple Software Updates..." = "Hämtar Apple-uppdateringar..."; - -/* managedsoftwareupdate message */ -"Finishing..." = "Avslutar..."; - -/* managedsoftwareupdate message */ -"Gathering information on installed packages" = "Samlar information om installerade paket"; - -/* managedsoftwareupdate message */ -"Installing available Apple Software Updates..." = "Installerar Apple-uppdateringar..."; - -/* managedsoftwareupdate message */ -"Nothing to remove." = "Inget att ta bort."; - -/* managedsoftwareupdate message */ -"Package removal complete." = "Paketet borttaget."; - -/* managedsoftwareupdate message */ -"Performing postflight tasks..." = "Rensar upp..."; - -/* managedsoftwareupdate message */ -"Performing preflight tasks..." = "Förbereder..."; - -/* managedsoftwareupdate message */ -"Preparing to run macOS Installer..." = "Förbereder macOS-installeraren..."; - -/* managedsoftwareupdate message */ -"Removing receipt info" = "Tar bort kvitto"; - -/* No comment provided by engineer. */ -"Restart" = "Omstart"; - -/* No comment provided by engineer. */ -"Restart Required" = "Omstart krävs"; - -/* managedsoftwareupdate message */ -"Retrieving list of software for this machine..." = "Hämtar lista med programvara för den här datorn..."; - -/* managedsoftwareupdate message */ -"Running Adobe Patch Installer" = "Kör Adobe Patch Installer"; - -/* managedsoftwareupdate message */ -"Running Adobe Setup" = "Kör Adobe Setup"; - -/* managedsoftwareupdate message */ -"Running Adobe Uninstall" = "Kör Adobe Uninstall"; - -/* managedsoftwareupdate message */ -"Software installed or removed requires a restart." = "Programvara som installerades eller togs bort kräver omstart."; - -/* No comment provided by engineer. */ -"Software installed or removed requires a restart. You will have a chance to save open documents." = "Program som installerades eller togs bort kräver en omstart. Du kommer att få möjlighet att spara ditt arbete."; - -/* managedsoftwareupdate message */ -"Starting Adobe installer..." = "Startar Adobe installer..."; - -/* managedsoftwareupdate message */ -"Starting macOS upgrade..." = "Påbörjar macOS-uppgradering..."; - -/* managedsoftwareupdate message */ -"Starting..." = "Startar..."; - -/* No comment provided by engineer. */ -"Starting…" = "Startar..."; - -/* managedsoftwareupdate message */ -"System will restart and begin upgrade of macOS." = "Datorn kommer att starta om och börja uppgradera macOS."; - -/* managedsoftwareupdate message */ -"The software was successfully installed." = "Programvaran installerades."; - -/* managedsoftwareupdate message */ -"Verifying package integrity..." = "Verifierar paket..."; - -/* managedsoftwareupdate message */ -"Waiting for network..." = "Väntar på nätverk..."; diff --git a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/MainMenu.strings b/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/MainMenu.strings deleted file mode 100644 index 8e6ead88..00000000 --- a/code/apps/pyobjc/MunkiStatus/MunkiStatus/sv.lproj/MainMenu.strings +++ /dev/null @@ -1,101 +0,0 @@ -/* Class = "NSMenuItem"; title = "Help"; ObjectID = "103"; */ -"103.title" = "Hjälp"; - -/* Class = "NSMenu"; title = "Help"; ObjectID = "106"; */ -"106.title" = "Hjälp"; - -/* Class = "NSMenuItem"; title = "MunkiStatus Help"; ObjectID = "111"; */ -"111.title" = "Hjälp för MunkiStatus"; - -/* Class = "NSMenu"; title = "Services"; ObjectID = "130"; */ -"130.title" = "Tjänster"; - -/* Class = "NSMenuItem"; title = "Services"; ObjectID = "131"; */ -"131.title" = "Tjänster"; - -/* Class = "NSMenuItem"; title = "Hide MunkiStatus"; ObjectID = "134"; */ -"134.title" = "Göm MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Quit MunkiStatus"; ObjectID = "136"; */ -"136.title" = "Avsluta MunkiStatus"; - -/* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "145"; */ -"145.title" = "Göm övriga"; - -/* Class = "NSMenuItem"; title = "Show All"; ObjectID = "150"; */ -"150.title" = "Visa alla"; - -/* Class = "NSMenuItem"; title = "Window"; ObjectID = "19"; */ -"19.title" = "Fönster"; - -/* Class = "NSMenuItem"; title = "Copy"; ObjectID = "197"; */ -"197.title" = "Kopiera"; - -/* Class = "NSMenuItem"; title = "Select All"; ObjectID = "198"; */ -"198.title" = "Markera alla"; - -/* Class = "NSMenuItem"; title = "Cut"; ObjectID = "199"; */ -"199.title" = "Klipp ut"; - -/* Class = "NSMenuItem"; title = "Show Log"; ObjectID = "1B8-Pq-rf7"; */ -"1B8-Pq-rf7.title" = "Visa logg"; - -/* Class = "NSWindow"; title = "Managed Software Center"; ObjectID = "1Nd-E7-vfR"; */ -"1Nd-E7-vfR.title" = "Managed Software Center"; - -/* Class = "NSMenuItem"; title = "Delete"; ObjectID = "202"; */ -"202.title" = "Radera"; - -/* Class = "NSMenuItem"; title = "Paste"; ObjectID = "203"; */ -"203.title" = "Klistra in"; - -/* Class = "NSMenu"; title = "Edit"; ObjectID = "205"; */ -"205.title" = "Redigera"; - -/* Class = "NSMenuItem"; title = "Undo"; ObjectID = "207"; */ -"207.title" = "Ångra"; - -/* Class = "NSMenuItem"; title = "Redo"; ObjectID = "215"; */ -"215.title" = "Ångra"; - -/* Class = "NSMenuItem"; title = "Edit"; ObjectID = "217"; */ -"217.title" = "Redigera"; - -/* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "23"; */ -"23.title" = "Minimera"; - -/* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "239"; */ -"239.title" = "Zooma"; - -/* Class = "NSMenu"; title = "Window"; ObjectID = "24"; */ -"24.title" = "Fönster"; - -/* Class = "NSMenu"; title = "AMainMenu"; ObjectID = "29"; */ -"29.title" = "AHuvudmeny"; - -/* Class = "NSMenuItem"; title = "MunkiStatus"; ObjectID = "56"; */ -"56.title" = "MunkiStatus"; - -/* Class = "NSMenu"; title = "MunkiStatus"; ObjectID = "57"; */ -"57.title" = "MunkiStatus"; - -/* Class = "NSMenuItem"; title = "About MunkiStatus"; ObjectID = "58"; */ -"58.title" = "Om MunkiStatus"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "8Nl-My-27d"; */ -"8Nl-My-27d.title" = "Text Cell"; - -/* Class = "NSWindow"; title = "Log"; ObjectID = "LgH-P6-zEw"; */ -"LgH-P6-zEw.title" = "Log"; - -/* Class = "NSButtonCell"; title = "Stop"; ObjectID = "M20-JG-Pmr"; */ -"M20-JG-Pmr.title" = "Stopp"; - -/* Class = "NSWindow"; title = "BackdropWindow"; ObjectID = "VCy-58-vwp"; */ -"VCy-58-vwp.title" = "BackdropWindow"; - -/* Class = "NSMenuItem"; title = "Close"; ObjectID = "bd5-IY-BXv"; */ -"bd5-IY-BXv.title" = "Stäng"; - -/* Class = "NSTextFieldCell"; title = "Text Cell"; ObjectID = "nGI-3K-7Wv"; */ -"nGI-3K-7Wv.title" = "Text Cell"; diff --git a/code/client/app_usage_monitor b/code/client/app_usage_monitor index b26b94a9..b1a344d6 100755 --- a/code/client/app_usage_monitor +++ b/code/client/app_usage_monitor @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -26,11 +26,11 @@ Borrowing lots of code and ideas from the crankd project, part of pymacadmin: and the application_usage scripts created by Google MacOps: https://github.com/google/macops/tree/master/crankd """ +from __future__ import absolute_import # standard Python libs import logging import os -import plistlib import select import socket import sys @@ -47,6 +47,9 @@ except ImportError: logging.critical("PyObjC wrappers for Apple frameworks are missing.") sys.exit(-1) +# our libs +from munkilib.wrappers import writePlistToString + APPUSAGED_SOCKET = "/var/run/appusaged" @@ -59,7 +62,7 @@ class AppUsageClientError(Exception): class AppUsageClient(object): '''Handles communication with auppusaged daemon''' def connect(self): - '''Connect to authrestartd''' + '''Connect to appusaged''' try: #pylint: disable=attribute-defined-outside-init self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) @@ -71,19 +74,17 @@ class AppUsageClient(object): def send_request(self, request): '''Send a request to appusaged''' - self.socket.send(plistlib.writePlistToString(request)) - with os.fdopen(self.socket.fileno()) as fileref: - # use select so we don't hang indefinitely if appusaged dies - ready = select.select([fileref], [], [], 2) - if ready[0]: - reply = fileref.read() - else: - reply = '' + self.socket.send(writePlistToString(request)) + # use select so we don't hang indefinitely if appusaged dies + ready = select.select([self.socket.fileno()], [], [], 2) + if ready[0]: + reply = self.socket.recv(8192).decode("UTF-8") + else: + reply = '' if reply: return reply.rstrip() - else: - return "ERROR:No reply" + return "ERROR:No reply" def disconnect(self): '''Disconnect from appusaged''' @@ -138,7 +139,7 @@ class NotificationHandler(NSObject): self.ws_nc.removeObserver_(self) self.dnc.removeObserver_(self) - def get_app_dict(self, app_object): + def getInfoDictForApp_(self, app_object): """Returns a dict with info about an application. Args: app_object: NSRunningApplication object @@ -176,26 +177,31 @@ class NotificationHandler(NSObject): def didLaunchApplicationNotification_(self, notification): """Handle NSWorkspaceDidLaunchApplicationNotification""" app_object = notification.userInfo().get('NSWorkspaceApplicationKey') - app_dict = self.get_app_dict(app_object) + app_dict = self.getInfoDictForApp_(app_object) self.usage.process({'event': 'launch', 'app_dict': app_dict}) def didActivateApplicationNotification_(self, notification): """Handle NSWorkspaceDidActivateApplicationNotification""" app_object = notification.userInfo().get('NSWorkspaceApplicationKey') - app_dict = self.get_app_dict(app_object) + app_dict = self.getInfoDictForApp_(app_object) self.usage.process({'event': 'activate', 'app_dict': app_dict}) def didTerminateApplicationNotification_(self, notification): """Handle NSWorkspaceDidTerminateApplicationNotification""" app_object = notification.userInfo().get('NSWorkspaceApplicationKey') - app_dict = self.get_app_dict(app_object) + app_dict = self.getInfoDictForApp_(app_object) self.usage.process({'event': 'quit', 'app_dict': app_dict}) def requestedItemForInstall_(self, notification): """Handle com.googlecode.munki.managedsoftwareupdate.installrequest""" logging.info('got install request notification') user_info = notification.userInfo() - self.usage.process(user_info) + self.usage.process( + {'event': user_info.get('event', 'unknown'), + 'name': user_info.get('name', 'unknown'), + 'version': user_info.get('version', 'unknown') + } + ) def main(): @@ -205,7 +211,9 @@ def main(): # PyLint can't tell that NotificationHandler' NSObject superclass # has an alloc() method # pylint: disable=no-member + # pylint: disable=unused-variable notification_handler = NotificationHandler.alloc().init() + # pylint: enable=unused-variable # pylint: enable=no-member while True: diff --git a/code/client/appusaged b/code/client/appusaged index 441e27ea..9e281d98 100755 --- a/code/client/appusaged +++ b/code/client/appusaged @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2018-2019 Greg Neagle. +# Copyright 2018-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ Much code based on and adapted from autopkgserver by Per Olofsson A privileged daemon that records app usage events and install requests to our app usage database. """ +from __future__ import absolute_import, print_function import os import sys @@ -31,14 +32,18 @@ import time import stat import logging import logging.handlers -import SocketServer +try: + import SocketServer +except ImportError: + import socketserver as SocketServer import socket -import plistlib import struct from munkilib import app_usage from munkilib import launchd +from munkilib import munkilog from munkilib import prefs +from munkilib.wrappers import readPlistFromString, PlistError, unicode_or_str APPNAME = 'appusaged' @@ -89,6 +94,7 @@ class AppUsageHandler(object): self.log.info('%s', self.request) self.server.usage.log_application_usage( self.request['event'], self.request['app_dict']) + return u"" class RunHandler(SocketServer.StreamRequestHandler): @@ -135,7 +141,7 @@ class RunHandler(SocketServer.StreamRequestHandler): cr_ngroups = 2 cr_groups = 3 - xucred_fmt = 'IIh%dI' % NGROUPS + xucred_fmt = b'IIh%dI' % NGROUPS res = struct.unpack( xucred_fmt, self.request.getsockopt( @@ -165,10 +171,10 @@ class RunHandler(SocketServer.StreamRequestHandler): # Try to parse it. try: - plist = plistlib.readPlistFromString(plist_string) - except BaseException as err: + plist = readPlistFromString(plist_string) + except PlistError as err: self.log.error('Malformed request') - self.request.send('ERROR:Malformed request\n') + self.request.send(u'ERROR:Malformed request\n'.encode('UTF-8')) return self.log.debug('Parsed request plist') @@ -176,7 +182,9 @@ class RunHandler(SocketServer.StreamRequestHandler): syntax_ok, errors = self.verify_request_syntax(plist) if not syntax_ok: self.log.error('Plist syntax error') - self.request.send(''.join(['ERROR:%s\n' % e for e in errors])) + msg = (u''.join( + [u'ERROR:%s\n' % e for e in errors])).encode('UTF-8') + self.request.send(msg) return self.log.debug( @@ -184,16 +192,20 @@ class RunHandler(SocketServer.StreamRequestHandler): try: appusagehandler = AppUsageHandler(self.server, plist, uid) result = appusagehandler.handle() - self.request.send(u'OK:%s\n' % result) + self.request.send( + (u'OK:%s\n' % unicode_or_str(result)).encode('UTF-8')) except AppUsageHandlerError as err: - self.request.send(u'ERROR:%s\n' % unicode(err)) + self.request.send( + (u'ERROR:%s\n' % unicode_or_str(err)).encode('UTF-8')) except BaseException as err: - self.log.error('Run failed: %s' % unicode(err)) - self.request.send(u'ERROR:%s\n' % unicode(err)) + self.log.error(u'Run failed: %s' % unicode_or_str(err)) + self.request.send( + (u'ERROR:%s\n' % unicode_or_str(err)).encode('UTF-8')) except BaseException as err: - self.log.error('Caught exception: %s' % repr(err)) - self.request.send('ERROR:Caught exception: %s' % repr(err)) + self.log.error(u'Caught exception: %s' % repr(err)) + self.request.send( + (u'ERROR:Caught exception: %s' % repr(err)).encode('UTF-8')) return class AppUsageDaemonError(Exception): @@ -227,7 +239,7 @@ class AppUsageDaemon(SocketServer.UnixStreamServer): '''Configure logging''' try: logging_level = logging.INFO - if prefs.pref('LoggingLevel') > 1: + if munkilog.logging_level() > 1: logging_level = logging.DEBUG self.log.setLevel(logging_level) @@ -262,7 +274,7 @@ def main(): '''Start our daemon, connect to socket and process events''' # Make sure we're launched as root if os.geteuid() != 0: - print >> sys.stderr, '%s must be run as root.' % APPNAME + print('%s must be run as root.' % APPNAME, file=sys.stderr) # Sleep to avoid respawn. time.sleep(10) return 1 @@ -278,13 +290,14 @@ def main(): while True: info = os.stat(exepath) if info.st_uid != root_uid: - print >> sys.stderr, '%s must be owned by root.' % exepath + print('%s must be owned by root.' % exepath, file=sys.stderr) path_ok = False if info.st_gid not in (wheel_gid, admin_gid): - print >> sys.stderr, '%s must have group wheel or admin.' % exepath + print('%s must have group wheel or admin.' % exepath, + file=sys.stderr) path_ok = False if info.st_mode & stat.S_IWOTH: - print >> sys.stderr, '%s mustn\'t be world writeable.' % exepath + print('%s mustn\'t be world writeable.' % exepath, file=sys.stderr) path_ok = False exepath = os.path.dirname(exepath) if exepath == '/': @@ -299,9 +312,9 @@ def main(): start_time = time.time() # Get socket file descriptors from launchd. - socket_fd = launchd.get_socket_fd(APPNAME) + socket_fd = launchd.get_socket_fd(APPNAME.encode('UTF-8')) if not socket_fd: - print >> sys.stderr, 'No socket provided to us by launchd' + print('No socket provided to us by launchd', file=sys.stderr) time.sleep(10) return 1 diff --git a/code/client/authrestartd b/code/client/authrestartd index 030f9e3c..af8981f3 100755 --- a/code/client/authrestartd +++ b/code/client/authrestartd @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ Root user can trigger an authrestart, possibly using the password stored earlier by a non-privileged user. """ +from __future__ import absolute_import, print_function import os import sys @@ -34,14 +35,17 @@ import time import stat import logging import logging.handlers -import SocketServer +try: + import SocketServer +except ImportError: + import socketserver as SocketServer import socket -import plistlib import struct from munkilib import authrestart from munkilib import launchd from munkilib import prefs +from munkilib.wrappers import readPlistFromString, PlistError, unicode_or_str APPNAME = 'authrestartd' @@ -212,7 +216,7 @@ class RunHandler(SocketServer.StreamRequestHandler): cr_ngroups = 2 cr_groups = 3 - xucred_fmt = 'IIh%dI' % NGROUPS + xucred_fmt = b'IIh%dI' % NGROUPS res = struct.unpack( xucred_fmt, self.request.getsockopt( @@ -242,10 +246,10 @@ class RunHandler(SocketServer.StreamRequestHandler): # Try to parse it. try: - plist = plistlib.readPlistFromString(plist_string) - except BaseException as err: + plist = readPlistFromString(plist_string) + except PlistError as err: self.log.error('Malformed request') - self.request.send('ERROR:Malformed request\n') + self.request.send(u'ERROR:Malformed request\n'.encode('UTF-8')) return self.log.debug('Parsed request plist') @@ -253,7 +257,9 @@ class RunHandler(SocketServer.StreamRequestHandler): syntax_ok, errors = self.verify_request_syntax(plist) if not syntax_ok: self.log.error('Plist syntax error') - self.request.send(''.join(['ERROR:%s\n' % e for e in errors])) + msg = (''.join( + ['ERROR:%s\n' % e for e in errors])).encode('UTF-8') + self.request.send(msg) return self.log.info( @@ -261,17 +267,22 @@ class RunHandler(SocketServer.StreamRequestHandler): try: fdeutil = FDEUtil(self.server, plist, uid) result = fdeutil.handle() - self.request.send(u'OK:%s\n' % result) + self.request.send( + (u'OK:%s\n' % unicode_or_str(result)).encode('UTF-8')) except FDEUtilError as err: - self.request.send(u'ERROR:%s\n' % unicode(err)) - except BaseException as err: - self.log.error('Run failed: %s' % unicode(err)) - self.request.send(u'ERROR:%s\n' % unicode(err)) + self.request.send( + (u'ERROR:%s\n' % unicode_or_str(err)).encode('UTF-8')) + except Exception as err: + raise + #self.log.error(u'Run failed: %s' % unicode_or_str(err)) + #self.request.send( + # (u'ERROR:%s\n' % unicode_or_str(err)).encode('UTF-8')) - except BaseException as err: + except Exception as err: self.log.error('Caught exception: %s' % repr(err)) - self.request.send('ERROR:Caught exception: %s' % repr(err)) - return + self.request.send( + (u'ERROR:Caught exception: %s' % repr(err)).encode('UTF-8')) + raise class RestartHelperDaemonError(Exception): @@ -290,9 +301,12 @@ class RestartHelperDaemon(SocketServer.UnixStreamServer): def __init__(self, socket_fd, RequestHandlerClass): # Avoid initialization of UnixStreamServer as we need to open the # socket from a file descriptor instead of creating our own. + # pylint: disable=super-init-not-called self.socket = socket.fromfd( socket_fd, socket.AF_UNIX, socket.SOCK_STREAM) self.socket.listen(self.request_queue_size) + # now do the base class's init + # pylint: disable=non-parent-init-called SocketServer.BaseServer.__init__( self, self.socket.getsockname(), RequestHandlerClass) self.timed_out = False @@ -336,7 +350,7 @@ def main(): '''Start our daemon, connect to socket and process requests''' # Make sure we're launched as root if os.geteuid() != 0: - print >>sys.stderr, '%s must be run as root.' % APPNAME + print('%s must be run as root.' % APPNAME, file=sys.stderr) # Sleep to avoid respawn. time.sleep(10) return 1 @@ -352,13 +366,14 @@ def main(): while True: info = os.stat(exepath) if info.st_uid != root_uid: - print >> sys.stderr, '%s must be owned by root.' % exepath + print('%s must be owned by root.' % exepath, file=sys.stderr) path_ok = False if info.st_gid not in (wheel_gid, admin_gid): - print >> sys.stderr, '%s must have group wheel or admin.' % exepath + print('%s must have group wheel or admin.' % exepath, + file=sys.stderr) path_ok = False if info.st_mode & stat.S_IWOTH: - print >> sys.stderr, '%s mustn\'t be world writeable.' % exepath + print('%s mustn\'t be world writeable.' % exepath, file=sys.stderr) path_ok = False exepath = os.path.dirname(exepath) if exepath == '/': @@ -373,9 +388,9 @@ def main(): start_time = time.time() # Get socket file descriptors from launchd. - socket_fd = launchd.get_socket_fd('authrestartd') + socket_fd = launchd.get_socket_fd('authrestartd'.encode("UTF-8")) if not socket_fd: - print >> sys.stderr, 'No socket provided to us by launchd' + print('No socket provided to us by launchd', file=sys.stderr) time.sleep(10) return 1 diff --git a/code/client/iconimporter b/code/client/iconimporter index aff3835b..695f544c 100755 --- a/code/client/iconimporter +++ b/code/client/iconimporter @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2010-2019 Greg Neagle. +# Copyright 2010-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -22,14 +22,17 @@ Created by Greg Neagle on 2014-03-03. Converts and imports icons as png files for Munki repo """ +from __future__ import absolute_import, print_function + # standard libs import os from optparse import OptionParser +import sys # our libs from munkilib.cliutils import TempFile from munkilib.cliutils import pref, path2url -from munkilib.cliutils import print_utf8, print_err_utf8 + from munkilib import dmgutils from munkilib import iconutils @@ -38,7 +41,9 @@ from munkilib import osinstaller from munkilib import osutils from munkilib import pkgutils -from munkilib import FoundationPlist +from munkilib.FoundationPlist import (readPlistFromString, + FoundationPlistException) +from munkilib.wrappers import unicode_or_str def copy_icon_to_repo(repo, name, path): @@ -46,9 +51,10 @@ def copy_icon_to_repo(repo, name, path): icon_ref = os.path.join(u'icons', name + u'.png') try: repo.put_from_local_file(icon_ref, path) - print_utf8(u'\tWrote: %s' % icon_ref) - except munkirepo.RepoError, err: - print_err_utf8(u'\tError uploading %s: %s' % (icon_ref, unicode(err))) + print(u'\tWrote: %s' % icon_ref) + except munkirepo.RepoError as err: + print(u'\tError uploading %s: %s' % (icon_ref, unicode_or_str(err)), + file=sys.stderr) def generate_png_from_startosinstall_item(repo, install_item): @@ -61,9 +67,12 @@ def generate_png_from_startosinstall_item(repo, install_item): dmg_temp = TempFile() try: repo.get_to_local_file(dmg_ref, dmg_temp.path) - except munkirepo.RepoError, err: - print_err_utf8(u'\tCan\'t download %s from repo: %s' - % (dmg_ref, unicode(err))) + except munkirepo.RepoError as err: + print( + u'\tCan\'t download %s from repo: %s' + % (dmg_ref, unicode_or_str(err)), + file=sys.stderr + ) return mountpoints = dmgutils.mountdmg(dmg_temp.path) if mountpoints: @@ -79,11 +88,12 @@ def generate_png_from_startosinstall_item(repo, install_item): copy_icon_to_repo( repo, install_item['name'], icon_temp.path) else: - print_err_utf8(u'\tError converting %s to png.' % icon_path) + print(u'\tError converting %s to png.' % icon_path, + file=sys.stderr) else: - print_utf8(u'\tNo application icons found.') + print(u'\tNo application icons found.') else: - print_utf8(u'\tNo application icons found.') + print(u'\tNo application icons found.') dmgutils.unmountdmg(mountpoint) @@ -97,16 +107,16 @@ def generate_png_from_dmg_item(repo, install_item): dmg_temp = TempFile() try: repo.get_to_local_file(dmg_ref, dmg_temp.path) - except munkirepo.RepoError, err: - print_err_utf8(u'\tCan\'t download %s from repo: %s' - % (dmg_ref, unicode(err))) + except munkirepo.RepoError as err: + print(u'\tCan\'t download %s from repo: %s' + % (dmg_ref, unicode_or_str(err)), file=sys.stderr) return mountpoints = dmgutils.mountdmg(dmg_temp.path) if mountpoints: mountpoint = mountpoints[0] apps = [item for item in install_item.get('items_to_copy', []) if item.get('source_item', '').endswith('.app')] - if len(apps): + if apps: app_path = os.path.join(mountpoint, apps[0]['source_item']) icon_path = iconutils.findIconForApp(app_path) if icon_path: @@ -117,11 +127,12 @@ def generate_png_from_dmg_item(repo, install_item): copy_icon_to_repo( repo, install_item['name'], icon_temp.path) else: - print_err_utf8(u'\tError converting %s to png.' % icon_path) + print(u'\tError converting %s to png.' % icon_path, + file=sys.stderr) else: - print_utf8(u'\tNo application icons found.') + print(u'\tNo application icons found.') else: - print_utf8(u'\tNo application icons found.') + print(u'\tNo application icons found.') dmgutils.unmountdmg(mountpoint) @@ -134,9 +145,9 @@ def generate_pngs_from_pkg(repo, install_item): file_temp = TempFile() try: repo.get_to_local_file(item_path, file_temp.path) - except munkirepo.RepoError, err: - print_err_utf8(u'\tCan\'t download %s from repo: %s' - % (item_path, unicode(err))) + except munkirepo.RepoError as err: + print(u'\tCan\'t download %s from repo: %s' + % (item_path, unicode_or_str(err)), file=sys.stderr) return if pkgutils.hasValidDiskImageExt(item_path): mountpoints = dmgutils.mountdmg(file_temp.path) @@ -179,7 +190,7 @@ def generate_pngs_from_pkg(repo, install_item): copy_icon_to_repo(repo, icon_name, icon_temp.path) index += 1 else: - print_utf8(u'\tNo application icons found.') + print(u'\tNo application icons found.') def find_items_to_check(repo, itemlist=None): @@ -188,10 +199,10 @@ def find_items_to_check(repo, itemlist=None): only on that list.''' try: all_catalog_data = repo.get('catalogs/all') - catalogitems = FoundationPlist.readPlistFromString(all_catalog_data) - except (munkirepo.RepoError, FoundationPlist.FoundationPlistException), err: - print_err_utf8( - 'Error getting catalog data from repo: %s' % unicode(err)) + catalogitems = readPlistFromString(all_catalog_data) + except (munkirepo.RepoError, FoundationPlistException) as err: + print('Error getting catalog data from repo: %s' % unicode_or_str(err), + file=sys.stderr) return [] itemdb = {} @@ -218,12 +229,12 @@ def generate_pngs_from_munki_items(repo, force=False, itemlist=None): except munkirepo.RepoError: icons_list = [] for item in itemlist: - print_utf8(u'Processing %s...' % item['name']) + print(u'Processing %s...' % item['name']) icon_name = item.get('icon_name') or item['name'] if not os.path.splitext(icon_name)[1]: icon_name += u'.png' if icon_name in icons_list and not force: - print_utf8(u'Found existing icon at %s' % icon_name) + print(u'Found existing icon at %s' % icon_name) continue installer_type = item.get('installer_type') if installer_type == 'copy_from_dmg': @@ -233,7 +244,7 @@ def generate_pngs_from_munki_items(repo, force=False, itemlist=None): elif installer_type in [None, '']: generate_pngs_from_pkg(repo, item) else: - print_utf8(u'\tCan\'t process installer_type: %s' % installer_type) + print(u'\tCan\'t process installer_type: %s' % installer_type) def main(): @@ -254,7 +265,7 @@ def main(): options, arguments = parser.parse_args() # Make sure we have a path to work with - if len(arguments): + if arguments: repo_path = arguments[0].rstrip("/") else: repo_path = pref('repo_path') @@ -267,8 +278,9 @@ def main(): # Make sure the repo exists try: repo = munkirepo.connect(options.repo_url, options.plugin) - except munkirepo.RepoError, err: - print_err_utf8(u'Could not connect to munki repo: %s' % unicode(err)) + except munkirepo.RepoError as err: + print(u'Could not connect to munki repo: %s' % unicode_or_str(err), + file=sys.stderr) exit(-1) # generate icons! diff --git a/code/client/launchapp b/code/client/launchapp index 76188456..eb93118c 100755 --- a/code/client/launchapp +++ b/code/client/launchapp @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2010-2019 Greg Neagle. +# Copyright 2010-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ Prevents multiple copies of the app from being launched when Fast User Switching is in use Intended for use by a launchd LaunchAgent. """ +from __future__ import absolute_import, print_function import sys import os @@ -62,7 +63,7 @@ def main(): if len(sys.argv) > 1: cmd.extend(sys.argv[1:]) else: - print >> sys.stderr, "Must specify an app to launch!" + print("Must specify an app to launch!", file=sys.stderr) exit(-1) retcode = subprocess.call(cmd) # sleep 10 secs to make launchd happy diff --git a/code/client/logouthelper b/code/client/logouthelper index 4cd7c2d4..d879d51b 100755 --- a/code/client/logouthelper +++ b/code/client/logouthelper @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2011-2019 Greg Neagle. +# Copyright 2011-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ A helper tool for forced logouts to allow munki to force install items by a certain deadline. """ +from __future__ import absolute_import # standard libs import os diff --git a/code/client/makecatalogs b/code/client/makecatalogs index f7f0c249..38f9ee53 100755 --- a/code/client/makecatalogs +++ b/code/client/makecatalogs @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,14 +26,24 @@ Assumes a pkgsinfo directory under repopath. User calling this needs to be able to write to repo/catalogs. """ +from __future__ import absolute_import, print_function import optparse import sys from munkilib.admin import makecatalogslib -from munkilib.cliutils import pref, path2url, print_utf8, print_err_utf8 +from munkilib.cliutils import pref, path2url from munkilib.cliutils import get_version from munkilib import munkirepo +from munkilib.wrappers import unicode_or_str + +# define a unicode function for Python 3 +try: + # Python 2 + _ = unicode # pylint: disable=unicode-builtin +except NameError: + # Python 3 + unicode = str def main(): @@ -44,7 +54,8 @@ def main(): help='Print the version of the munki tools and exit.') parser.add_option('--force', '-f', action='store_true', dest='force', help='Disable sanity checks.') - parser.add_option('--skip-pkg-check', '-s', action='store_true', dest='skip_payload_check', + parser.add_option('--skip-pkg-check', '-s', action='store_true', + dest='skip_payload_check', help='Skip checking of pkg existence. Useful ' 'when pkgs aren\'t on the same server ' 'as pkginfo, catalogs and manifests.') @@ -58,7 +69,7 @@ def main(): options, arguments = parser.parse_args() if options.version: - print get_version() + print(get_version()) exit(0) # backwards compatibility @@ -78,19 +89,19 @@ def main(): # Connect to the repo try: repo = munkirepo.connect(options.repo_url, options.plugin) - except munkirepo.RepoError, err: - print >> sys.stderr, (u'Could not connect to munki repo: %s' - % unicode(err)) + except munkirepo.RepoError as err: + print(u'Could not connect to munki repo: %s' % unicode_or_str(err), + file=sys.stderr) exit(-1) # Make the catalogs - errors = makecatalogslib.makecatalogs(repo, options, output_fn=print_utf8) + errors = makecatalogslib.makecatalogs(repo, options, output_fn=print) if errors: # group all errors at the end for better visibility - print + print() for error in errors: - print_err_utf8(error) + print(error, file=sys.stderr) exit(-1) diff --git a/code/client/makepkginfo b/code/client/makepkginfo index 6ae97cad..6eca440b 100755 --- a/code/client/makepkginfo +++ b/code/client/makepkginfo @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2008-2019 Greg Neagle. +# Copyright 2008-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ The generated plist is printed to STDOUT. Usage: makepkginfo /path/to/package_or_dmg [-f /path/to/item/it/installs ...] """ +from __future__ import absolute_import, print_function # standard libs import optparse @@ -69,14 +70,14 @@ def main(): # add all our option groups pkginfolib.add_option_groups(parser) - sys.argv = [unicode(item, 'utf-8') for item in sys.argv] + #sys.argv = [unicode(item, 'utf-8') for item in sys.argv] options, arguments = parser.parse_args() if options.version: - print info.get_version() + print(info.get_version()) exit(0) - if (len(arguments) == 0 and + if (not arguments and not options.file and not options.nopkg and not options.installer_environment and @@ -93,14 +94,14 @@ def main(): if (options.minimum_os_version and not options.minimum_os_version[0].isdigit()): - print >> sys.stderr, ( - 'Minimum OS Version must start with a number, e.g. 10.7.2.') + print('Minimum OS Version must start with a number, e.g. 10.7.2.', + file=sys.stderr) exit(-1) if len(arguments) > 1: - print >> sys.stderr, 'Can process only one installer item at a time.' - print >> sys.stderr, 'Ignoring additional installer items:' - print >> sys.stderr, '\t', '\n\t'.join(arguments[1:]) + print('Can process only one installer item at a time.', file=sys.stderr) + print('Ignoring additional installer items:', file=sys.stderr) + print('\t', '\n\t'.join(arguments[1:]), file=sys.stderr) os_version = osutils.getOsVersion( only_major_minor=False, as_tuple=True) @@ -114,8 +115,8 @@ def main(): installeritem = None try: pkginfo = pkginfolib.makepkginfo(installeritem, options) - except pkginfolib.PkgInfoGenerationError, err: - print >> sys.stderr, err + except pkginfolib.PkgInfoGenerationError as err: + print(err, file=sys.stderr) exit(-1) if (len(arguments) == 1 and @@ -132,10 +133,10 @@ def main(): " This can result in repeated installation attempts or failure " "to attempt\n" " installation at all.") - print >> sys.stderr, msg % item + print(msg % item, file=sys.stderr) # and now, what we've all been waiting for... - print FoundationPlist.writePlistToString(pkginfo) + print(FoundationPlist.writePlistToString(pkginfo).decode('UTF-8')) if __name__ == '__main__': diff --git a/code/client/managedsoftwareupdate b/code/client/managedsoftwareupdate index 7b9abdad..39566bf9 100755 --- a/code/client/managedsoftwareupdate +++ b/code/client/managedsoftwareupdate @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ """ managedsoftwareupdate """ +from __future__ import absolute_import, print_function import optparse import os @@ -43,16 +44,16 @@ try: except ImportError: # Python is missing ObjC bindings. Run external report script. from munkilib import utils - print >> sys.stderr, 'Python is missing ObjC bindings.' + print('Python is missing ObjC bindings.', file=sys.stderr) _scriptdir = os.path.realpath(os.path.dirname(sys.argv[0])) _script = os.path.join(_scriptdir, 'report_broken_client') try: _result, _stdout, _stderr = utils.runExternalScript(_script) - print >> sys.stderr, _result, _stdout, _stderr + print(_result, _stdout, _stderr, file=sys.stderr) except utils.ScriptNotFoundError: pass # script is not required, so pass - except utils.RunExternalScriptError, err: - print >> sys.stderr, str(err) + except utils.RunExternalScriptError as err: + print(str(err), file=sys.stderr) sys.exit(200) else: from munkilib import appleupdates @@ -73,11 +74,12 @@ else: from munkilib import updatecheck from munkilib import utils from munkilib import FoundationPlist + from munkilib.wrappers import unicode_or_str import munkilib.authrestart.client as authrestartd -def signal_handler(signum, dummy_frame): +def signal_handler(signum, _frame): """Handle any signals we've been told to. Right now just handle SIGTERM so clean up can happen, like garbage collection, which will trigger object destructors and @@ -93,7 +95,7 @@ def getIdleSeconds(): proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, dummy_err) = proc.communicate() - ioreglines = str(output).splitlines() + ioreglines = output.decode("UTF-8").splitlines() idle_time = 0 regex = re.compile(r'"?HIDIdleTime"?\s+=\s+(\d+)') for line in ioreglines: @@ -101,7 +103,7 @@ def getIdleSeconds(): if idle_re: idle_time = idle_re.group(1) break - return int(int(idle_time)/1000000000) + return int(int(idle_time)/1000000000) # pylint: disable=old-division def networkUp(): @@ -115,7 +117,7 @@ def networkUp(): proc = subprocess.Popen(cmd, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, dummy_err) = proc.communicate() - lines = str(output).splitlines() + lines = output.decode('UTF-8').splitlines() for line in lines: if 'inet' in line: parts = line.split() @@ -144,7 +146,7 @@ def createDirsIfNeeded(dirlist): try: os.mkdir(directory) except (OSError, IOError): - print >> sys.stderr, 'ERROR: Could not create %s' % directory + print('ERROR: Could not create %s' % directory, file=sys.stderr) return False return True @@ -168,8 +170,7 @@ def initMunkiDirs(): display.display_error('Could not create needed directories ' 'in %s' % ManagedInstallDir) return False - else: - return True + return True def runScript(script, display_name, runtype): @@ -183,12 +184,12 @@ def runScript(script, display_name, runtype): try: utils.verifyFileOnlyWritableByMunkiAndRoot(script) - except utils.VerifyFilePermissionsError, err: + except utils.VerifyFilePermissionsError as err: # preflight/postflight is insecure, but if the currently executing # file is insecure too we are no worse off. try: utils.verifyFileOnlyWritableByMunkiAndRoot(__file__) - except utils.VerifyFilePermissionsError, err: + except utils.VerifyFilePermissionsError as err: # OK, managedsoftwareupdate is insecure anyway - warn & execute. display.display_warning( 'Multiple munki executable scripts have insecure file ' @@ -213,8 +214,8 @@ def runScript(script, display_name, runtype): display.display_info('%s stderr: %s' % (display_name, stderr)) except utils.ScriptNotFoundError: pass # script is not required, so pass - except utils.RunExternalScriptError, err: - display.display_warning(unicode(err)) + except utils.RunExternalScriptError as err: + display.display_warning(unicode_or_str(err)) return result @@ -369,7 +370,7 @@ def doRestart(shutdown=False): # this restart (perhaps by force-quitting the app), # that's their problem... else: - print 'Please restart immediately.' + print('Please restart immediately.') def munkiUpdatesAvailable(): @@ -593,7 +594,7 @@ def main(): '--show-config', action='store_true', help='Print the current configuration and exit.') config_options.add_option( - '--id', default='', + '--id', default=u'', help='String to use as ClientIdentifier for this run only.') config_options.add_option( '--set-bootstrap-mode', action='store_true', @@ -641,12 +642,12 @@ def main(): options, dummy_arguments = parser.parse_args() if options.version: - print info.get_version() + print(info.get_version()) exit(0) # check to see if we're root if os.geteuid() != 0: - print >> sys.stderr, 'You must run this as root!' + print('You must run this as root!', file=sys.stderr) exit(constants.EXIT_STATUS_ROOT_REQUIRED) if options.show_config: @@ -656,19 +657,19 @@ def main(): if options.set_bootstrap_mode: try: bootstrapping.set_bootstrap_mode() - print 'Bootstrap mode is set.' + print('Bootstrap mode is set.') exit(0) - except bootstrapping.SetupError, err: - print >> sys.stderr, err + except bootstrapping.SetupError as err: + print(err, file=sys.stderr) exit(-1) if options.clear_bootstrap_mode: try: bootstrapping.clear_bootstrap_mode() - print 'Bootstrap mode cleared.' + print('Bootstrap mode cleared.') exit(0) - except bootstrapping.SetupError, err: - print >> sys.stderr, err + except bootstrapping.SetupError as err: + print(err, file=sys.stderr) exit(-1) # check to see if another instance of this script is running @@ -682,8 +683,8 @@ def main(): % (myname, other_managedsoftwareupdate_pid)) munkilog.log('pid %s exiting.' % os.getpid()) munkilog.log('*' * 60) - print >> sys.stderr, ( - 'Another instance of %s is running. Exiting.' % myname) + print('Another instance of %s is running. Exiting.' % myname, + file=sys.stderr) osutils.cleanUpTmpDir() exit(0) @@ -773,8 +774,8 @@ def main(): options.verbose = 0 if options.checkonly and options.installonly: - print >> sys.stderr, \ - '--checkonly and --installonly options are mutually exclusive!' + print('--checkonly and --installonly options are mutually exclusive!', + file=sys.stderr) exit(constants.EXIT_STATUS_INVALID_PARAMETERS) # set munkicommon globals @@ -801,9 +802,9 @@ def main(): munkilog.log("### Starting managedsoftwareupdate run: %s ###" % runtype) if options.verbose: - print 'Managed Software Update Tool' - print 'Copyright 2010-2019 The Munki Project' - print 'https://github.com/munki/munki\n' + print('Managed Software Update Tool') + print('Copyright 2010-2020 The Munki Project') + print('https://github.com/munki/munki\n') display.display_status_major('Starting...') sendStartNotification() @@ -858,7 +859,7 @@ def main(): if not skip_munki_check: try: updatecheckresult = updatecheck.check( - client_id=options.id.decode('UTF-8')) + client_id=unicode_or_str(options.id)) except: display.display_error('Unexpected error in updatecheck:') munkilog.log(traceback.format_exc()) @@ -1060,13 +1061,13 @@ def main(): else: # no updates available if options.installonly and not options.quiet: - print 'Nothing to install or remove.' + print('Nothing to install or remove.') if runtype == 'checkandinstallatstartup': # we have nothing to do, clear the bootstrapping mode # so we'll stop running at startup/logout try: bootstrapping.clear_bootstrap_mode() - except bootstrapping.SetupError, err: + except bootstrapping.SetupError as err: display.display_error(err) display.display_status_major('Finishing...') @@ -1076,7 +1077,7 @@ def main(): munkilog.log("### Ending managedsoftwareupdate run ###") if options.verbose: - print 'Done.' + print('Done.') if notify_user: # it may have been more than a minute since we ran our original @@ -1132,7 +1133,7 @@ def main(): 'clearing bootstrap mode to prevent relaunch') try: bootstrapping.clear_bootstrap_mode() - except bootstrapping.SetupError, err: + except bootstrapping.SetupError as err: display.display_error(err) diff --git a/code/client/manifestutil b/code/client/manifestutil index f8b93617..804c8d6c 100755 --- a/code/client/manifestutil +++ b/code/client/manifestutil @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2011-2019 Greg Neagle. +# Copyright 2011-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,24 +19,25 @@ manifestutil Created by Greg Neagle on 2011-03-04. """ +from __future__ import absolute_import, print_function # TODO: add support for delete-manifest import fnmatch import optparse import os -import plistlib import readline import shlex import sys -from xml.parsers.expat import ExpatError - # our libs from munkilib.cliutils import ConfigurationSaveError from munkilib.cliutils import configure as _configure from munkilib.cliutils import libedit from munkilib.cliutils import get_version, pref, path2url +from munkilib.wrappers import (get_input, + readPlistFromString, writePlistToString, + PlistReadError, PlistWriteError) from munkilib import munkirepo @@ -47,20 +48,20 @@ def get_installer_item_names(repo, catalog_limit_list): item_list = [] try: catalogs_list = repo.itemlist('catalogs') - except munkirepo.RepoError, err: - print >> sys.stderr, ( - 'Could not retrieve catalogs: %s' % unicode(err)) + except munkirepo.RepoError as err: + print(( + u'Could not retrieve catalogs: %s' % err), file=sys.stderr) return [] for catalog_name in catalogs_list: if catalog_name in catalog_limit_list: try: data = repo.get(os.path.join('catalogs', catalog_name)) - catalog = plistlib.readPlistFromString(data) - except munkirepo.RepoError, err: - print >> sys.stderr, ( - 'Could not retrieve catalog %s: %s' - % (catalog_name, unicode(err))) - except (IOError, OSError, ExpatError): + catalog = readPlistFromString(data) + except munkirepo.RepoError as err: + print(( + u'Could not retrieve catalog %s: %s' + % (catalog_name, err)), file=sys.stderr) + except (IOError, OSError, PlistReadError): # skip items that aren't valid plists # or that we can't read pass @@ -77,9 +78,9 @@ def get_manifest_names(repo): '''Returns a list of available manifests''' try: manifest_names = repo.itemlist('manifests') - except munkirepo.RepoError, err: - print >> sys.stderr, ( - 'Could not retrieve manifests: %s' % unicode(err)) + except munkirepo.RepoError as err: + print(( + u'Could not retrieve manifests: %s' % err), file=sys.stderr) manifest_names = [] manifest_names.sort() return manifest_names @@ -89,9 +90,9 @@ def get_catalogs(repo): '''Returns a list of available catalogs''' try: catalog_names = repo.itemlist('catalogs') - except munkirepo.RepoError, err: - print >> sys.stderr, ( - 'Could not retrieve catalogs: %s' % unicode(err)) + except munkirepo.RepoError as err: + print(( + u'Could not retrieve catalogs: %s' % err), file=sys.stderr) catalog_names = [] catalog_names.sort() return catalog_names @@ -109,24 +110,24 @@ def printplistitem(label, value, indent=0): """Prints a plist item in an 'attractive' way""" indentspace = ' ' if value is None: - print indentspace*indent, '%s: !NONE!' % label + print(indentspace*indent, '%s: !NONE!' % label) elif isinstance(value, list) or type(value).__name__ == 'NSCFArray': if label: - print indentspace*indent, '%s:' % label + print(indentspace*indent, '%s:' % label) for item in value: printplistitem('', item, indent+1) elif isinstance(value, dict) or type(value).__name__ == 'NSCFDictionary': if label: - print indentspace*indent, '%s:' % label + print(indentspace*indent, '%s:' % label) keys = list(value.keys()) keys.sort() for subkey in keys: printplistitem(subkey, value[subkey], indent+1) else: if label: - print indentspace*indent, '%s: %s' % (label, value) + print(indentspace*indent, '%s: %s' % (label, value)) else: - print indentspace*indent, '%s' % value + print(indentspace*indent, '%s' % value) def printplist(plistdict): @@ -142,14 +143,14 @@ def get_manifest(repo, manifest_name): manifest_ref = os.path.join('manifests', manifest_name) try: data = repo.get(manifest_ref) - return plistlib.readPlistFromString(data) - except munkirepo.RepoError, err: - print >> sys.stderr, (u'Could not retrieve manifest %s: %s' - % (manifest_name, unicode(err))) + return readPlistFromString(data) + except munkirepo.RepoError as err: + print(u'Could not retrieve manifest %s: %s' + % (manifest_name, err), file=sys.stderr) return None - except (IOError, OSError, ExpatError), err: - print >> sys.stderr, ( - u'Could not read manifest %s: %s' % (manifest_name, unicode(err))) + except (IOError, OSError, PlistReadError) as err: + print(u'Could not read manifest %s: %s' % (manifest_name, err), + file=sys.stderr) return None @@ -158,16 +159,16 @@ def save_manifest(repo, manifest_dict, manifest_name, overwrite_existing=False): existing_manifest_names = get_manifest_names(repo) if not overwrite_existing: if manifest_name in existing_manifest_names: - print >> sys.stderr, '%s already exists!' % manifest_name + print('%s already exists!' % manifest_name, file=sys.stderr) return False manifest_ref = os.path.join('manifests', manifest_name) try: - data = plistlib.writePlistToString(manifest_dict) + data = writePlistToString(manifest_dict) repo.put(manifest_ref, data) return True - except (IOError, OSError, ExpatError, munkirepo.RepoError), err: - print >> sys.stderr, ( - u'Saving %s failed: %s' % (manifest_name, unicode(err))) + except (IOError, OSError, PlistWriteError, munkirepo.RepoError) as err: + print(u'Saving %s failed: %s' % (manifest_name, err), + file=sys.stderr) return False @@ -181,16 +182,17 @@ def manifest_rename(repo, source_manifest_name, dest_manifest_name, existing_manifest_names = get_manifest_names(repo) if not overwrite_existing: if dest_manifest_name in existing_manifest_names: - print >> sys.stderr, u'%s already exists!' % dest_manifest_name + print(u'%s already exists!' % dest_manifest_name, file=sys.stderr) return False try: source_data = repo.get(source_manifest_ref) repo.put(dest_manifest_ref, source_data) repo.delete(source_manifest_ref) return True - except munkirepo.RepoError, err: - print >> sys.stderr, u'Renaming %s to %s failed: %s' % ( - source_manifest_name, dest_manifest_name, unicode(err)) + except munkirepo.RepoError as err: + print(u'Renaming %s to %s failed: %s' + % (source_manifest_name, dest_manifest_name, err), + file=sys.stderr) return False @@ -246,9 +248,9 @@ def version(repo, args): # we ignore repo arg but subcommand dispatcher sends it to all # subcommands if len(args) != 0: - print >> sys.stderr, 'Usage: version' + print('Usage: version', file=sys.stderr) return 22 # Invalid argument - print get_version() + print(get_version()) return 0 @@ -256,7 +258,7 @@ def configure(repo, args): '''Allow user to configure tool options''' # we ignore the repo arg, but all the other subcommands require it if len(args): - print >> sys.stderr, 'Usage: configure' + print('Usage: configure', file=sys.stderr) return 22 # Invalid argument prompt_list = [ ('repo_url', 'Repo URL (example: afp://munki.example.com/repo)'), @@ -276,8 +278,8 @@ def list_catalogs(repo, args): Prints the names of the available catalogs''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -286,7 +288,7 @@ def list_catalogs(repo, args): parser.print_usage(sys.stderr) return 22 # Invalid argument for item in get_catalogs(repo): - print item + print(item) return 0 @@ -297,8 +299,8 @@ def list_catalog_items(repo, args): Lists items in the given catalogs''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -309,10 +311,10 @@ def list_catalog_items(repo, args): available_catalogs = get_catalogs(repo) for catalog in arguments: if catalog not in available_catalogs: - print >> sys.stderr, '%s: no such catalog!' % catalog + print('%s: no such catalog!' % catalog, file=sys.stderr) return 2 # No such file or directory for pkg in get_installer_item_names(repo, arguments): - print pkg + print(pkg) return 0 @@ -324,8 +326,8 @@ def list_manifests(repo, args): Prints names of available manifests.''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -339,7 +341,7 @@ def list_manifests(repo, args): return 7 # Argument list too long for item in get_manifest_names(repo): if fnmatch.fnmatch(item, list_filter): - print item + print(item) return 0 @@ -356,8 +358,8 @@ def find(repo, args): 'FIND_TEXT')) try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -376,10 +378,10 @@ def find(repo, args): manifest_ref = os.path.join('manifests', name) try: data = repo.get(manifest_ref) - manifest = plistlib.readPlistFromString(data) - except (IOError, OSError, ExpatError, munkirepo.RepoError), err: - print >> sys.stderr, ( - u'Error reading %s: %s' % (manifest_ref, unicode(err))) + manifest = readPlistFromString(data) + except (IOError, OSError, PlistReadError, munkirepo.RepoError) as err: + print(u'Error reading %s: %s' % (manifest_ref, err), + file=sys.stderr) continue if keyname: if keyname in manifest: @@ -388,12 +390,12 @@ def find(repo, args): for item in value: try: if findtext.upper() in item.upper(): - print '%s: %s' % (name, item) + print('%s: %s' % (name, item)) count += 1 except AttributeError: pass elif findtext.upper() in value.upper(): - print '%s: %s' % (name, value) + print('%s: %s' % (name, value)) count += 1 else: for key in manifest.keys(): @@ -402,15 +404,15 @@ def find(repo, args): for item in value: try: if findtext.upper() in item.upper(): - print '%s (%s): %s' % (name, key, item) + print('%s (%s): %s' % (name, key, item)) count += 1 except AttributeError: pass elif findtext.upper() in value.upper(): - print '%s (%s): %s' % (name, key, value) + print('%s (%s): %s' % (name, key, value)) count += 1 - print '%s matches found.' % count + print('%s matches found.' % count) return 0 @@ -421,8 +423,8 @@ def display_manifest(repo, args): Prints the contents of the specified manifest''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -460,8 +462,8 @@ def expand_included_manifests(repo, args): Prints included manifests in the specified manifest''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -484,8 +486,8 @@ def new_manifest(repo, args): Creates a new empty manifest''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -513,8 +515,8 @@ def copy_manifest(repo, args): Copies the contents of one manifest to another''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -540,8 +542,8 @@ def rename_manifest(repo, args): Renames the manifest''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -577,8 +579,8 @@ def add_pkg(repo, args): 'Defaults to managed_installs.')) try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -600,18 +602,18 @@ def add_pkg(repo, args): return 2 # No such file or directory for section in get_manifest_pkg_sections(): if pkgname in manifest.get(section, []): - print >> sys.stderr, ( + print(( 'Package %s is already in section %s of manifest %s.' - % (pkgname, section, options.manifest)) + % (pkgname, section, options.manifest)), file=sys.stderr) return 1 # Operation not permitted manifest_catalogs = manifest.get('catalogs', []) available_pkgnames = get_installer_item_names(repo, manifest_catalogs) if pkgname not in available_pkgnames: - print >> sys.stderr, ( + print(( 'WARNING: Package %s is not available in catalogs %s ' 'of manifest %s.' - % (pkgname, manifest_catalogs, options.manifest)) + % (pkgname, manifest_catalogs, options.manifest)), file=sys.stderr) if not options.section in manifest: manifest[options.section] = [pkgname] else: @@ -635,8 +637,8 @@ def move_install_to_uninstall(repo, args): help='''name of manifest on which to operate''') try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -659,11 +661,13 @@ def move_install_to_uninstall(repo, args): else: if pkgname in manifest['managed_installs']: manifest['managed_installs'].remove(pkgname) - print ('Removed %s from section %s of manifest %s' % (pkgname, 'managed_installs', options.manifest)) + print ('Removed %s from section %s of manifest %s' + % (pkgname, 'managed_installs', options.manifest)) else: - print >> sys.stderr, ('WARNING: Package %s is not in section %s ' - 'of manifest %s. No changes made.' - % (pkgname, 'managed_installs', options.manifest)) + print('WARNING: Package %s is not in section %s ' + 'of manifest %s. No changes made.' + % (pkgname, 'managed_installs', options.manifest), + file=sys.stderr) return 1 # Operation not permitted if pkgname in manifest['managed_uninstalls']: print ('%s is already in section managed_uninstalls of manifest %s.' @@ -697,8 +701,8 @@ def remove_pkg(repo, args): 'package. Defaults to managed_installs.')) try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -719,13 +723,12 @@ def remove_pkg(repo, args): if not manifest: return 2 # No such file or directory if not options.section in manifest: - print >> sys.stderr, ('Section %s is not in manifest %s.' - % (options.section, options.manifest)) + print('Section %s is not in manifest %s.' + % (options.section, options.manifest), file=sys.stderr) return 1 # Operation not permitted if pkgname not in manifest[options.section]: - print >> sys.stderr, ('Package %s is not in section %s ' - 'of manifest %s.' - % (pkgname, options.section, options.manifest)) + print('Package %s is not in section %s of manifest %s.' + % (pkgname, options.section, options.manifest), file=sys.stderr) return 1 # Operation not permitted else: manifest[options.section].remove(pkgname) @@ -748,8 +751,8 @@ def add_catalog(repo, args): help='name of manifest on which to operate') try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -768,7 +771,7 @@ def add_catalog(repo, args): available_catalogs = get_catalogs(repo) if catalogname not in available_catalogs: - print >> sys.stderr, 'Unknown catalog name: %s.' % catalogname + print('Unknown catalog name: %s.' % catalogname, file=sys.stderr) return 2 # no such file or directory manifest = get_manifest(repo, options.manifest) @@ -777,9 +780,9 @@ def add_catalog(repo, args): if not 'catalogs' in manifest: manifest['catalogs'] = [] if catalogname in manifest['catalogs']: - print >> sys.stderr, ( + print(( 'Catalog %s is already in manifest %s.' - % (catalogname, options.manifest)) + % (catalogname, options.manifest)), file=sys.stderr) return 1 # Operation not permitted else: # put it at the front of the catalog list as that is usually @@ -804,8 +807,8 @@ def remove_catalog(repo, args): help='name of manifest on which to operate') try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -826,9 +829,9 @@ def remove_catalog(repo, args): if not manifest: return 2 # no such file or directory if catalogname not in manifest.get('catalogs', []): - print >> sys.stderr, ( + print(( 'Catalog %s is not in manifest %s.' - % (catalogname, options.manifest)) + % (catalogname, options.manifest)), file=sys.stderr) return 1 # Operation not permitted else: manifest['catalogs'].remove(catalogname) @@ -852,8 +855,8 @@ def add_included_manifest(repo, args): help='name of manifest on which to operate') try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -872,12 +875,12 @@ def add_included_manifest(repo, args): available_manifests = get_manifest_names(repo) if manifest_to_include not in available_manifests: - print >> sys.stderr, ('Unknown manifest name: %s.' - % manifest_to_include) + print('Unknown manifest name: %s.' % manifest_to_include, + file=sys.stderr) return 2 # no such file or directory if manifest_to_include == options.manifest: - print >> sys.stderr, ('Can\'t include %s in itself!.' - % manifest_to_include) + print('Can\'t include %s in itself!.' % manifest_to_include, + file=sys.stderr) return 1 # Operation not permitted manifest = get_manifest(repo, options.manifest) @@ -886,9 +889,8 @@ def add_included_manifest(repo, args): if not 'included_manifests' in manifest: manifest['included_manifests'] = [] if manifest_to_include in manifest['included_manifests']: - print >> sys.stderr, ( - 'Manifest %s is already included in manifest %s.' - % (manifest_to_include, options.manifest)) + print('Manifest %s is already included in manifest %s.' + % (manifest_to_include, options.manifest), file=sys.stderr) return 1 # Operation not permitted else: manifest['included_manifests'].append(manifest_to_include) @@ -912,8 +914,8 @@ def remove_included_manifest(repo, args): help='name of manifest on which to operate') try: options, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -934,16 +936,15 @@ def remove_included_manifest(repo, args): if not manifest: return 2 # no such file or directory if included_manifest not in manifest.get('included_manifests', []): - print >> sys.stderr, ( - 'Manifest %s is not included in manifest %s.' - % (included_manifest, options.manifest)) + print('Manifest %s is not included in manifest %s.' + % (included_manifest, options.manifest), file=sys.stderr) return 1 # Operation not permitted else: manifest['included_manifests'].remove(included_manifest) if save_manifest(repo, manifest, options.manifest, overwrite_existing=True): - print ('Removed %s from included_manifests of manifest %s.' - % (included_manifest, options.manifest)) + print('Removed %s from included_manifests of manifest %s.' + % (included_manifest, options.manifest)) return 0 else: return 1 # Operation not permitted @@ -957,8 +958,8 @@ def refresh_cache(repo, args): Refreshes the repo data''') try: _, arguments = parser.parse_args(args) - except MyOptParseError, errmsg: - print >> sys.stderr, str(errmsg) + except MyOptParseError as errmsg: + print(str(errmsg), file=sys.stderr) return 22 # Invalid argument except MyOptParseExit: return 0 @@ -975,11 +976,11 @@ def refresh_cache(repo, args): def show_help(): '''Prints available subcommands''' - print "Available sub-commands:" - subcommands = CMD_ARG_DICT['cmds'].keys() + print("Available sub-commands:") + subcommands = list(CMD_ARG_DICT['cmds'].keys()) subcommands.sort() for item in subcommands: - print '\t%s' % item + print('\t%s' % item) return 0 @@ -990,7 +991,7 @@ def tab_completer(text, state): # since we are at the start of the line # we are matching commands array_to_match = 'cmds' - match_list = CMD_ARG_DICT.get('cmds', {}).keys() + match_list = list(CMD_ARG_DICT.get('cmds', {}).keys()) else: # we are matching args cmd_line = readline.get_line_buffer()[0:readline.get_begidx()] @@ -1004,7 +1005,7 @@ def tab_completer(text, state): match_list = CMD_ARG_DICT[array_to_match] else: array_to_match = 'options' - match_list = CMD_ARG_DICT.get('options', {}).keys() + match_list = list(CMD_ARG_DICT.get('options', {}).keys()) matches = [item for item in match_list if item.upper().startswith(text.upper())] @@ -1054,7 +1055,7 @@ def handle_subcommand(repo, args): except MyOptParseExit: return 0 except (TypeError, KeyError): - print >> sys.stderr, 'Unknown subcommand: %s' % args[0] + print('Unknown subcommand: %s' % args[0], file=sys.stderr) show_help() return 2 else: @@ -1069,14 +1070,14 @@ def connect_to_repo(): if not repo_url and repo_path: repo_url = path2url(repo_path) if not repo_url: - print >> sys.stderr, ( + print(( u'No repo URL defined. Run %s --configure to define one.' - % os.path.basename(__file__)) + % os.path.basename(__file__)), file=sys.stderr) exit(-1) try: repo = munkirepo.connect(repo_url, repo_plugin) - except munkirepo.RepoError, err: - print >> sys.stderr, u'Repo error: %s' % unicode(err) + except munkirepo.RepoError as err: + print(u'Repo error: %s' % err, file=sys.stderr) exit(-1) return repo @@ -1133,13 +1134,13 @@ def main(): repo, CMD_ARG_DICT['catalogs']) set_up_tab_completer() - print 'Entering interactive mode... (type "help" for commands)' + print('Entering interactive mode... (type "help" for commands)') while 1: try: - cmd = raw_input('> ') + cmd = get_input('> ') except (KeyboardInterrupt, EOFError): # React to Control-C and Control-D - print # so we finish off the raw_input line + print() # so we finish off the raw_input line cleanup_and_exit(0) args = shlex.split(cmd) handle_subcommand(repo, args) diff --git a/code/client/munkiimport b/code/client/munkiimport index 3d627c76..39769a8b 100755 --- a/code/client/munkiimport +++ b/code/client/munkiimport @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2010-2019 Greg Neagle. +# Copyright 2010-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ Created by Greg Neagle on 2010-09-29. Assists with importing installer items into the munki repo """ +from __future__ import absolute_import, print_function + # std lib imports import optparse import os @@ -33,7 +35,8 @@ import tempfile from munkilib.cliutils import ConfigurationSaveError from munkilib.cliutils import configure as _configure from munkilib.cliutils import pref, path2url -from munkilib.cliutils import raw_input_with_default +from munkilib.cliutils import get_input_with_default +from munkilib.wrappers import get_input from munkilib import info from munkilib import dmgutils @@ -53,7 +56,7 @@ def make_dmg(pkgpath): Returns path to newly-created disk image.""" pkgname = os.path.basename(pkgpath) - print 'Making disk image containing %s...' % pkgname + print('Making disk image containing %s...' % pkgname) diskimagename = os.path.splitext(pkgname)[0] + '.dmg' diskimagepath = os.path.join(osutils.tmpdir(), diskimagename) cmd = ['/usr/bin/hdiutil', 'create', '-fs', 'HFS+', @@ -62,37 +65,35 @@ def make_dmg(pkgpath): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) while True: - output = proc.stdout.readline() + output = proc.stdout.readline().decode('UTF-8') if not output and (proc.poll() != None): break - line = output.rstrip('\n').decode('UTF-8') - if len(line) > 0: - print line + line = output.rstrip('\n') + if line: + print(line) sys.stdout.flush() retcode = proc.poll() if retcode: - print >> sys.stderr, 'Disk image creation failed.' + print('Disk image creation failed.', file=sys.stderr) return '' - else: - print 'Disk image created at: %s' % diskimagepath - return diskimagepath + print('Disk image created at: %s' % diskimagepath) + return diskimagepath def edit_pkginfo_in_editor(pkginfo): """Opens pkginfo list in the user's chosen editor.""" editor = pref('editor') if editor: - answer = raw_input('Edit pkginfo before upload? [y/n]: ') + answer = get_input('Edit pkginfo before upload? [y/N]: ') if answer.lower().startswith('y'): filedesc, filepath = tempfile.mkstemp() # we just want the path; close the file descriptor os.close(filedesc) try: FoundationPlist.writePlist(pkginfo, filepath) - except FoundationPlist.FoundationPlistException, err: - print >> sys.stderr, ( - u'Could not save pkginfo to temp file: %s' - % unicode(err)) + except FoundationPlist.FoundationPlistException as err: + print(u'Could not save pkginfo to temp file: %s' + % err, file=sys.stderr) return pkginfo if editor.endswith('.app'): @@ -101,9 +102,9 @@ def edit_pkginfo_in_editor(pkginfo): cmd = [editor, filepath] try: dummy_returncode = subprocess.check_call(cmd) - except (OSError, subprocess.CalledProcessError), err: - print >> sys.stderr, ( - 'Problem running editor %s: %s.' % (editor, err)) + except (OSError, subprocess.CalledProcessError) as err: + print('Problem running editor %s: %s.' % (editor, err), + file=sys.stderr) os.remove(filepath) return pkginfo else: @@ -111,12 +112,12 @@ def edit_pkginfo_in_editor(pkginfo): # wait for user to finish with GUI editor. answer = 'no' while not answer.lower().startswith('y'): - answer = raw_input('Pkginfo editing complete? [y/n]: ') + answer = get_input('Pkginfo editing complete? [y/N]: ') try: edited_pkginfo = FoundationPlist.readPlist(filepath) - except FoundationPlist.FoundationPlistException, err: - print >> sys.stderr, ( - u'Problem reading edited pkginfo: %s' % unicode(err)) + except FoundationPlist.FoundationPlistException as err: + print(u'Problem reading edited pkginfo: %s' % err, + file=sys.stderr) os.remove(filepath) return pkginfo os.remove(filepath) @@ -128,21 +129,21 @@ def prompt_for_subdirectory(repo, subdirectory): """Prompts the user for a subdirectory for the pkg and pkginfo""" try: pkgsinfo_list = munkiimportlib.list_items_of_kind(repo, 'pkgsinfo') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise munkiimportlib.RepoCopyError( - u'Unable to get list of current pkgsinfo: %s' % unicode(err)) + u'Unable to get list of current pkgsinfo: %s' % err) # filter the list of pkgsinfo to a list of subdirectories subdir_set = set() for item in pkgsinfo_list: subdir_set.add(os.path.dirname(item)) while True: - newdir = raw_input( + newdir = get_input( 'Upload item to subdirectory path [%s]: ' % subdirectory) if newdir: destination_path = os.path.join('pkgsinfo', newdir) if destination_path not in subdir_set: - answer = raw_input('Path %s doesn\'t exist. Create it? [y/n] ' + answer = get_input('Path %s doesn\'t exist. Create it? [y/N] ' % destination_path) if answer.lower().startswith('y'): break @@ -153,24 +154,19 @@ def prompt_for_subdirectory(repo, subdirectory): return newdir -def print_fn(text): - '''Wraps print in a function for make_catalogs''' - print text - - def make_catalogs(repo, options): """Rebuild our catalogs""" if not options.verbose: - print 'Rebuilding catalogs at %s...' % options.repo_url + print('Rebuilding catalogs at %s...' % options.repo_url) output_fn = None else: - output_fn = print_fn + output_fn = print errors = makecatalogslib.makecatalogs(repo, {}, output_fn=output_fn) if errors: - print '\nThe following issues occurred while building catalogs:\n' + print('\nThe following issues occurred while building catalogs:\n') for error in errors: - print error + print(error) def cleanup_and_exit(exitcode): @@ -179,7 +175,7 @@ def cleanup_and_exit(exitcode): # TODO: reimplement this #if repo and repo.mounted and repo.WE_MOUNTED_THE_REPO: # if not NOINTERACTIVE: - # answer = raw_input('Unmount the repo fileshare? [y/n] ') + # answer = raw_input('Unmount the repo fileshare? [y/N] ') # if answer.lower().startswith('y'): # result = repo.unmount() # else: @@ -258,9 +254,9 @@ def main(): # now add all the shared makepkginfo options pkginfolib.add_option_groups(parser) - sys.argv = [unicode(item, 'utf-8') for item in sys.argv] + #sys.argv = [unicode(item, 'utf-8') for item in sys.argv] options, arguments = parser.parse_args() - + # there are a lot of valid combinations of option flags and arguments # but if there are no option flags or arguments we should print usage if len(sys.argv) == 1: @@ -268,7 +264,7 @@ def main(): exit(0) if options.version: - print info.get_version() + print(info.get_version()) exit(0) if options.configure: @@ -281,9 +277,8 @@ def main(): options.repo_url = path2url(repo_path) if not options.repo_url: - print >> sys.stderr, ('No repo URL found. Please run this tool with ' - 'the --configure option, or use the --repo-url ' - 'option.') + print('No repo URL found. Please run this tool with the --configure ' + 'option, or use the --repo-url option.', file=sys.stderr) parser.print_help() exit(-1) @@ -291,15 +286,15 @@ def main(): options.plugin = 'FileRepo' if options.icon_path and not os.path.isfile(options.icon_path): - print >> sys.stderr, ('The specified icon file does not exist.') + print('The specified icon file does not exist.', file=sys.stderr) exit(-1) - if (options.apple_update and len(arguments) > 0) or len(arguments) > 1: + if (options.apple_update and arguments) or len(arguments) > 1: parser.print_usage() exit(0) installer_item = None - if len(arguments): + if arguments: installer_item = arguments[0] uninstaller_item = options.uninstalleritem is_applemetadata = options.apple_update @@ -319,26 +314,27 @@ def main(): if (not pkgutils.hasValidInstallerItemExt(installer_item) and not pkgutils.isApplication(installer_item)): - print >> sys.stderr, ( - 'Unknown installer item type: "%s"' % installer_item) + print('Unknown installer item type: "%s"' % installer_item, + file=sys.stderr) exit(-1) if not os.path.exists(installer_item): - print >> sys.stderr, '%s does not exist!' % installer_item + print('%s does not exist!' % installer_item, file=sys.stderr) exit(-1) try: repo = munkirepo.connect(options.repo_url, options.plugin) - except munkirepo.RepoError, err: - print >> sys.stderr, (u'Could not connect to munki repo: %s' - % unicode(err)) + except munkirepo.RepoError as err: + print(u'Could not connect to munki repo: %s' % err, + file=sys.stderr) exit(-1) if not is_applemetadata: if os.path.isdir(installer_item): if pkgutils.hasValidDiskImageExt(installer_item): # a directory named foo.dmg or foo.iso! - print >> sys.stderr, '%s is an unknown type.' % installer_item + print('%s is an unknown type.' % installer_item, + file=sys.stderr) cleanup_and_exit(-1) else: # we need to convert to dmg @@ -346,9 +342,8 @@ def main(): if dmg_path: installer_item = dmg_path else: - print >> sys.stderr, ( - 'Could not convert %s to a disk image.' - % installer_item) + print('Could not convert %s to a disk image.' + % installer_item, file=sys.stderr) cleanup_and_exit(-1) if uninstaller_item: @@ -358,8 +353,8 @@ def main(): if os.path.isdir(uninstaller_item): if pkgutils.hasValidDiskImageExt(uninstaller_item): # a directory named foo.dmg or foo.iso! - print >> sys.stderr, ( - '%s is an unknown type.' % uninstaller_item) + print('%s is an unknown type.' % uninstaller_item, + file=sys.stderr) cleanup_and_exit(-1) else: # we need to convert to dmg @@ -367,9 +362,8 @@ def main(): if dmg_path: uninstaller_item = dmg_path else: - print >> sys.stderr, ( - 'Could not convert %s to a disk image.' - % uninstaller_item) + print('Could not convert %s to a disk image.' + % uninstaller_item, file=sys.stderr) cleanup_and_exit(-1) options.uninstalleritem = uninstaller_item @@ -382,10 +376,10 @@ def main(): # make a pkginfo! try: pkginfo = pkginfolib.makepkginfo(installer_item, options) - except pkginfolib.PkgInfoGenerationError, err: + except pkginfolib.PkgInfoGenerationError as err: # makepkginfo returned an error - print >> sys.stderr, 'Getting package info failed.' - print >> sys.stderr, err + print('Getting package info failed.', file=sys.stderr) + print(err, file=sys.stderr) cleanup_and_exit(-1) if not options.nointeractive: @@ -400,22 +394,21 @@ def main(): print ('***This item is identical to an existing item in ' 'the repo***:') else: - print 'This item is similar to an existing item in the repo:' + print('This item is similar to an existing item in the repo:') fields = (('Item name', 'name'), ('Display name', 'display_name'), ('Description', 'description'), ('Version', 'version'), ('Installer item path', 'installer_item_location')) for (name, key) in fields: - print '%21s: %s' % ( - name, matchingpkginfo.get(key, '').encode('UTF-8')) - print + print('%21s: %s' % (name, matchingpkginfo.get(key, ''))) + print() if exactmatch: - answer = raw_input('Import this item anyway? [y/n] ') + answer = get_input('Import this item anyway? [y/N] ') if not answer.lower().startswith('y'): cleanup_and_exit(0) - answer = raw_input('Use existing item as a template? [y/n] ') + answer = get_input('Use existing item as a template? [y/N] ') if answer.lower().startswith('y'): pkginfo['name'] = matchingpkginfo['name'] pkginfo['display_name'] = ( @@ -442,7 +435,7 @@ def main(): 'localized_strings', 'featured']: if key in matchingpkginfo: - print 'Copying %s: %s' % (key, matchingpkginfo[key]) + print('Copying %s: %s' % (key, matchingpkginfo[key])) pkginfo[key] = matchingpkginfo[key] # now let user do some basic editing @@ -460,8 +453,8 @@ def main(): if kind == 'bool': default = str(pkginfo.get(key, False)) else: - default = pkginfo.get(key, '').encode('UTF-8') - pkginfo[key] = raw_input_with_default(prompt, default) + default = pkginfo.get(key, '') + pkginfo[key] = get_input_with_default(prompt, default) if kind == 'bool': value = pkginfo[key].lower().strip() pkginfo[key] = value.startswith(('y', 't')) @@ -469,7 +462,7 @@ def main(): # special handling for catalogs array prompt = '%20s: ' % 'Catalogs' default = ', '.join(pkginfo['catalogs']) - newvalue = raw_input_with_default(prompt, default) + newvalue = get_input_with_default(prompt, default) pkginfo['catalogs'] = [item.strip() for item in newvalue.split(',')] @@ -477,27 +470,26 @@ def main(): not pkginfo.get( 'installer_type') in ['profile', 'startosinstall']): if 'receipts' not in pkginfo and 'installs' not in pkginfo: - print >> sys.stderr, ('WARNING: There are no receipts and no ' - '\'installs\' items for this installer ' - 'item. You will need to add at least ' - 'one item to the \'installs\' list.') + print('WARNING: There are no receipts and no \'installs\' ' + 'items for this installer item. You will need to add at ' + 'least one item to the \'installs\' list.', + file=sys.stderr) - print + print() #for (name, key, kind) in editfields: # if kind == 'bool': # print '%20s: %s' % (name, pkginfo.get(key, False)) # else: - # print '%20s: %s' % (name, pkginfo.get(key, '').encode('UTF-8')) - #print '%20s: %s' % ( - # 'Catalogs', ', '.join(pkginfo['catalogs']).encode('UTF-8')) + # print '%20s: %s' % (name, pkginfo.get(key, '')) + #print '%20s: %s' % 'Catalogs', ', '.join(pkginfo['catalogs']) #print - answer = raw_input('Import this item? [y/n] ') + answer = get_input('Import this item? [y/N] ') if not answer.lower().startswith('y'): cleanup_and_exit(0) if options.subdirectory == '': if (not is_applemetadata and - isinstance(repo, munkirepo.FileRepo.FileRepo)): + isinstance(repo, munkirepo.FileRepo)): repo_pkgs_path = os.path.join(repo.root, 'pkgs') installer_item_abspath = os.path.abspath(installer_item) if installer_item_abspath.startswith(repo_pkgs_path): @@ -521,18 +513,18 @@ def main(): try: munkiimportlib.convert_and_install_icon(repo, pkginfo, options.icon_path) - except munkiimportlib.RepoCopyError, err: - print >> sys.stderr, err + except munkiimportlib.RepoCopyError as err: + print(err, file=sys.stderr) elif options.extract_icon: pass elif (not options.nointeractive and not munkiimportlib.icon_exists_in_repo(repo, pkginfo) and not is_applemetadata and not pkginfo.get('installer_type') == 'profile'): - print 'No existing product icon found.' - answer = raw_input('Attempt to create a product icon? [y/n] ') + print('No existing product icon found.') + answer = get_input('Attempt to create a product icon? [y/N] ') if answer.lower().startswith('y'): - print 'Attempting to extract and upload icon...' + print('Attempting to extract and upload icon...') options.extract_icon = True if options.extract_icon: @@ -540,11 +532,11 @@ def main(): imported_paths = munkiimportlib.extract_and_copy_icon( repo, installer_item, pkginfo) if imported_paths: - print 'Imported %s.' % imported_paths + print('Imported %s.' % imported_paths) else: - print 'No icons found for import.' - except munkiimportlib.RepoCopyError, err: - print >> sys.stderr, err + print('No icons found for import.') + except munkiimportlib.RepoCopyError as err: + print(err, file=sys.stderr) # fix in case user accidentally starts subdirectory with a slash if options.subdirectory.startswith('/'): @@ -552,14 +544,14 @@ def main(): if not is_applemetadata: try: - print 'Copying %s to repo...' % os.path.basename(installer_item) + print('Copying %s to repo...' % os.path.basename(installer_item)) uploaded_pkgpath = munkiimportlib.copy_item_to_repo( repo, installer_item, pkginfo.get('version'), options.subdirectory) - print 'Copied %s to %s.' % (os.path.basename(installer_item), - uploaded_pkgpath) - except munkiimportlib.RepoCopyError, errmsg: - print >> sys.stderr, errmsg + print('Copied %s to %s.' + % (os.path.basename(installer_item), uploaded_pkgpath)) + except munkiimportlib.RepoCopyError as errmsg: + print(errmsg, file=sys.stderr) cleanup_and_exit(-1) # adjust the installer_item_location to match @@ -568,15 +560,15 @@ def main(): if uninstaller_item: try: - print 'Copying %s to repo...' % os.path.basename( - uninstaller_item) + print('Copying %s to repo...' % os.path.basename( + uninstaller_item)) uploaded_pkgpath = munkiimportlib.copy_item_to_repo( repo, uninstaller_item, pkginfo.get('version'), options.subdirectory) - print 'Copied %s to %s.' % ( - os.path.basename(uninstaller_item), uploaded_pkgpath) - except munkiimportlib.RepoCopyError, errmsg: - print >> sys.stderr, errmsg + print('Copied %s to %s.' % ( + os.path.basename(uninstaller_item), uploaded_pkgpath)) + except munkiimportlib.RepoCopyError as errmsg: + print(errmsg, file=sys.stderr) cleanup_and_exit(-1) # adjust the uninstaller_item_location to match @@ -585,7 +577,7 @@ def main(): uploaded_pkgpath.partition('/')[2]) itemsize = int(os.path.getsize(uninstaller_item)) itemhash = munkihash.getsha256hash(uninstaller_item) - pkginfo['uninstaller_item_size'] = int(itemsize/1024) + pkginfo['uninstaller_item_size'] = int(itemsize/1024) # pylint: disable=old-division pkginfo['uninstaller_item_hash'] = itemhash # add icon to pkginfo if in repository @@ -596,14 +588,15 @@ def main(): # possibly edit the pkginfo file in the user's editor pkginfo = edit_pkginfo_in_editor(pkginfo) try: - pkginfo_path = munkiimportlib.copy_pkginfo_to_repo(repo, pkginfo, options.subdirectory) - print 'Saved pkginfo to %s.' % pkginfo_path - except munkiimportlib.RepoCopyError, errmsg: - print >> sys.stderr, errmsg + pkginfo_path = munkiimportlib.copy_pkginfo_to_repo( + repo, pkginfo, options.subdirectory) + print('Saved pkginfo to %s.' % pkginfo_path) + except munkiimportlib.RepoCopyError as errmsg: + print(errmsg, file=sys.stderr) cleanup_and_exit(-1) if not options.nointeractive: - answer = raw_input('Rebuild catalogs? [y/n] ') + answer = get_input('Rebuild catalogs? [y/N] ') if answer.lower().startswith('y'): make_catalogs(repo, options) diff --git a/code/client/munkilib/FoundationPlist.py b/code/client/munkilib/FoundationPlist.py index 6a5c44f2..23ee5554 100644 --- a/code/client/munkilib/FoundationPlist.py +++ b/code/client/munkilib/FoundationPlist.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ dictionary). To work with plist data in strings, you can use readPlistFromString() and writePlistToString(). """ +from __future__ import absolute_import, print_function # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. @@ -87,11 +88,11 @@ def readPlist(filepath): def readPlistFromString(data): - '''Read a plist data from a string. Return the root object.''' - try: - plistData = buffer(data) - except TypeError, err: - raise NSPropertyListSerializationException(err) + '''Read a plist data from a (byte)string. Return the root object.''' + plistData = NSData.dataWithBytes_length_(data, len(data)) + if not plistData: + raise NSPropertyListSerializationException( + "Could not convert string to NSData") dataObject, dummy_plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( @@ -129,7 +130,7 @@ def writePlist(dataObject, filepath): def writePlistToString(rootObject): - '''Return 'rootObject' as a plist-formatted string.''' + '''Return 'rootObject' as a plist-formatted (byte)string.''' plistData, error = ( NSPropertyListSerialization. dataFromPropertyList_format_errorDescription_( @@ -141,8 +142,8 @@ def writePlistToString(rootObject): error = "Unknown error" raise NSPropertyListSerializationException(error) else: - return str(plistData) + return bytes(plistData) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/admin/common.py b/code/client/munkilib/admin/common.py index 6219f0cc..0d00c223 100644 --- a/code/client/munkilib/admin/common.py +++ b/code/client/munkilib/admin/common.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ admin/common.py Created by Greg Neagle on 2017-11-19. Common code used by the admin libs """ +from __future__ import absolute_import import os diff --git a/code/client/munkilib/admin/makecatalogslib.py b/code/client/munkilib/admin/makecatalogslib.py index 81d0d3bc..f59b9df6 100644 --- a/code/client/munkilib/admin/makecatalogslib.py +++ b/code/client/munkilib/admin/makecatalogslib.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,17 +19,19 @@ makecatalogslib Created by Greg Neagle on 2017-11-19. Routines used by makecatalogs """ +from __future__ import absolute_import, print_function # std libs import hashlib import os -import plistlib # our libs from .common import list_items_of_kind, AttributeDict from .. import munkirepo +from ..wrappers import readPlistFromString, writePlistToString + class MakeCatalogsError(Exception): '''Error to raise when there is problem making catalogs''' @@ -53,11 +55,11 @@ def hash_icons(repo, output_fn=None): try: icondata = repo.get('icons/' + icon_ref) icons[icon_ref] = hashlib.sha256(icondata).hexdigest() - except munkirepo.RepoError, err: - errors.append(u'RepoError for %s: %s' % (icon_ref, unicode(err))) - except IOError, err: + except munkirepo.RepoError as err: + errors.append(u'RepoError for %s: %s' % (icon_ref, err)) + except IOError as err: errors.append(u'IO error for %s: %s' % (icon_ref, err)) - except BaseException, err: + except BaseException as err: errors.append(u'Unexpected error for %s: %s' % (icon_ref, err)) return icons, errors @@ -165,18 +167,18 @@ def process_pkgsinfo(repo, options, output_fn=None): output_fn("Getting list of pkgsinfo...") try: pkgsinfo_list = list_items_of_kind(repo, 'pkgsinfo') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise MakeCatalogsError( - "Error getting list of pkgsinfo items: %s" % unicode(err)) + u"Error getting list of pkgsinfo items: %s" % err) # get a list of pkgs items if output_fn: output_fn("Getting list of pkgs...") try: pkgs_list = list_items_of_kind(repo, 'pkgs') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise MakeCatalogsError( - "Error getting list of pkgs items: %s" % unicode(err)) + u"Error getting list of pkgs items: %s" % err) # start with empty catalogs dict catalogs = {} @@ -187,11 +189,11 @@ def process_pkgsinfo(repo, options, output_fn=None): # Try to read the pkginfo file try: data = repo.get(pkginfo_ref) - pkginfo = plistlib.readPlistFromString(data) - except IOError, err: + pkginfo = readPlistFromString(data) + except IOError as err: errors.append("IO error for %s: %s" % (pkginfo_ref, err)) continue - except BaseException, err: + except BaseException as err: errors.append("Unexpected error for %s: %s" % (pkginfo_ref, err)) continue @@ -204,7 +206,7 @@ def process_pkgsinfo(repo, options, output_fn=None): del pkginfo['notes'] # strip out any keys that start with "_" # (example: pkginfo _metadata) - for key in pkginfo.keys(): + for key in list(pkginfo.keys()): if key.startswith('_'): del pkginfo[key] @@ -263,7 +265,7 @@ def makecatalogs(repo, options, output_fn=None): except munkirepo.RepoError: catalog_list = [] for catalog_name in catalog_list: - if catalog_name not in catalogs.keys(): + if catalog_name not in list(catalogs.keys()): catalog_ref = os.path.join('catalogs', catalog_name) try: repo.delete(catalog_ref) @@ -273,28 +275,28 @@ def makecatalogs(repo, options, output_fn=None): # write the new catalogs for key in catalogs: catalogpath = os.path.join("catalogs", key) - if len(catalogs[key]): - catalog_data = plistlib.writePlistToString(catalogs[key]) + if catalogs[key] != "": + catalog_data = writePlistToString(catalogs[key]) try: repo.put(catalogpath, catalog_data) if output_fn: output_fn("Created %s..." % catalogpath) - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: errors.append( - u'Failed to create catalog %s: %s' % (key, unicode(err))) + u'Failed to create catalog %s: %s' % (key, err)) else: errors.append( "WARNING: Did not create catalog %s because it is empty" % key) if icons: icon_hashes_plist = os.path.join("icons", "_icon_hashes.plist") - icon_hashes = plistlib.writePlistToString(icons) + icon_hashes = writePlistToString(icons) try: repo.put(icon_hashes_plist, icon_hashes) - print "Created %s..." % (icon_hashes_plist) - except munkirepo.RepoError, err: + print("Created %s..." % (icon_hashes_plist)) + except munkirepo.RepoError as err: errors.append( - u'Failed to create %s: %s' % (icon_hashes_plist, unicode(err))) + u'Failed to create %s: %s' % (icon_hashes_plist, err)) - # Return and errors + # Return any errors return errors diff --git a/code/client/munkilib/admin/munkiimportlib.py b/code/client/munkilib/admin/munkiimportlib.py index 7c9d8eaf..e9a8b033 100644 --- a/code/client/munkilib/admin/munkiimportlib.py +++ b/code/client/munkilib/admin/munkiimportlib.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ munkiimportlib Created by Greg Neagle on 2017-11-18. Routines used by munkimport to import items into Munki repo """ +from __future__ import absolute_import, print_function # std lib imports import os @@ -62,9 +63,8 @@ def copy_item_to_repo(repo, itempath, vers, subdirectory=''): index = 0 try: pkgs_list = list_items_of_kind(repo, 'pkgs') - except munkirepo.RepoError, err: - raise RepoCopyError(u'Unable to get list of current pkgs: %s' - % unicode(err)) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to get list of current pkgs: %s' % err) while destination_path_name in pkgs_list: #print 'File %s already exists...' % destination_path_name # try appending numbers until we have a unique name @@ -74,9 +74,9 @@ def copy_item_to_repo(repo, itempath, vers, subdirectory=''): try: repo.put_from_local_file(destination_path_name, itempath) - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise RepoCopyError(u'Unable to copy %s to %s: %s' - % (itempath, destination_path_name, unicode(err))) + % (itempath, destination_path_name, err)) else: return destination_path_name @@ -95,9 +95,8 @@ def copy_pkginfo_to_repo(repo, pkginfo, subdirectory=''): index = 0 try: pkgsinfo_list = list_items_of_kind(repo, 'pkgsinfo') - except munkirepo.RepoError, err: - raise RepoCopyError(u'Unable to get list of current pkgsinfo: %s' - % unicode(err)) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to get list of current pkgsinfo: %s' % err) while pkginfo_path in pkgsinfo_list: index += 1 pkginfo_name = '%s-%s__%s%s' % (pkginfo['name'], pkginfo['version'], @@ -106,14 +105,14 @@ def copy_pkginfo_to_repo(repo, pkginfo, subdirectory=''): try: pkginfo_str = FoundationPlist.writePlistToString(pkginfo) - except FoundationPlist.NSPropertyListWriteException, errmsg: + except FoundationPlist.NSPropertyListWriteException as errmsg: raise RepoCopyError(errmsg) try: repo.put(pkginfo_path, pkginfo_str) return pkginfo_path - except munkirepo.RepoError, err: - raise RepoCopyError('Unable to save pkginfo to %s: %s' - % (pkginfo_path, unicode(err))) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to save pkginfo to %s: %s' + % (pkginfo_path, err)) class CatalogDBException(Exception): @@ -136,12 +135,12 @@ def make_catalog_db(repo): try: plist = repo.get('catalogs/all') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise CatalogReadException(err) try: catalogitems = FoundationPlist.readPlistFromString(plist) - except FoundationPlist.NSPropertyListSerializationException, err: + except FoundationPlist.NSPropertyListSerializationException as err: raise CatalogDecodeException(err) pkgid_table = {} @@ -157,7 +156,7 @@ def make_catalog_db(repo): vers = item.get('version', 'NO VERSION') if name == 'NO NAME' or vers == 'NO VERSION': - print >> sys.stderr, 'WARNING: Bad pkginfo: %s' % item + print('WARNING: Bad pkginfo: %s' % item, file=sys.stderr) # add to hash table if 'installer_item_hash' in item: @@ -191,8 +190,8 @@ def make_catalog_db(repo): pkgid_table[pkgid][pkgvers] = [] pkgid_table[pkgid][pkgvers].append(itemindex) except TypeError: - print >> sys.stderr, ( - 'Bad receipt data for %s-%s: %s' % (name, vers, receipt)) + print('Bad receipt data for %s-%s: %s' % (name, vers, receipt), + file=sys.stderr) # add to table of installed applications for install in item.get('installs', []): @@ -205,8 +204,8 @@ def make_catalog_db(repo): app_table[install['path']][vers] = [] app_table[install['path']][vers].append(itemindex) except TypeError: - print >> sys.stderr, ( - 'Bad install data for %s-%s: %s' % (name, vers, install)) + print('Bad install data for %s-%s: %s' % (name, vers, install), + file=sys.stderr) # add to table of PayloadIdentifiers if 'PayloadIdentifier' in item: @@ -231,27 +230,22 @@ def find_matching_pkginfo(repo, pkginfo): """Looks through repo catalogs looking for matching pkginfo Returns a pkginfo dictionary, or an empty dict""" - def compare_version_keys(value_a, value_b): - """Internal comparison function for use in sorting""" - return cmp(pkgutils.MunkiLooseVersion(value_b), - pkgutils.MunkiLooseVersion(value_a)) - try: catdb = make_catalog_db(repo) - except CatalogReadException, err: + except CatalogReadException as err: # could not retrieve catalogs/all # do we have any existing pkgsinfo items? pkgsinfo_items = repo.itemlist('pkgsinfo') - if len(pkgsinfo_items): + if pkgsinfo_items: # there _are_ existing pkgsinfo items. # warn about the problem since we can't seem to read catalogs/all - print (u'Could not get a list of existing items from the repo: %s' - % unicode(err)) + print(u'Could not get a list of existing items from the repo: %s' + % err) return {} - except CatalogDBException, err: + except CatalogDBException as err: # other error while processing catalogs/all print (u'Could not get a list of existing items from the repo: %s' - % unicode(err)) + % err) return {} if 'installer_item_hash' in pkginfo: @@ -267,8 +261,8 @@ def find_matching_pkginfo(repo, pkginfo): if pkgids: possiblematches = catdb['receipts'].get(pkgids[0]) if possiblematches: - versionlist = possiblematches.keys() - versionlist.sort(compare_version_keys) + versionlist = list(possiblematches.keys()) + versionlist.sort(key=pkgutils.MunkiLooseVersion, reverse=True) # go through possible matches, newest version first for versionkey in versionlist: testpkgindexes = possiblematches[versionkey] @@ -288,8 +282,8 @@ def find_matching_pkginfo(repo, pkginfo): app = applist[0]['path'] possiblematches = catdb['applications'].get(app) if possiblematches: - versionlist = possiblematches.keys() - versionlist.sort(compare_version_keys) + versionlist = list(possiblematches.keys()) + versionlist.sort(key=pkgutils.MunkiLooseVersion, reverse=True) indexes = catdb['applications'][app][versionlist[0]] return catdb['items'][indexes[0]] @@ -297,8 +291,8 @@ def find_matching_pkginfo(repo, pkginfo): identifier = pkginfo['PayloadIdentifier'] possiblematches = catdb['profiles'].get(identifier) if possiblematches: - versionlist = possiblematches.keys() - versionlist.sort(compare_version_keys) + versionlist = list(possiblematches.keys()) + versionlist.sort(key=pkgutils.MunkiLooseVersion, reverse=True) indexes = catdb['profiles'][identifier][versionlist[0]] return catdb['items'][indexes[0]] @@ -308,8 +302,8 @@ def find_matching_pkginfo(repo, pkginfo): pkginfo.get('installer_item_location', '')) possiblematches = catdb['installer_items'].get(installer_item_name) if possiblematches: - versionlist = possiblematches.keys() - versionlist.sort(compare_version_keys) + versionlist = list(possiblematches.keys()) + versionlist.sort(key=pkgutils.MunkiLooseVersion, reverse=True) indexes = catdb['installer_items'][installer_item_name][versionlist[0]] return catdb['items'][indexes[0]] @@ -330,9 +324,8 @@ def icon_exists_in_repo(repo, pkginfo): icon_path = get_icon_path(pkginfo) try: icon_list = list_items_of_kind(repo, 'icons') - except munkirepo.RepoError, err: - raise RepoCopyError(u'Unable to get list of current icons: %s' - % unicode(err)) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to get list of current icons: %s' % err) if icon_path in icon_list: return True return False @@ -363,7 +356,7 @@ def generate_png_from_startosinstall_item(repo, dmg_path, pkginfo): dmgutils.unmountdmg(mountpoint) raise dmgutils.unmountdmg(mountpoint) - return None + return None def generate_png_from_dmg_item(repo, dmg_path, pkginfo): @@ -374,7 +367,7 @@ def generate_png_from_dmg_item(repo, dmg_path, pkginfo): mountpoint = mountpoints[0] apps = [item for item in pkginfo.get('items_to_copy', []) if item.get('source_item', '').endswith('.app')] - if len(apps): + if apps: app_path = os.path.join(mountpoint, apps[0]['source_item']) icon_path = iconutils.findIconForApp(app_path) if icon_path: @@ -432,8 +425,7 @@ def generate_pngs_from_pkg(repo, item_path, pkginfo, import_multiple=True): imported_paths.append(imported_path) index += 1 return "\n\t".join(imported_paths) - else: - return None + return None def convert_and_install_icon(repo, pkginfo, icon_path, index=None): @@ -453,9 +445,9 @@ def convert_and_install_icon(repo, pkginfo, icon_path, index=None): try: repo.put_from_local_file(repo_png_path, local_png_tmp) return repo_png_path - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: raise RepoCopyError(u'Error uploading icon to %s: %s' - % (repo_png_path, unicode(err))) + % (repo_png_path, err)) else: raise RepoCopyError(u'Error converting %s to png.' % icon_path) @@ -468,23 +460,22 @@ def copy_icon_to_repo(repo, iconpath): try: icon_list = list_items_of_kind(repo, 'icons') - except munkirepo.RepoError, err: - raise RepoCopyError(u'Unable to get list of current icons: %s' - % unicode(err)) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to get list of current icons: %s' % err) if destination_path_name in icon_list: # remove any existing icon in the repo try: repo.delete(destination_path_name) - except munkirepo.RepoError, err: - raise RepoCopyError('Could not remove existing %s: %s' - % (destination_path_name, unicode(err))) - print 'Copying %s to %s...' % (icon_name, destination_path_name) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Could not remove existing %s: %s' + % (destination_path_name, err)) + print(u'Copying %s to %s...' % (icon_name, destination_path_name)) try: repo.put_from_local_file(destination_path_name, iconpath) return destination_path_name - except munkirepo.RepoError, err: - raise RepoCopyError('Unable to copy %s to %s: %s' - % (iconpath, destination_path_name, unicode(err))) + except munkirepo.RepoError as err: + raise RepoCopyError(u'Unable to copy %s to %s: %s' + % (iconpath, destination_path_name, err)) def extract_and_copy_icon(repo, installer_item, pkginfo, import_multiple=True): diff --git a/code/client/munkilib/admin/pkginfolib.py b/code/client/munkilib/admin/pkginfolib.py index a99ccfd0..20eaddf8 100755 --- a/code/client/munkilib/admin/pkginfolib.py +++ b/code/client/munkilib/admin/pkginfolib.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ pkginfolib Created by Greg Neagle on 2017-11-18. Routines used by makepkginfo to create pkginfo files """ +from __future__ import absolute_import, division, print_function # standard libs import optparse @@ -193,10 +194,9 @@ def get_catalog_info_from_dmg(dmgpath, options): install_macos_app = osinstaller.find_install_macos_app(mountpoints[0]) if (install_macos_app and options.print_warnings and osinstaller.install_macos_app_is_stub(install_macos_app)): - print >> sys.stderr, ( - 'WARNING: %s appears to be an Install macOS application, but ' - 'it does not contain Contents/SharedSupport/InstallESD.dmg' - % os.path.basename(install_macos_app)) + print('WARNING: %s appears to be an Install macOS application, but ' + 'it does not contain Contents/SharedSupport/InstallESD.dmg' + % os.path.basename(install_macos_app), file=sys.stderr) cataloginfo = osinstaller.get_catalog_info(mountpoints[0]) if not cataloginfo: @@ -297,7 +297,7 @@ def readfile(path): fileobject.close() return data except (OSError, IOError): - print >> sys.stderr, "Couldn't read %s" % path + print("Couldn't read %s" % path, file=sys.stderr) return "" @@ -393,7 +393,11 @@ def makepkginfo(installeritem, options): pkginfo = {} installs = [] - if installeritem and os.path.exists(installeritem): + if installeritem: + if not os.path.exists(installeritem): + raise PkgInfoGenerationError( + "File %s does not exist" % installeritem) + # Check if the item is a mount point for a disk image if dmgutils.pathIsVolumeMountPoint(installeritem): # Get the disk image path for the mount point @@ -405,16 +409,18 @@ def makepkginfo(installeritem, options): itemhash = "N/A" if os.path.isfile(installeritem): itemsize = int(os.path.getsize(installeritem)) - itemhash = munkihash.getsha256hash(installeritem) + try: + itemhash = munkihash.getsha256hash(installeritem) + except OSError as err: + raise PkgInfoGenerationError(err) if pkgutils.hasValidDiskImageExt(installeritem): if dmgutils.DMGisWritable(installeritem) and options.print_warnings: - print >> sys.stderr, ( - "WARNING: %s is a writable disk image. " - "Checksum verification is not supported." % installeritem) - print >> sys.stderr, ( - "WARNING: Consider converting %s to a read-only disk" - "image." % installeritem) + print("WARNING: %s is a writable disk image. " + "Checksum verification is not supported." % installeritem, + file=sys.stderr) + print("WARNING: Consider converting %s to a read-only disk" + "image." % installeritem, file=sys.stderr) itemhash = "N/A" pkginfo = get_catalog_info_from_dmg(installeritem, options) if (pkginfo and @@ -437,10 +443,9 @@ def makepkginfo(installeritem, options): "%s doesn't appear to be a valid installer item!" % installeritem) if os.path.isdir(installeritem) and options.print_warnings: - print >> sys.stderr, ( - "WARNING: %s is a bundle-style package!\n" - "To use it with Munki, you should encapsulate it " - "in a disk image.\n") % installeritem + print("WARNING: %s is a bundle-style package!\n" + "To use it with Munki, you should encapsulate it " + "in a disk image.\n" % installeritem, file=sys.stderr) # need to walk the dir and add it all up for (path, dummy_dirs, files) in os.walk(installeritem): for name in files: @@ -453,8 +458,8 @@ def makepkginfo(installeritem, options): elif pkgutils.hasValidConfigProfileExt(installeritem): try: pkginfo = get_catalog_info_for_profile(installeritem) - except ProfileMetadataGenerationError, err: - print >> sys.stderr, err + except ProfileMetadataGenerationError as err: + print(err, file=sys.stderr) raise PkgInfoGenerationError( "%s doesn't appear to be a supported configuration " "profile!" % installeritem) @@ -484,11 +489,10 @@ def makepkginfo(installeritem, options): # ADOBE STUFF - though maybe generalizable in the future? if (pkginfo.get('installer_type') == "AdobeCCPInstaller" and not options.uninstalleritem) and options.print_warnings: - print >> sys.stderr, ( - "WARNING: This item appears to be an Adobe Creative " - "Cloud product install.\n" - "No uninstaller package was specified so product " - "removal will not be possible.") + print("WARNING: This item appears to be an Adobe Creative " + "Cloud product install.\n" + "No uninstaller package was specified so product " + "removal will not be possible.", file=sys.stderr) pkginfo['uninstallable'] = False if 'uninstall_method' in pkginfo: del pkginfo['uninstall_method'] @@ -562,8 +566,8 @@ def makepkginfo(installeritem, options): if fitem.startswith('/Library/Receipts'): # no receipts, please! if options.print_warnings: - print >> sys.stderr, ( - "Item %s appears to be a receipt. Skipping." % fitem) + print("Item %s appears to be a receipt. Skipping." % fitem, + file=sys.stderr) continue if os.path.exists(fitem): iteminfodict = getiteminfo(fitem) @@ -575,8 +579,8 @@ def makepkginfo(installeritem, options): maxfileversion = thisitemversion installs.append(iteminfodict) elif options.print_warnings: - print >> sys.stderr, ( - "Item %s doesn't exist. Skipping." % fitem) + print("Item %s doesn't exist. Skipping." % fitem, + file=sys.stderr) if installs: pkginfo['installs'] = installs diff --git a/code/client/munkilib/adobeutils/__init__.py b/code/client/munkilib/adobeutils/__init__.py index a8ce586c..632a7cfc 100644 --- a/code/client/munkilib/adobeutils/__init__.py +++ b/code/client/munkilib/adobeutils/__init__.py @@ -1 +1,6 @@ -from .core import * \ No newline at end of file +'''Make all .core names available as adobeutils.foo''' +from __future__ import absolute_import + +# pylint: disable=wildcard-import +from .core import * +# pylint: enable=wildcard-import diff --git a/code/client/munkilib/adobeutils/adobeinfo.py b/code/client/munkilib/adobeutils/adobeinfo.py index 09c100cd..04f03698 100644 --- a/code/client/munkilib/adobeutils/adobeinfo.py +++ b/code/client/munkilib/adobeutils/adobeinfo.py @@ -1,5 +1,5 @@ # encoding: utf-8 -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-06. Utilities to get info about Adobe installers/uninstallers """ +from __future__ import absolute_import, print_function import os import json @@ -134,7 +135,7 @@ def get_payload_info(dirpath): properties = installer_properties[0].getElementsByTagName( 'Property') for prop in properties: - if 'name' in prop.attributes.keys(): + if 'name' in list(prop.attributes.keys()): propname = prop.attributes['name'].value.encode('UTF-8') propvalue = '' for node in prop.childNodes: @@ -155,7 +156,7 @@ def get_payload_info(dirpath): installsize = '' for node in totalsizes[0].childNodes: installsize += node.nodeValue - payloadinfo['installed_size'] = int(installsize)/1024 + payloadinfo['installed_size'] = int(int(installsize)/1024) return payloadinfo @@ -178,7 +179,7 @@ def get_adobe_setup_info(installroot): drivers = dom.getElementsByTagName('Driver') if drivers: driver = drivers[0] - if 'folder' in driver.attributes.keys(): + if 'folder' in list(driver.attributes.keys()): driverfolder = driver.attributes[ 'folder'].value.encode('UTF-8') if driverfolder == '': @@ -303,9 +304,9 @@ def parse_option_xml(option_xml_file): dom = minidom.parse(option_xml_file) installinfo = dom.getElementsByTagName('InstallInfo') if installinfo: - if 'id' in installinfo[0].attributes.keys(): + if 'id' in list(installinfo[0].attributes.keys()): info['packager_id'] = installinfo[0].attributes['id'].value - if 'version' in installinfo[0].attributes.keys(): + if 'version' in list(installinfo[0].attributes.keys()): info['packager_version'] = installinfo[ 0].attributes['version'].value info['package_name'] = get_xml_text_element( @@ -588,18 +589,18 @@ def getAdobeCatalogInfo(mountpoint, pkgname=""): # Because InDesign CC 2017 is not like any other package # and contains a 'Condition' key but as an empty # string, we explicitly test this case as well. - if ('Condition' not in package.keys() or + if ('Condition' not in list(package.keys()) or package.get('Condition') == '' or '[installLanguage]==en_US' in package.get('Condition', '')): - installed_size += package.get( - 'ExtractSize', 0) / 1024 + installed_size += int(package.get( + 'ExtractSize', 0) / 1024) # We get much closer to Adobe's "HDSetup" calculated # install space requirement if we include both the # DownloadSize and ExtractSize data # (DownloadSize is just the zip file size) - installed_size += package.get( - 'DownloadSize', 0) / 1024 + installed_size += int(package.get( + 'DownloadSize', 0) / 1024) # Add another 300MB for the CC app and plumbing in case they've # never been installed on the system installed_size += 307200 @@ -784,4 +785,4 @@ def getAdobeCatalogInfo(mountpoint, pkgname=""): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/adobeutils/core.py b/code/client/munkilib/adobeutils/core.py index d2e8e71e..d6dbff4e 100644 --- a/code/client/munkilib/adobeutils/core.py +++ b/code/client/munkilib/adobeutils/core.py @@ -1,5 +1,5 @@ # encoding: utf-8 -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ Utilities to enable Munki to install/uninstall Adobe CS3/CS4/CS5 products using the CS3/CS4/CS5 Deployment Toolkits. """ +from __future__ import absolute_import, print_function import os @@ -67,8 +68,8 @@ def rotate_pdapp_log(): newlogname = os.path.join(logdir, 'PDApp %s.log' % alternate_string) try: os.rename(pdapplog_path, newlogname) - except OSError, err: - munkilog.log('Could not rotate PDApp.log: %s', unicode(err)) + except OSError as err: + munkilog.log(u'Could not rotate PDApp.log: %s' % err) class AdobeInstallProgressMonitor(object): @@ -96,9 +97,9 @@ class AdobeInstallProgressMonitor(object): proc = subprocess.Popen(['/bin/ls', '-t1', logpath], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if output: - firstitem = str(output).splitlines()[0] + firstitem = output.splitlines()[0] if firstitem.endswith(".log"): # store path of most recently modified log file recent_adobe_log = os.path.join(logpath, firstitem) @@ -143,9 +144,9 @@ class AdobeInstallProgressMonitor(object): proc = subprocess.Popen(cmd, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if output: - lines = str(output).splitlines() + lines = output.splitlines() completed_payloads = len(lines) if (logfile not in self.payload_count @@ -304,7 +305,7 @@ def run_adobe_install_tool( #check output for errors output = proc.stdout.readlines() for line in output: - line = line.rstrip("\n") + line = line.decode("UTF-8").rstrip("\n") if line.startswith("Error"): display.display_error(line) if line.startswith("Exit Code:"): @@ -386,7 +387,7 @@ def writefile(stringdata, path): Returns the path on success, empty string on failure.''' try: fileobject = open(path, mode='w', buffering=1) - print >> fileobject, stringdata.encode('UTF-8') + print(stringdata.encode('UTF-8'), file=fileobject) fileobject.close() return path except (OSError, IOError): @@ -759,7 +760,8 @@ def update_acrobatpro(dmgpath): if item['path'].endswith('/' + appname)] # hope there's only one! - if len(candidates) == 0: + if not candidates: + # there are no candidates! if status == "optional": continue else: @@ -924,4 +926,4 @@ def do_adobe_install(item): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/app_usage.py b/code/client/munkilib/app_usage.py index e1cc03c3..e554b888 100644 --- a/code/client/munkilib/app_usage.py +++ b/code/client/munkilib/app_usage.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ Much code lifted from the application_usage scripts created by Google MacOps: https://github.com/google/macops/tree/master/crankd """ +from __future__ import absolute_import, print_function # standard Python libs import logging @@ -158,7 +159,7 @@ class ApplicationUsageRecorder(object): try: conn.execute(table_detection_sql) exists = True - except sqlite3.OperationalError, err: + except sqlite3.OperationalError as err: if err.args[0].startswith('no such table'): exists = False else: @@ -282,7 +283,7 @@ class ApplicationUsageRecorder(object): for table in tables: query = conn.execute(table['select_sql']) try: - while 1: + while True: row = query.fetchone() if not row: break @@ -291,7 +292,7 @@ class ApplicationUsageRecorder(object): pass # ok, done, hit an error conn.close() - except sqlite3.Error, err: + except sqlite3.Error as err: logging.error('Unhandled error reading existing db: %s', str(err)) return recovered @@ -310,13 +311,13 @@ class ApplicationUsageRecorder(object): conn.execute(table['insert_sql'], row) conn.commit() recovered += 1 - except sqlite3.IntegrityError, err: + except sqlite3.IntegrityError as err: logging.error( 'Ignored error: %s: %s', str(err), str(row)) self._close(conn) os.unlink(APPLICATION_USAGE_DB) os.rename(usage_db_tmp, APPLICATION_USAGE_DB) - except sqlite3.Error, err: + except sqlite3.Error as err: logging.error('Unhandled error: %s', str(err)) recovered = 0 @@ -365,9 +366,9 @@ class ApplicationUsageRecorder(object): self._create_application_usage_table(conn) self._insert_application_usage(conn, event, app_dict) conn.commit() - except sqlite3.OperationalError, err: + except sqlite3.OperationalError as err: logging.error('Error writing %s event to database: %s', event, err) - except sqlite3.DatabaseError, err: + except sqlite3.DatabaseError as err: if err.args[0] == 'database disk image is malformed': self._recreate_database() logging.error('Database error: %s', err) @@ -398,9 +399,9 @@ class ApplicationUsageRecorder(object): self._create_install_request_table(conn) self._insert_install_request(conn, request_dict) conn.commit() - except sqlite3.OperationalError, err: + except sqlite3.OperationalError as err: logging.error('Error writing install request to database: %s', err) - except sqlite3.DatabaseError, err: + except sqlite3.DatabaseError as err: if err.args[0] == 'database is malformed': self._recreate_database() logging.error('Database error: %s', err) @@ -417,7 +418,7 @@ class ApplicationUsageQuery(object): self.day_in_seconds = 24 * 60 * 60 try: self.conn = sqlite3.connect(self.database) - except sqlite3.Error, err: + except sqlite3.Error as err: logging.error( 'Error connecting to %s: %s', self.database, str(err)) self.conn = None @@ -445,7 +446,7 @@ class ApplicationUsageQuery(object): row = query.fetchone() time_diff = int(time.time()) - int(row[0]) return int(time_diff/self.day_in_seconds) - except sqlite3.Error, err: + except sqlite3.Error as err: logging.error( 'Error querying %s: %s', self.database, str(err)) return 0 @@ -471,9 +472,9 @@ class ApplicationUsageQuery(object): if row: time_diff = int(time.time()) - int(row[0]) return int(time_diff/self.day_in_seconds) - else: - return -1 - except sqlite3.Error, err: + # no row + return -1 + except sqlite3.Error as err: logging.error( 'Error querying %s: %s', self.database, str(err)) return None @@ -499,13 +500,13 @@ class ApplicationUsageQuery(object): if row: time_diff = int(time.time()) - int(row[0]) return int(time_diff/self.day_in_seconds) - else: - return -1 - except sqlite3.Error, err: + # no row + return -1 + except sqlite3.Error as err: logging.error( 'Error querying %s: %s', self.database, str(err)) return None if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/appleupdates/__init__.py b/code/client/munkilib/appleupdates/__init__.py index a8ce586c..9abfb14c 100644 --- a/code/client/munkilib/appleupdates/__init__.py +++ b/code/client/munkilib/appleupdates/__init__.py @@ -1 +1,6 @@ -from .core import * \ No newline at end of file +'''Make names from .core available appleupdates.foo''' +from __future__ import absolute_import + +# pylint: disable=wildcard-import +from .core import * +# pylint: enable=wildcard-import diff --git a/code/client/munkilib/appleupdates/au.py b/code/client/munkilib/appleupdates/au.py index 5387a33c..0b4c09d1 100644 --- a/code/client/munkilib/appleupdates/au.py +++ b/code/client/munkilib/appleupdates/au.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -14,19 +14,25 @@ # See the License for the specific language governing permissions and # limitations under the License. """ -appleupdates.au +appleupdates.py Created by Greg Neagle on 2017-01-06. AppleUpdates object defined here """ +from __future__ import absolute_import, print_function import glob import hashlib import os import subprocess -import time -import urllib2 + +try: + # Python 2 + from urllib2 import quote +except ImportError: + # Python 3 + from urllib.parse import quote # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. @@ -36,6 +42,7 @@ from Foundation import NSDate from . import dist from . import su_prefs +from . import su_tool from . import sync from ..updatecheck import catalogs @@ -43,7 +50,6 @@ from ..constants import POSTACTION_NONE, POSTACTION_RESTART, POSTACTION_SHUTDOWN from .. import display from .. import fetch -from .. import launchd from .. import munkistatus from .. import munkihash from .. import munkilog @@ -55,8 +61,6 @@ from .. import updatecheck from .. import FoundationPlist -# Apple's index of downloaded updates -INDEX_PLIST = '/Library/Updates/index.plist' INSTALLHISTORY_PLIST = '/Library/Receipts/InstallHistory.plist' @@ -93,7 +97,10 @@ class AppleUpdates(object): # fix things if somehow we died last time before resetting the # original CatalogURL os_version_tuple = osutils.getOsVersion(as_tuple=True) - if os_version_tuple in [(10, 9), (10, 10)]: + if os_version_tuple < (10, 10): + display.display_warning( + 'macOS versions below 10.10 are no longer supported') + if os_version_tuple == (10, 10): su_prefs.reset_original_catalogurl() self.applesync = sync.AppleUpdateSync() @@ -105,15 +112,6 @@ class AppleUpdates(object): self.client_id = '' self.force_catalog_refresh = False - def _display_status_major(self, message): - """Resets MunkiStatus detail/percent, logs and msgs GUI. - - Args: - message: str message to display to the user and log. - """ - # pylint: disable=no-self-use - display.display_status_major(message) - def restart_action_for_updates(self): """Returns the most heavily weighted postaction""" try: @@ -147,16 +145,7 @@ class AppleUpdates(object): Boolean. True if successful, False otherwise. """ msg = 'Checking for available Apple Software Updates...' - self._display_status_major(msg) - - if os.path.exists(INDEX_PLIST): - # try to remove old/stale /Library/Updates/index.plist -- - # in some older versions of OS X this can hang around and is not - # always cleaned up when /usr/sbin/softwareupdate finds no updates - try: - os.unlink(INDEX_PLIST) - except OSError: - pass + display.display_status_major(msg) os_version_tuple = osutils.getOsVersion(as_tuple=True) if os_version_tuple >= (10, 11): @@ -167,70 +156,65 @@ class AppleUpdates(object): # before we call softwareupdate, # clear stored value for LastSessionSuccessful su_prefs.set_pref('LastSessionSuccessful', None) - retcode = self._run_softwareupdate( + results = su_tool.run( ['-d', '-a'], catalog_url=catalog_url, stop_allowed=True) + retcode = results.get('exit_code', 0) if retcode: # there was an error display.display_error('softwareupdate error: %s', retcode) return False # not sure all older OS X versions set LastSessionSuccessful, so # react only if it's explicitly set to False - last_session_successful = su_prefs.pref( - 'LastSessionSuccessful') + last_session_successful = su_prefs.pref('LastSessionSuccessful') if last_session_successful is False: display.display_error( 'softwareupdate reported an unsuccessful download session.') return False return True + def get_filtered_recommendedupdates(self): + """Returns the list of RecommendedUpdates from com.apple.SoftwareUpdate + preferences, filtered by the output of `softwareupdate -l`""" + # pylint: disable=no-self-use + os_version_tuple = osutils.getOsVersion(as_tuple=True) + if os_version_tuple < (10, 10): + display.display_warning( + 'macOS versions below 10.10 are no longer supported') + recommended_updates = su_prefs.pref('RecommendedUpdates') or [] + if os_version_tuple < (10, 11): + # --no-scan flag is not supported; just return the + # RecommendedUpdates without filtering. We never saw the issues + # that this filtering is meant to address in 10.10 anyway! + return recommended_updates + su_results = su_tool.run(['-l', '--no-scan']) + filtered_updates = [] + for item in su_results.get('updates', []): + for update in recommended_updates: + if (item.get('identifier') == update.get('Identifier') and + item.get('version') == update.get('Display Version')): + # add update to filtered_updates only if it is also listed + # in `softwareupdate -l` output + filtered_updates.append(update) + return filtered_updates + def available_update_product_ids(self): """Returns a list of product IDs of available Apple updates. Returns: - A list of string Apple update products ids. + A list of string Apple update product ids. """ - # pylint: disable=no-self-use - # first, try to get the list from com.apple.SoftwareUpdate preferences - recommended_updates = su_prefs.pref( - 'RecommendedUpdates') - if recommended_updates is not None: - try: - return [item['Product Key'] for item in recommended_updates - if 'Product Key' in item] - except IndexError: - # RecommendedUpdates is an empty list - return [] - except (KeyError, AttributeError): - # RecommendedUpdates is in an unexpected format - display.display_debug1( - 'com.apple.SoftwareUpdate RecommendedUpdates is in an ' - 'unexpected format: %s', recommended_updates) - return [] - - os_version_tuple = osutils.getOsVersion(as_tuple=True) - # We've collected data that indicates that com.apple.SoftwareUpdate - # RecommendedUpdates should be present on 10.9+ if there are available - # updates. We don't have data on 10.7 or 10.8. - if os_version_tuple < (10, 8): - display.display_debug1( - 'com.apple.SoftwareUpdate RecommendedUpdates is not defined') - return [] - - # fall through to using index.plist only if com.apple.SoftwareUpdate - # RecommendedUpdates doesn't exist and we're on macOS < 10.9 - if not os.path.exists(INDEX_PLIST): - display.display_debug1('%s does not exist.' % INDEX_PLIST) - return [] - + recommended_updates = self.get_filtered_recommendedupdates() try: - product_index = FoundationPlist.readPlist(INDEX_PLIST) - products = product_index.get('ProductPaths', {}) - return products.keys() - except (FoundationPlist.FoundationPlistException, - KeyError, AttributeError), err: - display.display_error( - "Error processing %s: %s", INDEX_PLIST, err) - return [] - + return [item['Product Key'] for item in recommended_updates + if 'Product Key' in item] + except IndexError: + # RecommendedUpdates is an empty list + pass + except (KeyError, AttributeError): + # RecommendedUpdates is in an unexpected format + display.display_debug1( + 'com.apple.SoftwareUpdate RecommendedUpdates is in an ' + 'unexpected format: %s', recommended_updates) + return [] def installed_apple_pkgs_changed(self): """Generates a SHA-256 checksum of the info for all packages in the @@ -245,7 +229,7 @@ class AppleUpdates(object): proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, dummy_err = proc.communicate() + output = proc.communicate()[0] # don't decode because we need the bytes current_apple_packages_checksum = hashlib.sha256(output).hexdigest() old_apple_packages_checksum = prefs.pref( @@ -253,10 +237,10 @@ class AppleUpdates(object): if current_apple_packages_checksum == old_apple_packages_checksum: return False - else: - prefs.set_pref('InstalledApplePackagesChecksum', - current_apple_packages_checksum) - return True + + prefs.set_pref('InstalledApplePackagesChecksum', + current_apple_packages_checksum) + return True def _force_check_necessary(self, original_hash): """Returns True if a force check is needed, False otherwise. @@ -297,7 +281,7 @@ class AppleUpdates(object): self.applesync.apple_download_catalog_path) msg = 'Checking Apple Software Update catalog...' - self._display_status_major(msg) + display.display_status_major(msg) try: self.applesync.cache_apple_catalog() except sync.CatalogNotFoundError: @@ -305,7 +289,7 @@ class AppleUpdates(object): except (sync.ReplicationError, fetch.Error) as err: display.display_warning( 'Could not download Apple SUS catalog:') - display.display_warning('\t%s', unicode(err)) + display.display_warning(u'\t%s', err) return False if not force_check and not self._force_check_necessary(before_hash): @@ -328,7 +312,7 @@ class AppleUpdates(object): except sync.ReplicationError as err: display.display_warning( 'Could not replicate software update metadata:') - display.display_warning('\t%s', unicode(err)) + display.display_warning(u'\t%s', err) return False return True else: @@ -375,10 +359,9 @@ class AppleUpdates(object): return True def software_update_info(self): - """Uses /Library/Preferences/com.apple.SoftwareUpdate.plist or - /Library/Updates/index.plist to generate the AppleUpdates.plist, - which records available updates in the format that - Managed Software Update.app expects. + """Uses /Library/Preferences/com.apple.SoftwareUpdate.plist to generate + the AppleUpdates.plist, which records available updates in the format + that Managed Software Center.app expects. Returns: List of dictionary update data. @@ -390,37 +373,23 @@ class AppleUpdates(object): apple_updates = [] # first, try to get the list from com.apple.SoftwareUpdate preferences - recommended_updates = su_prefs.pref('RecommendedUpdates') - if recommended_updates: - for item in recommended_updates: - try: - update_display_names[item['Product Key']] = ( - item['Display Name']) - except (TypeError, AttributeError, KeyError): - pass - try: - update_versions[item['Product Key']] = ( - item['Display Version']) - except (TypeError, AttributeError, KeyError): - pass + recommended_updates = self.get_filtered_recommendedupdates() + for item in recommended_updates: try: - product_keys = [item['Product Key'] - for item in recommended_updates] + update_display_names[item['Product Key']] = ( + item['Display Name']) except (TypeError, AttributeError, KeyError): pass - - if not product_keys: - # next, try to get the applicable/recommended updates from - # /Library/Updates/index.plist - if os.path.exists(INDEX_PLIST): - try: - product_index = FoundationPlist.readPlist(INDEX_PLIST) - products = product_index.get('ProductPaths', {}) - product_keys = products.keys() - except (FoundationPlist.FoundationPlistException, - AttributeError, TypeError), err: - display.display_error( - "Error parsing %s: %s", INDEX_PLIST, err) + try: + update_versions[item['Product Key']] = ( + item['Display Version']) + except (TypeError, AttributeError, KeyError): + pass + try: + product_keys = [item['Product Key'] + for item in recommended_updates] + except (TypeError, AttributeError, KeyError): + pass for product_key in product_keys: if not self.update_downloaded(product_key): @@ -525,187 +494,6 @@ class AppleUpdates(object): display.display_info(' *Logout required') reports.report['LogoutRequired'] = True - def _run_softwareupdate( - self, options_list, catalog_url=None, stop_allowed=False, - mode=None, results=None): - """Runs /usr/sbin/softwareupdate with options. - - Provides user feedback via command line or MunkiStatus. - - Args: - options_list: sequence of options to send to softwareupdate. - stopped_allowed: - mode: - results: - Returns: - Integer softwareupdate exit code. - """ - if results is None: - # we're not interested in the results, - # but need to create a temporary dict anyway - results = {} - - # we need to wrap our call to /usr/sbin/softwareupdate with a utility - # that makes softwareupdate think it is connected to a tty-like - # device so its output is unbuffered so we can get progress info - # - # Try to find our ptyexec tool - # first look in the parent directory of the parent directory of this - # file's directory - # (../) - parent_dir = ( - os.path.dirname( - os.path.dirname( - os.path.dirname( - os.path.abspath(__file__))))) - ptyexec_path = os.path.join(parent_dir, 'ptyexec') - if not os.path.exists(ptyexec_path): - # try absolute path in munki's normal install dir - ptyexec_path = '/usr/local/munki/ptyexec' - if os.path.exists(ptyexec_path): - cmd = [ptyexec_path] - else: - # fall back to /usr/bin/script - # this is not preferred because it uses way too much CPU - # checking stdin for input that will never come... - cmd = ['/usr/bin/script', '-q', '-t', '1', '/dev/null'] - cmd.extend(['/usr/sbin/softwareupdate', '--verbose']) - - os_version_tuple = osutils.getOsVersion(as_tuple=True) - if catalog_url: - # OS version-specific stuff to use a specific CatalogURL - if os_version_tuple < (10, 9): - cmd.extend(['--CatalogURL', catalog_url]) - elif os_version_tuple in [(10, 9), (10, 10)]: - su_prefs.set_custom_catalogurl(catalog_url) - - cmd.extend(options_list) - - display.display_debug1('softwareupdate cmd: %s', cmd) - - try: - job = launchd.Job(cmd) - job.start() - except launchd.LaunchdJobException as err: - display.display_warning( - 'Error with launchd job (%s): %s', cmd, err) - display.display_warning('Skipping softwareupdate run.') - return -3 - - results['installed'] = [] - results['download'] = [] - results['failures'] = [] - - last_output = None - while True: - if stop_allowed and processes.stop_requested(): - job.stop() - break - - output = job.stdout.readline() - if not output: - if job.returncode() is not None: - break - else: - # no data, but we're still running - # sleep a bit before checking for more output - time.sleep(1) - continue - - # Don't bother parsing the stdout output if it hasn't changed since - # the last loop iteration. - if last_output == output: - continue - last_output = output - - output = output.decode('UTF-8').strip() - # send the output to STDOUT or MunkiStatus as applicable - if output.startswith('Progress: '): - # Snow Leopard/Lion progress info with '-v' flag - try: - percent = int(output[10:].rstrip('%')) - except ValueError: - percent = -1 - display.display_percent_done(percent, 100) - elif output.startswith('Software Update Tool'): - # don't display this - pass - elif output.startswith('Copyright 2'): - # don't display this - pass - elif output.startswith('Installing ') and mode == 'install': - item = output[11:] - if item: - self._display_status_major(output) - elif output.startswith('Downloaded ') and mode == 'install': - # don't display this - pass - elif output.startswith('Installed '): - # 10.6 / 10.7 / 10.8. Successful install of package name. - if mode == 'install': - display.display_status_minor(output) - results['installed'].append(output[10:]) - else: - pass - # don't display. - # softwareupdate logging "Installed" at the end of a - # successful download-only session is odd. - elif output.startswith('Done with ') and mode == 'install': - # 10.9 successful install - display.display_status_minor(output) - results['installed'].append(output[10:]) - elif output.startswith('Done '): - # 10.5. Successful install of package name. - display.display_status_minor(output) - results['installed'].append(output[5:]) - elif output.startswith('Downloading ') and mode == 'install': - # This is 10.5 & 10.7 behavior for a missing subpackage. - display.display_warning( - 'A necessary subpackage is not available on disk ' - 'during an Apple Software Update installation ' - 'run: %s' % output) - results['download'].append(output[12:]) - elif output.startswith('Package failed:'): - # Doesn't tell us which package. - display.display_error( - 'Apple update failed to install: %s' % output) - results['failures'].append(output) - elif output.startswith('x '): - # don't display this, it's just confusing - pass - elif 'Missing bundle identifier' in output: - # don't display this, it's noise - pass - elif (('Please call halt' in output - or 'your computer must shut down' in output) - and not self.shutdown_instead_of_restart): - # This update requires we shutdown instead of a restart. - display.display_status_minor(output) - display.display_info('### This update requires a shutdown. ###') - self.shutdown_instead_of_restart = True - elif output == '': - pass - else: - display.display_status_minor(output) - - if catalog_url: - # reset CatalogURL if needed - if os_version_tuple in [(10, 9), (10, 10)]: - su_prefs.reset_original_catalogurl() - - retcode = job.returncode() - if retcode == 0: - # get SoftwareUpdate's LastResultCode - last_result_code = su_prefs.pref( - 'LastResultCode') or 0 - if last_result_code > 2: - retcode = last_result_code - - if results['failures']: - return 1 - - return retcode - def install_apple_updates(self, only_unattended=False): """Uses softwareupdate to install previously downloaded updates. @@ -730,7 +518,7 @@ class AppleUpdates(object): msg = 'Installing available Apple Software Updates...' restart_action = self.restart_action_for_updates() - self._display_status_major(msg) + display.display_status_major(msg) installlist = self.software_update_info() remaining_apple_updates = [] @@ -784,13 +572,14 @@ class AppleUpdates(object): 'Missing local Software Update catalog at %s', self.applesync.local_catalog_path) return False # didn't do anything, so no restart needed - catalog_url = 'file://localhost' + urllib2.quote( + catalog_url = 'file://localhost' + quote( self.applesync.local_catalog_path) su_start_date = NSDate.new() - retcode = self._run_softwareupdate( - su_options, mode='install', catalog_url=catalog_url, - results=installresults) + installresults = su_tool.run(su_options, catalog_url=catalog_url) + retcode = installresults.get('exit_code', 0) + self.shutdown_instead_of_restart = installresults.get( + 'post_action') == POSTACTION_SHUTDOWN su_end_date = NSDate.new() # get the items that were just installed from InstallHistory.plist @@ -866,7 +655,6 @@ class AppleUpdates(object): # re-write the apple_update_info to match plist = {'AppleUpdates': remaining_apple_updates} FoundationPlist.writePlist(plist, self.apple_updates_plist) - #TODO: clean up cached items we no longer need # Also clear our pref value for last check date. We may have # just installed an update which is a pre-req for some other update. @@ -1021,12 +809,10 @@ class AppleUpdates(object): 'Error reading: %s', self.apple_updates_plist) return item_list, product_ids apple_updates = pl_dict.get('AppleUpdates', []) - os_version_tuple = osutils.getOsVersion(as_tuple=True) for item in apple_updates: if (item.get('unattended_install') or (prefs.pref('UnattendedAppleUpdates') and - item.get('RestartAction', 'None') == 'None' and - os_version_tuple >= (10, 10))): + item.get('RestartAction', 'None') == 'None')): if processes.blocking_applications_running(item): display.display_detail( 'Skipping unattended install of %s because ' @@ -1044,4 +830,4 @@ class AppleUpdates(object): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/appleupdates/core.py b/code/client/munkilib/appleupdates/core.py index 2fed4bbd..c24e6f7b 100644 --- a/code/client/munkilib/appleupdates/core.py +++ b/code/client/munkilib/appleupdates/core.py @@ -1,5 +1,5 @@ # encoding: utf-8 -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ appleupdates.py Utilities for dealing with Apple Software Update. """ +from __future__ import absolute_import, print_function from . import au from . import su_prefs @@ -79,4 +80,4 @@ def displayAppleUpdateInfo(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/appleupdates/dist.py b/code/client/munkilib/appleupdates/dist.py index d3562089..bb7d3359 100644 --- a/code/client/munkilib/appleupdates/dist.py +++ b/code/client/munkilib/appleupdates/dist.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-04. Utilities for working with Apple software update dist files """ +from __future__ import absolute_import, print_function import os from xml.dom import minidom @@ -61,7 +62,7 @@ def get_firmware_alert_text(dom): type_is_firmware = False options = dom.getElementsByTagName('options') for option in options: - if 'type' in option.attributes.keys(): + if 'type' in list(option.attributes.keys()): type_value = option.attributes['type'].value if type_value == 'firmware': type_is_firmware = True @@ -69,9 +70,13 @@ def get_firmware_alert_text(dom): if type_is_firmware: firmware_alert_text = '_DEFAULT_FIRMWARE_ALERT_TEXT_' readmes = dom.getElementsByTagName('readme') - if len(readmes): + if readmes: html = readmes[0].firstChild.data - html_data = buffer(html.encode('utf-8')) + try: + html_data = buffer(html.encode('utf-8')) + except NameError: + # Python 3 + html_data = memoryview(html.encode('utf-8')) attributed_string, _ = NSAttributedString.alloc( ).initWithHTML_documentAttributes_(html_data, None) firmware_alert_text = attributed_string.string() @@ -89,7 +94,7 @@ def get_blocking_apps_from_dom(dom): for item in must_close_items: apps = item.getElementsByTagName('app') for app in apps: - keys = app.attributes.keys() + keys = list(app.attributes.keys()) if 'id' in keys: must_close_app_ids.append(app.attributes['id'].value) @@ -127,7 +132,7 @@ def get_localization_strings(dom): text = strings.firstChild.wholeText # strings data can be parsed by FoundationPlist strings_data = FoundationPlist.readPlistFromString( - "\n" + text) + ("\n" + text).encode("UTF-8")) except (AttributeError, FoundationPlist.FoundationPlistException): strings_data = {} @@ -138,10 +143,10 @@ def populate_pkgs_from_pkg_refs(dom, pkgs): '''Uses pkg-ref elements in the dom to populate data in the pkgs dict''' dom_pkg_refs = dom.getElementsByTagName('pkg-ref') or [] for pkg_ref in dom_pkg_refs: - if not 'id' in pkg_ref.attributes.keys(): + if not 'id' in list(pkg_ref.attributes.keys()): continue pkg_id = pkg_ref.attributes['id'].value - if not pkg_id in pkgs.keys(): + if not pkg_id in list(pkgs.keys()): # this pkg_id was not in our choice list continue if pkg_ref.firstChild: @@ -151,16 +156,16 @@ def populate_pkgs_from_pkg_refs(dom, pkgs): pkgs[pkg_id]['name'] = pkg_name except AttributeError: pass - if 'onConclusion' in pkg_ref.attributes.keys(): + if 'onConclusion' in list(pkg_ref.attributes.keys()): pkgs[pkg_id]['RestartAction'] = ( pkg_ref.attributes['onConclusion'].value) - if 'version' in pkg_ref.attributes.keys(): + if 'version' in list(pkg_ref.attributes.keys()): pkgs[pkg_id]['version'] = ( pkg_ref.attributes['version'].value) - if 'installKBytes' in pkg_ref.attributes.keys(): + if 'installKBytes' in list(pkg_ref.attributes.keys()): pkgs[pkg_id]['installed_size'] = int( pkg_ref.attributes['installKBytes'].value) - if 'packageIdentifier' in pkg_ref.attributes.keys(): + if 'packageIdentifier' in list(pkg_ref.attributes.keys()): pkgs[pkg_id]['packageid'] = ( pkg_ref.attributes['packageIdentifier'].value) @@ -171,10 +176,10 @@ def get_su_choice_and_pkgs(dom): # look for 2: + retcode = last_result_code + + if results['failures']: + retcode = 1 + + results['exit_code'] = retcode + + display.display_debug2('softwareupdate run results: %s', results) + return results diff --git a/code/client/munkilib/appleupdates/sync.py b/code/client/munkilib/appleupdates/sync.py index 8560c208..c1dfd6c3 100644 --- a/code/client/munkilib/appleupdates/sync.py +++ b/code/client/munkilib/appleupdates/sync.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,15 +20,28 @@ Created by Greg Neagle on 2017-01-06. Utilities for replicating and retrieving Apple software update metadata """ +from __future__ import absolute_import, print_function import gzip import os import subprocess import time -import urllib2 -import urlparse import xattr +try: + # Python 2 + from urllib2 import quote, unquote +except ImportError: + # Python 3 + from urllib.parse import quote, unquote +try: + # Python 2 + from urlparse import urlsplit +except ImportError: + # Python 3 + from urllib.parse import urlsplit + + # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. # pylint: disable=E0611 @@ -44,6 +57,7 @@ from .. import osutils from .. import prefs from .. import processes from .. import FoundationPlist +from ..wrappers import unicode_or_str # Apple Software Update Catalog URLs. @@ -71,7 +85,10 @@ DEFAULT_CATALOG_URLS = { '-leopard.merged-1.sucatalog'), '10.14': ('https://swscan.apple.com/content/catalogs/others/' 'index-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-' - 'snowleopard-leopard.merged-1.sucatalog') + 'snowleopard-leopard.merged-1.sucatalog'), + '10.15': ('https://swscan.apple.com/content/catalogs/others/' + 'index-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-' + 'lion-snowleopard-leopard.merged-1.sucatalog') } # Preference domain for Apple Software Update. @@ -236,7 +253,7 @@ class AppleUpdateSync(object): try: catalog_url = self.get_apple_catalogurl() except CatalogNotFoundError as err: - display.display_error(unicode(err)) + display.display_error(unicode_or_str(err)) raise if not os.path.exists(self.temp_cache_dir): try: @@ -244,8 +261,8 @@ class AppleUpdateSync(object): except OSError as oserr: raise ReplicationError(oserr) if os.path.exists(self.apple_download_catalog_path): - stored_os_vers = fetch.getxattr( - self.apple_download_catalog_path, XATTR_OS_VERS) + stored_os_vers = str(fetch.getxattr( + self.apple_download_catalog_path, XATTR_OS_VERS)) if stored_os_vers != os_vers: try: # remove the cached apple catalog @@ -257,8 +274,8 @@ class AppleUpdateSync(object): try: dummy_file_changed = self.get_su_resource( catalog_url, self.apple_download_catalog_path, resume=True) - xattr.setxattr( - self.apple_download_catalog_path, XATTR_OS_VERS, os_vers) + xattr.setxattr(self.apple_download_catalog_path, + XATTR_OS_VERS, os_vers.encode("UTF-8")) self.copy_downloaded_catalog() except fetch.Error: raise @@ -272,7 +289,7 @@ class AppleUpdateSync(object): The str path of the URL. """ # pylint: disable=no-self-use - return urlparse.urlsplit(full_url)[2] # (schema, netloc, path, ...) + return urlsplit(full_url)[2] # (schema, netloc, path, ...) def rewrite_url(self, full_url): """Rewrites a single URL to point to our local replica. @@ -282,8 +299,7 @@ class AppleUpdateSync(object): Returns: A str URL, rewritten if needed to point to the local cache. """ - local_base_url = 'file://localhost' + urllib2.quote( - self.cache_dir) + local_base_url = 'file://localhost' + quote(self.cache_dir) if full_url.startswith(local_base_url): return full_url # url is already local, so just return it. return local_base_url + self._get_url_path(full_url) @@ -307,7 +323,9 @@ class AppleUpdateSync(object): package['MetadataURL'] = self.rewrite_url( package['MetadataURL']) distributions = product['Distributions'] - for dist_lang in distributions.keys(): + # coerce distributions.keys() to list so we don't mutate the dictionary + # while enumerating it in Python 3 + for dist_lang in list(distributions.keys()): distributions[dist_lang] = self.rewrite_url( distributions[dist_lang]) @@ -459,7 +477,7 @@ class AppleUpdateSync(object): prefs.pref('AppleSoftwareUpdateLanguages') or ['English']) preferred_langs = ( NSBundle.preferredLocalizationsFromArray_forPreferences_( - list_of_localizations, localization_preferences)) + list(list_of_localizations), localization_preferences)) if preferred_langs: return preferred_langs[0] @@ -495,7 +513,7 @@ class AppleUpdateSync(object): if product: distributions = product.get('Distributions', {}) if distributions: - available_languages = distributions.keys() + available_languages = list(distributions.keys()) if language: preferred_language = language else: @@ -511,14 +529,14 @@ class AppleUpdateSync(object): # look for it in the cache if url.startswith('file://localhost'): fileurl = url[len('file://localhost'):] - dist_path = urllib2.unquote(fileurl) + dist_path = unquote(fileurl) if os.path.exists(dist_path): return dist_path # we haven't downloaded this yet try: return self.retrieve_url_to_cache_dir( url, copy_only_if_missing=True) - except ReplicationError, err: + except ReplicationError as err: display.display_error( 'Could not retrieve %s: %s', url, err) return None @@ -531,4 +549,4 @@ class AppleUpdateSync(object): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/authrestart/__init__.py b/code/client/munkilib/authrestart/__init__.py index 29689835..4b0ca7a5 100644 --- a/code/client/munkilib/authrestart/__init__.py +++ b/code/client/munkilib/authrestart/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Initial work by Wes Whetstone, Summer/Fall 2016 Functions supporting FileVault authrestart. """ +from __future__ import absolute_import, print_function import subprocess @@ -37,10 +38,11 @@ def filevault_is_active(): active_cmd = ['/usr/bin/fdesetup', 'isactive'] try: is_active = subprocess.check_output( - active_cmd, stderr=subprocess.STDOUT) + active_cmd, stderr=subprocess.STDOUT).decode('UTF-8') except subprocess.CalledProcessError as exc: - if exc.output and 'false' in exc.output: - display.display_warning('FileVault appears to be disabled...') + if exc.output and 'false' in exc.output.decode('UTF-8'): + # fdesetup isactive returns 1 when FileVault is not active + display.display_debug1('FileVault appears to be disabled...') elif not exc.output: display.display_warning( 'Encountered problem determining FileVault status...') @@ -48,7 +50,9 @@ def filevault_is_active(): display.display_warning(exc.output) return False if 'true' in is_active: + display.display_debug1('FileVault appears to be enabled...') return True + display.display_debug1('Could not confirm FileVault is enabled...') return False @@ -61,7 +65,7 @@ def supports_auth_restart(): support_cmd = ['/usr/bin/fdesetup', 'supportsauthrestart'] try: is_supported = subprocess.check_output( - support_cmd, stderr=subprocess.STDOUT) + support_cmd, stderr=subprocess.STDOUT).decode('UTF-8') except subprocess.CalledProcessError as exc: if exc.output: display.display_warning(exc.output) @@ -72,9 +76,9 @@ def supports_auth_restart(): if 'true' in is_supported: display.display_debug1('FileVault supports AuthRestart...') return True - else: - display.display_warning('FileVault AuthRestart is not supported...') - return False + + display.display_warning('FileVault AuthRestart is not supported...') + return False def is_fv_user(username): @@ -82,7 +86,8 @@ def is_fv_user(username): authorized users""" cmd = ['/usr/bin/fdesetup', 'list'] try: - userlist = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + userlist = subprocess.check_output( + cmd, stderr=subprocess.STDOUT).decode('UTF-8') except subprocess.CalledProcessError: return False # output is in the format @@ -170,15 +175,15 @@ def perform_auth_restart(username=None, password=None): stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE) - err = proc.communicate(input=inputplist)[1] + err = proc.communicate(input=inputplist)[1].decode('UTF-8') os_version_tuple = osutils.getOsVersion(as_tuple=True) if os_version_tuple >= (10, 12) and 'System is being restarted' in err: return True if err: display.display_error(err) return False - else: - return True + # no error, so I guess we're successful + return True def do_authorized_or_normal_restart(username=None, @@ -214,4 +219,4 @@ def do_authorized_or_normal_restart(username=None, if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/authrestart/client.py b/code/client/munkilib/authrestart/client.py index f151561b..19ad061f 100644 --- a/code/client/munkilib/authrestart/client.py +++ b/code/client/munkilib/authrestart/client.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,13 +21,13 @@ Created by Greg Neagle on 2017-04-15. Routines for communicating with authrestartd. Socket communications code adapted from autopkg's PkgCreator by Per Olofsson """ +from __future__ import absolute_import, print_function import os -import plistlib import select import socket -import sys +from ..wrappers import writePlistToString AUTHRESTARTD_SOCKET = "/var/run/authrestartd" @@ -52,19 +52,17 @@ class AuthRestartClient(object): def send_request(self, request): '''Send a request to authrestartd''' - self.socket.send(plistlib.writePlistToString(request)) - with os.fdopen(self.socket.fileno()) as fileref: - # use select so we don't hang indefinitely if authrestartd dies - ready = select.select([fileref], [], [], 2) - if ready[0]: - reply = fileref.read() - else: - reply = '' + self.socket.send(writePlistToString(request)) + # use select so we don't hang indefinitely if authrestartd dies + ready = select.select([self.socket.fileno()], [], [], 2) + if ready[0]: + reply = self.socket.recv(8192).decode("UTF-8") + else: + reply = '' if reply: return reply.rstrip() - else: - return "ERROR:No reply" + return "ERROR:No reply" def disconnect(self): '''Disconnect from authrestartd''' @@ -175,26 +173,28 @@ def restart(): def test(): + '''A function for doing some basic testing''' import getpass import pwd - - print 'FileVault is active: %s' % fv_is_active() - print 'Recovery key is present: %s' % verify_recovery_key_present() + from ..wrappers import get_input + + print('FileVault is active: %s' % fv_is_active()) + print('Recovery key is present: %s' % verify_recovery_key_present()) username = pwd.getpwuid(os.getuid()).pw_name - print '%s is FV user: %s' % (username, verify_user(username)) + print('%s is FV user: %s' % (username, verify_user(username))) password = getpass.getpass('Enter password: ') if password: if username == 'root': username = None if store_password(password, username=username): - print 'store_password was successful' + print('store_password was successful') else: - print 'store_password failed' - print 'Can attempt auth restart: %s' % verify_can_attempt_auth_restart() - answer = raw_input('Test auth restart (y/n)? ') + print('store_password failed') + print('Can attempt auth restart: %s' % verify_can_attempt_auth_restart()) + answer = get_input('Test auth restart (y/n)? ') if answer.lower().startswith('y'): - print 'Attempting auth restart...' + print('Attempting auth restart...') if restart(): - print 'restart was successfully triggered' + print('restart was successfully triggered') else: - print 'restart failed' + print('restart failed') diff --git a/code/client/munkilib/bootstrapping.py b/code/client/munkilib/bootstrapping.py index 5607efe0..f0c8ce09 100644 --- a/code/client/munkilib/bootstrapping.py +++ b/code/client/munkilib/bootstrapping.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-08-31. Functions supporting bootstrapping mode """ +from __future__ import absolute_import import os @@ -44,7 +45,7 @@ class SetupError(Exception): def disable_fde_autologin(): '''Disables autologin to the unlocking user's account on a FileVault- encrypted machines.''' - + # See https://support.apple.com/en-us/HT202842 # We attempt to store the original value of com.apple.loginwindow # DisableFDEAutoLogin so if the local admin has set it to True for #reasons @@ -104,10 +105,10 @@ def set_bootstrap_mode(): # create CHECKANDINSTALLATSTARTUPFLAG file try: open(constants.CHECKANDINSTALLATSTARTUPFLAG, 'w').close() - except (OSError, IOError), err: + except (OSError, IOError) as err: reset_fde_autologin() raise SetupError( - 'Could not create bootstrapping flag file: %s', err) + 'Could not create bootstrapping flag file: %s' % err) def clear_bootstrap_mode(): @@ -116,6 +117,6 @@ def clear_bootstrap_mode(): if os.path.exists(constants.CHECKANDINSTALLATSTARTUPFLAG): try: os.unlink(constants.CHECKANDINSTALLATSTARTUPFLAG) - except OSError, err: + except OSError as err: raise SetupError( - 'Could not remove bootstrapping flag file: %s', err) + 'Could not remove bootstrapping flag file: %s' % err) diff --git a/code/client/munkilib/cliutils.py b/code/client/munkilib/cliutils.py index 54e0d75f..d0be47a7 100644 --- a/code/client/munkilib/cliutils.py +++ b/code/client/munkilib/cliutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-03-12. Functions supporting the admin command-line tools """ +from __future__ import absolute_import, print_function import ctypes from ctypes.util import find_library @@ -28,12 +29,28 @@ import plistlib import readline import sys import tempfile -import thread +try: + # Python 2 + import thread +except ImportError: + # Python 3 + import _thread as thread import time -import urllib -import urlparse +try: + # Python 2 + from urllib import pathname2url +except ImportError: + # Python 3 + from urllib.request import pathname2url +try: + # Python 2 + from urlparse import urlparse, urljoin +except ImportError: + # Python 3 + from urllib.parse import urlparse, urljoin from xml.parsers.expat import ExpatError +from munkilib.wrappers import unicode_or_str, get_input FOUNDATION_SUPPORT = True try: @@ -78,8 +95,8 @@ else: pref.cache = {} if prefname in pref.cache: return pref.cache[prefname] - else: - return None + # no pref found + return None def get_version(): @@ -109,18 +126,20 @@ def get_version(): def path2url(path): '''Converts a path to a file: url''' - return urlparse.urljoin('file:', urllib.pathname2url( - os.path.abspath(os.path.expanduser(path)))) + return urljoin( + 'file:', + pathname2url(os.path.abspath(os.path.expanduser(path))) + ) def print_utf8(text): '''Print Unicode text as UTF-8''' - print text.encode('UTF-8') + print(text.encode('UTF-8')) def print_err_utf8(text): '''Print Unicode text to stderr as UTF-8''' - print >> sys.stderr, text.encode('UTF-8') + print(text.encode('UTF-8'), file=sys.stderr) class TempFile(object): @@ -148,7 +167,7 @@ if 'libedit' in readline.__doc__: # pylint: enable=invalid-name -def raw_input_with_default(prompt, default_text): +def get_input_with_default(prompt, default_text): '''Get input from user with a prompt and a suggested default value''' # 10.6's libedit doesn't have the rl_set_prompt function, so we fall back @@ -157,33 +176,37 @@ def raw_input_with_default(prompt, default_text): if darwin_vers == 10: if default_text: prompt = '%s [%s]: ' % (prompt.rstrip(': '), default_text) - return (unicode(raw_input(prompt), encoding=sys.stdin.encoding) or - unicode(default_text)) - else: - # no default value, just call raw_input - return unicode(raw_input(prompt), encoding=sys.stdin.encoding) + return (unicode_or_str(get_input(prompt), encoding=sys.stdin.encoding) or + unicode_or_str(default_text)) + # no default value, just call raw_input + return unicode_or_str(get_input(prompt), encoding=sys.stdin.encoding) # A nasty, nasty hack to get around Python readline limitations under - # OS X. Gives us editable default text for configuration and munkiimport + # macOS. Gives us editable default text for configuration and munkiimport # choices''' def insert_default_text(prompt, text): '''Helper function''' time.sleep(0.01) + if not isinstance(prompt, bytes): + prompt = prompt.encode(sys.stdin.encoding) libedit.rl_set_prompt(prompt) + if isinstance(text, bytes): + text = text.decode(sys.stdin.encoding) readline.insert_text(text) libedit.rl_forced_update_display() readline.clear_history() if not default_text: - return unicode(raw_input(prompt), encoding=sys.stdin.encoding) + return unicode_or_str(get_input(prompt), encoding=sys.stdin.encoding) elif libedit: # readline module was compiled against libedit - thread.start_new_thread(insert_default_text, (prompt, default_text)) - return unicode(raw_input(), encoding=sys.stdin.encoding) + thread.start_new_thread( + insert_default_text, (prompt, default_text)) + return unicode_or_str(get_input(), encoding=sys.stdin.encoding) else: readline.set_startup_hook(lambda: readline.insert_text(default_text)) try: - return unicode(raw_input(prompt), encoding=sys.stdin.encoding) + return unicode_or_str(get_input(prompt), encoding=sys.stdin.encoding) finally: readline.set_startup_hook() @@ -198,7 +221,7 @@ def configure(prompt_list): darwin_vers = int(os.uname()[2].split('.')[0]) edited_prefs = {} for (key, prompt) in prompt_list: - newvalue = raw_input_with_default('%15s: ' % prompt, pref(key)) + newvalue = get_input_with_default('%15s: ' % prompt, pref(key)) if darwin_vers == 10: # old behavior in SL: hitting return gives you an empty string, # and means accept the default value. @@ -212,7 +235,7 @@ def configure(prompt_list): try: CFPreferencesSetAppValue(key, value, BUNDLE_ID) except BaseException: - print >> sys.stderr, 'Could not save configuration!' + print('Could not save configuration!', file=sys.stderr) raise ConfigurationSaveError # remove repo_path if it exists since we don't use that # any longer (except for backwards compatibility) and we don't @@ -231,10 +254,10 @@ def configure(prompt_list): del existing_prefs['repo_path'] plistlib.writePlist(existing_prefs, PREFSPATH) except (IOError, OSError, ExpatError): - print >> sys.stderr, ( - 'Could not save configuration to %s' % PREFSPATH) + print('Could not save configuration to %s' % PREFSPATH, + file=sys.stderr) raise ConfigurationSaveError if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/constants.py b/code/client/munkilib/constants.py index 1e850bde..a4a724b4 100644 --- a/code/client/munkilib/constants.py +++ b/code/client/munkilib/constants.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2016-12-14. Commonly used constants """ +from __future__ import absolute_import, print_function # NOTE: it's very important that defined exit codes are never changed! # Preflight exit codes. @@ -59,4 +60,4 @@ POSTACTION_SHUTDOWN = 4 if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/display.py b/code/client/munkilib/display.py index 0101b237..f4abebe8 100644 --- a/code/client/munkilib/display.py +++ b/code/client/munkilib/display.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,15 +20,15 @@ Created by Greg Neagle on 2016-12-13. Common output functions """ +from __future__ import absolute_import, print_function import sys import warnings from . import munkilog -from . import prefs from . import reports from . import munkistatus - +from .wrappers import unicode_or_str def _getsteps(num_of_steps, limit): @@ -81,16 +81,22 @@ def str_to_ascii(a_string): str, ascii form, no >7bit chars """ try: - return unicode(a_string).encode('ascii', 'ignore') + return unicode_or_str(a_string).encode('ascii', 'ignore') except UnicodeDecodeError: return a_string.decode('ascii', 'ignore') def _to_unicode(obj, encoding='UTF-8'): - """Coerces basestring obj to unicode""" - if isinstance(obj, basestring): - if not isinstance(obj, unicode): - obj = unicode(obj, encoding) + """Coerces obj to unicode""" + # pylint: disable=basestring-builtin, unicode-builtin + try: + if isinstance(obj, basestring): + if not isinstance(obj, unicode): + obj = unicode(obj, encoding) + except NameError: + # Python 3 + if isinstance(obj, bytes): + obj = obj.decode(encoding) return obj @@ -104,7 +110,7 @@ def _concat_message(msg, *args): args = [_to_unicode(arg) for arg in args] try: msg = msg % tuple(args) - except TypeError, dummy_err: + except TypeError as dummy_err: warnings.warn( 'String format does not match concat args: %s' % (str(sys.exc_info()))) @@ -124,9 +130,9 @@ def display_status_major(msg, *args): munkistatus.percent(-1) if verbose: if msg.endswith('.') or msg.endswith(u'…'): - print '%s' % msg.encode('UTF-8') + print('%s' % msg) else: - print '%s...' % msg.encode('UTF-8') + print('%s...' % msg) sys.stdout.flush() @@ -141,9 +147,9 @@ def display_status_minor(msg, *args): munkistatus.detail(msg) if verbose: if msg.endswith('.') or msg.endswith(u'…'): - print ' %s' % msg.encode('UTF-8') + print(' %s' % msg) else: - print ' %s...' % msg.encode('UTF-8') + print(' %s...' % msg) sys.stdout.flush() @@ -155,7 +161,7 @@ def display_info(msg, *args): msg = _concat_message(msg, *args) munkilog.log(u' ' + msg) if verbose > 0: - print ' %s' % msg.encode('UTF-8') + print(' %s' % msg) sys.stdout.flush() @@ -168,9 +174,9 @@ def display_detail(msg, *args): """ msg = _concat_message(msg, *args) if verbose > 1: - print ' %s' % msg.encode('UTF-8') + print(' %s' % msg) sys.stdout.flush() - if prefs.pref('LoggingLevel') > 0: + if munkilog.logging_level() > 0: munkilog.log(u' ' + msg) @@ -180,9 +186,9 @@ def display_debug1(msg, *args): """ msg = _concat_message(msg, *args) if verbose > 2: - print ' %s' % msg.encode('UTF-8') + print(' %s' % msg) sys.stdout.flush() - if prefs.pref('LoggingLevel') > 1: + if munkilog.logging_level() > 1: munkilog.log('DEBUG1: %s' % msg) @@ -192,8 +198,8 @@ def display_debug2(msg, *args): """ msg = _concat_message(msg, *args) if verbose > 3: - print ' %s' % msg.encode('UTF-8') - if prefs.pref('LoggingLevel') > 2: + print(' %s' % msg) + if munkilog.logging_level() > 2: munkilog.log('DEBUG2: %s' % msg) @@ -204,7 +210,7 @@ def display_warning(msg, *args): msg = _concat_message(msg, *args) warning = 'WARNING: %s' % msg if verbose > 0: - print >> sys.stderr, warning.encode('UTF-8') + print(warning, file=sys.stderr) munkilog.log(warning) # append this warning to our warnings log munkilog.log(warning, 'warnings.log') @@ -221,7 +227,7 @@ def display_error(msg, *args): msg = _concat_message(msg, *args) errmsg = 'ERROR: %s' % msg if verbose > 0: - print >> sys.stderr, errmsg.encode('UTF-8') + print(errmsg, file=sys.stderr) munkilog.log(errmsg) # append this error to our errors log munkilog.log(errmsg, 'errors.log') @@ -239,4 +245,4 @@ munkistatusoutput = True if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/dmgutils.py b/code/client/munkilib/dmgutils.py index 9cdde8fa..3b9ed3b4 100644 --- a/code/client/munkilib/dmgutils.py +++ b/code/client/munkilib/dmgutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,13 +20,14 @@ Created by Greg Neagle on 2016-12-13. Utilities for working with disk images. """ +from __future__ import absolute_import, print_function import os import subprocess from . import display from . import utils -from . import FoundationPlist +from .wrappers import readPlistFromString, PlistReadError # we use lots of camelCase-style names. Deal with it. @@ -47,11 +48,11 @@ def DMGisWritable(dmgpath): (pliststr, out) = utils.getFirstPlist(out) if pliststr: try: - plist = FoundationPlist.readPlistFromString(pliststr) + plist = readPlistFromString(pliststr) dmg_format = plist.get('Format') if dmg_format in ['UDSB', 'UDSP', 'UDRW', 'RdWr']: return True - except FoundationPlist.NSPropertyListSerializationException: + except PlistReadError: pass return False @@ -70,11 +71,11 @@ def dmg_has_sla(dmgpath): (pliststr, out) = utils.getFirstPlist(out) if pliststr: try: - plist = FoundationPlist.readPlistFromString(pliststr) + plist = readPlistFromString(pliststr) properties = plist.get('Properties') if properties: has_sla = properties.get('Software License Agreement', False) - except FoundationPlist.NSPropertyListSerializationException: + except PlistReadError: pass return has_sla @@ -91,13 +92,13 @@ def hdiutil_info(): bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = proc.communicate() if err: - display.display_error(u'hdiutil info error: %s', err) + display.display_error(u'hdiutil info error: %s', err.decode("UTF-8")) (pliststr, out) = utils.getFirstPlist(out) if pliststr: try: - plist = FoundationPlist.readPlistFromString(pliststr) + plist = readPlistFromString(pliststr) return plist - except FoundationPlist.NSPropertyListSerializationException: + except PlistReadError: pass return None @@ -194,9 +195,9 @@ def mountdmg(dmgpath, use_shadow=False, use_existing_mounts=False, return mountpoints # Attempt to mount the dmg - stdin = '' + stdin = b'' if dmg_has_sla(dmgpath): - stdin = 'Y\n' + stdin = b'Y\n' display.display_detail( 'NOTE: %s has embedded Software License Agreement' % dmgname) cmd = ['/usr/bin/hdiutil', 'attach', dmgpath, '-nobrowse', '-plist'] @@ -205,20 +206,22 @@ def mountdmg(dmgpath, use_shadow=False, use_existing_mounts=False, if use_shadow: cmd.append('-shadow') proc = subprocess.Popen(cmd, - bufsize=1, stdout=subprocess.PIPE, + bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) (out, err) = proc.communicate(stdin) if proc.returncode: display.display_error( - 'Error: "%s" while mounting %s.' % (err.rstrip(), dmgname)) + u'Error: "%s" while mounting %s.' + % (err.decode('UTF-8').rstrip(), dmgname)) (pliststr, out) = utils.getFirstPlist(out) if pliststr: try: - plist = FoundationPlist.readPlistFromString(pliststr) + plist = readPlistFromString(pliststr) for entity in plist.get('system-entities', []): if 'mount-point' in entity: mountpoints.append(entity['mount-point']) - except FoundationPlist.NSPropertyListSerializationException: + except PlistReadError as err: + display.display_error("%s" % err) display.display_error( 'Bad plist string returned when mounting diskimage %s:\n%s' % (dmgname, pliststr)) @@ -242,8 +245,9 @@ def unmountdmg(mountpoint): stderr=subprocess.PIPE) (dummy_output, err) = proc.communicate() if proc.returncode: - display.display_warning('Failed to unmount %s: %s', mountpoint, err) + display.display_warning( + 'Failed to unmount %s: %s', mountpoint, err.decode("UTF-8")) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/fetch.py b/code/client/munkilib/fetch.py index 03977287..08a6b4e2 100644 --- a/code/client/munkilib/fetch.py +++ b/code/client/munkilib/fetch.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2011-2019 Greg Neagle. +# Copyright 2011-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ fetch.py Created by Greg Neagle on 2011-09-29. """ +from __future__ import absolute_import, print_function # standard libs import calendar @@ -27,10 +28,21 @@ import imp import os import shutil import time -import urllib2 -import urlparse import xattr +try: + # Python 2 + from urllib2 import unquote +except ImportError: + # Python 3 + from urllib.parse import unquote +try: + # Python 2 + from urlparse import urlparse, urlsplit +except ImportError: + # Python 3 + from urllib.parse import urlparse, urlsplit + # Cocoa libs via PyObjC # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. @@ -153,8 +165,7 @@ def getxattr(pathname, attr): """Get a named xattr from a file. Return None if not present""" if attr in xattr.listxattr(pathname): return xattr.getxattr(pathname, attr) - else: - return None + return None def writeCachedChecksum(file_path, fhash=None): @@ -164,7 +175,7 @@ def writeCachedChecksum(file_path, fhash=None): if not fhash: fhash = munkihash.getsha256hash(file_path) if len(fhash) == 64: - xattr.setxattr(file_path, XATTR_SHA, fhash) + xattr.setxattr(file_path, XATTR_SHA, fhash.encode("UTF-8")) return fhash return None @@ -187,7 +198,7 @@ def header_dict_from_list(array): def get_url(url, destinationpath, custom_headers=None, message=None, onlyifnewer=False, - resume=False, follow_redirects=False): + resume=False, follow_redirects=False, pkginfo=None): """Gets an HTTP or HTTPS URL and stores it in destination path. Returns a dictionary of headers, which includes http_result_code and http_result_description. @@ -224,7 +235,8 @@ def get_url(url, destinationpath, 'additional_headers': header_dict_from_list(custom_headers), 'download_only_if_changed': onlyifnewer, 'cache_data': cache_data, - 'logging_function': display.display_debug2} + 'logging_function': display.display_debug2, + 'pkginfo': pkginfo} display.display_debug2('Options: %s' % options) # Allow middleware to modify options @@ -269,14 +281,14 @@ def get_url(url, destinationpath, # safely kill the connection then re-raise connection.cancel() raise - except Exception, err: # too general, I know + except Exception as err: # too general, I know # Let us out! ... Safely! Unexpectedly quit dialogs are annoying... connection.cancel() # Re-raise the error as a GurlError raise GurlError(-1, str(err)) if connection.error is not None: - # Gurl returned an error + # gurl returned an error display.display_detail( 'Download error %s: %s', connection.error.code(), connection.error.localizedDescription()) @@ -305,7 +317,7 @@ def get_url(url, destinationpath, if str(connection.status).startswith('2') and temp_download_exists: try: os.rename(tempdownloadpath, destinationpath) - except OSError, err: + except OSError as err: # Re-raise the error as a GurlError raise GurlError(-1, str(err)) return connection.headers @@ -331,7 +343,8 @@ def getResourceIfChangedAtomically(url, message=None, resume=False, verify=False, - follow_redirects=False): + follow_redirects=False, + pkginfo=None): """Gets file from a URL. Checks first if there is already a file with the necessary checksum. Then checks if the file has changed on the server, resuming or @@ -354,8 +367,11 @@ def getResourceIfChangedAtomically(url, xattr_hash = getxattr(destinationpath, XATTR_SHA) if not xattr_hash: xattr_hash = writeCachedChecksum(destinationpath) + else: + xattr_hash = xattr_hash.decode('UTF-8') if xattr_hash == expected_hash: #File is already current, no change. + munkilog.log(" Cached item is current.") return False elif prefs.pref( 'PackageVerificationMode').lower() in ['hash_strict', 'hash']: @@ -363,7 +379,7 @@ def getResourceIfChangedAtomically(url, os.unlink(destinationpath) except OSError: pass - munkilog.log('Cached payload does not match hash in catalog, ' + munkilog.log('Cached item does not match hash in catalog, ' 'will check if changed and redownload: %s' % destinationpath) # continue with normal if-modified-since/etag update methods. @@ -373,12 +389,13 @@ def getResourceIfChangedAtomically(url, # the preference decides follow_redirects = prefs.pref('FollowHTTPRedirects') - url_parse = urlparse.urlparse(url) + url_parse = urlparse(url) if url_parse.scheme in ['http', 'https']: changed = getHTTPfileIfChangedAtomically( url, destinationpath, custom_headers=custom_headers, - message=message, resume=resume, follow_redirects=follow_redirects) + message=message, resume=resume, follow_redirects=follow_redirects, + pkginfo=pkginfo) elif url_parse.scheme == 'file': changed = getFileIfChangedAtomically(url_parse.path, destinationpath) else: @@ -403,7 +420,7 @@ def getResourceIfChangedAtomically(url, def munki_resource( url, destinationpath, message=None, resume=False, expected_hash=None, - verify=False): + verify=False, pkginfo=None): '''The high-level function for getting resources from the Munki repo. Gets a given URL from the Munki server. @@ -425,7 +442,8 @@ def munki_resource( expected_hash=expected_hash, message=message, resume=resume, - verify=verify) + verify=verify, + pkginfo=pkginfo) def getFileIfChangedAtomically(path, destinationpath): @@ -436,7 +454,7 @@ def getFileIfChangedAtomically(path, destinationpath): item is already in the local cache. Raises FileCopyError if there is an error.""" - path = urllib2.unquote(path) + path = unquote(path) try: st_src = os.stat(path) except OSError: @@ -460,7 +478,7 @@ def getFileIfChangedAtomically(path, destinationpath): try: if st_dst: os.unlink(tmp_destinationpath) - except OSError, err: + except OSError as err: if err.args[0] == errno.ENOENT: pass # OK else: @@ -470,13 +488,13 @@ def getFileIfChangedAtomically(path, destinationpath): # copy from source to temporary destination try: shutil.copy2(path, tmp_destinationpath) - except IOError, err: + except IOError as err: raise FileCopyError('Copy IOError: %s' % str(err)) # rename temp destination to final destination try: os.rename(tmp_destinationpath, destinationpath) - except OSError, err: + except OSError as err: raise FileCopyError('Renaming %s: %s' % (destinationpath, str(err))) return True @@ -485,7 +503,8 @@ def getFileIfChangedAtomically(path, destinationpath): def getHTTPfileIfChangedAtomically(url, destinationpath, custom_headers=None, message=None, resume=False, - follow_redirects=False): + follow_redirects=False, + pkginfo=None): """Gets file from HTTP URL, checking first to see if it has changed on the server. @@ -510,19 +529,20 @@ def getHTTPfileIfChangedAtomically(url, destinationpath, message=message, onlyifnewer=getonlyifnewer, resume=resume, - follow_redirects=follow_redirects) + follow_redirects=follow_redirects, + pkginfo=pkginfo) except ConnectionError: # connection errors should be handled differently; don't re-raise # them as GurlDownloadError raise - except HTTPError, err: - err = 'HTTP result %s: %s' % tuple(err) + except HTTPError as err: + err = 'HTTP result %s: %s' % (err.args[0], err.args[1]) raise GurlDownloadError(err) - except GurlError, err: - err = 'Error %s: %s' % tuple(err) + except GurlError as err: + err = 'Error %s: %s' % (err.args[0], err.args[1]) raise GurlDownloadError(err) err = None @@ -554,7 +574,7 @@ def getURLitemBasename(url): "/path/foo.dmg" => "foo.dmg" """ - url_parse = urlparse.urlparse(url) + url_parse = urlparse(url) return os.path.basename(url_parse.path) @@ -600,22 +620,22 @@ def verifySoftwarePackageIntegrity(file_path, item_hash, always_hash=False): chash = munkihash.getsha256hash(file_path) if item_hash == chash: return (True, chash) - else: - display.display_error( - 'Hash value integrity check for %s failed.' % - item_name) - return (False, chash) + # item_hash != chash + display.display_error( + 'Hash value integrity check for %s failed.' % + item_name) + return (False, chash) else: if mode.lower() == 'hash_strict': display.display_error( 'Reference hash value for %s is missing in catalog.' % item_name) return (False, chash) - else: - display.display_warning( - 'Reference hash value missing for %s -- package ' - 'integrity verification skipped.' % item_name) - return (True, chash) + # mode.lower() != 'hash_strict' + display.display_warning( + 'Reference hash value missing for %s -- package ' + 'integrity verification skipped.' % item_name) + return (True, chash) else: display.display_error( 'The PackageVerificationMode in the ManagedInstalls.plist has an ' @@ -632,7 +652,7 @@ def getDataFromURL(url): if os.path.exists(urldata): try: os.unlink(urldata) - except (IOError, OSError), err: + except (IOError, OSError) as err: display.display_warning('Error in getDataFromURL: %s', err) dummy_result = munki_resource(url, urldata) try: @@ -641,7 +661,7 @@ def getDataFromURL(url): fdesc.close() os.unlink(urldata) return data - except (IOError, OSError), err: + except (IOError, OSError) as err: display.display_warning('Error in getDataFromURL: %s', err) return '' @@ -654,7 +674,7 @@ def check_server(url): # rewritten 12 Dec 2016 to use gurl so we use system proxies, if any # deconstruct URL to get scheme - url_parts = urlparse.urlsplit(url) + url_parts = urlsplit(url) if url_parts.scheme in ('http', 'https'): pass elif url_parts.scheme == 'file': @@ -662,8 +682,7 @@ def check_server(url): return (-1, 'Non-local hostnames not supported for file:// URLs') if os.path.exists(url_parts.path): return (0, 'OK') - else: - return (-1, 'Path %s does not exist' % url_parts.path) + return (-1, 'Path %s does not exist' % url_parts.path) else: return (-1, 'Unsupported URL scheme') @@ -671,9 +690,9 @@ def check_server(url): try: # attempt to get something at the url dummy_data = getDataFromURL(url) - except ConnectionError, err: - # err should contain a tuple with code and description - return (err[0], err[1]) + except ConnectionError as err: + # err.args should contain a tuple with code and description + return (err.args[0], err.args[1]) except (GurlError, DownloadError): # HTTP errors, etc are OK -- we just need to be able to connect pass @@ -681,4 +700,4 @@ def check_server(url): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/gurl.py b/code/client/munkilib/gurl.py index 4905535b..9aa18109 100644 --- a/code/client/munkilib/gurl.py +++ b/code/client/munkilib/gurl.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -18,23 +18,39 @@ gurl.py Created by Greg Neagle on 2013-11-21. Modified in Feb 2016 to add support for NSURLSession. +Updated June 2019 for compatibility with Python 3 and PyObjC 5.1.2+ curl replacement using NSURLConnection and friends + +Tested with PyObjC 2.5.1 (inlcuded with macOS) +and with PyObjC 5.2b1. Should also work with PyObjC 5.1.2. +May fail with other versions of PyObjC due to issues with completion handler +signatures. """ +from __future__ import absolute_import, print_function import os -from urlparse import urlparse import xattr +try: + # Python 2 + from urlparse import urlparse +except ImportError: + # Python 3 + from urllib.parse import urlparse + + # builtin super doesn't work with Cocoa classes in recent PyObjC releases. +# pylint: disable=redefined-builtin,no-name-in-module from objc import super +# pylint: enable=redefined-builtin,no-name-in-module # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. # pylint: disable=E0611 -from Foundation import (NSBundle, NSRunLoop, NSDate, +from Foundation import (NSBundle, NSRunLoop, NSData, NSDate, NSObject, NSURL, NSURLConnection, NSMutableURLRequest, NSURLRequestReloadIgnoringLocalCacheData, @@ -79,17 +95,23 @@ if NSURLSESSION_AVAILABLE: # define a helper function for block callbacks import ctypes import objc - _objc_so = ctypes.cdll.LoadLibrary( - os.path.join(objc.__path__[0], '_objc.so')) - PyObjCMethodSignature_WithMetaData = ( - _objc_so.PyObjCMethodSignature_WithMetaData) - PyObjCMethodSignature_WithMetaData.restype = ctypes.py_object + CALLBACK_HELPER_AVAILABLE = True + try: + _objc_so = ctypes.cdll.LoadLibrary( + os.path.join(objc.__path__[0], '_objc.so')) + except OSError: + # could not load _objc.so + CALLBACK_HELPER_AVAILABLE = False + else: + PyObjCMethodSignature_WithMetaData = ( + _objc_so.PyObjCMethodSignature_WithMetaData) + PyObjCMethodSignature_WithMetaData.restype = ctypes.py_object - def objc_method_signature(signature_str): - '''Return a PyObjCMethodSignature given a call signature in string - format''' - return PyObjCMethodSignature_WithMetaData( - ctypes.create_string_buffer(signature_str), None, False) + def objc_method_signature(signature_str): + '''Return a PyObjCMethodSignature given a call signature in string + format''' + return PyObjCMethodSignature_WithMetaData( + ctypes.create_string_buffer(signature_str), None, False) # pylint: enable=E0611 @@ -175,7 +197,7 @@ class Gurl(NSObject): '''Set up our Gurl object''' self = super(Gurl, self).init() if not self: - return + return None self.follow_redirects = options.get('follow_redirects', False) self.ignore_system_proxy = options.get('ignore_system_proxy', False) @@ -290,19 +312,19 @@ class Gurl(NSObject): '''Returns any stored headers for self.destination_path''' # try to read stored headers try: - stored_plist_str = xattr.getxattr( + stored_plist_bytestr = xattr.getxattr( self.destination_path, self.GURL_XATTR) except (KeyError, IOError): return {} - data = buffer(stored_plist_str) - dataObject, plistFormat, error = ( + data = NSData.dataWithBytes_length_( + stored_plist_bytestr, len(stored_plist_bytestr)) + dataObject, _plistFormat, error = ( NSPropertyListSerialization. propertyListFromData_mutabilityOption_format_errorDescription_( data, NSPropertyListMutableContainersAndLeaves, None, None)) if error: return {} - else: - return dataObject + return dataObject def storeHeaders_(self, headers): '''Store dictionary data as an xattr for self.destination_path''' @@ -311,12 +333,15 @@ class Gurl(NSObject): dataFromPropertyList_format_errorDescription_( headers, NSPropertyListXMLFormat_v1_0, None)) if error: - string = '' + byte_string = b'' else: - string = str(plistData) + try: + byte_string = bytes(plistData) + except NameError: + byte_string = str(plistData) try: - xattr.setxattr(self.destination_path, self.GURL_XATTR, string) - except IOError, err: + xattr.setxattr(self.destination_path, self.GURL_XATTR, byte_string) + except IOError as err: self.log('Could not store metadata to %s: %s' % (self.destination_path, err)) @@ -354,10 +379,8 @@ class Gurl(NSObject): del headers['expected-length'] self.storeHeaders_(headers) - def URLSession_task_didCompleteWithError_(self, session, task, error): + def URLSession_task_didCompleteWithError_(self, _session, _task, error): '''NSURLSessionTaskDelegate method.''' - # we don't actually use the session or task arguments, so - # pylint: disable=W0613 if self.destination and self.destination_path: self.destination.close() self.removeExpectedSizeFromStoredHeaders() @@ -365,23 +388,17 @@ class Gurl(NSObject): self.recordError_(error) self.done = True - def connection_didFailWithError_(self, connection, error): + def connection_didFailWithError_(self, _connection, error): '''NSURLConnectionDelegate method Sent when a connection fails to load its request successfully.''' - # we don't actually use the connection argument, so - # pylint: disable=W0613 self.recordError_(error) self.done = True if self.destination and self.destination_path: self.destination.close() - def connectionDidFinishLoading_(self, connection): + def connectionDidFinishLoading_(self, _connection): '''NSURLConnectionDataDelegate method Sent when a connection has finished loading successfully.''' - - # we don't actually use the connection argument, so - # pylint: disable=W0613 - self.done = True if self.destination and self.destination_path: self.destination.close() @@ -444,38 +461,50 @@ class Gurl(NSObject): self.bytesReceived = local_filesize self.expectedLength += local_filesize # open file for append - self.destination = open(self.destination_path, 'a') + self.destination = open(self.destination_path, 'ab') elif str(self.status).startswith('2'): # not resuming, just open the file for writing - self.destination = open(self.destination_path, 'w') + self.destination = open(self.destination_path, 'wb') # store some headers with the file for use if we need to resume # the download and for future checking if the file on the server # has changed self.storeHeaders_(download_data) + if completionHandler: # tell the session task to continue completionHandler(NSURLSessionResponseAllow) def URLSession_dataTask_didReceiveResponse_completionHandler_( - self, session, task, response, completionHandler): + self, _session, _task, response, completionHandler): '''NSURLSessionDataDelegate method''' - # we don't actually use the session or task arguments, so - # pylint: disable=W0613 - completionHandler.__block_signature__ = objc_method_signature('v@i') + if CALLBACK_HELPER_AVAILABLE: + completionHandler.__block_signature__ = objc_method_signature(b'v@i') self.handleResponse_withCompletionHandler_(response, completionHandler) - def connection_didReceiveResponse_(self, connection, response): + def connection_didReceiveResponse_(self, _connection, response): '''NSURLConnectionDataDelegate delegate method Sent when the connection has received sufficient data to construct the URL response for its request.''' - # we don't actually use the connection argument, so - # pylint: disable=W0613 self.handleResponse_withCompletionHandler_(response, None) def handleRedirect_newRequest_withCompletionHandler_( self, response, request, completionHandler): '''Handle the redirect request''' + def allowRedirect(): + '''Allow the redirect''' + if completionHandler: + completionHandler(request) + return None + return request + + def denyRedirect(): + '''Deny the redirect''' + if completionHandler: + completionHandler(None) + return None + + newURL = request.URL().absoluteString() if response is None: # the request has changed the NSURLRequest in order to standardize # its format, for example, changing a request for @@ -489,16 +518,12 @@ class Gurl(NSObject): # all in this scenario, unlike NSConnectionDelegate method # connection:willSendRequest:redirectResponse: # we'll leave this here anyway in case we're wrong about that - if completionHandler: - completionHandler(request) - return - else: - return request + self.log('Allowing redirect to: %s' % newURL) + return allowRedirect() # If we get here, it appears to be a real redirect attempt # Annoyingly, we apparently can't get access to the headers from the # site that told us to redirect. All we know is that we were told # to redirect and where the new location is. - newURL = request.URL().absoluteString() self.redirection.append([newURL, dict(response.allHeaderFields())]) newParsedURL = urlparse(newURL) # This code was largely based on the work of Andreas Fuchs @@ -506,65 +531,47 @@ class Gurl(NSObject): if self.follow_redirects is True or self.follow_redirects == 'all': # Allow the redirect self.log('Allowing redirect to: %s' % newURL) - if completionHandler: - completionHandler(request) - return - else: - return request + return allowRedirect() elif (self.follow_redirects == 'https' and newParsedURL.scheme == 'https'): # Once again, allow the redirect self.log('Allowing redirect to: %s' % newURL) - if completionHandler: - completionHandler(request) - return - else: - return request - else: - # If we're down here either the preference was set to 'none', - # the url we're forwarding on to isn't https or follow_redirects - # was explicitly set to False - self.log('Denying redirect to: %s' % newURL) - if completionHandler: - completionHandler(None) - return - else: - return None + return allowRedirect() + # If we're down here either the preference was set to 'none', + # the url we're forwarding on to isn't https or follow_redirects + # was explicitly set to False + self.log('Denying redirect to: %s' % newURL) + return denyRedirect() + # we don't control the API, so + # pylint: disable=too-many-arguments def URLSession_task_willPerformHTTPRedirection_newRequest_completionHandler_( - self, session, task, response, request, completionHandler): + self, _session, _task, response, request, completionHandler): '''NSURLSessionTaskDelegate method''' - # we don't actually use the session or task arguments, so - # pylint: disable=W0613 self.log( 'URLSession_task_willPerformHTTPRedirection_newRequest_' 'completionHandler_') - completionHandler.__block_signature__ = objc_method_signature('v@@') + if CALLBACK_HELPER_AVAILABLE: + completionHandler.__block_signature__ = objc_method_signature(b'v@@') self.handleRedirect_newRequest_withCompletionHandler_( response, request, completionHandler) + # pylint: enable=too-many-arguments def connection_willSendRequest_redirectResponse_( - self, connection, request, response): + self, _connection, request, response): '''NSURLConnectionDataDelegate method - Sent when the connection determines that it must change URLs in order to - continue loading a request.''' - - # we don't actually use the connection argument, so - # pylint: disable=W0613 + Sent when the connection determines that it must change URLs in order + to continue loading a request.''' self.log('connection_willSendRequest_redirectResponse_') return self.handleRedirect_newRequest_withCompletionHandler_( response, request, None) def connection_canAuthenticateAgainstProtectionSpace_( - self, connection, protectionSpace): + self, _connection, protectionSpace): '''NSURLConnection delegate method Sent to determine whether the delegate is able to respond to a protection space’s form of authentication. Deprecated in 10.10''' - - # we don't actually use the connection argument, so - # pylint: disable=W0613 - # this is not called in 10.5.x. self.log('connection_canAuthenticateAgainstProtectionSpace_') if protectionSpace: @@ -642,59 +649,55 @@ class Gurl(NSObject): challenge) def connection_willSendRequestForAuthenticationChallenge_( - self, connection, challenge): + self, _connection, challenge): '''NSURLConnection delegate method Tells the delegate that the connection will send a request for an authentication challenge. New in 10.7.''' - # we don't actually use the connection argument, so - # pylint: disable=W0613 self.log('connection_willSendRequestForAuthenticationChallenge_') self.handleChallenge_withCompletionHandler_(challenge, None) def URLSession_task_didReceiveChallenge_completionHandler_( - self, session, task, challenge, completionHandler): + self, _session, _task, challenge, completionHandler): '''NSURLSessionTaskDelegate method''' - # we don't actually use the session or task arguments, so - # pylint: disable=W0613 - completionHandler.__block_signature__ = objc_method_signature('v@i@') + if CALLBACK_HELPER_AVAILABLE: + completionHandler.__block_signature__ = objc_method_signature(b'v@i@') self.log('URLSession_task_didReceiveChallenge_completionHandler_') self.handleChallenge_withCompletionHandler_( challenge, completionHandler) def connection_didReceiveAuthenticationChallenge_( - self, connection, challenge): + self, _connection, challenge): '''NSURLConnection delegate method Sent when a connection must authenticate a challenge in order to download its request. Deprecated in 10.10''' - # we don't actually use the connection argument, so - # pylint: disable=W0613 self.log('connection_didReceiveAuthenticationChallenge_') self.handleChallenge_withCompletionHandler_(challenge, None) def handleReceivedData_(self, data): '''Handle received data''' if self.destination: - self.destination.write(str(data)) + self.destination.write(data) else: - self.log(str(data).decode('UTF-8')) + try: + self.log(str(data)) + except Exception: + pass self.bytesReceived += len(data) if self.expectedLength != NSURLResponseUnknownLength: + # pylint: disable=old-division self.percentComplete = int( float(self.bytesReceived)/float(self.expectedLength) * 100.0) + # pylint: enable=old-division - def URLSession_dataTask_didReceiveData_(self, session, task, data): + def URLSession_dataTask_didReceiveData_(self, _session, _task, data): '''NSURLSessionDataDelegate method''' - # we don't actually use the session or task arguments, so - # pylint: disable=W0613 self.handleReceivedData_(data) - def connection_didReceiveData_(self, connection, data): + def connection_didReceiveData_(self, _connection, data): '''NSURLConnectionDataDelegate method Sent as a connection loads data incrementally''' - # we don't actually use the connection argument, so - # pylint: disable=W0613 self.handleReceivedData_(data) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/iconutils.py b/code/client/munkilib/iconutils.py index fdba3d88..36d6e7d3 100644 --- a/code/client/munkilib/iconutils.py +++ b/code/client/munkilib/iconutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2010-2019 Greg Neagle. +# Copyright 2010-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2014-05-15. Functions to work with product images ('icons') for Managed Software Center """ +from __future__ import absolute_import, print_function import glob import os @@ -40,7 +41,7 @@ from Quartz import (CGImageSourceCreateWithURL, CGImageSourceCreateImageAtIndex, # pylint: enable=E0611 from . import display -from . import FoundationPlist +from .wrappers import readPlist, PlistReadError # we use lots of camelCase-style names. Deal with it. @@ -93,9 +94,9 @@ def findIconForApp(app_path): if not os.path.exists(app_path): return None try: - info = FoundationPlist.readPlist( + info = readPlist( os.path.join(app_path, u'Contents/Info.plist')) - except FoundationPlist.FoundationPlistException: + except PlistReadError: return None app_name = os.path.basename(app_path) icon_filename = info.get('CFBundleIconFile', app_name) @@ -131,7 +132,7 @@ def extractAppIconsFromFlatPkg(pkg_path): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output = proc.communicate()[0] + output = proc.communicate()[0].decode('UTF-8') if proc.returncode: display.display_error(u'Could not get bom files from %s', pkg_path) return [] @@ -148,13 +149,13 @@ def extractAppIconsFromFlatPkg(pkg_path): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output = proc.communicate()[0] + output = proc.communicate()[0].decode('UTF-8') if proc.returncode: display.display_error(u'Could not lsbom %s', bomfile) # record paths to all app Info.plist files pkg_dict[pkgname] = [ os.path.normpath(line) - for line in output.decode('utf-8').splitlines() + for line in output.splitlines() if line.endswith(u'.app/Contents/Info.plist')] if not pkg_dict[pkgname]: # remove empty lists @@ -260,11 +261,11 @@ def getAppInfoPathsFromBOM(bomfile): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output = proc.communicate()[0] + output = proc.communicate()[0].decode('UTF-8') return [line for line in output.splitlines() if line.endswith('.app/Contents/Info.plist')] return [] if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/info.py b/code/client/munkilib/info.py index 7e2c2ec7..d048b4a2 100644 --- a/code/client/munkilib/info.py +++ b/code/client/munkilib/info.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ Created by Greg Neagle on 2016-12-14. Utilities that retrieve information from the current machine. """ +from __future__ import absolute_import, print_function + # standard libs import ctypes import ctypes.util @@ -48,7 +50,13 @@ from . import prefs from . import reports from . import utils from . import FoundationPlist +from .wrappers import unicode_or_str +try: + _ = xrange # pylint: disable=xrange-builtin +except NameError: + # no xrange in Python 3 + xrange = range # Always ignore these directories when discovering applications. APP_DISCOVERY_EXCLUSION_DIRS = set([ @@ -95,7 +103,7 @@ class Popen(subprocess.Popen): output = [] inactive = 0 - while 1: + while True: (rlist, dummy_wlist, dummy_xlist) = select.select( [fileobj], [], [], 1.0) @@ -144,7 +152,12 @@ class Popen(subprocess.Popen): fds.append(self.stderr) if std_in is not None and sys.stdin is not None: - sys.stdin.write(std_in) + try: + # Python 3 + sys.stdin.buffer.write(std_in) + except AttributeError: + # Python 2 + sys.stdin.write(std_in) returncode = None inactive = 0 @@ -167,11 +180,11 @@ class Popen(subprocess.Popen): returncode = self.poll() if self.stdout is not None: - stdout_str = ''.join(stdout) + stdout_str = b''.join(stdout) else: stdout_str = None if self.stderr is not None: - stderr_str = ''.join(stderr) + stderr_str = b''.join(stderr) else: stderr_str = None @@ -184,14 +197,14 @@ def _unsigned(i): return i & 0xFFFFFFFF -def _asciiz_to_str(a_string): +def _asciiz_to_bytestr(a_bytestring): """Transform a null-terminated string of any length into a Python str. Returns a normal Python str that has been terminated. """ - i = a_string.find('\0') + i = a_bytestring.find(b'\0') if i > -1: - a_string = a_string[0:i] - return a_string + a_bytestring = a_bytestring[0:i] + return a_bytestring def _f_flags_to_set(f_flags): @@ -230,8 +243,8 @@ def get_filesystems(): libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c")) # see man GETFSSTAT(2) for struct - statfs_32_struct = '=hh ll ll ll lQ lh hl 2l 15s 90s 90s x 16x' - statfs_64_struct = '=Ll QQ QQ Q ll l LLL 16s 1024s 1024s 32x' + statfs_32_struct = b'=hh ll ll ll lQ lh hl 2l 15s 90s 90s x 16x' + statfs_64_struct = b'=Ll QQ QQ Q ll l LLL 16s 1024s 1024s 32x' os_version = osutils.getOsVersion(as_tuple=True) if os_version <= (10, 5): mode = 32 @@ -269,21 +282,21 @@ def get_filesystems(): f_ffree, f_fsid_0, f_fsid_1, f_owner, f_type, f_flags, f_fssubtype, f_fstypename, f_mntonname, f_mntfromname) = struct.unpack( - statfs_struct, str(buf[ofs:ofs+sizeof_statfs_struct])) + statfs_struct, bytes(buf[ofs:ofs+sizeof_statfs_struct])) elif mode == 32: (f_otype, f_oflags, f_bsize, f_iosize, f_blocks, f_bfree, f_bavail, f_files, f_ffree, f_fsid, f_owner, f_reserved1, f_type, f_flags, f_reserved2_0, f_reserved2_1, f_fstypename, f_mntonname, f_mntfromname) = struct.unpack( - statfs_struct, str(buf[ofs:ofs+sizeof_statfs_struct])) + statfs_struct, bytes(buf[ofs:ofs+sizeof_statfs_struct])) try: - stat_val = os.stat(_asciiz_to_str(f_mntonname)) + stat_val = os.stat(_asciiz_to_bytestr(f_mntonname)) output[stat_val.st_dev] = { 'f_flags_set': _f_flags_to_set(f_flags), - 'f_fstypename': _asciiz_to_str(f_fstypename), - 'f_mntonname': _asciiz_to_str(f_mntonname), - 'f_mntfromname': _asciiz_to_str(f_mntfromname), + 'f_fstypename': _asciiz_to_bytestr(f_fstypename), + 'f_mntonname': _asciiz_to_bytestr(f_mntonname), + 'f_mntfromname': _asciiz_to_bytestr(f_mntfromname), } except OSError: pass @@ -330,10 +343,10 @@ def is_excluded_filesystem(path, _retry=False): display.display_debug1( 'Trying isExcludedFilesystem again for %s' % path) return is_excluded_filesystem(path, True) - else: - display.display_debug1( - 'Could not match path %s to a filesystem' % path) - return None + # _retry defined + display.display_debug1( + 'Could not match path %s to a filesystem' % path) + return None exc_flags = ('read-only' in FILESYSTEMS[stat_val.st_dev]['f_flags_set'] or 'local' not in FILESYSTEMS[stat_val.st_dev]['f_flags_set']) @@ -541,7 +554,7 @@ def get_hardware_info(): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_error) = proc.communicate() + output = proc.communicate()[0] try: plist = FoundationPlist.readPlistFromString(output) # system_profiler xml is an array @@ -561,7 +574,7 @@ def get_ip_addresses(kind): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_error) = proc.communicate() + output = proc.communicate()[0] try: plist = FoundationPlist.readPlistFromString(output) # system_profiler xml is an array of length 1 @@ -608,12 +621,12 @@ def available_disk_space(volumepath='/'): volumepath = '/' try: stat_val = os.statvfs(volumepath) - except OSError, err: + except OSError as err: display.display_error( 'Error getting disk space in %s: %s', volumepath, str(err)) return 0 # f_bavail matches df(1) output - return int(stat_val.f_frsize * stat_val.f_bavail / 1024) + return int(stat_val.f_frsize * stat_val.f_bavail / 1024) # pylint: disable=old-division def get_os_build(): @@ -632,7 +645,7 @@ def getMachineFacts(): installer is applicable to this OS or hardware""" # pylint: disable=C0103 machine = dict() - machine['hostname'] = os.uname()[1].decode('UTF-8') + machine['hostname'] = unicode_or_str(os.uname()[1]) machine['arch'] = os.uname()[4] machine['os_vers'] = osutils.getOsVersion(only_major_minor=False) machine['os_build_number'] = get_os_build() @@ -689,8 +702,8 @@ def get_conditions(): utils.runExternalScript(conditionalscriptpath)) except utils.ScriptNotFoundError: pass # script is not required, so pass - except utils.RunExternalScriptError, err: - print >> sys.stderr, unicode(err) + except utils.RunExternalScriptError as err: + print(unicode_or_str(err), file=sys.stderr) else: # /usr/local/munki/conditions does not exist pass @@ -727,7 +740,7 @@ def saveappdata(): app_inventory, os.path.join( prefs.pref('ManagedInstallDir'), 'ApplicationInventory.plist')) - except FoundationPlist.NSPropertyListSerializationException, err: + except FoundationPlist.NSPropertyListSerializationException as err: display.display_warning( 'Unable to save inventory report: %s' % err) @@ -810,7 +823,7 @@ def predicate_evaluates_as_true(predicate_string, additional_info=None): info_object.update(additional_info) try: predicate = NSPredicate.predicateWithFormat_(predicate_string) - except BaseException, err: + except BaseException as err: display.display_warning('%s', err) # can't parse predicate, so return False return False @@ -821,4 +834,4 @@ def predicate_evaluates_as_true(predicate_string, additional_info=None): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/installer/__init__.py b/code/client/munkilib/installer/__init__.py index a8ce586c..fcc57447 100644 --- a/code/client/munkilib/installer/__init__.py +++ b/code/client/munkilib/installer/__init__.py @@ -1 +1,6 @@ -from .core import * \ No newline at end of file +'''Make names in .core available as installer.foo''' +from __future__ import absolute_import + +# pylint: disable=wildcard-import +from .core import * +# pylint: enable=wildcard-import diff --git a/code/client/munkilib/installer/core.py b/code/client/munkilib/installer/core.py index 390c2fcd..9b48025e 100644 --- a/code/client/munkilib/installer/core.py +++ b/code/client/munkilib/installer/core.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ installer.core munki module to automatically install pkgs, mpkgs, and dmgs (containing pkgs and mpkgs) from a defined folder. """ +from __future__ import absolute_import, print_function import datetime import os @@ -257,16 +258,15 @@ def install_with_info( display_name = item.get('display_name') or item.get('name') version_to_install = item.get('version_to_install', '') + display.display_status_major( + "Installing %s (%s of %s)" + % (display_name, itemindex, len(installlist))) retcode = 0 if 'preinstall_script' in item: retcode = scriptutils.run_embedded_script('preinstall_script', item) if retcode == 0 and 'installer_item' in item: - display.display_status_major( - "Installing %s (%s of %s)" - % (display_name, itemindex, len(installlist))) - installer_type = item.get("installer_type", "") itempath = os.path.join(dirpath, item["installer_item"]) @@ -759,4 +759,4 @@ def run(only_unattended=False): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/installer/dmg.py b/code/client/munkilib/installer/dmg.py index 71edf5d5..3fbd2579 100644 --- a/code/client/munkilib/installer/dmg.py +++ b/code/client/munkilib/installer/dmg.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-03. Routines for copying items from disk images """ +from __future__ import absolute_import, print_function import os import shutil @@ -106,8 +107,9 @@ def remove_quarantine_from_item(some_path): '''Removes com.apple.quarantine from some_path''' try: if "com.apple.quarantine" in xattr.xattr(some_path).list(): - xattr.xattr(some_path).remove("com.apple.quarantine") - except BaseException, err: + xattr.xattr(some_path).remove("com.apple.quarantine", + options=xattr.XATTR_NOFOLLOW) + except BaseException as err: display.display_warning( "Error removing com.apple.quarantine from %s: %s", some_path, err) @@ -207,10 +209,13 @@ def copy_items_from_mountpoint(mountpoint, itemlist): # mv temp_destination_path to final destination path try: - if os.path.isdir(destination_path): + if (os.path.islink(destination_path) or + os.path.isfile(destination_path)): + os.unlink(destination_path) + elif os.path.isdir(destination_path): shutil.rmtree(destination_path) os.rename(temp_destination_path, destination_path) - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_error("Error moving item to destination: %s" % err) return -1 @@ -289,4 +294,4 @@ def copy_from_dmg(dmgpath, itemlist): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/installer/pkg.py b/code/client/munkilib/installer/pkg.py index 20ad8f8d..ce53e363 100644 --- a/code/client/munkilib/installer/pkg.py +++ b/code/client/munkilib/installer/pkg.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-03. Routines for installing Apple pkgs """ +from __future__ import absolute_import, print_function import os import pwd @@ -87,8 +88,8 @@ def pkg_needs_restart(pkgpath, options): proc = subprocess.Popen(cmd, shell=False, bufsize=-1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() - restartaction = str(output).decode('UTF-8').rstrip('\n') + output = proc.communicate()[0].decode('UTF-8') + restartaction = output.rstrip('\n') return (restartaction == 'RequireRestart' or restartaction == 'RecommendRestart') @@ -149,7 +150,7 @@ def _run_installer(cmd, env_vars, packagename): try: job = launchd.Job(cmd, environment_vars=env_vars) job.start() - except launchd.LaunchdJobException, err: + except launchd.LaunchdJobException as err: display.display_error( 'Error with launchd job (%s): %s', cmd, str(err)) display.display_error('Can\'t run installer.') @@ -310,4 +311,4 @@ def installall(dirpath, options=None): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/installer/rmpkgs.py b/code/client/munkilib/installer/rmpkgs.py index fd736bda..cd87a86a 100644 --- a/code/client/munkilib/installer/rmpkgs.py +++ b/code/client/munkilib/installer/rmpkgs.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ is made to revert to older versions of a file when uninstalling; only file removals are done. """ +from __future__ import absolute_import, print_function import os import subprocess @@ -284,7 +285,7 @@ def import_bom(bompath, curs): proc = subprocess.Popen(["/usr/sbin/pkgutil", "--pkg-info-plist", pkgid], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (pliststr, dummy_err) = proc.communicate() + pliststr = proc.communicate()[0] if pliststr: plist = FoundationPlist.readPlistFromString(pliststr) if "install-location" in plist: @@ -327,7 +328,7 @@ def import_from_pkgutil(pkgname, curs): proc = subprocess.Popen(["/usr/sbin/pkgutil", "--pkg-info-plist", pkgid], bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (pliststr, dummy_err) = proc.communicate() + pliststr = proc.communicate()[0] if pliststr: plist = FoundationPlist.readPlistFromString(pliststr) if "pkg-version" in plist: @@ -394,13 +395,13 @@ def init_database(forcerebuild=False): "Could not remove out-of-date receipt database.") return False - receiptsdir = '/Library/Receipts' + receiptsdir = u'/Library/Receipts' receiptlist = [] if os.path.exists(receiptsdir): receiptlist = [item for item in osutils.listdir(receiptsdir) - if item.endswith('.pkg')] + if item.endswith(u'.pkg')] - bomsdir = '/Library/Receipts/boms' + bomsdir = u'/Library/Receipts/boms' bomslist = [] if os.path.exists(bomsdir): bomslist = [item for item in osutils.listdir(bomsdir) @@ -413,11 +414,11 @@ def init_database(forcerebuild=False): stdout=subprocess.PIPE, stderr=subprocess.PIPE) while True: - line = proc.stdout.readline() + line = proc.stdout.readline().decode('UTF-8') if not line and (proc.poll() != None): break - pkglist.append(line.rstrip('\n')) + pkglist.append(line.rstrip(u'\n')) pkgcount = len(receiptlist) + len(bomslist) + len(pkglist) conn = sqlite3.connect(PACKAGEDB) @@ -615,10 +616,9 @@ def remove_receipts(pkgkeylist, noupdateapplepkgdb): proc = subprocess.Popen(cmd, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if output: - display.display_detail( - str(output).decode('UTF-8').rstrip('\n')) + display.display_detail(output.rstrip('\n')) display.display_percent_done(2, 4) @@ -675,10 +675,7 @@ def is_bundle(pathname): extension = os.path.splitext(basename)[1] if extension in bundle_extensions: return True - else: - return False - else: - return False + return False def inside_bundle(pathname): @@ -732,7 +729,7 @@ def remove_filesystem_items(removalpaths, forcedeletebundles): # directory is empty try: os.rmdir(pathtoremove) - except (OSError, IOError), err: + except (OSError, IOError) as err: msg = "Couldn't remove directory %s - %s" % ( pathtoremove, err) display.display_error(msg) @@ -770,7 +767,7 @@ def remove_filesystem_items(removalpaths, forcedeletebundles): # not a directory, just unlink it try: os.remove(pathtoremove) - except (OSError, IOError), err: + except (OSError, IOError) as err: msg = "Couldn't remove item %s: %s" % (pathtoremove, err) display.display_error(msg) removalerrors = removalerrors + "\n" + msg @@ -804,7 +801,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False, return -3 pkgkeyslist = getpkgkeys(pkgnames) - if len(pkgkeyslist) == 0: + if not pkgkeyslist: return -4 if processes.stop_requested(): @@ -817,7 +814,7 @@ def removepackages(pkgnames, forcedeletebundles=False, listfiles=False, if listfiles: removalpaths.sort() for item in removalpaths: - print "/" + item.encode('UTF-8') + print("/" + item.encode('UTF-8')) else: munkistatus.disableStopButton() remove_filesystem_items(removalpaths, forcedeletebundles) @@ -838,4 +835,4 @@ PACKAGEDB = os.path.join(prefs.pref('ManagedInstallDir'), "b.receiptdb") if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/installinfo.py b/code/client/munkilib/installinfo.py index 2b23e5fa..9ff8261b 100644 --- a/code/client/munkilib/installinfo.py +++ b/code/client/munkilib/installinfo.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-01. Functions for getting data from the InstallInfo.plist, etc """ +from __future__ import absolute_import, print_function # standard libs import os @@ -38,6 +39,12 @@ from . import prefs from . import reports from . import FoundationPlist +try: + _ = xrange # pylint: disable=xrange-builtin +except NameError: + # no xrange in Python 3 + xrange = range + # This many hours before a force install deadline, start notifying the user. FORCE_INSTALL_WARNING_HOURS = 4 @@ -288,4 +295,4 @@ def force_install_package_check(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/keychain.py b/code/client/munkilib/keychain.py index c9b9bc9b..4a24fcbb 100644 --- a/code/client/munkilib/keychain.py +++ b/code/client/munkilib/keychain.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2014-2019 Greg Neagle. +# Copyright 2014-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ Incorporating work and ideas from Michael Lynn here: and here: https://gist.github.com/pudquick/836a19b5ff17c5b7640d#file-cert_tricks-py """ +from __future__ import absolute_import, print_function import base64 import hashlib @@ -31,6 +32,7 @@ import subprocess from . import display from . import osutils from . import prefs +from .wrappers import unicode_or_str DEFAULT_KEYCHAIN_NAME = 'munki.keychain' @@ -46,7 +48,7 @@ def read_file(pathname): data = fileobj.read() fileobj.close() return data - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_error( 'Could not read %s: %s', pathname, err) return '' @@ -60,7 +62,7 @@ def write_file(stringdata, pathname): fileobject.write(stringdata) fileobject.close() return pathname - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_error( 'Couldn\'t write %s to %s: %s', stringdata, pathname, err) return '' @@ -84,7 +86,7 @@ def pem_cert_sha1_digest(cert_path): try: raw_bytes = pem_cert_bytes(cert_path) return hashlib.sha1(raw_bytes).hexdigest().upper() - except BaseException, err: + except BaseException as err: display.display_error('Error reading %s: %s' % (cert_path, err)) return None @@ -155,7 +157,7 @@ def get_client_cert_common_name(): proc = subprocess.Popen(cmd, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) - (out, err) = proc.communicate() + out = proc.communicate()[0].decode("UTF-8") if out: for i in out.split('/'): if i.startswith('CN='): @@ -203,7 +205,7 @@ def add_ca_certs_to_system_keychain(cert_info=None): '-k', system_keychain, cert) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not add CA cert %s into System keychain: %s', cert, err) @@ -256,7 +258,7 @@ def make_client_keychain(cert_info=None): '-p', keychain_pass, abs_keychain_path) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not create keychain %s: %s', abs_keychain_path, err) if original_home: @@ -292,7 +294,7 @@ def make_client_keychain(cert_info=None): 'import', client_cert_file, '-A', '-k', abs_keychain_path) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not import %s: %s', client_cert_file, err) if combined_pem: @@ -319,7 +321,7 @@ def make_client_keychain(cert_info=None): default_keychain = [ x.strip().strip('"') for x in output.split('\n') if x.strip()][0] - except SecurityError, err: + except SecurityError as err: # error raised if there is no default default_keychain = None # Temporarily assign the default keychain to ours @@ -330,7 +332,7 @@ def make_client_keychain(cert_info=None): display.display_debug2( 'Attempting to set default keychain to %s resulted in: %s', abs_keychain_path, output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not set default keychain to %s failed: %s' % (abs_keychain_path, err)) @@ -346,7 +348,7 @@ def make_client_keychain(cert_info=None): if output: display.display_debug2( 'security set-identity-preference output: ' + output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Setting identity preference for %s failed: %s' % (url, err)) @@ -390,7 +392,7 @@ def add_to_keychain_list(keychain_path): if output: display.display_debug2(output) added_keychain = True - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not add keychain %s to keychain list: %s', keychain_path, err) @@ -418,7 +420,7 @@ def remove_from_keychain_list(keychain_path): 'list-keychains', '-d', 'user', '-s', *filtered_keychains) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not set new keychain list: %s', err) @@ -432,14 +434,14 @@ def unlock_and_set_nonlocking(keychain_path): 'unlock-keychain', '-p', keychain_pass, keychain_path) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: # some problem unlocking the keychain. display.display_error( 'Could not unlock %s: %s.', keychain_path, err) # delete it try: os.unlink(keychain_path) - except OSError, err: + except OSError as err: display.display_error( 'Could not remove %s: %s.', keychain_path, err) return @@ -447,7 +449,7 @@ def unlock_and_set_nonlocking(keychain_path): output = security('set-keychain-settings', keychain_path) if output: display.display_debug2(output) - except SecurityError, err: + except SecurityError as err: display.display_error( 'Could not set keychain settings for %s: %s', keychain_path, err) @@ -466,6 +468,7 @@ def client_certs_exist(): def client_certs_newer_than_keychain(): '''Returns True if we have client certs that are newer than our client keychain, False otherwise''' + # pylint: disable=invalid-name cert_info = get_munki_client_cert_info() client_cert_path = cert_info['client_cert_path'] client_key_path = cert_info['client_key_path'] @@ -495,8 +498,8 @@ def debug_output(): display.display_debug1('***Info for %s***' % keychainfile) display.display_debug1( security('show-keychain-info', keychainfile)) - except SecurityError, err: - display.display_error(unicode(err)) + except SecurityError as err: + display.display_error(unicode_or_str(err)) class SecurityError(Exception): @@ -514,8 +517,8 @@ def security(verb_name, *args): stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, err) = proc.communicate() if proc.returncode: - raise SecurityError('%s: %s' % (proc.returncode, err)) - return output or err + raise SecurityError('%s: %s' % (proc.returncode, err.decode("UTF-8"))) + return (output or err).decode("UTF-8") def get_keychain_path(): @@ -543,6 +546,7 @@ def get_keychain_path(): class MunkiKeychain(object): '''Wrapper class for handling the client keychain''' + # pylint: disable=too-few-public-methods keychain_path = None added_keychain = False @@ -558,7 +562,7 @@ class MunkiKeychain(object): # we have client certs; we should build a keychain using them try: os.unlink(self.keychain_path) - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_error( 'Could not remove pre-existing %s: %s' % (self.keychain_path, err)) @@ -584,4 +588,4 @@ class MunkiKeychain(object): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/launchd/__init__.py b/code/client/munkilib/launchd/__init__.py index 06bc748f..eab62c9e 100644 --- a/code/client/munkilib/launchd/__init__.py +++ b/code/client/munkilib/launchd/__init__.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2011-2019 Greg Neagle. +# Copyright 2011-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ Returns a file descriptor for a socket defined in a launchd plist. A wrapper for using launchd to run a process as root outside of Munki's process space. Needed to properly run /usr/sbin/softwareupdate, for example. """ +from __future__ import absolute_import, print_function import os import subprocess @@ -80,11 +81,11 @@ def job_info(job_label): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output = proc.communicate()[0] + output = proc.communicate()[0].decode('UTF-8') if proc.returncode or not output: return info else: - lines = str(output).splitlines() + lines = output.splitlines() # search launchctl list output for our job label job_lines = [item for item in lines if item.endswith('\t' + job_label)] @@ -115,7 +116,7 @@ def stop_job(job_label): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - err = proc.communicate()[1] + err = proc.communicate()[1].decode('UTF-8') if proc.returncode: raise LaunchdJobException(err) @@ -127,7 +128,7 @@ def remove_job(job_label): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - err = proc.communicate()[1] + err = proc.communicate()[1].decode('UTF-8') if proc.returncode: raise LaunchdJobException(err) @@ -156,6 +157,12 @@ class Job(object): self.plist['StandardErrorPath'] = self.stderr_path if environment_vars: self.plist['EnvironmentVariables'] = environment_vars + # create stdout and stderr files + try: + open(self.stdout_path, 'wb').close() + open(self.stderr_path, 'wb').close() + except (OSError, IOError) as err: + raise LaunchdJobException(err) # write out launchd plist FoundationPlist.writePlist(self.plist, self.plist_path) # set owner, group and mode to those required @@ -167,7 +174,7 @@ class Job(object): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - err = proc.communicate()[1] + err = proc.communicate()[1].decode('UTF-8') if proc.returncode: raise LaunchdJobException(err) @@ -208,9 +215,9 @@ class Job(object): try: # open the stdout and stderr output files and # store their file descriptors for use - self.stdout = open(self.stdout_path, 'r') - self.stderr = open(self.stderr_path, 'r') - except (OSError, IOError), err: + self.stdout = open(self.stdout_path, 'rb') + self.stderr = open(self.stderr_path, 'rb') + except (OSError, IOError) as err: raise LaunchdJobException(err) def stop(self): @@ -231,4 +238,4 @@ class Job(object): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/launchd/launch1.py b/code/client/munkilib/launchd/launch1.py index 832e2dc3..9344f3ca 100755 --- a/code/client/munkilib/launchd/launch1.py +++ b/code/client/munkilib/launchd/launch1.py @@ -13,15 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. '''Python wrapper for original launchd checkin API''' +from __future__ import absolute_import # pylint: disable=wildcard-import # pylint: disable=unused-wildcard-import from ctypes import * # pylint: enable=unused-wildcard-import # pylint: enable=wildcard-import + # pylint: disable=invalid-name libc = CDLL("/usr/lib/libc.dylib") -# pylint: enable=invalid-name c_launch_data_t = c_void_p @@ -83,7 +84,7 @@ launch_data_new_string.argtypes = [c_char_p] launch_msg = libc.launch_msg launch_msg.restype = c_launch_data_t launch_msg.argtypes = [c_launch_data_t] - +# pylint: enable=invalid-name LAUNCH_KEY_SUBMITJOB = c_char_p("SubmitJob") LAUNCH_KEY_REMOVEJOB = c_char_p("RemoveJob") @@ -204,7 +205,7 @@ LAUNCH_JOBSOCKETKEY_MULTICASTGROUP = c_char_p("MulticastGroup") LAUNCH_DATA_OPAQUE, LAUNCH_DATA_ERRNO, LAUNCH_DATA_MACHPORT -) = range(1, 11) +) = list(range(1, 11)) class LaunchDCheckInError(Exception): @@ -217,7 +218,7 @@ def get_launchd_socket_fds(): # Return a dictionary with keys pointing to lists of file descriptors. launchd_socket_fds = dict() - def add_socket(launch_array, name, context=None): + def add_socket(launch_array, name, _context=None): '''Callback for dict iterator.''' if launch_data_get_type(launch_array) != LAUNCH_DATA_ARRAY: raise LaunchDCheckInError( @@ -245,7 +246,7 @@ def get_launchd_socket_fds(): if launch_data_get_type(checkin_response) == LAUNCH_DATA_ERRNO: errno = launch_data_get_errno(checkin_response) - raise LaunchDCheckInError("Checkin failed") + raise LaunchDCheckInError("Checkin failed. Error: %s" % errno) # Get a dictionary of sockets. sockets = launch_data_dict_lookup( diff --git a/code/client/munkilib/launchd/launch2.py b/code/client/munkilib/launchd/launch2.py index 3b83dd9a..bf669105 100755 --- a/code/client/munkilib/launchd/launch2.py +++ b/code/client/munkilib/launchd/launch2.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. '''Python wrapper for updated launchd checkin API''' +from __future__ import absolute_import import os # pylint: disable=wildcard-import @@ -24,6 +25,11 @@ from ctypes import * libc = CDLL("/usr/lib/libc.dylib") # pylint: enable=invalid-name +try: + _ = xrange # pylint: disable=xrange-builtin +except NameError: + # no xrange in Python 3 + xrange = range # int launch_activate_socket(const char *name, int **fds, size_t *cnt) libc.launch_activate_socket.restype = c_int diff --git a/code/client/munkilib/munkicommon.py b/code/client/munkilib/munkicommon.py index f6c75cbc..c3e04245 100644 --- a/code/client/munkilib/munkicommon.py +++ b/code/client/munkilib/munkicommon.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2008-11-18. Common functions used by the munki tools. """ +from __future__ import absolute_import, print_function # this module currently exists purely for backwards compatibility so that # anything calling munkicommon functions will still work (for now) @@ -42,4 +43,4 @@ from .scriptutils import * # pylint: enable=wildcard-import if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/munkihash.py b/code/client/munkilib/munkihash.py index 8474c16e..49914624 100644 --- a/code/client/munkilib/munkihash.py +++ b/code/client/munkilib/munkihash.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2016-12-14. Munki's hash functions """ +from __future__ import absolute_import, print_function import hashlib import os @@ -39,15 +40,17 @@ def gethash(filename, hash_function): """ if not os.path.isfile(filename): return 'NOT A FILE' - - fileref = open(filename, 'rb') - while 1: - chunk = fileref.read(2**16) - if not chunk: - break - hash_function.update(chunk) - fileref.close() - return hash_function.hexdigest() + try: + fileref = open(filename, 'rb') + while True: + chunk = fileref.read(2**16) + if not chunk: + break + hash_function.update(chunk) + fileref.close() + return hash_function.hexdigest() + except (OSError, IOError): + return 'HASH_ERROR' def getmd5hash(filename): @@ -67,4 +70,4 @@ def getsha256hash(filename): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/munkilog.py b/code/client/munkilib/munkilog.py index 36753429..f422a521 100644 --- a/code/client/munkilib/munkilog.py +++ b/code/client/munkilib/munkilog.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,7 +21,9 @@ Created by Greg Neagle on 2016-12-14. Logging functions for Munki """ +from __future__ import absolute_import, print_function +import codecs import logging import logging.handlers import os @@ -30,6 +32,14 @@ import time from . import prefs +def logging_level(): + '''Returns the logging level, which might be defined badly by the admin''' + try: + return int(prefs.pref('LoggingLevel')) + except TypeError: + return 1 + + def log(msg, logname=''): """Generic logging function.""" if len(msg) > 1000: @@ -50,9 +60,9 @@ def log(msg, logname=''): else: logpath = os.path.join(os.path.dirname(prefs.pref('LogFile')), logname) try: - fileobj = open(logpath, mode='a', buffering=1) + fileobj = codecs.open(logpath, mode='a', buffering=1, encoding='UTF-8') try: - print >> fileobj, time.strftime(formatstr), msg.encode('UTF-8') + fileobj.write("%s %s\n" % (time.strftime(formatstr), msg)) except (OSError, IOError): pass fileobj.close() @@ -130,4 +140,4 @@ def reset_errors(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/munkirepo/FileRepo.py b/code/client/munkilib/munkirepo/FileRepo.py index 837a7896..6957ee12 100644 --- a/code/client/munkilib/munkirepo/FileRepo.py +++ b/code/client/munkilib/munkirepo/FileRepo.py @@ -1,5 +1,6 @@ # encoding: utf-8 '''Defines FileRepo plugin. See docstring for FileRepo class''' +from __future__ import absolute_import, print_function import errno import getpass @@ -7,11 +8,23 @@ import os import shutil import subprocess import sys -import urllib -from urlparse import urlparse +try: + # Python 2 + from urllib import unquote +except ImportError: + # Python 3 + from urllib.parse import unquote + +try: + # Python 2 + from urlparse import urlparse +except ImportError: + # Python 3 + from urllib.parse import urlparse from munkilib.munkirepo import Repo, RepoError +from munkilib.wrappers import get_input # NetFS share mounting code borrowed and liberally adapted from Michael Lynn's @@ -25,6 +38,7 @@ try: __getattr__ = dict.__getitem__ __setattr__ = dict.__setitem__ + # pylint: disable=invalid-name NetFS = Attrdict() # Can cheat and provide 'None' for the identifier, it'll just use # frameworkPath instead @@ -33,13 +47,16 @@ try: 'NetFS', frameworkIdentifier=None, frameworkPath=objc.pathForFramework('NetFS.framework'), globals=NetFS, scan_classes=False) + # pylint: enable=invalid-name # https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ # ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html # Fix NetFSMountURLSync signature del NetFS['NetFSMountURLSync'] + # pylint: disable=no-member objc.loadBundleFunctions( - NetFS_bundle, NetFS, [('NetFSMountURLSync', 'i@@@@@@o^@')]) + NetFS_bundle, NetFS, [('NetFSMountURLSync', b'i@@@@@@o^@')]) + # pylint: enable=no-member NETFSMOUNTURLSYNC_AVAILABLE = True except (ImportError, KeyError): NETFSMOUNTURLSYNC_AVAILABLE = False @@ -57,9 +74,13 @@ class ShareAuthenticationNeededException(ShareMountException): def unicodeize(path): '''Convert a path to unicode''' - if type(path) is str: + # pylint: disable=unicode-builtin + # Python 3 all paths are unicode! + if sys.version_info.major > 2: + return path + if isinstance(path, str): return unicode(path, 'utf-8') - elif type(path) is not unicode: + elif not isinstance(path, unicode): return unicode(path) return path @@ -131,7 +152,7 @@ def mount_share_url(share_url): try: mountpoint = mount_share(share_url) except ShareAuthenticationNeededException: - username = raw_input('Username: ') + username = get_input('Username: ') password = getpass.getpass() mountpoint = mount_share_with_credentials(share_url, username, password) return mountpoint @@ -140,6 +161,7 @@ def mount_share_url(share_url): class FileRepo(Repo): '''Handles local filesystem repo and repos mounted via filesharing''' + # pylint: disable=super-init-not-called def __init__(self, baseurl): '''Constructor''' self.baseurl = baseurl @@ -147,14 +169,15 @@ class FileRepo(Repo): self.url_scheme = url_parts.scheme if self.url_scheme == 'file': # local file repo - self.root = unicodeize(urllib.unquote(url_parts.path)) + self.root = unicodeize(unquote(url_parts.path)) else: # repo is on a fileshare that will be mounted under /Volumes self.root = os.path.join( u'/Volumes', - unicodeize(urllib.unquote(url_parts.path).lstrip('/'))) + unicodeize(unquote(url_parts.path).lstrip('/'))) self.we_mounted_repo = False self._connect() + # pylint: enable=super-init-not-called def __del__(self): '''Destructor -- unmount the fileshare if we mounted it''' @@ -166,20 +189,19 @@ class FileRepo(Repo): '''If self.root is present, return. Otherwise, if the url scheme is not "file:" then try to mount the share url.''' if not os.path.exists(self.root) and self.url_scheme != 'file': - print u'Attempting to mount fileshare %s:' % self.baseurl + print(u'Attempting to mount fileshare %s:' % self.baseurl) if NETFSMOUNTURLSYNC_AVAILABLE: try: self.root = mount_share_url(self.baseurl) - except ShareMountException, err: + except ShareMountException as err: raise RepoError(err) else: self.we_mounted_repo = True else: try: os.mkdir(self.root) - except (OSError, IOError), err: - raise RepoError( - u'Could not make repo mountpoint: %s' % unicode(err)) + except (OSError, IOError) as err: + raise RepoError(u'Could not make repo mountpoint: %s' % err) if self.baseurl.startswith('afp:'): cmd = ['/sbin/mount_afp', '-i', self.baseurl, self.root] elif self.baseurl.startswith('smb:'): @@ -187,7 +209,7 @@ class FileRepo(Repo): elif self.baseurl.startswith('nfs://'): cmd = ['/sbin/mount_nfs', self.baseurl[6:], self.root] else: - print >> sys.stderr, 'Unsupported filesystem URL!' + print('Unsupported filesystem URL!', file=sys.stderr) return retcode = subprocess.call(cmd) if retcode: @@ -219,7 +241,7 @@ class FileRepo(Repo): rel_path = abs_path[len(search_dir):].lstrip("/") file_list.append(rel_path) return file_list - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) def get(self, resource_identifier): @@ -232,11 +254,11 @@ class FileRepo(Repo): resource_identifier = unicodeize(resource_identifier) repo_filepath = os.path.join(self.root, resource_identifier) try: - fileref = open(repo_filepath) + fileref = open(repo_filepath, 'rb') data = fileref.read() fileref.close() return data - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) def get_to_local_file(self, resource_identifier, local_file_path): @@ -251,7 +273,7 @@ class FileRepo(Repo): local_file_path = unicodeize(local_file_path) try: shutil.copyfile(repo_filepath, local_file_path) - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) def put(self, resource_identifier, content): @@ -263,12 +285,12 @@ class FileRepo(Repo): repo_filepath = os.path.join(self.root, resource_identifier) dir_path = os.path.dirname(repo_filepath) if not os.path.exists(dir_path): - os.makedirs(dir_path, 0755) + os.makedirs(dir_path, 0o755) try: - fileref = open(repo_filepath, 'w') + fileref = open(repo_filepath, 'wb') fileref.write(content) fileref.close() - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) def put_from_local_file(self, resource_identifier, local_file_path): @@ -284,10 +306,10 @@ class FileRepo(Repo): return dir_path = os.path.dirname(repo_filepath) if not os.path.exists(dir_path): - os.makedirs(dir_path, 0755) + os.makedirs(dir_path, 0o755) try: shutil.copyfile(local_file_path, repo_filepath) - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) def delete(self, resource_identifier): @@ -299,5 +321,5 @@ class FileRepo(Repo): repo_filepath = os.path.join(self.root, resource_identifier) try: os.remove(repo_filepath) - except (OSError, IOError), err: + except (OSError, IOError) as err: raise RepoError(err) diff --git a/code/client/munkilib/munkirepo/GitFileRepo.py b/code/client/munkilib/munkirepo/GitFileRepo.py index 11bf900d..0fda25ac 100644 --- a/code/client/munkilib/munkirepo/GitFileRepo.py +++ b/code/client/munkilib/munkirepo/GitFileRepo.py @@ -1,5 +1,6 @@ # encoding: utf-8 '''Subclasses FileRepo to do git commits of file changes''' +from __future__ import absolute_import, print_function import inspect import os @@ -7,7 +8,7 @@ import pwd import subprocess import sys -from FileRepo import FileRepo +from munkilib.munkirepo.FileRepo import FileRepo # TODO: make this more easily customized GITCMD = '/usr/bin/git' @@ -36,8 +37,11 @@ class MunkiGit(object): stdout=subprocess.PIPE, stderr=subprocess.PIPE) (output, error) = proc.communicate() - self.results = {"output": output, - "error": error, "returncode": proc.returncode} + self.results = { + "output": output.decode('UTF-8'), + "error": error.decode('UTF-8'), + "returncode": proc.returncode + } return self.results def path_is_gitignored(self, a_path): @@ -88,11 +92,11 @@ class MunkiGit(object): # generate the log message log_msg = ( '%s %s \'%s\' via %s' % (username, action, itempath, toolname)) - print "Doing git commit: %s" % log_msg + print("Doing git commit: %s" % log_msg) self.run_git(['commit', '-m', log_msg]) if self.results['returncode'] != 0: - print >> sys.stderr, "Failed to commit changes to %s" % a_path - print >> sys.stderr, self.results['error'] + print("Failed to commit changes to %s" % a_path, file=sys.stderr) + print(self.results['error'], file=sys.stderr) return -1 return 0 @@ -106,9 +110,10 @@ class MunkiGit(object): if self.results['returncode'] == 0: self.commit_file_at_path(a_path) else: - print >> sys.stderr, "Git error: %s" % self.results['error'] + print("Git error: %s" % self.results['error'], + file=sys.stderr) else: - print >> sys.stderr, "%s is not in a git repo." % a_path + print("%s is not in a git repo." % a_path, file=sys.stderr) def add_file_at_path(self, a_path): """Commits a file to the Git repo.""" diff --git a/code/client/munkilib/munkirepo/MWA2APIRepo.py b/code/client/munkilib/munkirepo/MWA2APIRepo.py index 0567c7e9..1f545381 100644 --- a/code/client/munkilib/munkirepo/MWA2APIRepo.py +++ b/code/client/munkilib/munkirepo/MWA2APIRepo.py @@ -1,48 +1,60 @@ # encoding: utf-8 '''Defines MWA2APIRepo plugin. See docstring for MWA2APIRepo class''' +from __future__ import absolute_import, print_function import base64 import getpass import os -import plistlib import subprocess import tempfile -import urllib2 -from xml.parsers.expat import ExpatError + +try: + # Python 2 + from urllib2 import quote +except ImportError: + from urllib.parse import quote from munkilib.munkirepo import Repo, RepoError +from munkilib.wrappers import get_input, readPlistFromString, PlistReadError DEBUG = False # TODO: make this more easily configurable CURL_CMD = '/usr/bin/curl' + class CurlError(Exception): + '''Error for curl operations''' pass class MWA2APIRepo(Repo): + '''Class for working with a repo accessible via the MWA2 API''' + # pylint: disable=super-init-not-called def __init__(self, baseurl): '''Constructor''' self.baseurl = baseurl self.authtoken = None self._connect() + # pylint: enable=super-init-not-called def _connect(self): '''For a fileshare repo, we'd mount the share, prompting for - credentials if needed. For the API repo, well look for a stored + credentials if needed. For the API repo, we'll look for a stored authtoken; if we don't find one, we'll prompt for credentials and make an authtoken.''' if not self.authtoken: if 'MUNKIREPO_AUTHTOKEN' in os.environ: self.authtoken = os.environ['MUNKIREPO_AUTHTOKEN'] else: - print 'Please provide credentials for %s:' % self.baseurl - username = raw_input('Username: ') + print('Please provide credentials for %s:' % self.baseurl) + username = get_input('Username: ') password = getpass.getpass() - user_and_pass = '%s:%s' % (username, password) - self.authtoken = 'Basic %s' % base64.b64encode(user_and_pass) + user_and_pass = ('%s:%s' % (username, password)).encode("UTF-8") + self.authtoken = 'Basic %s' % base64.b64encode( + user_and_pass).decode("UTF-8") + def _curl(self, relative_url, headers=None, method='GET', filename=None, content=None, formdata=None): @@ -52,23 +64,23 @@ class MWA2APIRepo(Repo): contentpath = None fileref, directivepath = tempfile.mkstemp() fileobj = os.fdopen(fileref, 'w') - print >> fileobj, 'silent' # no progress meter - print >> fileobj, 'show-error' # print error msg to stderr - print >> fileobj, 'fail' # throw error if download fails - print >> fileobj, 'location' # follow redirects - print >> fileobj, 'request = %s' % method + print('silent', file=fileobj) # no progress meter + print('show-error', file=fileobj) # print error msg to stderr + print('fail', file=fileobj) # throw error if download fails + print('location', file=fileobj) # follow redirects + print('request = %s' % method, file=fileobj) if headers: for key in headers: - print >> fileobj, 'header = "%s: %s"' % (key, headers[key]) - print >> fileobj, 'header = "Authorization: %s"' % self.authtoken + print('header = "%s: %s"' % (key, headers[key]), file=fileobj) + print('header = "Authorization: %s"' % self.authtoken, file=fileobj) if formdata: for line in formdata: - print >> fileobj, 'form = "%s"' % line + print('form = "%s"' % line, file=fileobj) url = os.path.join(self.baseurl, relative_url) - print >> fileobj, 'url = "%s"' % url + print('url = "%s"' % url, file=fileobj) fileobj.close() cmd = [CURL_CMD, '-q', '--config', directivepath] @@ -81,7 +93,7 @@ class MWA2APIRepo(Repo): # it's a lot of data; let's write it to a local file first # because we can't really pass it all via subprocess fileref, contentpath = tempfile.mkstemp() - fileobj = os.fdopen(fileref, 'w') + fileobj = os.fdopen(fileref, 'wb') fileobj.write(content) fileobj.close() cmd.extend(['-d', '@%s' % contentpath]) @@ -92,7 +104,8 @@ class MWA2APIRepo(Repo): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) output, err = proc.communicate() - + output = output.decode('UTF-8') + err = err.decode('UTF-8') if DEBUG: # save our curl_directives for debugging fileref = open(directivepath) @@ -115,22 +128,22 @@ class MWA2APIRepo(Repo): '''Returns a list of identifiers for each item of kind. Kind might be 'catalogs', 'manifests', 'pkgsinfo', 'pkgs', or 'icons'. For a file-backed repo this would be a list of pathnames.''' - url = urllib2.quote(kind.encode('UTF-8')) + '?api_fields=filename' + url = quote(kind.encode('UTF-8')) + '?api_fields=filename' headers = {'Accept': 'application/xml'} try: data = self._curl(url, headers=headers) - except CurlError, err: + except CurlError as err: raise RepoError(err) try: - plist = plistlib.readPlistFromString(data) - except ExpatError, err: + plist = readPlistFromString(data) + except PlistReadError as err: raise RepoError(err) if kind in ['catalogs', 'manifests', 'pkgsinfo']: # it's a list of dicts containing 'filename' key/values return [item['filename'] for item in plist] - else: - # it's a list of filenames - return plist + + # it's a list of filenames (pkgs, icons) + return plist def get(self, resource_identifier): '''Returns the content of item with given resource_identifier. @@ -139,7 +152,7 @@ class MWA2APIRepo(Repo): /pkgsinfo/apps/Firefox-52.0.plist. Avoid using this method with the 'pkgs' kind as it might return a really large blob of data.''' - url = urllib2.quote(resource_identifier.encode('UTF-8')) + url = quote(resource_identifier.encode('UTF-8')) if resource_identifier.startswith( ('catalogs/', 'manifests/', 'pkgsinfo/')): headers = {'Accept': 'application/xml'} @@ -147,7 +160,7 @@ class MWA2APIRepo(Repo): headers = {} try: return self._curl(url, headers=headers) - except CurlError, err: + except CurlError as err: raise RepoError(err) def get_to_local_file(self, resource_identifier, local_file_path): @@ -157,7 +170,7 @@ class MWA2APIRepo(Repo): of 'pkgsinfo/apps/Firefox-52.0.plist' would copy the contents of /pkgsinfo/apps/Firefox-52.0.plist to a local file given by local_file_path.''' - url = urllib2.quote(resource_identifier.encode('UTF-8')) + url = quote(resource_identifier.encode('UTF-8')) if resource_identifier.startswith( ('catalogs/', 'manifests/', 'pkgsinfo/')): headers = {'Accept': 'application/xml'} @@ -165,7 +178,7 @@ class MWA2APIRepo(Repo): headers = {} try: self._curl(url, headers=headers, filename=local_file_path) - except CurlError, err: + except CurlError as err: raise RepoError(err) def put(self, resource_identifier, content): @@ -173,7 +186,7 @@ class MWA2APIRepo(Repo): For a file-backed repo, a resource_identifier of 'pkgsinfo/apps/Firefox-52.0.plist' would result in the content being saved to /pkgsinfo/apps/Firefox-52.0.plist.''' - url = urllib2.quote(resource_identifier.encode('UTF-8')) + url = quote(resource_identifier.encode('UTF-8')) if resource_identifier.startswith( ('catalogs/', 'manifests/', 'pkgsinfo/')): headers = {'Content-type': 'application/xml'} @@ -181,7 +194,7 @@ class MWA2APIRepo(Repo): headers = {} try: self._curl(url, headers=headers, method='PUT', content=content) - except CurlError, err: + except CurlError as err: raise RepoError(err) def put_from_local_file(self, resource_identifier, local_file_path): @@ -189,7 +202,7 @@ class MWA2APIRepo(Repo): resource_identifier. For a file-backed repo, a resource_identifier of 'pkgsinfo/apps/Firefox-52.0.plist' would result in the content being saved to /pkgsinfo/apps/Firefox-52.0.plist.''' - url = urllib2.quote(resource_identifier.encode('UTF-8')) + url = quote(resource_identifier.encode('UTF-8')) if resource_identifier.startswith(('pkgs/', 'icons/')): # MWA2API only supports POST for pkgs and icons @@ -197,14 +210,14 @@ class MWA2APIRepo(Repo): formdata = ['filedata=@%s' % local_file_path] try: self._curl(url, method='POST', formdata=formdata) - except CurlError, err: + except CurlError as err: raise RepoError(err) else: headers = {'Content-type': 'application/xml'} try: self._curl(url, headers=headers, method='PUT', filename=local_file_path) - except CurlError, err: + except CurlError as err: raise RepoError(err) def delete(self, resource_identifier): @@ -212,9 +225,9 @@ class MWA2APIRepo(Repo): For a file-backed repo, a resource_identifier of 'pkgsinfo/apps/Firefox-52.0.plist' would result in the deletion of /pkgsinfo/apps/Firefox-52.0.plist.''' - url = urllib2.quote(resource_identifier.encode('UTF-8')) + url = quote(resource_identifier.encode('UTF-8')) try: self._curl(url, method='DELETE') - except CurlError, err: + except CurlError as err: raise RepoError(err) \ No newline at end of file diff --git a/code/client/munkilib/munkirepo/__init__.py b/code/client/munkilib/munkirepo/__init__.py index 6b9a84da..55289f42 100644 --- a/code/client/munkilib/munkirepo/__init__.py +++ b/code/client/munkilib/munkirepo/__init__.py @@ -1,28 +1,61 @@ +'''Base bits for repo plugins''' +from __future__ import absolute_import, print_function + import imp import os import sys - -class RepoError(Exception): - '''Base exception for repo errors''' - pass +from ._baseclasses import RepoError, Repo +from .FileRepo import FileRepo -class Repo(object): - '''Abstract base class for repo''' - def __init__(self, url): - '''Override in subclasses''' - pass +def import_plugins(dirpath=None): + """Imports plugins from dirpath or the directory this file is in""" + plugin_names = [] + + if not dirpath: + # get the directory this __init__.py file is in + dirpath = os.path.dirname(os.path.abspath(__file__)) + + # find all the .py files (minus __init__.py) + plugin_files = [ + os.path.splitext(name)[0] + for name in os.listdir(dirpath) + if name.endswith(".py") and not name.startswith("_") + ] + + for name in plugin_files: + if name in globals(): + # we already imported it + plugin_names.append(name) + continue + plugin_filename = os.path.join(dirpath, name + ".py") + try: + # attempt to import the module + _tmp = imp.load_source(name, plugin_filename) + # look for an attribute with the plugin name + plugin = getattr(_tmp, name) + # add the processor to munkirepo's namespace + globals()[name] = plugin + plugin_names.append(name) + except (ImportError, AttributeError) as err: + # if we aren't successful, print a warning + print( + "WARNING: %s: %s" % (plugin_filename, err), file=sys.stderr + ) + return plugin_names + +__all__ = import_plugins() -def plugin_named(name): +# Helper functions for munkirepo plugins + +def plugin_named(some_name): '''Returns a plugin object given a name''' try: - module = globals()[name] - return getattr(module, name) + return globals()[some_name] except (KeyError, AttributeError): - print >> sys.stderr, ( - "ERROR: %s repo plugin not found." % name) + print("ERROR: %s repo plugin not found." % some_name, file=sys.stderr) return None @@ -32,12 +65,4 @@ def connect(repo_url, plugin_name): if plugin: return plugin(repo_url) else: - raise RepoError('Could not find repo plugin named %s' % plugin_name) - - -# yes, having this at the end is weird. But it allows us to dynamically import -# additional modules from our directory -__all__ = [os.path.splitext(name)[0] - for name in os.listdir(os.path.dirname(os.path.abspath(__file__))) - if name.endswith('.py') and not name == '__init__.py'] -from . import * + raise RepoError('Could not find repo plugin named: %s' % plugin_name) diff --git a/code/client/munkilib/munkirepo/_baseclasses.py b/code/client/munkilib/munkirepo/_baseclasses.py new file mode 100644 index 00000000..ba89381d --- /dev/null +++ b/code/client/munkilib/munkirepo/_baseclasses.py @@ -0,0 +1,15 @@ +# encoding: utf-8 +"""Base classes for repo plugins""" + +class RepoError(Exception): + '''Base exception for repo errors''' + pass + + +# pylint: disable=too-few-public-methods +class Repo(object): + '''Abstract base class for repo''' + def __init__(self, url): + '''Override in subclasses''' + pass +# pylint: enable=too-few-public-methods diff --git a/code/client/munkilib/munkistatus.py b/code/client/munkilib/munkistatus.py index ed4be49e..871507e4 100644 --- a/code/client/munkilib/munkistatus.py +++ b/code/client/munkilib/munkistatus.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2009-09-24. Utility functions for using MunkiStatus.app to display status and progress. """ +from __future__ import absolute_import, print_function import os import time @@ -157,4 +158,4 @@ def restartAlert(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/osinstaller.py b/code/client/munkilib/osinstaller.py index 49fd69cc..9247adae 100644 --- a/code/client/munkilib/osinstaller.py +++ b/code/client/munkilib/osinstaller.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-03-29. Support for using startosinstall to install macOS. """ +from __future__ import absolute_import, print_function # stdlib imports import os @@ -124,7 +125,7 @@ class StartOSInstallRunner(object): self.dmg_mountpoint = None self.got_sigusr1 = False - def sigusr1_handler(self, dummy_signum, dummy_frame): + def sigusr1_handler(self, _signum, _frame): '''Signal handler for SIGUSR1 from startosinstall, which tells us it's done setting up the macOS install and is ready and waiting to reboot''' display.display_debug1('Got SIGUSR1 from startosinstall') @@ -139,7 +140,7 @@ class StartOSInstallRunner(object): # set Munki to run at boot after the OS upgrade is complete try: bootstrapping.set_bootstrap_mode() - except bootstrapping.SetupError, err: + except bootstrapping.SetupError as err: display.display_error( 'Could not set up Munki to run after OS upgrade is complete: ' '%s', err) @@ -464,11 +465,11 @@ def startosinstall(installer, finishing_tasks=None, installinfo=None): installer, finishing_tasks=finishing_tasks, installinfo=installinfo).start() return True - except StartOSInstallError, err: + except StartOSInstallError as err: display.display_error( - u'Error starting macOS install: %s', unicode(err)) + u'Error starting macOS install: %s', err) munkilog.log( - 'Starting macOS install: FAILED: %s' % unicode(err), 'Install.log') + u'Starting macOS install: FAILED: %s' % err, 'Install.log') return False @@ -519,4 +520,4 @@ def run(finishing_tasks=None): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/osutils.py b/code/client/munkilib/osutils.py index e6a8e221..806d50ff 100644 --- a/code/client/munkilib/osutils.py +++ b/code/client/munkilib/osutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2016-12-13. Common functions and classes used by the munki tools. """ +from __future__ import absolute_import, print_function import platform import os @@ -52,8 +53,8 @@ def getOsVersion(only_major_minor=True, as_tuple=False): os_version_tuple = os_version_tuple[0:2] if as_tuple: return tuple(map(int, os_version_tuple)) - else: - return '.'.join(os_version_tuple) + # default + return '.'.join(os_version_tuple) def tmpdir(): @@ -68,7 +69,7 @@ def cleanUpTmpDir(): if hasattr(tmpdir, 'cache'): try: shutil.rmtree(tmpdir.cache) - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_warning( 'Unable to clean up temporary dir %s: %s', tmpdir.cache, str(err)) @@ -95,9 +96,14 @@ def listdir(path): # https://developer.apple.com/library/mac/#qa/qa2001/qa1235.html # http://lists.zerezo.com/git/msg643117.html # http://unicode.org/reports/tr15/ section 1.2 - if type(path) is str: - path = unicode(path, 'utf-8') - elif type(path) is not unicode: + # pylint: disable=unicode-builtin + if isinstance(path, str): + try: + path = unicode(path, 'utf-8') + except NameError: + # Python 3 + pass + elif not isinstance(path, unicode): path = unicode(path) return os.listdir(path) @@ -114,8 +120,8 @@ def currentGUIusers(): proc = subprocess.Popen('/usr/bin/who', shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() - lines = str(output).splitlines() + output = proc.communicate()[0].decode("UTF-8") + lines = output.splitlines() for line in lines: if 'console' in line: parts = line.split() @@ -134,7 +140,7 @@ def pythonScriptRunning(scriptname): proc = subprocess.Popen(cmd, shell=False, bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() + out = proc.communicate()[0].decode("UTF-8") mypid = os.getpid() lines = str(out).splitlines() for line in lines: @@ -172,10 +178,11 @@ def osascript(osastring): stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = proc.communicate() if proc.returncode != 0: - print >> sys.stderr, 'Error: ', err + print('Error: ', err.decode('UTF-8'), file=sys.stderr) if out: - return str(out).decode('UTF-8').rstrip('\n') + return out.decode('UTF-8').rstrip('\n') + return u'' if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/pkgutils.py b/code/client/munkilib/pkgutils.py index 9fe2bcab..61b387bc 100644 --- a/code/client/munkilib/pkgutils.py +++ b/code/client/munkilib/pkgutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,16 +20,23 @@ Created by Greg Neagle on 2016-12-14. Common pkg/receipt functions and classes used by the munki tools. """ +from __future__ import absolute_import, print_function import os import re import shutil import subprocess import tempfile -import urllib2 + +try: + # Python 2 + from urllib import unquote +except ImportError: + # Python 3 + from urllib.parse import unquote + from distutils import version -from types import StringType from xml.dom import minidom from . import display @@ -37,6 +44,7 @@ from . import osutils from . import utils from . import FoundationPlist + # we use lots of camelCase-style names. Deal with it. # pylint: disable=C0103 @@ -56,31 +64,50 @@ def getPkgRestartInfo(filename): stdout=subprocess.PIPE, stderr=subprocess.PIPE) (out, err) = proc.communicate() + out = out.decode('UTF-8') + err = err.decode('UTF-8') if proc.returncode: display.display_error("installer -query failed: %s %s", out, err) return {} if out: - restartAction = str(out).rstrip('\n') + restartAction = out.rstrip('\n') if restartAction != 'None': installerinfo['RestartAction'] = restartAction return installerinfo +def _cmp(x, y): + """ + Replacement for built-in function cmp that was removed in Python 3 + + Compare the two objects x and y and return an integer according to + the outcome. The return value is negative if x < y, zero if x == y + and strictly positive if x > y. + """ + return (x > y) - (x < y) + + class MunkiLooseVersion(version.LooseVersion): '''Subclass version.LooseVersion to compare things like "10.6" and "10.6.0" as equal''' def __init__(self, vstring=None): + """init method""" + # pylint: disable=unicode-builtin if vstring is None: # treat None like an empty string self.parse('') if vstring is not None: - if isinstance(vstring, unicode): - # unicode string! Why? Oh well... - # convert to string so version.LooseVersion doesn't choke - vstring = vstring.encode('UTF-8') + try: + if isinstance(vstring, unicode): + # unicode string! Why? Oh well... + # convert to string so version.LooseVersion doesn't choke + vstring = vstring.encode('UTF-8') + except NameError: + # python 3 + pass self.parse(str(vstring)) def _pad(self, version_list, max_length): @@ -92,15 +119,56 @@ class MunkiLooseVersion(version.LooseVersion): cmp_list.append(0) return cmp_list - def __cmp__(self, other): - if isinstance(other, StringType): + def _compare(self, other): + """Complete comparison mechanism since LooseVersion's is broken + in Python 3""" + if not isinstance(other, version.LooseVersion): other = MunkiLooseVersion(other) max_length = max(len(self.version), len(other.version)) self_cmp_version = self._pad(self.version, max_length) other_cmp_version = self._pad(other.version, max_length) + cmp_result = 0 + for index, value in enumerate(self_cmp_version): + try: + cmp_result = _cmp(value, other_cmp_version[index]) + except TypeError: + # integer is less than character/string + if isinstance(value, int): + return -1 + return 1 + else: + if cmp_result: + return cmp_result + return cmp_result - return cmp(self_cmp_version, other_cmp_version) + def __hash__(self): + """Hash method""" + return hash(self.version) + + def __eq__(self, other): + """Equals comparison""" + return self._compare(other) == 0 + + def __ne__(self, other): + """Not-equals comparison""" + return self._compare(other) != 0 + + def __lt__(self, other): + """Less than comparison""" + return self._compare(other) < 0 + + def __le__(self, other): + """Less than or equals comparison""" + return self._compare(other) <= 0 + + def __gt__(self, other): + """Greater than comparison""" + return self._compare(other) > 0 + + def __ge__(self, other): + """Greater than or equals comparison""" + return self._compare(other) >= 0 def padVersionString(versString, tupleCount): @@ -150,6 +218,7 @@ def getVersionString(plist, key=None): # lets us use crappy values like '1.0 (100)' VersionString = plist[key].split()[0] if VersionString: + # check first character to see if it's a digit if VersionString[0] in '0123456789': # starts with a number; that's good # now for another edge case thanks to Adobe: @@ -161,7 +230,8 @@ def getVersionString(plist, key=None): # a future version of the Munki tools may drop this magic # and require admins to explicitly choose the CFBundleVersion # but for now Munki does some magic - VersionString = plist['CFBundleVersion'].encode('utf-8').split()[0] + VersionString = plist['CFBundleVersion'].split()[0] + # check first character to see if it's a digit if VersionString[0] in '0123456789': # starts with a number; that's good # now for another edge case thanks to Adobe: @@ -207,6 +277,33 @@ def getAppBundleExecutable(bundlepath): return None +def parseInfoFile(infofile): + '''Returns a dict of keys and values parsed from an .info file + At least some of these old files use MacRoman encoding...''' + infodict = {} + fileobj = open(infofile, mode='rb') + info = fileobj.read() + fileobj.close() + infolines = info.splitlines() + for line in infolines: + try: + parts = line.split(None, 1) + if len(parts) == 2: + try: + key = parts[0].decode("mac_roman") + except (LookupError, UnicodeDecodeError): + key = parts[0].decode("UTF-8") + try: + value = parts[1].decode("mac_roman") + except (LookupError, UnicodeDecodeError): + value = parts[1].decode("UTF-8") + infodict[key] = value + except UnicodeDecodeError: + # something we could not handle; just skip it + pass + return infodict + + def getBundleVersion(bundlepath, key=None): """ Returns version number from a bundle. @@ -228,16 +325,8 @@ def getBundleVersion(bundlepath, key=None): for item in osutils.listdir(infopath): if os.path.join(infopath, item).endswith('.info'): infofile = os.path.join(infopath, item) - fileobj = open(infofile, mode='r') - info = fileobj.read() - fileobj.close() - infolines = info.splitlines() - for line in infolines: - parts = line.split(None, 1) - if len(parts) == 2: - label = parts[0] - if label == 'Version': - return parts[1] + infodict = parseInfoFile(infofile) + return infodict.get("Version", "0.0.0.0.0") # didn't find a version number, so return 0... return '0.0.0.0.0' @@ -252,20 +341,20 @@ def parsePkgRefs(filename, path_to_pkg=None): if pkgrefs: # this is a PackageInfo file for ref in pkgrefs: - keys = ref.attributes.keys() + keys = list(ref.attributes.keys()) if 'identifier' in keys and 'version' in keys: pkginfo = {} pkginfo['packageid'] = \ - ref.attributes['identifier'].value.encode('UTF-8') + ref.attributes['identifier'].value pkginfo['version'] = \ - ref.attributes['version'].value.encode('UTF-8') + ref.attributes['version'].value payloads = ref.getElementsByTagName('payload') if payloads: - keys = payloads[0].attributes.keys() + keys = list(payloads[0].attributes.keys()) if 'installKBytes' in keys: pkginfo['installed_size'] = int(float( payloads[0].attributes[ - 'installKBytes'].value.encode('UTF-8'))) + 'installKBytes'].value)) if pkginfo not in info: info.append(pkginfo) # if there isn't a payload, no receipt is left by a flat @@ -276,24 +365,22 @@ def parsePkgRefs(filename, path_to_pkg=None): # this is a Distribution or .dist file pkgref_dict = {} for ref in pkgrefs: - keys = ref.attributes.keys() + keys = list(ref.attributes.keys()) if 'id' in keys: - pkgid = ref.attributes['id'].value.encode('UTF-8') + pkgid = ref.attributes['id'].value if not pkgid in pkgref_dict: pkgref_dict[pkgid] = {'packageid': pkgid} if 'version' in keys: pkgref_dict[pkgid]['version'] = \ - ref.attributes['version'].value.encode('UTF-8') + ref.attributes['version'].value if 'installKBytes' in keys: pkgref_dict[pkgid]['installed_size'] = int(float( - ref.attributes['installKBytes'].value.encode( - 'UTF-8'))) + ref.attributes['installKBytes'].value)) if ref.firstChild: text = ref.firstChild.wholeText if text.endswith('.pkg'): if text.startswith('file:'): - relativepath = urllib2.unquote( - text[5:].encode('UTF-8')) + relativepath = unquote(text[5:]) pkgdir = os.path.dirname( path_to_pkg or filename) pkgref_dict[pkgid]['file'] = os.path.join( @@ -301,13 +388,12 @@ def parsePkgRefs(filename, path_to_pkg=None): else: if text.startswith('#'): text = text[1:] - relativepath = urllib2.unquote( - text.encode('UTF-8')) + relativepath = unquote(text) thisdir = os.path.dirname(filename) pkgref_dict[pkgid]['file'] = os.path.join( thisdir, relativepath) - for key in pkgref_dict.keys(): + for key in pkgref_dict: pkgref = pkgref_dict[key] if 'file' in pkgref: if os.path.exists(pkgref['file']): @@ -341,12 +427,12 @@ def getFlatPackageInfo(pkgpath): proc = subprocess.Popen(cmd_toc, bufsize=-1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) (toc, err) = proc.communicate() - toc = toc.strip().split('\n') + toc = toc.decode('UTF-8').strip().split('\n') if proc.returncode == 0: # Walk trough the TOC entries for toc_entry in toc: # If the TOC entry is a top-level PackageInfo, extract it - if toc_entry.startswith('PackageInfo') and len(infoarray) == 0: + if toc_entry.startswith('PackageInfo') and not infoarray: cmd_extract = ['/usr/bin/xar', '-xf', abspkgpath, toc_entry] result = subprocess.call(cmd_extract) if result == 0: @@ -356,7 +442,7 @@ def getFlatPackageInfo(pkgpath): break else: display.display_warning( - "An error occurred while extracting %s: %s", + u"An error occurred while extracting %s: %s", toc_entry, err) # If there are PackageInfo files elsewhere, gather them up elif toc_entry.endswith('.pkg/PackageInfo'): @@ -368,9 +454,9 @@ def getFlatPackageInfo(pkgpath): infoarray.extend(parsePkgRefs(packageinfoabspath)) else: display.display_warning( - "An error occurred while extracting %s: %s", + u"An error occurred while extracting %s: %s", toc_entry, err) - if len(infoarray) == 0: + if not infoarray: for toc_entry in [item for item in toc if item.startswith('Distribution')]: # Extract the Distribution file @@ -384,14 +470,14 @@ def getFlatPackageInfo(pkgpath): break else: display.display_warning( - "An error occurred while extracting %s: %s", + u"An error occurred while extracting %s: %s", toc_entry, err) - if len(infoarray) == 0: + if not infoarray: display.display_warning( 'No valid Distribution or PackageInfo found.') else: - display.display_warning(err) + display.display_warning(err.decode('UTF-8')) # change back to original working dir os.chdir(cwd) @@ -417,7 +503,7 @@ def getBomList(pkgpath): shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if proc.returncode == 0: return output.splitlines() return [] @@ -466,20 +552,9 @@ def getOnePackageInfo(pkgpath): pkginfo['filename'] = os.path.basename(pkgpath) pkginfo['packageid'] = os.path.basename(pkgpath) infofile = os.path.join(infopath, item) - fileobj = open(infofile, mode='r') - info = fileobj.read() - fileobj.close() - infolines = info.splitlines() - pkginfo['version'] = '0.0' - pkginfo['name'] = 'UNKNOWN' - for line in infolines: - parts = line.split(None, 1) - if len(parts) == 2: - label = parts[0] - if label == 'Version': - pkginfo['version'] = parts[1] - if label == 'Title': - pkginfo['name'] = parts[1] + infodict = parseInfoFile(infofile) + pkginfo['version'] = infodict.get('Version', '0.0') + pkginfo['name'] = infodict.get('Title', 'UNKNOWN') break return pkginfo @@ -563,7 +638,7 @@ def getInstalledPackageVersion(pkgid): bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() + out = proc.communicate()[0] if out: try: @@ -587,7 +662,7 @@ def getInstalledPackageVersion(pkgid): for item in installitems: if item.endswith('.pkg'): info = getBundlePackageInfo(os.path.join(receiptsdir, item)) - if len(info): + if info: infoitem = info[0] foundbundleid = infoitem['packageid'] foundvers = infoitem['version'] @@ -666,10 +741,9 @@ def nameAndVersion(aString): break vers = aString[index:] return (aString[0:index].rstrip(' .-_v'), vers) - else: - # no version number found, - # just return original string and empty string - return (aString, '') + # no version number found, + # just return original string and empty string + return (aString, '') def hasValidConfigProfileExt(path): @@ -703,7 +777,7 @@ def getChoiceChangesXML(pkgitem): proc = subprocess.Popen( ['/usr/sbin/installer', '-showChoiceChangesXML', '-pkg', pkgitem], bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() + out = proc.communicate()[0] if out: plist = FoundationPlist.readPlistFromString(out) @@ -802,7 +876,7 @@ def getInstalledPackages(): proc = subprocess.Popen(['/usr/sbin/pkgutil', '--regexp', '--pkg-info-plist', '.*'], bufsize=8192, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (out, dummy_err) = proc.communicate() + out = proc.communicate()[0] while out: (pliststr, out) = utils.getFirstPlist(out) if pliststr: @@ -871,4 +945,4 @@ def isApplication(pathname): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/powermgr.py b/code/client/munkilib/powermgr.py index 106a5663..f1013108 100644 --- a/code/client/munkilib/powermgr.py +++ b/code/client/munkilib/powermgr.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,8 +17,7 @@ powermgr.py Munki module to handle Power Manager tasks """ - -from . import display +from __future__ import absolute_import, print_function import objc @@ -28,6 +27,8 @@ import objc from Foundation import NSBundle # pylint:enable=no-name-in-module +from . import display + # lots of camelCase names # pylint: disable=invalid-name @@ -120,4 +121,4 @@ class Caffeinator(object): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/prefs.py b/code/client/munkilib/prefs.py index c827041d..16b18ad1 100644 --- a/code/client/munkilib/prefs.py +++ b/code/client/munkilib/prefs.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ Created by Greg Neagle on 2016-12-13. Preferences functions and classes used by the munki tools. """ +from __future__ import absolute_import, print_function + # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. # pylint: disable=E0611 @@ -37,11 +39,14 @@ from Foundation import kCFPreferencesCurrentHost # pylint: enable=E0611 from .constants import BUNDLE_ID +from .wrappers import is_a_string ##################################################### # managed installs preferences/metadata ##################################################### +DEFAULT_INSECURE_REPO_URL = 'http://munki/repo' + DEFAULT_PREFS = { 'AdditionalHttpHeaders': None, 'AppleSoftwareUpdatesOnly': False, @@ -72,7 +77,7 @@ DEFAULT_PREFS = { 'ShowOptionalInstallsForHigherOSVersions': False, 'SoftwareRepoCACertificate': None, 'SoftwareRepoCAPath': None, - 'SoftwareRepoURL': None, + 'SoftwareRepoURL': DEFAULT_INSECURE_REPO_URL, 'SoftwareUpdateServerURL': None, 'SuppressAutoInstall': False, 'SuppressLoginwindowInstall': False, @@ -140,8 +145,7 @@ class Preferences(object): """Return a preference or the default value""" if not pref_name in self: return default - else: - return self.__getitem__(pref_name) + return self.__getitem__(pref_name) class ManagedInstallsPreferences(Preferences): @@ -153,6 +157,7 @@ class ManagedInstallsPreferences(Preferences): Preferences are written to /Library/Preferences/ManagedInstalls.plist Since this code is usually run as root, ~ is root's home dir""" + # pylint: disable=too-few-public-methods def __init__(self): Preferences.__init__(self, 'ManagedInstalls', kCFPreferencesAnyUser) @@ -166,6 +171,7 @@ class SecureManagedInstallsPreferences(Preferences): Preferences are written to ~/Library/Preferences/ByHost/ManagedInstalls.XXXX.plist Since this code is usually run as root, ~ is root's home dir""" + # pylint: disable=too-few-public-methods def __init__(self): Preferences.__init__(self, 'ManagedInstalls', kCFPreferencesCurrentUser) @@ -269,34 +275,33 @@ def get_config_level(domain, pref_name, value): def print_config(): '''Prints the current Munki configuration''' - print 'Current Munki configuration:' - max_pref_name_len = max( - [len(pref_name) for pref_name in DEFAULT_PREFS.keys()]) - for pref_name in sorted(DEFAULT_PREFS.keys()): + print('Current Munki configuration:') + max_pref_name_len = max([len(pref_name) for pref_name in DEFAULT_PREFS]) + for pref_name in sorted(DEFAULT_PREFS): if pref_name == 'LastNotifiedDate': # skip it continue value = pref(pref_name) where = get_config_level(BUNDLE_ID, pref_name, value) repr_value = value - if isinstance(value, basestring): + if is_a_string(value): repr_value = repr(value) - print ('%' + str(max_pref_name_len) + 's: %5s %s ') % ( - pref_name, repr_value, where) + print(('%' + str(max_pref_name_len) + 's: %5s %s ') % ( + pref_name, repr_value, where)) # also print com.apple.SoftwareUpdate CatalogURL config if # Munki is configured to install Apple updates if pref('InstallAppleSoftwareUpdates'): - print 'Current Apple softwareupdate configuration:' + print('Current Apple softwareupdate configuration:') domain = 'com.apple.SoftwareUpdate' pref_name = 'CatalogURL' value = CFPreferencesCopyAppValue(pref_name, domain) where = get_config_level(domain, pref_name, value) repr_value = value - if isinstance(value, basestring): + if is_a_string(value): repr_value = repr(value) - print ('%' + str(max_pref_name_len) + 's: %5s %s ') % ( - pref_name, repr_value, where) + print(('%' + str(max_pref_name_len) + 's: %5s %s ') % ( + pref_name, repr_value, where)) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/processes.py b/code/client/munkilib/processes.py index 038add24..b01d1658 100644 --- a/code/client/munkilib/processes.py +++ b/code/client/munkilib/processes.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2016-12-14. Functions for finding, listing, etc processes """ +from __future__ import absolute_import, print_function import os import signal @@ -36,7 +37,7 @@ def get_running_processes(): shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if proc.returncode == 0: proc_list = [item for item in output.splitlines() if item.startswith('/')] @@ -48,7 +49,7 @@ def get_running_processes(): shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if proc.returncode == 0: carbon_apps = [item[len(launchcfmapp)+1:] for item in output.splitlines() @@ -105,7 +106,7 @@ def blocking_applications_running(pkginfoitem): # from 'installs' list if it exists appnames = [os.path.basename(item.get('path')) for item in pkginfoitem.get('installs', []) - if item['type'] == 'application'] + if item.get('type') == 'application'] display.display_debug1("Checking for %s" % appnames) running_apps = [appname for appname in appnames @@ -137,7 +138,7 @@ def find_processes(user=None, exe=None): argv = ['/bin/ps', '-x', '-w', '-w', '-a', '-o', 'pid=,user=,comm='] ps_proc = subprocess.Popen( argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (stdout, dummy_stderr) = ps_proc.communicate() + stdout = ps_proc.communicate()[0].decode('UTF-8') pids = {} @@ -189,7 +190,7 @@ def force_logout_now(): except OSError: pass - except BaseException, err: + except BaseException as err: display.display_error('Exception in force_logout_now(): %s' % str(err)) @@ -211,7 +212,7 @@ def stop_requested(): display.display_info('### User stopped session ###') try: os.unlink(stop_request_flag) - except OSError, err: + except OSError as err: display.display_error( 'Could not remove %s: %s', stop_request_flag, err) return True @@ -219,4 +220,4 @@ def stop_requested(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/profiles.py b/code/client/munkilib/profiles.py index 13c1f2cf..b5778186 100644 --- a/code/client/munkilib/profiles.py +++ b/code/client/munkilib/profiles.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2014-2019 Greg Neagle. +# Copyright 2014-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ profiles.py Munki module for working with configuration profiles. """ +from __future__ import absolute_import, print_function import os import subprocess @@ -53,7 +54,7 @@ def config_profile_info(ignore_cache=False): # so let's redirect everything to stdout and just use that proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] + stdout = proc.communicate()[0].decode('UTF-8') if proc.returncode != 0: display.display_error( 'Could not obtain configuration profile info: %s' % stdout) @@ -62,7 +63,7 @@ def config_profile_info(ignore_cache=False): try: config_profile_info.cache = FoundationPlist.readPlist( output_plist + '.plist') - except BaseException, err: + except BaseException as err: display.display_error( 'Could not read configuration profile info: %s' % err) config_profile_info.cache = {} @@ -120,11 +121,11 @@ def store_profile_receipt_data(identifier, hash_value): 'FileHash': hash_value, 'ProfileInstallDate': install_date } - elif identifier in profile_data.keys(): + elif identifier in list(profile_data.keys()): del profile_data[identifier] try: FoundationPlist.writePlist(profile_data, profile_receipt_data_path()) - except BaseException, err: + except BaseException as err: display.display_error( 'Cannot update hash for %s: %s' % (identifier, err)) @@ -136,7 +137,7 @@ def read_profile(profile_path): except FoundationPlist.NSPropertyListSerializationException: # possibly a signed profile return read_signed_profile(profile_path) - except BaseException, err: + except BaseException as err: display.display_error( 'Error reading profile %s: %s' % (profile_path, err)) return {} @@ -161,11 +162,12 @@ def read_signed_profile(profile_path): if proc.returncode: # security cms -D couldn't decode the file display.display_error( - 'Error reading profile %s: %s' % (profile_path, stderr)) + 'Error reading profile %s: %s' + % (profile_path, stderr.decode('UTF-8'))) return {} try: return FoundationPlist.readPlistFromString(stdout) - except FoundationPlist.NSPropertyListSerializationException, err: + except FoundationPlist.NSPropertyListSerializationException as err: # not a valid plist display.display_error( 'Error reading profile %s: %s' % (profile_path, err)) @@ -206,11 +208,11 @@ def install_profile(profile_path, profile_identifier): # so let's redirect everything to stdout and just use that proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] + stdout = proc.communicate()[0].decode('UTF-8') if proc.returncode != 0: display.display_error( u'Profile %s installation failed: %s' - % (os.path.basename(profile_path), stdout.decode('UTF-8'))) + % (os.path.basename(profile_path), stdout)) return False if profile_identifier: record_profile_receipt(profile_path, profile_identifier) @@ -231,7 +233,7 @@ def remove_profile(identifier): # so let's redirect everything to stdout and just use that proc = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - stdout = proc.communicate()[0] + stdout = proc.communicate()[0].decode('UTF-8') if proc.returncode != 0: display.display_error( 'Profile %s removal failed: %s' % (identifier, stdout)) @@ -288,4 +290,4 @@ def profile_is_installed(identifier): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/reports.py b/code/client/munkilib/reports.py index 555d3aad..b6236b33 100644 --- a/code/client/munkilib/reports.py +++ b/code/client/munkilib/reports.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2016-12-14. Reporting functions """ +from __future__ import absolute_import, print_function import os import subprocess @@ -44,29 +45,28 @@ def format_time(timestamp=None): If timestamp isn't given the current time is used.""" if timestamp is None: return str(NSDate.new()) - else: - return str(NSDate.dateWithTimeIntervalSince1970_(timestamp)) + return str(NSDate.dateWithTimeIntervalSince1970_(timestamp)) def printreportitem(label, value, indent=0): """Prints a report item in an 'attractive' way""" indentspace = ' ' - if type(value) == type(None): - print indentspace*indent, '%s: !NONE!' % label - elif type(value) == list or type(value).__name__ == 'NSCFArray': + if isinstance(value, type(None)): + print(indentspace*indent, '%s: !NONE!' % label) + elif isinstance(value, list) or type(value).__name__ == 'NSCFArray': if label: - print indentspace*indent, '%s:' % label + print(indentspace*indent, '%s:' % label) index = 0 for item in value: index += 1 printreportitem(index, item, indent+1) - elif type(value) == dict or type(value).__name__ == 'NSCFDictionary': + elif isinstance(value, dict) or type(value).__name__ == 'NSCFDictionary': if label: - print indentspace*indent, '%s:' % label + print(indentspace*indent, '%s:' % label) for subkey in value.keys(): printreportitem(subkey, value[subkey], indent+1) else: - print indentspace*indent, '%s: %s' % (label, value) + print(indentspace*indent, '%s: %s' % (label, value)) def printreport(reportdict): @@ -97,7 +97,7 @@ def _warn(msg): """We can't use display module functions here because that would require circular imports. So a partial reimplementation.""" warning = 'WARNING: %s' % msg - print >> sys.stderr, warning.encode('UTF-8') + print(warning.encode('UTF-8'), file=sys.stderr) munkilog.log(warning) # append this warning to our warnings log munkilog.log(warning, 'warnings.log') @@ -126,7 +126,7 @@ def archive_report(): proc = subprocess.Popen(['/bin/ls', '-t1', archivepath], bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - (output, dummy_err) = proc.communicate() + output = proc.communicate()[0].decode('UTF-8') if output: archiveitems = [item for item in str(output).splitlines() @@ -148,4 +148,4 @@ report = {} if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/scriptutils.py b/code/client/munkilib/scriptutils.py index 1bd13d8e..4ef0e318 100644 --- a/code/client/munkilib/scriptutils.py +++ b/code/client/munkilib/scriptutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ Created by Greg Neagle on 2016-12-14. Functions to run scripts inside Munki """ +from __future__ import absolute_import, print_function import os import subprocess @@ -35,10 +36,10 @@ def _writefile(stringdata, path): '''Writes string data to path. Returns the path on success, empty string on failure.''' try: - fileobject = open(path, mode='w', buffering=1) + fileobject = open(path, mode='wb', buffering=1) # write line-by-line to ensure proper UNIX line-endings for line in stringdata.splitlines(): - print >> fileobject, line.encode('UTF-8') + fileobject.write(line.encode('UTF-8') + b"\n") fileobject.close() return path except (OSError, IOError): @@ -96,7 +97,7 @@ def run_script(itemname, path, scriptname, suppress_error=False): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - except OSError, err: + except OSError as err: display.display_error( 'Error executing script %s: %s' % (scriptname, str(err))) return -1 @@ -131,4 +132,4 @@ def run_script(itemname, path, scriptname, suppress_error=False): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/__init__.py b/code/client/munkilib/updatecheck/__init__.py index a8ce586c..ad7c8ab1 100644 --- a/code/client/munkilib/updatecheck/__init__.py +++ b/code/client/munkilib/updatecheck/__init__.py @@ -1 +1,6 @@ -from .core import * \ No newline at end of file +'''Make names in .core available as updatecheck.foo''' +from __future__ import absolute_import + +# pylint: disable=wildcard-import +from .core import * +# pylint: enable=wildcard-import \ No newline at end of file diff --git a/code/client/munkilib/updatecheck/analyze.py b/code/client/munkilib/updatecheck/analyze.py index b34799cd..f6c511ab 100644 --- a/code/client/munkilib/updatecheck/analyze.py +++ b/code/client/munkilib/updatecheck/analyze.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ updatecheck.analyze Created by Greg Neagle on 2017-01-10. """ +from __future__ import absolute_import, print_function import datetime import os @@ -36,6 +37,7 @@ from .. import info from .. import munkilog from .. import prefs from .. import processes +from ..wrappers import is_a_string def item_in_installinfo(item_pl, thelist, vers=''): @@ -367,7 +369,7 @@ def process_install(manifestitem, cataloglist, installinfo, dependencies = item_pl['requires'] # fix things if 'requires' was specified as a string # instead of an array of strings - if isinstance(dependencies, basestring): + if is_a_string(dependencies): dependencies = [dependencies] for item in dependencies: display.display_detail( @@ -555,7 +557,7 @@ def process_install(manifestitem, cataloglist, installinfo, #if manifestitemname in installinfo['processed_installs']: # installinfo['processed_installs'].remove(manifestitemname) return False - except (fetch.GurlError, fetch.GurlDownloadError), errmsg: + except (fetch.GurlError, fetch.GurlDownloadError) as errmsg: display.display_warning( 'Download of %s failed: %s', manifestitem, errmsg) iteminfo['installed'] = False @@ -568,7 +570,7 @@ def process_install(manifestitem, cataloglist, installinfo, #if manifestitemname in installinfo['processed_installs']: # installinfo['processed_installs'].remove(manifestitemname) return False - except fetch.Error, errmsg: + except fetch.Error as errmsg: display.display_warning( 'Can\'t install %s because: %s', manifestitemname, errmsg) iteminfo['installed'] = False @@ -641,10 +643,10 @@ def process_manifest_for_key(manifest, manifest_key, installinfo, manifest can be a path to a manifest file or a dictionary object. """ - if isinstance(manifest, basestring): + if is_a_string(manifest): display.display_debug1( - "** Processing manifest %s for %s" % - (os.path.basename(manifest), manifest_key)) + "** Processing manifest %s for %s", + os.path.basename(manifest), manifest_key) manifestdata = manifestutils.get_manifest_data(manifest) else: manifestdata = manifest @@ -666,7 +668,7 @@ def process_manifest_for_key(manifest, manifest_key, installinfo, if not nestedmanifestpath: raise manifestutils.ManifestException if processes.stop_requested(): - return {} + return process_manifest_for_key(nestedmanifestpath, manifest_key, installinfo, cataloglist) @@ -696,7 +698,7 @@ def process_manifest_for_key(manifest, manifest_key, installinfo, for item in manifestdata.get(manifest_key, []): if processes.stop_requested(): - return {} + return if manifest_key == 'managed_installs': dummy_result = process_install(item, cataloglist, installinfo) elif manifest_key == 'managed_updates': @@ -942,14 +944,14 @@ def process_removal(manifestitem, cataloglist, installinfo): pkgdata['pkg_references'][pkg]) if iteminfo['name'] in pkgdata['pkg_references'][pkg]: pkgdata['pkg_references'][pkg].remove(iteminfo['name']) - if len(pkgdata['pkg_references'][pkg]) == 0: + if not pkgdata['pkg_references'][pkg]: + # no other items reference this pkg display.display_debug1( 'Adding %s to removal list.', pkg) packages_to_really_remove.append(pkg) else: # This shouldn't happen - display.display_warning( - 'pkg id %s missing from pkgdata', pkg) + display.display_warning('pkg id %s missing from pkgdata', pkg) if packages_to_really_remove: iteminfo['packages'] = packages_to_really_remove else: @@ -980,7 +982,7 @@ def process_removal(manifestitem, cataloglist, installinfo): 'Can\'t uninstall %s because the integrity check ' 'failed.', iteminfo['name']) return False - except fetch.Error, errmsg: + except fetch.Error as errmsg: display.display_warning( 'Failed to download the uninstaller for %s because %s', iteminfo['name'], errmsg) @@ -1018,4 +1020,4 @@ def process_removal(manifestitem, cataloglist, installinfo): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/autoconfig.py b/code/client/munkilib/updatecheck/autoconfig.py index d9f0a7da..7b3adf36 100644 --- a/code/client/munkilib/updatecheck/autoconfig.py +++ b/code/client/munkilib/updatecheck/autoconfig.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2018-2019 Greg Neagle. +# Copyright 2018-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ Created by Greg Neagle on 2018-04-17. Functions for automatically discovering and configuring some Munki settings. """ +from __future__ import absolute_import, print_function + # Apple frameworks via PyObjC # PyLint cannot properly find names inside Cocoa libraries, so issues bogus # No name 'Foo' in module 'Bar' warnings. Disable them. @@ -36,7 +38,10 @@ from .. import prefs def get_domain_name(): '''Return current domain name''' dns_config = SCDynamicStoreCopyValue(None, 'State:/Network/Global/DNS') - return dns_config.get('DomainName') + try: + return dns_config.get('DomainName') + except AttributeError: + return None def guess_repo_url(): @@ -44,7 +49,7 @@ def guess_repo_url(): utterly''' # default to the default repo for Munki up until version 3.2.x - autodetected_url = 'http://munki/repo' + autodetected_url = prefs.DEFAULT_INSECURE_REPO_URL domain_name = get_domain_name() if domain_name is None: @@ -69,7 +74,7 @@ def guess_repo_url(): fetch.getDataFromURL(url + '/catalogs/all') autodetected_url = url break - except fetch.Error, err: + except fetch.Error as err: # couldn't connect or other error display.display_info('URL error: %s', err) @@ -77,9 +82,10 @@ def guess_repo_url(): def autodetect_repo_url_if_needed(): - '''If Munki repo URL is not defined, attempt to discover one. If successful, - record the discovered URL in Munki's preferences.''' - if prefs.pref('SoftwareRepoURL'): + '''If Munki repo URL is not defined, (or is the insecure default) attempt + to discover one. If successful, record the discovered URL in Munki's + preferences.''' + if prefs.pref('SoftwareRepoURL') not in (None, prefs.DEFAULT_INSECURE_REPO_URL): # SoftwareRepoURL key is defined. exit. return all_keys_defined = True @@ -98,9 +104,10 @@ def autodetect_repo_url_if_needed(): if detected_url: display.display_info( 'Auto-detected Munki repo at %s', detected_url) - # save it to Munki's prefs - prefs.set_pref('SoftwareRepoURL', detected_url) + if detected_url != prefs.DEFAULT_INSECURE_REPO_URL: + # save it to Munki's prefs + prefs.set_pref('SoftwareRepoURL', detected_url) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/catalogs.py b/code/client/munkilib/updatecheck/catalogs.py index b5c83f68..79146ed1 100644 --- a/code/client/munkilib/updatecheck/catalogs.py +++ b/code/client/munkilib/updatecheck/catalogs.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-01. Functions for working with Munki catalogs """ +from __future__ import absolute_import, print_function import os @@ -31,6 +32,7 @@ from .. import pkgutils from .. import prefs from .. import utils from .. import FoundationPlist +from ..wrappers import is_a_string def make_catalog_db(catalogitems): @@ -77,7 +79,7 @@ def make_catalog_db(catalogitems): # now fix possible admin errors where 'update_for' is a string instead # of a list of strings for update in updaters: - if isinstance(update['update_for'], basestring): + if is_a_string(update['update_for']): # convert to list of strings update['update_for'] = [update['update_for']] @@ -115,7 +117,7 @@ def add_package_ids(catalogitems, itemname_to_pkgid, pkgid_to_itemname): itemname_to_pkgid[name] = {} for receipt in item['receipts']: - if 'packageid' in receipt: + if 'packageid' in receipt and 'version' in receipt: pkgid = receipt['packageid'] vers = receipt['version'] if not pkgid in itemname_to_pkgid[name]: @@ -158,10 +160,10 @@ def get_all_items_with_name(name, cataloglist): list of pkginfo items; sorted with newest version first. No precedence is given to catalog order. """ - def compare_item_versions(item_a, item_b): - """Internal comparison function for use with sorting""" - return cmp(pkgutils.MunkiLooseVersion(item_b['version']), - pkgutils.MunkiLooseVersion(item_a['version'])) + + def item_version(item): + """Returns a MunkiLooseVersion for pkginfo item""" + return pkgutils.MunkiLooseVersion(item['version']) itemlist = [] # we'll throw away any included version info @@ -169,7 +171,7 @@ def get_all_items_with_name(name, cataloglist): display.display_debug1('Looking for all items matching: %s...', name) for catalogname in cataloglist: - if not catalogname in _CATALOG.keys(): + if not catalogname in list(_CATALOG.keys()): # in case catalogname refers to a non-existent catalog... continue # is name in the catalog name table? @@ -189,7 +191,7 @@ def get_all_items_with_name(name, cataloglist): if itemlist: # sort so latest version is first - itemlist.sort(compare_item_versions) + itemlist.sort(key=item_version, reverse=True) return itemlist @@ -202,7 +204,7 @@ def get_auto_removal_items(installinfo, cataloglist): """ autoremovalnames = [] for catalogname in cataloglist or []: - if catalogname in _CATALOG.keys(): + if catalogname in list(_CATALOG.keys()): autoremovalnames += _CATALOG[catalogname]['autoremoveitems'] processed_installs_names = [split_name_and_version(item)[0] @@ -309,25 +311,22 @@ def analyze_installed_pkgs(): installedpkgsmatchedtoname = {} for name in itemname_to_pkgid: # name is a Munki install item name - somepkgsfound = False - allpkgsfound = True + foundpkgcount = 0 for pkgid in itemname_to_pkgid[name]: if pkgid in installedpkgs: - somepkgsfound = True + foundpkgcount += 1 if not name in installedpkgsmatchedtoname: installedpkgsmatchedtoname[name] = [] # record this pkgid for Munki install item name installedpkgsmatchedtoname[name].append(pkgid) + if foundpkgcount > 0: + if foundpkgcount == len(itemname_to_pkgid[name]): + # we found all receipts by pkgid on disk + installed.append(name) else: - # didn't find pkgid in installedpkgs - allpkgsfound = False - if allpkgsfound: - # we found all receipts by pkgid on disk - installed.append(name) - elif somepkgsfound: - # we found only some receipts for the item - # on disk - partiallyinstalled.append(name) + # we found only some receipts for the item + # on disk + partiallyinstalled.append(name) # we pay special attention to the items that seem partially installed. # we need to see if there are any packages that are unique to this item @@ -364,8 +363,7 @@ def analyze_installed_pkgs(): # look through all our installedpkgs, looking for ones that have not been # attached to any Munki names yet - orphans = [pkgid for pkgid in installedpkgs.keys() - if pkgid not in references] + orphans = [pkgid for pkgid in installedpkgs if pkgid not in references] # attempt to match orphans to Munki item names matched_orphans = [] @@ -425,10 +423,6 @@ def get_item_detail(name, cataloglist, vers='', If no version is given at all, the latest version is assumed. Returns a pkginfo item, or None. """ - def compare_versions(item_a, item_b): - """Internal comparison function for use in sorting""" - return cmp(pkgutils.MunkiLooseVersion(item_b), - pkgutils.MunkiLooseVersion(item_a)) rejected_items = [] machine = info.getMachineFacts() @@ -561,7 +555,7 @@ def get_item_detail(name, cataloglist, vers='', if skip_min_os_check: display.display_debug1( 'Looking for detail for: %s, version %s, ' - 'ignoring minimum_os_version...', name, vers), + 'ignoring minimum_os_version...', name, vers) else: display.display_debug1( 'Looking for detail for: %s, version %s...', name, vers) @@ -573,11 +567,11 @@ def get_item_detail(name, cataloglist, vers='', indexlist = [] if vers == 'latest': # order all our items, highest version first - versionlist = itemsmatchingname.keys() - versionlist.sort(compare_versions) + versionlist = list(itemsmatchingname.keys()) + versionlist.sort(key=pkgutils.MunkiLooseVersion, reverse=True) for versionkey in versionlist: indexlist.extend(itemsmatchingname[versionkey]) - elif vers in itemsmatchingname.keys(): + elif vers in list(itemsmatchingname.keys()): # get the specific requested version indexlist = itemsmatchingname[vers] @@ -649,4 +643,4 @@ def catalogs(): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/compare.py b/code/client/munkilib/updatecheck/compare.py index bd1269f5..04f83042 100644 --- a/code/client/munkilib/updatecheck/compare.py +++ b/code/client/munkilib/updatecheck/compare.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2016-12-13. Comparison/checking functions used by updatecheck """ +from __future__ import absolute_import, print_function import os from operator import itemgetter @@ -52,8 +53,7 @@ def compare_versions(thisvers, thatvers): elif (pkgutils.MunkiLooseVersion(thisvers) == pkgutils.MunkiLooseVersion(thatvers)): return VERSION_IS_THE_SAME - else: - return VERSION_IS_HIGHER + return VERSION_IS_HIGHER def compare_application_version(app): @@ -261,18 +261,18 @@ def filesystem_item_exists(item): if storedchecksum == ondiskchecksum: display.display_debug2('Checksums match.') return ITEM_MATCHES - else: - display.display_debug2( - 'Checksums differ: expected %s, got %s', - storedchecksum, ondiskchecksum) - return ITEM_DOES_NOT_MATCH - else: - return ITEM_MATCHES - else: - display.display_debug2('\tDoes not exist.') - return ITEM_NOT_PRESENT - else: - raise utils.Error('No path specified for filesystem item.') + # storedchecksum != ondiskchecksum + display.display_debug2( + 'Checksums differ: expected %s, got %s', + storedchecksum, ondiskchecksum) + return ITEM_DOES_NOT_MATCH + # 'md5checksum' not in item + return ITEM_MATCHES + # not os.path.lexists(filepath) + display.display_debug2('\tDoes not exist.') + return ITEM_NOT_PRESENT + # not 'path' in item + raise utils.Error('No path specified for filesystem item.') def compare_item_version(item): @@ -305,7 +305,7 @@ def compare_item_version(item): return compare_plist_version(item) if itemtype == 'file': return filesystem_item_exists(item) - raise utils.Error('Unknown installs item type: %s', itemtype) + raise utils.Error('Unknown installs item type: %s' % itemtype) def compare_receipt_version(item): @@ -340,10 +340,9 @@ def compare_receipt_version(item): installedvers = installedpkgs.get(pkgid) if installedvers: return compare_versions(installedvers, vers) - else: - display.display_debug1( - '\tThis package is not currently installed.') - return ITEM_NOT_PRESENT + # not installedvers + display.display_debug1('\tThis package is not currently installed.') + return ITEM_NOT_PRESENT def get_installed_version(item_plist): @@ -431,4 +430,4 @@ def get_installed_version(item_plist): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/core.py b/code/client/munkilib/updatecheck/core.py index da047caa..13df429b 100644 --- a/code/client/munkilib/updatecheck/core.py +++ b/code/client/munkilib/updatecheck/core.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ updatecheck.core Created by Greg Neagle on 2008-11-13. """ +from __future__ import absolute_import, print_function # standard libs import os @@ -91,9 +92,9 @@ def check(client_id='', localmanifestpath=None): download.stop_precaching_agent() # prevent idle sleep only if we are on AC power - caffeinator = None + _caffeinator = None if powermgr.onACPower(): - caffeinator = powermgr.Caffeinator( + _caffeinator = powermgr.Caffeinator( 'Munki is checking for new software') # initialize our installinfo record @@ -449,7 +450,7 @@ def check(client_id='', localmanifestpath=None): 'Could not read InstallInfo.plist. Deleting...') try: os.unlink(installinfopath) - except OSError, err: + except OSError as err: display.display_error( 'Failed to delete InstallInfo.plist: %s', str(err)) if oldinstallinfo == installinfo: @@ -487,8 +488,8 @@ def check(client_id='', localmanifestpath=None): if installcount or removalcount: return 1 - else: - return 0 + # installcount and removalcount are 0 + return 0 def get_primary_manifest_catalogs(client_id='', force_refresh=False): @@ -534,4 +535,4 @@ def get_primary_manifest_catalogs(client_id='', force_refresh=False): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/download.py b/code/client/munkilib/updatecheck/download.py index bbd56236..a08582a9 100644 --- a/code/client/munkilib/updatecheck/download.py +++ b/code/client/munkilib/updatecheck/download.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,10 +21,22 @@ Created by Greg Neagle on 2016-12-31. Functions for downloading resources from the Munki server """ +from __future__ import absolute_import, print_function import os -import urllib2 -import urlparse + +try: + # Python 2 + from urllib2 import quote +except ImportError: + # Python 3 + from urllib.parse import quote +try: + # Python 2 + from urlparse import urlparse +except ImportError: + # Python 3 + from urllib.parse import urlparse from .. import display from .. import fetch @@ -46,7 +58,7 @@ def get_url_basename(url): "/path/foo.dmg" => "foo.dmg" """ - url_parse = urlparse.urlparse(url) + url_parse = urlparse(url) return os.path.basename(url_parse.path) @@ -107,7 +119,7 @@ def enough_disk_space(item_pl, installlist=None, item_pl.get('name')) display.display_warning( ' %sMB needed; %sMB available', - diskspaceneeded/1024, availablediskspace/1024) + int(diskspaceneeded/1024), int(availablediskspace/1024)) return False @@ -140,7 +152,7 @@ def download_installeritem(item_pl, else: if not downloadbaseurl.endswith('/'): downloadbaseurl = downloadbaseurl + '/' - pkgurl = downloadbaseurl + urllib2.quote(location.encode('UTF-8')) + pkgurl = downloadbaseurl + quote(location.encode('UTF-8')) pkgname = get_url_basename(location) display.display_debug2('Download base URL is: %s', downloadbaseurl) @@ -170,7 +182,8 @@ def download_installeritem(item_pl, resume=True, message=dl_message, expected_hash=expected_hash, - verify=True) + verify=True, + pkginfo=item_pl) def clean_up_icons_dir(icons_to_keep): @@ -242,13 +255,16 @@ def download_icons(item_list): if not local_hash: local_hash = munkihash.getsha256hash(icon_path) fetch.writeCachedChecksum(icon_path, local_hash) + else: + # make sure it's a string and not a bytearray + local_hash = local_hash.decode("UTF-8") else: local_hash = 'nonexistent' icon_subdir = os.path.dirname(icon_path) if not os.path.isdir(icon_subdir): try: - os.makedirs(icon_subdir, 0755) - except OSError, err: + os.makedirs(icon_subdir, 0o755) + except OSError as err: display.display_error('Could not create %s' % icon_subdir) return if server_icon_hash != local_hash: @@ -259,13 +275,15 @@ def download_icons(item_list): # download this icon continue item_name = item.get('display_name') or item['name'] - message = 'Getting icon %s for %s...' % (icon_name, item_name) - icon_url = icon_base_url + urllib2.quote(icon_name.encode('UTF-8')) + icon_url = icon_base_url + quote(icon_name.encode('UTF-8')) try: fetch.munki_resource( - icon_url, icon_path, message=message) + icon_url, + icon_path, + message='Getting icon %s for %s...' % (icon_name, item_name) + ) fetch.writeCachedChecksum(icon_path) - except fetch.Error, err: + except fetch.Error as err: display.display_debug1( 'Error when retrieving icon %s from the server: %s', icon_name, err) @@ -301,8 +319,8 @@ def download_client_resources(): # make sure local resource directory exists if not os.path.isdir(resource_dir): try: - os.makedirs(resource_dir, 0755) - except OSError, err: + os.makedirs(resource_dir, 0o755) + except OSError as err: display.display_error( 'Could not create %s' % resource_dir) return @@ -310,14 +328,13 @@ def download_client_resources(): message = 'Getting client resources...' downloaded_resource_path = None for filename in filenames: - resource_url = resource_base_url + urllib2.quote( - filename.encode('UTF-8')) + resource_url = resource_base_url + quote(filename.encode('UTF-8')) try: fetch.munki_resource( resource_url, resource_archive_path, message=message) downloaded_resource_path = resource_archive_path break - except fetch.Error, err: + except fetch.Error as err: display.display_debug1( 'Could not retrieve client resources with name %s: %s', filename, err) @@ -326,7 +343,7 @@ def download_client_resources(): if os.path.exists(resource_archive_path): try: os.unlink(resource_archive_path) - except (OSError, IOError), err: + except (OSError, IOError) as err: display.display_error( 'Could not remove stale %s: %s', resource_archive_path, err) @@ -340,14 +357,14 @@ def download_catalog(catalogname): catalogbaseurl = catalogbaseurl + '/' display.display_debug2('Catalog base URL is: %s', catalogbaseurl) catalog_dir = os.path.join(prefs.pref('ManagedInstallDir'), 'catalogs') - catalogurl = catalogbaseurl + urllib2.quote(catalogname.encode('UTF-8')) + catalogurl = catalogbaseurl + quote(catalogname.encode('UTF-8')) catalogpath = os.path.join(catalog_dir, catalogname) display.display_detail('Getting catalog %s...', catalogname) message = 'Retrieving catalog "%s"...' % catalogname try: fetch.munki_resource(catalogurl, catalogpath, message=message) return catalogpath - except fetch.Error, err: + except fetch.Error as err: display.display_error( 'Could not retrieve catalog %s from server: %s', catalogname, err) @@ -384,10 +401,10 @@ def cache(): for item in _items_to_precache(install_info): try: download_installeritem(item, install_info, precaching=True) - except fetch.Error, err: + except fetch.Error as err: display.display_warning( - 'Failed to precache the installer for %s because %s', - item['name'], unicode(err)) + u'Failed to precache the installer for %s because %s', + item['name'], err) display.display_info("### Ending precaching session ###") @@ -416,7 +433,7 @@ def uncache(space_needed_in_kb): item_path = os.path.join(cachedir, item[0]) try: itemsize = int(os.path.getsize(item_path)/1024) - except OSError, err: + except OSError as err: display.display_warning("Could not get size of %s: %s" % (item_path, err)) itemsize = 0 @@ -447,7 +464,7 @@ def uncache(space_needed_in_kb): try: os.remove(item_path) deleted_kb += item_size - except OSError, err: + except OSError as err: display.display_error( "Could not remove precached item %s: %s" % (item_path, err)) @@ -495,9 +512,9 @@ def stop_precaching_agent(): display.display_info("Stopping precaching agent") try: launchd.remove_job(PRECACHING_AGENT_LABEL) - except launchd.LaunchdJobException, err: + except launchd.LaunchdJobException as err: display.display_error('Error stopping precaching agent: %s', err) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/installationstate.py b/code/client/munkilib/updatecheck/installationstate.py index 49f31221..d1443ae6 100644 --- a/code/client/munkilib/updatecheck/installationstate.py +++ b/code/client/munkilib/updatecheck/installationstate.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-01-01. Utilities for determining installation status for Munki items. """ +from __future__ import absolute_import, print_function import os @@ -31,6 +32,7 @@ from .. import osutils from .. import profiles from .. import scriptutils from .. import utils +from ..wrappers import unicode_or_str def installed_state(item_pl): @@ -97,8 +99,8 @@ def installed_state(item_pl): hash_value = item_pl.get('installer_item_hash') if profiles.profile_needs_to_be_installed(identifier, hash_value): return 0 - else: - return 1 + # does not need to be installed + return 1 # does 'installs' exist and is it non-empty? if item_pl.get('installs', None): @@ -111,9 +113,9 @@ def installed_state(item_pl): elif comparison == 2: # this item is newer foundnewer = True - except utils.Error, errmsg: + except utils.Error as err: # some problem with the installs data - display.display_error(unicode(errmsg)) + display.display_error(unicode_or_str(err)) # return 1 so we're marked as not needing to be installed return 1 @@ -129,9 +131,9 @@ def installed_state(item_pl): return 0 elif comparison == 2: foundnewer = True - except utils.Error, errmsg: + except utils.Error as err: # some problem with the receipts data - display.display_error(unicode(errmsg)) + display.display_error(unicode_or_str(err)) # return 1 so we're marked as not needing to be installed return 1 @@ -139,8 +141,8 @@ def installed_state(item_pl): # must be installed (or we don't have enough info...) if foundnewer: return 2 - else: - return 1 + # not newer + return 1 def some_version_installed(item_pl): @@ -186,9 +188,9 @@ def some_version_installed(item_pl): if compare.compare_item_version(item) == 0: # not there return False - except utils.Error, errmsg: + except utils.Error as err: # some problem with the installs data - display.display_error(unicode(errmsg)) + display.display_error(unicode_or_str(err)) return False # if there is no 'installs' key, then we'll use receipt info @@ -200,9 +202,9 @@ def some_version_installed(item_pl): if compare.compare_receipt_version(item) == 0: # not there return False - except utils.Error, errmsg: + except utils.Error as err: # some problem with the installs data - display.display_error(unicode(errmsg)) + display.display_error(unicode_or_str(err)) return False # if we got this far, we passed all the tests, so the item @@ -293,4 +295,4 @@ def evidence_this_is_installed(item_pl): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/licensing.py b/code/client/munkilib/updatecheck/licensing.py index d032f24c..e0e8f65d 100644 --- a/code/client/munkilib/updatecheck/licensing.py +++ b/code/client/munkilib/updatecheck/licensing.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -19,8 +19,13 @@ updatecheck.licensing Created by Greg Neagle on 2017-01-01. """ +from __future__ import absolute_import, print_function -from urllib import quote_plus +try: + from urllib import quote_plus +except ImportError: + # Python 3 + from urllib.parse import quote_plus from .. import display from .. import fetch @@ -57,8 +62,7 @@ def update_available_license_seats(installinfo): while True: query_items = ['name=' + quote_plus(item) for item in items_to_check[start_index:end_index]] - querystring = q_char + '&'.join(query_items) - url = license_info_url + querystring + url = license_info_url + q_char + '&'.join(query_items) if len(url) < 256: break # drop an item and see if we're under 256 characters @@ -68,8 +72,9 @@ def update_available_license_seats(installinfo): try: license_data = fetch.getDataFromURL(url) display.display_debug1('Got: %s', license_data) - license_dict = FoundationPlist.readPlistFromString(license_data) - except fetch.Error, err: + license_dict = FoundationPlist.readPlistFromString( + license_data.encode("UTF-8")) + except fetch.Error as err: # problem fetching from URL display.display_error('Error from %s: %s', url, err) except FoundationPlist.FoundationPlistException: @@ -102,4 +107,4 @@ def update_available_license_seats(installinfo): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/manifestutils.py b/code/client/munkilib/updatecheck/manifestutils.py index d8e9a971..dd82a4e5 100644 --- a/code/client/munkilib/updatecheck/manifestutils.py +++ b/code/client/munkilib/updatecheck/manifestutils.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -21,9 +21,16 @@ Created by Greg Neagle on 2016-12-16. Functions for working with manifest files """ +from __future__ import absolute_import, print_function import os -import urllib2 + +try: + # Python 2 + from urllib2 import quote +except ImportError: + # Python 3 + from urllib.parse import quote from .. import display from .. import fetch @@ -32,6 +39,7 @@ from .. import keychain from .. import prefs from .. import reports from .. import FoundationPlist +from ..wrappers import unicode_or_str PRIMARY_MANIFEST_TAG = '_primary_manifest_' @@ -88,7 +96,7 @@ def get_manifest(manifest_name, suppress_errors=False): 'manifests') manifesturl = ( - manifestbaseurl + urllib2.quote(manifest_name.encode('UTF-8'))) + manifestbaseurl + quote(manifest_name.encode('UTF-8'))) display.display_debug2('Manifest base URL is: %s', manifestbaseurl) display.display_detail('Getting manifest %s...', manifest_name) @@ -98,7 +106,7 @@ def get_manifest(manifest_name, suppress_errors=False): destinationdir = os.path.dirname(manifestpath) try: os.makedirs(destinationdir) - except OSError, err: + except OSError as err: # OSError will be raised if destinationdir exists, ignore this case if not os.path.isdir(destinationdir): if not suppress_errors: @@ -112,9 +120,9 @@ def get_manifest(manifest_name, suppress_errors=False): try: dummy_value = fetch.munki_resource( manifesturl, manifestpath, message=message) - except fetch.ConnectionError, err: + except fetch.ConnectionError as err: raise ManifestServerConnectionException(err) - except fetch.Error, err: + except fetch.Error as err: if not suppress_errors: display.display_error( 'Could not retrieve manifest %s from the server: %s', @@ -134,6 +142,7 @@ def get_manifest(manifest_name, suppress_errors=False): raise ManifestInvalidException(errormsg) else: # plist is valid + display.display_detail('Retrieved manifest %s', manifest_name) _MANIFESTS[manifest_name] = manifestpath return manifestpath @@ -156,7 +165,7 @@ def get_primary_manifest(alternate_id=''): manifest = get_manifest(clientidentifier) else: # no client identifier specified, so try the hostname - hostname = os.uname()[1].decode('UTF-8') + hostname = unicode_or_str(os.uname()[1]) # os.uname()[1] seems to always return UTF-8 for hostnames that # contain unicode characters, so we decode to Unicode clientidentifier = hostname @@ -232,7 +241,7 @@ def clean_up_manifests(): # If the directory isn't the main manifest dir and is empty, try to # remove it - if dirpath != manifest_dir and not len(os.listdir(dirpath)): + if dirpath != manifest_dir and not os.listdir(dirpath): try: os.rmdir(dirpath) except OSError: @@ -245,13 +254,12 @@ def get_manifest_data(manifestpath): try: plist = FoundationPlist.readPlist(manifestpath) except FoundationPlist.NSPropertyListSerializationException: - display.display_error('Could not read plist: %s', manifestpath) + display.display_error(u'Could not read plist: %s', manifestpath) if os.path.exists(manifestpath): try: os.unlink(manifestpath) - except OSError, err: - display.display_error( - 'Failed to delete plist: %s', unicode(err)) + except OSError as err: + display.display_error(u'Failed to delete plist: %s', err) else: display.display_error('plist does not exist.') return plist @@ -262,12 +270,11 @@ def get_manifest_value_for_key(manifestpath, keyname): plist = get_manifest_data(manifestpath) try: return plist.get(keyname, None) - except AttributeError, err: + except AttributeError as err: display.display_error( - 'Failed to get manifest value for key: %s (%s)', + u'Failed to get manifest value for key: %s (%s)', manifestpath, keyname) - display.display_error( - 'Manifest is likely corrupt: %s', unicode(err)) + display.display_error(u'Manifest is likely corrupt: %s', err) return None @@ -275,7 +282,7 @@ def remove_from_selfserve_section(itemname, section): """Remove the given itemname from the self-serve manifest's managed_uninstalls list""" display.display_debug1( - "Removing %s from SelfSeveManifest's %s...", itemname, section) + "Removing %s from SelfServeManifest's %s...", itemname, section) selfservemanifest = os.path.join( prefs.pref('ManagedInstallDir'), 'manifests', 'SelfServeManifest') if not os.path.exists(selfservemanifest): @@ -284,7 +291,7 @@ def remove_from_selfserve_section(itemname, section): return try: plist = FoundationPlist.readPlist(selfservemanifest) - except FoundationPlist.FoundationPlistException, err: + except FoundationPlist.FoundationPlistException as err: # SelfServeManifest is broken, bail display.display_debug1( "Error reading %s: %s", selfservemanifest, err) @@ -297,7 +304,7 @@ def remove_from_selfserve_section(itemname, section): ] try: FoundationPlist.writePlist(plist, selfservemanifest) - except FoundationPlist.FoundationPlistException, err: + except FoundationPlist.FoundationPlistException as err: display.display_debug1( "Error writing %s: %s", selfservemanifest, err) @@ -319,4 +326,4 @@ def remove_from_selfserve_uninstalls(itemname): _MANIFESTS = {} if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/updatecheck/unused_software.py b/code/client/munkilib/updatecheck/unused_software.py index 54e189f1..280a4771 100644 --- a/code/client/munkilib/updatecheck/unused_software.py +++ b/code/client/munkilib/updatecheck/unused_software.py @@ -1,6 +1,6 @@ # encoding: utf-8 # -# Copyright 2017-2019 Greg Neagle. +# Copyright 2017-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ Created by Greg Neagle on 2017-02-18. Functions for removing unused optional install items """ +from __future__ import absolute_import, print_function # Apple frameworks via PyObjC # PyLint cannot properly find names inside Cocoa libraries, so issues bogus @@ -139,4 +140,4 @@ def should_be_removed(item_pl): if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/utils.py b/code/client/munkilib/utils.py index 23d94046..f48393d8 100644 --- a/code/client/munkilib/utils.py +++ b/code/client/munkilib/utils.py @@ -22,6 +22,7 @@ Common utility functions used throughout Munki. Note: this module should be 100% free of ObjC-dependent Python imports. """ +from __future__ import absolute_import, print_function import grp @@ -34,6 +35,7 @@ class Memoize(dict): '''Class to cache the return values of an expensive function. This version supports only functions with non-keyword arguments''' def __init__(self, func): + super(Memoize, self).__init__() self.func = func def __call__(self, *args): @@ -83,7 +85,7 @@ def verifyFileOnlyWritableByMunkiAndRoot(file_path): """ try: file_stat = os.stat(file_path) - except OSError, err: + except OSError as err: raise VerifyFilePermissionsError( '%s does not exist. \n %s' % (file_path, str(err))) @@ -103,7 +105,7 @@ def verifyFileOnlyWritableByMunkiAndRoot(file_path): # verify other users cannot write to the file. elif file_stat.st_mode & stat.S_IWOTH != 0: raise InsecureFilePermissionsError('world writable!') - except InsecureFilePermissionsError, err: + except InsecureFilePermissionsError as err: raise InsecureFilePermissionsError( '%s is not secure! %s' % (file_path, err.args[0])) @@ -127,31 +129,31 @@ def runExternalScript(script, allow_insecure=False, script_args=()): if not allow_insecure: try: verifyFileOnlyWritableByMunkiAndRoot(script) - except VerifyFilePermissionsError, err: + except VerifyFilePermissionsError as err: msg = ('Skipping execution due to failed file permissions ' 'verification: %s\n%s' % (script, str(err))) raise RunExternalScriptError(msg) - if os.access(script, os.X_OK): - cmd = [script] - if script_args: - cmd.extend(script_args) - proc = None - try: - proc = subprocess.Popen(cmd, shell=False, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - except (OSError, IOError), err: - raise RunExternalScriptError( - 'Error %s when attempting to run %s' % (unicode(err), script)) - if proc: - (stdout, stderr) = proc.communicate() - return proc.returncode, stdout.decode('UTF-8', 'replace'), \ - stderr.decode('UTF-8', 'replace') - else: + if not os.access(script, os.X_OK): raise RunExternalScriptError('%s not executable' % script) + cmd = [script] + if script_args: + cmd.extend(script_args) + proc = None + try: + proc = subprocess.Popen(cmd, shell=False, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + except (OSError, IOError) as err: + raise RunExternalScriptError( + u'Error %s when attempting to run %s' % (err, script)) + (stdout, stderr) = proc.communicate() + return (proc.returncode, stdout.decode('UTF-8', 'replace'), + stderr.decode('UTF-8', 'replace')) + + def getPIDforProcessName(processname): '''Returns a process ID for processname''' @@ -176,32 +178,32 @@ def getPIDforProcessName(processname): pass else: if process.find(processname) != -1: - return str(pid) + return pid return 0 -def getFirstPlist(textString): - """Gets the next plist from a text string that may contain one or +def getFirstPlist(byteString): + """Gets the next plist from a byte string that may contain one or more text-style plists. Returns a tuple - the first plist (if any) and the remaining string after the plist""" - plist_header = '' - plist_start_index = textString.find(plist_header) + plist_header = b'' + plist_start_index = byteString.find(plist_header) if plist_start_index == -1: # not found - return ("", textString) - plist_end_index = textString.find( + return (b"", byteString) + plist_end_index = byteString.find( plist_footer, plist_start_index + len(plist_header)) if plist_end_index == -1: # not found - return ("", textString) + return (b"", byteString) # adjust end value plist_end_index = plist_end_index + len(plist_footer) - return (textString[plist_start_index:plist_end_index], - textString[plist_end_index:]) + return (byteString[plist_start_index:plist_end_index], + byteString[plist_end_index:]) if __name__ == '__main__': - print 'This is a library of support tools for the Munki Suite.' + print('This is a library of support tools for the Munki Suite.') diff --git a/code/client/munkilib/version.plist b/code/client/munkilib/version.plist index 18eeaa6e..0c846dc6 100644 --- a/code/client/munkilib/version.plist +++ b/code/client/munkilib/version.plist @@ -3,6 +3,6 @@ CFBundleShortVersionString - 3.6.0 + 4.1.0 diff --git a/code/client/munkilib/wrappers.py b/code/client/munkilib/wrappers.py new file mode 100644 index 00000000..682c6565 --- /dev/null +++ b/code/client/munkilib/wrappers.py @@ -0,0 +1,139 @@ +# encoding: utf-8 +# +# Copyright 2019-2020 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. +""" +wrappers.py + +Created by Greg Neagle on 2018-05-29. + +Some wrappers to paper over the differences between Python 2 and Python 3 +""" + +import plistlib + + +# plistlib wrappers + +class PlistError(Exception): + """Base error for plists""" + pass + + +class PlistReadError(PlistError): + """Error when reading plists""" + pass + + +class PlistWriteError(PlistError): + """Error when writing plists""" + pass + + +# Disable PyLint complaining about 'invalid' camelCase names +# pylint: disable=C0103 + +def readPlist(filepath): + '''Wrapper for the differences between Python 2 and Python 3's plistlib''' + try: + with open(filepath, "rb") as fileobj: + return plistlib.load(fileobj) + except AttributeError: + # plistlib module doesn't have a load function (as in Python 2) + try: + return plistlib.readPlist(filepath) + except BaseException as err: + raise PlistReadError(err) + except Exception as err: + raise PlistReadError(err) + + +def readPlistFromString(bytestring): + '''Wrapper for the differences between Python 2 and Python 3's plistlib''' + try: + return plistlib.loads(bytestring) + except AttributeError: + # plistlib module doesn't have a loads function (as in Python 2) + try: + return plistlib.readPlistFromString(bytestring) + except BaseException as err: + raise PlistReadError(err) + except Exception as err: + raise PlistReadError(err) + + +def writePlist(data, filepath): + '''Wrapper for the differences between Python 2 and Python 3's plistlib''' + try: + with open(filepath, "wb") as fileobj: + plistlib.dump(data, fileobj) + except AttributeError: + # plistlib module doesn't have a dump function (as in Python 2) + try: + plistlib.writePlist(data, filepath) + except BaseException as err: + raise PlistWriteError(err) + except Exception as err: + raise PlistWriteError(err) + + +def writePlistToString(data): + '''Wrapper for the differences between Python 2 and Python 3's plistlib''' + try: + return plistlib.dumps(data) + except AttributeError: + # plistlib module doesn't have a dumps function (as in Python 2) + try: + return plistlib.writePlistToString(data) + except BaseException as err: + raise PlistWriteError(err) + except Exception as err: + raise PlistWriteError(err) + + +# pylint: enable=C0103 + + +# Python 2 and 3 wrapper for raw_input/input +try: + # Python 2 + get_input = raw_input # pylint: disable=raw_input-builtin +except NameError: + # Python 3 + get_input = input # pylint: disable=input-builtin + + +# remap basestring in Python 3 +try: + _ = basestring +except NameError: + basestring = str + +def is_a_string(something): + '''Wrapper for basestring vs str''' + return isinstance(something, basestring) + + +def unicode_or_str(something, encoding="UTF-8"): + '''Wrapper for unicode vs str''' + try: + # Python 2 + if isinstance(something, str): + return unicode(something, encoding) + return unicode(something) + except NameError: + # Python 3 + if isinstance(something, bytes): + return str(something, encoding) + return str(something) diff --git a/code/client/precache_agent b/code/client/precache_agent index 79ffd5ca..5c2f8441 100755 --- a/code/client/precache_agent +++ b/code/client/precache_agent @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2018-2019 Greg Neagle. +# Copyright 2018-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ Created by Greg Neagle on 2018-07-18. A privileged agent that downloads optional installs items marked for precaching. """ +from __future__ import absolute_import + import signal import sys import time diff --git a/code/client/ptyexec b/code/client/ptyexec index b813ad65..5fe5f9d5 100755 --- a/code/client/ptyexec +++ b/code/client/ptyexec @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # # Copyright 2011-2017 Google Inc. All Rights Reserved. @@ -25,6 +25,7 @@ This will have the effect of unbuffering output I/O from the subprocess. stdin of the subprocess is not connected to the stdin of this parent process. """ +from __future__ import absolute_import, print_function import fcntl import os @@ -34,10 +35,14 @@ import signal import sys +# lots of variable names lifted from the C code this has been mostly +# translated from +# pylint: disable=invalid-name + child_exited = {} -def SetFileNonBlocking(f, non_blocking=True): +def set_file_nonblocking(f, non_blocking=True): """Set non-blocking flag on a file object. Args: @@ -50,7 +55,7 @@ def SetFileNonBlocking(f, non_blocking=True): fcntl.fcntl(f.fileno(), fcntl.F_SETFL, flags) -def SigHandler(signum, frame): +def sighandler(signum, _frame): """Handle a signal. Args: @@ -65,13 +70,13 @@ def SigHandler(signum, frame): child_exited[x[0]] = x[1] >> 8 # get exit status from LSB -def Usage(arg0): +def usage(arg0): """Print usage.""" - print >>sys.stderr, 'Usage: %s [command to run] [arguments...]' % arg0 + print('Usage: %s [command to run] [arguments...]' % arg0, file=sys.stderr) return 0 -def PtyExec(argv): +def ptyexec(argv): """Setup pty and exec argv. Args: @@ -81,27 +86,31 @@ def PtyExec(argv): error occurs. never returns on the child side of the fork. """ - signal.signal(signal.SIGCHLD, SigHandler) + signal.signal(signal.SIGCHLD, sighandler) pid, fd = pty.fork() if pid == 0: # child try: os.execv(argv[0], argv) - except OSError, e: - print >>sys.stderr, str(e) + except OSError as err: + print(str(err), file=sys.stderr) sys.exit(1) elif pid > 0: # parent - f = os.fdopen(fd, 'r+', 0) - SetFileNonBlocking(f) - while 1: + f = os.fdopen(fd, 'rb+', 0) + set_file_nonblocking(f) + while True: try: - (rl, wl, xl) = select.select([f], [], [], 5.0) - except select.error, e: + (rl, _wl, _xl) = select.select([f], [], [], 5.0) + except select.error: rl = [] if f in rl: l = f.read() - sys.stdout.write(l) + try: + sys.stdout.buffer.write(l) + except AttributeError: + # Python 2 + sys.stdout.write(l) sys.stdout.flush() if pid in child_exited: @@ -109,7 +118,7 @@ def PtyExec(argv): f.close() elif pid == -1: # error, never forked. - print >>sys.stderr, 'fork() error' + print('fork() error', file=sys.stderr) return 1 return child_exited.get(pid, 1) @@ -118,10 +127,10 @@ def PtyExec(argv): def main(argv): """Main.""" if len(argv) < 2: - return Usage(argv[0]) + return usage(argv[0]) argv.pop(0) - return PtyExec(argv) + return ptyexec(argv) if __name__ == '__main__': diff --git a/code/client/removepackages b/code/client/removepackages index 0e7d837a..86168be5 100755 --- a/code/client/removepackages +++ b/code/client/removepackages @@ -1,7 +1,7 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # -# Copyright 2009-2019 Greg Neagle. +# Copyright 2009-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ is made to revert to older versions of a file when uninstalling; only file removals are done. """ +from __future__ import absolute_import, print_function import optparse import os @@ -66,7 +67,7 @@ def main(): # check to see if we're root if os.geteuid() != 0: - print >> sys.stderr, "You must run this as root!" + print("You must run this as root!", file=sys.stderr) exit(-1) # set the display globals diff --git a/code/client/repoclean b/code/client/repoclean index c46ab017..3cecd686 100755 --- a/code/client/repoclean +++ b/code/client/repoclean @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 # -# Copyright 2016-2019 Greg Neagle. +# Copyright 2016-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,19 +22,19 @@ Created by Greg Neagle on 2016-06-22. A tool to remove older, unused software items from a Munki repo. """ +from __future__ import absolute_import, print_function -import plistlib import subprocess import sys import os import optparse from distutils.version import LooseVersion -from xml.parsers.expat import ExpatError from munkilib.cliutils import get_version, pref, path2url -from munkilib.cliutils import print_utf8, print_err_utf8 from munkilib import munkirepo +from munkilib.wrappers import (is_a_string, get_input, readPlistFromString, + unicode_or_str, PlistReadError) def name_and_version(a_string): @@ -80,17 +80,28 @@ class RepoCleaner(object): def human_readable(size_in_bytes): """Returns sizes in human-readable units.""" - units = [(" bytes", 2**10), + units = ((" bytes", 2**10), (" KB", 2**20), (" MB", 2**30), (" GB", 2**40), - (" TB", 2**50),] - for suffix, limit in units: - if size_in_bytes > limit: + (" TB", 2**50), + (" PB", 2**60),) + # set suffix and limit to last items in units in case the value + # is so big it falls off the edge + suffix = units[-1][0] + limit = units[-1][1] + # find an appropriate suffix + for test_suffix, test_limit in units: + if size_in_bytes > (test_limit - 1): continue else: - return str( - round(size_in_bytes/float(limit/2**10), 1)) + suffix + suffix = test_suffix + limit = test_limit + break + if limit == 2**10: + # no decimal since "1.0 bytes" is silly + return str(size_in_bytes) + " bytes" + return str(round(size_in_bytes/float(limit/2**10), 1)) + suffix count = 0 pkginfo_total_size = 0 @@ -112,23 +123,25 @@ class RepoCleaner(object): def analyze_manifests(self): '''Examine all manifests and populate our sets of manifest_items and manifest_items_with_versions''' - print_utf8('Analyzing manifest files...') + print('Analyzing manifest files...') # look through all manifests for "Foo-1.0" style items # we need to note these so the specific referenced version is not # deleted try: manifests_list = self.repo.itemlist('manifests') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: self.errors.append( - "Repo error getting list of manifests: %s" % unicode(err)) + "Repo error getting list of manifests: %s" + % unicode_or_str(err)) manifests_list = [] for manifest_name in manifests_list: try: data = self.repo.get(os.path.join('manifests', manifest_name)) - manifest = plistlib.readPlistFromString(data) - except (munkirepo.RepoError, IOError, OSError, ExpatError), err: + manifest = readPlistFromString(data) + except (munkirepo.RepoError, IOError, + OSError, PlistReadError) as err: self.errors.append("Unexpected error for %s: %s" - % (manifest_name, unicode(err))) + % (manifest_name, unicode_or_str(err))) continue for key in ['managed_installs', 'managed_uninstalls', 'managed_updates', 'optional_installs']: @@ -152,22 +165,24 @@ class RepoCleaner(object): def analyze_pkgsinfo(self): '''Examines all pkginfo files and populates self.pkginfodb, self.required_items and self.pkginfo_count''' - print_utf8('Analyzing pkginfo files...') + print('Analyzing pkginfo files...') try: pkgsinfo_list = self.repo.itemlist('pkgsinfo') - except munkirepo.RepoError, err: + except munkirepo.RepoError as err: self.errors.append( - "Repo error getting list of pkgsinfo: %s" % unicode(err)) + "Repo error getting list of pkgsinfo: %s" + % unicode_or_str(err)) pkgsinfo_list = [] for pkginfo_name in pkgsinfo_list: pkginfo_identifier = os.path.join('pkgsinfo', pkginfo_name) try: data = self.repo.get(pkginfo_identifier) - pkginfo = plistlib.readPlistFromString(data) - except (munkirepo.RepoError, IOError, OSError, ExpatError), err: + pkginfo = readPlistFromString(data) + except (munkirepo.RepoError, IOError, + OSError, PlistReadError) as err: self.errors.append("Unexpected error for %s: %s" - % (pkginfo_name, unicode(err))) + % (pkginfo_name, unicode_or_str(err))) continue try: name = pkginfo['name'] @@ -181,13 +196,13 @@ class RepoCleaner(object): uninstallpkgpath = pkginfo.get('uninstaller_item_location', '') uninstallpkgsize = pkginfo.get('uninstaller_item_size', 0) * 1024 - # track required items; if these are in "Foo-1.0" format, we need to - # note these so we don't delete the specific referenced version + # track required items; if these are in "Foo-1.0" format, we need + # to note these so we don't delete the specific referenced version if 'requires' in pkginfo: dependencies = pkginfo['requires'] # fix things if 'requires' was specified as a string # instead of an array of strings - if isinstance(dependencies, basestring): + if is_a_string(dependencies): dependencies = [dependencies] for dependency in dependencies: required_name, required_vers = name_and_version(dependency) @@ -205,10 +220,11 @@ class RepoCleaner(object): update_items = pkginfo['update_for'] # fix things if 'update_for' was specified as a string # instead of an array of strings - if isinstance(update_items, basestring): + if is_a_string(update_items): update_items = [update_items] for update_item in update_items: - update_item_name, dummy_vers = name_and_version(update_item) + (update_item_name, + dummy_vers) = name_and_version(update_item) if update_item_name in self.manifest_items: # add our name self.manifest_items.add(name) @@ -251,22 +267,21 @@ class RepoCleaner(object): and self.pkgs_to_keep: pkgs (install and uninstall items) that we need to keep.''' - def compare_versions(thing_a, thing_b): - """sort highest version to top""" - return cmp(LooseVersion(thing_b), LooseVersion(thing_a)) - - for key in sorted(self.pkginfodb.keys()): - print_this = (self.options.show_all or - len(self.pkginfodb[key].keys()) > self.options.keep) + for key in sorted(list(self.pkginfodb.keys())): + print_this = ( + self.options.show_all or + len(list(self.pkginfodb[key].keys())) > self.options.keep + ) item_name = self.pkginfodb[key][ - self.pkginfodb[key].keys()[0]][0]['name'] + list(self.pkginfodb[key].keys())[0]][0]['name'] if print_this: - print key + print(key) if item_name not in self.manifest_items: - print "[not in any manifests]" - print "versions:" + print("[not in any manifests]") + print("versions:") index = 0 - for version in sorted(self.pkginfodb[key].keys(), compare_versions): + for version in sorted(list(self.pkginfodb[key].keys()), + key=LooseVersion, reverse=True): line_info = '' index += 1 item_list = self.pkginfodb[key][version] @@ -303,52 +318,54 @@ class RepoCleaner(object): line_info = "(%s) %s" % (item['resource_identifier'], line_info) if print_this: - print " ", version, line_info + print(" ", version, line_info) if len(item_list) > 1: for item in item_list: - print " ", " " * len(version), - print "(%s)" % item['resource_identifier'] + print(" ", " " * len(version), end=' ') + print("(%s)" % item['resource_identifier']) if print_this: - print + print() - print_utf8("Total pkginfo items: %s" % self.pkginfo_count) - print_utf8("Item variants: %s" % len(self.pkginfodb.keys())) - print_utf8("pkginfo items to delete: %s" % len(self.items_to_delete)) + print("Total pkginfo items: %s" % self.pkginfo_count) + print("Item variants: %s" % len(list(self.pkginfodb.keys()))) + print("pkginfo items to delete: %s" % len(self.items_to_delete)) pkg_count, pkginfo_size, pkg_size = self.get_items_to_delete_stats() - print_utf8("pkgs to delete: %s" % pkg_count) - print_utf8("pkginfo space savings: %s" % pkginfo_size) - print_utf8("pkg space savings: %s" % pkg_size) + print("pkgs to delete: %s" % pkg_count) + print("pkginfo space savings: %s" % pkginfo_size) + print("pkg space savings: %s" % pkg_size) if self.errors: - print_err_utf8("\nErrors encountered when processing repo:\n") + print("\nErrors encountered when processing repo:\n", + file=sys.stderr) for error in self.errors: - print_err_utf8(error) + print(error, file=sys.stderr) def delete_items(self): '''Deletes items from the repo''' for item in self.items_to_delete: if 'resource_identifier' in item: - print_utf8('Removing %s' % item['resource_identifier']) + print('Removing %s' % item['resource_identifier'], + file=sys.stderr) try: self.repo.delete(item['resource_identifier']) - except munkirepo.RepoError, err: - print_err_utf8(unicode(err)) + except munkirepo.RepoError as err: + print(unicode_or_str(err), file=sys.stderr) if (item.get('pkg_path') and not item['pkg_path'] in self.pkgs_to_keep): pkg_to_remove = os.path.join('pkgs', item['pkg_path']) - print_utf8('Removing %s' % pkg_to_remove) + print('Removing %s' % pkg_to_remove) try: self.repo.delete(pkg_to_remove) - except munkirepo.RepoError, err: - print_err_utf8(unicode(err)) + except munkirepo.RepoError as err: + print(unicode_or_str(err), file=sys.stderr) if (item.get('uninstallpkg_path') and not item['uninstallpkg_path'] in self.pkgs_to_keep): pkg_to_remove = os.path.join('pkgs', item['uninstallpkg_path']) - print_utf8('Removing %s' % pkg_to_remove) + print('Removing %s' % pkg_to_remove, file=sys.stderr) try: self.repo.delete(pkg_to_remove) - except munkirepo.RepoError, err: - print_err_utf8(unicode(err)) + except munkirepo.RepoError as err: + print(unicode_or_str(err), file=sys.stderr) def make_catalogs(self): """Calls makecatalogs to rebuild our catalogs""" @@ -364,7 +381,7 @@ class RepoCleaner(object): if not os.path.exists(makecatalogs_path): # didn't find it; assume the default install path makecatalogs_path = '/usr/local/munki/makecatalogs' - print 'Rebuilding catalogs at %s...' % self.options.repo_url + print('Rebuilding catalogs at %s...' % self.options.repo_url) cmd = [makecatalogs_path] cmd.append('--repo-url') cmd.append(self.options.repo_url) @@ -379,28 +396,33 @@ class RepoCleaner(object): # we don't print stdout -- too much info #print output.rstrip('\n').decode('UTF-8') - errors = proc.stderr.read() + errors = proc.stderr.read().decode('UTF-8') if errors: - print '\nThe following issues occurred while building catalogs:\n' - print errors - + print('\nThe following issues occurred while building catalogs:\n') + print(errors) def clean(self): '''Clean our repo!''' self.analyze_manifests() self.analyze_pkgsinfo() self.find_cleanup_items() - if len(self.items_to_delete): - print - answer = raw_input( - 'Delete pkginfo and pkg items marked as [to be DELETED]? ' - 'WARNING: This action cannot be undone. [y/n] ') - if answer.lower().startswith('y'): - answer = raw_input( - 'Are you sure? This action cannot be undone. [y/n] ') + if self.items_to_delete: + print() + if not self.options.auto: + answer = get_input( + 'Delete pkginfo and pkg items marked as [to be DELETED]? ' + 'WARNING: This action cannot be undone. [y/N] ') if answer.lower().startswith('y'): - self.delete_items() - self.make_catalogs() + answer = get_input( + 'Are you sure? This action cannot be undone. [y/N] ') + if answer.lower().startswith('y'): + self.delete_items() + self.make_catalogs() + else: + print('Auto mode selected, deleting pkginfo and pkg items ' + 'marked as [to be DELETED]') + self.delete_items() + self.make_catalogs() def main(): @@ -423,11 +445,14 @@ def main(): parser.add_option('--plugin', default=pref('plugin'), help='Optional plugin to connect to repo. If specified, ' 'overrides any plugin specified via --configure.') + parser.add_option('--auto', '-a', action='store_true', default=False, + help='Do not prompt for confirmation before deleting ' + 'repo items. Use with caution.') options, arguments = parser.parse_args() if options.version: - print get_version() + print(get_version()) exit(0) if not options.repo_url: @@ -442,24 +467,24 @@ def main(): try: options.keep = int(options.keep) except ValueError: - print_err_utf8('--keep value must be a positive integer!') + print('--keep value must be a positive integer!', file=sys.stderr) exit(-1) if options.keep < 1: - print_err_utf8('--keep value must be a positive integer!') + print('--keep value must be a positive integer!', file=sys.stderr) exit(-1) # Make sure we have a repo_url to work with if not options.repo_url: - print_err_utf8("Need to specify a path to the repo root!") + print("Need to specify a path to the repo root!", file=sys.stderr) exit(-1) else: - print_utf8("Using repo url: %s" % options.repo_url) + print("Using repo url: %s" % options.repo_url, file=sys.stderr) try: repo = munkirepo.connect(options.repo_url, options.plugin) - except munkirepo.RepoError, err: - print >> sys.stderr, (u'Could not connect to munki repo: %s' - % unicode(err)) + except munkirepo.RepoError as err: + print(u'Could not connect to munki repo: %s' + % unicode_or_str(err), file=sys.stderr) exit(-1) # clean up the repo diff --git a/code/client/supervisor b/code/client/supervisor index 45fee326..d52f6feb 100755 --- a/code/client/supervisor +++ b/code/client/supervisor @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/local/munki/python # encoding: utf-8 # # Copyright 2011-2013 Google Inc. All Rights Reserved. @@ -16,7 +16,7 @@ # limitations under the License. """Tool to supervise launch other binaries.""" - +from __future__ import absolute_import, print_function import errno import getopt @@ -53,6 +53,7 @@ KILL_WAIT_SECS = 1 class Supervisor(object): + """Class to handle our supervisiory functions""" def __init__(self, delayrandom_abort=False): """Init. @@ -73,14 +74,20 @@ class Supervisor(object): 'stderr': None, 'debug': None, } + self.continue_sleeping = True + self.error_exec_codes = [] self.exit_status = None self.delayrandom_abort = delayrandom_abort + self.stderr = None + self.stdout = None - def setOptions(self, **kwargs): + def set_options(self, **kwargs): + """Set keyword options""" for k in kwargs: self.options[k] = kwargs[k] - def signalHandler(self, signum, dummy_frame): + def signal_handler(self, signum, _frame): + """Handle USR1 signal""" if signum == signal.SIGUSR1: self.continue_sleeping = False @@ -90,27 +97,26 @@ class Supervisor(object): Args: args: list, arguments to execute, args[0] is binary name """ - logging.debug('execute(%s)' % str(args)) + logging.debug('execute(%s)', str(args)) if self.delayrandom_abort: # A second Supervisor process will not take over the previous # Supervisor process who is holding this signal now. if signal.getsignal(signal.SIGUSR1) == signal.SIG_DFL: - signal.signal(signal.SIGUSR1, self.signalHandler) + signal.signal(signal.SIGUSR1, self.signal_handler) self.continue_sleeping = True if 'delayrandom' in self.options and self.options['delayrandom']: - max_secs = self.options['delayrandom'] - random_secs = random.randrange(0, max_secs) - logging.debug( - 'Applying random delay up to %s seconds: %s', - max_secs, random_secs) - time.sleep(random_secs) + max_secs = self.options['delayrandom'] + random_secs = random.randrange(0, max_secs) + logging.debug('Applying random delay up to %s seconds: %s', + max_secs, random_secs) + time.sleep(random_secs) if self.delayrandom_abort: - if not self.continue_sleeping: - logging.debug('Awoken from random delay by signal') - signal.signal(signal.SIGUSR1, signal.SIG_DFL) + if not self.continue_sleeping: + logging.debug('Awoken from random delay by signal') + signal.signal(signal.SIGUSR1, signal.SIG_DFL) if self.options['error-exec']: self.stdout = tempfile.NamedTemporaryFile() @@ -134,9 +140,9 @@ class Supervisor(object): stdout=stdout_pipe, stderr=stderr_pipe, ) - except OSError, e: + except OSError as err: self.exit_status = 127 - raise ExecuteError(str(e)) + raise ExecuteError(str(err)) self.exit_status = None self.continue_sleeping = True @@ -165,20 +171,21 @@ class Supervisor(object): except TimeoutError: logging.critical('Timeout error executing %s', ' '.join(args)) - self.killPid(proc.pid) + self.kill_pid(proc.pid) self.exit_status = EXIT_STATUS_TIMEOUT raise - def killPid(self, pid): + def kill_pid(self, pid): """Kill a pid, aggressively if necessary.""" + # pylint: disable=no-self-use exited = {} class __ChildExit(Exception): """Child exited.""" - def __sigchld_handler(signum, frame): + def __sigchld_handler(signum, _frame): if signum == signal.SIGCHLD: os.waitpid(pid, os.WNOHANG) exited[pid] = True @@ -193,18 +200,19 @@ class Supervisor(object): logging.warning('Sending SIGKILL to %d', pid) os.kill(-1 * pid, signal.SIGKILL) time.sleep(KILL_WAIT_SECS) - except OSError, e: - if e.args[0] == errno.ESRCH: + except OSError as err: + if err.args[0] == errno.ESRCH: logging.warning('pid %d died on its own') else: - logging.critical('killPid: %s', str(e)) + logging.critical('killPid: %s', str(err)) if pid in exited: return logging.debug('pid %d will not die', pid) - def getExitStatus(self): + def get_exit_status(self): + """Return supervised process exit status""" return self.exit_status def cleanup(self): @@ -221,7 +229,7 @@ class Supervisor(object): arg_str = arg_str.replace('{STDERR}', self.stderr.name) args = ('/bin/sh', '-c', arg_str) error_supv = Supervisor() - error_supv.setOptions(timeout=5 * 3600) + error_supv.set_options(timeout=5 * 3600) error_supv.execute(args) self.stdout.close() @@ -230,7 +238,7 @@ class Supervisor(object): self.stderr = None -def parseOpts(argv): +def parse_opts(argv): """Parse argv and return options and arguments. Args: @@ -245,21 +253,21 @@ def parseOpts(argv): 'timeout=', 'delayrandom=', 'debug', 'help', 'error-exec=', 'error-exec-exit-codes=', ]) - except getopt.GetoptError, e: - raise OptionError(str(e)) + except getopt.GetoptError as err: + raise OptionError(str(err)) options = {} - for k, v in argopts: - if k in ['--timeout', '--delayrandom']: - options[k[2:]] = int(v) + for key, val in argopts: + if key in ['--timeout', '--delayrandom']: + options[key[2:]] = int(val) else: - options[k[2:]] = v + options[key[2:]] = val return options, args -def Usage(): +def usage(): """Print usage.""" - print """supervisor [options] [--] [path to executable] [arguments] + print("""supervisor [options] [--] [path to executable] [arguments] options: --timeout n @@ -297,10 +305,10 @@ def Usage(): -- use the -- to separate supervisor options from arguments to the executable which will appear as options. - """ + """) -def processOpts(options, args): +def process_opts(options, args): """Process options for validity etc. Args: @@ -312,14 +320,14 @@ def processOpts(options, args): OptionError: if there is an error in options """ if not args or options.get('help', None) is not None: - Usage() + usage() return False if options.get('debug', None) is not None: logging.getLogger().setLevel(logging.DEBUG) return True -def setupSyslog(): +def setup_syslog(): """Setup syslog as a logger.""" logger = logging.getLogger() syslog = logging.handlers.SysLogHandler('/var/run/syslog') @@ -331,37 +339,38 @@ def setupSyslog(): def main(argv): + """Main function!""" try: - options, args = parseOpts(argv[1:]) - if not processOpts(options, args): + options, args = parse_opts(argv[1:]) + if not process_opts(options, args): return 0 - except OptionError, e: - logging.error(str(e)) + except OptionError as err: + logging.error(str(err)) return 1 if options.get('debug', None) is None: - setupSyslog() + setup_syslog() try: - sp = Supervisor(delayrandom_abort=True) - sp.setOptions(**options) - except Error, e: - logging.exception('%s %s', e.__class__.__name__, str(e)) + supervisor = Supervisor(delayrandom_abort=True) + supervisor.set_options(**options) + except Error as err: + logging.exception('%s %s', err.__class__.__name__, str(err)) return 1 - ex = 0 + exit_code = 0 try: - sp.execute(args) - ex = sp.getExitStatus() - except TimeoutError, e: - ex = 1 - except Error, e: - logging.exception('%s %s', e.__class__.__name__, str(e)) - ex = 1 + supervisor.execute(args) + exit_code = supervisor.get_exit_status() + except TimeoutError: + exit_code = 1 + except Error as err: + logging.exception('%s %s', err.__class__.__name__, str(err)) + exit_code = 1 - sp.cleanup() - return ex + supervisor.cleanup() + return exit_code if __name__ == '__main__': diff --git a/code/client/tests/munkilib/display/test_display_unicode.py b/code/client/tests/munkilib/display/test_display_unicode.py index bf948f3e..9a6bf054 100755 --- a/code/client/tests/munkilib/display/test_display_unicode.py +++ b/code/client/tests/munkilib/display/test_display_unicode.py @@ -6,7 +6,7 @@ test_display_unicode.py Unit tests for display.display_* functions. """ -# Copyright 2014-2019 Greg Neagle. +# Copyright 2014-2020 Greg Neagle. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ Unit tests for display.display_* functions. # 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 __future__ import absolute_import import unittest @@ -26,10 +27,10 @@ from munkilib import display MSG_UNI = u'Günther\'s favorite thing is %s' -MSG_STR = 'Günther\'s favorite thing is %s' +MSG_STR = u'Günther\'s favorite thing is %s'.encode('UTF-8') ARG_UNI = u'Günther' -ARG_STR = 'Günther' +ARG_STR = u'Günther'.encode('UTF-8') def log(msg, logname=''): diff --git a/code/client/tests/munkilib/processes/test_isapprunning.py b/code/client/tests/munkilib/processes/test_isapprunning.py index 26ea9cdd..62d95876 100755 --- a/code/client/tests/munkilib/processes/test_isapprunning.py +++ b/code/client/tests/munkilib/processes/test_isapprunning.py @@ -6,6 +6,7 @@ test_isapprunning.py Unit tests for processes.isAppRunning. """ +from __future__ import absolute_import, print_function # Copyright 2016-present Nate Walck. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,6 +20,7 @@ Unit tests for processes.isAppRunning. # 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 __future__ import absolute_import import unittest @@ -30,7 +32,7 @@ try: from mock import patch except ImportError: import sys - print >>sys.stderr, "mock module is required. run: easy_install mock" + print("mock module is required. run: easy_install mock", file=sys.stderr) raise diff --git a/code/client/tests/travisci.py b/code/client/tests/travisci.py old mode 100644 new mode 100755 index 2b73b8d8..c4edcfcb --- a/code/client/tests/travisci.py +++ b/code/client/tests/travisci.py @@ -1,4 +1,5 @@ #!/usr/bin/python env +from __future__ import absolute_import, print_function import subprocess import sys diff --git a/code/pkgtemplate/Resources_no_python/English.lproj/Description.plist b/code/pkgtemplate/Resources_no_python/English.lproj/Description.plist new file mode 100644 index 00000000..4dc3253b --- /dev/null +++ b/code/pkgtemplate/Resources_no_python/English.lproj/Description.plist @@ -0,0 +1,10 @@ + + + + + IFPkgDescriptionTitle + System Python link + IFPkgDescriptionDescription + Creates symlink to system Python at /usr/local/munki/python. Available for install if you choose not to install Munki's embedded Python. + + diff --git a/code/pkgtemplate/Resources_python/English.lproj/Description.plist b/code/pkgtemplate/Resources_python/English.lproj/Description.plist new file mode 100644 index 00000000..b794fda5 --- /dev/null +++ b/code/pkgtemplate/Resources_python/English.lproj/Description.plist @@ -0,0 +1,10 @@ + + + + + IFPkgDescriptionTitle + Munki embedded Python + IFPkgDescriptionDescription + Embedded Python 3 framework for Munki. + + diff --git a/code/pkgtemplate/Scripts_launchd/postinstall b/code/pkgtemplate/Scripts_launchd/postinstall index 9c0a29a5..1ea56263 100755 --- a/code/pkgtemplate/Scripts_launchd/postinstall +++ b/code/pkgtemplate/Scripts_launchd/postinstall @@ -24,7 +24,7 @@ def launchctld(identifier): cmd = ['/bin/launchctl', 'load', path] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - output, err = proc.communicate() + output = proc.communicate()[0].decode("UTF-8") return output except KeyError: pass diff --git a/code/tools/build_python_framework.sh b/code/tools/build_python_framework.sh new file mode 100755 index 00000000..bf7e63e6 --- /dev/null +++ b/code/tools/build_python_framework.sh @@ -0,0 +1,52 @@ +#!/bin/bash +# +# Build script for Python 3 framework for Munki + +TOOLSDIR=$(dirname "$0") +REQUIREMENTS="${TOOLSDIR}/py3_requirements.txt" +PYTHON_VERSION=3.7.4 +CODEDIR=$(dirname "${TOOLSDIR}") +MUNKIROOT=$(dirname "${CODEDIR}") + +# Sanity checks. +GIT=$(which git) +WHICH_GIT_RESULT="$?" +if [ "${WHICH_GIT_RESULT}" != "0" ]; then + echo "Could not find git in command path. Maybe it's not installed?" 1>&2 + echo "You can get a Git package here:" 1>&2 + echo " https://git-scm.com/download/mac" + exit 1 +fi +if [ ! -d "${MUNKIROOT}/code" ]; then + echo "Does not look like you are running this script from a Munki git repo." 1>&2 + exit 1 +fi +if [ ! -f "${REQUIREMENTS}" ]; then + echo "Missing requirements file at ${REQUIREMENTS}." 1>&2 + exit 1 +fi + +# clone our relocatable-python tool +PYTHONTOOLDIR="/tmp/relocatable-python-git" +if [ -d "${PYTHONTOOLDIR}" ]; then + rm -rf "${PYTHONTOOLDIR}" +fi +echo "Cloning relocatable-python tool from github..." +git clone https://github.com/gregneagle/relocatable-python.git "${PYTHONTOOLDIR}" +CLONE_RESULT="$?" +if [ "${CLONE_RESULT}" != "0" ]; then + echo "Error cloning relocatable-python tool repo: ${CLONE_RESULT}" 1>&2 + exit 1 +fi + +# remove existing Python.framework if present +if [ -d "${MUNKIROOT}/Python.framework" ]; then + rm -rf "${MUNKIROOT}/Python.framework" +fi + +# build the framework +"${PYTHONTOOLDIR}/make_relocatable_python_framework.py" \ + --python-version "${PYTHON_VERSION}" \ + --pip-requirements "${REQUIREMENTS}" \ + --destination "${MUNKIROOT}" + diff --git a/code/tools/make_munki_mpkg.sh b/code/tools/make_munki_mpkg.sh index 1d76009c..22df71b7 100755 --- a/code/tools/make_munki_mpkg.sh +++ b/code/tools/make_munki_mpkg.sh @@ -7,24 +7,25 @@ PKGID="com.googlecode.munki" MUNKIROOT="." # Convert to absolute path. -MUNKIROOT=`cd "$MUNKIROOT"; pwd` +MUNKIROOT=$(cd "$MUNKIROOT"; pwd) OUTPUTDIR="." # Convert to absolute path. -OUTPUTDIR=`cd "$OUTPUTDIR"; pwd` +OUTPUTDIR=$(cd "$OUTPUTDIR"; pwd) CONFPKG="" # add this number to Git revision index to get "build" number # consistent with old SVN repo MAGICNUMBER=482 +BUILDPYTHON=NO # try to automagically find munki source root -TOOLSDIR=`dirname $0` +TOOLSDIR=$(dirname "$0") # Convert to absolute path. -TOOLSDIR=`cd "$TOOLSDIR"; pwd` -PARENTDIR=`dirname $TOOLSDIR` -PARENTDIRNAME=`basename $PARENTDIR` +TOOLSDIR=$(cd "$TOOLSDIR"; pwd) +PARENTDIR=$(dirname "$TOOLSDIR") +PARENTDIRNAME=$(basename "$PARENTDIR") if [ "$PARENTDIRNAME" == "code" ]; then - GRANDPARENTDIR=`dirname $PARENTDIR` - GRANDPARENTDIRNAME=`basename $GRANDPARENTDIR` + GRANDPARENTDIR=$(dirname "$PARENTDIR") + GRANDPARENTDIRNAME=$(basename "$GRANDPARENTDIR") if [ "$GRANDPARENTDIRNAME" == "Munki2" ]; then MUNKIROOT="$GRANDPARENTDIR" fi @@ -32,11 +33,12 @@ fi usage() { cat <&2 @@ -104,50 +109,66 @@ if [ "$WHICH_GIT_RESULT" != "0" ]; then exit 1 fi if [ ! -x "/usr/bin/pkgbuild" ]; then - echo "pkgbuild is not installed!" + echo "pkgbuild is not installed!" 1>&2 exit 1 fi if [ ! -x "/usr/bin/productbuild" ]; then - echo "productbuild is not installed!" + echo "productbuild is not installed!" 1>&2 exit 1 fi if [ ! -x "/usr/bin/xcodebuild" ]; then - echo "xcodebuild is not installed!" + echo "xcodebuild is not installed!" 1>&2 exit 1 fi -# Get the munki version. -MUNKIVERS=`defaults read "$MUNKIROOT/code/client/munkilib/version" CFBundleShortVersionString` + +# Get the munki version +MUNKIVERS=$(defaults read "$MUNKIROOT/code/client/munkilib/version" CFBundleShortVersionString) if [ "$?" != "0" ]; then echo "$MUNKIROOT/code/client/munkilib/version is missing!" 1>&2 - echo "Perhaps $MUNKIROOT does not contain the munki source?" 1>&2 + echo "Perhaps $MUNKIROOT does not contain the munki source?" 1>&2 exit 1 fi +# Build the Python framework if requested or missing +if [ "$BUILDPYTHON" == "YES" -o ! -d "$MUNKIROOT/Python.framework" ]; then + PYTHONBUILDTOOL="${TOOLSDIR}/build_python_framework.sh" + if [ ! -x "${PYTHONBUILDTOOL}" ] ; then + echo "${PYTHONBUILDTOOL} is missing!" 1>&2 + exit 1 + fi + echo "Building Python.framework..." + "${PYTHONBUILDTOOL}" + if [ $? -ne 0 ]; then + echo "Building Python.framework failed!" 1>&2 + exit 1 + fi +fi + cd "$MUNKIROOT" # generate a pseudo-svn revision number for the core tools (and admin tools) # from the list of Git revisions -GITREV=`git log -n1 --format="%H" -- code/client` -GITREVINDEX=`git rev-list --count $GITREV` +GITREV=$(git log -n1 --format="%H" -- code/client) +GITREVINDEX=$(git rev-list --count $GITREV) SVNREV=$(($GITREVINDEX + $MAGICNUMBER)) MPKGSVNREV=$SVNREV VERSION=$MUNKIVERS.$SVNREV # get a pseudo-svn revision number for the apps pkg -APPSGITREV=`git log -n1 --format="%H" -- code/apps` -GITREVINDEX=`git rev-list --count $APPSGITREV` +APPSGITREV=$(git log -n1 --format="%H" -- code/apps) +GITREVINDEX=$(git rev-list --count $APPSGITREV) APPSSVNREV=$(($GITREVINDEX + $MAGICNUMBER)) if [ $APPSSVNREV -gt $MPKGSVNREV ] ; then MPKGSVNREV=$APPSSVNREV fi # get base apps version from MSC.app -APPSVERSION=`defaults read "$MUNKIROOT/code/apps/Managed Software Center/Managed Software Center/Info" CFBundleShortVersionString` +APPSVERSION=$(defaults read "$MUNKIROOT/code/apps/Managed Software Center/Managed Software Center/Info" CFBundleShortVersionString) # append the APPSSVNREV APPSVERSION=$APPSVERSION.$APPSSVNREV # get a pseudo-svn revision number for the launchd pkg -LAUNCHDGITREV=`git log -n1 --format="%H" -- launchd/LaunchDaemons launchd/LaunchAgents` -GITREVINDEX=`git rev-list --count $LAUNCHDGITREV` +LAUNCHDGITREV=$(git log -n1 --format="%H" -- launchd/LaunchDaemons launchd/LaunchAgents) +GITREVINDEX=$(git rev-list --count $LAUNCHDGITREV) LAUNCHDSVNREV=$(($GITREVINDEX + $MAGICNUMBER)) if [ $LAUNCHDSVNREV -gt $MPKGSVNREV ] ; then MPKGSVNREV=$LAUNCHDSVNREV @@ -155,9 +176,15 @@ fi # Get launchd version if different LAUNCHDVERSION=$MUNKIVERS if [ -e "$MUNKIROOT/launchd/version.plist" ]; then - LAUNCHDVERSION=`defaults read "$MUNKIROOT/launchd/version" CFBundleShortVersionString` + LAUNCHDVERSION=$(defaults read "$MUNKIROOT/launchd/version" CFBundleShortVersionString) fi LAUNCHDVERSION=$LAUNCHDVERSION.$LAUNCHDSVNREV +# Get Python version +PYTHONVERSION="NOT FOUND" +PYTHONINFOPLIST="$MUNKIROOT"/Python.framework/Versions/Current/Resources/Info.plist +if [ -f "$PYTHONINFOPLIST" ]; then + PYTHONVERSION=$(defaults read "$PYTHONINFOPLIST" CFBundleVersion) +fi # get a pseudo-svn revision number for the metapackage @@ -185,6 +212,7 @@ echo " Output directory: $OUTPUTDIR" echo " munki core tools version: $VERSION" echo " LaunchAgents/LaunchDaemons version: $LAUNCHDVERSION" echo " Apps package version: $APPSVERSION" +echo " Python version: $PYTHONVERSION" echo echo " metapackage version: $MPKGVERSION" echo @@ -207,7 +235,7 @@ if [ ! -e "$MSCAPP" ]; then echo "Open the Xcode project $MUNKIROOT/code/apps/Managed Software Center/Managed Software Center.xcodeproj and build it." exit 2 else - MSCVERSION=`defaults read "$MSCAPP/Contents/Info" CFBundleShortVersionString` + MSCVERSION=$(defaults read "$MSCAPP/Contents/Info" CFBundleShortVersionString) echo "Managed Software Center.app version: $MSCVERSION" fi @@ -229,7 +257,7 @@ if [ ! -e "$MSAPP" ]; then echo "Open the Xcode project $MUNKIROOT/code/apps/MunkiStatus/MunkiStatus.xcodeproj and build it." exit 2 else - MSVERSION=`defaults read "$MSAPP/Contents/Info" CFBundleShortVersionString` + MSVERSION=$(defaults read "$MSAPP/Contents/Info" CFBundleShortVersionString) echo "MunkiStatus.app version: $MSVERSION" fi @@ -251,7 +279,7 @@ if [ ! -e "$NOTIFIERAPP" ]; then echo "Open the Xcode project $MUNKIROOT/code/apps/notifier/munki-notifier.xcodeproj and build it." exit 2 else - NOTIFIERVERSION=`defaults read "$NOTIFIERAPP/Contents/Info" CFBundleShortVersionString` + NOTIFIERVERSION=$(defaults read "$NOTIFIERAPP/Contents/Info" CFBundleShortVersionString) echo "munki-notifier.app version: $NOTIFIERVERSION" fi @@ -264,8 +292,8 @@ makeinfo() { size="$5" nfiles="$6" restart="$7" - major=`echo $ver | cut -d. -f1` - minor=`echo $ver | cut -d. -f2` + major=$(echo $ver | cut -d. -f1) + minor=$(echo $ver | cut -d. -f2) # Flat packages want a PackageInfo. if [ "$restart" == "restart" ]; then restart=' postinstall-action="restart"' # Leading space is important. @@ -273,7 +301,7 @@ makeinfo() { restart="" fi if [ "$pkg" == "app" ]; then - MSUID=`defaults read "$MUNKIROOT/code/apps/Managed Software Center/build/Release/Managed Software Center.app/Contents/Info" CFBundleIdentifier` + MSUID=$(defaults read "$MUNKIROOT/code/apps/Managed Software Center/build/Release/Managed Software Center.app/Contents/Info" CFBundleIdentifier) app="" CONFCHOICE=" @@ -573,6 +635,8 @@ cat > "$DISTFILE" < + + $CONFOUTLINE @@ -590,12 +654,20 @@ cat > "$DISTFILE" < + + + + + + $CONFCHOICE ${PKGPREFIX}munkitools_core-$VERSION.pkg ${PKGPREFIX}munkitools_admin-$VERSION.pkg ${PKGPREFIX}munkitools_app-$APPSVERSION.pkg ${PKGPREFIX}munkitools_launchd-$LAUNCHDVERSION.pkg - ${PKGPREFIX}munkitools_app_usage-$VERSION.pkg + ${PKGPREFIX}munkitools_app_usage-$VERSION.pkg + ${PKGPREFIX}munkitools_python-$PYTHONVERSION.pkg + ${PKGPREFIX}munkitools_no_python-$VERSION.pkg $CONFREF @@ -627,11 +699,14 @@ sudo chown -hR root:wheel "$APPUSAGEROOT/Library/LaunchDaemons" sudo chown -hR root:wheel "$APPUSAGEROOT/Library/LaunchAgents" sudo chown -hR root:wheel "$APPUSAGEROOT/usr" +sudo chown -hR root:wheel "$PYTHONROOT/usr" +sudo chown -hR root:wheel "$NOPYTHONROOT/usr" + ###################### ## Run pkgbuild ## ###################### -CURRENTUSER=`whoami` -for pkg in core admin app launchd app_usage; do +CURRENTUSER=$(whoami) +for pkg in core admin app launchd app_usage python no_python; do case $pkg in "app") ver="$APPSVERSION" @@ -645,6 +720,10 @@ for pkg in core admin app launchd app_usage; do ver="$VERSION" SCRIPTS="${MUNKIROOT}/code/pkgtemplate/Scripts_app_usage" ;; + "python") + ver="$PYTHONVERSION" + SCRIPTS="" + ;; *) ver="$VERSION" SCRIPTS="" diff --git a/code/tools/make_munki_mpkg_DEP.sh b/code/tools/make_munki_mpkg_DEP.sh index ab25f489..98c1ec7a 100755 --- a/code/tools/make_munki_mpkg_DEP.sh +++ b/code/tools/make_munki_mpkg_DEP.sh @@ -7,24 +7,25 @@ PKGID="com.googlecode.munki" MUNKIROOT="." # Convert to absolute path. -MUNKIROOT=`cd "$MUNKIROOT"; pwd` +MUNKIROOT=$(cd "$MUNKIROOT"; pwd) OUTPUTDIR="." # Convert to absolute path. -OUTPUTDIR=`cd "$OUTPUTDIR"; pwd` +OUTPUTDIR=$(cd "$OUTPUTDIR"; pwd) CONFPKG="" # add this number to Git revision index to get "build" number # consistent with old SVN repo MAGICNUMBER=482 +BUILDPYTHON=NO # try to automagically find munki source root -TOOLSDIR=`dirname $0` +TOOLSDIR=$(dirname "$0") # Convert to absolute path. -TOOLSDIR=`cd "$TOOLSDIR"; pwd` -PARENTDIR=`dirname $TOOLSDIR` -PARENTDIRNAME=`basename $PARENTDIR` +TOOLSDIR=$(cd "$TOOLSDIR"; pwd) +PARENTDIR=$(dirname "$TOOLSDIR") +PARENTDIRNAME=$(basename "$PARENTDIR") if [ "$PARENTDIRNAME" == "code" ]; then - GRANDPARENTDIR=`dirname $PARENTDIR` - GRANDPARENTDIRNAME=`basename $GRANDPARENTDIR` + GRANDPARENTDIR=$(dirname "$PARENTDIR") + GRANDPARENTDIRNAME=$(basename "$GRANDPARENTDIR") if [ "$GRANDPARENTDIRNAME" == "Munki2" ]; then MUNKIROOT="$GRANDPARENTDIR" fi @@ -32,20 +33,23 @@ fi usage() { cat <&2 @@ -102,50 +112,69 @@ if [ "$WHICH_GIT_RESULT" != "0" ]; then exit 1 fi if [ ! -x "/usr/bin/pkgbuild" ]; then - echo "pkgbuild is not installed!" + echo "pkgbuild is not installed!" 1>&2 exit 1 fi if [ ! -x "/usr/bin/productbuild" ]; then - echo "productbuild is not installed!" + echo "productbuild is not installed!" 1>&2 exit 1 fi if [ ! -x "/usr/bin/xcodebuild" ]; then - echo "xcodebuild is not installed!" + echo "xcodebuild is not installed!" 1>&2 + exit 1 +fi +if [ ! -d "$MUNKIROOT/Python.framework" -a "$BUILDPYTHON" != "YES" ]; then + echo "Python.framework is missing!" 1>&2 + exit 1 +fi + +# Get the munki version. +MUNKIVERS=$(defaults read "$MUNKIROOT/code/client/munkilib/version" CFBundleShortVersionString) +if [ "$?" != "0" ]; then + echo "$MUNKIROOT/code/client/munkilib/version is missing!" 1>&2 + echo "Perhaps $MUNKIROOT does not contain the munki source?" 1>&2 exit 1 fi -# Get the munki version. -MUNKIVERS=`defaults read "$MUNKIROOT/code/client/munkilib/version" CFBundleShortVersionString` -if [ "$?" != "0" ]; then - echo "$MUNKIROOT/code/client/munkilib/version is missing!" 1>&2 - echo "Perhaps $MUNKIROOT does not contain the munki source?" 1>&2 - exit 1 -fi +# Build the Python framework if requested or missing +if [ "$BUILDPYTHON" == "YES" -o ! -d "$MUNKIROOT/Python.framework" ]; then + PYTHONBUILDTOOL="${TOOLSDIR}/build_python_framework.sh" + if [ ! -x "${PYTHONBUILDTOOL}" ] ; then + echo "${PYTHONBUILDTOOL} is missing!" 1>&2 + exit 1 + fi + echo "Building Python.framework..." + "${PYTHONBUILDTOOL}" + if [ $? -ne 0 ]; then + echo "Building Python.framework failed!" 1>&2 + exit 1 + fi + fi cd "$MUNKIROOT" # generate a pseudo-svn revision number for the core tools (and admin tools) # from the list of Git revisions -GITREV=`git log -n1 --format="%H" -- code/client` -GITREVINDEX=`git rev-list --count $GITREV` +GITREV=$(git log -n1 --format="%H" -- code/client) +GITREVINDEX=$(git rev-list --count $GITREV) SVNREV=$(($GITREVINDEX + $MAGICNUMBER)) MPKGSVNREV=$SVNREV VERSION=$MUNKIVERS.$SVNREV # get a pseudo-svn revision number for the apps pkg -APPSGITREV=`git log -n1 --format="%H" -- code/apps` -GITREVINDEX=`git rev-list --count $APPSGITREV` +APPSGITREV=$(git log -n1 --format="%H" -- code/apps) +GITREVINDEX=$(git rev-list --count $APPSGITREV) APPSSVNREV=$(($GITREVINDEX + $MAGICNUMBER)) if [ $APPSSVNREV -gt $MPKGSVNREV ] ; then MPKGSVNREV=$APPSSVNREV fi # get base apps version from MSC.app -APPSVERSION=`defaults read "$MUNKIROOT/code/apps/Managed Software Center/Managed Software Center/Info" CFBundleShortVersionString` +APPSVERSION=$(defaults read "$MUNKIROOT/code/apps/Managed Software Center/Managed Software Center/Info" CFBundleShortVersionString) # append the APPSSVNREV APPSVERSION=$APPSVERSION.$APPSSVNREV # get a pseudo-svn revision number for the launchd pkg -LAUNCHDGITREV=`git log -n1 --format="%H" -- launchd/LaunchDaemons launchd/LaunchAgents` -GITREVINDEX=`git rev-list --count $LAUNCHDGITREV` +LAUNCHDGITREV=$(git log -n1 --format="%H" -- launchd/LaunchDaemons launchd/LaunchAgents) +GITREVINDEX=$(git rev-list --count $LAUNCHDGITREV) LAUNCHDSVNREV=$(($GITREVINDEX + $MAGICNUMBER)) if [ $LAUNCHDSVNREV -gt $MPKGSVNREV ] ; then MPKGSVNREV=$LAUNCHDSVNREV @@ -153,9 +182,15 @@ fi # Get launchd version if different LAUNCHDVERSION=$MUNKIVERS if [ -e "$MUNKIROOT/launchd/version.plist" ]; then - LAUNCHDVERSION=`defaults read "$MUNKIROOT/launchd/version" CFBundleShortVersionString` + LAUNCHDVERSION=$(defaults read "$MUNKIROOT/launchd/version" CFBundleShortVersionString) fi LAUNCHDVERSION=$LAUNCHDVERSION.$LAUNCHDSVNREV +# Get Python version +PYTHONVERSION="NOT FOUND" +PYTHONINFOPLIST="$MUNKIROOT"/Python.framework/Versions/Current/Resources/Info.plist +if [ -f "$PYTHONINFOPLIST" ]; then + PYTHONVERSION=$(defaults read "$PYTHONINFOPLIST" CFBundleVersion) +fi # get a pseudo-svn revision number for the metapackage MPKGVERSION=$MUNKIVERS.$MPKGSVNREV @@ -182,6 +217,7 @@ echo " Output directory: $OUTPUTDIR" echo " munki core tools version: $VERSION" echo " LaunchAgents/LaunchDaemons version: $LAUNCHDVERSION" echo " Apps package version: $APPSVERSION" +echo " Python version: $PYTHONVERSION" echo echo " metapackage version: $MPKGVERSION" echo @@ -226,7 +262,7 @@ if [ ! -e "$MSAPP" ]; then echo "Open the Xcode project $MUNKIROOT/code/apps/MunkiStatus/MunkiStatus.xcodeproj and build it." exit 2 else - MSVERSION=`defaults read "$MSAPP/Contents/Info" CFBundleShortVersionString` + MSVERSION=$(defaults read "$MSAPP/Contents/Info" CFBundleShortVersionString) echo "MunkiStatus.app version: $MSVERSION" fi @@ -248,7 +284,7 @@ if [ ! -e "$NOTIFIERAPP" ]; then echo "Open the Xcode project $MUNKIROOT/code/apps/notifier/munki-notifier.xcodeproj and build it." exit 2 else - NOTIFIERVERSION=`defaults read "$NOTIFIERAPP/Contents/Info" CFBundleShortVersionString` + NOTIFIERVERSION=$(defaults read "$NOTIFIERAPP/Contents/Info" CFBundleShortVersionString) echo "munki-notifier.app version: $NOTIFIERVERSION" fi @@ -261,8 +297,8 @@ makeinfo() { size="$5" nfiles="$6" restart="$7" - major=`echo $ver | cut -d. -f1` - minor=`echo $ver | cut -d. -f2` + major=$(echo $ver | cut -d. -f1) + minor=$(echo $ver | cut -d. -f2) # Flat packages want a PackageInfo. if [ "$restart" == "restart" ]; then restart=' postinstall-action="restart"' # Leading space is important. @@ -270,7 +306,7 @@ makeinfo() { restart="" fi if [ "$pkg" == "app" ]; then - MSUID=`defaults read "$MUNKIROOT/code/apps/Managed Software Center/build/Release/Managed Software Center.app/Contents/Info" CFBundleIdentifier` + MSUID=$(defaults read "$MUNKIROOT/code/apps/Managed Software Center/build/Release/Managed Software Center.app/Contents/Info" CFBundleIdentifier) app="" CONFCHOICE=" @@ -572,6 +657,8 @@ function requirerestart() { + + $CONFOUTLINE @@ -589,12 +676,20 @@ function requirerestart() { + + + + + + $CONFCHOICE ${PKGPREFIX}munkitools_core-$VERSION.pkg ${PKGPREFIX}munkitools_admin-$VERSION.pkg ${PKGPREFIX}munkitools_app-$APPSVERSION.pkg ${PKGPREFIX}munkitools_launchd-$LAUNCHDVERSION.pkg - ${PKGPREFIX}munkitools_app_usage-$VERSION.pkg + ${PKGPREFIX}munkitools_app_usage-$VERSION.pkg + ${PKGPREFIX}munkitools_python-$PYTHONVERSION.pkg + ${PKGPREFIX}munkitools_no_python-$VERSION.pkg $CONFREF @@ -627,11 +722,14 @@ sudo chown -hR root:wheel "$APPUSAGEROOT/Library/LaunchDaemons" sudo chown -hR root:wheel "$APPUSAGEROOT/Library/LaunchAgents" sudo chown -hR root:wheel "$APPUSAGEROOT/usr" +sudo chown -hR root:wheel "$PYTHONROOT/usr" +sudo chown -hR root:wheel "$NOPYTHONROOT/usr" + ###################### ## Run pkgbuild ## ###################### -CURRENTUSER=`whoami` -for pkg in core admin app launchd app_usage; do +CURRENTUSER=$(whoami) +for pkg in core admin app launchd app_usage python no_python; do case $pkg in "app") ver="$APPSVERSION" @@ -645,6 +743,10 @@ for pkg in core admin app launchd app_usage; do ver="$VERSION" SCRIPTS="${MUNKIROOT}/code/pkgtemplate/Scripts_app_usage" ;; + "python") + ver="$PYTHONVERSION" + SCRIPTS="" + ;; *) ver="$VERSION" SCRIPTS="" diff --git a/code/tools/pkginfo_hash_updater.py b/code/tools/pkginfo_hash_updater.py index 397ff242..f9541980 100755 --- a/code/tools/pkginfo_hash_updater.py +++ b/code/tools/pkginfo_hash_updater.py @@ -102,7 +102,7 @@ def AddHashesToPkginfoPlists(pkgsinfo_path, pkgs_path, update_existing=False): # read plist try: plist = plistlib.readPlist(f_path) - except IOError, e: + except IOError as e: print 'WARNING: pkginfo plist failed to open: %s\n%s' % (f_path, str(e)) continue diff --git a/code/tools/py3_requirements.txt b/code/tools/py3_requirements.txt new file mode 100644 index 00000000..89852cf7 --- /dev/null +++ b/code/tools/py3_requirements.txt @@ -0,0 +1,3 @@ +xattr==0.9.6 +pyobjc==5.1.2 +six \ No newline at end of file
- ${description} -
${secondary_status_text}
- ${description_without_images} - ${more_link_text} -