├── .gitignore ├── .gitmodules ├── DockServiceManager.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── DockServiceManager ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── MainMenu.xib └── Info.plist ├── LICENSE ├── Package ├── .gitignore ├── DockServiceManager.pkgproj ├── LaunchAgent │ └── com.github.ygini.DockServiceManager.plist └── postinstall ├── README.md ├── sample └── com.github.ygini.DockServiceManager.plist └── tools ├── runall.debug ├── sendFakeNotification.swift └── stopall.debug /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendors/dockutil"] 2 | path = vendors/dockutil 3 | url=git@github.com:ygini/dockutil.git 4 | -------------------------------------------------------------------------------- /DockServiceManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F8B08A781D196B8400F353F0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B08A771D196B8400F353F0 /* AppDelegate.swift */; }; 11 | F8B08A7A1D196B8400F353F0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8B08A791D196B8400F353F0 /* Assets.xcassets */; }; 12 | F8B08A7D1D196B8400F353F0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F8B08A7B1D196B8400F353F0 /* MainMenu.xib */; }; 13 | F8B08A871D198E0700F353F0 /* dockutil in Copy ShellScripts */ = {isa = PBXBuildFile; fileRef = F8B08A841D198DE100F353F0 /* dockutil */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXCopyFilesBuildPhase section */ 17 | F8B08A861D198DF900F353F0 /* Copy ShellScripts */ = { 18 | isa = PBXCopyFilesBuildPhase; 19 | buildActionMask = 2147483647; 20 | dstPath = ""; 21 | dstSubfolderSpec = 7; 22 | files = ( 23 | F8B08A871D198E0700F353F0 /* dockutil in Copy ShellScripts */, 24 | ); 25 | name = "Copy ShellScripts"; 26 | runOnlyForDeploymentPostprocessing = 0; 27 | }; 28 | /* End PBXCopyFilesBuildPhase section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | F8B08A741D196B8400F353F0 /* DockServiceManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DockServiceManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | F8B08A771D196B8400F353F0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33 | F8B08A791D196B8400F353F0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 34 | F8B08A7C1D196B8400F353F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 35 | F8B08A7E1D196B8400F353F0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | F8B08A841D198DE100F353F0 /* dockutil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = dockutil; path = vendors/dockutil/scripts/dockutil; sourceTree = SOURCE_ROOT; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | F8B08A711D196B8400F353F0 /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | ); 45 | runOnlyForDeploymentPostprocessing = 0; 46 | }; 47 | /* End PBXFrameworksBuildPhase section */ 48 | 49 | /* Begin PBXGroup section */ 50 | F8B08A6B1D196B8400F353F0 = { 51 | isa = PBXGroup; 52 | children = ( 53 | F8B08A761D196B8400F353F0 /* DockServiceManager */, 54 | F8B08A751D196B8400F353F0 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | F8B08A751D196B8400F353F0 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | F8B08A741D196B8400F353F0 /* DockServiceManager.app */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | F8B08A761D196B8400F353F0 /* DockServiceManager */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | F8B08A841D198DE100F353F0 /* dockutil */, 70 | F8B08A771D196B8400F353F0 /* AppDelegate.swift */, 71 | F8B08A791D196B8400F353F0 /* Assets.xcassets */, 72 | F8B08A7B1D196B8400F353F0 /* MainMenu.xib */, 73 | F8B08A7E1D196B8400F353F0 /* Info.plist */, 74 | ); 75 | path = DockServiceManager; 76 | sourceTree = ""; 77 | }; 78 | /* End PBXGroup section */ 79 | 80 | /* Begin PBXNativeTarget section */ 81 | F8B08A731D196B8400F353F0 /* DockServiceManager */ = { 82 | isa = PBXNativeTarget; 83 | buildConfigurationList = F8B08A811D196B8400F353F0 /* Build configuration list for PBXNativeTarget "DockServiceManager" */; 84 | buildPhases = ( 85 | F8634C941D199E8E00EB99DC /* GitVersion */, 86 | F8B08A701D196B8400F353F0 /* Sources */, 87 | F8B08A711D196B8400F353F0 /* Frameworks */, 88 | F8B08A721D196B8400F353F0 /* Resources */, 89 | F8B08A861D198DF900F353F0 /* Copy ShellScripts */, 90 | ); 91 | buildRules = ( 92 | ); 93 | dependencies = ( 94 | ); 95 | name = DockServiceManager; 96 | productName = DockServiceManager; 97 | productReference = F8B08A741D196B8400F353F0 /* DockServiceManager.app */; 98 | productType = "com.apple.product-type.application"; 99 | }; 100 | /* End PBXNativeTarget section */ 101 | 102 | /* Begin PBXProject section */ 103 | F8B08A6C1D196B8400F353F0 /* Project object */ = { 104 | isa = PBXProject; 105 | attributes = { 106 | LastSwiftUpdateCheck = 0730; 107 | LastUpgradeCheck = 0730; 108 | ORGANIZATIONNAME = "Yoann Gini"; 109 | TargetAttributes = { 110 | F8B08A731D196B8400F353F0 = { 111 | CreatedOnToolsVersion = 7.3.1; 112 | }; 113 | }; 114 | }; 115 | buildConfigurationList = F8B08A6F1D196B8400F353F0 /* Build configuration list for PBXProject "DockServiceManager" */; 116 | compatibilityVersion = "Xcode 3.2"; 117 | developmentRegion = English; 118 | hasScannedForEncodings = 0; 119 | knownRegions = ( 120 | en, 121 | Base, 122 | ); 123 | mainGroup = F8B08A6B1D196B8400F353F0; 124 | productRefGroup = F8B08A751D196B8400F353F0 /* Products */; 125 | projectDirPath = ""; 126 | projectRoot = ""; 127 | targets = ( 128 | F8B08A731D196B8400F353F0 /* DockServiceManager */, 129 | ); 130 | }; 131 | /* End PBXProject section */ 132 | 133 | /* Begin PBXResourcesBuildPhase section */ 134 | F8B08A721D196B8400F353F0 /* Resources */ = { 135 | isa = PBXResourcesBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | F8B08A7A1D196B8400F353F0 /* Assets.xcassets in Resources */, 139 | F8B08A7D1D196B8400F353F0 /* MainMenu.xib in Resources */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | /* End PBXResourcesBuildPhase section */ 144 | 145 | /* Begin PBXShellScriptBuildPhase section */ 146 | F8634C941D199E8E00EB99DC /* GitVersion */ = { 147 | isa = PBXShellScriptBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | ); 151 | inputPaths = ( 152 | ); 153 | name = GitVersion; 154 | outputPaths = ( 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | shellPath = /bin/sh; 158 | shellScript = "set -o errexit\nset -o nounset\n\nINFO_PLIST=\"${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}/Info\"\n\nVERSION=$(git rev-list HEAD | wc -l)\n\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $VERSION\" \"${INFOPLIST_FILE}\""; 159 | }; 160 | /* End PBXShellScriptBuildPhase section */ 161 | 162 | /* Begin PBXSourcesBuildPhase section */ 163 | F8B08A701D196B8400F353F0 /* Sources */ = { 164 | isa = PBXSourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | F8B08A781D196B8400F353F0 /* AppDelegate.swift in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | F8B08A7B1D196B8400F353F0 /* MainMenu.xib */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | F8B08A7C1D196B8400F353F0 /* Base */, 178 | ); 179 | name = MainMenu.xib; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXVariantGroup section */ 183 | 184 | /* Begin XCBuildConfiguration section */ 185 | F8B08A7F1D196B8400F353F0 /* Debug */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | ALWAYS_SEARCH_USER_PATHS = NO; 189 | CLANG_ANALYZER_NONNULL = YES; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_WARN_BOOL_CONVERSION = YES; 195 | CLANG_WARN_CONSTANT_CONVERSION = YES; 196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 197 | CLANG_WARN_EMPTY_BODY = YES; 198 | CLANG_WARN_ENUM_CONVERSION = YES; 199 | CLANG_WARN_INT_CONVERSION = YES; 200 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 201 | CLANG_WARN_UNREACHABLE_CODE = YES; 202 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 203 | CODE_SIGN_IDENTITY = "-"; 204 | COPY_PHASE_STRIP = NO; 205 | DEBUG_INFORMATION_FORMAT = dwarf; 206 | ENABLE_STRICT_OBJC_MSGSEND = YES; 207 | ENABLE_TESTABILITY = YES; 208 | GCC_C_LANGUAGE_STANDARD = gnu99; 209 | GCC_DYNAMIC_NO_PIC = NO; 210 | GCC_NO_COMMON_BLOCKS = YES; 211 | GCC_OPTIMIZATION_LEVEL = 0; 212 | GCC_PREPROCESSOR_DEFINITIONS = ( 213 | "DEBUG=1", 214 | "$(inherited)", 215 | ); 216 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 217 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 218 | GCC_WARN_UNDECLARED_SELECTOR = YES; 219 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 220 | GCC_WARN_UNUSED_FUNCTION = YES; 221 | GCC_WARN_UNUSED_VARIABLE = YES; 222 | MACOSX_DEPLOYMENT_TARGET = 10.11; 223 | MTL_ENABLE_DEBUG_INFO = YES; 224 | ONLY_ACTIVE_ARCH = YES; 225 | SDKROOT = macosx; 226 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 227 | }; 228 | name = Debug; 229 | }; 230 | F8B08A801D196B8400F353F0 /* Release */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | CLANG_ANALYZER_NONNULL = YES; 235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 236 | CLANG_CXX_LIBRARY = "libc++"; 237 | CLANG_ENABLE_MODULES = YES; 238 | CLANG_ENABLE_OBJC_ARC = YES; 239 | CLANG_WARN_BOOL_CONVERSION = YES; 240 | CLANG_WARN_CONSTANT_CONVERSION = YES; 241 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 242 | CLANG_WARN_EMPTY_BODY = YES; 243 | CLANG_WARN_ENUM_CONVERSION = YES; 244 | CLANG_WARN_INT_CONVERSION = YES; 245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 246 | CLANG_WARN_UNREACHABLE_CODE = YES; 247 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 248 | CODE_SIGN_IDENTITY = "-"; 249 | COPY_PHASE_STRIP = NO; 250 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 251 | ENABLE_NS_ASSERTIONS = NO; 252 | ENABLE_STRICT_OBJC_MSGSEND = YES; 253 | GCC_C_LANGUAGE_STANDARD = gnu99; 254 | GCC_NO_COMMON_BLOCKS = YES; 255 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 256 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 257 | GCC_WARN_UNDECLARED_SELECTOR = YES; 258 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 259 | GCC_WARN_UNUSED_FUNCTION = YES; 260 | GCC_WARN_UNUSED_VARIABLE = YES; 261 | MACOSX_DEPLOYMENT_TARGET = 10.11; 262 | MTL_ENABLE_DEBUG_INFO = NO; 263 | SDKROOT = macosx; 264 | }; 265 | name = Release; 266 | }; 267 | F8B08A821D196B8400F353F0 /* Debug */ = { 268 | isa = XCBuildConfiguration; 269 | buildSettings = { 270 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 271 | COMBINE_HIDPI_IMAGES = YES; 272 | INFOPLIST_FILE = DockServiceManager/Info.plist; 273 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 274 | MACOSX_DEPLOYMENT_TARGET = 10.9; 275 | PRODUCT_BUNDLE_IDENTIFIER = com.github.ygini.DockServiceManager; 276 | PRODUCT_NAME = "$(TARGET_NAME)"; 277 | }; 278 | name = Debug; 279 | }; 280 | F8B08A831D196B8400F353F0 /* Release */ = { 281 | isa = XCBuildConfiguration; 282 | buildSettings = { 283 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 284 | COMBINE_HIDPI_IMAGES = YES; 285 | INFOPLIST_FILE = DockServiceManager/Info.plist; 286 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 287 | MACOSX_DEPLOYMENT_TARGET = 10.9; 288 | PRODUCT_BUNDLE_IDENTIFIER = com.github.ygini.DockServiceManager; 289 | PRODUCT_NAME = "$(TARGET_NAME)"; 290 | }; 291 | name = Release; 292 | }; 293 | /* End XCBuildConfiguration section */ 294 | 295 | /* Begin XCConfigurationList section */ 296 | F8B08A6F1D196B8400F353F0 /* Build configuration list for PBXProject "DockServiceManager" */ = { 297 | isa = XCConfigurationList; 298 | buildConfigurations = ( 299 | F8B08A7F1D196B8400F353F0 /* Debug */, 300 | F8B08A801D196B8400F353F0 /* Release */, 301 | ); 302 | defaultConfigurationIsVisible = 0; 303 | defaultConfigurationName = Release; 304 | }; 305 | F8B08A811D196B8400F353F0 /* Build configuration list for PBXNativeTarget "DockServiceManager" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | F8B08A821D196B8400F353F0 /* Debug */, 309 | F8B08A831D196B8400F353F0 /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | /* End XCConfigurationList section */ 315 | }; 316 | rootObject = F8B08A6C1D196B8400F353F0 /* Project object */; 317 | } 318 | -------------------------------------------------------------------------------- /DockServiceManager.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DockServiceManager/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DockServiceManager 4 | // 5 | // Created by Yoann Gini on 21/06/2016. 6 | // Copyright © 2016 Yoann Gini. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet weak var window: NSWindow! 15 | var previousDockItems: [[String:String]]? 16 | 17 | func applicationDidFinishLaunching(aNotification: NSNotification) { 18 | // Insert code here to initialize your application 19 | NSDistributedNotificationCenter.defaultCenter().addObserverForName("com.apple.MCX._managementStatusChangedForDomains", object: "com.apple.MCX", queue: nil) { (note) -> Void in 20 | note.userInfo 21 | guard let bundleID = NSBundle.mainBundle().bundleIdentifier else { return } 22 | guard let domains = note.userInfo?["com.apple.MCX.changedDomains"] as? [String] else { return } 23 | if domains.contains(bundleID) { 24 | self.handleSettingsUpdates() 25 | } 26 | } 27 | 28 | self.handleSettingsUpdates() 29 | 30 | } 31 | 32 | func applicationWillTerminate(aNotification: NSNotification) { 33 | // Insert code here to tear down your application 34 | } 35 | 36 | func handleSettingsUpdates() { 37 | guard let dockitems = NSUserDefaults.standardUserDefaults().arrayForKey("dockitems") as? [[String:String]] else { 38 | return 39 | } 40 | 41 | 42 | if let previousDockItems = previousDockItems { 43 | if dockitems == previousDockItems { 44 | return 45 | } 46 | } 47 | 48 | for item in dockitems { 49 | var args = [String]() 50 | 51 | for key in item.keys { 52 | var potentialValue = item[key] 53 | 54 | if var value = potentialValue { 55 | if key == "add" { 56 | if !value.containsString("/") 57 | && value.containsString(".") { 58 | potentialValue = NSWorkspace.sharedWorkspace().absolutePathForAppBundleWithIdentifier(value) 59 | if (potentialValue != nil) { 60 | value = potentialValue! 61 | } 62 | } 63 | } 64 | 65 | args.append("--"+key) 66 | args.append(value) 67 | } 68 | } 69 | 70 | guard let dockutil = NSBundle.mainBundle().pathForResource("dockutil", ofType: nil) else { 71 | return 72 | } 73 | 74 | let dockutilTask = NSTask.launchedTaskWithLaunchPath(dockutil, arguments: args) 75 | 76 | dockutilTask.waitUntilExit() 77 | } 78 | 79 | previousDockItems = dockitems 80 | 81 | } 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /DockServiceManager/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /DockServiceManager/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /DockServiceManager/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 26 25 | LSBackgroundOnly 26 | 27 | LSMinimumSystemVersion 28 | $(MACOSX_DEPLOYMENT_TARGET) 29 | NSHumanReadableCopyright 30 | Copyright © 2016 Yoann Gini. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Yoann Gini 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of DockServiceManager nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Package/.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /Package/DockServiceManager.pkgproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PROJECT 6 | 7 | PACKAGE_FILES 8 | 9 | DEFAULT_INSTALL_LOCATION 10 | / 11 | HIERARCHY 12 | 13 | CHILDREN 14 | 15 | 16 | CHILDREN 17 | 18 | 19 | CHILDREN 20 | 21 | GID 22 | 80 23 | PATH 24 | Utilities 25 | PATH_TYPE 26 | 0 27 | PERMISSIONS 28 | 493 29 | TYPE 30 | 1 31 | UID 32 | 0 33 | 34 | 35 | GID 36 | 80 37 | PATH 38 | Applications 39 | PATH_TYPE 40 | 0 41 | PERMISSIONS 42 | 509 43 | TYPE 44 | 1 45 | UID 46 | 0 47 | 48 | 49 | CHILDREN 50 | 51 | 52 | CHILDREN 53 | 54 | 55 | CHILDREN 56 | 57 | 58 | CHILDREN 59 | 60 | GID 61 | 80 62 | PATH 63 | build/DockServiceManager.app 64 | PATH_TYPE 65 | 1 66 | PERMISSIONS 67 | 493 68 | TYPE 69 | 3 70 | UID 71 | 0 72 | 73 | 74 | GID 75 | 80 76 | PATH 77 | com.github.ygini.DockServiceManager 78 | PATH_TYPE 79 | 0 80 | PERMISSIONS 81 | 493 82 | TYPE 83 | 2 84 | UID 85 | 0 86 | 87 | 88 | GID 89 | 80 90 | PATH 91 | Application Support 92 | PATH_TYPE 93 | 0 94 | PERMISSIONS 95 | 493 96 | TYPE 97 | 1 98 | UID 99 | 0 100 | 101 | 102 | CHILDREN 103 | 104 | GID 105 | 0 106 | PATH 107 | Automator 108 | PATH_TYPE 109 | 0 110 | PERMISSIONS 111 | 493 112 | TYPE 113 | 1 114 | UID 115 | 0 116 | 117 | 118 | CHILDREN 119 | 120 | GID 121 | 0 122 | PATH 123 | Documentation 124 | PATH_TYPE 125 | 0 126 | PERMISSIONS 127 | 493 128 | TYPE 129 | 1 130 | UID 131 | 0 132 | 133 | 134 | CHILDREN 135 | 136 | GID 137 | 0 138 | PATH 139 | Filesystems 140 | PATH_TYPE 141 | 0 142 | PERMISSIONS 143 | 493 144 | TYPE 145 | 1 146 | UID 147 | 0 148 | 149 | 150 | CHILDREN 151 | 152 | GID 153 | 0 154 | PATH 155 | Frameworks 156 | PATH_TYPE 157 | 0 158 | PERMISSIONS 159 | 493 160 | TYPE 161 | 1 162 | UID 163 | 0 164 | 165 | 166 | CHILDREN 167 | 168 | GID 169 | 0 170 | PATH 171 | Input Methods 172 | PATH_TYPE 173 | 0 174 | PERMISSIONS 175 | 493 176 | TYPE 177 | 1 178 | UID 179 | 0 180 | 181 | 182 | CHILDREN 183 | 184 | GID 185 | 0 186 | PATH 187 | Internet Plug-Ins 188 | PATH_TYPE 189 | 0 190 | PERMISSIONS 191 | 493 192 | TYPE 193 | 1 194 | UID 195 | 0 196 | 197 | 198 | CHILDREN 199 | 200 | 201 | CHILDREN 202 | 203 | GID 204 | 0 205 | PATH 206 | LaunchAgent/com.github.ygini.DockServiceManager.plist 207 | PATH_TYPE 208 | 1 209 | PERMISSIONS 210 | 420 211 | TYPE 212 | 3 213 | UID 214 | 0 215 | 216 | 217 | GID 218 | 0 219 | PATH 220 | LaunchAgents 221 | PATH_TYPE 222 | 0 223 | PERMISSIONS 224 | 493 225 | TYPE 226 | 1 227 | UID 228 | 0 229 | 230 | 231 | CHILDREN 232 | 233 | GID 234 | 0 235 | PATH 236 | LaunchDaemons 237 | PATH_TYPE 238 | 0 239 | PERMISSIONS 240 | 493 241 | TYPE 242 | 1 243 | UID 244 | 0 245 | 246 | 247 | CHILDREN 248 | 249 | GID 250 | 0 251 | PATH 252 | PreferencePanes 253 | PATH_TYPE 254 | 0 255 | PERMISSIONS 256 | 493 257 | TYPE 258 | 1 259 | UID 260 | 0 261 | 262 | 263 | CHILDREN 264 | 265 | GID 266 | 0 267 | PATH 268 | Preferences 269 | PATH_TYPE 270 | 0 271 | PERMISSIONS 272 | 493 273 | TYPE 274 | 1 275 | UID 276 | 0 277 | 278 | 279 | CHILDREN 280 | 281 | GID 282 | 80 283 | PATH 284 | Printers 285 | PATH_TYPE 286 | 0 287 | PERMISSIONS 288 | 493 289 | TYPE 290 | 1 291 | UID 292 | 0 293 | 294 | 295 | CHILDREN 296 | 297 | GID 298 | 0 299 | PATH 300 | PrivilegedHelperTools 301 | PATH_TYPE 302 | 0 303 | PERMISSIONS 304 | 493 305 | TYPE 306 | 1 307 | UID 308 | 0 309 | 310 | 311 | CHILDREN 312 | 313 | GID 314 | 0 315 | PATH 316 | QuickLook 317 | PATH_TYPE 318 | 0 319 | PERMISSIONS 320 | 493 321 | TYPE 322 | 1 323 | UID 324 | 0 325 | 326 | 327 | CHILDREN 328 | 329 | GID 330 | 0 331 | PATH 332 | QuickTime 333 | PATH_TYPE 334 | 0 335 | PERMISSIONS 336 | 493 337 | TYPE 338 | 1 339 | UID 340 | 0 341 | 342 | 343 | CHILDREN 344 | 345 | GID 346 | 0 347 | PATH 348 | Screen Savers 349 | PATH_TYPE 350 | 0 351 | PERMISSIONS 352 | 493 353 | TYPE 354 | 1 355 | UID 356 | 0 357 | 358 | 359 | CHILDREN 360 | 361 | GID 362 | 0 363 | PATH 364 | Scripts 365 | PATH_TYPE 366 | 0 367 | PERMISSIONS 368 | 493 369 | TYPE 370 | 1 371 | UID 372 | 0 373 | 374 | 375 | CHILDREN 376 | 377 | GID 378 | 0 379 | PATH 380 | Services 381 | PATH_TYPE 382 | 0 383 | PERMISSIONS 384 | 493 385 | TYPE 386 | 1 387 | UID 388 | 0 389 | 390 | 391 | CHILDREN 392 | 393 | GID 394 | 0 395 | PATH 396 | Widgets 397 | PATH_TYPE 398 | 0 399 | PERMISSIONS 400 | 493 401 | TYPE 402 | 1 403 | UID 404 | 0 405 | 406 | 407 | GID 408 | 0 409 | PATH 410 | Library 411 | PATH_TYPE 412 | 0 413 | PERMISSIONS 414 | 493 415 | TYPE 416 | 1 417 | UID 418 | 0 419 | 420 | 421 | CHILDREN 422 | 423 | 424 | CHILDREN 425 | 426 | 427 | CHILDREN 428 | 429 | GID 430 | 0 431 | PATH 432 | Extensions 433 | PATH_TYPE 434 | 0 435 | PERMISSIONS 436 | 493 437 | TYPE 438 | 1 439 | UID 440 | 0 441 | 442 | 443 | GID 444 | 0 445 | PATH 446 | Library 447 | PATH_TYPE 448 | 0 449 | PERMISSIONS 450 | 493 451 | TYPE 452 | 1 453 | UID 454 | 0 455 | 456 | 457 | GID 458 | 0 459 | PATH 460 | System 461 | PATH_TYPE 462 | 0 463 | PERMISSIONS 464 | 493 465 | TYPE 466 | 1 467 | UID 468 | 0 469 | 470 | 471 | CHILDREN 472 | 473 | 474 | CHILDREN 475 | 476 | GID 477 | 0 478 | PATH 479 | Shared 480 | PATH_TYPE 481 | 0 482 | PERMISSIONS 483 | 1023 484 | TYPE 485 | 1 486 | UID 487 | 0 488 | 489 | 490 | GID 491 | 80 492 | PATH 493 | Users 494 | PATH_TYPE 495 | 0 496 | PERMISSIONS 497 | 493 498 | TYPE 499 | 1 500 | UID 501 | 0 502 | 503 | 504 | GID 505 | 0 506 | PATH 507 | / 508 | PATH_TYPE 509 | 0 510 | PERMISSIONS 511 | 493 512 | TYPE 513 | 1 514 | UID 515 | 0 516 | 517 | PAYLOAD_TYPE 518 | 0 519 | VERSION 520 | 3 521 | 522 | PACKAGE_SCRIPTS 523 | 524 | POSTINSTALL_PATH 525 | 526 | PATH 527 | postinstall 528 | PATH_TYPE 529 | 1 530 | 531 | RESOURCES 532 | 533 | 534 | PACKAGE_SETTINGS 535 | 536 | AUTHENTICATION 537 | 1 538 | CONCLUSION_ACTION 539 | 0 540 | IDENTIFIER 541 | com.github.ygini.DockServiceManager 542 | OVERWRITE_PERMISSIONS 543 | 544 | RELOCATABLE 545 | 546 | VERSION 547 | 1.0.1 548 | 549 | PROJECT_COMMENTS 550 | 551 | NOTES 552 | 553 | 554 | 555 | PROJECT_SETTINGS 556 | 557 | BUILD_PATH 558 | 559 | PATH 560 | build 561 | PATH_TYPE 562 | 1 563 | 564 | CERTIFICATE 565 | 566 | NAME 567 | Developer ID Installer: Yoann GINI 568 | PATH 569 | /Users/ygi/Library/Keychains/login.keychain 570 | 571 | EXCLUDED_FILES 572 | 573 | 574 | PATTERNS_ARRAY 575 | 576 | 577 | REGULAR_EXPRESSION 578 | 579 | STRING 580 | .DS_Store 581 | TYPE 582 | 0 583 | 584 | 585 | PROTECTED 586 | 587 | PROXY_NAME 588 | Remove .DS_Store files 589 | PROXY_TOOLTIP 590 | Remove ".DS_Store" files created by the Finder. 591 | STATE 592 | 593 | 594 | 595 | PATTERNS_ARRAY 596 | 597 | 598 | REGULAR_EXPRESSION 599 | 600 | STRING 601 | .pbdevelopment 602 | TYPE 603 | 0 604 | 605 | 606 | PROTECTED 607 | 608 | PROXY_NAME 609 | Remove .pbdevelopment files 610 | PROXY_TOOLTIP 611 | Remove ".pbdevelopment" files created by ProjectBuilder or Xcode. 612 | STATE 613 | 614 | 615 | 616 | PATTERNS_ARRAY 617 | 618 | 619 | REGULAR_EXPRESSION 620 | 621 | STRING 622 | CVS 623 | TYPE 624 | 1 625 | 626 | 627 | REGULAR_EXPRESSION 628 | 629 | STRING 630 | .cvsignore 631 | TYPE 632 | 0 633 | 634 | 635 | REGULAR_EXPRESSION 636 | 637 | STRING 638 | .cvspass 639 | TYPE 640 | 0 641 | 642 | 643 | REGULAR_EXPRESSION 644 | 645 | STRING 646 | .svn 647 | TYPE 648 | 1 649 | 650 | 651 | REGULAR_EXPRESSION 652 | 653 | STRING 654 | .git 655 | TYPE 656 | 1 657 | 658 | 659 | REGULAR_EXPRESSION 660 | 661 | STRING 662 | .gitignore 663 | TYPE 664 | 0 665 | 666 | 667 | PROTECTED 668 | 669 | PROXY_NAME 670 | Remove SCM metadata 671 | PROXY_TOOLTIP 672 | Remove helper files and folders used by the CVS, SVN or Git Source Code Management systems. 673 | STATE 674 | 675 | 676 | 677 | PATTERNS_ARRAY 678 | 679 | 680 | REGULAR_EXPRESSION 681 | 682 | STRING 683 | classes.nib 684 | TYPE 685 | 0 686 | 687 | 688 | REGULAR_EXPRESSION 689 | 690 | STRING 691 | designable.db 692 | TYPE 693 | 0 694 | 695 | 696 | REGULAR_EXPRESSION 697 | 698 | STRING 699 | info.nib 700 | TYPE 701 | 0 702 | 703 | 704 | PROTECTED 705 | 706 | PROXY_NAME 707 | Optimize nib files 708 | PROXY_TOOLTIP 709 | Remove "classes.nib", "info.nib" and "designable.nib" files within .nib bundles. 710 | STATE 711 | 712 | 713 | 714 | PATTERNS_ARRAY 715 | 716 | 717 | REGULAR_EXPRESSION 718 | 719 | STRING 720 | Resources Disabled 721 | TYPE 722 | 1 723 | 724 | 725 | PROTECTED 726 | 727 | PROXY_NAME 728 | Remove Resources Disabled folders 729 | PROXY_TOOLTIP 730 | Remove "Resources Disabled" folders. 731 | STATE 732 | 733 | 734 | 735 | SEPARATOR 736 | 737 | 738 | 739 | NAME 740 | DockServiceManager 741 | 742 | 743 | TYPE 744 | 1 745 | VERSION 746 | 2 747 | 748 | 749 | -------------------------------------------------------------------------------- /Package/LaunchAgent/com.github.ygini.DockServiceManager.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.github.ygini.DockServiceManager 7 | KeepAlive 8 | 9 | Program 10 | /Library/Application Support/com.github.ygini.DockServiceManager/DockServiceManager.app/Contents/MacOS/DockServiceManager 11 | 12 | 13 | -------------------------------------------------------------------------------- /Package/postinstall: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while read shortname 4 | do 5 | uid=$(id -u "$shortname") 6 | 7 | if [ -n "$uid" ] 8 | then 9 | /bin/launchctl asuser "$uid" /bin/launchctl unload /Library/LaunchAgents/com.github.ygini.DockServiceManager.plist 10 | /bin/launchctl asuser "$uid" /bin/launchctl remove com.github.ygini.DockServiceManager 11 | /bin/launchctl asuser "$uid" /bin/launchctl load /Library/LaunchAgents/com.github.ygini.DockServiceManager.plist 12 | fi 13 | done < <(ps aux | grep "MacOS/[F]inder" | awk '{print $1}') 14 | 15 | exit 0 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DockServiceManager 2 | 3 | This service allows a system administrator to remotely execute dockutil command via MDM. 4 | 5 | The service runs as a LaunchAgent and read its settings inside com.github.ygini.DockServiceManager preference domain. The dockitems key in this preference domain contain an array of dictionaries composed by keys used by dockutils. 6 | 7 | The service runs all commands at login and each time a change is made. Currently, only MDM based update are caught. If you modify the settings with defaults, it won’t be seen by the service (but applied at the next login time if the settings are persistent). 8 | 9 | 10.9 or better is needed for this tool since it's written in Swift (not sure if it's a good idea but we will see). 10 | 11 | When using add verbs with dockutil, DockServiceManager allow you to specify full path for app or app BundleID. If you use BundleID, it will use NSWorkspace API to find the best candidate. 12 | 13 | # Sample settings 14 | 15 | ``` 16 | 17 | 18 | 19 | 20 | dockitems 21 | 22 | 23 | add 24 | com.microsoft.Outlook 25 | position 26 | end 27 | 28 | 29 | add 30 | /Applications/Managed Software Center.app 31 | position 32 | beginning 33 | 34 | 35 | 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /sample/com.github.ygini.DockServiceManager.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | dockitems 6 | 7 | 8 | add 9 | com.microsoft.Outlook 10 | position 11 | end 12 | 13 | 14 | add 15 | /Applications/Managed Software Center.app 16 | position 17 | beginning 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tools/runall.debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while read shortname 4 | do 5 | uid=$(id -u "$shortname") 6 | 7 | if [ -n "$uid" ] 8 | then 9 | /bin/launchctl asuser "$uid" /bin/launchctl load /Library/LaunchAgents/com.github.ygini.DockServiceManager.plist 10 | fi 11 | done < <(ps aux | grep "MacOS/[F]inder" | awk '{print $1}') 12 | 13 | exit 0 14 | -------------------------------------------------------------------------------- /tools/sendFakeNotification.swift: -------------------------------------------------------------------------------- 1 | #!/usr/bin/swift 2 | 3 | import Foundation 4 | 5 | let userInfo = ["com.apple.MCX.changedDomains": ["com.github.ygini.DockServiceManager"]] 6 | 7 | NSDistributedNotificationCenter.defaultCenter().postNotificationName("com.apple.MCX._managementStatusChangedForDomains", object: "com.apple.MCX", userInfo: userInfo) 8 | 9 | exit(0) 10 | -------------------------------------------------------------------------------- /tools/stopall.debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while read shortname 4 | do 5 | uid=$(id -u "$shortname") 6 | 7 | if [ -n "$uid" ] 8 | then 9 | /bin/launchctl asuser "$uid" /bin/launchctl unload /Library/LaunchAgents/com.github.ygini.DockServiceManager.plist 10 | /bin/launchctl asuser "$uid" /bin/launchctl remove com.github.ygini.DockServiceManager 11 | fi 12 | done < <(ps aux | grep "MacOS/[F]inder" | awk '{print $1}') 13 | 14 | exit 0 15 | --------------------------------------------------------------------------------