diff --git a/assets.sketch b/assets.sketch new file mode 100644 index 0000000..12f1a5d Binary files /dev/null and b/assets.sketch differ diff --git a/phpmon.xcodeproj/project.pbxproj b/phpmon.xcodeproj/project.pbxproj index f79b9a6..75b2821 100644 --- a/phpmon.xcodeproj/project.pbxproj +++ b/phpmon.xcodeproj/project.pbxproj @@ -13,10 +13,10 @@ C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; }; C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4622B009A400E7CF16 /* Shell.swift */; }; C41C1B4922B00A9800E7CF16 /* ImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */; }; - C41C1B4B22B019FF00E7CF16 /* PHPVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */; }; + C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */; }; C41C1B4D22B0215A00E7CF16 /* Services.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4C22B0215A00E7CF16 /* Services.swift */; }; C476FF9822B0DD830098105B /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C476FF9722B0DD830098105B /* Alert.swift */; }; - C4D8016622B1584700C6DA1B /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Environment.swift */; }; + C4D8016622B1584700C6DA1B /* BootChecks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* BootChecks.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -29,10 +29,10 @@ C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = ""; }; C41C1B4622B009A400E7CF16 /* Shell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shell.swift; sourceTree = ""; }; C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGenerator.swift; sourceTree = ""; }; - C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHPVersion.swift; sourceTree = ""; }; + C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersion.swift; sourceTree = ""; }; C41C1B4C22B0215A00E7CF16 /* Services.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Services.swift; sourceTree = ""; }; C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = ""; }; - C4D8016522B1584700C6DA1B /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; }; + C4D8016522B1584700C6DA1B /* BootChecks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootChecks.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -65,13 +65,15 @@ C41C1B3522B0097F00E7CF16 /* phpmon */ = { isa = PBXGroup; children = ( + C41E181722CB61EB0072CF09 /* Classes */, + C41E181622CB61890072CF09 /* Startup */, + C41E181522CB614C0072CF09 /* Terminal */, + C41C1B4E22B024F100E7CF16 /* Helpers */, + C41E181822CB62200072CF09 /* View Controllers */, C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */, - C41C1B3822B0097F00E7CF16 /* ViewController.swift */, - C41C1B3A22B0098000E7CF16 /* Assets.xcassets */, - C41C1B3C22B0098000E7CF16 /* Main.storyboard */, C41C1B3F22B0098000E7CF16 /* Info.plist */, C41C1B4022B0098000E7CF16 /* phpmon.entitlements */, - C41C1B4E22B024F100E7CF16 /* Helpers */, + C41C1B3A22B0098000E7CF16 /* Assets.xcassets */, ); path = phpmon; sourceTree = ""; @@ -79,16 +81,46 @@ C41C1B4E22B024F100E7CF16 /* Helpers */ = { isa = PBXGroup; children = ( - C41C1B4622B009A400E7CF16 /* Shell.swift */, C41C1B4822B00A9800E7CF16 /* ImageGenerator.swift */, - C41C1B4A22B019FF00E7CF16 /* PHPVersion.swift */, - C41C1B4C22B0215A00E7CF16 /* Services.swift */, C476FF9722B0DD830098105B /* Alert.swift */, - C4D8016522B1584700C6DA1B /* Environment.swift */, ); path = Helpers; sourceTree = ""; }; + C41E181522CB614C0072CF09 /* Terminal */ = { + isa = PBXGroup; + children = ( + C41C1B4622B009A400E7CF16 /* Shell.swift */, + C41C1B4C22B0215A00E7CF16 /* Services.swift */, + ); + path = Terminal; + sourceTree = ""; + }; + C41E181622CB61890072CF09 /* Startup */ = { + isa = PBXGroup; + children = ( + C4D8016522B1584700C6DA1B /* BootChecks.swift */, + ); + path = Startup; + sourceTree = ""; + }; + C41E181722CB61EB0072CF09 /* Classes */ = { + isa = PBXGroup; + children = ( + C41C1B4A22B019FF00E7CF16 /* PhpVersion.swift */, + ); + path = Classes; + sourceTree = ""; + }; + C41E181822CB62200072CF09 /* View Controllers */ = { + isa = PBXGroup; + children = ( + C41C1B3C22B0098000E7CF16 /* Main.storyboard */, + C41C1B3822B0097F00E7CF16 /* ViewController.swift */, + ); + path = "View Controllers"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -159,13 +191,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C4D8016622B1584700C6DA1B /* Environment.swift in Sources */, + C4D8016622B1584700C6DA1B /* BootChecks.swift in Sources */, C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */, C41C1B4D22B0215A00E7CF16 /* Services.swift in Sources */, C41C1B4922B00A9800E7CF16 /* ImageGenerator.swift in Sources */, C41C1B3922B0097F00E7CF16 /* ViewController.swift in Sources */, C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */, - C41C1B4B22B019FF00E7CF16 /* PHPVersion.swift in Sources */, + C41C1B4B22B019FF00E7CF16 /* PhpVersion.swift in Sources */, C476FF9822B0DD830098105B /* Alert.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/phpmon/AppDelegate.swift b/phpmon/AppDelegate.swift index 614610c..7626071 100644 --- a/phpmon/AppDelegate.swift +++ b/phpmon/AppDelegate.swift @@ -16,7 +16,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { ) var timer: Timer? - var version: PHPVersion? = nil + var version: PhpVersion? = nil var availablePhpVersions : [String] = [] var busy: Bool = false @@ -25,7 +25,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) // Perform environment boot checks DispatchQueue.global(qos: .userInitiated).async { [unowned self] in - Environment.performBootChecks() + BootChecks.perform() self.availablePhpVersions = Services.detectPhpVersions() print("The following PHP versions were detected:") print(self.availablePhpVersions) @@ -57,7 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } @objc func updatePhpVersionInStatusBar() { - self.version = PHPVersion() + self.version = PhpVersion() if (self.busy) { DispatchQueue.main.async { self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) @@ -78,6 +78,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { string = "You are running PHP \(self.version!.long)" } menu.addItem(NSMenuItem(title: string, action: nil, keyEquivalent: "")) + if (self.version != nil) { + // Actions + menu.addItem(NSMenuItem.separator()) + menu.addItem(NSMenuItem(title: "Open php.ini in Finder", action: #selector(self.openActiveConfigFolder), keyEquivalent: "")) + // menu.addItem(NSMenuItem(title: "Restart PHP \(self.version!.short) service", action: #selector(self.restartPhp), keyEquivalent: "")) + } menu.addItem(NSMenuItem.separator()) if (self.availablePhpVersions.count > 0 && !self.busy) { var shortcutKey = 1 @@ -108,6 +114,16 @@ class AppDelegate: NSObject, NSApplicationDelegate { NSApplication.shared.orderFrontStandardAboutPanel() } + @objc public func openActiveConfigFolder() + { + Services.openPhpConfigFolder(version: self.version!.short) + } + + @objc public func restartPhp() + { + Services.restartPhp(version: self.version!.short) + } + @objc public func switchToPhpVersion(sender: AnyObject) { self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!) let index = sender.tag! diff --git a/phpmon/Helpers/PHPVersion.swift b/phpmon/Classes/PhpVersion.swift similarity index 51% rename from phpmon/Helpers/PHPVersion.swift rename to phpmon/Classes/PhpVersion.swift index a8f2819..f2886a7 100644 --- a/phpmon/Helpers/PHPVersion.swift +++ b/phpmon/Classes/PhpVersion.swift @@ -8,21 +8,23 @@ import Foundation -class PHPVersion { +class PhpVersion { var short : String = "???" var long : String = "???" init() { - // Get the info about the PHP installation - let output = Shell.execute(command: "php -v") - // Get everything before "(cli)" (PHP X.X.X (cli) ...) - var version = output.components(separatedBy: " (cli)")[0] - // Strip away the text before the version number - version = version.components(separatedBy: "PHP ")[1] + let version = Shell + // Get the version directly from PHP + .execute(command: "php -r 'print phpversion();'") + // also remove any colors + .replacingOccurrences(of: "\u{1b}(B\u{1b}[m", with: "") + + // That's the long version self.long = version + // Next up, let's strip away the minor version number - let segments = version.components(separatedBy: ".") + let segments = long.components(separatedBy: ".") // Get the first two elements self.short = segments[0...1].joined(separator: ".") } diff --git a/phpmon/Info.plist b/phpmon/Info.plist index 41770a1..04aa118 100644 --- a/phpmon/Info.plist +++ b/phpmon/Info.plist @@ -17,9 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + 1.1 CFBundleVersion - 4 + 10 + LSApplicationCategoryType + public.app-category.utilities LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement diff --git a/phpmon/Helpers/Environment.swift b/phpmon/Startup/BootChecks.swift similarity index 60% rename from phpmon/Helpers/Environment.swift rename to phpmon/Startup/BootChecks.swift index 789c1c1..684204f 100644 --- a/phpmon/Helpers/Environment.swift +++ b/phpmon/Startup/BootChecks.swift @@ -8,22 +8,9 @@ import Foundation -class Environment { +class BootChecks { - public static func presentAlertOnMainThreadIf(_ condition: Bool, messageText: String, informativeText: String) - { - if (condition) { - DispatchQueue.main.async { - Alert.present( - messageText: messageText, - informativeText: informativeText - ) - } - // TODO: Quit the app in any of these scenarios? - } - } - - public static func performBootChecks() + public static func perform() { self.presentAlertOnMainThreadIf( !Shell.execute(command: "which php").contains("/usr/local/bin/php"), @@ -43,6 +30,32 @@ class Environment { informativeText: "You must install Valet via brew. Try running `which valet` in Terminal, it should return `/usr/local/bin/valet`. The app will not work correctly until you resolve this issue." ) - // TODO: Add check for /private/etc/sudoers.d/brew || /private/etc/sudoers.d/valet + self.presentAlertOnMainThreadIf( + !Shell.execute(command: "cat /private/etc/sudoers.d/brew").contains("/usr/local/bin/brew"), + messageText: "Brew has not been added to sudoers.d", + informativeText: "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue." + ) + + self.presentAlertOnMainThreadIf( + !Shell.execute(command: "cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet"), + messageText: "Valet has not been added to sudoers.d", + informativeText: "You must run `sudo valet trust` to ensure Valet can start and stop services without having to use sudo every time. The app will not work correctly until you resolve this issue." + ) + } + + private static func presentAlertOnMainThreadIf( + _ condition: Bool, + messageText: String, + informativeText: String + ) + { + if (condition) { + DispatchQueue.main.async { + Alert.present( + messageText: messageText, + informativeText: informativeText + ) + } + } } } diff --git a/phpmon/Helpers/Services.swift b/phpmon/Terminal/Services.swift similarity index 79% rename from phpmon/Helpers/Services.swift rename to phpmon/Terminal/Services.swift index 521a609..19c3943 100644 --- a/phpmon/Helpers/Services.swift +++ b/phpmon/Terminal/Services.swift @@ -7,6 +7,7 @@ // import Foundation +import AppKit class Services { public static func mysqlIsRunning() -> Bool { @@ -43,4 +44,13 @@ class Services { _ = Shell.execute(command: "valet use php@\(version)") } } + + public static func restartPhp(version: String) { + _ = Shell.execute(command: "brew services restart php@\(version)") + } + + public static func openPhpConfigFolder(version: String) { + let files = [NSURL(fileURLWithPath: "/usr/local/etc/php/\(version)/php.ini")]; + NSWorkspace.shared.activateFileViewerSelecting(files as [URL]); + } } diff --git a/phpmon/Helpers/Shell.swift b/phpmon/Terminal/Shell.swift similarity index 78% rename from phpmon/Helpers/Shell.swift rename to phpmon/Terminal/Shell.swift index 9aac1ca..fd32474 100644 --- a/phpmon/Helpers/Shell.swift +++ b/phpmon/Terminal/Shell.swift @@ -9,6 +9,7 @@ import Cocoa class Shell { + public static func execute(command: String) -> String { let task = Process() @@ -20,7 +21,11 @@ class Shell { task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() - let output: String = NSString(data: data, encoding: String.Encoding.utf8.rawValue)! as String + + let output: String = NSString( + data: data, + encoding: String.Encoding.utf8.rawValue + )! as String return output } diff --git a/phpmon/Base.lproj/Main.storyboard b/phpmon/View Controllers/Base.lproj/Main.storyboard similarity index 100% rename from phpmon/Base.lproj/Main.storyboard rename to phpmon/View Controllers/Base.lproj/Main.storyboard diff --git a/phpmon/ViewController.swift b/phpmon/View Controllers/ViewController.swift similarity index 100% rename from phpmon/ViewController.swift rename to phpmon/View Controllers/ViewController.swift