├── Docs ├── RunService.png ├── RunService2.png ├── Services1.png └── Services2.png ├── LICENSE ├── README.md ├── Run Service.action └── Contents │ ├── Info.plist │ ├── MacOS │ └── Run Service │ └── Resources │ └── en.lproj │ ├── InfoPlist.strings │ └── main.nib ├── Run Service.xcodeproj ├── .gitignore └── project.pbxproj ├── RunService.action.zip └── Services Automator Actions ├── Info.plist ├── Services_Automator_Actions.h ├── Services_Automator_Actions.m └── en.lproj ├── InfoPlist.strings └── main.xib /Docs/RunService.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Docs/RunService.png -------------------------------------------------------------------------------- /Docs/RunService2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Docs/RunService2.png -------------------------------------------------------------------------------- /Docs/Services1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Docs/Services1.png -------------------------------------------------------------------------------- /Docs/Services2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Docs/Services2.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RunServiceActionForAutomator 2 | 3 | **An Automator Action plugin for invoking macOS Service items.** 4 | 5 | Most Services operate on either texts or files & folders (they can also work with web archives, but this isn't currently handled here). 6 | 7 | Any of those services can now be used in Automator actions with the "Run Service" action provided by this project's free Automator plugin. This is handy in cases where such an operation is not provided as a distinct Automator action. 8 | 9 | ## Installation 10 | 11 | To install the "Run Service" action, simply double click the **Run Service.action** file. That will prompt you to install it into *~/Library/Automator* if it's not already there. 12 | 13 | To get the file, use the **Download ZIP** button on the right side of this page. The zip contains both the source code (which you probably don't need) and the action file. 14 | 15 | Enjoy! 16 | 17 | Thomas Tempelmann 18 | 19 | [http://apps.tempel.org/](http://apps.tempel.org/) 20 | 21 | ## Examples 22 | 23 | ### Example of using the "Run Service" action in Automator in order to invoke the "Summarize" Service. 24 | 25 | ![Run Service](Docs/RunService2.png "Example of using the 'Run Service' action") 26 | 27 | ### Invoking DEVONthink 3 services 28 | 29 | Even though services of [DEVONthink 3](https://www.devontechnologies.com) appear as "DEVONthink 3: …", you need to use "DEVONthink Pro: …" as the service name for this automator action, or the service may not be found. 30 | -------------------------------------------------------------------------------- /Run Service.action/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AMAccepts 6 | 7 | Container 8 | List 9 | Optional 10 | 11 | Types 12 | 13 | com.apple.cocoa.string 14 | com.apple.cocoa.attributed-string 15 | com.apple.cocoa.path 16 | 17 | 18 | AMCanShowSelectedItemsWhenRun 19 | 20 | AMCanShowWhenRun 21 | 22 | AMCategory 23 | AMCategoryUtilities 24 | AMDefaultParameters 25 | 26 | AMDescription 27 | 28 | AMDAlert 29 | 30 | AMDInput 31 | Specify how the input data shall be passed on to the Service (Note: Services usually either work with Text or with Files) 32 | AMDNote 33 | 34 | AMDOptions 35 | 36 | AMDRelatedActions 37 | 38 | AMDRequires 39 | 40 | AMDResult 41 | Specify how any response from the Service shall be processed as output 42 | AMDSummary 43 | Invoke any System Service by its name 44 | AMDWebsite 45 | (* AMDWebsite URL to additional information or documentation for the action goes here. (optional) *) 46 | 47 | AMKeywords 48 | 49 | Services 50 | Service 51 | 52 | AMName 53 | Run Service 54 | AMProvides 55 | 56 | Container 57 | List 58 | Types 59 | 60 | com.apple.cocoa.attributed-string 61 | com.apple.cocoa.string 62 | com.apple.cocoa.path 63 | 64 | 65 | AMRequiredResources 66 | 67 | AMWarning 68 | 69 | Action 70 | ActionName 71 | Level 72 | 0 73 | 74 | BuildMachineOSBuild 75 | 14F27 76 | CFBundleDevelopmentRegion 77 | en 78 | CFBundleExecutable 79 | Run Service 80 | CFBundleIdentifier 81 | org.tempel.Run-Service 82 | CFBundleInfoDictionaryVersion 83 | 6.0 84 | CFBundleName 85 | Run Service 86 | CFBundlePackageType 87 | BNDL 88 | CFBundleShortVersionString 89 | 1.0 90 | CFBundleSignature 91 | ???? 92 | CFBundleSupportedPlatforms 93 | 94 | MacOSX 95 | 96 | CFBundleVersion 97 | 1.0 98 | DTCompiler 99 | com.apple.compilers.llvm.clang.1_0 100 | DTPlatformBuild 101 | 7A220 102 | DTPlatformVersion 103 | GM 104 | DTSDKBuild 105 | 15A278 106 | DTSDKName 107 | macosx10.11 108 | DTXcode 109 | 0700 110 | DTXcodeBuild 111 | 7A220 112 | NSHumanReadableCopyright 113 | 2015 Thomas Tempelmann 114 | NSPrincipalClass 115 | Services_Automator_Actions 116 | 117 | 118 | -------------------------------------------------------------------------------- /Run Service.action/Contents/MacOS/Run Service: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Run Service.action/Contents/MacOS/Run Service -------------------------------------------------------------------------------- /Run Service.action/Contents/Resources/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Run Service.action/Contents/Resources/en.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /Run Service.action/Contents/Resources/en.lproj/main.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/Run Service.action/Contents/Resources/en.lproj/main.nib -------------------------------------------------------------------------------- /Run Service.xcodeproj/.gitignore: -------------------------------------------------------------------------------- 1 | /xcuserdata/ 2 | -------------------------------------------------------------------------------- /Run Service.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A49649521B4AFF1000F8C66B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A49649501B4AFF1000F8C66B /* InfoPlist.strings */; }; 11 | A49649551B4AFF1000F8C66B /* Services_Automator_Actions.m in Sources */ = {isa = PBXBuildFile; fileRef = A49649541B4AFF1000F8C66B /* Services_Automator_Actions.m */; }; 12 | A49649581B4AFF1000F8C66B /* main.xib in Resources */ = {isa = PBXBuildFile; fileRef = A49649561B4AFF1000F8C66B /* main.xib */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | A47908AE1BAC6A4500140F54 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/main.xib; sourceTree = ""; }; 17 | A496494B1B4AFF1000F8C66B /* Run Service.action */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Run Service.action"; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | A496494F1B4AFF1000F8C66B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 19 | A49649511B4AFF1000F8C66B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 20 | A49649531B4AFF1000F8C66B /* Services_Automator_Actions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Services_Automator_Actions.h; sourceTree = ""; }; 21 | A49649541B4AFF1000F8C66B /* Services_Automator_Actions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Services_Automator_Actions.m; sourceTree = ""; }; 22 | /* End PBXFileReference section */ 23 | 24 | /* Begin PBXFrameworksBuildPhase section */ 25 | A49649471B4AFF1000F8C66B /* Frameworks */ = { 26 | isa = PBXFrameworksBuildPhase; 27 | buildActionMask = 2147483647; 28 | files = ( 29 | ); 30 | runOnlyForDeploymentPostprocessing = 0; 31 | }; 32 | /* End PBXFrameworksBuildPhase section */ 33 | 34 | /* Begin PBXGroup section */ 35 | A49649411B4AFF1000F8C66B = { 36 | isa = PBXGroup; 37 | children = ( 38 | A496494D1B4AFF1000F8C66B /* Automator Actions */, 39 | A496494C1B4AFF1000F8C66B /* Products */, 40 | ); 41 | sourceTree = ""; 42 | }; 43 | A496494C1B4AFF1000F8C66B /* Products */ = { 44 | isa = PBXGroup; 45 | children = ( 46 | A496494B1B4AFF1000F8C66B /* Run Service.action */, 47 | ); 48 | name = Products; 49 | sourceTree = ""; 50 | }; 51 | A496494D1B4AFF1000F8C66B /* Automator Actions */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | A49649531B4AFF1000F8C66B /* Services_Automator_Actions.h */, 55 | A49649541B4AFF1000F8C66B /* Services_Automator_Actions.m */, 56 | A49649561B4AFF1000F8C66B /* main.xib */, 57 | A496494E1B4AFF1000F8C66B /* Supporting Files */, 58 | ); 59 | name = "Automator Actions"; 60 | path = "Services Automator Actions"; 61 | sourceTree = ""; 62 | }; 63 | A496494E1B4AFF1000F8C66B /* Supporting Files */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | A496494F1B4AFF1000F8C66B /* Info.plist */, 67 | A49649501B4AFF1000F8C66B /* InfoPlist.strings */, 68 | ); 69 | name = "Supporting Files"; 70 | sourceTree = ""; 71 | }; 72 | /* End PBXGroup section */ 73 | 74 | /* Begin PBXNativeTarget section */ 75 | A496494A1B4AFF1000F8C66B /* Run Service */ = { 76 | isa = PBXNativeTarget; 77 | buildConfigurationList = A496495B1B4AFF1000F8C66B /* Build configuration list for PBXNativeTarget "Run Service" */; 78 | buildPhases = ( 79 | A49649461B4AFF1000F8C66B /* Sources */, 80 | A49649471B4AFF1000F8C66B /* Frameworks */, 81 | A49649481B4AFF1000F8C66B /* Resources */, 82 | A49649491B4AFF1000F8C66B /* ShellScript */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = "Run Service"; 89 | productName = "Services Automator Actions"; 90 | productReference = A496494B1B4AFF1000F8C66B /* Run Service.action */; 91 | productType = "com.apple.product-type.bundle"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | A49649421B4AFF1000F8C66B /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 0700; 100 | ORGANIZATIONNAME = "Thomas Tempelmann"; 101 | TargetAttributes = { 102 | A496494A1B4AFF1000F8C66B = { 103 | CreatedOnToolsVersion = 6.2; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = A49649451B4AFF1000F8C66B /* Build configuration list for PBXProject "Perform Service" */; 108 | compatibilityVersion = "Xcode 3.2"; 109 | developmentRegion = English; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | ); 114 | mainGroup = A49649411B4AFF1000F8C66B; 115 | productRefGroup = A496494C1B4AFF1000F8C66B /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | A496494A1B4AFF1000F8C66B /* Run Service */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXResourcesBuildPhase section */ 125 | A49649481B4AFF1000F8C66B /* Resources */ = { 126 | isa = PBXResourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | A49649581B4AFF1000F8C66B /* main.xib in Resources */, 130 | A49649521B4AFF1000F8C66B /* InfoPlist.strings in Resources */, 131 | ); 132 | runOnlyForDeploymentPostprocessing = 0; 133 | }; 134 | /* End PBXResourcesBuildPhase section */ 135 | 136 | /* Begin PBXShellScriptBuildPhase section */ 137 | A49649491B4AFF1000F8C66B /* ShellScript */ = { 138 | isa = PBXShellScriptBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | ); 142 | inputPaths = ( 143 | ); 144 | outputPaths = ( 145 | ); 146 | runOnlyForDeploymentPostprocessing = 0; 147 | shellPath = /bin/sh; 148 | shellScript = "amlint \"${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}\""; 149 | }; 150 | /* End PBXShellScriptBuildPhase section */ 151 | 152 | /* Begin PBXSourcesBuildPhase section */ 153 | A49649461B4AFF1000F8C66B /* Sources */ = { 154 | isa = PBXSourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | A49649551B4AFF1000F8C66B /* Services_Automator_Actions.m in Sources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXSourcesBuildPhase section */ 162 | 163 | /* Begin PBXVariantGroup section */ 164 | A49649501B4AFF1000F8C66B /* InfoPlist.strings */ = { 165 | isa = PBXVariantGroup; 166 | children = ( 167 | A49649511B4AFF1000F8C66B /* en */, 168 | ); 169 | name = InfoPlist.strings; 170 | sourceTree = ""; 171 | }; 172 | A49649561B4AFF1000F8C66B /* main.xib */ = { 173 | isa = PBXVariantGroup; 174 | children = ( 175 | A47908AE1BAC6A4500140F54 /* en */, 176 | ); 177 | name = main.xib; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXVariantGroup section */ 181 | 182 | /* Begin XCBuildConfiguration section */ 183 | A49649591B4AFF1000F8C66B /* Debug */ = { 184 | isa = XCBuildConfiguration; 185 | buildSettings = { 186 | ALWAYS_SEARCH_USER_PATHS = NO; 187 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 188 | CLANG_CXX_LIBRARY = "libc++"; 189 | CLANG_ENABLE_MODULES = YES; 190 | CLANG_ENABLE_OBJC_ARC = YES; 191 | CLANG_WARN_BOOL_CONVERSION = YES; 192 | CLANG_WARN_CONSTANT_CONVERSION = YES; 193 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 194 | CLANG_WARN_EMPTY_BODY = YES; 195 | CLANG_WARN_ENUM_CONVERSION = YES; 196 | CLANG_WARN_INT_CONVERSION = YES; 197 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 198 | CLANG_WARN_UNREACHABLE_CODE = YES; 199 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 200 | COPY_PHASE_STRIP = NO; 201 | ENABLE_STRICT_OBJC_MSGSEND = YES; 202 | ENABLE_TESTABILITY = YES; 203 | GCC_C_LANGUAGE_STANDARD = gnu99; 204 | GCC_DYNAMIC_NO_PIC = NO; 205 | GCC_OPTIMIZATION_LEVEL = 0; 206 | GCC_PREPROCESSOR_DEFINITIONS = ( 207 | "DEBUG=1", 208 | "$(inherited)", 209 | ); 210 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 211 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 212 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 213 | GCC_WARN_UNDECLARED_SELECTOR = YES; 214 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 215 | GCC_WARN_UNUSED_FUNCTION = YES; 216 | GCC_WARN_UNUSED_VARIABLE = YES; 217 | MACOSX_DEPLOYMENT_TARGET = 10.7; 218 | MTL_ENABLE_DEBUG_INFO = YES; 219 | ONLY_ACTIVE_ARCH = YES; 220 | SDKROOT = macosx; 221 | }; 222 | name = Debug; 223 | }; 224 | A496495A1B4AFF1000F8C66B /* Release */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 229 | CLANG_CXX_LIBRARY = "libc++"; 230 | CLANG_ENABLE_MODULES = YES; 231 | CLANG_ENABLE_OBJC_ARC = YES; 232 | CLANG_WARN_BOOL_CONVERSION = YES; 233 | CLANG_WARN_CONSTANT_CONVERSION = YES; 234 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 235 | CLANG_WARN_EMPTY_BODY = YES; 236 | CLANG_WARN_ENUM_CONVERSION = YES; 237 | CLANG_WARN_INT_CONVERSION = YES; 238 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 239 | CLANG_WARN_UNREACHABLE_CODE = YES; 240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 241 | COPY_PHASE_STRIP = NO; 242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 243 | ENABLE_NS_ASSERTIONS = NO; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu99; 246 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 247 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 248 | GCC_WARN_UNDECLARED_SELECTOR = YES; 249 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 250 | GCC_WARN_UNUSED_FUNCTION = YES; 251 | GCC_WARN_UNUSED_VARIABLE = YES; 252 | MACOSX_DEPLOYMENT_TARGET = 10.7; 253 | MTL_ENABLE_DEBUG_INFO = NO; 254 | SDKROOT = macosx; 255 | }; 256 | name = Release; 257 | }; 258 | A496495C1B4AFF1000F8C66B /* Debug */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | COMBINE_HIDPI_IMAGES = YES; 262 | CONFIGURATION_BUILD_DIR = /Users/tempi/Library/Automator; 263 | INFOPLIST_FILE = "Services Automator Actions/Info.plist"; 264 | INSTALL_PATH = "$(HOME)/Library/Automator"; 265 | OTHER_OSAFLAGS = "-x -t 0 -c 0"; 266 | PRODUCT_BUNDLE_IDENTIFIER = "org.tempel.$(PRODUCT_NAME:rfc1034identifier)"; 267 | PRODUCT_NAME = "$(TARGET_NAME)"; 268 | WRAPPER_EXTENSION = action; 269 | }; 270 | name = Debug; 271 | }; 272 | A496495D1B4AFF1000F8C66B /* Release */ = { 273 | isa = XCBuildConfiguration; 274 | buildSettings = { 275 | COMBINE_HIDPI_IMAGES = YES; 276 | INFOPLIST_FILE = "Services Automator Actions/Info.plist"; 277 | INSTALL_PATH = "$(HOME)/Library/Automator"; 278 | OTHER_OSAFLAGS = "-x -t 0 -c 0"; 279 | PRODUCT_BUNDLE_IDENTIFIER = "org.tempel.$(PRODUCT_NAME:rfc1034identifier)"; 280 | PRODUCT_NAME = "$(TARGET_NAME)"; 281 | WRAPPER_EXTENSION = action; 282 | }; 283 | name = Release; 284 | }; 285 | /* End XCBuildConfiguration section */ 286 | 287 | /* Begin XCConfigurationList section */ 288 | A49649451B4AFF1000F8C66B /* Build configuration list for PBXProject "Perform Service" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | A49649591B4AFF1000F8C66B /* Debug */, 292 | A496495A1B4AFF1000F8C66B /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | A496495B1B4AFF1000F8C66B /* Build configuration list for PBXNativeTarget "Run Service" */ = { 298 | isa = XCConfigurationList; 299 | buildConfigurations = ( 300 | A496495C1B4AFF1000F8C66B /* Debug */, 301 | A496495D1B4AFF1000F8C66B /* Release */, 302 | ); 303 | defaultConfigurationIsVisible = 0; 304 | defaultConfigurationName = Release; 305 | }; 306 | /* End XCConfigurationList section */ 307 | }; 308 | rootObject = A49649421B4AFF1000F8C66B /* Project object */; 309 | } 310 | -------------------------------------------------------------------------------- /RunService.action.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tempelmann/RunServiceActionForAutomator/442a0de2f748794f84f6dd72d38b143fcb137cb3/RunService.action.zip -------------------------------------------------------------------------------- /Services Automator Actions/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AMAccepts 6 | 7 | Container 8 | List 9 | Optional 10 | 11 | Types 12 | 13 | com.apple.cocoa.string 14 | com.apple.cocoa.attributed-string 15 | com.apple.cocoa.path 16 | 17 | 18 | AMCanShowSelectedItemsWhenRun 19 | 20 | AMCanShowWhenRun 21 | 22 | AMCategory 23 | AMCategoryUtilities 24 | AMDefaultParameters 25 | 26 | AMDescription 27 | 28 | AMDAlert 29 | 30 | AMDInput 31 | Specify how the input data shall be passed on to the Service (Note: Services usually either work with Text or with Files) 32 | AMDNote 33 | 34 | AMDOptions 35 | 36 | AMDRelatedActions 37 | 38 | AMDRequires 39 | 40 | AMDResult 41 | Specify how any response from the Service shall be processed as output 42 | AMDSummary 43 | Invoke any System Service by its name 44 | AMDWebsite 45 | (* AMDWebsite URL to additional information or documentation for the action goes here. (optional) *) 46 | 47 | AMKeywords 48 | 49 | Services 50 | Service 51 | 52 | AMName 53 | Run Service 54 | AMProvides 55 | 56 | Container 57 | List 58 | Types 59 | 60 | com.apple.cocoa.attributed-string 61 | com.apple.cocoa.string 62 | com.apple.cocoa.path 63 | 64 | 65 | AMRequiredResources 66 | 67 | AMWarning 68 | 69 | Action 70 | ActionName 71 | Level 72 | 0 73 | 74 | CFBundleDevelopmentRegion 75 | en 76 | CFBundleExecutable 77 | $(EXECUTABLE_NAME) 78 | CFBundleIdentifier 79 | $(PRODUCT_BUNDLE_IDENTIFIER) 80 | CFBundleInfoDictionaryVersion 81 | 6.0 82 | CFBundleName 83 | $(PRODUCT_NAME) 84 | CFBundlePackageType 85 | BNDL 86 | CFBundleShortVersionString 87 | 1.0 88 | CFBundleSignature 89 | ???? 90 | CFBundleVersion 91 | 1.0 92 | NSHumanReadableCopyright 93 | 2015 Thomas Tempelmann 94 | NSPrincipalClass 95 | Services_Automator_Actions 96 | 97 | 98 | -------------------------------------------------------------------------------- /Services Automator Actions/Services_Automator_Actions.h: -------------------------------------------------------------------------------- 1 | // 2 | // Services_Automator_Actions.h 3 | // Services Automator Actions 4 | // 5 | // Created by Thomas Tempelmann on 06.07.15. 6 | // No rights reserved, use as you like. 7 | // 8 | 9 | #import 10 | 11 | @interface Services_Automator_Actions : AMBundleAction 12 | 13 | //@property (readonly, strong) IBOutlet NSView *view; // temporarily needed for setting the nib's view property, probably a bug in Xcode 6.4 & 7 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Services Automator Actions/Services_Automator_Actions.m: -------------------------------------------------------------------------------- 1 | // 2 | // Services_Automator_Actions.m 3 | // Services Automator Actions 4 | // 5 | // Created by Thomas Tempelmann on 06.07.15. 6 | // No rights reserved, use as you like. 7 | // 8 | 9 | #import "Services_Automator_Actions.h" 10 | #import 11 | 12 | const int InputTypeAuto = 0; 13 | const int InputTypeNone = 1; 14 | const int InputTypeText = 2; 15 | const int InputTypeFiles = 3; 16 | 17 | const int OutputTypeAuto = 0; 18 | const int OutputTypeText = 1; 19 | const int OutputTypeFiles = 2; 20 | 21 | @implementation Services_Automator_Actions 22 | 23 | - (instancetype)initWithDefinition:(NSDictionary *)dict fromArchive:(BOOL)archived 24 | { 25 | //NSLog(@"%s dict: %@", __FUNCTION__, dict); 26 | self = [super initWithDefinition:dict fromArchive:archived]; 27 | if (self) { 28 | // We're using Auto Layout - we need to do this here to avoid a warning in the Console log: 29 | self.view.translatesAutoresizingMaskIntoConstraints = NO; 30 | } 31 | return self; 32 | } 33 | 34 | - (id)runWithInput:(NSArray*)input error:(NSError * _Nullable *)errorOut 35 | { 36 | //NSLog(@"%s parms: %@", __FUNCTION__, self.parameters); 37 | 38 | NSString *serviceName = self.parameters[@"serviceName"]; 39 | int inputType = [(NSNumber*)self.parameters[@"inputType"] intValue]; 40 | int outputType = [(NSNumber*)self.parameters[@"outputType"] intValue]; 41 | 42 | NSPasteboard *pboard = [NSPasteboard pasteboardWithUniqueName]; 43 | 44 | NSMutableArray *fileURLs = [NSMutableArray array]; 45 | NSMutableArray *plainTexts = [NSMutableArray array]; 46 | NSMutableArray *richTexts = [NSMutableArray array]; 47 | 48 | if (!self.ignoresInput) { 49 | // Check every item in the clipboard 50 | for (id item in input) { 51 | 52 | if ([item isKindOfClass:[NSAttributedString class]]) { 53 | // It's a rich text item 54 | NSLog(@"Got NSAttributedString: %@", item); 55 | NSAttributedString *text = item; 56 | [richTexts addObject:text]; 57 | 58 | } else if ([item isKindOfClass:[NSString class]]) { 59 | // It's a plain text item 60 | NSString *str = item; 61 | NSLog(@"Got NSString: %@", str); 62 | [plainTexts addObject:str]; 63 | NSString *firstChar = [str substringToIndex:1]; 64 | if ([firstChar isEqualToString:@"/"]) { 65 | // could be a file path - check if it's valid and if the path actually exists 66 | @try { 67 | NSURL *url = [NSURL fileURLWithPath:str]; 68 | if (url && url.isFileURL && [url checkResourceIsReachableAndReturnError:NULL]) { 69 | [fileURLs addObject:url]; 70 | } 71 | } 72 | @catch (NSException *exception) { 73 | // ignore 74 | } 75 | } 76 | 77 | } else { 78 | // I'd expect fo find other types as well, such as URLs, but nothing else came so far. 79 | NSLog(@"Unsupported input type: %s", object_getClassName(item)); 80 | 81 | } 82 | } 83 | } 84 | 85 | // 86 | // Now, depending on the collected data and the "Input Type" settings, prepare the Pasteboard contents for the Service 87 | // 88 | if (inputType == InputTypeNone || self.ignoresInput) { 89 | // Send empty pasteboard to Service 90 | 91 | } else if ((inputType == InputTypeFiles) || ((inputType == InputTypeAuto) && richTexts.count == 0 && fileURLs.count > 0 && fileURLs.count == plainTexts.count)) { 92 | // Handle these automatically as file refs if all the input items are plain text and contain a valid path. 93 | // Write the URL(s) to the pasteboard 94 | //NSLog(@"Adding files: %@", fileURLs); 95 | if (![pboard writeObjects:fileURLs]) { 96 | NSLog(@"Error: Could not set fileURLs"); 97 | } 98 | // Write a plain text with all paths (separated by EOLn) to the pasteboard. 99 | // Note: This is a little different from the Finder, who doesn't include full paths but only the file names. Not sure if this matters, though. 100 | NSString *txt = [plainTexts componentsJoinedByString:@"\n"]; 101 | //NSLog(@"Adding texts: %@", txt); 102 | [pboard setString:txt forType:NSStringPboardType]; 103 | 104 | } else if (richTexts.count > 0) { 105 | // Send rich text to Service 106 | //NSLog(@"Adding rich texts: %@", richTexts); 107 | if (![pboard writeObjects:richTexts]) { 108 | NSLog(@"Error: Could not set richTexts"); 109 | } 110 | } else if (plainTexts.count > 0) { 111 | // Send plain text to Service 112 | //NSLog(@"Adding texts: %@", plainTexts); 113 | if (![pboard writeObjects:plainTexts]) { 114 | NSLog(@"Error: Could not set plainTexts"); 115 | } 116 | } 117 | 118 | // Remember the change count to see if the Pasteboard contents gets changed by the Service 119 | NSInteger pbChangeCount = pboard.changeCount; 120 | 121 | // 122 | // Invoke the Service now 123 | // 124 | BOOL done = NSPerformService (serviceName, pboard); 125 | if (!done) { 126 | // Service didn't get invoked. Let's report this back 127 | *errorOut = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileNoSuchFileError userInfo: 128 | @{NSLocalizedDescriptionKey: 129 | [NSString stringWithFormat:@"The Service '%@' could not be invoked. Check its name and input type.", serviceName] 130 | }]; 131 | return NULL; 132 | } 133 | 134 | if (pbChangeCount == pboard.changeCount) { 135 | // Service did not produce a result 136 | NSLog(@"No returns from Service"); 137 | return NULL; 138 | } 139 | 140 | // 141 | // Parse pasteboard contents returned by the service. 142 | // 143 | 144 | if (pboard.pasteboardItems.count == 0) { 145 | return @""; 146 | } 147 | 148 | // Try to extract file URLs 149 | if (outputType == OutputTypeAuto || outputType == OutputTypeFiles) { 150 | NSArray *urls = [pboard readObjectsForClasses:@[[NSURL class]] options:@{NSPasteboardURLReadingFileURLsOnlyKey:@1}]; 151 | if (urls) { 152 | if ((outputType == OutputTypeFiles && urls.count > 0) || urls.count == pboard.pasteboardItems.count) { 153 | // We need to convert the URLs to paths 154 | NSMutableArray *filePaths = [NSMutableArray array]; 155 | for (NSURL *url in urls) { 156 | [filePaths addObject:[url path]]; 157 | } 158 | NSLog(@"Result URLs as paths: %@", filePaths); 159 | return urls; 160 | } 161 | } 162 | } 163 | 164 | // Otherwise we expect to find text in the Service's result. 165 | NSArray *result = [pboard readObjectsForClasses:@[[NSAttributedString class], [NSString class]] options:nil]; 166 | 167 | if (outputType == OutputTypeFiles) { 168 | // Check the results for file paths 169 | NSMutableArray *filePaths = [NSMutableArray array]; 170 | @try { 171 | // if we have multiple items, each of them should be a path. Otherwise it might be LF-delimited lines of paths. 172 | NSArray *candidates; 173 | if (result.count > 1) { 174 | candidates = result; 175 | } else { 176 | id singleText = result[0]; 177 | if ([singleText isKindOfClass:[NSAttributedString class]]) { 178 | // extract plain text 179 | singleText = [(NSAttributedString*)singleText string]; 180 | } 181 | candidates = [singleText componentsSeparatedByString:@"\n"]; 182 | } 183 | for (id item in candidates) { 184 | if (![item isKindOfClass:[NSString class]]) { 185 | break; // any non-path item stops the search 186 | } 187 | NSURL *url = [NSURL fileURLWithPath:item]; 188 | if (url && url.isFileURL && [url checkResourceIsReachableAndReturnError:NULL]) { 189 | [filePaths addObject:item]; 190 | } else { 191 | break; // any non-path item stops the search 192 | } 193 | } 194 | if (filePaths.count == candidates.count) { 195 | // all the items are file paths 196 | NSLog(@"Result paths: %@", filePaths); 197 | return filePaths; 198 | } 199 | } 200 | @catch (NSException *exception) { 201 | // any non-path item stops the search for file paths 202 | } 203 | } 204 | 205 | NSLog(@"Result text: %@", result); 206 | return result; 207 | } 208 | 209 | @end 210 | -------------------------------------------------------------------------------- /Services Automator Actions/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* AMApplication localized strings */ 2 | 3 | AMName = "Run Service"; 4 | 5 | /* AMDefaultParameters localized values */ 6 | /* e.g. myDefaultIntegerParameter = 0; */ 7 | /* e.g. myDefaultStringParameter = "Default String Value"; */ 8 | 9 | /* AMDescription localized strings */ 10 | AMDInput = ""; 11 | AMDNote = ""; 12 | AMDOptions = ""; 13 | AMDResult = ""; 14 | AMDSummary = "Invokes a Service (Services operate on selected text and files and are shown in the application's menu bar and in right-click menus)"; 15 | AMDWebsite = "http://apps.tempel.org/RunServiceActionForAutomator"; 16 | -------------------------------------------------------------------------------- /Services Automator Actions/en.lproj/main.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | serviceName 115 | inputType 116 | outputType 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | --------------------------------------------------------------------------------