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

♻️ Refactor waitAndExecuteasyncExecution

This change allows for errors to be thrown during the `asyncExecution`
initial callback, and if one is thrown, allows a `failure` callback to
be used. Existing instances of `waitAndExecute` have been replaced.

I also added the ability to tweak the behaviours of the actions that are
always performed when the asyncExecution method is called: you can now
specify limited behaviours (e.g. only set busy icon). For that use case
I have already created a new method: `asyncWithBusyUI`.

With this change, the handling of the Homebrew Permissions flow has also
been modified: when the user does not get administrative permissions
an error is now thrown which results in an alert being presented to
the user once the error occurs.

There is now an opportunity to further refactor other parts of the app
to more gracefully handle failure states using the Error and
AlertableError protocols.
This commit is contained in:
2022-02-06 13:15:03 +01:00
parent a13a17b106
commit 927e1c02fa
10 changed files with 215 additions and 64 deletions

View File

@ -79,6 +79,12 @@
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
C44CCD4227AFE2FC00CE40E5 /* AlertableError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */; };
C44CCD4527AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4427AFE94900CE40E5 /* HomebrewPermissionError.swift */; };
C44CCD4627AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4427AFE94900CE40E5 /* HomebrewPermissionError.swift */; };
C44CCD4727AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4427AFE94900CE40E5 /* HomebrewPermissionError.swift */; };
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
@ -264,6 +270,8 @@
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
C44C198C276E3A1C0072762D /* ProgressWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressWindow.swift; sourceTree = "<group>"; };
C44C1990276E44CB0072762D /* ProgressWindow.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = ProgressWindow.storyboard; sourceTree = "<group>"; };
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertableError.swift; sourceTree = "<group>"; };
C44CCD4427AFE94900CE40E5 /* HomebrewPermissionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPermissionError.swift; sourceTree = "<group>"; };
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = "<group>"; };
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = "<group>"; };
@ -517,6 +525,15 @@
path = Progress;
sourceTree = "<group>";
};
C44CCD4327AFE93300CE40E5 /* Errors */ = {
isa = PBXGroup;
children = (
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */,
C44CCD4427AFE94900CE40E5 /* HomebrewPermissionError.swift */,
);
path = Errors;
sourceTree = "<group>";
};
C464ADAA275A7A25003FCD53 /* SiteList */ = {
isa = PBXGroup;
children = (
@ -617,6 +634,7 @@
C4B5853A2770FE2500DA4FBE /* phpmon-common */ = {
isa = PBXGroup;
children = (
C44CCD4327AFE93300CE40E5 /* Errors */,
C40C7F2127721F7300DDDCDC /* Core */,
54B20EDF263AA22C00D3250E /* PHP */,
);
@ -872,7 +890,9 @@
C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */,
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */,
C4B585462770FE3900DA4FBE /* Command.swift in Sources */,
C44CCD4727AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */,
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */,
C44CCD4227AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */,
C417DC76277614690015E6EE /* Helpers.swift in Sources */,
@ -919,6 +939,7 @@
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
C44CCD4527AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */,
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
5420395F2613607600FB00FA /* Preferences.swift in Sources */,
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
@ -939,6 +960,7 @@
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
@ -1010,7 +1032,9 @@
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
C44CCD4627AFE94900CE40E5 /* HomebrewPermissionError.swift in Sources */,
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
@ -1215,7 +1239,7 @@
C41C1B4422B0098000E7CF16 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconBeta;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -1230,7 +1254,7 @@
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = "5.1-dev";
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.beta;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
@ -1240,7 +1264,7 @@
C41C1B4522B0098000E7CF16 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIconBeta;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = phpmon/phpmon.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@ -1255,7 +1279,7 @@
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = "5.1-dev";
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon.beta;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;

View File

@ -320,9 +320,9 @@ You can always still ask Valet using the command line, should it be necessary. I
<details>
<summary><strong>After running PHP Monitor, Homebrew sometimes has issues with `brew upgrade` or `brew cleanup`!</strong></summary>
This is a security feature of Homebrew. When you start a service as an administrator, the root user becomes the owner of relevant binaries. You will need to manually clean up those folders yourself using `rm -rf` (or by manually removing those folders via Finder).
You can now use **First Aid & Services > Restore Homebrew Permissions** to (temporarily) resolve this issue and allow for a clean and painless `brew upgrade` or `brew cleanup` process.
If you would like to know more, consult [this issue](https://github.com/nicoverbruggen/phpmon/issues/85) for more information.
If you would like to know more, consult [this issue](https://github.com/nicoverbruggen/phpmon/issues/85) for more information about why this is needed.
</details>
@ -399,4 +399,4 @@ I have done my best to annotate as much as humanly possible, and have avoided us
I also have a few tests for key parts of the application that I found needed to be tested. In the future, I would like to add even more tests for some of the UI stuff, but for now the tests are more unit tests than feature tests.
For more detailed information for developers, please see [the documentation file for developers](./DEVS.md).
For more detailed information for developers, please see [the documentation file for developers](./DEVS.md).

View File

@ -34,7 +34,7 @@ class Actions {
brew("services stop dnsmasq", sudo: true)
}
public static func fixHomebrewPermissions()
public static func fixHomebrewPermissions() throws
{
var servicesCommands = [
"\(Paths.brew) services stop nginx",
@ -65,10 +65,7 @@ class Actions {
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
if (eventResult == nil) {
print("Oh no, that didn't work.")
} else {
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
print("Oh, that worked.")
throw HomebrewPermissionError(kind: .applescriptNilError)
}
}

View File

@ -0,0 +1,13 @@
//
// Errors.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 06/02/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
protocol AlertableError {
func getErrorMessageKey() -> String
}

View File

@ -0,0 +1,21 @@
//
// HomebrewPermissionError.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 06/02/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
struct HomebrewPermissionError: Error, AlertableError {
enum Kind: String {
case applescriptNilError = "homebrew_permissions.applescript_returned_nil"
}
let kind: Kind
func getErrorMessageKey() -> String {
return "alert.errors.\(self.kind.rawValue)"
}
}

View File

@ -771,7 +771,7 @@ Gw
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
</constraints>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
<rect key="frame" x="1" y="293" width="598" height="15"/>
<rect key="frame" x="1" y="292" width="598" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
@ -808,7 +808,7 @@ Gw
</scenes>
<resources>
<image name="Checkmark" width="512" height="512"/>
<image name="IconLinked" width="512" height="512"/>
<image name="IconLinked" width="25" height="25"/>
<image name="Lock" width="30" height="30"/>
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
<image name="plus" catalog="system" width="14" height="13"/>

View File

@ -59,5 +59,16 @@ class Alert {
secondButtonTitle: "",
style: style
)
}
}
public static func notifyAbout(error: Error&AlertableError) {
let key = error.getErrorMessageKey()
_ = present(
messageText: "\(key).title".localized,
informativeText: "\(key).description".localized,
buttonTitle: "OK",
secondButtonTitle: "",
style: .critical
)
}
}

View File

@ -87,31 +87,83 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
// MARK: - Nicer callbacks
enum AsyncBehaviour {
case setsBusyUI
case reloadsPhpInstallation
case updatesMenuBarContents
case broadcastServicesUpdate
}
/**
Executes a specific callback and fires the completion callback,
while updating the UI as required. As long as the completion callback
does not fire, the app is presumed to be busy and the UI reflects this.
Attempts asynchronous execution of a callback that may throw an Error.
While the callback is being executed, the UI will be marked as busy.
- Parameter execute: Callback of the work that needs to happen.
- Parameter completion: Callback that is fired when the work is done.
- Parameter success: Callback that is fired when all was OK.
- Parameter failure: Callback that is fired when an Error was thrown.
- Parameter behaviours: Various behaviours that can be tweaked, but usually best left to the default.
*/
private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
{
PhpEnv.shared.isBusy = true
setBusyImage()
private func asyncExecution(
_ execute: @escaping () throws -> Void,
success: @escaping () -> Void = {},
failure: @escaping (Error) -> Void = { _ in },
behaviours: [AsyncBehaviour] = [
.setsBusyUI,
.reloadsPhpInstallation,
.updatesMenuBarContents,
.broadcastServicesUpdate
]
) {
if behaviours.contains(.reloadsPhpInstallation) {
PhpEnv.shared.isBusy = true
}
if behaviours.contains(.setsBusyUI) {
setBusyImage()
}
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
execute()
PhpEnv.shared.isBusy = false
var error: Error? = nil
do { try execute() } catch let e { error = e }
if behaviours.contains(.setsBusyUI) {
PhpEnv.shared.isBusy = false
}
DispatchQueue.main.async { [self] in
PhpEnv.shared.currentInstall = ActivePhpInstallation()
updatePhpVersionInStatusBar()
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
completion()
if behaviours.contains(.reloadsPhpInstallation) {
PhpEnv.shared.currentInstall = ActivePhpInstallation()
}
if behaviours.contains(.updatesMenuBarContents) {
// Refresh the entire menu bar menu's contents
updatePhpVersionInStatusBar()
} else {
// We do still need to refresh the icon based on the busy state
if behaviours.contains(.setsBusyUI) {
refreshIcon()
}
}
if behaviours.contains(.broadcastServicesUpdate) {
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
}
error == nil ? success() : failure(error!)
}
}
}
public func asyncWithBusyUI(
_ execute: @escaping () throws -> Void,
completion: @escaping () -> Void = {}
) {
asyncExecution({
try! execute()
}, success: {
completion()
}, behaviours: [.setsBusyUI])
}
// MARK: - User Interface
@objc func refreshActiveInstallation() {
@ -146,14 +198,14 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
}
@objc func reloadPhpMonitorMenuInBackground() {
waitAndExecute {
asyncExecution {
// This automatically reloads the menu
Log.info("Reloading information about the PHP installation (in the background)...")
}
}
@objc func reloadPhpMonitorMenu() {
waitAndExecute {
asyncExecution {
// This automatically reloads the menu
Log.info("Reloading information about the PHP installation...")
}
@ -168,21 +220,43 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
// MARK: - Actions
@objc func fixHomebrewPermissions() {
Actions.fixHomebrewPermissions()
if !Alert.present(
messageText: "alert.fix_homebrew_permissions.title".localized,
informativeText: "alert.fix_homebrew_permissions.info".localized,
buttonTitle: "alert.fix_homebrew_permissions.ok".localized,
secondButtonTitle: "alert.fix_homebrew_permissions.cancel".localized,
style: .warning
) {
return
}
asyncExecution {
try Actions.fixHomebrewPermissions()
} success: {
Alert.notify(
message: "alert.fix_homebrew_permissions_done.title".localized,
info: "alert.fix_homebrew_permissions_done.info".localized,
style: .warning
)
} failure: { error in
let error = error as! HomebrewPermissionError
Alert.notifyAbout(error: error)
}
}
@objc func restartPhpFpm() {
waitAndExecute {
asyncExecution {
Actions.restartPhpFpm()
}
}
@objc func restartAllServices() {
waitAndExecute {
asyncExecution {
Actions.restartDnsMasq()
Actions.restartPhpFpm()
Actions.restartNginx()
} completion: {
} success: {
DispatchQueue.main.async {
LocalNotification.send(
title: "notification.services_restarted".localized,
@ -193,9 +267,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
}
@objc func stopAllServices() {
waitAndExecute {
asyncExecution {
Actions.stopAllServices()
} completion: {
} success: {
DispatchQueue.main.async {
LocalNotification.send(
title: "notification.services_stopped".localized,
@ -206,19 +280,19 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
}
@objc func restartNginx() {
waitAndExecute {
asyncExecution {
Actions.restartNginx()
}
}
@objc func restartDnsMasq() {
waitAndExecute {
asyncExecution {
Actions.restartDnsMasq()
}
}
@objc func toggleExtension(sender: ExtensionMenuItem) {
waitAndExecute {
asyncExecution {
sender.phpExtension?.toggle()
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
@ -230,34 +304,32 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
@objc func openPhpInfo() {
var url: URL? = nil
waitAndExecute {
asyncWithBusyUI {
url = Actions.createTempPhpInfoFile()
} completion: {
// When this has been completed, open the URL to the file in the browser
NSWorkspace.shared.open(url!)
if url != nil { NSWorkspace.shared.open(url!) }
}
}
@objc func fixMyValet() {
// Tell the user the switch is about to occur
if Alert.present(
if !Alert.present(
messageText: "alert.fix_my_valet.title".localized,
informativeText: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion),
buttonTitle: "alert.fix_my_valet.ok".localized,
secondButtonTitle: "alert.fix_my_valet.cancel".localized,
style: .warning
) {
// Start the fix
waitAndExecute {
Actions.fixMyValet()
} completion: {
Alert.notify(
message: "alert.fix_my_valet_done.title".localized,
info: "alert.fix_my_valet_done.info".localized
)
}
} else {
Log.info("The user has chosen to abort Fix My Valet")
return
}
asyncExecution {
Actions.fixMyValet()
} success: {
Alert.notify(
message: "alert.fix_my_valet_done.title".localized,
info: "alert.fix_my_valet_done.info".localized
)
}
}
@ -288,7 +360,6 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
self.switchToPhpVersion(sender.version)
}
// TODO (5.1): Investigate if `waitAndExecute` cannot be used here
@objc func switchToPhpVersion(_ version: String) {
setBusyImage()
PhpEnv.shared.isBusy = true

View File

@ -46,19 +46,19 @@ class StatusMenu : NSMenu {
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
servicesMenu.addItem(NSMenuItem(
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
action: nil, keyEquivalent: "f"
))
action: nil, keyEquivalent: "f")
)
} else {
servicesMenu.addItem(NSMenuItem(
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""))
action: #selector(MainMenu.fixMyValet), keyEquivalent: "")
)
}
/*
servicesMenu.addItem(NSMenuItem(
title: "mi_fix_brew_permissions".localized(),
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: ""))
*/
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: "")
)
servicesMenu.addItem(NSMenuItem(title: "mi_services".localized, action: nil, keyEquivalent: ""))

View File

@ -27,7 +27,7 @@
"mi_stop_all_services" = "Stop All Services";
"mi_fix_my_valet" = "Fix My Valet (PHP & Services)";
"mi_fix_my_valet_unavailable" = "Fix My Valet Unavailable";
"mi_fix_brew_permissions" = "Fix Homebrew Permissions";
"mi_fix_brew_permissions" = "Restore Homebrew Permissions";
"mi_php_refresh" = "Refresh Information";
"mi_configuration" = "PHP Configuration";
@ -214,6 +214,15 @@ problem manually, using your own Terminal app (this just shows you the output)."
"alert.fix_my_valet_done.title" = "Fix My Valet has completed its operations.";
"alert.fix_my_valet_done.info" = "All appropriate services have been stopped and the correct ones restarted, and the latest version of PHP should now be active. You can now try switching to another version of PHP.\n\nIf visiting sites still does not work, you may try running `valet install` again, this can fix a 502 issue (Bad Gateway).\n\nIf Valet is broken and you cannot run `valet install`, you may need to run `composer global update`. Please consult the FAQ on GitHub if you have further issues.";
// Restore Homebrew Permissions
"alert.fix_homebrew_permissions.title" = "About \"Restore Homebrew Permissions\"";
"alert.fix_homebrew_permissions.info" = "This feature was created so you can run `brew upgrade` or `brew cleanup` without permission issues.\n\nThis will require administrative privileges, because PHP Monitor will restore your ownership of the files and folders that are currently owned by the `root` user, due to Valet services running as root.\n\n(You will be notified when this fix has been applied.)";
"alert.fix_homebrew_permissions.ok" = "Restore Permissions";
"alert.fix_homebrew_permissions.cancel" = "Cancel";
"alert.fix_homebrew_permissions_done.title" = "Permissions Fixed!";
"alert.fix_homebrew_permissions_done.info" = "When you are done with Homebrew, you should open PHP Monitor and restart all services if you want Valet to work again. It is also recommended to restart PHP Monitor after running `brew upgrade`.";
// PHP FPM Broken
"alert.php_fpm_broken.title" = "PHP-FPM configuration is incorrect";
"alert.php_fpm_broken.info" = "PHP Monitor has determined that there are issues with your PHP-FPM config: it's not pointing to the Valet socket. This will result in 502 Bad Gateway if you visit websites linked via Valet.\n\nYou can usually fix this by running\n`valet install`, which updates your\n PHP-FPM configuration.";
@ -279,3 +288,8 @@ You can do this by running `composer global update` in your terminal. After that
"startup.sponsor_encouragement.desc" = "If you have already donated, then YOU are the reason why the app was able to get all these new features. In that case, this is a THANK YOU message to you.\n\nTo be 100% transparent: I plan to keep PHP Monitor open source and free. Your support makes this decision very easy.\n\n(You will only see this prompt once.)";
"startup.sponsor_encouragement.accept" = "Yes, I would like to sponsor";
"startup.sponsor_encouragement.skip" = "Nevermind";
// ERROR MESSAGES (based on AlertableError)
"alert.errors.homebrew_permissions.applescript_returned_nil.title" = "Restore Homebrew Permissions has been cancelled.";
"alert.errors.homebrew_permissions.applescript_returned_nil.description" = "The outcome of the script that is executed to adjust the permissions returned nil, which usually means that you did not grant administrative permissions to PHP Monitor.\n\nIf you clicked on Cancel during the authentication prompt, this is normal. If you did actually authenticate and you are still seeing this message, something probably went wrong.";