🚀 Version 5.0.1
🔀 Merge branch 'dev/5.x'
46
DEVELOPER.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# DEVELOPER README
|
||||||
|
|
||||||
|
## 🔧 Build instructions
|
||||||
|
|
||||||
|
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||||
|
|
||||||
|
If you'd like to build PHP Monitor yourself, you need:
|
||||||
|
|
||||||
|
* Xcode (usually the latest version)
|
||||||
|
* The contents of this repository
|
||||||
|
|
||||||
|
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to immediately build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
||||||
|
|
||||||
|
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||||
|
|
||||||
|
## 🐛 Symbolication of crashes
|
||||||
|
|
||||||
|
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
||||||
|
|
||||||
|
For example, given the following crash (from an .ips file):
|
||||||
|
|
||||||
|
```
|
||||||
|
Thread 2 Crashed:: Dispatch queue: com.apple.root.user-initiated-qos
|
||||||
|
0 libswiftDispatch.dylib 0x7ff82aa3ab8c static OS_dispatch_source.makeProcessSource(identifier:eventMask:queue:) + 28
|
||||||
|
1 PHP Monitor 0x1096907d8 0x10965e000 + 206808
|
||||||
|
| |
|
||||||
|
address load address
|
||||||
|
2 PHP Monitor 0x1096903ac 0x10965e000 + 205740
|
||||||
|
3 PHP Monitor 0x10968f88b 0x10965e000 + 202891
|
||||||
|
```
|
||||||
|
|
||||||
|
You must use the correct order for the the address and load address in the command below:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ atos -arch x86_64 -o '/path/to/PHP Monitor.app.dSYM/Contents/Resources/DWARF/PHP Monitor' -l 0x10965e000 0x1096907d8
|
||||||
|
| | | |
|
||||||
|
architecture path to DSYM load address address
|
||||||
|
```
|
||||||
|
|
||||||
|
This will return the relevant information, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
FSWatcher.startMonitoring(_:behaviour:) (in PHP Monitor) (PhpConfigWatcher.swift:95)
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, see [Apple's documentation](https://developer.apple.com/documentation/xcode/adding-identifiable-symbol-names-to-a-crash-report).
|
@ -9,13 +9,11 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
|
||||||
54AB03272763858F00A29D5F /* Timer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54AB03252763858F00A29D5F /* Timer.swift */; };
|
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */; };
|
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
54FCFD27276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */; };
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
||||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */; };
|
||||||
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */; };
|
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */; };
|
||||||
@ -24,37 +22,31 @@
|
|||||||
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
|
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */; };
|
||||||
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; };
|
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */; };
|
||||||
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; };
|
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */ = {isa = PBXBuildFile; fileRef = C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */; };
|
||||||
|
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; };
|
||||||
|
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */; };
|
||||||
|
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
||||||
|
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
||||||
|
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||||
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C40B24F327A310780018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
|
||||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||||
C40B24F527A3108B0018C7D2 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
C40B24F527A3108B0018C7D2 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
||||||
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
|
||||||
C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
|
||||||
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
|
||||||
C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
|
||||||
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
|
||||||
C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
|
||||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||||
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||||
C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
|
||||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
||||||
C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2F27722E8D00DDDCDC /* Logger.swift */; };
|
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */; };
|
||||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
||||||
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */; };
|
||||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
C415D3B72770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
||||||
C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
C415D3B82770F294005EF286 /* Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3B62770F294005EF286 /* Actions.swift */; };
|
||||||
C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3DE2770F34D005EF286 /* AllowedArguments.swift */; };
|
|
||||||
C415D3E62770F540005EF286 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E52770F540005EF286 /* main.swift */; };
|
|
||||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */; };
|
||||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
C417DC74277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||||
C417DC76277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
|
||||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||||
@ -79,6 +71,10 @@
|
|||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
||||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C44C1990276E44CB0072762D /* ProgressWindow.storyboard */; };
|
||||||
C44C1992276E44CB0072762D /* 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 */; };
|
||||||
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||||
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
||||||
C464ADAD275A7A3F003FCD53 /* 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 */; };
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||||
@ -101,8 +97,9 @@
|
|||||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
|
||||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
||||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */; };
|
|
||||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */; };
|
||||||
|
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||||
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
||||||
@ -121,13 +118,10 @@
|
|||||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; };
|
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; };
|
||||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
||||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
||||||
C4B585402770FE3900DA4FBE /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853B2770FE3900DA4FBE /* Paths.swift */; };
|
|
||||||
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
||||||
C4B585432770FE3900DA4FBE /* Shell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853C2770FE3900DA4FBE /* Shell.swift */; };
|
|
||||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
C4B585442770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
C4B585452770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
||||||
C4B585462770FE3900DA4FBE /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5853D2770FE3900DA4FBE /* Command.swift */; };
|
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */; };
|
||||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
||||||
@ -142,19 +136,20 @@
|
|||||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */; };
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
|
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
|
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
||||||
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
|
C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
||||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
|
||||||
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
||||||
C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */; };
|
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||||
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
||||||
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */; };
|
|
||||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
||||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
||||||
C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
||||||
@ -170,12 +165,12 @@
|
|||||||
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
C4F2E43B27530F750020E974 /* PhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F2E4392752F7D00020E974 /* PhpInstallation.swift */; };
|
||||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
||||||
C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F30B02278E16BA00755FCE /* HomebrewService.swift */; };
|
|
||||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
C4F30B07278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
||||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
C4F30B08278E195800755FCE /* brew-services.json in Resources */ = {isa = PBXBuildFile; fileRef = C4F30B06278E195800755FCE /* brew-services.json */; };
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D89BC52783C99400A02B68 /* ComposerJson.swift */; };
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||||
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F7809B25D80344000DBC97 /* CommandTest.swift */; };
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; };
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */ = {isa = PBXBuildFile; fileRef = C4F780A725D80AE8000DBC97 /* php.ini */; };
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; };
|
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */; };
|
||||||
@ -209,39 +204,27 @@
|
|||||||
};
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
|
||||||
C415D3D42770F341005EF286 /* CopyFiles */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = /usr/share/man/man1/;
|
|
||||||
dstSubfolderSpec = 0;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 1;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
5420395826135DC100FB00FA /* PrefsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsVC.swift; sourceTree = "<group>"; };
|
||||||
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
5420395E2613607600FB00FA /* Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
54AB03252763858F00A29D5F /* Timer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Timer.swift; sourceTree = "<group>"; };
|
|
||||||
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
54B48B5E275F66AE006D90C5 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
|
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotkeyPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
C405A4CE24B9B9130062FAFA /* InternetAccessPolicy.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = InternetAccessPolicy.strings; sourceTree = "<group>"; };
|
||||||
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
C405A4CF24B9B9140062FAFA /* InternetAccessPolicy.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = InternetAccessPolicy.plist; sourceTree = "<group>"; };
|
||||||
|
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
|
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
|
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
||||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
||||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
||||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; };
|
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackage.swift; sourceTree = "<group>"; };
|
||||||
C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpFrameworks.swift; sourceTree = "<group>"; };
|
C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpFrameworks.swift; sourceTree = "<group>"; };
|
||||||
C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
|
C415D3B62770F294005EF286 /* Actions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Actions.swift; sourceTree = "<group>"; };
|
||||||
C415D3D62770F341005EF286 /* phpmon-cli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "phpmon-cli"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
C415D3DE2770F34D005EF286 /* AllowedArguments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllowedArguments.swift; sourceTree = "<group>"; };
|
|
||||||
C415D3E52770F540005EF286 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
|
||||||
C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+InterApp.swift"; sourceTree = "<group>"; };
|
C415D3E72770F692005EF286 /* AppDelegate+InterApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+InterApp.swift"; sourceTree = "<group>"; };
|
||||||
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
||||||
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||||
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
||||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -263,6 +246,8 @@
|
|||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
||||||
|
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Async.swift"; sourceTree = "<group>"; };
|
||||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.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>"; };
|
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>"; };
|
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = "<group>"; };
|
||||||
@ -280,6 +265,7 @@
|
|||||||
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
|
||||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpVersionNumber.swift; sourceTree = "<group>"; };
|
||||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionNumberTest.swift; sourceTree = "<group>"; };
|
||||||
|
C4927F0A27B2DFC200C55AFD /* Errors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
C4930849279F331F009C240B /* AddSiteVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddSiteVC.swift; sourceTree = "<group>"; };
|
||||||
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
|
||||||
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
C49E171E27A5736E00787921 /* PMServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMServicesView.swift; sourceTree = "<group>"; };
|
||||||
@ -301,6 +287,8 @@
|
|||||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||||
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
||||||
|
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Composer.swift"; sourceTree = "<group>"; };
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
|
||||||
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
|
C4D89BC52783C99400A02B68 /* ComposerJson.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerJson.swift; sourceTree = "<group>"; };
|
||||||
C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
|
C4D9ADBE277610E1007277F4 /* PhpSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpSwitcher.swift; sourceTree = "<group>"; };
|
||||||
@ -333,13 +321,6 @@
|
|||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
C415D3D32770F341005EF286 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
C41C1B3022B0097F00E7CF16 /* Frameworks */ = {
|
C41C1B3022B0097F00E7CF16 /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -365,6 +346,7 @@
|
|||||||
5420395826135DC100FB00FA /* PrefsVC.swift */,
|
5420395826135DC100FB00FA /* PrefsVC.swift */,
|
||||||
5420395E2613607600FB00FA /* Preferences.swift */,
|
5420395E2613607600FB00FA /* Preferences.swift */,
|
||||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
|
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */,
|
||||||
|
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */,
|
||||||
C4DEB7D327A5D60B00834718 /* Stats.swift */,
|
C4DEB7D327A5D60B00834718 /* Stats.swift */,
|
||||||
C41CD0272628D8E20065BBED /* Keybinds */,
|
C41CD0272628D8E20065BBED /* Keybinds */,
|
||||||
54FCFD28276C88C0004CE748 /* Views */,
|
54FCFD28276C88C0004CE748 /* Views */,
|
||||||
@ -388,8 +370,10 @@
|
|||||||
54FCFD28276C88C0004CE748 /* Views */ = {
|
54FCFD28276C88C0004CE748 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
54FCFD25276C883F004CE748 /* CheckboxPreferenceView.xib */,
|
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */,
|
||||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */,
|
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */,
|
||||||
|
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */,
|
||||||
|
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */,
|
||||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */,
|
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */,
|
||||||
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */,
|
54FCFD2F276C8DA4004CE748 /* HotkeyPreferenceView.swift */,
|
||||||
);
|
);
|
||||||
@ -431,26 +415,16 @@
|
|||||||
path = Core;
|
path = Core;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C415D3D72770F341005EF286 /* phpmon-cli */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
C415D3E52770F540005EF286 /* main.swift */,
|
|
||||||
C415D3DE2770F34D005EF286 /* AllowedArguments.swift */,
|
|
||||||
);
|
|
||||||
path = "phpmon-cli";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
C41C1B2A22B0097F00E7CF16 = {
|
C41C1B2A22B0097F00E7CF16 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
||||||
C4E713562570150F00007428 /* SECURITY.md */,
|
C4E713562570150F00007428 /* SECURITY.md */,
|
||||||
C4F7807425D7F7E5000DBC97 /* RELEASE.md */,
|
C4F7807425D7F7E5000DBC97 /* RELEASE.md */,
|
||||||
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
||||||
C4E713572570151400007428 /* docs */,
|
C4E713572570151400007428 /* docs */,
|
||||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||||
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
||||||
C415D3D72770F341005EF286 /* phpmon-cli */,
|
|
||||||
C4B5853A2770FE2500DA4FBE /* phpmon-common */,
|
|
||||||
C41C1B3422B0097F00E7CF16 /* Products */,
|
C41C1B3422B0097F00E7CF16 /* Products */,
|
||||||
C4D309E72770EF2F00958BCF /* Frameworks */,
|
C4D309E72770EF2F00958BCF /* Frameworks */,
|
||||||
);
|
);
|
||||||
@ -461,7 +435,6 @@
|
|||||||
children = (
|
children = (
|
||||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */,
|
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */,
|
||||||
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */,
|
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */,
|
||||||
C415D3D62770F341005EF286 /* phpmon-cli */,
|
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -469,6 +442,7 @@
|
|||||||
C41C1B3522B0097F00E7CF16 /* phpmon */ = {
|
C41C1B3522B0097F00E7CF16 /* phpmon */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C4B5853A2770FE2500DA4FBE /* Common */,
|
||||||
C41E181722CB61EB0072CF09 /* Domain */,
|
C41E181722CB61EB0072CF09 /* Domain */,
|
||||||
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
||||||
C4232EE42612526500158FC6 /* Credits.html */,
|
C4232EE42612526500158FC6 /* Credits.html */,
|
||||||
@ -492,15 +466,13 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
||||||
C4B13B1D25C4915000548C3A /* Core */,
|
C4B13B1D25C4915000548C3A /* App */,
|
||||||
C4D9ADBD27761084007277F4 /* PHP */,
|
C4D9ADBD27761084007277F4 /* PHP */,
|
||||||
C47331A0247093AC009A0597 /* Menu */,
|
C47331A0247093AC009A0597 /* Menu */,
|
||||||
C464ADAA275A7A25003FCD53 /* SiteList */,
|
C464ADAA275A7A25003FCD53 /* SiteList */,
|
||||||
5420395726135DB800FB00FA /* Preferences */,
|
5420395726135DB800FB00FA /* Preferences */,
|
||||||
C44C198F276E3A380072762D /* Progress */,
|
C44C198F276E3A380072762D /* Progress */,
|
||||||
C4C8E81D276F5686003AC782 /* Watcher */,
|
C4C8E81D276F5686003AC782 /* Watcher */,
|
||||||
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
|
||||||
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
|
||||||
C4EE55B027708BB2001DF387 /* SwiftUI */,
|
C4EE55B027708BB2001DF387 /* SwiftUI */,
|
||||||
);
|
);
|
||||||
path = Domain;
|
path = Domain;
|
||||||
@ -515,6 +487,15 @@
|
|||||||
path = Progress;
|
path = Progress;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C44CCD4327AFE93300CE40E5 /* Errors */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C44CCD3F27AFE2FC00CE40E5 /* AlertableError.swift */,
|
||||||
|
C4927F0A27B2DFC200C55AFD /* Errors.swift */,
|
||||||
|
);
|
||||||
|
path = Errors;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -532,7 +513,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
||||||
|
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
||||||
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
||||||
|
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */,
|
||||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
||||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
||||||
@ -553,7 +537,6 @@
|
|||||||
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
C474B00524C0E98C00066A22 /* LocalNotification.swift */,
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
||||||
54AB03252763858F00A29D5F /* Timer.swift */,
|
|
||||||
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
|
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
|
||||||
C4EC1E6C279DF87A0010F296 /* Async.swift */,
|
C4EC1E6C279DF87A0010F296 /* Async.swift */,
|
||||||
);
|
);
|
||||||
@ -595,7 +578,7 @@
|
|||||||
path = Homebrew;
|
path = Homebrew;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C4B13B1D25C4915000548C3A /* Core */ = {
|
C4B13B1D25C4915000548C3A /* App */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
|
C41C1B3C22B0098000E7CF16 /* Main.storyboard */,
|
||||||
@ -609,16 +592,19 @@
|
|||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||||
);
|
);
|
||||||
path = Core;
|
path = App;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C4B5853A2770FE2500DA4FBE /* phpmon-common */ = {
|
C4B5853A2770FE2500DA4FBE /* Common */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C40C7F2127721F7300DDDCDC /* Core */,
|
C40C7F2127721F7300DDDCDC /* Core */,
|
||||||
54B20EDF263AA22C00D3250E /* PHP */,
|
54B20EDF263AA22C00D3250E /* PHP */,
|
||||||
|
C44CCD4327AFE93300CE40E5 /* Errors */,
|
||||||
|
C4F8C0A222D4F100002EFE61 /* Extensions */,
|
||||||
|
C4811D2822D70D9C00B5F6B3 /* Helpers */,
|
||||||
);
|
);
|
||||||
path = "phpmon-common";
|
path = Common;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
||||||
@ -715,23 +701,6 @@
|
|||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
C415D3D52770F341005EF286 /* phpmon-cli */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */;
|
|
||||||
buildPhases = (
|
|
||||||
C415D3D22770F341005EF286 /* Sources */,
|
|
||||||
C415D3D32770F341005EF286 /* Frameworks */,
|
|
||||||
C415D3D42770F341005EF286 /* CopyFiles */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = "phpmon-cli";
|
|
||||||
productName = "phpmon-cli";
|
|
||||||
productReference = C415D3D62770F341005EF286 /* phpmon-cli */;
|
|
||||||
productType = "com.apple.product-type.tool";
|
|
||||||
};
|
|
||||||
C41C1B3222B0097F00E7CF16 /* PHP Monitor */ = {
|
C41C1B3222B0097F00E7CF16 /* PHP Monitor */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = C41C1B4322B0098000E7CF16 /* Build configuration list for PBXNativeTarget "PHP Monitor" */;
|
buildConfigurationList = C41C1B4322B0098000E7CF16 /* Build configuration list for PBXNativeTarget "PHP Monitor" */;
|
||||||
@ -780,9 +749,6 @@
|
|||||||
LastUpgradeCheck = 1320;
|
LastUpgradeCheck = 1320;
|
||||||
ORGANIZATIONNAME = "Nico Verbruggen";
|
ORGANIZATIONNAME = "Nico Verbruggen";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
C415D3D52770F341005EF286 = {
|
|
||||||
CreatedOnToolsVersion = 13.2.1;
|
|
||||||
};
|
|
||||||
C41C1B3222B0097F00E7CF16 = {
|
C41C1B3222B0097F00E7CF16 = {
|
||||||
CreatedOnToolsVersion = 10.2.1;
|
CreatedOnToolsVersion = 10.2.1;
|
||||||
};
|
};
|
||||||
@ -810,7 +776,6 @@
|
|||||||
targets = (
|
targets = (
|
||||||
C41C1B3222B0097F00E7CF16 /* PHP Monitor */,
|
C41C1B3222B0097F00E7CF16 /* PHP Monitor */,
|
||||||
C4F7807825D7F84B000DBC97 /* phpmon-tests */,
|
C4F7807825D7F84B000DBC97 /* phpmon-tests */,
|
||||||
C415D3D52770F341005EF286 /* phpmon-cli */,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@ -827,9 +792,10 @@
|
|||||||
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
|
C405A4D124B9B9140062FAFA /* InternetAccessPolicy.plist in Resources */,
|
||||||
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
C44C1991276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||||
C4232EE52612526500158FC6 /* Credits.html in Resources */,
|
C4232EE52612526500158FC6 /* Credits.html in Resources */,
|
||||||
54FCFD26276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */,
|
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
|
C473319F2470923A009A0597 /* Localizable.strings in Resources */,
|
||||||
C4F30B07278E195800755FCE /* brew-services.json in Resources */,
|
C4F30B07278E195800755FCE /* brew-services.json in Resources */,
|
||||||
|
C4068CA427B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */,
|
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */,
|
||||||
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
54FCFD2D276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||||
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
|
C405A4D024B9B9140062FAFA /* InternetAccessPolicy.strings in Resources */,
|
||||||
@ -841,9 +807,10 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
54FCFD27276C883F004CE748 /* CheckboxPreferenceView.xib in Resources */,
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||||
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
||||||
|
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */,
|
C43A8A2025D9D1D700591B77 /* brew.json in Resources */,
|
||||||
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */,
|
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */,
|
||||||
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
C44C1992276E44CB0072762D /* ProgressWindow.storyboard in Resources */,
|
||||||
@ -854,33 +821,6 @@
|
|||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
C415D3D22770F341005EF286 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
C40C7F2427721F8200DDDCDC /* PhpExtension.swift in Sources */,
|
|
||||||
C40C7F2627721FA200DDDCDC /* Constants.swift in Sources */,
|
|
||||||
C4B585402770FE3900DA4FBE /* Paths.swift in Sources */,
|
|
||||||
C415D3E62770F540005EF286 /* main.swift in Sources */,
|
|
||||||
C40B24F327A310780018C7D2 /* Events.swift in Sources */,
|
|
||||||
C40C7F2227721F8200DDDCDC /* PhpInstallation.swift in Sources */,
|
|
||||||
C4B585432770FE3900DA4FBE /* Shell.swift in Sources */,
|
|
||||||
C4D9ADC1277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
|
||||||
C4F30B05278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
|
||||||
C4EC1E6E279DF87A0010F296 /* Async.swift in Sources */,
|
|
||||||
C40C7F2327721F8200DDDCDC /* ActivePhpInstallation.swift in Sources */,
|
|
||||||
C4B585462770FE3900DA4FBE /* Command.swift in Sources */,
|
|
||||||
C4D9ADCA277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
|
||||||
C48D6C72279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
|
||||||
C40C7F2527721F9800DDDCDC /* HomebrewPackage.swift in Sources */,
|
|
||||||
C417DC76277614690015E6EE /* Helpers.swift in Sources */,
|
|
||||||
C40C7F3227722E8D00DDDCDC /* Logger.swift in Sources */,
|
|
||||||
C40C7F2B2772201C00DDDCDC /* Actions.swift in Sources */,
|
|
||||||
C415D3E12770F34D005EF286 /* AllowedArguments.swift in Sources */,
|
|
||||||
C40C7F202772136000DDDCDC /* PhpEnv.swift in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
C41C1B2F22B0097F00E7CF16 /* Sources */ = {
|
C41C1B2F22B0097F00E7CF16 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -894,6 +834,7 @@
|
|||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
|
||||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
|
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
||||||
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
@ -914,6 +855,7 @@
|
|||||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */,
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */,
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */,
|
||||||
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
C4B97B78275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||||
|
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||||
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415937F27A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
@ -926,7 +868,7 @@
|
|||||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC74277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E82770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
@ -934,9 +876,12 @@
|
|||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
||||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */,
|
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */,
|
||||||
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
||||||
|
C4927F0B27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||||
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853E2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */,
|
||||||
|
C44CCD4027AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
||||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||||
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
|
C4EED88927A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||||
@ -944,6 +889,7 @@
|
|||||||
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
||||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
||||||
|
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */,
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||||
@ -967,7 +913,6 @@
|
|||||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||||
54AB03272763858F00A29D5F /* Timer.swift in Sources */,
|
|
||||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||||
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B82770F294005EF286 /* Actions.swift in Sources */,
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */,
|
||||||
@ -976,12 +921,14 @@
|
|||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
|
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */,
|
||||||
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
C40C7F2927721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
@ -1000,15 +947,19 @@
|
|||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||||
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */,
|
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */,
|
||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
||||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
||||||
|
C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */,
|
||||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
||||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||||
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
||||||
|
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
|
||||||
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD31276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||||
@ -1027,6 +978,8 @@
|
|||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
||||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
||||||
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||||
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
@ -1065,34 +1018,6 @@
|
|||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
C415D3DB2770F341005EF286 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
|
||||||
CODE_SIGN_IDENTITY = "-";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
C415D3DC2770F341005EF286 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
|
|
||||||
CODE_SIGN_IDENTITY = "-";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
SWIFT_VERSION = 5.0;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
C41C1B4122B0098000E7CF16 /* Debug */ = {
|
C41C1B4122B0098000E7CF16 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@ -1218,7 +1143,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 560;
|
CURRENT_PROJECT_VERSION = 610;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@ -1227,7 +1152,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.0;
|
MARKETING_VERSION = 5.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -1243,7 +1168,7 @@
|
|||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 560;
|
CURRENT_PROJECT_VERSION = 610;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@ -1252,7 +1177,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.0;
|
MARKETING_VERSION = 5.0.1;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@ -1305,15 +1230,6 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
C415D3DA2770F341005EF286 /* Build configuration list for PBXNativeTarget "phpmon-cli" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
C415D3DB2770F341005EF286 /* Debug */,
|
|
||||||
C415D3DC2770F341005EF286 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
C41C1B2E22B0097F00E7CF16 /* Build configuration list for PBXProject "PHP Monitor" */ = {
|
C41C1B2E22B0097F00E7CF16 /* Build configuration list for PBXProject "PHP Monitor" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1320"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
|
||||||
BuildableName = "phpmon-cli"
|
|
||||||
BlueprintName = "phpmon-cli"
|
|
||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
|
||||||
BuildableName = "phpmon-cli"
|
|
||||||
BlueprintName = "phpmon-cli"
|
|
||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<CommandLineArguments>
|
|
||||||
<CommandLineArgument
|
|
||||||
argument = "help"
|
|
||||||
isEnabled = "YES">
|
|
||||||
</CommandLineArgument>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "C415D3D52770F341005EF286"
|
|
||||||
BuildableName = "phpmon-cli"
|
|
||||||
BlueprintName = "phpmon-cli"
|
|
||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
17
README.md
@ -320,9 +320,9 @@ You can always still ask Valet using the command line, should it be necessary. I
|
|||||||
<details>
|
<details>
|
||||||
<summary><strong>After running PHP Monitor, Homebrew sometimes has issues with `brew upgrade` or `brew cleanup`!</strong></summary>
|
<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>
|
</details>
|
||||||
|
|
||||||
@ -399,15 +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.
|
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.
|
||||||
|
|
||||||
## 🔧 Build instructions
|
For more detailed information for developers, please see [the documentation file for developers](./DEVS.md).
|
||||||
|
|
||||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
|
||||||
|
|
||||||
If you'd like to build PHP Monitor yourself, you need:
|
|
||||||
|
|
||||||
* Xcode (usually the latest version)
|
|
||||||
* The contents of this repository
|
|
||||||
|
|
||||||
Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you should be able to immediately build the app for your system by pressing Cmd-R. This will create a debug build. (If Xcode complains about code signing, you can turn it off.)
|
|
||||||
|
|
||||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
|
||||||
|
BIN
assets/icons.afdesign
Normal file
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// AllowedArguments.swift
|
|
||||||
// phpmon-cli
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 20/12/2021.
|
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
enum AllowedArguments: String, CaseIterable {
|
|
||||||
case use = "use"
|
|
||||||
case performSwitch = "switch"
|
|
||||||
case fix = "fix"
|
|
||||||
case help = "help"
|
|
||||||
|
|
||||||
static func has(_ string: String) -> Bool {
|
|
||||||
return Self.allCases.contains { arg in
|
|
||||||
return arg.rawValue == string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static var rawValues: [String] {
|
|
||||||
return Self.allCases.map { $0.rawValue }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,103 +0,0 @@
|
|||||||
//
|
|
||||||
// main.swift
|
|
||||||
// phpmon-cli
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 20/12/2021.
|
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
let toolver = "0.1 (early access)"
|
|
||||||
|
|
||||||
let log = Log.shared
|
|
||||||
log.verbosity = .info
|
|
||||||
|
|
||||||
if CommandLine.arguments.contains("-q") || CommandLine.arguments.contains("--quiet") {
|
|
||||||
Log.shared.verbosity = .warning
|
|
||||||
}
|
|
||||||
if CommandLine.arguments.contains("-p") || CommandLine.arguments.contains("--performance") {
|
|
||||||
Log.shared.verbosity = .performance
|
|
||||||
}
|
|
||||||
|
|
||||||
var argument = "help"
|
|
||||||
if CommandLine.arguments.count > 1 {
|
|
||||||
argument = CommandLine.arguments[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !AllowedArguments.has(argument) {
|
|
||||||
Log.err("The supported arguments are: \(AllowedArguments.rawValues)")
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let action = AllowedArguments.init(rawValue: argument)
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case .use, .performSwitch:
|
|
||||||
if !Shell.fileExists("\(Paths.binPath)/php") {
|
|
||||||
Log.err("PHP is currently not linked. Attempting quick fix...")
|
|
||||||
_ = Shell.user.executeSynchronously("brew link php", requiresPath: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
let phpenv = PhpEnv.shared
|
|
||||||
PhpEnv.detectPhpVersions()
|
|
||||||
|
|
||||||
if CommandLine.arguments.count < 3 {
|
|
||||||
Log.err("You must enter at least two additional arguments when using this command.")
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
let version = CommandLine.arguments[2].replacingOccurrences(of: "php@", with: "")
|
|
||||||
if phpenv.availablePhpVersions.contains(version) {
|
|
||||||
Log.info("Switching to PHP \(version)...")
|
|
||||||
Actions.switchToPhpVersion(
|
|
||||||
version: version,
|
|
||||||
availableVersions: phpenv.availablePhpVersions,
|
|
||||||
completed: {
|
|
||||||
Log.info("The switch has been completed.")
|
|
||||||
exit(0)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Log.err("A PHP installation with version \(version) is not installed.")
|
|
||||||
Log.err("The installed versions are: \(phpenv.availablePhpVersions.joined(separator: ", ")).")
|
|
||||||
Log.err("If this version is available, you may be able to install it by using `brew install php@\(version)`.")
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
case .fix:
|
|
||||||
Log.info("Fixing your PHP installation...")
|
|
||||||
Actions.fixMyPhp()
|
|
||||||
Log.info("All operations completed. You can check which version of PHP is linked by using `php -v`.")
|
|
||||||
exit(0)
|
|
||||||
case .help:
|
|
||||||
print("""
|
|
||||||
===============================================================
|
|
||||||
PHP MONITOR CLI \(toolver)
|
|
||||||
by Nico Verbruggen
|
|
||||||
===============================================================
|
|
||||||
|
|
||||||
Gives access to the quick version switcher from PHP Monitor,
|
|
||||||
but without the GUI and 100% of the speed!
|
|
||||||
|
|
||||||
SUPPORTED COMMANDS
|
|
||||||
|
|
||||||
* use {version}: Switch to a specific version of PHP.
|
|
||||||
(e.g. `phpmon-cli use 8.0`)
|
|
||||||
* switch {version}: Alias for the `use` command.
|
|
||||||
* fix Attempts to unlink all PHP versions,
|
|
||||||
and link the latest version of PHP.
|
|
||||||
* help: Show this help.
|
|
||||||
|
|
||||||
SUPPORTED FLAGS
|
|
||||||
|
|
||||||
* `-q / --quiet`: Silences all logs except for warnings and exceptions.
|
|
||||||
* `-p / --perf`: Enables performance mode.
|
|
||||||
|
|
||||||
""")
|
|
||||||
exit(0)
|
|
||||||
case .none:
|
|
||||||
Log.err("Action not recognized!")
|
|
||||||
exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
RunLoop.main.run()
|
|
@ -11,6 +11,10 @@ import XCTest
|
|||||||
class PhpVersionNumberTest: XCTestCase {
|
class PhpVersionNumberTest: XCTestCase {
|
||||||
|
|
||||||
func testCanDeconstructPhpVersion() throws {
|
func testCanDeconstructPhpVersion() throws {
|
||||||
|
XCTAssertEqual(
|
||||||
|
try! PhpVersionNumber.parse("PHP 8.1.0RC5-dev"),
|
||||||
|
PhpVersionNumber(major: 8, minor: 1, patch: 0)
|
||||||
|
)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumber.make(from: "8.0.11"),
|
PhpVersionNumber.make(from: "8.0.11"),
|
||||||
PhpVersionNumber(major: 8, minor: 0, patch: 11)
|
PhpVersionNumber(major: 8, minor: 0, patch: 11)
|
||||||
@ -29,6 +33,12 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testPhpVersionNumberParse() throws {
|
||||||
|
XCTAssertThrowsError(try PhpVersionNumber.parse("OOF")) { error in
|
||||||
|
XCTAssertTrue(error is VersionParseError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testCanCheckFixedConstraints() throws {
|
func testCanCheckFixedConstraints() throws {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "Linked.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "link.svg",
|
"filename" : "Linked@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
BIN
phpmon/Assets.xcassets/IconLinked.imageset/Linked.png
vendored
Normal file
After Width: | Height: | Size: 978 B |
BIN
phpmon/Assets.xcassets/IconLinked.imageset/Linked@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M326.612 185.391c59.747 59.809 58.927 155.698.36 214.59-.11.12-.24.25-.36.37l-67.2 67.2c-59.27 59.27-155.699 59.262-214.96 0-59.27-59.26-59.27-155.7 0-214.96l37.106-37.106c9.84-9.84 26.786-3.3 27.294 10.606.648 17.722 3.826 35.527 9.69 52.721 1.986 5.822.567 12.262-3.783 16.612l-13.087 13.087c-28.026 28.026-28.905 73.66-1.155 101.96 28.024 28.579 74.086 28.749 102.325.51l67.2-67.19c28.191-28.191 28.073-73.757 0-101.83-3.701-3.694-7.429-6.564-10.341-8.569a16.037 16.037 0 0 1-6.947-12.606c-.396-10.567 3.348-21.456 11.698-29.806l21.054-21.055c5.521-5.521 14.182-6.199 20.584-1.731a152.482 152.482 0 0 1 20.522 17.197zM467.547 44.449c-59.261-59.262-155.69-59.27-214.96 0l-67.2 67.2c-.12.12-.25.25-.36.37-58.566 58.892-59.387 154.781.36 214.59a152.454 152.454 0 0 0 20.521 17.196c6.402 4.468 15.064 3.789 20.584-1.731l21.054-21.055c8.35-8.35 12.094-19.239 11.698-29.806a16.037 16.037 0 0 0-6.947-12.606c-2.912-2.005-6.64-4.875-10.341-8.569-28.073-28.073-28.191-73.639 0-101.83l67.2-67.19c28.239-28.239 74.3-28.069 102.325.51 27.75 28.3 26.872 73.934-1.155 101.96l-13.087 13.087c-4.35 4.35-5.769 10.79-3.783 16.612 5.864 17.194 9.042 34.999 9.69 52.721.509 13.906 17.454 20.446 27.294 10.606l37.106-37.106c59.271-59.259 59.271-155.699.001-214.959z"/></svg>
|
|
Before Width: | Height: | Size: 1.5 KiB |
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "Parked.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "car-alt.svg",
|
"filename" : "Parked@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
BIN
phpmon/Assets.xcassets/IconParked.imageset/Parked.png
vendored
Normal file
After Width: | Height: | Size: 868 B |
BIN
phpmon/Assets.xcassets/IconParked.imageset/Parked@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 512"><!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M438.66 212.33l-11.24-28.1-19.93-49.83C390.38 91.63 349.57 64 303.5 64h-127c-46.06 0-86.88 27.63-103.99 70.4l-19.93 49.83-11.24 28.1C17.22 221.5 0 244.66 0 272v48c0 16.12 6.16 30.67 16 41.93V416c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-32h256v32c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32v-54.07c9.84-11.25 16-25.8 16-41.93v-48c0-27.34-17.22-50.5-41.34-59.67zm-306.73-54.16c7.29-18.22 24.94-30.17 44.57-30.17h127c19.63 0 37.28 11.95 44.57 30.17L368 208H112l19.93-49.83zM80 319.8c-19.2 0-32-12.76-32-31.9S60.8 256 80 256s48 28.71 48 47.85-28.8 15.95-48 15.95zm320 0c-19.2 0-48 3.19-48-15.95S380.8 256 400 256s32 12.76 32 31.9-12.8 31.9-32 31.9z"/></svg>
|
|
Before Width: | Height: | Size: 918 B |
6
phpmon/Assets.xcassets/Menu Bar Icons/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
22
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Menu Bar Elephant.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Menu Bar Elephant@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Menu Bar Elephant.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_elephant.imageset/Menu Bar Elephant@2x.png
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "Menu Bar.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "php@2x.png",
|
"filename" : "Menu Bar@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_php.imageset/Menu Bar.png
vendored
Normal file
After Width: | Height: | Size: 658 B |
BIN
phpmon/Assets.xcassets/Menu Bar Icons/MenuBar_php.imageset/Menu Bar@2x.png
vendored
Normal file
After Width: | Height: | Size: 793 B |
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "ServiceLoading.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "question-circle.svg",
|
"filename" : "ServiceLoading@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
BIN
phpmon/Assets.xcassets/ServiceLoading.imageset/ServiceLoading.png
vendored
Normal file
After Width: | Height: | Size: 854 B |
BIN
phpmon/Assets.xcassets/ServiceLoading.imageset/ServiceLoading@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 448c-110.532 0-200-89.431-200-200 0-110.495 89.472-200 200-200 110.491 0 200 89.471 200 200 0 110.53-89.431 200-200 200zm107.244-255.2c0 67.052-72.421 68.084-72.421 92.863V300c0 6.627-5.373 12-12 12h-45.647c-6.627 0-12-5.373-12-12v-8.659c0-35.745 27.1-50.034 47.579-61.516 17.561-9.845 28.324-16.541 28.324-29.579 0-17.246-21.999-28.693-39.784-28.693-23.189 0-33.894 10.977-48.942 29.969-4.057 5.12-11.46 6.071-16.666 2.124l-27.824-21.098c-5.107-3.872-6.251-11.066-2.644-16.363C184.846 131.491 214.94 112 261.794 112c49.071 0 101.45 38.304 101.45 88.8zM298 368c0 23.159-18.841 42-42 42s-42-18.841-42-42 18.841-42 42-42 42 18.841 42 42z"/></svg>
|
|
Before Width: | Height: | Size: 966 B |
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "ServiceOff.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "times-circle.svg",
|
"filename" : "ServiceOff@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
BIN
phpmon/Assets.xcassets/ServiceOff.imageset/ServiceOff.png
vendored
Normal file
After Width: | Height: | Size: 826 B |
BIN
phpmon/Assets.xcassets/ServiceOff.imageset/ServiceOff@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm0 448c-110.5 0-200-89.5-200-200S145.5 56 256 56s200 89.5 200 200-89.5 200-200 200zm101.8-262.2L295.6 256l62.2 62.2c4.7 4.7 4.7 12.3 0 17l-22.6 22.6c-4.7 4.7-12.3 4.7-17 0L256 295.6l-62.2 62.2c-4.7 4.7-12.3 4.7-17 0l-22.6-22.6c-4.7-4.7-4.7-12.3 0-17l62.2-62.2-62.2-62.2c-4.7-4.7-4.7-12.3 0-17l22.6-22.6c4.7-4.7 12.3-4.7 17 0l62.2 62.2 62.2-62.2c4.7-4.7 12.3-4.7 17 0l22.6 22.6c4.7 4.7 4.7 12.3 0 17z"/></svg>
|
|
Before Width: | Height: | Size: 685 B |
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "ServiceOn.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "check-circle.svg",
|
"filename" : "ServiceOn@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
|
BIN
phpmon/Assets.xcassets/ServiceOn.imageset/ServiceOn.png
vendored
Normal file
After Width: | Height: | Size: 819 B |
BIN
phpmon/Assets.xcassets/ServiceOn.imageset/ServiceOn@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 48c110.532 0 200 89.451 200 200 0 110.532-89.451 200-200 200-110.532 0-200-89.451-200-200 0-110.532 89.451-200 200-200m140.204 130.267l-22.536-22.718c-4.667-4.705-12.265-4.736-16.97-.068L215.346 303.697l-59.792-60.277c-4.667-4.705-12.265-4.736-16.97-.069l-22.719 22.536c-4.705 4.667-4.736 12.265-.068 16.971l90.781 91.516c4.667 4.705 12.265 4.736 16.97.068l172.589-171.204c4.704-4.668 4.734-12.266.067-16.971z"/></svg>
|
|
Before Width: | Height: | Size: 718 B |
Before Width: | Height: | Size: 940 B |
@ -34,17 +34,39 @@ class Actions {
|
|||||||
brew("services stop dnsmasq", sudo: true)
|
brew("services stop dnsmasq", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static func fixHomebrewPermissions() throws
|
||||||
Kindly asks Valet to switch to a specific PHP version.
|
{
|
||||||
*/
|
var servicesCommands = [
|
||||||
public static func switchToPhpVersionUsingValet(
|
"\(Paths.brew) services stop nginx",
|
||||||
version: String,
|
"\(Paths.brew) services stop dnsmasq",
|
||||||
availableVersions: [String],
|
]
|
||||||
completed: @escaping () -> Void
|
var cellarCommands = [
|
||||||
) {
|
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/nginx",
|
||||||
Log.info("Switching to \(version) using Valet")
|
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/dnsmasq"
|
||||||
Log.info(valet("use php@\(version)"))
|
]
|
||||||
completed()
|
|
||||||
|
PhpEnv.shared.availablePhpVersions.forEach { version in
|
||||||
|
let formula = version == PhpEnv.brewPhpVersion
|
||||||
|
? "php"
|
||||||
|
: "php@\(version)"
|
||||||
|
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||||
|
cellarCommands.append("chown -R \(Paths.whoami):staff \(Paths.cellarPath)/\(formula)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let script =
|
||||||
|
servicesCommands.joined(separator: " && ")
|
||||||
|
+ " && "
|
||||||
|
+ cellarCommands.joined(separator: " && ")
|
||||||
|
|
||||||
|
let appleScript = NSAppleScript(
|
||||||
|
source: "do shell script \"\(script)\" with administrator privileges"
|
||||||
|
)
|
||||||
|
|
||||||
|
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
|
||||||
|
|
||||||
|
if (eventResult == nil) {
|
||||||
|
throw HomebrewPermissionError(kind: .applescriptNilError)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Finding Config Files
|
// MARK: - Finding Config Files
|
@ -27,13 +27,13 @@ class Log {
|
|||||||
|
|
||||||
static func err(_ item: Any) {
|
static func err(_ item: Any) {
|
||||||
if Verbosity.error.isApplicable() {
|
if Verbosity.error.isApplicable() {
|
||||||
print(item)
|
print("[ERR] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func warn(_ item: Any) {
|
static func warn(_ item: Any) {
|
||||||
if Verbosity.warning.isApplicable() {
|
if Verbosity.warning.isApplicable() {
|
||||||
print(item)
|
print("[WRN] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ public class Paths {
|
|||||||
private var userName : String
|
private var userName : String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
baseDir = Shell.fileExists("\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
baseDir = FileManager.default.fileExists(atPath: "\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
||||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +48,10 @@ public class Paths {
|
|||||||
return shared.userName
|
return shared.userName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static var cellarPath: String {
|
||||||
|
return "\(shared.baseDir.rawValue)/Cellar"
|
||||||
|
}
|
||||||
|
|
||||||
public static var binPath: String {
|
public static var binPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/bin"
|
return "\(shared.baseDir.rawValue)/bin"
|
||||||
}
|
}
|
@ -105,12 +105,12 @@ public class Shell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a file exists at the provided path.
|
Checks if a file exists at a certain path.
|
||||||
Uses `/bin/echo` instead of the `builtin` (which does not support `-n`).
|
Used to be done with a shell command, now uses the native FileManager class instead.
|
||||||
*/
|
*/
|
||||||
public static func fileExists(_ path: String) -> Bool {
|
public static func fileExists(_ path: String) -> Bool {
|
||||||
let escapedPath = path.replacingOccurrences(of: " ", with: "\\ ")
|
let fullPath = path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
||||||
return Shell.pipe("if [ -f \(escapedPath) ]; then /bin/echo -n \"0\"; fi") == "0"
|
return FileManager.default.fileExists(atPath: fullPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
13
phpmon/Common/Errors/AlertableError.swift
Normal 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
|
||||||
|
}
|
29
phpmon/Common/Errors/Errors.swift
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
// VersionParseError.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// MARK: - Alertable Errors
|
||||||
|
// These errors must be resolved by the user.
|
||||||
|
|
||||||
|
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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Errors that do not have an associated alert message
|
||||||
|
// The errors must be resolved by the developer.
|
||||||
|
|
||||||
|
struct VersionParseError: Error {}
|
@ -51,6 +51,9 @@ class Alert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notify the user about something by showing an alert.
|
||||||
|
*/
|
||||||
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
||||||
_ = present(
|
_ = present(
|
||||||
messageText: message,
|
messageText: message,
|
||||||
@ -60,4 +63,19 @@ class Alert {
|
|||||||
style: style
|
style: style
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Notify the user about a particular error (which must be `Alertable`)
|
||||||
|
by showing an alert.
|
||||||
|
*/
|
||||||
|
public static func notify(about error: Error & AlertableError) {
|
||||||
|
let key = error.getErrorMessageKey()
|
||||||
|
_ = present(
|
||||||
|
messageText: "\(key).title".localized,
|
||||||
|
informativeText: "\(key).description".localized,
|
||||||
|
buttonTitle: "OK",
|
||||||
|
secondButtonTitle: "",
|
||||||
|
style: .critical
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -42,7 +42,7 @@ class MenuBarImageGenerator {
|
|||||||
|
|
||||||
let targetImage: NSImage = NSImage(size: image.size)
|
let targetImage: NSImage = NSImage(size: image.size)
|
||||||
|
|
||||||
let rep: NSBitmapImageRep = NSBitmapImageRep(
|
let representation: NSBitmapImageRep = NSBitmapImageRep(
|
||||||
bitmapDataPlanes: nil,
|
bitmapDataPlanes: nil,
|
||||||
pixelsWide: Int(image.size.width),
|
pixelsWide: Int(image.size.width),
|
||||||
pixelsHigh: Int(image.size.height),
|
pixelsHigh: Int(image.size.height),
|
||||||
@ -55,7 +55,7 @@ class MenuBarImageGenerator {
|
|||||||
bitsPerPixel: 0
|
bitsPerPixel: 0
|
||||||
)!
|
)!
|
||||||
|
|
||||||
targetImage.addRepresentation(rep)
|
targetImage.addRepresentation(representation)
|
||||||
targetImage.lockFocus()
|
targetImage.lockFocus()
|
||||||
|
|
||||||
image.draw(in: imageRect)
|
image.draw(in: imageRect)
|
||||||
@ -69,32 +69,69 @@ class MenuBarImageGenerator {
|
|||||||
The same as before, but also attempts to add an icon to the left.
|
The same as before, but also attempts to add an icon to the left.
|
||||||
*/
|
*/
|
||||||
public static func textToImageWithIcon(text: String) -> NSImage {
|
public static func textToImageWithIcon(text: String) -> NSImage {
|
||||||
let textImage = self.textToImage(text: text)
|
|
||||||
let iconImage = NSImage(named: "StatusBarPHP")!
|
|
||||||
let iconWidthSize = iconImage.size.width
|
|
||||||
let divider = iconWidthSize
|
|
||||||
|
|
||||||
|
// We'll start out with the image containing the text
|
||||||
|
let textImage = self.textToImage(text: text)
|
||||||
|
|
||||||
|
// Then we'll fetch the image we want on the left
|
||||||
|
var iconType = Preferences.preferences[.iconTypeToDisplay] as? String
|
||||||
|
if iconType == nil {
|
||||||
|
Log.warn("Invalid icon type found, using the default")
|
||||||
|
iconType = MenuBarIcon.iconPhp.rawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconImage = NSImage(named: "MenuBar_\(iconType!)")!
|
||||||
|
|
||||||
|
// We'll need to reference the width of the icon a bunch of times
|
||||||
|
let iconWidthSize = iconImage.size.width
|
||||||
|
|
||||||
|
// There will also be an additional divider between the image and the text (image)
|
||||||
|
let divider: CGFloat = 3
|
||||||
|
|
||||||
|
// Use a fixed size for the height of the menu bar (18pt)
|
||||||
let imageRect = CGRect(
|
let imageRect = CGRect(
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
width: textImage.size.width + divider,
|
width: textImage.size.width + iconWidthSize + divider,
|
||||||
height: textImage.size.height
|
height: 18
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Create a new image, we'll draw the text and our icon in there
|
||||||
let image: NSImage = NSImage(size: imageRect.size)
|
let image: NSImage = NSImage(size: imageRect.size)
|
||||||
image.lockFocus()
|
image.lockFocus()
|
||||||
|
|
||||||
let difference = imageRect.size.width - textImage.size.width
|
// Calculate the offset between the image and the text
|
||||||
|
let offset = imageRect.size.width - textImage.size.width
|
||||||
|
|
||||||
textImage.draw(in: imageRect, from: NSRect(
|
// Draw the text with a negative x offset (so there is room on the left for the icon)
|
||||||
x: -difference,
|
textImage.draw(
|
||||||
y: 0, width: textImage.size.width + difference,
|
in: imageRect,
|
||||||
height: textImage.size.height
|
from: NSRect(
|
||||||
), operation: .overlay, fraction: 1)
|
x: -offset,
|
||||||
|
y: 0,
|
||||||
|
width: textImage.size.width + offset,
|
||||||
|
height: textImage.size.height
|
||||||
|
),
|
||||||
|
operation: .overlay,
|
||||||
|
fraction: 1
|
||||||
|
)
|
||||||
|
|
||||||
iconImage.draw(in: imageRect, from: NSRect(x: 0, y: 0, width: imageRect.size.width * 1.6, height: imageRect.size.height * 2.0), operation: .overlay, fraction: 1)
|
// Draw the icon directly in the left of the imageRect (where we left space)
|
||||||
|
iconImage.draw(
|
||||||
|
in: imageRect,
|
||||||
|
from: NSRect(
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: imageRect.size.width,
|
||||||
|
height: imageRect.size.height
|
||||||
|
),
|
||||||
|
operation: .overlay,
|
||||||
|
fraction: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// We're done with this image
|
||||||
image.unlockFocus()
|
image.unlockFocus()
|
||||||
|
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ class VersionExtractor {
|
|||||||
public static func from(_ string: String) -> String? {
|
public static func from(_ string: String) -> String? {
|
||||||
do {
|
do {
|
||||||
let regex = try NSRegularExpression(
|
let regex = try NSRegularExpression(
|
||||||
pattern: #"Laravel Valet (?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
pattern: #"(?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
||||||
options: []
|
options: []
|
||||||
)
|
)
|
||||||
|
|
@ -8,11 +8,6 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol PhpSwitcherDelegate: AnyObject {
|
|
||||||
func switcherDidStartSwitching()
|
|
||||||
func switcherDidCompleteSwitch()
|
|
||||||
}
|
|
||||||
|
|
||||||
class PhpEnv {
|
class PhpEnv {
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
@ -161,7 +156,7 @@ class PhpEnv {
|
|||||||
*/
|
*/
|
||||||
public func validate(_ version: String) -> Bool {
|
public func validate(_ version: String) -> Bool {
|
||||||
if self.currentInstall.version.short == version {
|
if self.currentInstall.version.short == version {
|
||||||
print("Switching to version \(version) seems to have succeeded. Validation passed.")
|
Log.info("Switching to version \(version) seems to have succeeded. Validation passed.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -118,6 +118,14 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func parse(_ text: String) throws -> Self {
|
||||||
|
guard let versionText = VersionExtractor.from(text) else {
|
||||||
|
throw VersionParseError()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Self.make(from: versionText)!
|
||||||
|
}
|
||||||
|
|
||||||
public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? {
|
public static func make(from versionString: String, type: MatchType = .versionOnly) -> Self? {
|
||||||
let regex = try! NSRegularExpression(pattern: type.rawValue, options: [])
|
let regex = try! NSRegularExpression(pattern: type.rawValue, options: [])
|
||||||
let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first
|
let match = regex.matches(in: versionString, options: [], range: NSMakeRange(0, versionString.count)).first
|
@ -27,9 +27,10 @@ class PhpInstallation {
|
|||||||
arguments: ["--version"]
|
arguments: ["--version"]
|
||||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
self.longVersion = PhpVersionNumber.make(
|
// The parser should always work, or the string has to be very unusual.
|
||||||
from: String(longVersionString.split(separator: "-")[0])
|
// If so, the app SHOULD crash, so that the users report what's up.
|
||||||
)!
|
// TODO: Alert the user that the version number could not be parsed.
|
||||||
|
self.longVersion = try! PhpVersionNumber.parse(longVersionString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,14 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
protocol PhpSwitcherDelegate: AnyObject {
|
||||||
|
|
||||||
|
func switcherDidStartSwitching(to: String)
|
||||||
|
|
||||||
|
func switcherDidCompleteSwitch(to: String)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protocol PhpSwitcher {
|
protocol PhpSwitcher {
|
||||||
|
|
||||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
func performSwitch(to version: String, completion: @escaping () -> Void)
|
@ -8,7 +8,7 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
import HotKey
|
||||||
|
|
||||||
class App: PhpSwitcherDelegate {
|
class App {
|
||||||
|
|
||||||
// MARK: Static Vars
|
// MARK: Static Vars
|
||||||
|
|
||||||
@ -73,20 +73,4 @@ class App: PhpSwitcherDelegate {
|
|||||||
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
The `PhpConfigWatcher` is responsible for watching the `.ini` files and the `.conf.d` folder.
|
||||||
*/
|
*/
|
||||||
var watcher: PhpConfigWatcher!
|
var watcher: PhpConfigWatcher!
|
||||||
|
|
||||||
// MARK: - PhpSwitcherDelegate
|
|
||||||
|
|
||||||
func switcherDidStartSwitching() {
|
|
||||||
}
|
|
||||||
|
|
||||||
func switcherDidCompleteSwitch() {
|
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
|
||||||
handlePhpConfigWatcher()
|
|
||||||
|
|
||||||
if let window = siteListWindowController {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window.contentVC.reloadSites()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -45,12 +45,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
let valet: Valet
|
let valet: Valet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The PhpSwitcher singleton that handles PHP version
|
The PhpEnv singleton that handles PHP version
|
||||||
detection, as well as switching. It is initialized
|
detection, as well as switching. It is initialized
|
||||||
when the app is ready and passed all checks.
|
when the app is ready and passed all checks.
|
||||||
*/
|
*/
|
||||||
var switcher: PhpEnv! = nil
|
var phpEnvironment: PhpEnv! = nil
|
||||||
|
|
||||||
|
/**
|
||||||
|
The logger is responsible for different levels of logging.
|
||||||
|
You can tweak the verbosity in the `init` method here.
|
||||||
|
*/
|
||||||
var logger = Log.shared
|
var logger = Log.shared
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
@ -59,7 +63,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
When the application initializes, create all singletons.
|
When the application initializes, create all singletons.
|
||||||
*/
|
*/
|
||||||
override init() {
|
override init() {
|
||||||
logger.verbosity = .performance
|
logger.verbosity = .info
|
||||||
Log.info("==================================")
|
Log.info("==================================")
|
||||||
Log.info("PHP MONITOR by Nico Verbruggen")
|
Log.info("PHP MONITOR by Nico Verbruggen")
|
||||||
Log.info("Version \(App.version)")
|
Log.info("Version \(App.version)")
|
||||||
@ -73,8 +77,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initializeSwitcher() {
|
func initializeSwitcher() {
|
||||||
self.switcher = PhpEnv.shared
|
self.phpEnvironment = PhpEnv.shared
|
||||||
self.switcher.delegate = self.state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
@ -771,7 +771,7 @@ Gw
|
|||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
<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"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
||||||
@ -808,7 +808,7 @@ Gw
|
|||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="Checkmark" width="512" height="512"/>
|
<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="Lock" width="30" height="30"/>
|
||||||
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
|
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
|
||||||
<image name="plus" catalog="system" width="14" height="13"/>
|
<image name="plus" catalog="system" width="14" height="13"/>
|
@ -49,14 +49,6 @@ class Startup {
|
|||||||
breaking: true
|
breaking: true
|
||||||
)
|
)
|
||||||
|
|
||||||
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
|
||||||
performEnvironmentCheck(
|
|
||||||
Valet.shared.version == nil,
|
|
||||||
messageText: "startup.errors.valet_version_unknown.title".localized,
|
|
||||||
informativeText: "startup.errors.valet_version_unknown.desc".localized,
|
|
||||||
breaking: true
|
|
||||||
)
|
|
||||||
|
|
||||||
performEnvironmentCheck(
|
performEnvironmentCheck(
|
||||||
HomebrewDiagnostics.cannotLoadService(),
|
HomebrewDiagnostics.cannotLoadService(),
|
||||||
messageText: "startup.errors.services_json_error.title".localized,
|
messageText: "startup.errors.services_json_error.title".localized,
|
||||||
@ -81,12 +73,13 @@ class Startup {
|
|||||||
breaking: true
|
breaking: true
|
||||||
)
|
)
|
||||||
|
|
||||||
let services = Shell.pipe("\(Paths.brew) services list | grep php")
|
// Determine the Valet version only AFTER confirming the correct permission is in place
|
||||||
|
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
||||||
performEnvironmentCheck(
|
performEnvironmentCheck(
|
||||||
(services.countInstances(of: "started") > 1),
|
Valet.shared.version == nil,
|
||||||
messageText: "startup.errors.services.title".localized,
|
messageText: "startup.errors.valet_version_unknown.title".localized,
|
||||||
informativeText: "startup.errors.services.desc".localized,
|
informativeText: "startup.errors.valet_version_unknown.desc".localized,
|
||||||
breaking: false
|
breaking: true
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!failed) {
|
if (!failed) {
|
@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// BenchmarkTimer.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 10/12/2021.
|
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class BenchmarkTimer {
|
|
||||||
let startTime: CFAbsoluteTime
|
|
||||||
var endTime: CFAbsoluteTime?
|
|
||||||
|
|
||||||
init() {
|
|
||||||
startTime = CFAbsoluteTimeGetCurrent()
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop() -> CFAbsoluteTime {
|
|
||||||
endTime = CFAbsoluteTimeGetCurrent()
|
|
||||||
|
|
||||||
return duration!
|
|
||||||
}
|
|
||||||
|
|
||||||
var duration: CFAbsoluteTime? {
|
|
||||||
if let endTime = endTime {
|
|
||||||
return endTime - startTime
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -163,7 +163,7 @@ class Valet {
|
|||||||
|
|
||||||
// We should also check that we can interpret the path correctly
|
// We should also check that we can interpret the path correctly
|
||||||
if URL(fileURLWithPath: siteDir).lastPathComponent == "" {
|
if URL(fileURLWithPath: siteDir).lastPathComponent == "" {
|
||||||
print("Warning: could not parse the site: \(siteDir), skipping!")
|
Log.warn("Could not parse the site: \(siteDir), skipping!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17701"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customView id="c22-O7-iKe" customClass="HeaderView" customModule="PHP_Monitor" customModuleProvider="target">
|
<customView id="c22-O7-iKe" customClass="HeaderView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="350" height="24"/>
|
<rect key="frame" x="0.0" y="0.0" width="270" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ddg-VQ-cOT">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ddg-VQ-cOT">
|
||||||
@ -29,7 +29,7 @@
|
|||||||
<connections>
|
<connections>
|
||||||
<outlet property="textField" destination="ddg-VQ-cOT" id="aaQ-Xb-o2X"/>
|
<outlet property="textField" destination="ddg-VQ-cOT" id="aaQ-Xb-o2X"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-75" y="38"/>
|
<point key="canvasLocation" x="177" y="105"/>
|
||||||
</customView>
|
</customView>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
96
phpmon/Domain/Menu/MainMenu+Async.swift
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+Async.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 06/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension MainMenu {
|
||||||
|
|
||||||
|
// MARK: - Nicer callbacks
|
||||||
|
|
||||||
|
enum AsyncBehaviour {
|
||||||
|
case setsBusyUI
|
||||||
|
case reloadsPhpInstallation
|
||||||
|
case updatesMenuBarContents
|
||||||
|
case broadcastServicesUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attempts asynchronous execution of a callback that may throw an `Error`.
|
||||||
|
While the callback is being executed, the UI will be marked as busy.
|
||||||
|
|
||||||
|
(Preferably, if an `Error` is thrown, it should also be an `AlertableError`,
|
||||||
|
which will make presenting errors easier.)
|
||||||
|
|
||||||
|
- Parameter execute: Required callback of the work that needs to happen.
|
||||||
|
|
||||||
|
- Parameter success: Optional callback that is fired when all was OK.
|
||||||
|
- Parameter failure: Optional callback that is fired when an `Error` was thrown.
|
||||||
|
- Parameter behaviours: Various behaviours that can be tweaked, but usually best left to the default.
|
||||||
|
The default will set the UI to busy, reload PHP info, update the menu bar,
|
||||||
|
and broadcast to the services view that the list has been updated.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
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
|
||||||
|
if behaviours.contains(.reloadsPhpInstallation) {
|
||||||
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
|
}
|
||||||
|
|
||||||
|
if behaviours.contains(.updatesMenuBarContents) {
|
||||||
|
updatePhpVersionInStatusBar()
|
||||||
|
} else if behaviours.contains(.setsBusyUI) {
|
||||||
|
refreshIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
if behaviours.contains(.broadcastServicesUpdate) {
|
||||||
|
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
error == nil ? success() : failure(error!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func asyncWithBusyUI(
|
||||||
|
_ execute: @escaping () throws -> Void,
|
||||||
|
completion: @escaping () -> Void = {}
|
||||||
|
) {
|
||||||
|
asyncExecution({
|
||||||
|
try! execute()
|
||||||
|
}, success: {
|
||||||
|
completion()
|
||||||
|
}, behaviours: [.setsBusyUI])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
98
phpmon/Domain/Menu/MainMenu+Composer.swift
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+Composer.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension MainMenu {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Updates the global dependencies and runs the completion callback when done.
|
||||||
|
This method should probably be broken up into several smaller methods at some point.
|
||||||
|
*/
|
||||||
|
func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
||||||
|
if !Shell.fileExists("/usr/local/bin/composer") {
|
||||||
|
Alert.notify(
|
||||||
|
message: "alert.composer_missing.title".localized,
|
||||||
|
info: "alert.composer_missing.info".localized
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PhpEnv.shared.isBusy = true
|
||||||
|
setBusyImage()
|
||||||
|
self.rebuild()
|
||||||
|
|
||||||
|
let noLongerBusy = {
|
||||||
|
PhpEnv.shared.isBusy = false
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
self.updatePhpVersionInStatusBar()
|
||||||
|
self.rebuild()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var window: ProgressWindowController? = ProgressWindowController.display(
|
||||||
|
title: "alert.composer_progress.title".localized,
|
||||||
|
description: "alert.composer_progress.info".localized
|
||||||
|
)
|
||||||
|
window?.setType(info: true)
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
let task = Shell.user.createTask(
|
||||||
|
for: "/usr/local/bin/composer global update", requiresPath: true
|
||||||
|
)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
window?.addToConsole("/usr/local/bin/composer global update\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
Shell.captureOutput(
|
||||||
|
task,
|
||||||
|
didReceiveStdOutData: { string in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
window?.addToConsole(string)
|
||||||
|
}
|
||||||
|
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||||
|
},
|
||||||
|
didReceiveStdErrData: { string in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
window?.addToConsole(string)
|
||||||
|
}
|
||||||
|
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
|
Shell.haltCapturingOutput(task)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if task.terminationStatus <= 0 {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||||
|
window?.close()
|
||||||
|
if (notify) {
|
||||||
|
LocalNotification.send(
|
||||||
|
title: "alert.composer_success.title".localized,
|
||||||
|
subtitle: "alert.composer_success.info".localized
|
||||||
|
)
|
||||||
|
}
|
||||||
|
window = nil
|
||||||
|
noLongerBusy()
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window?.setType(info: false)
|
||||||
|
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
||||||
|
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
||||||
|
window = nil
|
||||||
|
noLongerBusy()
|
||||||
|
completion(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
phpmon/Domain/Menu/MainMenu+Switcher.swift
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+PhpSwitcherDelegate.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension MainMenu {
|
||||||
|
|
||||||
|
// MARK: - PhpSwitcherDelegate
|
||||||
|
|
||||||
|
func switcherDidStartSwitching(to version: String) {}
|
||||||
|
|
||||||
|
func switcherDidCompleteSwitch(to version: String) {
|
||||||
|
// Update the PHP version
|
||||||
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
|
|
||||||
|
// Ensure the config watcher gets reloaded
|
||||||
|
App.shared.handlePhpConfigWatcher()
|
||||||
|
|
||||||
|
// Mark as no longer busy
|
||||||
|
PhpEnv.shared.isBusy = false
|
||||||
|
|
||||||
|
// Reload the site list
|
||||||
|
self.reloadSiteListData()
|
||||||
|
|
||||||
|
// Perform UI updates on main thread
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
updatePhpVersionInStatusBar()
|
||||||
|
rebuild()
|
||||||
|
|
||||||
|
if !PhpEnv.shared.validate(version) {
|
||||||
|
self.suggestFixMyValet(failed: version)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run composer updates
|
||||||
|
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
||||||
|
self.updateGlobalDependencies(notify: false, completion: { _ in
|
||||||
|
self.notifyAboutVersionChange(to: version)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
self.notifyAboutVersionChange(to: version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update stats
|
||||||
|
Stats.incrementSuccessfulSwitchCount()
|
||||||
|
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func suggestFixMyValet(failed version: String) {
|
||||||
|
let outcome = Alert.present(
|
||||||
|
messageText: "alert.php_switch_failed.title".localized(version),
|
||||||
|
informativeText: "alert.php_switch_failed.info".localized(version),
|
||||||
|
buttonTitle: "alert.php_switch_failed.confirm".localized,
|
||||||
|
secondButtonTitle: "alert.php_switch_failed.cancel".localized, style: .informational)
|
||||||
|
if outcome {
|
||||||
|
MainMenu.shared.fixMyValet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reloadSiteListData() {
|
||||||
|
if let window = App.shared.siteListWindowController {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
window.contentVC.reloadSites()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Valet.shared.reloadSites()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func notifyAboutVersionChange(to version: String) {
|
||||||
|
LocalNotification.send(
|
||||||
|
title: String(format: "notification.version_changed_title".localized, version),
|
||||||
|
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
||||||
|
)
|
||||||
|
|
||||||
|
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate {
|
||||||
|
|
||||||
static let shared = MainMenu()
|
static let shared = MainMenu()
|
||||||
|
|
||||||
@ -23,52 +23,67 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
// MARK: - UI related
|
// MARK: - UI related
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Update the menu's contents, based on what's going on.
|
Rebuilds the menu (either asynchronously or synchronously).
|
||||||
This will rebuild the entire menu, so this can take a few moments.
|
Defaults to rebuilding the menu asynchronously.
|
||||||
*/
|
*/
|
||||||
func rebuild() {
|
func rebuild(async: Bool = true) {
|
||||||
|
if !async {
|
||||||
|
self.rebuildMenu()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Update the menu item on the main thread
|
// Update the menu item on the main thread
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
// Create a new menu
|
self.rebuildMenu()
|
||||||
let menu = StatusMenu()
|
|
||||||
|
|
||||||
// Add the PHP versions (or error messages)
|
|
||||||
menu.addPhpVersionMenuItems()
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
|
||||||
|
|
||||||
// Add the possible actions
|
|
||||||
menu.addPhpActionMenuItems()
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
|
||||||
|
|
||||||
// Add Valet interactions
|
|
||||||
menu.addValetMenuItems()
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
|
||||||
|
|
||||||
// Add services
|
|
||||||
menu.addPhpConfigurationMenuItems()
|
|
||||||
menu.addItem(NSMenuItem.separator())
|
|
||||||
|
|
||||||
// Add about & quit menu items
|
|
||||||
menu.addItem(NSMenuItem(title: "mi_preferences".localized, action: #selector(openPrefs), keyEquivalent: ","))
|
|
||||||
menu.addItem(NSMenuItem(title: "mi_about".localized, action: #selector(openAbout), keyEquivalent: ""))
|
|
||||||
menu.addItem(NSMenuItem(title: "mi_quit".localized, action: #selector(terminateApp), keyEquivalent: "q"))
|
|
||||||
|
|
||||||
// Make sure every item can be interacted with
|
|
||||||
menu.items.forEach({ (item) in
|
|
||||||
item.target = self
|
|
||||||
})
|
|
||||||
|
|
||||||
statusItem.menu = menu
|
|
||||||
statusItem.menu?.delegate = self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Update the menu's contents, based on what's going on.
|
||||||
|
This will rebuild the entire menu, so this can take a few moments.
|
||||||
|
|
||||||
|
Use `rebuild(async:)` to ensure the rebuilding happens in the background.
|
||||||
|
*/
|
||||||
|
private func rebuildMenu() {
|
||||||
|
// Create a new menu
|
||||||
|
let menu = StatusMenu()
|
||||||
|
|
||||||
|
// Add the PHP versions (or error messages)
|
||||||
|
menu.addPhpVersionMenuItems()
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
// Add the possible actions
|
||||||
|
menu.addPhpActionMenuItems()
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
// Add Valet interactions
|
||||||
|
menu.addValetMenuItems()
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
// Add services
|
||||||
|
menu.addPhpConfigurationMenuItems()
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
// Add about & quit menu items
|
||||||
|
menu.addItem(NSMenuItem(title: "mi_preferences".localized, action: #selector(openPrefs), keyEquivalent: ","))
|
||||||
|
menu.addItem(NSMenuItem(title: "mi_about".localized, action: #selector(openAbout), keyEquivalent: ""))
|
||||||
|
menu.addItem(NSMenuItem(title: "mi_quit".localized, action: #selector(terminateApp), keyEquivalent: "q"))
|
||||||
|
|
||||||
|
// Make sure every item can be interacted with
|
||||||
|
menu.items.forEach({ (item) in
|
||||||
|
item.target = self
|
||||||
|
})
|
||||||
|
|
||||||
|
statusItem.menu = menu
|
||||||
|
statusItem.menu?.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets the status bar image based on a version string.
|
Sets the status bar image based on a version string.
|
||||||
*/
|
*/
|
||||||
func setStatusBarImage(version: String) {
|
func setStatusBarImage(version: String) {
|
||||||
setStatusBar(
|
setStatusBar(
|
||||||
image: Preferences.isEnabled(.shouldDisplayPhpHintInIcon)
|
image: (Preferences.preferences[.iconTypeToDisplay] as! String != MenuBarIcon.noIcon.rawValue)
|
||||||
? MenuBarImageGenerator.textToImageWithIcon(text: version)
|
? MenuBarImageGenerator.textToImageWithIcon(text: version)
|
||||||
: MenuBarImageGenerator.textToImage(text: version)
|
: MenuBarImageGenerator.textToImage(text: version)
|
||||||
)
|
)
|
||||||
@ -85,35 +100,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Nicer callbacks
|
|
||||||
|
|
||||||
/**
|
|
||||||
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.
|
|
||||||
|
|
||||||
- Parameter execute: Callback of the work that needs to happen.
|
|
||||||
- Parameter completion: Callback that is fired when the work is done.
|
|
||||||
*/
|
|
||||||
private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
|
||||||
{
|
|
||||||
PhpEnv.shared.isBusy = true
|
|
||||||
setBusyImage()
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
|
||||||
execute()
|
|
||||||
PhpEnv.shared.isBusy = false
|
|
||||||
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
|
||||||
updatePhpVersionInStatusBar()
|
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - User Interface
|
// MARK: - User Interface
|
||||||
|
|
||||||
|
/** Reloads which PHP versions is currently active. */
|
||||||
@objc func refreshActiveInstallation() {
|
@objc func refreshActiveInstallation() {
|
||||||
if !PhpEnv.shared.isBusy {
|
if !PhpEnv.shared.isBusy {
|
||||||
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
PhpEnv.shared.currentInstall = ActivePhpInstallation()
|
||||||
@ -123,12 +112,38 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates the icon (refresh icon) and rebuilds the menu. */
|
||||||
@objc func updatePhpVersionInStatusBar() {
|
@objc func updatePhpVersionInStatusBar() {
|
||||||
refreshIcon()
|
refreshIcon()
|
||||||
rebuild()
|
rebuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshIcon() {
|
/**
|
||||||
|
Reloads the menu in the foreground.
|
||||||
|
This mimics the exact behaviours of `asyncExecution` as set in the method below.
|
||||||
|
*/
|
||||||
|
@objc func reloadPhpMonitorMenuInForeground() {
|
||||||
|
refreshActiveInstallation()
|
||||||
|
refreshIcon()
|
||||||
|
rebuild(async: false)
|
||||||
|
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reloads the menu in the background, using `asyncExecution`. */
|
||||||
|
@objc func reloadPhpMonitorMenuInBackground() {
|
||||||
|
asyncExecution({
|
||||||
|
// This automatically reloads the menu
|
||||||
|
Log.info("Reloading information about the PHP installation (in the background)...")
|
||||||
|
}, behaviours: [
|
||||||
|
.setsBusyUI,
|
||||||
|
.reloadsPhpInstallation,
|
||||||
|
.broadcastServicesUpdate,
|
||||||
|
.updatesMenuBarContents
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Refreshes the icon with the PHP version. */
|
||||||
|
@objc func refreshIcon() {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
if (App.busy) {
|
if (App.busy) {
|
||||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||||
@ -145,20 +160,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func reloadPhpMonitorMenuInBackground() {
|
/** Updates the icon to be displayed as busy. */
|
||||||
waitAndExecute {
|
|
||||||
// This automatically reloads the menu
|
|
||||||
Log.info("Reloading information about the PHP installation (in the background)...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func reloadPhpMonitorMenu() {
|
|
||||||
waitAndExecute {
|
|
||||||
// This automatically reloads the menu
|
|
||||||
Log.info("Reloading information about the PHP installation...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func setBusyImage() {
|
@objc func setBusyImage() {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||||
@ -167,18 +169,42 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
|
|
||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
|
@objc func 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
|
||||||
|
Alert.notify(about: error as! HomebrewPermissionError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func restartPhpFpm() {
|
@objc func restartPhpFpm() {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
Actions.restartPhpFpm()
|
Actions.restartPhpFpm()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func restartAllServices() {
|
@objc func restartAllServices() {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
Actions.restartDnsMasq()
|
Actions.restartDnsMasq()
|
||||||
Actions.restartPhpFpm()
|
Actions.restartPhpFpm()
|
||||||
Actions.restartNginx()
|
Actions.restartNginx()
|
||||||
} completion: {
|
} success: {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
LocalNotification.send(
|
LocalNotification.send(
|
||||||
title: "notification.services_restarted".localized,
|
title: "notification.services_restarted".localized,
|
||||||
@ -189,9 +215,9 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func stopAllServices() {
|
@objc func stopAllServices() {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
Actions.stopAllServices()
|
Actions.stopAllServices()
|
||||||
} completion: {
|
} success: {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
LocalNotification.send(
|
LocalNotification.send(
|
||||||
title: "notification.services_stopped".localized,
|
title: "notification.services_stopped".localized,
|
||||||
@ -202,19 +228,19 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func restartNginx() {
|
@objc func restartNginx() {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
Actions.restartNginx()
|
Actions.restartNginx()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func restartDnsMasq() {
|
@objc func restartDnsMasq() {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
Actions.restartDnsMasq()
|
Actions.restartDnsMasq()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
@objc func toggleExtension(sender: ExtensionMenuItem) {
|
||||||
waitAndExecute {
|
asyncExecution {
|
||||||
sender.phpExtension?.toggle()
|
sender.phpExtension?.toggle()
|
||||||
|
|
||||||
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
if Preferences.isEnabled(.autoServiceRestartAfterExtensionToggle) {
|
||||||
@ -226,34 +252,32 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
@objc func openPhpInfo() {
|
@objc func openPhpInfo() {
|
||||||
var url: URL? = nil
|
var url: URL? = nil
|
||||||
|
|
||||||
waitAndExecute {
|
asyncWithBusyUI {
|
||||||
url = Actions.createTempPhpInfoFile()
|
url = Actions.createTempPhpInfoFile()
|
||||||
} completion: {
|
} completion: {
|
||||||
// When this has been completed, open the URL to the file in the browser
|
if url != nil { NSWorkspace.shared.open(url!) }
|
||||||
NSWorkspace.shared.open(url!)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func fixMyValet() {
|
@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,
|
messageText: "alert.fix_my_valet.title".localized,
|
||||||
informativeText: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion),
|
informativeText: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion),
|
||||||
buttonTitle: "alert.fix_my_valet.ok".localized,
|
buttonTitle: "alert.fix_my_valet.ok".localized,
|
||||||
secondButtonTitle: "alert.fix_my_valet.cancel".localized,
|
secondButtonTitle: "alert.fix_my_valet.cancel".localized,
|
||||||
style: .warning
|
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")
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,70 +308,26 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
self.switchToPhpVersion(sender.version)
|
self.switchToPhpVersion(sender.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (5.1): Investigate if `waitAndExecute` cannot be used here
|
|
||||||
@objc func switchToPhpVersion(_ version: String) {
|
@objc func switchToPhpVersion(_ version: String) {
|
||||||
setBusyImage()
|
setBusyImage()
|
||||||
PhpEnv.shared.isBusy = true
|
PhpEnv.shared.isBusy = true
|
||||||
|
PhpEnv.shared.delegate = self
|
||||||
|
PhpEnv.shared.delegate?.switcherDidStartSwitching(to: version)
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
// Update the PHP version in the status bar
|
|
||||||
updatePhpVersionInStatusBar()
|
updatePhpVersionInStatusBar()
|
||||||
|
|
||||||
// Update the menu
|
|
||||||
rebuild()
|
rebuild()
|
||||||
|
|
||||||
let sendLocalNotification = {
|
|
||||||
LocalNotification.send(
|
|
||||||
title: String(format: "notification.version_changed_title".localized, version),
|
|
||||||
subtitle: String(format: "notification.version_changed_desc".localized, version)
|
|
||||||
)
|
|
||||||
PhpEnv.phpInstall.notifyAboutBrokenPhpFpm()
|
|
||||||
}
|
|
||||||
|
|
||||||
let completion = {
|
|
||||||
// Fire off the delegate method
|
|
||||||
PhpEnv.shared.delegate?.switcherDidCompleteSwitch()
|
|
||||||
|
|
||||||
// Mark as no longer busy
|
|
||||||
PhpEnv.shared.isBusy = false
|
|
||||||
|
|
||||||
// Perform UI updates on main thread
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
updatePhpVersionInStatusBar()
|
|
||||||
rebuild()
|
|
||||||
|
|
||||||
if !PhpEnv.shared.validate(version) {
|
|
||||||
let outcome = Alert.present(
|
|
||||||
messageText: "alert.php_switch_failed.title".localized(version),
|
|
||||||
informativeText: "alert.php_switch_failed.info".localized(version),
|
|
||||||
buttonTitle: "alert.php_switch_failed.confirm".localized,
|
|
||||||
secondButtonTitle: "alert.php_switch_failed.cancel".localized, style: .informational)
|
|
||||||
if outcome {
|
|
||||||
MainMenu.shared.fixMyValet()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run composer updates
|
|
||||||
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
|
||||||
self.updateGlobalDependencies(notify: false, completion: { _ in sendLocalNotification() })
|
|
||||||
} else {
|
|
||||||
sendLocalNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update stats
|
|
||||||
Stats.incrementSuccessfulSwitchCount()
|
|
||||||
Stats.evaluateSponsorMessageShouldBeDisplayed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PhpEnv.switcher.performSwitch(
|
PhpEnv.switcher.performSwitch(
|
||||||
to: version,
|
to: version,
|
||||||
completion: completion
|
completion: {
|
||||||
|
PhpEnv.shared.delegate?.switcherDidCompleteSwitch(to: version)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Menu Item Functionality
|
||||||
|
|
||||||
@objc func openAbout() {
|
@objc func openAbout() {
|
||||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||||
NSApplication.shared.orderFrontStandardAboutPanel()
|
NSApplication.shared.orderFrontStandardAboutPanel()
|
||||||
@ -380,91 +360,4 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate {
|
|||||||
// When the menu is closed, allow the shortcut to work again
|
// When the menu is closed, allow the shortcut to work again
|
||||||
App.shared.shortcutHotkey?.isPaused = false
|
App.shared.shortcutHotkey?.isPaused = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private Methods
|
|
||||||
|
|
||||||
/**
|
|
||||||
Updates the global dependencies and runs the completion callback when done.
|
|
||||||
*/
|
|
||||||
private func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
|
||||||
if !Shell.fileExists("/usr/local/bin/composer") {
|
|
||||||
Alert.notify(
|
|
||||||
message: "alert.composer_missing.title".localized,
|
|
||||||
info: "alert.composer_missing.info".localized
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
PhpEnv.shared.isBusy = true
|
|
||||||
setBusyImage()
|
|
||||||
self.rebuild()
|
|
||||||
|
|
||||||
let noLongerBusy = {
|
|
||||||
PhpEnv.shared.isBusy = false
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
self.updatePhpVersionInStatusBar()
|
|
||||||
self.rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var window: ProgressWindowController? = ProgressWindowController.display(
|
|
||||||
title: "alert.composer_progress.title".localized,
|
|
||||||
description: "alert.composer_progress.info".localized
|
|
||||||
)
|
|
||||||
window?.setType(info: true)
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
let task = Shell.user.createTask(
|
|
||||||
for: "/usr/local/bin/composer global update", requiresPath: true
|
|
||||||
)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole("/usr/local/bin/composer global update\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
Shell.captureOutput(
|
|
||||||
task,
|
|
||||||
didReceiveStdOutData: { string in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole(string)
|
|
||||||
}
|
|
||||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
|
||||||
},
|
|
||||||
didReceiveStdErrData: { string in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole(string)
|
|
||||||
}
|
|
||||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
task.launch()
|
|
||||||
task.waitUntilExit()
|
|
||||||
Shell.haltCapturingOutput(task)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if task.terminationStatus <= 0 {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
|
||||||
window?.close()
|
|
||||||
if (notify) {
|
|
||||||
LocalNotification.send(
|
|
||||||
title: "alert.composer_success.title".localized,
|
|
||||||
subtitle: "alert.composer_success.info".localized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
window = nil
|
|
||||||
noLongerBusy()
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
window?.setType(info: false)
|
|
||||||
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
|
||||||
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
|
||||||
window = nil
|
|
||||||
noLongerBusy()
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,18 +9,18 @@
|
|||||||
<customObject id="-2" userLabel="File's Owner"/>
|
<customObject id="-2" userLabel="File's Owner"/>
|
||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customView id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
|
<customView wantsLayer="YES" id="c22-O7-iKe" customClass="ServicesView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="340" height="46"/>
|
<rect key="frame" x="0.0" y="0.0" width="330" height="46"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
||||||
<rect key="frame" x="30" y="3" width="280" height="40"/>
|
<rect key="frame" x="30" y="3" width="270" height="40"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
||||||
<rect key="frame" x="0.0" y="4" width="80" height="32"/>
|
<rect key="frame" x="0.0" y="4" width="77" height="32"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="At1-ch-qv2">
|
||||||
<rect key="frame" x="25" y="18" width="31" height="14"/>
|
<rect key="frame" x="23" y="18" width="31" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="PHP" id="LKe-C4-jxo">
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="PHP" id="LKe-C4-jxo">
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -28,7 +28,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tko-cP-XSz">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tko-cP-XSz">
|
||||||
<rect key="frame" x="28" y="0.0" width="24" height="16"/>
|
<rect key="frame" x="26" y="0.0" width="24" height="16"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="16" id="Fxu-6h-A2h"/>
|
<constraint firstAttribute="height" constant="16" id="Fxu-6h-A2h"/>
|
||||||
<constraint firstAttribute="width" constant="24" id="hOc-Ur-dmA"/>
|
<constraint firstAttribute="width" constant="24" id="hOc-Ur-dmA"/>
|
||||||
@ -47,10 +47,10 @@
|
|||||||
</customSpacing>
|
</customSpacing>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
||||||
<rect key="frame" x="100" y="4" width="80" height="32"/>
|
<rect key="frame" x="97" y="4" width="76" height="32"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
||||||
<rect key="frame" x="20" y="18" width="40" height="14"/>
|
<rect key="frame" x="18" y="18" width="40" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="NGINX" id="Qfq-Bl-yuh">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="NGINX" id="Qfq-Bl-yuh">
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -58,7 +58,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZqW-6d-vpe">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZqW-6d-vpe">
|
||||||
<rect key="frame" x="32" y="0.0" width="16" height="16"/>
|
<rect key="frame" x="30" y="0.0" width="16" height="16"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="16" id="EPG-jm-7Xs"/>
|
<constraint firstAttribute="height" constant="16" id="EPG-jm-7Xs"/>
|
||||||
<constraint firstAttribute="width" constant="16" id="iif-kT-phn"/>
|
<constraint firstAttribute="width" constant="16" id="iif-kT-phn"/>
|
||||||
@ -77,10 +77,10 @@
|
|||||||
</customSpacing>
|
</customSpacing>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
||||||
<rect key="frame" x="200" y="4" width="80" height="32"/>
|
<rect key="frame" x="193" y="4" width="77" height="32"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
||||||
<rect key="frame" x="9" y="18" width="62" height="14"/>
|
<rect key="frame" x="8" y="18" width="62" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="DNSMASQ" id="lGh-MT-TgI">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="DNSMASQ" id="lGh-MT-TgI">
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -88,7 +88,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DcG-x3-lvy">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="DcG-x3-lvy">
|
||||||
<rect key="frame" x="32" y="0.0" width="16" height="16"/>
|
<rect key="frame" x="31" y="0.0" width="16" height="16"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="16" id="AKl-Gq-RtM"/>
|
<constraint firstAttribute="width" constant="16" id="AKl-Gq-RtM"/>
|
||||||
<constraint firstAttribute="height" constant="16" id="q2g-Ua-eIJ"/>
|
<constraint firstAttribute="height" constant="16" id="q2g-Ua-eIJ"/>
|
||||||
@ -145,6 +145,6 @@
|
|||||||
</customView>
|
</customView>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="ServiceLoading" width="512" height="512"/>
|
<image name="ServiceLoading" width="17" height="16"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
@ -10,11 +10,11 @@
|
|||||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
<customView id="c22-O7-iKe" customClass="StatsView" customModule="PHP_Monitor" customModuleProvider="target">
|
<customView id="c22-O7-iKe" customClass="StatsView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="340" height="55"/>
|
<rect key="frame" x="0.0" y="0.0" width="330" height="55"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" spacing="20" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="TnH-dX-qaQ">
|
||||||
<rect key="frame" x="30" y="6" width="280" height="43"/>
|
<rect key="frame" x="30" y="6" width="270" height="43"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="doH-ww-BDw">
|
||||||
<rect key="frame" x="0.0" y="4" width="87" height="35"/>
|
<rect key="frame" x="0.0" y="4" width="87" height="35"/>
|
||||||
@ -46,10 +46,10 @@
|
|||||||
</customSpacing>
|
</customSpacing>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
<stackView distribution="fillEqually" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g4d-4N-NkC">
|
||||||
<rect key="frame" x="107" y="4" width="77" height="35"/>
|
<rect key="frame" x="107" y="4" width="68" height="35"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7um-XA-djV">
|
||||||
<rect key="frame" x="7" y="21" width="63" height="14"/>
|
<rect key="frame" x="3" y="21" width="63" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="MAX POST" id="Qfq-Bl-yuh">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="MAX POST" id="Qfq-Bl-yuh">
|
||||||
<font key="font" metaFont="systemMedium" size="11"/>
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -57,7 +57,7 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vyu-AO-8SH">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Vyu-AO-8SH">
|
||||||
<rect key="frame" x="11" y="0.0" width="55" height="19"/>
|
<rect key="frame" x="7" y="0.0" width="55" height="19"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="uH4-Zy-43x">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="1024M" id="uH4-Zy-43x">
|
||||||
<font key="font" metaFont="systemMedium" size="16"/>
|
<font key="font" metaFont="systemMedium" size="16"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
</customSpacing>
|
</customSpacing>
|
||||||
</stackView>
|
</stackView>
|
||||||
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
<stackView distribution="fill" orientation="vertical" alignment="centerX" spacing="2" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nWj-33-m8Q">
|
||||||
<rect key="frame" x="204" y="4" width="76" height="35"/>
|
<rect key="frame" x="195" y="4" width="75" height="35"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Oef-6n-9QI">
|
||||||
<rect key="frame" x="-2" y="21" width="79" height="14"/>
|
<rect key="frame" x="-2" y="21" width="79" height="14"/>
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class StatusMenu : NSMenu {
|
class StatusMenu : NSMenu {
|
||||||
|
|
||||||
func addPhpVersionMenuItems() {
|
func addPhpVersionMenuItems() {
|
||||||
if PhpEnv.phpInstall.version.error {
|
if PhpEnv.phpInstall.version.error {
|
||||||
for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] {
|
for message in ["mi_php_broken_1", "mi_php_broken_2", "mi_php_broken_3", "mi_php_broken_4"] {
|
||||||
@ -37,47 +38,6 @@ class StatusMenu : NSMenu {
|
|||||||
self.addItem(NSMenuItem.separator())
|
self.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
|
|
||||||
func addOtherMenuItems() {
|
|
||||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
|
||||||
let servicesMenu = NSMenu()
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_help".localized, action: nil, keyEquivalent: ""))
|
|
||||||
|
|
||||||
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
|
||||||
servicesMenu.addItem(NSMenuItem(
|
|
||||||
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
|
|
||||||
action: nil, keyEquivalent: "f"
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
servicesMenu.addItem(NSMenuItem(
|
|
||||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
|
||||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: "f"))
|
|
||||||
}
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_services".localized, action: nil, keyEquivalent: ""))
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
|
||||||
|
|
||||||
servicesMenu.addItem(
|
|
||||||
NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"),
|
|
||||||
withKeyModifier: [.command, .shift])
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s"))
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_manual_actions".localized, action: nil, keyEquivalent: ""))
|
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenu), keyEquivalent: "r"))
|
|
||||||
|
|
||||||
for item in servicesMenu.items {
|
|
||||||
item.target = MainMenu.shared
|
|
||||||
}
|
|
||||||
|
|
||||||
self.setSubmenu(servicesMenu, for: services)
|
|
||||||
self.addItem(services)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addValetMenuItems() {
|
func addValetMenuItems() {
|
||||||
self.addItem(HeaderView.asMenuItem(text: "mi_valet".localized))
|
self.addItem(HeaderView.asMenuItem(text: "mi_valet".localized))
|
||||||
self.addItem(NSMenuItem(title: "mi_valet_config".localized, action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
|
self.addItem(NSMenuItem(title: "mi_valet_config".localized, action: #selector(MainMenu.openValetConfigFolder), keyEquivalent: "v"))
|
||||||
@ -131,9 +91,59 @@ class StatusMenu : NSMenu {
|
|||||||
|
|
||||||
// Other
|
// Other
|
||||||
self.addItem(NSMenuItem.separator())
|
self.addItem(NSMenuItem.separator())
|
||||||
self.addOtherMenuItems()
|
self.addFirstAidAndServicesMenuItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addFirstAidAndServicesMenuItems() {
|
||||||
|
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||||
|
let servicesMenu = NSMenu()
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
||||||
|
|
||||||
|
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
||||||
|
servicesMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
|
||||||
|
action: nil, keyEquivalent: "f")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
servicesMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||||
|
action: #selector(MainMenu.fixMyValet), keyEquivalent: "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* (disabled until v5.1 and further tweaking)
|
||||||
|
servicesMenu.addItem(NSMenuItem(
|
||||||
|
title: "mi_fix_brew_permissions".localized(),
|
||||||
|
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: "")
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_dnsmasq".localized, action: #selector(MainMenu.restartDnsMasq), keyEquivalent: "d"))
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_php_fpm".localized, action: #selector(MainMenu.restartPhpFpm), keyEquivalent: "p"))
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_nginx".localized, action: #selector(MainMenu.restartNginx), keyEquivalent: "n"))
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_restart_all_services".localized, action: #selector(MainMenu.restartAllServices), keyEquivalent: "s"))
|
||||||
|
servicesMenu.addItem(
|
||||||
|
NSMenuItem(title: "mi_stop_all_services".localized, action: #selector(MainMenu.stopAllServices), keyEquivalent: "s"),
|
||||||
|
withKeyModifier: [.command, .shift])
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_manual_actions".localized))
|
||||||
|
|
||||||
|
servicesMenu.addItem(NSMenuItem(title: "mi_php_refresh".localized, action: #selector(MainMenu.reloadPhpMonitorMenuInForeground), keyEquivalent: "r"))
|
||||||
|
|
||||||
|
for item in servicesMenu.items {
|
||||||
|
item.target = MainMenu.shared
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setSubmenu(servicesMenu, for: services)
|
||||||
|
self.addItem(services)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIVATE HELPERS
|
||||||
|
|
||||||
private func addSwitchToPhpMenuItems() {
|
private func addSwitchToPhpMenuItems() {
|
||||||
var shortcutKey = 1
|
var shortcutKey = 1
|
||||||
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
|
for index in (0..<PhpEnv.shared.availablePhpVersions.count).reversed() {
|
||||||
|
16
phpmon/Domain/Preferences/MenuBarIcons.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// MenuBarIcons.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 06/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
enum MenuBarIcon: String, CaseIterable {
|
||||||
|
case iconPhp = "php"
|
||||||
|
case iconElephant = "elephant"
|
||||||
|
case noIcon = "none"
|
||||||
|
}
|
@ -14,7 +14,7 @@ import Foundation
|
|||||||
enum PreferenceName: String {
|
enum PreferenceName: String {
|
||||||
case wasLaunchedBefore = "launched_before"
|
case wasLaunchedBefore = "launched_before"
|
||||||
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
case shouldDisplayDynamicIcon = "use_dynamic_icon"
|
||||||
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
|
case iconTypeToDisplay = "icon_type_to_display"
|
||||||
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
case fullPhpVersionDynamicIcon = "full_php_in_menu_bar"
|
||||||
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
case autoServiceRestartAfterExtensionToggle = "auto_restart_after_extension_toggle"
|
||||||
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
|
case autoComposerGlobalUpdateAfterSwitch = "auto_composer_global_update_after_switch"
|
||||||
@ -22,6 +22,13 @@ enum PreferenceName: String {
|
|||||||
case globalHotkey = "global_hotkey"
|
case globalHotkey = "global_hotkey"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
These are retired preferences that, if present, should be migrated.
|
||||||
|
*/
|
||||||
|
enum RetiredPreferenceName: String {
|
||||||
|
case shouldDisplayPhpHintInIcon = "add_php_to_icon"
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
These are internal stats. They NEVER get shared.
|
These are internal stats. They NEVER get shared.
|
||||||
*/
|
*/
|
||||||
@ -64,7 +71,7 @@ class Preferences {
|
|||||||
UserDefaults.standard.register(defaults: [
|
UserDefaults.standard.register(defaults: [
|
||||||
/// Preferences
|
/// Preferences
|
||||||
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
PreferenceName.shouldDisplayDynamicIcon.rawValue: true,
|
||||||
PreferenceName.shouldDisplayPhpHintInIcon.rawValue: true,
|
PreferenceName.iconTypeToDisplay.rawValue: MenuBarIcon.iconPhp.rawValue,
|
||||||
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
PreferenceName.fullPhpVersionDynamicIcon.rawValue: false,
|
||||||
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
|
PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue: true,
|
||||||
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
|
PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue: false,
|
||||||
@ -76,6 +83,7 @@ class Preferences {
|
|||||||
])
|
])
|
||||||
|
|
||||||
if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) {
|
if UserDefaults.standard.bool(forKey: PreferenceName.wasLaunchedBefore.rawValue) {
|
||||||
|
handleMigration()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +92,23 @@ class Preferences {
|
|||||||
UserDefaults.standard.synchronize()
|
UserDefaults.standard.synchronize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sometimes preferences will change, and a migration is required to take the user's previous preference
|
||||||
|
and migrate it over to the new type. For example, the choice to disable the icon next to the version
|
||||||
|
number was once a boolean (do you want the icon? yes / no) but has now become a multi-faceted option.
|
||||||
|
*/
|
||||||
|
static func handleMigration() {
|
||||||
|
// If the user chose the "no icon" option, migrate it over
|
||||||
|
if (
|
||||||
|
UserDefaults.standard.value(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue) != nil &&
|
||||||
|
UserDefaults.standard.bool(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue) == false
|
||||||
|
) {
|
||||||
|
Log.info("The preference where the user chose no icon has been migrated over.")
|
||||||
|
UserDefaults.standard.set(MenuBarIcon.noIcon.rawValue, forKey: PreferenceName.iconTypeToDisplay.rawValue)
|
||||||
|
UserDefaults.standard.removeObject(forKey: RetiredPreferenceName.shouldDisplayPhpHintInIcon.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - API
|
// MARK: - API
|
||||||
|
|
||||||
static var preferences: [PreferenceName: Any?] {
|
static var preferences: [PreferenceName: Any?] {
|
||||||
@ -112,7 +137,6 @@ class Preferences {
|
|||||||
return [
|
return [
|
||||||
// Part 1: Always Booleans
|
// Part 1: Always Booleans
|
||||||
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
|
||||||
.shouldDisplayPhpHintInIcon: UserDefaults.standard.bool(forKey: PreferenceName.shouldDisplayPhpHintInIcon.rawValue) as Any,
|
|
||||||
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
.fullPhpVersionDynamicIcon: UserDefaults.standard.bool(forKey: PreferenceName.fullPhpVersionDynamicIcon.rawValue) as Any,
|
||||||
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any,
|
.autoServiceRestartAfterExtensionToggle: UserDefaults.standard.bool(forKey: PreferenceName.autoServiceRestartAfterExtensionToggle.rawValue) as Any,
|
||||||
.autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any,
|
.autoComposerGlobalUpdateAfterSwitch: UserDefaults.standard.bool(forKey: PreferenceName.autoComposerGlobalUpdateAfterSwitch.rawValue) as Any,
|
||||||
@ -120,6 +144,7 @@ class Preferences {
|
|||||||
|
|
||||||
// Part 2: Always Strings
|
// Part 2: Always Strings
|
||||||
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
|
.globalHotkey: UserDefaults.standard.string(forKey: PreferenceName.globalHotkey.rawValue) as Any,
|
||||||
|
.iconTypeToDisplay: UserDefaults.standard.string(forKey: PreferenceName.iconTypeToDisplay.rawValue) as Any,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,11 +57,12 @@ class PrefsVC: NSViewController {
|
|||||||
MainMenu.shared.refreshIcon()
|
MainMenu.shared.refreshIcon()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
CheckboxPreferenceView.make(
|
SelectPreferenceView.make(
|
||||||
sectionText: "",
|
sectionText: "",
|
||||||
descriptionText: "prefs.icon_hint_desc".localized,
|
descriptionText: "prefs.icon_options_desc".localized,
|
||||||
checkboxText: "prefs.icon_hint_title".localized,
|
options: MenuBarIcon.allCases.map({ return $0.rawValue }),
|
||||||
preference: .shouldDisplayPhpHintInIcon,
|
localizationPrefix: "prefs.icon_options",
|
||||||
|
preference: .iconTypeToDisplay,
|
||||||
action: {
|
action: {
|
||||||
MainMenu.shared.refreshIcon()
|
MainMenu.shared.refreshIcon()
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@
|
|||||||
<outlet property="labelDescription" destination="Bcg-X1-qca" id="T23-ag-AUf"/>
|
<outlet property="labelDescription" destination="Bcg-X1-qca" id="T23-ag-AUf"/>
|
||||||
<outlet property="labelSection" destination="B8f-nb-Y0A" id="i61-ls-yM0"/>
|
<outlet property="labelSection" destination="B8f-nb-Y0A" id="i61-ls-yM0"/>
|
||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="149" y="-114.5"/>
|
<point key="canvasLocation" x="149" y="-115"/>
|
||||||
</customView>
|
</customView>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
82
phpmon/Domain/Preferences/Views/SelectPreferenceView.swift
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// SelectPreferenceView.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 06/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class SelectPreferenceView: NSView, XibLoadable {
|
||||||
|
|
||||||
|
@IBOutlet weak var labelSection: NSTextField!
|
||||||
|
@IBOutlet weak var labelDescription: NSTextField!
|
||||||
|
@IBOutlet weak var popupButton: NSPopUpButton!
|
||||||
|
|
||||||
|
var localizationPrefix: String = ""
|
||||||
|
var imagePrefix: String? = nil
|
||||||
|
|
||||||
|
var options: [String] = [] {
|
||||||
|
didSet {
|
||||||
|
self.popupButton.removeAllItems()
|
||||||
|
self.options.forEach { value in
|
||||||
|
self.popupButton.addItem(
|
||||||
|
withTitle: "\(localizationPrefix).\(value)".localized
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if imagePrefix == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.popupButton.itemArray.enumerated().forEach { item in
|
||||||
|
item.element.image = NSImage(named: "\(imagePrefix!)_\(self.options[item.offset])")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var action: (() -> Void)!
|
||||||
|
|
||||||
|
var preference: PreferenceName! {
|
||||||
|
didSet {
|
||||||
|
let value = Preferences.preferences[preference] as! String
|
||||||
|
self.options.enumerated().forEach { option in
|
||||||
|
if option.element == value {
|
||||||
|
self.popupButton.selectItem(at: option.offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func make(
|
||||||
|
sectionText: String,
|
||||||
|
descriptionText: String,
|
||||||
|
options: [String],
|
||||||
|
localizationPrefix: String,
|
||||||
|
imagePrefix: String? = nil,
|
||||||
|
preference: PreferenceName,
|
||||||
|
action: @escaping () -> Void) -> NSView
|
||||||
|
{
|
||||||
|
let view = Self.createFromXib()!
|
||||||
|
|
||||||
|
view.labelSection.stringValue = sectionText
|
||||||
|
view.labelDescription.stringValue = descriptionText
|
||||||
|
|
||||||
|
view.localizationPrefix = localizationPrefix
|
||||||
|
view.imagePrefix = imagePrefix
|
||||||
|
view.options = options
|
||||||
|
view.preference = preference
|
||||||
|
view.action = action
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func valueChanged(_ sender: Any) {
|
||||||
|
let index = self.popupButton.indexOfSelectedItem
|
||||||
|
Preferences.update(.iconTypeToDisplay, value: self.options[index])
|
||||||
|
self.action()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
72
phpmon/Domain/Preferences/Views/SelectPreferenceView.xib
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner"/>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="c22-O7-iKe" customClass="SelectPreferenceView" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="596" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Bcg-X1-qca">
|
||||||
|
<rect key="frame" x="168" y="5" width="410" height="14"/>
|
||||||
|
<textFieldCell key="cell" title="DESCRIPTION" id="9fH-up-Sob">
|
||||||
|
<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="B8f-nb-Y0A">
|
||||||
|
<rect key="frame" x="-2" y="29" width="154" height="16"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="150" id="euj-t0-xv4"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="SECTION" id="46w-Sv-y21">
|
||||||
|
<font key="font" metaFont="systemMedium" size="13"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YaB-Tg-Ir3">
|
||||||
|
<rect key="frame" x="167" y="23" width="110" height="25"/>
|
||||||
|
<popUpButtonCell key="cell" type="push" title="Icon Option" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="SaA-mm-HBo" id="Su6-C4-aGo">
|
||||||
|
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="menu"/>
|
||||||
|
<menu key="menu" id="WjV-7P-3WA">
|
||||||
|
<items>
|
||||||
|
<menuItem title="Icon Option" state="on" id="SaA-mm-HBo"/>
|
||||||
|
<menuItem title="Item 2" id="I5Z-Ka-zo6"/>
|
||||||
|
<menuItem title="Item 3" id="qUM-nV-HdU"/>
|
||||||
|
</items>
|
||||||
|
</menu>
|
||||||
|
</popUpButtonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="valueChanged:" target="c22-O7-iKe" id="M95-aC-xtz"/>
|
||||||
|
</connections>
|
||||||
|
</popUpButton>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="YaB-Tg-Ir3" firstAttribute="centerY" secondItem="B8f-nb-Y0A" secondAttribute="centerY" id="1t0-bW-LpQ"/>
|
||||||
|
<constraint firstItem="B8f-nb-Y0A" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="5" id="2Zu-h3-qb0"/>
|
||||||
|
<constraint firstItem="Bcg-X1-qca" firstAttribute="leading" secondItem="YaB-Tg-Ir3" secondAttribute="leading" id="5IQ-Nb-Q3V"/>
|
||||||
|
<constraint firstItem="YaB-Tg-Ir3" firstAttribute="leading" secondItem="B8f-nb-Y0A" secondAttribute="trailing" constant="20" id="6sr-bU-xvG"/>
|
||||||
|
<constraint firstItem="Bcg-X1-qca" firstAttribute="top" secondItem="YaB-Tg-Ir3" secondAttribute="bottom" constant="8" symbolic="YES" id="Mji-pe-CNl"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="Bcg-X1-qca" secondAttribute="trailing" constant="20" symbolic="YES" id="UPo-Il-l81"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="YaB-Tg-Ir3" secondAttribute="trailing" constant="20" symbolic="YES" id="Zlg-jj-uKY"/>
|
||||||
|
<constraint firstItem="B8f-nb-Y0A" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="Ztd-uk-4aw"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="Bcg-X1-qca" secondAttribute="bottom" constant="5" id="hNE-mU-jcu"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="labelDescription" destination="Bcg-X1-qca" id="T23-ag-AUf"/>
|
||||||
|
<outlet property="labelSection" destination="B8f-nb-Y0A" id="i61-ls-yM0"/>
|
||||||
|
<outlet property="popupButton" destination="YaB-Tg-Ir3" id="sAB-BL-qUQ"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="149" y="-115"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
@ -47,7 +47,6 @@ class SiteListCell: NSTableCellView
|
|||||||
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
||||||
|
|
||||||
// Show the green or red lock based on whether the site was secured
|
// Show the green or red lock based on whether the site was secured
|
||||||
// imageViewLock.image = NSImage(named: site.secured ? "Lock" : "LockUnlocked")
|
|
||||||
imageViewLock.contentTintColor = site.secured ?
|
imageViewLock.contentTintColor = site.secured ?
|
||||||
NSColor(named: "IconColorGreen") // green
|
NSColor(named: "IconColorGreen") // green
|
||||||
: NSColor(named: "IconColorRed")
|
: NSColor(named: "IconColorRed")
|
||||||
@ -103,7 +102,7 @@ class SiteListCell: NSTableCellView
|
|||||||
if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue {
|
if response.rawValue > NSApplication.ModalResponse.alertFirstButtonReturn.rawValue {
|
||||||
if map.keys.contains(response.rawValue) {
|
if map.keys.contains(response.rawValue) {
|
||||||
let version = map[response.rawValue]!
|
let version = map[response.rawValue]!
|
||||||
print("Pressed button to switch to \(version)")
|
Log.info("Pressed button to switch to \(version)")
|
||||||
MainMenu.shared.switchToPhpVersion(version)
|
MainMenu.shared.switchToPhpVersion(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,8 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
return sites[tableView.selectedRow]
|
return sites[tableView.selectedRow]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var timer: Timer? = nil
|
||||||
|
|
||||||
// MARK: - Display
|
// MARK: - Display
|
||||||
|
|
||||||
public static func create(delegate: NSWindowDelegate?) {
|
public static func create(delegate: NSWindowDelegate?) {
|
||||||
@ -90,7 +92,11 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
Also shows a spinner to indicate that we're busy.
|
Also shows a spinner to indicate that we're busy.
|
||||||
*/
|
*/
|
||||||
private func setUIBusy() {
|
private func setUIBusy() {
|
||||||
progressIndicator.startAnimation(nil)
|
// If it takes more than 0.5s to set the UI to not busy, show a spinner
|
||||||
|
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in
|
||||||
|
self.progressIndicator.startAnimation(true)
|
||||||
|
})
|
||||||
|
|
||||||
tableView.alphaValue = 0.3
|
tableView.alphaValue = 0.3
|
||||||
tableView.isEnabled = false
|
tableView.isEnabled = false
|
||||||
tableView.selectRowIndexes([], byExtendingSelection: true)
|
tableView.selectRowIndexes([], byExtendingSelection: true)
|
||||||
@ -100,6 +106,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
Re-enables the UI so the user can interact with it.
|
Re-enables the UI so the user can interact with it.
|
||||||
*/
|
*/
|
||||||
private func setUINotBusy() {
|
private func setUINotBusy() {
|
||||||
|
timer?.invalidate()
|
||||||
progressIndicator.stopAnimation(nil)
|
progressIndicator.stopAnimation(nil)
|
||||||
tableView.alphaValue = 1.0
|
tableView.alphaValue = 1.0
|
||||||
tableView.isEnabled = true
|
tableView.isEnabled = true
|
||||||
@ -118,7 +125,9 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
setUIBusy()
|
setUIBusy()
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
execute()
|
execute()
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
|
// For a smoother animation, expect at least a 0.2 second delay
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
|
||||||
completion()
|
completion()
|
||||||
setUINotBusy()
|
setUINotBusy()
|
||||||
}
|
}
|
||||||
|
@ -30,17 +30,22 @@ extension App {
|
|||||||
func handlePhpConfigWatcher(forceReload: Bool = false) {
|
func handlePhpConfigWatcher(forceReload: Bool = false) {
|
||||||
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(PhpEnv.phpInstall.version.short)")
|
let url = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(PhpEnv.phpInstall.version.short)")
|
||||||
|
|
||||||
// Watcher needs to be created
|
// Check whether the watcher exists and schedule on the main thread
|
||||||
if self.watcher == nil {
|
// if we don't consistently do this, the app will create duplicate watchers
|
||||||
startWatcher(url)
|
// due to timing issues, which creates retain cycles.
|
||||||
}
|
DispatchQueue.main.async {
|
||||||
|
// Watcher needs to be created
|
||||||
|
if self.watcher == nil {
|
||||||
|
self.startWatcher(url)
|
||||||
|
}
|
||||||
|
|
||||||
// Watcher needs to be updated
|
// Watcher needs to be updated
|
||||||
if self.watcher.url != url || forceReload {
|
if self.watcher.url != url || forceReload {
|
||||||
self.watcher.disable()
|
self.watcher.disable()
|
||||||
self.watcher = nil
|
self.watcher = nil
|
||||||
Log.info("Watcher has stopped watching files. Starting new one...")
|
Log.info("Watcher has stopped watching files. Starting new one...")
|
||||||
startWatcher(url)
|
self.startWatcher(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,19 +40,24 @@ class PhpConfigWatcher {
|
|||||||
self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write)
|
self.addWatcher(for: self.url.appendingPathComponent("conf.d/\(file)"), eventMask: .write)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info("A watcher exists for the following config paths:")
|
Log.perf("A watcher exists for the following config paths:")
|
||||||
Log.info(self.watchers.map({ watcher in
|
Log.perf(self.watchers.map({ watcher in
|
||||||
return watcher.url.relativePath
|
return watcher.url.relativePath
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu) {
|
func addWatcher(for url: URL, eventMask: DispatchSource.FileSystemEvent, behaviour: FSWatcherBehaviour = .reloadsMenu) {
|
||||||
|
if !Filesystem.fileExists(url.path) {
|
||||||
|
Log.warn("No watcher was created for \(url.path) because the requested file does not exist.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self, behaviour: behaviour)
|
let watcher = FSWatcher(for: url, eventMask: eventMask, parent: self, behaviour: behaviour)
|
||||||
self.watchers.append(watcher)
|
self.watchers.append(watcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
func disable() {
|
func disable() {
|
||||||
Log.info("Turning off existing watchers...")
|
Log.perf("Turning off all individual existing watchers...")
|
||||||
self.watchers.forEach { (watcher) in
|
self.watchers.forEach { (watcher) in
|
||||||
watcher.stopMonitoring()
|
watcher.stopMonitoring()
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"mi_stop_all_services" = "Stop All Services";
|
"mi_stop_all_services" = "Stop All Services";
|
||||||
"mi_fix_my_valet" = "Fix My Valet (PHP & Services)";
|
"mi_fix_my_valet" = "Fix My Valet (PHP & Services)";
|
||||||
"mi_fix_my_valet_unavailable" = "Fix My Valet Unavailable";
|
"mi_fix_my_valet_unavailable" = "Fix My Valet Unavailable";
|
||||||
|
"mi_fix_brew_permissions" = "Restore Homebrew Permissions";
|
||||||
"mi_php_refresh" = "Refresh Information";
|
"mi_php_refresh" = "Refresh Information";
|
||||||
|
|
||||||
"mi_configuration" = "PHP Configuration";
|
"mi_configuration" = "PHP Configuration";
|
||||||
@ -36,11 +37,11 @@
|
|||||||
"mi_upload_max_filesize" = "Max Upload";
|
"mi_upload_max_filesize" = "Max Upload";
|
||||||
"mi_manual_actions" = "Manual Actions";
|
"mi_manual_actions" = "Manual Actions";
|
||||||
"mi_services" = "Services";
|
"mi_services" = "Services";
|
||||||
"mi_help" = "First Aid";
|
|
||||||
"mi_other" = "First Aid & Services";
|
"mi_other" = "First Aid & Services";
|
||||||
|
"mi_first_aid" = "First Aid";
|
||||||
|
|
||||||
"mi_composer" = "Composer";
|
"mi_composer" = "Composer";
|
||||||
"mi_valet_config" = "Locate Valet folder (.config/valet)";
|
"mi_valet_config" = "Locate Valet Folder (.config/valet)";
|
||||||
"mi_php_config" = "Locate PHP Configuration File (php.ini)";
|
"mi_php_config" = "Locate PHP Configuration File (php.ini)";
|
||||||
"mi_global_composer" = "Locate Global Composer File (.composer)";
|
"mi_global_composer" = "Locate Global Composer File (.composer)";
|
||||||
"mi_phpinfo" = "Show Current Configuration (phpinfo)";
|
"mi_phpinfo" = "Show Current Configuration (phpinfo)";
|
||||||
@ -132,22 +133,26 @@
|
|||||||
"prefs.subtitle" = "Preferences";
|
"prefs.subtitle" = "Preferences";
|
||||||
"prefs.close" = "Close";
|
"prefs.close" = "Close";
|
||||||
|
|
||||||
"prefs.global_shortcut" = "Global shortcut:";
|
"prefs.global_shortcut" = "Global Shortcut:";
|
||||||
"prefs.dynamic_icon" = "Icon customization:";
|
"prefs.dynamic_icon" = "Dynamic Icon:";
|
||||||
"prefs.info_density" = "Info density:";
|
"prefs.dynamic_icon" = "Icon Type:";
|
||||||
|
"prefs.info_density" = "Info Density:";
|
||||||
"prefs.services" = "Services:";
|
"prefs.services" = "Services:";
|
||||||
"prefs.switcher" = "Switcher:";
|
"prefs.switcher" = "Switcher:";
|
||||||
"prefs.integrations" = "Integrations:";
|
"prefs.integrations" = "Integrations:";
|
||||||
|
|
||||||
|
"prefs.icon_options.php" = "Display PHP Icon";
|
||||||
|
"prefs.icon_options.elephant" = "Display Elephant Icon";
|
||||||
|
"prefs.icon_options.none" = "Do Not Display Icon";
|
||||||
|
|
||||||
|
"prefs.icon_options_desc" = "This option decides which icon will be displayed next to the version number of the currently linked PHP version. If the Dynamic Icon option has been disabled, this will have no effect.";
|
||||||
|
|
||||||
"prefs.auto_restart_services_title" = "Auto-restart PHP-FPM";
|
"prefs.auto_restart_services_title" = "Auto-restart PHP-FPM";
|
||||||
"prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when you check or uncheck an extension. Slightly slower when enabled, but this applies the extension change immediately for all sites you're serving, no need to restart PHP-FPM manually.";
|
"prefs.auto_restart_services_desc" = "When checked, will automatically restart PHP-FPM when you check or uncheck an extension. Slightly slower when enabled, but this applies the extension change immediately for all sites you're serving, no need to restart PHP-FPM manually.";
|
||||||
|
|
||||||
"prefs.dynamic_icon_title" = "Display dynamic icon in menu bar";
|
"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. If checked, it will display the major version number of the currently linked PHP version.";
|
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible. If checked, it will display the major version number of the currently linked PHP version.";
|
||||||
|
|
||||||
"prefs.icon_hint_title" = "Display PHP hint next to version number";
|
|
||||||
"prefs.icon_hint_desc" = "If you uncheck this box, the icon in the menu bar will only show the version number and not the PHP hint. This preference does not do anything if the dynamic icon has been disabled.";
|
|
||||||
|
|
||||||
"prefs.display_full_php_version" = "Display full PHP version everywhere";
|
"prefs.display_full_php_version" = "Display full PHP version everywhere";
|
||||||
"prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)";
|
"prefs.display_full_php_version_desc" = "Display the full version instead of the major version displayed in the menu bar and the dropdown menu. (This may be undesirable on smaller displays, so this is disabled by default.)";
|
||||||
|
|
||||||
@ -180,7 +185,7 @@
|
|||||||
|
|
||||||
// Composer Update
|
// Composer Update
|
||||||
"alert.composer_missing.title" = "Composer not found!";
|
"alert.composer_missing.title" = "Composer not found!";
|
||||||
"alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /user/local/bin`.";
|
"alert.composer_missing.info" = "Make sure you have Composer available in `/usr/local/bin/composer`. If Composer is located somewhere else, please create a symlink, like so (make sure to use the correct path):\n\n`ln -s /path/to/composer /usr/local/bin`.";
|
||||||
|
|
||||||
"alert.composer_progress.title" = "Updating global dependencies...";
|
"alert.composer_progress.title" = "Updating global dependencies...";
|
||||||
"alert.composer_progress.info" = "You can see the progress in the terminal output below.";
|
"alert.composer_progress.info" = "You can see the progress in the terminal output below.";
|
||||||
@ -213,6 +218,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.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.";
|
"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
|
// PHP FPM Broken
|
||||||
"alert.php_fpm_broken.title" = "PHP-FPM configuration is incorrect";
|
"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.";
|
"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.";
|
||||||
@ -278,3 +292,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.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.accept" = "Yes, I would like to sponsor";
|
||||||
"startup.sponsor_encouragement.skip" = "Nevermind";
|
"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.";
|
||||||
|