├── .gitignore ├── LICENSE ├── README.md ├── Screenshots ├── ConnectionPreferences.png ├── GeneralPreferences.png └── Overview.png ├── Shortcuts for Pi-hole.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── lukas.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist └── Shortcuts for Pi-hole ├── About ├── AboutViewController.swift └── AboutWindowController.swift ├── AppDelegate.swift ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json └── StatusBarButtonImage.imageset │ ├── Contents.json │ ├── pi-hole_logo_x18.png │ ├── pi-hole_logo_x32.png │ └── pi-hole_logo_x64.png ├── Credits.rtf ├── Info.plist ├── Main ├── Main.storyboard ├── MainMenu.xib └── MainMenuController.swift ├── Preferences ├── ColoredStatusView.swift ├── ConnectionPreferencesViewController.swift ├── GeneralPreferencesViewController.swift ├── Preferences.swift └── PreferencesWindowController.swift ├── Shortcuts for Pi-hole.entitlements └── Tools ├── PiHoleAction.swift ├── PiHoleConnectionResult.swift └── PiHoleProxy.swift /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xcode,macos,cocoapods 3 | 4 | ### Pi-hole docker data ### 5 | dnsmasq.d/ 6 | pihole/ 7 | 8 | ### CocoaPods ### 9 | ## CocoaPods GitIgnore Template 10 | 11 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing 12 | # - Also handy if you have a large number of dependant pods 13 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE 14 | Pods/ 15 | 16 | ### macOS ### 17 | # General 18 | .DS_Store 19 | .AppleDouble 20 | .LSOverride 21 | 22 | # Icon must end with two \r 23 | Icon 24 | 25 | # Thumbnails 26 | ._* 27 | 28 | # Files that might appear in the root of a volume 29 | .DocumentRevisions-V100 30 | .fseventsd 31 | .Spotlight-V100 32 | .TemporaryItems 33 | .Trashes 34 | .VolumeIcon.icns 35 | .com.apple.timemachine.donotpresent 36 | 37 | # Directories potentially created on remote AFP share 38 | .AppleDB 39 | .AppleDesktop 40 | Network Trash Folder 41 | Temporary Items 42 | .apdisk 43 | 44 | ### Xcode ### 45 | # Xcode 46 | # 47 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 48 | 49 | ## User settings 50 | xcuserdata/ 51 | 52 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 53 | *.xcscmblueprint 54 | *.xccheckout 55 | 56 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 57 | build/ 58 | DerivedData/ 59 | *.moved-aside 60 | *.pbxuser 61 | !default.pbxuser 62 | *.mode1v3 63 | !default.mode1v3 64 | *.mode2v3 65 | !default.mode2v3 66 | *.perspectivev3 67 | !default.perspectivev3 68 | 69 | ### Xcode Patch ### 70 | *.xcodeproj/* 71 | !*.xcodeproj/project.pbxproj 72 | !*.xcodeproj/xcshareddata/ 73 | !*.xcworkspace/contents.xcworkspacedata 74 | /*.gcno 75 | 76 | 77 | # End of https://www.gitignore.io/api/xcode,macos,cocoapods 78 | 79 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2018 Lukas Wolfsteiner 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **_DISCONTINUED_**, as i don't use Pi-hole anymore (and this application never matured from POC state) 2 | > 3 | > If you're looking for a gread Pi-hole Remote on macOS & iOS - check out [Pi-hole Remote](https://rocketscience-it.nl) 4 | 5 | # Shortcuts for Pi-hole 6 | 7 | Shortcuts for Pi-hole is a small menu bar application that lives in your status bar. It provides quick actions for managing and monitoring your Pi-hole® instance. 8 | 9 | 10 | 11 | General Preferences | Connection Preferences 12 | :-------------------------:|:-------------------------: 13 | | 14 | 15 | ## Things to complete 16 | 17 | - [ ] Application icon 18 | - [ ] Update mechanism (using the [Spark Framework](https://sparkle-project.org/)) 19 | - [ ] Implement Unit-/Testing 20 | - [ ] Create as cask 21 | 22 | ## Copyright acknowledges and credits 23 | 24 | Big thanks to the whole Pi-hole userspace for developing and maintaining the Pi-hole project. Visit [pi-hole.net](https://pi-hole.net/) for more information. This application uses the Pi-hole API and it's logo as menu bar icon. 25 | 26 | **Pi-hole® is a registered trademark of Pi-hole LLC** 27 | 28 | ## Source code license 29 | 30 | MIT License 31 | 32 | Copyright (c) 2018 Lukas Wolfsteiner 33 | 34 | Permission is hereby granted, free of charge, to any person obtaining a copy 35 | of this software and associated documentation files (the "Software"), to deal 36 | in the Software without restriction, including without limitation the rights 37 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | copies of the Software, and to permit persons to whom the Software is 39 | furnished to do so, subject to the following conditions: 40 | 41 | The above copyright notice and this permission notice shall be included in all 42 | copies or substantial portions of the Software. 43 | 44 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 50 | SOFTWARE. 51 | -------------------------------------------------------------------------------- /Screenshots/ConnectionPreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Screenshots/ConnectionPreferences.png -------------------------------------------------------------------------------- /Screenshots/GeneralPreferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Screenshots/GeneralPreferences.png -------------------------------------------------------------------------------- /Screenshots/Overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Screenshots/Overview.png -------------------------------------------------------------------------------- /Shortcuts for Pi-hole.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 800A2FFB217D3038002EC725 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 800A2FFA217D3038002EC725 /* README.md */; }; 11 | 8033056A2171429100132604 /* GeneralPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 803305692171429100132604 /* GeneralPreferencesViewController.swift */; }; 12 | 8033056C217152AE00132604 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8033056B217152AE00132604 /* Preferences.swift */; }; 13 | 8033056E2171649C00132604 /* ColoredStatusView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8033056D2171649C00132604 /* ColoredStatusView.swift */; }; 14 | 803305702171796B00132604 /* PiHoleProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8033056F2171796B00132604 /* PiHoleProxy.swift */; }; 15 | 8046492121725E3A0028E457 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8046492021725E3A0028E457 /* Main.storyboard */; }; 16 | 804C398E217C9A2600576BFD /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 804C398D217C9A2600576BFD /* Credits.rtf */; }; 17 | 804C3991217CD24700576BFD /* AboutWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804C3990217CD24700576BFD /* AboutWindowController.swift */; }; 18 | 804C3993217CD6D600576BFD /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804C3992217CD6D600576BFD /* AboutViewController.swift */; }; 19 | 804E3D5021713D1400BD1DA0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804E3D4F21713D1400BD1DA0 /* AppDelegate.swift */; }; 20 | 804E3D5221713D1500BD1DA0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 804E3D5121713D1500BD1DA0 /* Assets.xcassets */; }; 21 | 804E7FFE21977F5000A22D35 /* PiHoleConnectionResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804E7FFD21977F5000A22D35 /* PiHoleConnectionResult.swift */; }; 22 | 804E800021977F7E00A22D35 /* PiHoleAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804E7FFF21977F7E00A22D35 /* PiHoleAction.swift */; }; 23 | 804E800321977FBA00A22D35 /* Overview.png in Resources */ = {isa = PBXBuildFile; fileRef = 804E800221977FBA00A22D35 /* Overview.png */; }; 24 | 804E80062197810200A22D35 /* GeneralPreferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 804E80042197810200A22D35 /* GeneralPreferences.png */; }; 25 | 804E80072197810200A22D35 /* ConnectionPreferences.png in Resources */ = {isa = PBXBuildFile; fileRef = 804E80052197810200A22D35 /* ConnectionPreferences.png */; }; 26 | 805F21F4217E1BAF00F12C6E /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 805F21F3217E1BAF00F12C6E /* LICENSE */; }; 27 | 80CA0040217260DA00CC9FB6 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CA003F217260DA00CC9FB6 /* PreferencesWindowController.swift */; }; 28 | 80CA005321729EF800CC9FB6 /* ConnectionPreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CA005221729EF800CC9FB6 /* ConnectionPreferencesViewController.swift */; }; 29 | 80ECF9AD217BC3E500879DA2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 80ECF9AC217BC3E500879DA2 /* MainMenu.xib */; }; 30 | 80ECF9B0217BCE9400879DA2 /* MainMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80ECF9AF217BCE9400879DA2 /* MainMenuController.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 800A2FFA217D3038002EC725 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 35 | 803305692171429100132604 /* GeneralPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesViewController.swift; sourceTree = ""; }; 36 | 8033056B217152AE00132604 /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = ""; }; 37 | 8033056D2171649C00132604 /* ColoredStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColoredStatusView.swift; sourceTree = ""; }; 38 | 8033056F2171796B00132604 /* PiHoleProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PiHoleProxy.swift; path = "Shortcuts for Pi-hole/Tools/PiHoleProxy.swift"; sourceTree = SOURCE_ROOT; }; 39 | 803305712171882E00132604 /* Shortcuts for Pi-hole.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Shortcuts for Pi-hole.entitlements"; sourceTree = ""; }; 40 | 8046492021725E3A0028E457 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 41 | 804C398D217C9A2600576BFD /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = ""; }; 42 | 804C3990217CD24700576BFD /* AboutWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutWindowController.swift; sourceTree = ""; }; 43 | 804C3992217CD6D600576BFD /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; }; 44 | 804E3D4C21713D1400BD1DA0 /* Shortcuts for Pi-hole.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Shortcuts for Pi-hole.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 804E3D4F21713D1400BD1DA0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 46 | 804E3D5121713D1500BD1DA0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 804E3D5621713D1500BD1DA0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | 804E7FFD21977F5000A22D35 /* PiHoleConnectionResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiHoleConnectionResult.swift; sourceTree = ""; }; 49 | 804E7FFF21977F7E00A22D35 /* PiHoleAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiHoleAction.swift; sourceTree = ""; }; 50 | 804E800221977FBA00A22D35 /* Overview.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Overview.png; sourceTree = ""; }; 51 | 804E80042197810200A22D35 /* GeneralPreferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = GeneralPreferences.png; sourceTree = ""; }; 52 | 804E80052197810200A22D35 /* ConnectionPreferences.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = ConnectionPreferences.png; sourceTree = ""; }; 53 | 805F21F3217E1BAF00F12C6E /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 54 | 80CA003F217260DA00CC9FB6 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = ""; }; 55 | 80CA005221729EF800CC9FB6 /* ConnectionPreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionPreferencesViewController.swift; sourceTree = ""; }; 56 | 80ECF9AC217BC3E500879DA2 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainMenu.xib; sourceTree = ""; }; 57 | 80ECF9AF217BCE9400879DA2 /* MainMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenuController.swift; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 804E3D4921713D1400BD1DA0 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 8046491B2172597A0028E457 /* Preferences */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 8033056B217152AE00132604 /* Preferences.swift */, 75 | 8033056D2171649C00132604 /* ColoredStatusView.swift */, 76 | 80CA003F217260DA00CC9FB6 /* PreferencesWindowController.swift */, 77 | 803305692171429100132604 /* GeneralPreferencesViewController.swift */, 78 | 80CA005221729EF800CC9FB6 /* ConnectionPreferencesViewController.swift */, 79 | ); 80 | path = Preferences; 81 | sourceTree = ""; 82 | }; 83 | 804C398F217CC62700576BFD /* About */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 804C3990217CD24700576BFD /* AboutWindowController.swift */, 87 | 804C3992217CD6D600576BFD /* AboutViewController.swift */, 88 | ); 89 | path = About; 90 | sourceTree = ""; 91 | }; 92 | 804C3997217D251100576BFD /* Tools */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 8033056F2171796B00132604 /* PiHoleProxy.swift */, 96 | 804E7FFD21977F5000A22D35 /* PiHoleConnectionResult.swift */, 97 | 804E7FFF21977F7E00A22D35 /* PiHoleAction.swift */, 98 | ); 99 | path = Tools; 100 | sourceTree = ""; 101 | }; 102 | 804E3D4321713D1400BD1DA0 = { 103 | isa = PBXGroup; 104 | children = ( 105 | 804E800121977FBA00A22D35 /* Screenshots */, 106 | 805F21F3217E1BAF00F12C6E /* LICENSE */, 107 | 800A2FFA217D3038002EC725 /* README.md */, 108 | 804E3D4E21713D1400BD1DA0 /* Shortcuts for Pi-hole */, 109 | 804E3D4D21713D1400BD1DA0 /* Products */, 110 | ); 111 | sourceTree = ""; 112 | }; 113 | 804E3D4D21713D1400BD1DA0 /* Products */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 804E3D4C21713D1400BD1DA0 /* Shortcuts for Pi-hole.app */, 117 | ); 118 | name = Products; 119 | sourceTree = ""; 120 | }; 121 | 804E3D4E21713D1400BD1DA0 /* Shortcuts for Pi-hole */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 804C3997217D251100576BFD /* Tools */, 125 | 804C398F217CC62700576BFD /* About */, 126 | 8046491B2172597A0028E457 /* Preferences */, 127 | 80ECF9AE217BCD6700879DA2 /* Main */, 128 | 803305712171882E00132604 /* Shortcuts for Pi-hole.entitlements */, 129 | 804E3D4F21713D1400BD1DA0 /* AppDelegate.swift */, 130 | 804E3D5121713D1500BD1DA0 /* Assets.xcassets */, 131 | 804E3D5621713D1500BD1DA0 /* Info.plist */, 132 | 804C398D217C9A2600576BFD /* Credits.rtf */, 133 | ); 134 | path = "Shortcuts for Pi-hole"; 135 | sourceTree = ""; 136 | }; 137 | 804E800121977FBA00A22D35 /* Screenshots */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 804E80052197810200A22D35 /* ConnectionPreferences.png */, 141 | 804E80042197810200A22D35 /* GeneralPreferences.png */, 142 | 804E800221977FBA00A22D35 /* Overview.png */, 143 | ); 144 | path = Screenshots; 145 | sourceTree = ""; 146 | }; 147 | 80ECF9AE217BCD6700879DA2 /* Main */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 8046492021725E3A0028E457 /* Main.storyboard */, 151 | 80ECF9AC217BC3E500879DA2 /* MainMenu.xib */, 152 | 80ECF9AF217BCE9400879DA2 /* MainMenuController.swift */, 153 | ); 154 | path = Main; 155 | sourceTree = ""; 156 | }; 157 | /* End PBXGroup section */ 158 | 159 | /* Begin PBXNativeTarget section */ 160 | 804E3D4B21713D1400BD1DA0 /* Shortcuts for Pi-hole */ = { 161 | isa = PBXNativeTarget; 162 | buildConfigurationList = 804E3D5A21713D1500BD1DA0 /* Build configuration list for PBXNativeTarget "Shortcuts for Pi-hole" */; 163 | buildPhases = ( 164 | 804E3D4821713D1400BD1DA0 /* Sources */, 165 | 804E3D4921713D1400BD1DA0 /* Frameworks */, 166 | 804E3D4A21713D1400BD1DA0 /* Resources */, 167 | ); 168 | buildRules = ( 169 | ); 170 | dependencies = ( 171 | ); 172 | name = "Shortcuts for Pi-hole"; 173 | productName = "Shortcuts for Pi-hole"; 174 | productReference = 804E3D4C21713D1400BD1DA0 /* Shortcuts for Pi-hole.app */; 175 | productType = "com.apple.product-type.application"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | 804E3D4421713D1400BD1DA0 /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | LastSwiftUpdateCheck = 1000; 184 | LastUpgradeCheck = 1000; 185 | ORGANIZATIONNAME = "Lukas Wolfsteiner"; 186 | TargetAttributes = { 187 | 804E3D4B21713D1400BD1DA0 = { 188 | CreatedOnToolsVersion = 10.0; 189 | SystemCapabilities = { 190 | com.apple.Sandbox = { 191 | enabled = 1; 192 | }; 193 | }; 194 | }; 195 | }; 196 | }; 197 | buildConfigurationList = 804E3D4721713D1400BD1DA0 /* Build configuration list for PBXProject "Shortcuts for Pi-hole" */; 198 | compatibilityVersion = "Xcode 9.3"; 199 | developmentRegion = en; 200 | hasScannedForEncodings = 0; 201 | knownRegions = ( 202 | en, 203 | Base, 204 | ); 205 | mainGroup = 804E3D4321713D1400BD1DA0; 206 | productRefGroup = 804E3D4D21713D1400BD1DA0 /* Products */; 207 | projectDirPath = ""; 208 | projectRoot = ""; 209 | targets = ( 210 | 804E3D4B21713D1400BD1DA0 /* Shortcuts for Pi-hole */, 211 | ); 212 | }; 213 | /* End PBXProject section */ 214 | 215 | /* Begin PBXResourcesBuildPhase section */ 216 | 804E3D4A21713D1400BD1DA0 /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 805F21F4217E1BAF00F12C6E /* LICENSE in Resources */, 221 | 804E80062197810200A22D35 /* GeneralPreferences.png in Resources */, 222 | 804E800321977FBA00A22D35 /* Overview.png in Resources */, 223 | 80ECF9AD217BC3E500879DA2 /* MainMenu.xib in Resources */, 224 | 804E3D5221713D1500BD1DA0 /* Assets.xcassets in Resources */, 225 | 800A2FFB217D3038002EC725 /* README.md in Resources */, 226 | 8046492121725E3A0028E457 /* Main.storyboard in Resources */, 227 | 804C398E217C9A2600576BFD /* Credits.rtf in Resources */, 228 | 804E80072197810200A22D35 /* ConnectionPreferences.png in Resources */, 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXSourcesBuildPhase section */ 235 | 804E3D4821713D1400BD1DA0 /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 803305702171796B00132604 /* PiHoleProxy.swift in Sources */, 240 | 8033056C217152AE00132604 /* Preferences.swift in Sources */, 241 | 80ECF9B0217BCE9400879DA2 /* MainMenuController.swift in Sources */, 242 | 80CA0040217260DA00CC9FB6 /* PreferencesWindowController.swift in Sources */, 243 | 804E800021977F7E00A22D35 /* PiHoleAction.swift in Sources */, 244 | 8033056E2171649C00132604 /* ColoredStatusView.swift in Sources */, 245 | 804E7FFE21977F5000A22D35 /* PiHoleConnectionResult.swift in Sources */, 246 | 8033056A2171429100132604 /* GeneralPreferencesViewController.swift in Sources */, 247 | 804C3991217CD24700576BFD /* AboutWindowController.swift in Sources */, 248 | 80CA005321729EF800CC9FB6 /* ConnectionPreferencesViewController.swift in Sources */, 249 | 804C3993217CD6D600576BFD /* AboutViewController.swift in Sources */, 250 | 804E3D5021713D1400BD1DA0 /* AppDelegate.swift in Sources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | /* End PBXSourcesBuildPhase section */ 255 | 256 | /* Begin XCBuildConfiguration section */ 257 | 804E3D5821713D1500BD1DA0 /* Debug */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | ALWAYS_SEARCH_USER_PATHS = NO; 261 | CLANG_ANALYZER_NONNULL = YES; 262 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 263 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 264 | CLANG_CXX_LIBRARY = "libc++"; 265 | CLANG_ENABLE_MODULES = YES; 266 | CLANG_ENABLE_OBJC_ARC = YES; 267 | CLANG_ENABLE_OBJC_WEAK = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 284 | CLANG_WARN_STRICT_PROTOTYPES = YES; 285 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 286 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | CODE_SIGN_IDENTITY = "Mac Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = dwarf; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | ENABLE_TESTABILITY = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu11; 295 | GCC_DYNAMIC_NO_PIC = NO; 296 | GCC_NO_COMMON_BLOCKS = YES; 297 | GCC_OPTIMIZATION_LEVEL = 0; 298 | GCC_PREPROCESSOR_DEFINITIONS = ( 299 | "DEBUG=1", 300 | "$(inherited)", 301 | ); 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | MACOSX_DEPLOYMENT_TARGET = 10.14; 309 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 310 | MTL_FAST_MATH = YES; 311 | ONLY_ACTIVE_ARCH = YES; 312 | SDKROOT = macosx; 313 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 314 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 315 | }; 316 | name = Debug; 317 | }; 318 | 804E3D5921713D1500BD1DA0 /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_ANALYZER_NONNULL = YES; 323 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_ENABLE_OBJC_WEAK = YES; 329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_COMMA = YES; 332 | CLANG_WARN_CONSTANT_CONVERSION = YES; 333 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 335 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 336 | CLANG_WARN_EMPTY_BODY = YES; 337 | CLANG_WARN_ENUM_CONVERSION = YES; 338 | CLANG_WARN_INFINITE_RECURSION = YES; 339 | CLANG_WARN_INT_CONVERSION = YES; 340 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 348 | CLANG_WARN_UNREACHABLE_CODE = YES; 349 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 350 | CODE_SIGN_IDENTITY = "Mac Developer"; 351 | COPY_PHASE_STRIP = NO; 352 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 353 | ENABLE_NS_ASSERTIONS = NO; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | GCC_C_LANGUAGE_STANDARD = gnu11; 356 | GCC_NO_COMMON_BLOCKS = YES; 357 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 358 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 359 | GCC_WARN_UNDECLARED_SELECTOR = YES; 360 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 361 | GCC_WARN_UNUSED_FUNCTION = YES; 362 | GCC_WARN_UNUSED_VARIABLE = YES; 363 | MACOSX_DEPLOYMENT_TARGET = 10.14; 364 | MTL_ENABLE_DEBUG_INFO = NO; 365 | MTL_FAST_MATH = YES; 366 | SDKROOT = macosx; 367 | SWIFT_COMPILATION_MODE = wholemodule; 368 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 369 | }; 370 | name = Release; 371 | }; 372 | 804E3D5B21713D1500BD1DA0 /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | CODE_SIGN_ENTITLEMENTS = "Shortcuts for Pi-hole/Shortcuts for Pi-hole.entitlements"; 377 | CODE_SIGN_IDENTITY = "Mac Developer"; 378 | CODE_SIGN_STYLE = Automatic; 379 | COMBINE_HIDPI_IMAGES = YES; 380 | DEVELOPMENT_TEAM = 4EGEDRDDUV; 381 | INFOPLIST_FILE = "Shortcuts for Pi-hole/Info.plist"; 382 | LD_RUNPATH_SEARCH_PATHS = ( 383 | "$(inherited)", 384 | "@executable_path/../Frameworks", 385 | ); 386 | PRODUCT_BUNDLE_IDENTIFIER = "de.dotwee.Shortcuts-for-Pi-hole"; 387 | PRODUCT_NAME = "$(TARGET_NAME)"; 388 | PROVISIONING_PROFILE_SPECIFIER = ""; 389 | SWIFT_VERSION = 4.2; 390 | }; 391 | name = Debug; 392 | }; 393 | 804E3D5C21713D1500BD1DA0 /* Release */ = { 394 | isa = XCBuildConfiguration; 395 | buildSettings = { 396 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 397 | CODE_SIGN_ENTITLEMENTS = "Shortcuts for Pi-hole/Shortcuts for Pi-hole.entitlements"; 398 | CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application"; 399 | CODE_SIGN_STYLE = Automatic; 400 | COMBINE_HIDPI_IMAGES = YES; 401 | DEVELOPMENT_TEAM = 4EGEDRDDUV; 402 | INFOPLIST_FILE = "Shortcuts for Pi-hole/Info.plist"; 403 | LD_RUNPATH_SEARCH_PATHS = ( 404 | "$(inherited)", 405 | "@executable_path/../Frameworks", 406 | ); 407 | PRODUCT_BUNDLE_IDENTIFIER = "de.dotwee.Shortcuts-for-Pi-hole"; 408 | PRODUCT_NAME = "$(TARGET_NAME)"; 409 | PROVISIONING_PROFILE_SPECIFIER = ""; 410 | SWIFT_VERSION = 4.2; 411 | }; 412 | name = Release; 413 | }; 414 | /* End XCBuildConfiguration section */ 415 | 416 | /* Begin XCConfigurationList section */ 417 | 804E3D4721713D1400BD1DA0 /* Build configuration list for PBXProject "Shortcuts for Pi-hole" */ = { 418 | isa = XCConfigurationList; 419 | buildConfigurations = ( 420 | 804E3D5821713D1500BD1DA0 /* Debug */, 421 | 804E3D5921713D1500BD1DA0 /* Release */, 422 | ); 423 | defaultConfigurationIsVisible = 0; 424 | defaultConfigurationName = Release; 425 | }; 426 | 804E3D5A21713D1500BD1DA0 /* Build configuration list for PBXNativeTarget "Shortcuts for Pi-hole" */ = { 427 | isa = XCConfigurationList; 428 | buildConfigurations = ( 429 | 804E3D5B21713D1500BD1DA0 /* Debug */, 430 | 804E3D5C21713D1500BD1DA0 /* Release */, 431 | ); 432 | defaultConfigurationIsVisible = 0; 433 | defaultConfigurationName = Release; 434 | }; 435 | /* End XCConfigurationList section */ 436 | }; 437 | rootObject = 804E3D4421713D1400BD1DA0 /* Project object */; 438 | } 439 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole.xcodeproj/xcuserdata/lukas.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Shortcuts for Pi-hole.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | Shortcuts for Pi-hole.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/About/AboutViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutViewController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 21.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AboutViewController: NSViewController { 12 | 13 | @IBOutlet weak var versionTextField: NSTextField! 14 | 15 | @IBAction func viewSourceCodeActionHandler(_ sender: NSButton) { 16 | NSWorkspace.shared.open(URL(string: "https://github.com/dotWee/macOS-PiholeShortcuts")!) 17 | } 18 | 19 | @IBAction func visitPiHoleProjectActionHandler(_ sender: NSButton) { 20 | NSWorkspace.shared.open(URL(string: "https://pi-hole.net")!) 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | // Do view setup here. 27 | print("AboutViewController: viewDidLoad()") 28 | 29 | let versionValue = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String 30 | let bundleValue = Bundle.main.infoDictionary?["CFBundleVersion"] as? String 31 | versionTextField.stringValue = "Version: \(versionValue!) (Bundle No. #\(bundleValue!))"; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/About/AboutWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AboutWindowController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 21.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class AboutWindowController: NSWindowController { 12 | 13 | @IBOutlet weak var aboutWindow: NSWindow! 14 | 15 | override func showWindow(_ sender: Any?) { 16 | super.showWindow(sender) 17 | 18 | AppDelegate.bringToFront(window: self.window!) 19 | } 20 | 21 | override func windowDidLoad() { 22 | super.windowDidLoad() 23 | 24 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 12.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | func applicationWillTerminate(_ aNotification: Notification) { 19 | // Insert code here to tear down your application 20 | } 21 | 22 | public static func bringToFront(window: NSWindow) { 23 | window.center() 24 | window.makeKeyAndOrderFront(nil) 25 | NSApp.activate(ignoringOtherApps: true) 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/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 | } -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "pi-hole_logo_x18.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "pi-hole_logo_x32.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "pi-hole_logo_x64.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | }, 23 | "properties" : { 24 | "template-rendering-intent" : "template" 25 | } 26 | } -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x18.png -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x32.png -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotWee/macOS-PiholeShortcuts/d7eaeece7de8ec8ed67c2add89a5a0cf41552a9c/Shortcuts for Pi-hole/Assets.xcassets/StatusBarButtonImage.imageset/pi-hole_logo_x64.png -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf1\ansi\ansicpg1252\cocoartf1671 2 | {\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;} 3 | {\colortbl;\red255\green255\blue255;} 4 | {\*\expandedcolortbl;;} 5 | {\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{upper-roman\}.}{\leveltext\leveltemplateid1\'02\'00.;}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid1} 6 | {\list\listtemplateid2\listhybrid{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{upper-roman\}.}{\leveltext\leveltemplateid101\'02\'00.;}{\levelnumbers\'01;}\fi-360\li720\lin720 }{\listname ;}\listid2}} 7 | {\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}} 8 | \paperw11900\paperh16840\margl1440\margr1440\vieww15920\viewh14840\viewkind0 9 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 10 | 11 | \f0\b\fs28 \cf0 References and links\ 12 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 13 | 14 | \f1\b0\fs20 \cf0 \ 15 | \pard\tx220\tx720\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\li720\fi-720\sl288\slmult1\pardirnatural\partightenfactor0 16 | \ls1\ilvl0{\field{\*\fldinst{HYPERLINK "https://pi-hole.net/"}}{\fldrslt 17 | \fs24 \cf0 The Pi-hole project}} 18 | \fs24 \ 19 | \ls1\ilvl0{\field{\*\fldinst{HYPERLINK "https://github.com/dotWee/macOS-PiholeShortcuts"}}{\fldrslt Source code}} 20 | \fs28 \ 21 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 22 | \cf0 \ 23 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0 24 | 25 | \f0\b \cf0 Copyright acknowledgments\ 26 | 27 | \fs20 \ 28 | \pard\tx220\tx720\tx796\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\li720\fi-720\sl288\slmult1\pardirnatural\partightenfactor0 29 | \ls2\ilvl0 30 | \f1\b0\fs24 \cf0 Pi-hole\'ae is a registered trademark of Pi-hole LLC} -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | $(PRODUCT_BUNDLE_IDENTIFIER) 7 | CFBundleInfoDictionaryVersion 8 | 6.0 9 | LSApplicationCategoryType 10 | public.app-category.utilities 11 | CFBundleVersion 12 | 1 13 | CFBundleShortVersionString 14 | 1.0.0 15 | CFBundleExecutable 16 | $(EXECUTABLE_NAME) 17 | NSAppTransportSecurity 18 | 19 | NSAllowsArbitraryLoads 20 | 21 | 22 | CFBundleDisplayName 23 | Shortcuts for Pi-hole 24 | NSPrincipalClass 25 | NSApplication 26 | CFBundlePackageType 27 | APPL 28 | CFBundleIconFile 29 | 30 | LSUIElement 31 | 32 | NSMainNibFile 33 | MainMenu 34 | LSMinimumSystemVersion 35 | $(MACOSX_DEPLOYMENT_TARGET) 36 | CFBundleDevelopmentRegion 37 | $(DEVELOPMENT_LANGUAGE) 38 | NSHumanReadableCopyright 39 | Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 40 | CFBundleName 41 | $(PRODUCT_NAME) 42 | 43 | 44 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Main/Main.storyboard: -------------------------------------------------------------------------------- 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 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | NSAllRomanInputSourcesLocaleIdentifier 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | Shortcuts for Pi-hole is a small menu bar application that lives in your status bar. 578 | 579 | It provides quick actions for managing and monitoring your Pi-hole® instance. 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 608 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Main/MainMenu.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 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Main/MainMenuController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatusBarMenuController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 20.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainMenuController: NSObject, NSMenuDelegate { 12 | 13 | @IBOutlet weak var statusBarMenu: NSMenu! 14 | let statusBarItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) 15 | 16 | @IBOutlet weak var statusMenuItem: NSMenuItem! 17 | @IBOutlet weak var aboutMenuItem: NSMenuItem! 18 | @IBOutlet weak var preferencesMenuItem: NSMenuItem! 19 | @IBOutlet weak var quitMenuItem: NSMenuItem! 20 | 21 | let mainWindowController = NSStoryboard(name: "Main", bundle: nil).instantiateInitialController() as! PreferencesWindowController 22 | let aboutWindowController = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "AboutWindowControllerIdentifier") as! AboutWindowController 23 | 24 | override init() { 25 | super.init() 26 | } 27 | 28 | override func awakeFromNib() { 29 | if let statusBarButton = statusBarItem.button { 30 | statusBarButton.image = NSImage(named: "StatusBarButtonImage") 31 | } 32 | 33 | statusBarItem.menu = statusBarMenu 34 | statusBarMenu.delegate = self 35 | 36 | // refresh status on creation 37 | performPiHoleAction(action: PiHoleAction.Status) 38 | } 39 | 40 | @IBAction func refreshMenuItemActionHandler(_ sender: NSMenuItem) { 41 | performPiHoleAction(action: PiHoleAction.Status) 42 | } 43 | 44 | @IBAction func enableFor30sMenuItemActionHandler(_ sender: NSMenuItem) { 45 | performPiHoleAction(action: PiHoleAction.Enable, seconds: 30) 46 | } 47 | 48 | @IBAction func enableFor1mMenuItemActionHandler(_ sender: NSMenuItem) { 49 | performPiHoleAction(action: PiHoleAction.Enable, seconds: 60) 50 | } 51 | 52 | @IBAction func enableFor1hMenuItemActionHandler(_ sender: NSMenuItem) { 53 | performPiHoleAction(action: PiHoleAction.Enable, seconds: 3600) 54 | } 55 | 56 | @IBAction func enablePermanentlyMenuItemActionHandler(_ sender: NSMenuItem) { 57 | performPiHoleAction(action: PiHoleAction.Enable) 58 | } 59 | 60 | @IBAction func disableFor30sMenuItemActionHandler(_ sender: NSMenuItem) { 61 | performPiHoleAction(action: PiHoleAction.Disable, seconds: 30) 62 | } 63 | 64 | @IBAction func disableFor1mMenuItemActionHandler(_ sender: NSMenuItem) { 65 | performPiHoleAction(action: PiHoleAction.Disable, seconds: 60) 66 | } 67 | 68 | @IBAction func disableFor1hMenuItemActionHandler(_ sender: NSMenuItem) { 69 | performPiHoleAction(action: PiHoleAction.Disable, seconds: 3600) 70 | } 71 | 72 | @IBAction func disablePermanentlyMenuItemActionHandler(_ sender: NSMenuItem) { 73 | performPiHoleAction(action: PiHoleAction.Disable) 74 | } 75 | 76 | func performPiHoleAction(action: PiHoleAction, seconds: Int = 0) { 77 | if PiHoleProxy.getConfigStatus().isPositive() { 78 | self.dispatchStatusMenuItemUpdate(withTitle: "Pi-hole Status: Requesting...") 79 | 80 | PiHoleProxy.performActionRequest(action, seconds: seconds, onSuccess: { (status) in 81 | self.dispatchStatusMenuItemUpdate(withTitle: "Pi-hole Status: " + status) 82 | }) { (error) in self.dispatchStatusMenuItemUpdate(withTitle: "Error: No connection.") } 83 | } else { 84 | self.dispatchStatusMenuItemUpdate(withTitle: "Error: Configuration invalid.") 85 | } 86 | } 87 | 88 | func dispatchStatusMenuItemUpdate(withTitle: String) { 89 | print("dispatchStatusMenuItemUpdate with title " + withTitle) 90 | DispatchQueue.main.async { 91 | self.statusMenuItem.title = withTitle 92 | } 93 | } 94 | 95 | @IBAction func aboutMenuItemActionHandler(_ sender: NSMenuItem) { 96 | aboutWindowController.showWindow(self) 97 | } 98 | 99 | @IBAction func dashboardMenuItemActionHandler(_ sender: NSMenuItem) { 100 | let url = PiHoleProxy.getDashboardUrl() 101 | NSWorkspace.shared.open(url!) 102 | } 103 | 104 | @IBAction func preferencesMenuItemActionHandler(_ sender: NSMenuItem) { 105 | mainWindowController.showWindow(self) 106 | } 107 | 108 | @IBAction func quitMenuItemActionHandler(_ sender: NSMenuItem) { 109 | NSApplication.shared.terminate(self) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Preferences/ColoredStatusView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColoredStatusView.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 13.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ColoredStatusView: NSView { 12 | var fillingColor: NSColor = NSColor.red 13 | 14 | override func draw(_ dirtyRect: NSRect) { 15 | super.draw(dirtyRect) 16 | 17 | // Drawing code here. 18 | let fillColor = self.fillingColor 19 | let path = NSBezierPath(ovalIn: dirtyRect) 20 | fillColor.setFill() 21 | path.fill() 22 | } 23 | 24 | public func updateFillingColor(color: NSColor) { 25 | self.fillingColor = color 26 | self.display() 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Preferences/ConnectionPreferencesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainAboutViewController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 13.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ConnectionPreferencesViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do view setup here. 17 | _ = self.displayBuiltUrl() 18 | } 19 | 20 | @IBOutlet weak var coloredStatusViewOutlet: ColoredStatusView! 21 | 22 | @IBOutlet weak var connectionStatusTextField: NSTextField! 23 | 24 | @IBOutlet weak var urlTextField: NSTextField! 25 | 26 | @IBOutlet var connectionLogTextView: NSTextView! 27 | 28 | private func displayStatus(status: String, color: NSColor, log: String? = nil) { 29 | self.connectionStatusTextField.stringValue = status 30 | self.coloredStatusViewOutlet.updateFillingColor(color: color) 31 | 32 | if (log != nil) { 33 | self.connectionLogTextView.string = log! 34 | } 35 | } 36 | 37 | private func displayConfigurationErrorAlert() { 38 | let alert = NSAlert() 39 | alert.messageText = "Invalid configuration" 40 | alert.informativeText = "Please check your configuration." 41 | alert.addButton(withTitle: "Ok") 42 | 43 | if let mainWindow = self.view.window { 44 | alert.beginSheetModal(for: mainWindow) { (returnCode: NSApplication.ModalResponse) -> Void in 45 | print ("returnCode: ", returnCode) 46 | } 47 | } 48 | } 49 | 50 | private func displayBuiltUrl() -> Bool { 51 | let connectionStatus = PiHoleProxy.getConfigStatus() 52 | 53 | if connectionStatus.isPositive() { 54 | 55 | // display url 56 | let url = PiHoleProxy.getApiUrl(action: PiHoleAction.Enable) 57 | urlTextField.stringValue = url!.absoluteString 58 | 59 | self.displayStatus(status: "Press connect to verify", color: PiHoleConnectionResult.colorResultNeutral) 60 | return true; 61 | } else { 62 | self.displayStatus(status: connectionStatus.message, color: connectionStatus.color) 63 | 64 | // show invalid config alert 65 | self.displayConfigurationErrorAlert() 66 | return false; 67 | } 68 | } 69 | 70 | @IBAction func connectionButtonActionHandler(_ sender: Any) { 71 | 72 | if self.displayBuiltUrl() { 73 | displayStatus(status: "Requesting...", color: PiHoleConnectionResult.colorResultNeutral) 74 | 75 | PiHoleProxy.performActionRequest(PiHoleAction.Status, onSuccess: { (status) in 76 | DispatchQueue.main.async { 77 | self.displayStatus(status: "Connection established", color: PiHoleConnectionResult.colorResultPositive, log: "Pi-hole status: \(status)") 78 | } 79 | }) { (error) in 80 | DispatchQueue.main.async { 81 | self.displayStatus(status: "An error occurred", color: PiHoleConnectionResult.colorResultNegative, log: error.domain) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Preferences/GeneralPreferencesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GeneralPreferenceViewController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 12.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | final class GeneralPreferencesViewController: NSViewController { 12 | @IBOutlet weak var secureTextFieldApiKey: NSSecureTextField! 13 | @IBOutlet weak var popUpButtonRequestProtocol: NSPopUpButton! 14 | 15 | @IBOutlet weak var textFieldHostPort: NSTextField! 16 | @IBOutlet weak var textFieldHostAddress: NSTextField! 17 | 18 | @IBOutlet weak var viewCircleConnectionStatus: ColoredStatusView! 19 | 20 | @IBOutlet weak var labelConnectionStatus: NSTextField! 21 | @IBOutlet weak var textFieldTimeout: NSTextField! 22 | 23 | @IBAction func hostAddressActionHandler(_ sender: NSTextField) { 24 | let hostAddress = sender.stringValue 25 | print("hostAddressActionHandler", hostAddress) 26 | Preferences.saveHostAddress(hostAddress: hostAddress) 27 | 28 | self.onPreferencesChange() 29 | } 30 | 31 | @IBAction func hostPortActionHandler(_ sender: NSTextField) { 32 | let hostPort = sender.integerValue 33 | print("hostPortActionHandler", hostPort) 34 | Preferences.saveHostPort(hostPort: hostPort) 35 | 36 | self.onPreferencesChange() 37 | } 38 | 39 | @IBAction func requestProtocolActionHandler(_ sender: NSPopUpButton) { 40 | let requestProtocol = sender.titleOfSelectedItem! 41 | print("requestProtocolActionHandler", requestProtocol) 42 | Preferences.saveRequestProtocol(requestProtocol: requestProtocol) 43 | 44 | self.onPreferencesChange() 45 | } 46 | @IBAction func apiKeyActionHandler(_ sender: NSSecureTextField) { 47 | let apiKey = sender.stringValue 48 | print("apiKeyActionHandler", apiKey) 49 | Preferences.saveApiKey(apiKey: apiKey) 50 | 51 | self.onPreferencesChange() 52 | } 53 | @IBAction func timeoutActionHandler(_ sender: NSTextField) { 54 | let timeout = sender.integerValue 55 | print("timeoutActionHandler", timeout) 56 | Preferences.saveTimeout(timeout: timeout) 57 | 58 | self.onPreferencesChange() 59 | } 60 | 61 | func onConnectionStatusChange(status: PiHoleConnectionResult) { 62 | self.viewCircleConnectionStatus.updateFillingColor(color: status.color) 63 | self.labelConnectionStatus.stringValue = status.message 64 | } 65 | 66 | func onPreferencesChange() { 67 | let connectionStatus = PiHoleProxy.getConfigStatus() 68 | self.onConnectionStatusChange(status: connectionStatus) 69 | } 70 | 71 | override func viewDidLoad() { 72 | super.viewDidLoad() 73 | 74 | // restore persisted preferences in view 75 | if let hostAddress = Preferences.getHostAddress(), !hostAddress.isEmpty { 76 | textFieldHostAddress.stringValue = hostAddress 77 | } 78 | 79 | let hostPort = Preferences.getHostPort() 80 | textFieldHostPort.integerValue = hostPort 81 | 82 | let requestProtocol = Preferences.getRequestProtocol() 83 | popUpButtonRequestProtocol.selectItem(withTitle: requestProtocol) 84 | 85 | let apiKey = Preferences.getApiKey() 86 | if !apiKey.isEmpty { 87 | secureTextFieldApiKey.stringValue = apiKey 88 | } 89 | 90 | // check status 91 | self.onPreferencesChange() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Preferences/Preferences.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Preferences.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 13.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | struct Preferences { 12 | static let (hostAddressKey, hostPortKey, requestProtocolKey, apiKey, timeoutKey) = ("HOST_ADDRESS", "HOST_PORT", "REQUEST_PROTOCOL", "API_KEY", "REQUEST_TIMEOUT") 13 | static let userSessionKey = Bundle.main.bundleIdentifier! 14 | 15 | struct Model { 16 | var hostAddress: String? 17 | var hostPort: String? 18 | var requestProtocol: String? 19 | var apiKey: String? 20 | var timeout: String? 21 | 22 | init(_ json: [String: String]) { 23 | self.hostAddress = json[Preferences.hostAddressKey] 24 | self.hostPort = json[Preferences.hostPortKey] 25 | self.requestProtocol = json[Preferences.requestProtocolKey] 26 | self.apiKey = json[Preferences.apiKey] 27 | self.timeout = json[Preferences.timeoutKey] 28 | } 29 | } 30 | 31 | static func isHostAddressValid() -> Bool { 32 | if let hostAddress = UserDefaults.standard.string(forKey: Preferences.hostAddressKey), !hostAddress.isEmpty { 33 | // it's not nil nor an empty string 34 | return true 35 | } else { 36 | return false 37 | } 38 | } 39 | 40 | static func isRequestProtocolValid() -> Bool { 41 | let requestProtocol = getRequestProtocol() 42 | return requestProtocol == "http" || requestProtocol == "https" 43 | } 44 | 45 | static func isApiKeyValid() -> Bool { 46 | if let apiKey = UserDefaults.standard.string(forKey: Preferences.apiKey), !apiKey.isEmpty { 47 | // it's not nil nor an empty string 48 | return true 49 | } else { 50 | return false 51 | } 52 | } 53 | 54 | static func isHostPortValid() -> Bool { 55 | return getHostPort() > 0 56 | } 57 | 58 | static func isTimeoutValid() -> Bool { 59 | return getTimeout() > 0 60 | } 61 | 62 | static func getHostAddress() -> String? { 63 | return UserDefaults.standard.string(forKey: Preferences.hostAddressKey) 64 | } 65 | 66 | static func getHostPort() -> Int { 67 | let hostPort = UserDefaults.standard.integer(forKey: Preferences.hostPortKey) 68 | return (hostPort == 0) ? 80 : hostPort 69 | } 70 | 71 | static func getRequestProtocol() -> String { 72 | if let requestProtocol = UserDefaults.standard.string(forKey: Preferences.requestProtocolKey), !requestProtocol.isEmpty { 73 | // it's not nil nor an empty string 74 | return requestProtocol 75 | } 76 | 77 | return "http" 78 | } 79 | 80 | static func getApiKey() -> String { 81 | return UserDefaults.standard.string(forKey: Preferences.apiKey) ?? "" 82 | } 83 | 84 | static func getTimeout() -> Int { 85 | return UserDefaults.standard.integer(forKey: Preferences.timeoutKey) 86 | } 87 | 88 | static func saveHostAddress(hostAddress: String) { 89 | UserDefaults.standard.set(hostAddress, forKey: Preferences.hostAddressKey) 90 | } 91 | 92 | static func saveHostPort(hostPort: Int) { 93 | UserDefaults.standard.set(hostPort, forKey: Preferences.hostPortKey) 94 | } 95 | 96 | static func saveRequestProtocol(requestProtocol: String) { 97 | UserDefaults.standard.set(requestProtocol, forKey: Preferences.requestProtocolKey) 98 | } 99 | 100 | static func saveApiKey(apiKey: String) { 101 | UserDefaults.standard.set(apiKey, forKey: Preferences.apiKey) 102 | } 103 | 104 | static func saveTimeout(timeout: Int) { 105 | UserDefaults.standard.set(timeout, forKey: Preferences.timeoutKey) 106 | } 107 | 108 | static var savePreferences = { (hostAddress: String, hostPort: String, requestProtocol: String, apiKey: String, timeout: Int) in UserDefaults.standard.set([Preferences.hostAddressKey: hostAddress, Preferences.hostPortKey: hostPort, Preferences.requestProtocolKey: requestProtocol, Preferences.apiKey: apiKey, Preferences.timeoutKey: timeout], forKey: Preferences.userSessionKey) 109 | } 110 | 111 | static var getPreferences = { _ -> Model in 112 | return Model((UserDefaults.standard.value(forKey: Preferences.userSessionKey) as? [String: String]) ?? [:]) 113 | }(()) 114 | 115 | static func clearPreferences() { 116 | UserDefaults.standard.removeObject(forKey: Preferences.userSessionKey) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Preferences/PreferencesWindowController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainWindowController.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 13.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PreferencesWindowController: NSWindowController { 12 | 13 | override func showWindow(_ sender: Any?) { 14 | super.showWindow(sender) 15 | 16 | AppDelegate.bringToFront(window: self.window!) 17 | } 18 | 19 | override func windowDidLoad() { 20 | super.windowDidLoad() 21 | 22 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Shortcuts for Pi-hole.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Tools/PiHoleAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PiHoleAction.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 10.11.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | enum PiHoleAction { 12 | case Status 13 | case Enable 14 | case Disable 15 | } 16 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Tools/PiHoleConnectionResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PiHoleConnectionResult.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 10.11.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PiHoleConnectionResult: NSObject { 12 | 13 | let message: String 14 | let color: NSColor 15 | let error: NSError? 16 | 17 | init(message: String, color: NSColor, error: NSError? = nil) { 18 | self.message = message 19 | self.color = color 20 | self.error = error 21 | } 22 | 23 | public static func Positive(message: String) -> PiHoleConnectionResult { 24 | return PiHoleConnectionResult(message: message, color: colorResultPositive) 25 | } 26 | 27 | public static func Negative(message: String, error: NSError? = nil) -> PiHoleConnectionResult { 28 | return PiHoleConnectionResult(message: message, color: colorResultNegative, error: error) 29 | } 30 | 31 | public func isPositive() -> Bool { 32 | return self.color == PiHoleConnectionResult.colorResultPositive 33 | } 34 | 35 | static let lightRed = NSColor(red: 0.96, green: 0.05, blue: 0.10, alpha: 1.0) 36 | static let lightGreen = NSColor(red: 0.16, green: 0.99, blue: 0.18, alpha: 1.0) 37 | 38 | static let darkRed = NSColor(red: 0.59, green: 0.02, blue: 0.05, alpha: 1.0) 39 | static let darkGreen = NSColor(red: 0.13, green: 0.70, blue: 0.15, alpha: 1.0) 40 | 41 | static let colorResultPositive = lightGreen 42 | static let colorResultNegative = lightRed 43 | static let colorResultNeutral = NSColor.gray 44 | } 45 | -------------------------------------------------------------------------------- /Shortcuts for Pi-hole/Tools/PiHoleProxy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PiHoleProxy.swift 3 | // Shortcuts for Pi-hole 4 | // 5 | // Created by Lukas Wolfsteiner on 13.10.18. 6 | // Copyright © 2018 Lukas Wolfsteiner. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PiHoleProxy: NSObject { 12 | 13 | public static func getBaseUrlString() -> String { 14 | let hostAddress = Preferences.getHostAddress() 15 | let hostPort = Preferences.getHostPort() 16 | let requestProtocol = Preferences.getRequestProtocol() 17 | 18 | return requestProtocol + "://" + hostAddress! + ":" + String(hostPort) 19 | } 20 | 21 | public static func getDashboardUrl() -> URL? { 22 | return URL(string: getBaseUrlString() + "/admin") 23 | } 24 | 25 | public static func getApiUrl(action: PiHoleAction = PiHoleAction.Status, seconds: Int = 0) -> URL? { 26 | let apiKey = Preferences.getApiKey() 27 | 28 | let base = getBaseUrlString() 29 | let path: String = "/admin/api.php" 30 | 31 | var urlString: String = base + path 32 | 33 | if action == PiHoleAction.Enable || action == PiHoleAction.Disable { 34 | urlString += "?auth=" + apiKey 35 | urlString += (action == PiHoleAction.Enable ? "&enable" : "&disable") 36 | 37 | if seconds > 0 { 38 | urlString += "=" + String(seconds) 39 | } 40 | } else { 41 | // PiHoleAction.Status 42 | } 43 | 44 | return URL(string: urlString) 45 | } 46 | 47 | public static func getConfigStatus() -> PiHoleConnectionResult { 48 | // check if host address is valid 49 | if (!Preferences.isHostAddressValid()) { 50 | return PiHoleConnectionResult.Negative(message: "Invalid Host Address") 51 | } 52 | 53 | // check if host port is valid 54 | if (!Preferences.isHostPortValid()) { 55 | return PiHoleConnectionResult.Negative(message: "Invalid Host Port") 56 | } 57 | 58 | // check if request protocol is valid 59 | if (!Preferences.isRequestProtocolValid()) { 60 | return PiHoleConnectionResult.Negative(message: "Invalid Protocol") 61 | } 62 | 63 | // check if api key is valid 64 | if (!Preferences.isApiKeyValid()) { 65 | return PiHoleConnectionResult.Negative(message: "Invalid API Key") 66 | } 67 | 68 | // check if timeout is valid 69 | if (!Preferences.isTimeoutValid()) { 70 | return PiHoleConnectionResult.Negative(message: "Invalid Timeout Value") 71 | } 72 | 73 | // define url on possible host 74 | if (getApiUrl() == nil) { 75 | return PiHoleConnectionResult.Negative(message: "Invalid URL") 76 | } 77 | 78 | // config looks good! 79 | return PiHoleConnectionResult.Positive(message: "Configuration looks valid") 80 | } 81 | 82 | public static func getDefaultURLSession() -> URLSession { 83 | let config = URLSessionConfiguration.default 84 | let timeout = Preferences.getTimeout() 85 | 86 | // timeout 2s 87 | config.timeoutIntervalForRequest = TimeInterval(timeout) 88 | config.timeoutIntervalForResource = TimeInterval(timeout) 89 | 90 | return URLSession(configuration: config) 91 | } 92 | 93 | public static func performActionRequest(_ action: PiHoleAction, seconds: Int = 0, onSuccess success: @escaping (_ status: String) -> Void, onFailure failure: @escaping (_ error: NSError) -> Void) { 94 | 95 | do { 96 | guard let url = getApiUrl(action: action, seconds: seconds) else { 97 | failure(NSError(domain: "Error invalid endpoint", code: 1, userInfo: nil)) 98 | return 99 | } 100 | 101 | let urlRequest = URLRequest(url: url) 102 | let session = getDefaultURLSession() 103 | 104 | let task = session.dataTask(with: urlRequest) { 105 | (data, response, error) in 106 | 107 | // Check for any errors 108 | guard error == nil else { 109 | failure(NSError(domain: "Error at executing request (timeout)", code: 2)) 110 | return 111 | } 112 | 113 | // Make sure we got data 114 | guard let responseData = data else { 115 | failure(NSError(domain: "Error at reading response", code: 3, userInfo: nil)) 116 | return 117 | } 118 | 119 | // Parse the result as JSON, since that's what the API provides 120 | do { 121 | guard let responseObj = try JSONSerialization.jsonObject(with: responseData, options: []) 122 | as? [String: Any] else { 123 | failure(NSError(domain: "Error converting response to JSON", code: 4, userInfo: nil)) 124 | return 125 | } 126 | 127 | // The response object is a dictionary so we just access the title using the "status" key 128 | guard let status = responseObj["status"] as? String else { 129 | failure(NSError(domain: "Error getting status from response", code: 5, userInfo: nil)) 130 | return 131 | } 132 | 133 | success(status) 134 | } catch { 135 | failure(NSError(domain: "Error at converting response into JSON", code: 6, userInfo: nil)) 136 | return 137 | } 138 | } 139 | task.resume() 140 | } 141 | } 142 | } 143 | --------------------------------------------------------------------------------