1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 12:00:09 +02:00

Add option to auto-restart services (#32)

This commit is contained in:
2021-11-13 20:50:17 +01:00
parent fe3cf9adb1
commit 5dbd05fdfb
6 changed files with 133 additions and 48 deletions

View File

@ -647,7 +647,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 60;
CURRENT_PROJECT_VERSION = 70;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
@ -655,7 +655,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.5.0;
MARKETING_VERSION = 4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -671,7 +671,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 60;
CURRENT_PROJECT_VERSION = 70;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
@ -679,7 +679,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.5.0;
MARKETING_VERSION = 4.0;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";

View File

@ -84,8 +84,8 @@
<scene sceneID="iyi-IS-7Ps">
<objects>
<viewController title="Preferences" storyboardIdentifier="preferences" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="PrefsVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" wantsLayer="YES" id="Pf1-A5-3Xz">
<rect key="frame" x="0.0" y="0.0" width="574" height="249"/>
<view key="view" wantsLayer="YES" misplaced="YES" id="Pf1-A5-3Xz">
<rect key="frame" x="0.0" y="0.0" width="574" height="311"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="GSr-K5-3yw">
@ -102,7 +102,7 @@ Gw
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MEf-MN-oXt">
<rect key="frame" x="148" y="212" width="406" height="18"/>
<rect key="frame" x="148" y="274" width="406" height="18"/>
<buttonCell key="cell" type="check" title="DYN_ICON" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="m5s-qp-Iaj">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@ -112,7 +112,7 @@ Gw
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JrH-aa-AzL">
<rect key="frame" x="148" y="191" width="408" height="14"/>
<rect key="frame" x="148" y="253" width="408" height="14"/>
<textFieldCell key="cell" title="DYN_ICON_DESC" id="MHA-Xt-xgF">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -165,7 +165,7 @@ Gw
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="31d-gd-auR">
<rect key="frame" x="18" y="213" width="124" height="16"/>
<rect key="frame" x="18" y="275" width="124" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="8dt-Pg-wFI"/>
</constraints>
@ -184,7 +184,7 @@ Gw
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vSc-oQ-NC5">
<rect key="frame" x="148" y="158" width="121" height="18"/>
<rect key="frame" x="148" y="220" width="121" height="18"/>
<buttonCell key="cell" type="check" title="FULL_PHP_VER" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="eCd-ja-EwE">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@ -194,18 +194,49 @@ Gw
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="t24-LR-wKz">
<rect key="frame" x="148" y="137" width="123" height="14"/>
<rect key="frame" x="148" y="199" width="123" height="14"/>
<textFieldCell key="cell" title="FULL_PHP_VER_DESC" id="8gG-qs-mHR">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ogC-wz-ZfO">
<rect key="frame" x="18" y="153" width="124" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="120" id="i9O-6m-Gr9"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="PREF_SERVICES:" id="bm4-rf-kCF">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="47u-9B-eDu">
<rect key="frame" x="148" y="152" width="126" height="18"/>
<buttonCell key="cell" type="check" title="AUTO_RESTART" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="n1d-l4-inL">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggledAutoRestartServices:" target="AW2-rV-rbS" id="THn-nu-IiJ"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ObP-GE-ejZ">
<rect key="frame" x="148" y="131" width="126" height="14"/>
<textFieldCell key="cell" title="AUTO_RESTART_DESC" id="F9P-iQ-gBk">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="ogC-wz-ZfO" firstAttribute="trailing" secondItem="31d-gd-auR" secondAttribute="trailing" id="2Lr-Ht-qKI"/>
<constraint firstItem="t24-LR-wKz" firstAttribute="leading" secondItem="vSc-oQ-NC5" secondAttribute="leading" id="3tK-kp-q5R"/>
<constraint firstItem="t24-LR-wKz" firstAttribute="top" secondItem="vSc-oQ-NC5" secondAttribute="bottom" constant="8" symbolic="YES" id="4Ft-lN-vwA"/>
<constraint firstAttribute="trailing" secondItem="JrH-aa-AzL" secondAttribute="trailing" constant="20" symbolic="YES" id="8iM-Xf-ShU"/>
<constraint firstItem="ObP-GE-ejZ" firstAttribute="leading" secondItem="47u-9B-eDu" secondAttribute="leading" id="ASF-WR-A3X"/>
<constraint firstAttribute="trailing" secondItem="GSr-K5-3yw" secondAttribute="trailing" constant="20" symbolic="YES" id="AT9-5F-6g1"/>
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="trailing" constant="12" symbolic="YES" id="Bk6-4V-GLk"/>
<constraint firstItem="31d-gd-auR" firstAttribute="top" secondItem="Pf1-A5-3Xz" secondAttribute="top" constant="20" symbolic="YES" id="C3K-NX-BBl"/>
@ -214,7 +245,10 @@ Gw
<constraint firstItem="MEf-MN-oXt" firstAttribute="leading" secondItem="31d-gd-auR" secondAttribute="trailing" constant="10" id="G5S-JV-re3"/>
<constraint firstItem="V7b-jv-oCB" firstAttribute="firstBaseline" secondItem="5ZK-BG-o1t" secondAttribute="firstBaseline" id="H5D-2D-DLH"/>
<constraint firstItem="1TO-9H-z2d" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="leading" id="Imk-o0-2fS"/>
<constraint firstItem="ObP-GE-ejZ" firstAttribute="top" secondItem="47u-9B-eDu" secondAttribute="bottom" constant="8" symbolic="YES" id="JqR-Jd-SoR"/>
<constraint firstItem="JrH-aa-AzL" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="K2H-Af-2qK"/>
<constraint firstItem="5ZK-BG-o1t" firstAttribute="top" secondItem="ObP-GE-ejZ" secondAttribute="bottom" constant="30" id="LO4-8j-ihp"/>
<constraint firstItem="47u-9B-eDu" firstAttribute="top" secondItem="ogC-wz-ZfO" secondAttribute="top" id="T9j-v2-fSW"/>
<constraint firstItem="JrH-aa-AzL" firstAttribute="top" secondItem="MEf-MN-oXt" secondAttribute="bottom" constant="8" symbolic="YES" id="Vf8-fx-H50"/>
<constraint firstItem="MEf-MN-oXt" firstAttribute="firstBaseline" secondItem="31d-gd-auR" secondAttribute="firstBaseline" id="W36-bE-iAT"/>
<constraint firstItem="1TO-9H-z2d" firstAttribute="firstBaseline" secondItem="V7b-jv-oCB" secondAttribute="baseline" constant="25" id="bJG-ed-pch"/>
@ -222,29 +256,33 @@ Gw
<constraint firstItem="5ZK-BG-o1t" firstAttribute="trailing" secondItem="31d-gd-auR" secondAttribute="trailing" id="c4g-jO-JUm"/>
<constraint firstAttribute="bottom" secondItem="GSr-K5-3yw" secondAttribute="bottom" constant="20" symbolic="YES" id="dAS-yW-vua"/>
<constraint firstItem="vSc-oQ-NC5" firstAttribute="top" secondItem="JrH-aa-AzL" secondAttribute="bottom" constant="16" id="hQf-4s-iHn"/>
<constraint firstItem="5ZK-BG-o1t" firstAttribute="top" secondItem="t24-LR-wKz" secondAttribute="bottom" constant="36" id="hZR-8k-Hq1"/>
<constraint firstItem="GSr-K5-3yw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="mTE-WD-54L"/>
<constraint firstItem="47u-9B-eDu" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="n8B-C8-dXs"/>
<constraint firstItem="31d-gd-auR" firstAttribute="leading" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="o0J-yT-TDX"/>
<constraint firstItem="ogC-wz-ZfO" firstAttribute="top" secondItem="t24-LR-wKz" secondAttribute="bottom" constant="30" id="oXh-LE-sRS"/>
<constraint firstAttribute="trailing" secondItem="MEf-MN-oXt" secondAttribute="trailing" constant="20" symbolic="YES" id="pJg-zj-cBs"/>
<constraint firstItem="GSr-K5-3yw" firstAttribute="top" secondItem="1TO-9H-z2d" secondAttribute="bottom" constant="20" id="pMZ-Gx-Jmm"/>
</constraints>
</view>
<connections>
<outlet property="buttonAutoRestartServices" destination="47u-9B-eDu" id="kyg-BX-PQK"/>
<outlet property="buttonClearShortcut" destination="YsQ-AZ-Aei" id="1xo-hk-HgM"/>
<outlet property="buttonClose" destination="GSr-K5-3yw" id="d4I-Cf-gXD"/>
<outlet property="buttonDisplayFullPhpVersion" destination="vSc-oQ-NC5" id="ZLa-Vf-4Dq"/>
<outlet property="buttonDynamicIcon" destination="MEf-MN-oXt" id="qEN-Vg-EZS"/>
<outlet property="buttonSetShortcut" destination="V7b-jv-oCB" id="2aS-S4-cKR"/>
<outlet property="labelAutoRestartServices" destination="ObP-GE-ejZ" id="uwY-D7-Uve"/>
<outlet property="labelDisplayFullPhpVersion" destination="t24-LR-wKz" id="wYj-Z0-a3h"/>
<outlet property="labelDynamicIcon" destination="JrH-aa-AzL" id="CFc-fF-oPq"/>
<outlet property="labelShortcut" destination="1TO-9H-z2d" id="paF-hK-78x"/>
<outlet property="leftLabelDynamicIcon" destination="31d-gd-auR" id="ANZ-Zs-4d7"/>
<outlet property="leftLabelGlobalShortcut" destination="5ZK-BG-o1t" id="73E-9i-cg8"/>
<outlet property="leftLabelServices" destination="ogC-wz-ZfO" id="BYx-Gv-N1p"/>
</connections>
</viewController>
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="264" y="399.5"/>
<point key="canvasLocation" x="264" y="457"/>
</scene>
</scenes>
</document>

View File

@ -257,6 +257,10 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
@objc func toggleExtension(sender: ExtensionMenuItem) {
waitAndExecute {
sender.phpExtension?.toggle()
if Preferences.preferences[.autoServiceRestartAfterExtensionToggle] as! Bool == true {
Actions.restartPhpFpm()
}
}
}

View File

@ -9,57 +9,71 @@
import Foundation
enum PreferenceName: String {
case wasLaunchedBefore = "launched_before"
case shouldDisplayDynamicIcon = "use_dynamic_icon"
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
case globalHotkey = "global_hotkey"
}
class Preferences {
// MARK: - Singleton
static var shared = Preferences()
var cachedPreferences: [PreferenceName: Any?]
public init() {
Preferences.handleFirstTimeLaunch()
self.cachedPreferences = Self.cache()
}
// MARK: - First Time Run
/**
Note: macOS seems to cache plist values in memory as well as in files.
You can find the persisted configuration file in: ~/Library/Preferences/com.nicoverbruggen.phpmon.plist
To clear the cache, and get a first-run experience you may need to run:
```
defaults delete com.nicoverbruggen.phpmon
killall cfprefsd
```
*/
static func handleFirstTimeLaunch() {
let launchedBefore = UserDefaults.standard.bool(forKey: "launched_before")
UserDefaults.standard.register(defaults: [
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true
])
if launchedBefore {
if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) {
return
}
// Set sensible defaults
UserDefaults.standard.setValue(true, forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue)
UserDefaults.standard.setValue(false, forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue)
UserDefaults.standard.setValue(true, forKey: "launched_before")
UserDefaults.standard.synchronize()
print("Saving first-time preferences!")
UserDefaults.standard.setValue(true, forKey: PreferenceName.wasLaunchedBefore.rawValue)
UserDefaults.standard.synchronize()
}
static func handleMissingPreferences() {
var migrated = false
// Any defaults that need to be adopted in case they are missing?
// If any new preferences are added in updates, they should get a default value here!
if UserDefaults.standard.value(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) == nil {
UserDefaults.standard.setValue(false, forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue)
migrated = true
}
if migrated {
UserDefaults.standard.synchronize()
}
}
static func retrieve() -> [PreferenceName: Any] {
Preferences.handleFirstTimeLaunch()
Preferences.handleMissingPreferences()
return [
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
]
}
// MARK: - API
static var preferences: [PreferenceName: Any?] {
return Preferences.retrieve()
return Self.shared.cachedPreferences
}
// MARK: - Internal Functionality
static func cache() -> [PreferenceName: Any] {
return [
// Part 1: Always Booleans
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any,
// Part 2: Always Strings
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
]
}
static func update(_ preference: PreferenceName, value: Any?) {
@ -69,6 +83,9 @@ class Preferences {
UserDefaults.standard.setValue(value, forKey: preference.rawValue)
}
UserDefaults.standard.synchronize()
// Update the preferences cache in memory!
Preferences.shared.cachedPreferences = Preferences.cache()
}
}

View File

@ -14,6 +14,7 @@ class PrefsVC: NSViewController {
// Labels on the left
@IBOutlet weak var leftLabelDynamicIcon: NSTextField!
@IBOutlet weak var leftLabelServices: NSTextField!
@IBOutlet weak var leftLabelGlobalShortcut: NSTextField!
// Dynamic icon
@ -24,6 +25,10 @@ class PrefsVC: NSViewController {
@IBOutlet weak var buttonDisplayFullPhpVersion: NSButton!
@IBOutlet weak var labelDisplayFullPhpVersion: NSTextField!
// Auto-restart services
@IBOutlet weak var buttonAutoRestartServices: NSButton!
@IBOutlet weak var labelAutoRestartServices: NSTextField!
// Shortcut
@IBOutlet weak var buttonSetShortcut: NSButton!
@IBOutlet weak var buttonClearShortcut: NSButton!
@ -76,6 +81,11 @@ class PrefsVC: NSViewController {
buttonDisplayFullPhpVersion.title = "prefs.display_full_php_version".localized
labelDisplayFullPhpVersion.stringValue = "prefs.display_full_php_version_desc".localized
// Services
leftLabelServices.stringValue = "prefs.services".localized
buttonAutoRestartServices.title = "prefs.auto_restart_services_title".localized
labelAutoRestartServices.stringValue = "prefs_auto_restart_services_desc".localized
// Global Shortcut
leftLabelGlobalShortcut.stringValue = "prefs.global_shortcut".localized
labelShortcut.stringValue = "prefs.shortcut_desc".localized
@ -98,6 +108,11 @@ class PrefsVC: NSViewController {
self.buttonDisplayFullPhpVersion.state = shouldDisplay ? .on : .off
}
func loadAutoRestartServicesFromPreferences() {
let shouldDisplay = Preferences.preferences[.autoServiceRestartAfterExtensionToggle] as! Bool == true
self.buttonAutoRestartServices.state = shouldDisplay ? .on : .off
}
// MARK: - Actions
@IBAction func toggledDynamicIcon(_ sender: Any) {
@ -110,6 +125,10 @@ class PrefsVC: NSViewController {
MainMenu.shared.refreshIcon()
}
@IBAction func toggledAutoRestartServices(_ sender: Any) {
Preferences.update(.autoServiceRestartAfterExtensionToggle, value: buttonAutoRestartServices.state == .on)
}
// MARK: - Shortcut Preference
// Adapted from: https://dev.to/mitchartemis/creating-a-global-configurable-shortcut-for-macos-apps-in-swift-25e9

View File

@ -52,6 +52,10 @@
"prefs.global_shortcut" = "Global shortcut:";
"prefs.dynamic_icon" = "Dynamic icon:";
"prefs.services" = "Services:";
"prefs.auto_restart_services_title" = "Auto-restart PHP-FPM";
"prefs_auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when\nyou check or uncheck an extension. Slightly slower when enabled, \nbut this applies the extension change immediately for all sites \nyou're serving, no need to restart PHP-FPM manually.";
"prefs.dynamic_icon_title" = "Display dynamic icon in menu bar";
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible.\nIf checked, it will display the major version number of the\ncurrently linked PHP version.";
@ -69,6 +73,9 @@
"notification.version_changed_title" = "PHP %@ now active";
"notification.version_changed_desc" = "PHP Monitor has switched to PHP %@.";
"notification.php_fpm_restarted" = "PHP-FPM automatically restarted";
"notification.php_fpm_restarted_desc" = "You toggled an extension, so PHP-FPM was automatically restarted.";
"notification.services_stopped" = "Valet services stopped";
"notification.services_stopped_desc" = "All services have been successfully stopped.";