mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-03-27 06:20:08 +01:00
🚀 Version 5.3
Merge branch 'dev/5.3'
This commit is contained in:
15
.swiftlint.yml
Normal file
15
.swiftlint.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
disabled_rules:
|
||||||
|
- todo
|
||||||
|
- identifier_name
|
||||||
|
- force_try
|
||||||
|
- force_cast
|
||||||
|
|
||||||
|
opt_in_rules:
|
||||||
|
- empty_count
|
||||||
|
|
||||||
|
included:
|
||||||
|
- phpmon
|
||||||
|
- phpmon-tests
|
||||||
|
|
||||||
|
excluded:
|
||||||
|
- phpmon/Vendor
|
||||||
14
DEVELOPER.md
14
DEVELOPER.md
@@ -1,5 +1,19 @@
|
|||||||
# DEVELOPER README
|
# DEVELOPER README
|
||||||
|
|
||||||
|
## ✅ Linting
|
||||||
|
|
||||||
|
This project uses the [SwiftLint](https://github.com/realm/SwiftLint) linter. You must install it and can run it like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
swiftlint
|
||||||
|
```
|
||||||
|
|
||||||
|
It also automatically runs when you try to build the project. You'll get a warning if `swiftlint` is not installed, though. You can attempt to automatically fix issues:
|
||||||
|
|
||||||
|
```
|
||||||
|
swiftlint --fix
|
||||||
|
```
|
||||||
|
|
||||||
## 🔧 Build instructions
|
## 🔧 Build instructions
|
||||||
|
|
||||||
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
<img src="./docs/build.png" width="404px" alt="build button in Xcode"/>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
/* 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 */; };
|
||||||
|
54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */ = {isa = PBXBuildFile; fileRef = 54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */; };
|
||||||
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 */; };
|
||||||
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
||||||
@@ -51,6 +52,9 @@
|
|||||||
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 */; };
|
||||||
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 */; };
|
||||||
|
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
|
||||||
|
C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE736282ABA4F00A302C2 /* AppVersion.swift */; };
|
||||||
|
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40FE739282ABB2E00A302C2 /* AppVersionTest.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 */; };
|
||||||
@@ -71,32 +75,38 @@
|
|||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
||||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */; };
|
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
||||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */; };
|
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */; };
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; };
|
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
|
||||||
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; };
|
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */; };
|
||||||
|
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
||||||
|
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4205A7D27F4D21800191A39 /* ValetProxy.swift */; };
|
||||||
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
||||||
|
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.swift */; };
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42759662627662800093CAE /* NSMenuExtension.swift */; };
|
||||||
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
||||||
C42CFB1627DFDE7900862737 /* nicoverbruggen.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nicoverbruggen.test */; };
|
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nginx-site.test */; };
|
||||||
C42CFB1827DFDFDC00862737 /* nicoverbruggen_isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */; };
|
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */; };
|
||||||
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */; };
|
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */; };
|
||||||
|
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
||||||
|
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42F26722805B4B400938AC7 /* DomainListable.swift */; };
|
||||||
|
C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */; };
|
||||||
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
C43603A0275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */; };
|
||||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A1925D9CD1000591B77 /* Utility.swift */; };
|
||||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; };
|
C43A8A2025D9D1D700591B77 /* brew-formula.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew-formula.json */; };
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; };
|
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */; };
|
||||||
C44067F527E2582B0045BD4E /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
||||||
C44067F727E258410045BD4E /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
||||||
C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
||||||
C44067FB27E25FD70045BD4E /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
||||||
C449B4F027EE7FB800C47E8A /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */; };
|
||||||
C449B4F127EE7FC200C47E8A /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
C449B4F127EE7FC200C47E8A /* DomainListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* DomainListNameCell.swift */; };
|
||||||
C449B4F227EE7FC400C47E8A /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* DomainListPhpCell.swift */; };
|
||||||
C449B4F327EE7FC600C47E8A /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */; };
|
||||||
C449B4F427EE7FC800C47E8A /* SiteListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */; };
|
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44C198C276E3A1C0072762D /* ProgressWindow.swift */; };
|
||||||
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 */; };
|
||||||
@@ -105,11 +115,15 @@
|
|||||||
C44CCD4127AFE2FC00CE40E5 /* 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 */; };
|
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 */; };
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */; };
|
||||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */ = {isa = PBXBuildFile; fileRef = C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */; };
|
||||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; };
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */; };
|
||||||
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */; };
|
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* DomainListVC.swift */; };
|
||||||
|
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */; };
|
||||||
|
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
|
||||||
|
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */; };
|
||||||
|
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */; };
|
||||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C46FA23E246C358E00944F05 /* StringExtension.swift */; };
|
||||||
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
|
C473319F2470923A009A0597 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C473319E2470923A009A0597 /* Localizable.strings */; };
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||||
@@ -119,6 +133,8 @@
|
|||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */; };
|
||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395826135DC100FB00FA /* PrefsVC.swift */; };
|
||||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
|
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
||||||
|
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C484437A2804BB560041A78A /* ValetProxyScanner.swift */; };
|
||||||
C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C8F25CC7FD000CC7490 /* StatsView.xib */; };
|
C48D0C9025CC7FD000CC7490 /* StatsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C8F25CC7FD000CC7490 /* StatsView.xib */; };
|
||||||
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9225CC804200CC7490 /* XibLoadable.swift */; };
|
||||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
|
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
|
||||||
@@ -134,11 +150,11 @@
|
|||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||||
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; };
|
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49E171E27A5736E00787921 /* PMServicesView.swift */; };
|
||||||
C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */; };
|
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */; };
|
||||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
||||||
C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
C4AF9F71275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
||||||
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
C4AF9F72275445FF00D44ED0 /* valet-config.json in Resources */ = {isa = PBXBuildFile; fileRef = C4AF9F70275445FF00D44ED0 /* valet-config.json */; };
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */; };
|
C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */; };
|
||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F792754499000D44ED0 /* Valet.swift */; };
|
||||||
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */; };
|
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */; };
|
||||||
@@ -158,6 +174,14 @@
|
|||||||
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7B275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */; };
|
||||||
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */; };
|
||||||
|
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8DE27F88AEB002D32A9 /* FakeSiteScanner.swift */; };
|
||||||
|
C4C0E8E027F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8DE27F88AEB002D32A9 /* FakeSiteScanner.swift */; };
|
||||||
|
C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E127F88B13002D32A9 /* ValetSiteScanner.swift */; };
|
||||||
|
C4C0E8E327F88B13002D32A9 /* ValetSiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E127F88B13002D32A9 /* ValetSiteScanner.swift */; };
|
||||||
|
C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */; };
|
||||||
|
C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */; };
|
||||||
|
C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */; };
|
||||||
|
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */; };
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
||||||
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C1019A27C65C6F001FACC2 /* Process.swift */; };
|
||||||
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */; };
|
||||||
@@ -172,17 +196,19 @@
|
|||||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
||||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
||||||
C4D5CFCA27E0F9CD00035329 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */; };
|
C4D5CFCA27E0F9CD00035329 /* NginxConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */; };
|
||||||
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */; };
|
C4D5CFCB27E0F9CD00035329 /* NginxConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfiguration.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 */; };
|
||||||
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
||||||
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
||||||
C4D936CB27E3EE4A00BD69FE /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */; };
|
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.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 */; };
|
||||||
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 */; };
|
||||||
|
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */; };
|
||||||
|
C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */; };
|
||||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
||||||
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
||||||
@@ -211,9 +237,10 @@
|
|||||||
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 */; };
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4DEB7D327A5D60B00834718 /* Stats.swift */; };
|
||||||
|
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42337A2281F19F000459A48 /* Xdebug.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 /* PhpExtensionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */; };
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
|
||||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
|
C4F780B725D80B5D000DBC97 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4811D2322D70A4700B5F6B3 /* App.swift */; };
|
||||||
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||||
@@ -232,6 +259,8 @@
|
|||||||
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
C4F780CE25D80B75000DBC97 /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = C474B00524C0E98C00066A22 /* LocalNotification.swift */; };
|
||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */; };
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */; };
|
||||||
|
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
||||||
|
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4FE011028084FC200D1DE6D /* SelectionVC.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -247,6 +276,7 @@
|
|||||||
/* 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>"; };
|
||||||
|
54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy-custom-tld.test"; 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>"; };
|
||||||
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeysController.swift; sourceTree = "<group>"; };
|
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKeysController.swift; sourceTree = "<group>"; };
|
||||||
54D9E0AD27E4F51E003B9AD9 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; };
|
54D9E0AD27E4F51E003B9AD9 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; };
|
||||||
@@ -269,6 +299,8 @@
|
|||||||
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>"; };
|
||||||
|
C40FE736282ABA4F00A302C2 /* AppVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersion.swift; sourceTree = "<group>"; };
|
||||||
|
C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionTest.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>"; };
|
||||||
@@ -286,30 +318,37 @@
|
|||||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
|
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
|
||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
||||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+Actions.swift"; sourceTree = "<group>"; };
|
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+Actions.swift"; sourceTree = "<group>"; };
|
||||||
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
||||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DomainListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
||||||
|
C4205A7D27F4D21800191A39 /* ValetProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxy.swift; sourceTree = "<group>"; };
|
||||||
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
||||||
|
C42337A2281F19F000459A48 /* Xdebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Xdebug.swift; sourceTree = "<group>"; };
|
||||||
C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; };
|
C42759662627662800093CAE /* NSMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSMenuExtension.swift; sourceTree = "<group>"; };
|
||||||
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; };
|
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+FixMyValet.swift"; sourceTree = "<group>"; };
|
||||||
C42CFB1527DFDE7900862737 /* nicoverbruggen.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen.test; sourceTree = "<group>"; };
|
C42CFB1527DFDE7900862737 /* nginx-site.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site.test"; sourceTree = "<group>"; };
|
||||||
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen_isolated.test; sourceTree = "<group>"; };
|
C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-site-isolated.test"; sourceTree = "<group>"; };
|
||||||
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigParserTest.swift; sourceTree = "<group>"; };
|
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigurationTest.swift; sourceTree = "<group>"; };
|
||||||
|
C42F26722805B4B400938AC7 /* DomainListable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListable.swift; sourceTree = "<group>"; };
|
||||||
|
C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-secure-proxy.test"; sourceTree = "<group>"; };
|
||||||
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; };
|
C436039F275E67610028EFC6 /* AppDelegate+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Notifications.swift"; sourceTree = "<group>"; };
|
||||||
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
C43A8A1925D9CD1000591B77 /* Utility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = "<group>"; };
|
||||||
C43A8A1F25D9D1D700591B77 /* brew.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = "<group>"; };
|
C43A8A1F25D9D1D700591B77 /* brew-formula.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "brew-formula.json"; sourceTree = "<group>"; };
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewPackageTest.swift; sourceTree = "<group>"; };
|
||||||
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListNameCell.swift; sourceTree = "<group>"; };
|
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListNameCell.swift; sourceTree = "<group>"; };
|
||||||
C44067F627E258410045BD4E /* SiteListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListPhpCell.swift; sourceTree = "<group>"; };
|
C44067F627E258410045BD4E /* DomainListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListPhpCell.swift; sourceTree = "<group>"; };
|
||||||
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTypeCell.swift; sourceTree = "<group>"; };
|
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTLSCell.swift; sourceTree = "<group>"; };
|
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListTLSCell.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>"; };
|
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>"; };
|
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>"; };
|
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "nginx-proxy.test"; sourceTree = "<group>"; };
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListWC.swift; sourceTree = "<group>"; };
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCellProtocol.swift; sourceTree = "<group>"; };
|
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListVC.swift; sourceTree = "<group>"; };
|
||||||
|
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainListCellProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
|
||||||
|
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUpdaterCheckTest.swift; sourceTree = "<group>"; };
|
||||||
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
C46FA23E246C358E00944F05 /* StringExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = "<group>"; };
|
||||||
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
C473319E2470923A009A0597 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = "<group>"; };
|
||||||
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
|
C47331A1247093B7009A0597 /* StatusMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusMenu.swift; sourceTree = "<group>"; };
|
||||||
@@ -317,6 +356,7 @@
|
|||||||
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
C476FF9722B0DD830098105B /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
|
||||||
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
C4811D2322D70A4700B5F6B3 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
|
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
|
||||||
|
C484437A2804BB560041A78A /* ValetProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetProxyScanner.swift; sourceTree = "<group>"; };
|
||||||
C48D0C8F25CC7FD000CC7490 /* StatsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsView.xib; sourceTree = "<group>"; };
|
C48D0C8F25CC7FD000CC7490 /* StatsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = StatsView.xib; sourceTree = "<group>"; };
|
||||||
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
|
C48D0C9225CC804200CC7490 /* XibLoadable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XibLoadable.swift; sourceTree = "<group>"; };
|
||||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
|
||||||
@@ -328,10 +368,10 @@
|
|||||||
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>"; };
|
||||||
C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListKindCell.swift; sourceTree = "<group>"; };
|
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainListKindCell.swift; sourceTree = "<group>"; };
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
|
||||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
|
C4AF9F70275445FF00D44ED0 /* valet-config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "valet-config.json"; sourceTree = "<group>"; };
|
||||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.swift; sourceTree = "<group>"; };
|
C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigurationTest.swift; sourceTree = "<group>"; };
|
||||||
C4AF9F792754499000D44ED0 /* Valet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valet.swift; sourceTree = "<group>"; };
|
C4AF9F792754499000D44ED0 /* Valet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Valet.swift; sourceTree = "<group>"; };
|
||||||
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetVersionExtractorTest.swift; sourceTree = "<group>"; };
|
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetVersionExtractorTest.swift; sourceTree = "<group>"; };
|
||||||
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionExtractor.swift; sourceTree = "<group>"; };
|
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionExtractor.swift; sourceTree = "<group>"; };
|
||||||
@@ -342,6 +382,10 @@
|
|||||||
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
C4B97B74275CF08C003F3378 /* AppDelegate+MenuOutlets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+MenuOutlets.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+ActivationPolicy.swift"; sourceTree = "<group>"; };
|
||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "App+GlobalHotkey.swift"; sourceTree = "<group>"; };
|
||||||
|
C4C0E8DE27F88AEB002D32A9 /* FakeSiteScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeSiteScanner.swift; sourceTree = "<group>"; };
|
||||||
|
C4C0E8E127F88B13002D32A9 /* ValetSiteScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSiteScanner.swift; sourceTree = "<group>"; };
|
||||||
|
C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyScanner.swift; sourceTree = "<group>"; };
|
||||||
|
C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ValetProxy+Fake.swift"; sourceTree = "<group>"; };
|
||||||
C4C1019A27C65C6F001FACC2 /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = "<group>"; };
|
C4C1019A27C65C6F001FACC2 /* Process.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Process.swift; sourceTree = "<group>"; };
|
||||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = "<group>"; };
|
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Startup.swift"; sourceTree = "<group>"; };
|
||||||
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomPrefs.swift; sourceTree = "<group>"; };
|
||||||
@@ -350,12 +394,13 @@
|
|||||||
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>"; };
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
||||||
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
|
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerWindow.swift; sourceTree = "<group>"; };
|
||||||
C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigParser.swift; sourceTree = "<group>"; };
|
C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfiguration.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>"; };
|
||||||
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpHelper.swift; sourceTree = "<group>"; };
|
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpHelper.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>"; };
|
||||||
C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = "<group>"; };
|
C4D9ADC7277611A0007277F4 /* InternalSwitcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalSwitcher.swift; sourceTree = "<group>"; };
|
||||||
|
C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddProxyVC.swift; sourceTree = "<group>"; };
|
||||||
C4DEB7D327A5D60B00834718 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
C4DEB7D327A5D60B00834718 /* Stats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stats.swift; sourceTree = "<group>"; };
|
||||||
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSWindowExtension.swift; sourceTree = "<group>"; };
|
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSWindowExtension.swift; sourceTree = "<group>"; };
|
||||||
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
||||||
@@ -373,14 +418,16 @@
|
|||||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpInstallation.swift; sourceTree = "<group>"; };
|
||||||
C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = "<group>"; };
|
C4F30B02278E16BA00755FCE /* HomebrewService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomebrewService.swift; sourceTree = "<group>"; };
|
||||||
C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = "<group>"; };
|
C4F30B06278E195800755FCE /* brew-services.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "brew-services.json"; sourceTree = "<group>"; };
|
||||||
|
C4F5FBCC28218C93001065C5 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = "<group>"; };
|
||||||
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "phpmon-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
C4F7807D25D7F84B000DBC97 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
C4F7809B25D80344000DBC97 /* CommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTest.swift; sourceTree = "<group>"; };
|
C4F7809B25D80344000DBC97 /* CommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTest.swift; sourceTree = "<group>"; };
|
||||||
C4F780A725D80AE8000DBC97 /* php.ini */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = php.ini; sourceTree = "<group>"; };
|
C4F780A725D80AE8000DBC97 /* php.ini */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = php.ini; sourceTree = "<group>"; };
|
||||||
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionParserTest.swift; sourceTree = "<group>"; };
|
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtensionTest.swift; sourceTree = "<group>"; };
|
||||||
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
|
C4F8C0A322D4F12C002EFE61 /* DateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = "<group>"; };
|
||||||
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
C4F8C0A522D4FA41002EFE61 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; };
|
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpVersionDetectionTest.swift; sourceTree = "<group>"; };
|
||||||
|
C4FE011028084FC200D1DE6D /* SelectionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionVC.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -422,6 +469,7 @@
|
|||||||
C48D6C6E279CD29C00F26D7E /* PHP Version */,
|
C48D6C6E279CD29C00F26D7E /* PHP Version */,
|
||||||
C4D9ADC2277610E4007277F4 /* Switcher */,
|
C4D9ADC2277610E4007277F4 /* Switcher */,
|
||||||
C4F30B01278E169B00755FCE /* Homebrew */,
|
C4F30B01278E169B00755FCE /* Homebrew */,
|
||||||
|
C42337A1281F19DC00459A48 /* Extensions */,
|
||||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
||||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
||||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
||||||
@@ -484,12 +532,10 @@
|
|||||||
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C42CFB1527DFDE7900862737 /* nicoverbruggen.test */,
|
C459B4C127F6097E00E9B4B4 /* php */,
|
||||||
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */,
|
C459B4C027F6096300E9B4B4 /* valet */,
|
||||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
C459B4BF27F6094100E9B4B4 /* brew */,
|
||||||
C43A8A1F25D9D1D700591B77 /* brew.json */,
|
C459B4BE27F6093A00E9B4B4 /* nginx */,
|
||||||
C4F30B06278E195800755FCE /* brew-services.json */,
|
|
||||||
C4F780A725D80AE8000DBC97 /* php.ini */,
|
|
||||||
);
|
);
|
||||||
path = "Test Files";
|
path = "Test Files";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -517,6 +563,7 @@
|
|||||||
C4E713562570150F00007428 /* SECURITY.md */,
|
C4E713562570150F00007428 /* SECURITY.md */,
|
||||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
||||||
54D9E0C027E4F5E9003B9AD9 /* LICENSE */,
|
54D9E0C027E4F5E9003B9AD9 /* LICENSE */,
|
||||||
|
C4F5FBCC28218C93001065C5 /* .swiftlint.yml */,
|
||||||
C4E713572570151400007428 /* docs */,
|
C4E713572570151400007428 /* docs */,
|
||||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||||
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
||||||
@@ -566,7 +613,7 @@
|
|||||||
C4B13B1D25C4915000548C3A /* App */,
|
C4B13B1D25C4915000548C3A /* App */,
|
||||||
C4D9ADBD27761084007277F4 /* PHP */,
|
C4D9ADBD27761084007277F4 /* PHP */,
|
||||||
C47331A0247093AC009A0597 /* Menu */,
|
C47331A0247093AC009A0597 /* Menu */,
|
||||||
C464ADAA275A7A25003FCD53 /* SiteList */,
|
C464ADAA275A7A25003FCD53 /* DomainList */,
|
||||||
5420395726135DB800FB00FA /* Preferences */,
|
5420395726135DB800FB00FA /* Preferences */,
|
||||||
C44C198F276E3A380072762D /* Progress */,
|
C44C198F276E3A380072762D /* Progress */,
|
||||||
C4C8E81D276F5686003AC782 /* Watcher */,
|
C4C8E81D276F5686003AC782 /* Watcher */,
|
||||||
@@ -575,15 +622,23 @@
|
|||||||
path = Domain;
|
path = Domain;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C42337A1281F19DC00459A48 /* Extensions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C42337A2281F19F000459A48 /* Xdebug.swift */,
|
||||||
|
);
|
||||||
|
path = Extensions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44067F327E256560045BD4E /* Cells */ = {
|
C44067F327E256560045BD4E /* Cells */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */,
|
C464ADB1275A87CA003FCD53 /* DomainListCellProtocol.swift */,
|
||||||
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */,
|
C44067FA27E25FD70045BD4E /* DomainListTLSCell.swift */,
|
||||||
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */,
|
C44067F427E2582B0045BD4E /* DomainListNameCell.swift */,
|
||||||
C44067F627E258410045BD4E /* SiteListPhpCell.swift */,
|
C44067F627E258410045BD4E /* DomainListPhpCell.swift */,
|
||||||
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */,
|
C44067F827E2585E0045BD4E /* DomainListTypeCell.swift */,
|
||||||
C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */,
|
C4AC51FB27E27F47008528CA /* DomainListKindCell.swift */,
|
||||||
);
|
);
|
||||||
path = Cells;
|
path = Cells;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -606,17 +661,56 @@
|
|||||||
path = Errors;
|
path = Errors;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
C459B4BE27F6093A00E9B4B4 /* nginx */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C459B4BC27F6093700E9B4B4 /* nginx-proxy.test */,
|
||||||
|
C42F26752805FEE200938AC7 /* nginx-secure-proxy.test */,
|
||||||
|
54A18D3F282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test */,
|
||||||
|
C42CFB1527DFDE7900862737 /* nginx-site.test */,
|
||||||
|
C42CFB1727DFDFDC00862737 /* nginx-site-isolated.test */,
|
||||||
|
);
|
||||||
|
path = nginx;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C459B4BF27F6094100E9B4B4 /* brew */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C43A8A1F25D9D1D700591B77 /* brew-formula.json */,
|
||||||
|
C4F30B06278E195800755FCE /* brew-services.json */,
|
||||||
|
);
|
||||||
|
path = brew;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C459B4C027F6096300E9B4B4 /* valet */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
||||||
|
);
|
||||||
|
path = valet;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C459B4C127F6097E00E9B4B4 /* php */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4F780A725D80AE8000DBC97 /* php.ini */,
|
||||||
|
);
|
||||||
|
path = php;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C464ADAA275A7A25003FCD53 /* DomainList */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C44067F327E256560045BD4E /* Cells */,
|
C44067F327E256560045BD4E /* Cells */,
|
||||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
C464ADAB275A7A3F003FCD53 /* DomainListWC.swift */,
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
C464ADAE275A7A69003FCD53 /* DomainListVC.swift */,
|
||||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
C41E87192763D42300161EE0 /* DomainListVC+ContextMenu.swift */,
|
||||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
C41CA5EC2774F8EE00A2C80E /* DomainListVC+Actions.swift */,
|
||||||
|
C4FE011028084FC200D1DE6D /* SelectionVC.swift */,
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
||||||
|
C4D9F24A280B69E100DCD39A /* AddProxyVC.swift */,
|
||||||
);
|
);
|
||||||
path = SiteList;
|
path = DomainList;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
C47331A0247093AC009A0597 /* Menu */ = {
|
C47331A0247093AC009A0597 /* Menu */ = {
|
||||||
@@ -666,10 +760,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
||||||
C4E4404527C56F4700D225E1 /* ValetSite.swift */,
|
C42F26722805B4B400938AC7 /* DomainListable.swift */,
|
||||||
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */,
|
C4C0E8D927F887BD002D32A9 /* Proxies */,
|
||||||
C41C02A527E60D7A009F26CB /* SiteScanner.swift */,
|
C4C0E8D827F887A5002D32A9 /* Sites */,
|
||||||
C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */,
|
|
||||||
);
|
);
|
||||||
path = Valet;
|
path = Valet;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -677,6 +770,7 @@
|
|||||||
C4AF9F6B275445D300D44ED0 /* Integrations */ = {
|
C4AF9F6B275445D300D44ED0 /* Integrations */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C4C0E8DA27F887CC002D32A9 /* Nginx */,
|
||||||
C4D89BC42783C98800A02B68 /* Composer */,
|
C4D89BC42783C98800A02B68 /* Composer */,
|
||||||
C4AF9F6C275445D900D44ED0 /* Homebrew */,
|
C4AF9F6C275445D900D44ED0 /* Homebrew */,
|
||||||
C4AF9F6A275445C900D44ED0 /* Valet */,
|
C4AF9F6A275445C900D44ED0 /* Valet */,
|
||||||
@@ -705,6 +799,8 @@
|
|||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
|
C46E206C28299B3800D909D6 /* AppUpdateChecker.swift */,
|
||||||
|
C40FE736282ABA4F00A302C2 /* AppVersion.swift */,
|
||||||
);
|
);
|
||||||
path = App;
|
path = App;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -721,13 +817,60 @@
|
|||||||
path = Common;
|
path = Common;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4C0E8D827F887A5002D32A9 /* Sites */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */,
|
||||||
|
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */,
|
||||||
|
C4C0E8E427F88B1F002D32A9 /* SiteScanner */,
|
||||||
|
);
|
||||||
|
path = Sites;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C0E8D927F887BD002D32A9 /* Proxies */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4205A7D27F4D21800191A39 /* ValetProxy.swift */,
|
||||||
|
C4C0E8E927F88B80002D32A9 /* ValetProxy+Fake.swift */,
|
||||||
|
C4C0E8E527F88B36002D32A9 /* ProxyScanner */,
|
||||||
|
);
|
||||||
|
path = Proxies;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C0E8DA27F887CC002D32A9 /* Nginx */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4D5CFC927E0F9CD00035329 /* NginxConfiguration.swift */,
|
||||||
|
);
|
||||||
|
path = Nginx;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C0E8E427F88B1F002D32A9 /* SiteScanner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C41C02A527E60D7A009F26CB /* SiteScanner.swift */,
|
||||||
|
C4C0E8E127F88B13002D32A9 /* ValetSiteScanner.swift */,
|
||||||
|
C4C0E8DE27F88AEB002D32A9 /* FakeSiteScanner.swift */,
|
||||||
|
);
|
||||||
|
path = SiteScanner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C0E8E527F88B36002D32A9 /* ProxyScanner */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4C0E8E627F88B41002D32A9 /* ProxyScanner.swift */,
|
||||||
|
C484437A2804BB560041A78A /* ValetProxyScanner.swift */,
|
||||||
|
);
|
||||||
|
path = ProxyScanner;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C4C1019727C65A11001FACC2 /* Parsers */ = {
|
C4C1019727C65A11001FACC2 /* Parsers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
C4AF9F76275447F100D44ED0 /* ValetConfigurationTest.swift */,
|
||||||
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
C4F780AD25D80B37000DBC97 /* PhpExtensionTest.swift */,
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
C43A8A2325D9D20D00591B77 /* HomebrewPackageTest.swift */,
|
||||||
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */,
|
C42CFB1927DFE8BD00862737 /* NginxConfigurationTest.swift */,
|
||||||
);
|
);
|
||||||
path = Parsers;
|
path = Parsers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -739,6 +882,8 @@
|
|||||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */,
|
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */,
|
||||||
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */,
|
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */,
|
||||||
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */,
|
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */,
|
||||||
|
C40FE739282ABB2E00A302C2 /* AppVersionTest.swift */,
|
||||||
|
C46E206F2829D27F00D909D6 /* AppUpdaterCheckTest.swift */,
|
||||||
);
|
);
|
||||||
path = Versions;
|
path = Versions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -849,6 +994,7 @@
|
|||||||
C41C1B2F22B0097F00E7CF16 /* Sources */,
|
C41C1B2F22B0097F00E7CF16 /* Sources */,
|
||||||
C41C1B3022B0097F00E7CF16 /* Frameworks */,
|
C41C1B3022B0097F00E7CF16 /* Frameworks */,
|
||||||
C41C1B3122B0097F00E7CF16 /* Resources */,
|
C41C1B3122B0097F00E7CF16 /* Resources */,
|
||||||
|
C4F5FBCB28216985001065C5 /* Run `swiftlint` */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -949,19 +1095,43 @@
|
|||||||
files = (
|
files = (
|
||||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */,
|
||||||
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
54FCFD2E276C8D67004CE748 /* HotkeyPreferenceView.xib in Resources */,
|
||||||
C42CFB1827DFDFDC00862737 /* nicoverbruggen_isolated.test in Resources */,
|
C42CFB1827DFDFDC00862737 /* nginx-site-isolated.test in Resources */,
|
||||||
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
C4F780A825D80AE8000DBC97 /* php.ini in Resources */,
|
||||||
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
C4068CA527B0780A00544CD5 /* CheckboxPreferenceView.xib in Resources */,
|
||||||
C43A8A2025D9D1D700591B77 /* brew.json in Resources */,
|
C43A8A2025D9D1D700591B77 /* brew-formula.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 */,
|
||||||
|
C42F26762805FEE200938AC7 /* nginx-secure-proxy.test in Resources */,
|
||||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
||||||
C42CFB1627DFDE7900862737 /* nicoverbruggen.test in Resources */,
|
54A18D40282A566E000A0D81 /* nginx-secure-proxy-custom-tld.test in Resources */,
|
||||||
|
C42CFB1627DFDE7900862737 /* nginx-site.test in Resources */,
|
||||||
|
C459B4BD27F6093700E9B4B4 /* nginx-proxy.test in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXResourcesBuildPhase section */
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXShellScriptBuildPhase section */
|
||||||
|
C4F5FBCB28216985001065C5 /* Run `swiftlint` */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = "Run `swiftlint`";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\n\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
|
||||||
|
};
|
||||||
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
C41C1B2F22B0097F00E7CF16 /* Sources */ = {
|
C41C1B2F22B0097F00E7CF16 /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
@@ -975,6 +1145,7 @@
|
|||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||||
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
|
||||||
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7A2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
|
C4C0E8EA27F88B80002D32A9 /* ValetProxy+Fake.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 */,
|
C4068CA727B07A1300544CD5 /* SelectPreferenceView.swift in Sources */,
|
||||||
@@ -982,27 +1153,32 @@
|
|||||||
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
C4E0F7ED27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||||
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
C49E171F27A5736E00787921 /* PMServicesView.swift in Sources */,
|
||||||
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C4EE55AD27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||||
|
C4205A7E27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
54FCFD30276C8DA4004CE748 /* HotkeyPreferenceView.swift in Sources */,
|
||||||
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
|
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */,
|
||||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */,
|
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */,
|
||||||
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
C4F2E43A2752F7D00020E974 /* PhpInstallation.swift in Sources */,
|
||||||
C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */,
|
C4D9F24B280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
||||||
|
C41E871A2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */,
|
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */,
|
||||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */,
|
||||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
||||||
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
|
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
|
C4C0E8DF27F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
|
||||||
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
|
C40FE737282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
C4B585442770FE3900DA4FBE /* Command.swift in Sources */,
|
||||||
C44067F527E2582B0045BD4E /* SiteListNameCell.swift in Sources */,
|
C44067F527E2582B0045BD4E /* DomainListNameCell.swift in Sources */,
|
||||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */,
|
C4EE55AB27708B9E001DF387 /* Preview.swift in Sources */,
|
||||||
C44067F727E258410045BD4E /* SiteListPhpCell.swift in Sources */,
|
C44067F727E258410045BD4E /* DomainListPhpCell.swift in Sources */,
|
||||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
||||||
C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */,
|
C4AC51FC27E27F47008528CA /* DomainListKindCell.swift in Sources */,
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
||||||
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
|
C4C0E8E727F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
||||||
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 */,
|
||||||
@@ -1012,29 +1188,34 @@
|
|||||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B03278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
|
C4C0E8E227F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
||||||
|
C42F26732805B4B400938AC7 /* DomainListable.swift in Sources */,
|
||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */,
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */,
|
||||||
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
C48D0C9325CC804200CC7490 /* XibLoadable.swift in Sources */,
|
||||||
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||||
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
||||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
||||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5ED2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||||
|
C46E206D28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
||||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||||
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADBF277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.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 */,
|
||||||
|
C484437B2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */,
|
||||||
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759672627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */,
|
C464ADAF275A7A69003FCD53 /* DomainListVC.swift in Sources */,
|
||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
||||||
C44067FB27E25FD70045BD4E /* SiteListTLSCell.swift in Sources */,
|
C44067FB27E25FD70045BD4E /* DomainListTLSCell.swift in Sources */,
|
||||||
C4927F0B27B2DFC200C55AFD /* Errors.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 */,
|
||||||
|
C4FE011128084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
||||||
C44CCD4027AFE2FC00CE40E5 /* AlertableError.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 */,
|
||||||
@@ -1043,7 +1224,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 */,
|
||||||
C4D5CFCA27E0F9CD00035329 /* NginxConfigParser.swift in Sources */,
|
C4D5CFCA27E0F9CD00035329 /* NginxConfiguration.swift in Sources */,
|
||||||
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
@@ -1051,15 +1232,16 @@
|
|||||||
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||||
C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */,
|
C44067F927E2585E0045BD4E /* DomainListTypeCell.swift in Sources */,
|
||||||
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||||
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */,
|
C4C3ED412783497000AB15D8 /* MainMenu+Startup.swift in Sources */,
|
||||||
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */,
|
C4D89BC62783C99400A02B68 /* ComposerJson.swift in Sources */,
|
||||||
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
|
C46FA23F246C358E00944F05 /* StringExtension.swift in Sources */,
|
||||||
|
C42337A3281F19F000459A48 /* Xdebug.swift in Sources */,
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
||||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
C464ADAC275A7A3F003FCD53 /* DomainListWC.swift in Sources */,
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.swift in Sources */,
|
C464ADB2275A87CA003FCD53 /* DomainListCellProtocol.swift in Sources */,
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */,
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
|
C4DEB7D427A5D60B00834718 /* Stats.swift in Sources */,
|
||||||
@@ -1070,15 +1252,18 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C449B4F427EE7FC800C47E8A /* SiteListKindCell.swift in Sources */,
|
C449B4F427EE7FC800C47E8A /* DomainListKindCell.swift in Sources */,
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
C41CA5EE2774F8EE00A2C80E /* DomainListVC+Actions.swift in Sources */,
|
||||||
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
|
C4205A7F27F4D21800191A39 /* ValetProxy.swift in Sources */,
|
||||||
|
C42F26742805B4B400938AC7 /* DomainListable.swift in Sources */,
|
||||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
C4F780C425D80B75000DBC97 /* MainMenu.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 */,
|
||||||
|
C4FE011228084FC200D1DE6D /* SelectionVC.swift in Sources */,
|
||||||
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
C4F780C825D80B75000DBC97 /* DateExtension.swift in Sources */,
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */,
|
||||||
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
C4D9ADC0277610E1007277F4 /* PhpSwitcher.swift in Sources */,
|
||||||
@@ -1087,13 +1272,14 @@
|
|||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.swift in Sources */,
|
C4D5CFCB27E0F9CD00035329 /* NginxConfiguration.swift in Sources */,
|
||||||
C4068CA827B07A1300544CD5 /* SelectPreferenceView.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 */,
|
||||||
C449B4F027EE7FB800C47E8A /* SiteListTLSCell.swift in Sources */,
|
C4C0E8E827F88B41002D32A9 /* ProxyScanner.swift in Sources */,
|
||||||
|
C449B4F027EE7FB800C47E8A /* DomainListTLSCell.swift in Sources */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* HomebrewPackageTest.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 */,
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||||
@@ -1102,27 +1288,31 @@
|
|||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
|
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
|
C4C0E8E327F88B13002D32A9 /* ValetSiteScanner.swift in Sources */,
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
|
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
|
||||||
|
C4C0E8EB27F88B80002D32A9 /* ValetProxy+Fake.swift in Sources */,
|
||||||
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* PhpExtensionTest.swift in Sources */,
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
||||||
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */,
|
||||||
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
C4EED88A27A48778006D7272 /* InterAppHandler.swift in Sources */,
|
||||||
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
C48D6C75279CD3E400F26D7E /* PhpVersionNumberTest.swift in Sources */,
|
||||||
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
C43603A1275E67610028EFC6 /* AppDelegate+Notifications.swift in Sources */,
|
||||||
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
C42759682627662800093CAE /* NSMenuExtension.swift in Sources */,
|
||||||
C4D936CB27E3EE4A00BD69FE /* SiteListCellProtocol.swift in Sources */,
|
C4D936CB27E3EE4A00BD69FE /* DomainListCellProtocol.swift in Sources */,
|
||||||
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B76275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
C4F780CD25D80B75000DBC97 /* Alert.swift in Sources */,
|
||||||
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
|
||||||
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */,
|
C41E871B2763D42300161EE0 /* DomainListVC+ContextMenu.swift in Sources */,
|
||||||
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
C40C7F3127722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */,
|
||||||
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
C4F30B09278E1A0E00755FCE /* CustomPrefs.swift in Sources */,
|
||||||
|
C40FE738282ABA4F00A302C2 /* AppVersion.swift in Sources */,
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
C484437C2804BB560041A78A /* ValetProxyScanner.swift in Sources */,
|
||||||
|
C4AF9F78275447F100D44ED0 /* ValetConfigurationTest.swift in Sources */,
|
||||||
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */,
|
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */,
|
||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */,
|
||||||
@@ -1131,10 +1321,11 @@
|
|||||||
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */,
|
||||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */,
|
||||||
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
C4CE3BBB27B324230086CA49 /* MainMenu+Switcher.swift in Sources */,
|
||||||
|
C46E20702829D27F00D909D6 /* AppUpdaterCheckTest.swift in Sources */,
|
||||||
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
|
||||||
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
C44CCD4127AFE2FC00CE40E5 /* AlertableError.swift in Sources */,
|
||||||
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
C449B4F127EE7FC200C47E8A /* SiteListNameCell.swift in Sources */,
|
C449B4F127EE7FC200C47E8A /* DomainListNameCell.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 */,
|
||||||
@@ -1144,11 +1335,13 @@
|
|||||||
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C44C198E276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
||||||
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
C415938027A1B54F00D2E1B7 /* PhpFrameworks.swift in Sources */,
|
||||||
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC9277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
C449B4F227EE7FC400C47E8A /* SiteListPhpCell.swift in Sources */,
|
C449B4F227EE7FC400C47E8A /* DomainListPhpCell.swift in Sources */,
|
||||||
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */,
|
C42CFB1A27DFE8BD00862737 /* NginxConfigurationTest.swift in Sources */,
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
||||||
|
C4F5FBCD28218CB8001065C5 /* Xdebug.swift in Sources */,
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||||
|
C4C0E8E027F88AEB002D32A9 /* FakeSiteScanner.swift in Sources */,
|
||||||
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
|
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */,
|
||||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
|
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */,
|
||||||
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
|
C4B585452770FE3900DA4FBE /* Command.swift in Sources */,
|
||||||
@@ -1158,22 +1351,25 @@
|
|||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
||||||
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
|
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */,
|
||||||
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4A27AFF3BC00CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
C449B4F327EE7FC600C47E8A /* SiteListTypeCell.swift in Sources */,
|
C449B4F327EE7FC600C47E8A /* DomainListTypeCell.swift in Sources */,
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
||||||
|
C4D9F24C280B69E100DCD39A /* AddProxyVC.swift in Sources */,
|
||||||
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
C4B5853F2770FE3900DA4FBE /* Paths.swift in Sources */,
|
||||||
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
C481F79A26164A7C004FBCFF /* Preferences.swift in Sources */,
|
||||||
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */,
|
||||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
C464ADAD275A7A3F003FCD53 /* DomainListWC.swift in Sources */,
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
||||||
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */,
|
C4F780CB25D80B75000DBC97 /* StatsView.swift in Sources */,
|
||||||
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */,
|
C464ADB0275A7A6A003FCD53 /* DomainListVC.swift in Sources */,
|
||||||
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
|
C43A8A1A25D9CD1000591B77 /* Utility.swift in Sources */,
|
||||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */,
|
||||||
|
C40FE73B282ABB2E00A302C2 /* AppVersionTest.swift in Sources */,
|
||||||
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
|
C4F780C625D80B75000DBC97 /* XibLoadable.swift in Sources */,
|
||||||
C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
C4EE55AA27708B9E001DF387 /* PMHeaderView.swift in Sources */,
|
||||||
|
C46E206E28299B3800D909D6 /* AppUpdateChecker.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -1324,7 +1520,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 = 761;
|
CURRENT_PROJECT_VERSION = 786;
|
||||||
DEBUG = YES;
|
DEBUG = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1334,7 +1530,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.2.2;
|
MARKETING_VERSION = 5.3;
|
||||||
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 = "";
|
||||||
@@ -1350,7 +1546,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 = 761;
|
CURRENT_PROJECT_VERSION = 786;
|
||||||
DEBUG = NO;
|
DEBUG = NO;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -1360,7 +1556,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.2.2;
|
MARKETING_VERSION = 5.3;
|
||||||
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 = "";
|
||||||
|
|||||||
@@ -61,6 +61,12 @@
|
|||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--v"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
</CommandLineArguments>
|
||||||
<EnvironmentVariables>
|
<EnvironmentVariables>
|
||||||
<EnvironmentVariable
|
<EnvironmentVariable
|
||||||
key = "PHPMON_MARKETING_MODE"
|
key = "PHPMON_MARKETING_MODE"
|
||||||
|
|||||||
17
README.md
17
README.md
@@ -149,6 +149,15 @@ This should install `dnsmasq` and set up Valet. Great, almost there!
|
|||||||
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
Finally, run PHP Monitor. Since the app is notarized and signed with a developer ID, it should work.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>How frequently does PHP Monitor check for updates?</strong></summary>
|
||||||
|
|
||||||
|
PHP Monitor will check if an update is available every time you start the app.
|
||||||
|
|
||||||
|
You can disable this behaviour by going to Preferences (via the PHP Monitor icon in the menu bar) and unchecking "Automatically check for updates". You can always check for updates manually.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>I have PHP Monitor installed, and it works. I want to upgrade my PHP installations to the latest version, what's the best way to do this?</strong></summary>
|
<summary><strong>I have PHP Monitor installed, and it works. I want to upgrade my PHP installations to the latest version, what's the best way to do this?</strong></summary>
|
||||||
|
|
||||||
@@ -278,9 +287,13 @@ PHP Monitor is a universal app and supports both architectures, so [find out her
|
|||||||
<details>
|
<details>
|
||||||
<summary><strong>Why is the app doing network requests?</strong></summary>
|
<summary><strong>Why is the app doing network requests?</strong></summary>
|
||||||
|
|
||||||
It's Homebrew. I can't prevent `brew` from doing things via the network when I invoke it.
|
The app will automatically check for updates, which is the most likely culprit.
|
||||||
|
|
||||||
PHP Monitor itself doesn't do any network requests. Feel free to check the source code or intercept the traffic, if you don't believe me.
|
This happens at launch (unless disabled), and the app directly checks the Caskfile hosted on GitHub. This data is not, and will not be used for analytics (and, as far as I can tell, cannot).
|
||||||
|
|
||||||
|
I also can't prevent `brew` from doing things via the network when PHP Monitor uses the binary.
|
||||||
|
|
||||||
|
The app includes an Internet Access Policy file, so if you're using something like Little Snitch there should be a description why these calls occur.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 13/02/2021.
|
// Created by Nico Verbruggen on 13/02/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@@ -15,11 +15,11 @@ class CommandTest: XCTestCase {
|
|||||||
path: Paths.php,
|
path: Paths.php,
|
||||||
arguments: ["-v"]
|
arguments: ["-v"]
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssert(version.contains("(cli)"))
|
XCTAssert(version.contains("(cli)"))
|
||||||
XCTAssert(version.contains("NTS"))
|
XCTAssert(version.contains("NTS"))
|
||||||
XCTAssert(version.contains("built"))
|
XCTAssert(version.contains("built"))
|
||||||
XCTAssert(version.contains("Zend"))
|
XCTAssert(version.contains("Zend"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,18 +3,18 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 14/02/2021.
|
// Created by Nico Verbruggen on 14/02/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class BrewJsonParserTest: XCTestCase {
|
class HomebrewPackageTest: XCTestCase {
|
||||||
|
|
||||||
// - MARK: SYNTHETIC TESTS
|
// - MARK: SYNTHETIC TESTS
|
||||||
|
|
||||||
static var jsonBrewFile: URL {
|
static var jsonBrewFile: URL {
|
||||||
return Bundle(for: Self.self)
|
return Bundle(for: Self.self)
|
||||||
.url(forResource: "brew", withExtension: "json")!
|
.url(forResource: "brew-formula", withExtension: "json")!
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanLoadExtensionJson() throws {
|
func testCanLoadExtensionJson() throws {
|
||||||
@@ -22,7 +22,7 @@ class BrewJsonParserTest: XCTestCase {
|
|||||||
let package = try! JSONDecoder().decode(
|
let package = try! JSONDecoder().decode(
|
||||||
[HomebrewPackage].self, from: json.data(using: .utf8)!
|
[HomebrewPackage].self, from: json.data(using: .utf8)!
|
||||||
).first!
|
).first!
|
||||||
|
|
||||||
XCTAssertEqual(package.name, "php")
|
XCTAssertEqual(package.name, "php")
|
||||||
XCTAssertEqual(package.full_name, "php")
|
XCTAssertEqual(package.full_name, "php")
|
||||||
XCTAssertEqual(package.aliases.first!, "php@8.0")
|
XCTAssertEqual(package.aliases.first!, "php@8.0")
|
||||||
@@ -30,23 +30,23 @@ class BrewJsonParserTest: XCTestCase {
|
|||||||
installed.version.starts(with: "8.0")
|
installed.version.starts(with: "8.0")
|
||||||
}), true)
|
}), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
static var jsonBrewServicesFile: URL {
|
static var jsonBrewServicesFile: URL {
|
||||||
return Bundle(for: Self.self)
|
return Bundle(for: Self.self)
|
||||||
.url(forResource: "brew-services", withExtension: "json")!
|
.url(forResource: "brew-services", withExtension: "json")!
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanParseServicesJson() throws {
|
func testCanParseServicesJson() throws {
|
||||||
let json = try! String(contentsOf: Self.jsonBrewServicesFile, encoding: .utf8)
|
let json = try! String(contentsOf: Self.jsonBrewServicesFile, encoding: .utf8)
|
||||||
let services = try! JSONDecoder().decode(
|
let services = try! JSONDecoder().decode(
|
||||||
[HomebrewService].self, from: json.data(using: .utf8)!
|
[HomebrewService].self, from: json.data(using: .utf8)!
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertGreaterThan(services.count, 0)
|
XCTAssertGreaterThan(services.count, 0)
|
||||||
XCTAssertEqual(services.first?.name, "dnsmasq")
|
XCTAssertEqual(services.first?.name, "dnsmasq")
|
||||||
XCTAssertEqual(services.first?.service_name, "homebrew.mxcl.dnsmasq")
|
XCTAssertEqual(services.first?.service_name, "homebrew.mxcl.dnsmasq")
|
||||||
}
|
}
|
||||||
|
|
||||||
// - MARK: LIVE TESTS
|
// - MARK: LIVE TESTS
|
||||||
|
|
||||||
/// This test requires that you have a valid Homebrew installation set up,
|
/// This test requires that you have a valid Homebrew installation set up,
|
||||||
@@ -63,13 +63,13 @@ class BrewJsonParserTest: XCTestCase {
|
|||||||
).filter({ service in
|
).filter({ service in
|
||||||
return ["php", "nginx", "dnsmasq"].contains(service.name)
|
return ["php", "nginx", "dnsmasq"].contains(service.name)
|
||||||
})
|
})
|
||||||
|
|
||||||
XCTAssertTrue(services.contains(where: {$0.name == "php"} ))
|
XCTAssertTrue(services.contains(where: {$0.name == "php"}))
|
||||||
XCTAssertTrue(services.contains(where: {$0.name == "nginx"} ))
|
XCTAssertTrue(services.contains(where: {$0.name == "nginx"}))
|
||||||
XCTAssertTrue(services.contains(where: {$0.name == "dnsmasq"} ))
|
XCTAssertTrue(services.contains(where: {$0.name == "dnsmasq"}))
|
||||||
XCTAssertEqual(services.count, 3)
|
XCTAssertEqual(services.count, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This test requires that you have a valid Homebrew installation set up,
|
/// This test requires that you have a valid Homebrew installation set up,
|
||||||
/// and requires the `php` formula to be installed.
|
/// and requires the `php` formula to be installed.
|
||||||
/// If this test fails, there is an issue with your Homebrew installation
|
/// If this test fails, there is an issue with your Homebrew installation
|
||||||
@@ -79,7 +79,7 @@ class BrewJsonParserTest: XCTestCase {
|
|||||||
[HomebrewPackage].self,
|
[HomebrewPackage].self,
|
||||||
from: Shell.pipe("\(Paths.brew) info php --json", requiresPath: true).data(using: .utf8)!
|
from: Shell.pipe("\(Paths.brew) info php --json", requiresPath: true).data(using: .utf8)!
|
||||||
).first!
|
).first!
|
||||||
|
|
||||||
XCTAssertTrue(package.name == "php")
|
XCTAssertTrue(package.name == "php")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// NginxConfigParserTest.swift
|
|
||||||
// phpmon-tests
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 29/11/2021.
|
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
class NginxConfigParserTest: XCTestCase {
|
|
||||||
|
|
||||||
static var regularUrl: URL {
|
|
||||||
return Bundle(for: Self.self).url(forResource: "nicoverbruggen", withExtension: "test")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var isolatedUrl: URL {
|
|
||||||
return Bundle(for: Self.self).url(forResource: "nicoverbruggen_isolated", withExtension: "test")!
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCanDetermineIsolation() throws {
|
|
||||||
XCTAssertNil(
|
|
||||||
NginxConfigParser(filePath: NginxConfigParserTest.regularUrl.path).isolatedVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertEqual(
|
|
||||||
"8.1",
|
|
||||||
NginxConfigParser(filePath: NginxConfigParserTest.isolatedUrl.path).isolatedVersion
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
81
phpmon-tests/Parsers/NginxConfigurationTest.swift
Normal file
81
phpmon-tests/Parsers/NginxConfigurationTest.swift
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
//
|
||||||
|
// NginxConfigurationTest.swift
|
||||||
|
// phpmon-tests
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 29/11/2021.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class NginxConfigurationTest: XCTestCase {
|
||||||
|
|
||||||
|
// MARK: - Test Files
|
||||||
|
|
||||||
|
static var regularUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nginx-site", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var isolatedUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nginx-site-isolated", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var proxyUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nginx-proxy", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var secureProxyUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nginx-secure-proxy", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
static var customTldProxyUrl: URL {
|
||||||
|
return Bundle(for: Self.self).url(forResource: "nginx-secure-proxy-custom-tld", withExtension: "test")!
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Tests
|
||||||
|
|
||||||
|
func testCanDetermineSiteNameAndTld() throws {
|
||||||
|
XCTAssertEqual(
|
||||||
|
"nginx-site",
|
||||||
|
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.domain
|
||||||
|
)
|
||||||
|
XCTAssertEqual(
|
||||||
|
"test",
|
||||||
|
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.tld
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanDetermineIsolation() throws {
|
||||||
|
XCTAssertNil(
|
||||||
|
NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)?.isolatedVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
XCTAssertEqual(
|
||||||
|
"8.1",
|
||||||
|
NginxConfiguration.from(filePath: NginxConfigurationTest.isolatedUrl.path)?.isolatedVersion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanDetermineProxy() throws {
|
||||||
|
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.proxyUrl.path)!
|
||||||
|
XCTAssertTrue(proxied.contents.contains("# valet stub: proxy.valet.conf"))
|
||||||
|
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
||||||
|
|
||||||
|
let normal = NginxConfiguration.from(filePath: NginxConfigurationTest.regularUrl.path)!
|
||||||
|
XCTAssertFalse(normal.contents.contains("# valet stub: proxy.valet.conf"))
|
||||||
|
XCTAssertEqual(nil, normal.proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanDetermineSecuredProxy() throws {
|
||||||
|
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.secureProxyUrl.path)!
|
||||||
|
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
||||||
|
XCTAssertEqual("http://127.0.0.1:90", proxied.proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanDetermineProxyWithCustomTld() throws {
|
||||||
|
let proxied = NginxConfiguration.from(filePath: NginxConfigurationTest.customTldProxyUrl.path)!
|
||||||
|
XCTAssertTrue(proxied.contents.contains("# valet stub: secure.proxy.valet.conf"))
|
||||||
|
XCTAssertEqual("http://localhost:8080", proxied.proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,30 +3,30 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 13/02/2021.
|
// Created by Nico Verbruggen on 13/02/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class ExtensionParserTest: XCTestCase {
|
class PhpExtensionTest: XCTestCase {
|
||||||
|
|
||||||
static var phpIniFileUrl: URL {
|
static var phpIniFileUrl: URL {
|
||||||
return Bundle(for: Self.self).url(forResource: "php", withExtension: "ini")!
|
return Bundle(for: Self.self).url(forResource: "php", withExtension: "ini")!
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanLoadExtension() throws {
|
func testCanLoadExtension() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
||||||
|
|
||||||
XCTAssertGreaterThan(extensions.count, 0)
|
XCTAssertGreaterThan(extensions.count, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExtensionNameIsCorrect() throws {
|
func testExtensionNameIsCorrect() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
||||||
|
|
||||||
let extensionNames = extensions.map { (ext) -> String in
|
let extensionNames = extensions.map { (ext) -> String in
|
||||||
return ext.name
|
return ext.name
|
||||||
}
|
}
|
||||||
|
|
||||||
// These 6 should be found
|
// These 6 should be found
|
||||||
XCTAssertTrue(extensionNames.contains("xdebug"))
|
XCTAssertTrue(extensionNames.contains("xdebug"))
|
||||||
XCTAssertTrue(extensionNames.contains("imagick"))
|
XCTAssertTrue(extensionNames.contains("imagick"))
|
||||||
@@ -34,39 +34,44 @@ class ExtensionParserTest: XCTestCase {
|
|||||||
XCTAssertTrue(extensionNames.contains("opcache"))
|
XCTAssertTrue(extensionNames.contains("opcache"))
|
||||||
XCTAssertTrue(extensionNames.contains("yaml"))
|
XCTAssertTrue(extensionNames.contains("yaml"))
|
||||||
XCTAssertTrue(extensionNames.contains("custom"))
|
XCTAssertTrue(extensionNames.contains("custom"))
|
||||||
|
|
||||||
XCTAssertFalse(extensionNames.contains("fake"))
|
XCTAssertFalse(extensionNames.contains("fake"))
|
||||||
XCTAssertFalse(extensionNames.contains("nice"))
|
XCTAssertFalse(extensionNames.contains("nice"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExtensionStatusIsCorrect() throws {
|
func testExtensionStatusIsCorrect() throws {
|
||||||
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
let extensions = PhpExtension.load(from: Self.phpIniFileUrl)
|
||||||
|
|
||||||
// xdebug should be enabled
|
// xdebug should be enabled
|
||||||
XCTAssertEqual(extensions[0].enabled, true)
|
XCTAssertEqual(extensions[0].enabled, true)
|
||||||
|
|
||||||
// imagick should be disabled
|
// imagick should be disabled
|
||||||
XCTAssertEqual(extensions[1].enabled, false)
|
XCTAssertEqual(extensions[1].enabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testToggleWorksAsExpected() throws {
|
func testToggleWorksAsExpected() throws {
|
||||||
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
let destination = Utility.copyToTemporaryFile(resourceName: "php", fileExtension: "ini")!
|
||||||
let extensions = PhpExtension.load(from: destination)
|
let extensions = PhpExtension.load(from: destination)
|
||||||
XCTAssertEqual(extensions.count, 6)
|
XCTAssertEqual(extensions.count, 6)
|
||||||
|
|
||||||
// Try to disable xdebug (should be detected first)!
|
// Try to disable xdebug (should be detected first)!
|
||||||
let xdebug = extensions.first!
|
let xdebug = extensions.first!
|
||||||
XCTAssertTrue(xdebug.name == "xdebug")
|
XCTAssertTrue(xdebug.name == "xdebug")
|
||||||
XCTAssertEqual(xdebug.enabled, true)
|
XCTAssertEqual(xdebug.enabled, true)
|
||||||
xdebug.toggle()
|
xdebug.toggle()
|
||||||
XCTAssertEqual(xdebug.enabled, false)
|
XCTAssertEqual(xdebug.enabled, false)
|
||||||
|
|
||||||
// Check if the file contains the appropriate data
|
// Check if the file contains the appropriate data
|
||||||
let file = try! String(contentsOf: destination, encoding: .utf8)
|
let file = try! String(contentsOf: destination, encoding: .utf8)
|
||||||
XCTAssertTrue(file.contains("; zend_extension=\"xdebug.so\""))
|
XCTAssertTrue(file.contains("; zend_extension=\"xdebug.so\""))
|
||||||
|
|
||||||
// Make sure if we load the data again, it's disabled
|
// Make sure if we load the data again, it's disabled
|
||||||
XCTAssertEqual(PhpExtension.load(from: destination).first!.enabled, false)
|
XCTAssertEqual(PhpExtension.load(from: destination).first!.enabled, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testCanRetrieveXdebugMode() throws {
|
||||||
|
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('xdebug.mode');"])
|
||||||
|
XCTAssertEqual(value, "coverage")
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,20 +3,20 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 29/11/2021.
|
// Created by Nico Verbruggen on 29/11/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class ValetConfigParserTest: XCTestCase {
|
class ValetConfigurationTest: XCTestCase {
|
||||||
|
|
||||||
static var jsonConfigFileUrl: URL {
|
static var jsonConfigFileUrl: URL {
|
||||||
return Bundle(for: Self.self).url(
|
return Bundle(for: Self.self).url(
|
||||||
forResource: "valet-config",
|
forResource: "valet-config",
|
||||||
withExtension: "json"
|
withExtension: "json"
|
||||||
)!
|
)!
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanLoadConfigFile() throws {
|
func testCanLoadConfigFile() throws {
|
||||||
let json = try? String(
|
let json = try? String(
|
||||||
contentsOf: Self.jsonConfigFileUrl,
|
contentsOf: Self.jsonConfigFileUrl,
|
||||||
@@ -26,7 +26,7 @@ class ValetConfigParserTest: XCTestCase {
|
|||||||
Valet.Configuration.self,
|
Valet.Configuration.self,
|
||||||
from: json!.data(using: .utf8)!
|
from: json!.data(using: .utf8)!
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(config.tld, "test")
|
XCTAssertEqual(config.tld, "test")
|
||||||
XCTAssertEqual(config.paths, [
|
XCTAssertEqual(config.paths, [
|
||||||
"/Users/username/.config/valet/Sites",
|
"/Users/username/.config/valet/Sites",
|
||||||
@@ -35,5 +35,5 @@ class ValetConfigParserTest: XCTestCase {
|
|||||||
XCTAssertEqual(config.defaultSite, "/Users/username/default-site")
|
XCTAssertEqual(config.defaultSite, "/Users/username/default-site")
|
||||||
XCTAssertEqual(config.loopback, "127.0.0.1")
|
XCTAssertEqual(config.loopback, "127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
81
phpmon-tests/Test Files/nginx/nginx-proxy.test
Normal file
81
phpmon-tests/Test Files/nginx/nginx-proxy.test
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# valet stub: proxy.valet.conf
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name my-proxy.test www.my-proxy.test *.my-proxy.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/my-proxy.test-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:90;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Client-Verify SUCCESS;
|
||||||
|
proxy_set_header X-Client-DN $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Subject $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
|
||||||
|
proxy_set_header X-NginX-Proxy true;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 1800;
|
||||||
|
proxy_connect_timeout 1800;
|
||||||
|
chunked_transfer_encoding on;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:60;
|
||||||
|
#listen 127.0.0.1:60; # valet loopback
|
||||||
|
server_name my-proxy.test www.my-proxy.test *.my-proxy.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
|
||||||
|
add_header X-Robots-Tag 'noindex, nofollow, nosnippet, noarchive';
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/my-proxy.test-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:90;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# valet stub: secure.proxy.valet.conf
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name live.whatagraph.dev.com www.live.whatagraph.dev.com *.live.whatagraph.dev.com;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:443 ssl http2;
|
||||||
|
#listen 127.0.0.1:443 ssl http2; # valet loopback
|
||||||
|
server_name live.whatagraph.dev.com www.live.whatagraph.dev.com *.live.whatagraph.dev.com;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/phpmon/.config/valet/Certificates/live.whatagraph.dev.com.crt";
|
||||||
|
ssl_certificate_key "/Users/phpmon/.config/valet/Certificates/live.whatagraph.dev.com.key";
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/phpmon/.config/valet/Log/live.whatagraph.dev.com-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/phpmon/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:8080/;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Client-Verify SUCCESS;
|
||||||
|
proxy_set_header X-Client-DN $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Subject $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
|
||||||
|
proxy_set_header X-NginX-Proxy true;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 1800;
|
||||||
|
proxy_connect_timeout 1800;
|
||||||
|
chunked_transfer_encoding on;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
57
phpmon-tests/Test Files/nginx/nginx-secure-proxy.test
Normal file
57
phpmon-tests/Test Files/nginx/nginx-secure-proxy.test
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# valet stub: secure.proxy.valet.conf
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name my-proxy.test www.my-proxy.test *.my-proxy.test;
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:443 ssl http2;
|
||||||
|
#listen 127.0.0.1:443 ssl http2; # valet loopback
|
||||||
|
server_name my-proxy.test www.my-proxy.test *.my-proxy.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 128M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/nicoverbruggen/.config/valet/Certificates/my-proxy.test.crt";
|
||||||
|
ssl_certificate_key "/Users/nicoverbruggen/.config/valet/Certificates/my-proxy.test.key";
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/my-proxy.test-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:90;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Client-Verify SUCCESS;
|
||||||
|
proxy_set_header X-Client-DN $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Subject $ssl_client_s_dn;
|
||||||
|
proxy_set_header X-SSL-Issuer $ssl_client_i_dn;
|
||||||
|
proxy_set_header X-NginX-Proxy true;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 1800;
|
||||||
|
proxy_connect_timeout 1800;
|
||||||
|
chunked_transfer_encoding on;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_buffering off;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,18 +3,18 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 14/02/2021.
|
// Created by Nico Verbruggen on 14/02/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Utility {
|
class Utility {
|
||||||
|
|
||||||
public static func copyToTemporaryFile(resourceName: String, fileExtension: String) -> URL? {
|
public static func copyToTemporaryFile(resourceName: String, fileExtension: String) -> URL? {
|
||||||
if let bundleURL = Bundle(for: Self.self).url(forResource: resourceName, withExtension: fileExtension) {
|
if let bundleURL = Bundle(for: Self.self).url(forResource: resourceName, withExtension: fileExtension) {
|
||||||
let tempDirectoryURL = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
|
let tempDirectoryURL = NSURL.fileURL(withPath: NSTemporaryDirectory(), isDirectory: true)
|
||||||
let targetURL = tempDirectoryURL.appendingPathComponent("\(UUID().uuidString).\(fileExtension)")
|
let targetURL = tempDirectoryURL.appendingPathComponent("\(UUID().uuidString).\(fileExtension)")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try FileManager.default.copyItem(at: bundleURL, to: targetURL)
|
try FileManager.default.copyItem(at: bundleURL, to: targetURL)
|
||||||
return targetURL
|
return targetURL
|
||||||
@@ -22,7 +22,7 @@ class Utility {
|
|||||||
Log.err("Unable to copy file: \(error)")
|
Log.err("Unable to copy file: \(error)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
phpmon-tests/Versions/AppUpdaterCheckTest.swift
Normal file
21
phpmon-tests/Versions/AppUpdaterCheckTest.swift
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// AppUpdaterCheckTest.swift
|
||||||
|
// phpmon-tests
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class AppUpdaterCheckTest: XCTestCase {
|
||||||
|
|
||||||
|
func testCanRetrieveVersionFromCask() {
|
||||||
|
let caskVersion = AppUpdateChecker.retrieveVersionFromCask()
|
||||||
|
|
||||||
|
let version = VersionExtractor.from(caskVersion)
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
62
phpmon-tests/Versions/AppVersionTest.swift
Normal file
62
phpmon-tests/Versions/AppVersionTest.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// AppVersionTest.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class AppVersionTest: XCTestCase {
|
||||||
|
|
||||||
|
func testCanRetrieveInternalAppVersion() {
|
||||||
|
XCTAssertNotNil(AppVersion.fromCurrentVersion())
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanParseNormalVersionString() {
|
||||||
|
let version = AppVersion.from("1.0.0")
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
XCTAssertEqual("1.0.0", version?.version)
|
||||||
|
XCTAssertEqual(nil, version?.build)
|
||||||
|
XCTAssertEqual(nil, version?.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanParseCaskVersionString() {
|
||||||
|
let version = AppVersion.from("1.0.0_600")
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
XCTAssertEqual("1.0.0", version?.version)
|
||||||
|
XCTAssertEqual("600", version?.build)
|
||||||
|
XCTAssertEqual(nil, version?.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanParseDevVersionStringWithoutBuildNumber() {
|
||||||
|
let version = AppVersion.from("1.0.0-dev")
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
XCTAssertEqual("1.0.0", version?.version)
|
||||||
|
XCTAssertEqual(nil, version?.build)
|
||||||
|
XCTAssertEqual("dev", version?.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanParseDevVersionStringWithBuildNumber() {
|
||||||
|
let version = AppVersion.from("1.0.0-dev,870")
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
XCTAssertEqual("1.0.0", version?.version)
|
||||||
|
XCTAssertEqual("870", version?.build)
|
||||||
|
XCTAssertEqual("dev", version?.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCanParseUnderscoresAsBuildSeparatorToo() {
|
||||||
|
let version = AppVersion.from("1.0.0-dev_870")
|
||||||
|
|
||||||
|
XCTAssertNotNil(version)
|
||||||
|
XCTAssertEqual("1.0.0", version?.version)
|
||||||
|
XCTAssertEqual("870", version?.build)
|
||||||
|
XCTAssertEqual("dev", version?.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 01/04/2021.
|
// Created by Nico Verbruggen on 01/04/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@@ -23,7 +23,7 @@ class PhpVersionDetectionTest: XCTestCase {
|
|||||||
"php@5.6",
|
"php@5.6",
|
||||||
"php@5.4" // should be omitted, not supported
|
"php@5.4" // should be omitted, not supported
|
||||||
], checkBinaries: false, generateHelpers: false)
|
], checkBinaries: false, generateHelpers: false)
|
||||||
|
|
||||||
XCTAssertEqual(outcome, ["8.0", "7.0"])
|
XCTAssertEqual(outcome, ["8.0", "7.0"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
nil
|
nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPhpVersionNumberParse() throws {
|
func testPhpVersionNumberParse() throws {
|
||||||
XCTAssertThrowsError(try PhpVersionNumber.parse("OOF")) { error in
|
XCTAssertThrowsError(try PhpVersionNumber.parse("OOF")) { error in
|
||||||
XCTAssertTrue(error is VersionParseError)
|
XCTAssertTrue(error is VersionParseError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanCheckFixedConstraints() throws {
|
func testCanCheckFixedConstraints() throws {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -51,7 +51,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.0"]).all
|
.make(from: ["7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4.3", "7.3.3", "7.2.3", "7.1.3", "7.0.3"])
|
.make(from: ["7.4.3", "7.3.3", "7.2.3", "7.1.3", "7.0.3"])
|
||||||
@@ -59,7 +59,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.0.3"]).all
|
.make(from: ["7.0.3"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||||
@@ -67,7 +67,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.0"]).all
|
.make(from: ["7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||||
@@ -76,7 +76,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
.make(from: []).all
|
.make(from: []).all
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanCheckCaretConstraints() throws {
|
func testCanCheckCaretConstraints() throws {
|
||||||
// 1. Imprecise checks
|
// 1. Imprecise checks
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -86,7 +86,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -96,7 +96,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 3. Imprecise check with precise constraint (strict mode)
|
// 3. Imprecise check with precise constraint (strict mode)
|
||||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -106,7 +106,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 4. Precise members and constraint all around
|
// 4. Precise members and constraint all around
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -115,7 +115,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 5. Precise members but imprecise constraint (strict mode)
|
// 5. Precise members but imprecise constraint (strict mode)
|
||||||
// In strict mode the constraint's patch version is assumed to be 0
|
// In strict mode the constraint's patch version is assumed to be 0
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -125,7 +125,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 6. Precise members but imprecise constraint (lenient mode)
|
// 6. Precise members but imprecise constraint (lenient mode)
|
||||||
// In lenient mode the constraint's patch version is assumed to be equal
|
// In lenient mode the constraint's patch version is assumed to be equal
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -136,7 +136,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanCheckTildeConstraints() throws {
|
func testCanCheckTildeConstraints() throws {
|
||||||
// 1. Imprecise checks
|
// 1. Imprecise checks
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -146,7 +146,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
// 2. Imprecise check with precise constraint (lenient AKA not strict)
|
||||||
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
// These versions are interpreted as 7.4.999, 7.3.999, 7.2.999, etc.
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -159,7 +159,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.0"]).all
|
.make(from: ["7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 3. Imprecise check with precise constraint (strict mode)
|
// 3. Imprecise check with precise constraint (strict mode)
|
||||||
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
// These versions are interpreted as 7.4.0, 7.3.0, 7.2.0, etc.
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -172,7 +172,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: []).all
|
.make(from: []).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 4. Precise members and constraint all around
|
// 4. Precise members and constraint all around
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -183,7 +183,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.0.10"]).all
|
.make(from: ["7.0.10"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 5. Precise members but imprecise constraint (strict mode)
|
// 5. Precise members but imprecise constraint (strict mode)
|
||||||
// In strict mode the constraint's patch version is assumed to be 0.
|
// In strict mode the constraint's patch version is assumed to be 0.
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
@@ -193,7 +193,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// 6. Precise members but imprecise constraint (lenient mode)
|
// 6. Precise members but imprecise constraint (lenient mode)
|
||||||
// In lenient mode the constraint's patch version is assumed to be equal.
|
// In lenient mode the constraint's patch version is assumed to be equal.
|
||||||
// (Strictness does not make any difference here, but both should be tested.)
|
// (Strictness does not make any difference here, but both should be tested.)
|
||||||
@@ -205,7 +205,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
.make(from: ["7.4.10", "7.3.10", "7.2.10", "7.1.10", "7.0.10"]).all
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanCheckGreaterThanOrEqualConstraints() throws {
|
func testCanCheckGreaterThanOrEqualConstraints() throws {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -214,7 +214,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||||
@@ -222,7 +222,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0)
|
// Strict check (>7.2.5 is too new for 7.2 which resolves to 7.2.0)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -231,7 +231,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3"]).all
|
.make(from: ["7.4", "7.3"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
// Non-strict check (ignoring patch, 7.2 resolves to 7.2.999)
|
// Non-strict check (ignoring patch, 7.2 resolves to 7.2.999)
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -241,7 +241,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
.make(from: ["7.4", "7.3", "7.2"]).all
|
.make(from: ["7.4", "7.3", "7.2"]).all
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCanCheckGreaterThanConstraints() throws {
|
func testCanCheckGreaterThanConstraints() throws {
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
@@ -250,7 +250,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
.make(from: ["7.4", "7.3", "7.2", "7.1"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||||
@@ -259,7 +259,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2"]).all
|
.make(from: ["7.4", "7.3", "7.2"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
.make(from: ["7.4", "7.3", "7.2", "7.1", "7.0"])
|
||||||
@@ -268,7 +268,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.4", "7.3"]).all
|
.make(from: ["7.4", "7.3"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.3.1", "7.2.9", "7.2.8", "7.2.6", "7.2.5", "7.2"])
|
.make(from: ["7.3.1", "7.2.9", "7.2.8", "7.2.6", "7.2.5", "7.2"])
|
||||||
@@ -277,7 +277,7 @@ class PhpVersionNumberTest: XCTestCase {
|
|||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.3.1", "7.2.9", "7.2"]).all
|
.make(from: ["7.3.1", "7.2.9", "7.2"]).all
|
||||||
)
|
)
|
||||||
|
|
||||||
XCTAssertEqual(
|
XCTAssertEqual(
|
||||||
PhpVersionNumberCollection
|
PhpVersionNumberCollection
|
||||||
.make(from: ["7.3.1", "7.2.9", "7.2.8", "7.2.6", "7.2.5", "7.2"])
|
.make(from: ["7.3.1", "7.2.9", "7.2.8", "7.2.6", "7.2.5", "7.2"])
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 29/11/2021.
|
// Created by Nico Verbruggen on 29/11/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@@ -14,5 +14,5 @@ class ValetVersionExtractorTest: XCTestCase {
|
|||||||
let version = valet("--version", sudo: false)
|
let version = valet("--version", sudo: false)
|
||||||
XCTAssert(version.contains("Laravel Valet 2") || version.contains("Laravel Valet 3"))
|
XCTAssert(version.contains("Laravel Valet 2") || version.contains("Laravel Valet 3"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// phpmon-tests
|
// phpmon-tests
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 16/12/2021.
|
// Created by Nico Verbruggen on 16/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
@@ -14,12 +14,12 @@ class VersionExtractorTest: XCTestCase {
|
|||||||
XCTAssertEqual(VersionExtractor.from("Laravel Valet 2.17.1"), "2.17.1")
|
XCTAssertEqual(VersionExtractor.from("Laravel Valet 2.17.1"), "2.17.1")
|
||||||
XCTAssertEqual(VersionExtractor.from("Laravel Valet 2.0"), "2.0")
|
XCTAssertEqual(VersionExtractor.from("Laravel Valet 2.0"), "2.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testVersionComparison() {
|
func testVersionComparison() {
|
||||||
XCTAssertEqual("2.0".versionCompare("2.1"), .orderedAscending)
|
XCTAssertEqual("2.0".versionCompare("2.1"), .orderedAscending)
|
||||||
XCTAssertEqual("2.1".versionCompare("2.0"), .orderedDescending)
|
XCTAssertEqual("2.1".versionCompare("2.0"), .orderedDescending)
|
||||||
XCTAssertEqual("2.0".versionCompare("2.0"), .orderedSame)
|
XCTAssertEqual("2.0".versionCompare("2.0"), .orderedSame)
|
||||||
XCTAssertEqual("2.17.0".versionCompare("2.17.1"), .orderedAscending)
|
XCTAssertEqual("2.17.0".versionCompare("2.17.1"), .orderedAscending)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
25
phpmon/Assets.xcassets/IconProxy.imageset/Contents.json
vendored
Normal file
25
phpmon/Assets.xcassets/IconProxy.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Proxy.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Proxy@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
phpmon/Assets.xcassets/IconProxy.imageset/Proxy.png
vendored
Normal file
BIN
phpmon/Assets.xcassets/IconProxy.imageset/Proxy.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 935 B |
BIN
phpmon/Assets.xcassets/IconProxy.imageset/Proxy@2x.png
vendored
Normal file
BIN
phpmon/Assets.xcassets/IconProxy.imageset/Proxy@2x.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -2,49 +2,44 @@
|
|||||||
// Services.swift
|
// Services.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class Actions {
|
class Actions {
|
||||||
|
|
||||||
// MARK: - Services
|
// MARK: - Services
|
||||||
|
|
||||||
public static func restartPhpFpm()
|
public static func restartPhpFpm() {
|
||||||
{
|
|
||||||
brew("services restart \(PhpEnv.phpInstall.formula)", sudo: true)
|
brew("services restart \(PhpEnv.phpInstall.formula)", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func restartNginx()
|
public static func restartNginx() {
|
||||||
{
|
|
||||||
brew("services restart nginx", sudo: true)
|
brew("services restart nginx", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func restartDnsMasq()
|
public static func restartDnsMasq() {
|
||||||
{
|
|
||||||
brew("services restart dnsmasq", sudo: true)
|
brew("services restart dnsmasq", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func stopAllServices()
|
public static func stopAllServices() {
|
||||||
{
|
|
||||||
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
|
brew("services stop \(PhpEnv.phpInstall.formula)", sudo: true)
|
||||||
brew("services stop nginx", sudo: true)
|
brew("services stop nginx", sudo: true)
|
||||||
brew("services stop dnsmasq", sudo: true)
|
brew("services stop dnsmasq", sudo: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func fixHomebrewPermissions() throws
|
public static func fixHomebrewPermissions() throws {
|
||||||
{
|
|
||||||
var servicesCommands = [
|
var servicesCommands = [
|
||||||
"\(Paths.brew) services stop nginx",
|
"\(Paths.brew) services stop nginx",
|
||||||
"\(Paths.brew) services stop dnsmasq",
|
"\(Paths.brew) services stop dnsmasq"
|
||||||
]
|
]
|
||||||
var cellarCommands = [
|
var cellarCommands = [
|
||||||
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/nginx",
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/nginx",
|
||||||
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/dnsmasq"
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/dnsmasq"
|
||||||
]
|
]
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { version in
|
PhpEnv.shared.availablePhpVersions.forEach { version in
|
||||||
let formula = version == PhpEnv.brewPhpVersion
|
let formula = version == PhpEnv.brewPhpVersion
|
||||||
? "php"
|
? "php"
|
||||||
@@ -52,66 +47,61 @@ class Actions {
|
|||||||
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||||
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
|
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let script =
|
let script =
|
||||||
servicesCommands.joined(separator: " && ")
|
servicesCommands.joined(separator: " && ")
|
||||||
+ " && "
|
+ " && "
|
||||||
+ cellarCommands.joined(separator: " && ")
|
+ cellarCommands.joined(separator: " && ")
|
||||||
|
|
||||||
let appleScript = NSAppleScript(
|
let appleScript = NSAppleScript(
|
||||||
source: "do shell script \"\(script)\" with administrator privileges"
|
source: "do shell script \"\(script)\" with administrator privileges"
|
||||||
)
|
)
|
||||||
|
|
||||||
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
|
let eventResult: NSAppleEventDescriptor? = appleScript?.executeAndReturnError(nil)
|
||||||
|
|
||||||
if (eventResult == nil) {
|
if eventResult == nil {
|
||||||
throw HomebrewPermissionError(kind: .applescriptNilError)
|
throw HomebrewPermissionError(kind: .applescriptNilError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Finding Config Files
|
// MARK: - Finding Config Files
|
||||||
|
|
||||||
public static func openGenericPhpConfigFolder()
|
public static func openGenericPhpConfigFolder() {
|
||||||
{
|
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php")]
|
||||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php")];
|
|
||||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func openGlobalComposerFolder()
|
public static func openGlobalComposerFolder() {
|
||||||
{
|
|
||||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent(".composer/composer.json")
|
.appendingPathComponent(".composer/composer.json")
|
||||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func openPhpConfigFolder(version: String)
|
public static func openPhpConfigFolder(version: String) {
|
||||||
{
|
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php/\(version)/php.ini")]
|
||||||
let files = [NSURL(fileURLWithPath: "\(Paths.etcPath)/php/\(version)/php.ini")];
|
|
||||||
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
NSWorkspace.shared.activateFileViewerSelecting(files as [URL])
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func openValetConfigFolder()
|
public static func openValetConfigFolder() {
|
||||||
{
|
|
||||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent(".config/valet")
|
.appendingPathComponent(".config/valet")
|
||||||
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
NSWorkspace.shared.activateFileViewerSelecting([file] as [URL])
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Other Actions
|
// MARK: - Other Actions
|
||||||
|
|
||||||
public static func createTempPhpInfoFile() -> URL
|
public static func createTempPhpInfoFile() -> URL {
|
||||||
{
|
|
||||||
// Write a file called `phpmon_phpinfo.php` to /tmp
|
// Write a file called `phpmon_phpinfo.php` to /tmp
|
||||||
try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8)
|
try! "<?php phpinfo();".write(toFile: "/tmp/phpmon_phpinfo.php", atomically: true, encoding: .utf8)
|
||||||
|
|
||||||
// Tell php-cgi to run the PHP and output as an .html file
|
// Tell php-cgi to run the PHP and output as an .html file
|
||||||
Shell.run("\(Paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
Shell.run("\(Paths.binPath)/php-cgi -q /tmp/phpmon_phpinfo.php > /tmp/phpmon_phpinfo.html")
|
||||||
|
|
||||||
return URL(string: "file:///private/tmp/phpmon_phpinfo.html")!
|
return URL(string: "file:///private/tmp/phpmon_phpinfo.html")!
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Fix My Valet
|
// MARK: - Fix My Valet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Detects all currently available PHP versions,
|
Detects all currently available PHP versions,
|
||||||
and unlinks each and every one of them.
|
and unlinks each and every one of them.
|
||||||
@@ -124,8 +114,7 @@ class Actions {
|
|||||||
If this does not solve the issue, the user may need to install additional
|
If this does not solve the issue, the user may need to install additional
|
||||||
extensions and/or run `composer global update`.
|
extensions and/or run `composer global update`.
|
||||||
*/
|
*/
|
||||||
public static func fixMyValet(completed: @escaping () -> Void)
|
public static func fixMyValet(completed: @escaping () -> Void) {
|
||||||
{
|
|
||||||
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpVersion, completion: {
|
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpVersion, completion: {
|
||||||
brew("services restart dnsmasq", sudo: true)
|
brew("services restart dnsmasq", sudo: true)
|
||||||
brew("services restart php", sudo: true)
|
brew("services restart php", sudo: true)
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
// Command.swift
|
// Command.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
public class Command {
|
public class Command {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Immediately executes a command.
|
Immediately executes a command.
|
||||||
|
|
||||||
@@ -20,21 +20,21 @@ public class Command {
|
|||||||
let task = Process()
|
let task = Process()
|
||||||
task.launchPath = path
|
task.launchPath = path
|
||||||
task.arguments = arguments
|
task.arguments = arguments
|
||||||
|
|
||||||
let pipe = Pipe()
|
let pipe = Pipe()
|
||||||
task.standardOutput = pipe
|
task.standardOutput = pipe
|
||||||
task.launch()
|
task.launch()
|
||||||
|
|
||||||
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
let data = pipe.fileHandleForReading.readDataToEndOfFile()
|
||||||
let output: String = String.init(data: data, encoding: String.Encoding.utf8)!
|
let output: String = String.init(data: data, encoding: String.Encoding.utf8)!
|
||||||
|
|
||||||
if (trimNewlines) {
|
if trimNewlines {
|
||||||
return output.components(separatedBy: .newlines)
|
return output.components(separatedBy: .newlines)
|
||||||
.filter({ !$0.isEmpty })
|
.filter({ !$0.isEmpty })
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,19 @@
|
|||||||
// Constants.swift
|
// Constants.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
struct Constants {
|
struct Constants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The latest PHP version that is considered to be stable at the time of release.
|
* The latest PHP version that is considered to be stable at the time of release.
|
||||||
* This version number is currently not used (only as a default fallback).
|
* This version number is currently not used (only as a default fallback).
|
||||||
*/
|
*/
|
||||||
static let LatestStablePhpVersion = "8.1"
|
static let LatestStablePhpVersion = "8.1"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The minimum version of Valet that is recommended.
|
The minimum version of Valet that is recommended.
|
||||||
If the installed version is older, a notification will be shown
|
If the installed version is older, a notification will be shown
|
||||||
@@ -24,7 +24,7 @@ struct Constants {
|
|||||||
See also: https://github.com/laravel/valet/releases/tag/v2.16.2
|
See also: https://github.com/laravel/valet/releases/tag/v2.16.2
|
||||||
*/
|
*/
|
||||||
static let MinimumRecommendedValetVersion = "2.16.2"
|
static let MinimumRecommendedValetVersion = "2.16.2"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The PHP versions supported by this application.
|
* The PHP versions supported by this application.
|
||||||
* Versions that do not appear in this array are omitted from the list.
|
* Versions that do not appear in this array are omitted from the list.
|
||||||
@@ -42,7 +42,7 @@ struct Constants {
|
|||||||
"7.4",
|
"7.4",
|
||||||
"8.0",
|
"8.0",
|
||||||
"8.1",
|
"8.1",
|
||||||
|
|
||||||
// ====================
|
// ====================
|
||||||
// EXPERIMENTAL SUPPORT
|
// EXPERIMENTAL SUPPORT
|
||||||
// ====================
|
// ====================
|
||||||
@@ -50,19 +50,33 @@ struct Constants {
|
|||||||
// dev release. In this case, that means that the version below is detected.
|
// dev release. In this case, that means that the version below is detected.
|
||||||
"8.2"
|
"8.2"
|
||||||
]
|
]
|
||||||
|
|
||||||
struct Urls {
|
struct Urls {
|
||||||
|
|
||||||
static let DonationPayment = URL(
|
static let DonationPayment = URL(
|
||||||
string: "https://nicoverbruggen.be/sponsor#pay-now"
|
string: "https://nicoverbruggen.be/sponsor#pay-now"
|
||||||
)!
|
)!
|
||||||
|
|
||||||
static let DonationPage = URL(
|
static let DonationPage = URL(
|
||||||
string: "https://nicoverbruggen.be/sponsor"
|
string: "https://nicoverbruggen.be/sponsor"
|
||||||
)!
|
)!
|
||||||
|
|
||||||
static let FrequentlyAskedQuestions = URL(
|
static let FrequentlyAskedQuestions = URL(
|
||||||
string: "https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting"
|
string: "https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting"
|
||||||
)!
|
)!
|
||||||
|
|
||||||
|
static let GitHubReleases = URL(
|
||||||
|
string: "https://github.com/nicoverbruggen/phpmon/releases"
|
||||||
|
)!
|
||||||
|
|
||||||
|
static let StableBuildCaskFile = URL(
|
||||||
|
string: "https://raw.githubusercontent.com/nicoverbruggen/homebrew-cask/master/Casks/phpmon.rb"
|
||||||
|
)!
|
||||||
|
|
||||||
|
static let DevBuildCaskFile = URL(
|
||||||
|
string: "https://raw.githubusercontent.com/nicoverbruggen/homebrew-cask/master/Casks/phpmon-dev.rb"
|
||||||
|
)!
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Events {
|
class Events {
|
||||||
|
|
||||||
static let ServicesUpdated = Notification.Name("ServicesUpdated")
|
static let ServicesUpdated = Notification.Name("ServicesUpdated")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 24/12/2021.
|
// Created by Nico Verbruggen on 24/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
// MARK: Common Shell Commands
|
// MARK: Common Shell Commands
|
||||||
@@ -11,28 +11,25 @@
|
|||||||
/**
|
/**
|
||||||
Runs a `valet` command. Defaults to running as superuser.
|
Runs a `valet` command. Defaults to running as superuser.
|
||||||
*/
|
*/
|
||||||
func valet(_ command: String, sudo: Bool = true) -> String
|
func valet(_ command: String, sudo: Bool = true) -> String {
|
||||||
{
|
|
||||||
return Shell.pipe("\(sudo ? "sudo " : "")" + "\(Paths.valet) \(command)", requiresPath: true)
|
return Shell.pipe("\(sudo ? "sudo " : "")" + "\(Paths.valet) \(command)", requiresPath: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs a `brew` command. Can run as superuser.
|
Runs a `brew` command. Can run as superuser.
|
||||||
*/
|
*/
|
||||||
func brew(_ command: String, sudo: Bool = false)
|
func brew(_ command: String, sudo: Bool = false) {
|
||||||
{
|
|
||||||
Shell.run("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)")
|
Shell.run("\(sudo ? "sudo " : "")" + "\(Paths.brew) \(command)")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs `sed` in order to replace all occurrences of a string in a specific file with another.
|
Runs `sed` in order to replace all occurrences of a string in a specific file with another.
|
||||||
*/
|
*/
|
||||||
func sed(file: String, original: String, replacement: String)
|
func sed(file: String, original: String, replacement: String) {
|
||||||
{
|
|
||||||
// Escape slashes (or `sed` won't work)
|
// Escape slashes (or `sed` won't work)
|
||||||
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
let e_original = original.replacingOccurrences(of: "/", with: "\\/")
|
||||||
let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/")
|
let e_replacement = replacement.replacingOccurrences(of: "/", with: "\\/")
|
||||||
|
|
||||||
// Check if gsed exists; it is able to follow symlinks,
|
// Check if gsed exists; it is able to follow symlinks,
|
||||||
// which we want to do to toggle the extension
|
// which we want to do to toggle the extension
|
||||||
if Filesystem.fileExists("\(Paths.binPath)/gsed") {
|
if Filesystem.fileExists("\(Paths.binPath)/gsed") {
|
||||||
@@ -45,8 +42,7 @@ func sed(file: String, original: String, replacement: String)
|
|||||||
/**
|
/**
|
||||||
Uses `grep` to determine whether a particular query string can be found in a particular file.
|
Uses `grep` to determine whether a particular query string can be found in a particular file.
|
||||||
*/
|
*/
|
||||||
func grepContains(file: String, query: String) -> Bool
|
func grepContains(file: String, query: String) -> Bool {
|
||||||
{
|
|
||||||
return Shell.pipe("""
|
return Shell.pipe("""
|
||||||
grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO"
|
grep -q '\(query)' \(file); [ $? -eq 0 ] && echo "YES" || echo "NO"
|
||||||
""")
|
""")
|
||||||
|
|||||||
@@ -3,56 +3,56 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 21/12/2021.
|
// Created by Nico Verbruggen on 21/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class Log {
|
class Log {
|
||||||
|
|
||||||
static var shared = Log()
|
static var shared = Log()
|
||||||
|
|
||||||
enum Verbosity: Int {
|
enum Verbosity: Int {
|
||||||
case error = 1,
|
case error = 1,
|
||||||
warning = 2,
|
warning = 2,
|
||||||
info = 3,
|
info = 3,
|
||||||
performance = 4
|
performance = 4
|
||||||
|
|
||||||
public func isApplicable() -> Bool {
|
public func isApplicable() -> Bool {
|
||||||
return Log.shared.verbosity.rawValue >= self.rawValue
|
return Log.shared.verbosity.rawValue >= self.rawValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var verbosity: Verbosity = .warning
|
var verbosity: Verbosity = .warning
|
||||||
|
|
||||||
static func err(_ item: Any) {
|
static func err(_ item: Any) {
|
||||||
if Verbosity.error.isApplicable() {
|
if Verbosity.error.isApplicable() {
|
||||||
print("[E] \(item)")
|
print("[E] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func warn(_ item: Any) {
|
static func warn(_ item: Any) {
|
||||||
if Verbosity.warning.isApplicable() {
|
if Verbosity.warning.isApplicable() {
|
||||||
print("[W] \(item)")
|
print("[W] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func info(_ item: Any) {
|
static func info(_ item: Any) {
|
||||||
if Verbosity.info.isApplicable() {
|
if Verbosity.info.isApplicable() {
|
||||||
print("\(item)")
|
print("\(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func perf(_ item: Any) {
|
static func perf(_ item: Any) {
|
||||||
if Verbosity.performance.isApplicable() {
|
if Verbosity.performance.isApplicable() {
|
||||||
print("[P] \(item)")
|
print("[P] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func separator(as verbosity: Verbosity = .info) {
|
static func separator(as verbosity: Verbosity = .info) {
|
||||||
if verbosity.isApplicable() {
|
if verbosity.isApplicable() {
|
||||||
print("==================================")
|
print("==================================")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Paths.swift
|
// Paths.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -12,71 +12,71 @@ import Foundation
|
|||||||
The path to the Homebrew directory and the user's name are fetched only once, at boot.
|
The path to the Homebrew directory and the user's name are fetched only once, at boot.
|
||||||
*/
|
*/
|
||||||
public class Paths {
|
public class Paths {
|
||||||
|
|
||||||
public static let shared = Paths()
|
public static let shared = Paths()
|
||||||
|
|
||||||
internal var baseDir: Paths.HomebrewDir
|
internal var baseDir: Paths.HomebrewDir
|
||||||
|
|
||||||
private var userName: String
|
private var userName: String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
||||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func detectBinaryPaths() {
|
public func detectBinaryPaths() {
|
||||||
detectComposerBinary()
|
detectComposerBinary()
|
||||||
}
|
}
|
||||||
|
|
||||||
// - MARK: Binaries
|
// - MARK: Binaries
|
||||||
|
|
||||||
public static var valet: String {
|
public static var valet: String {
|
||||||
return "\(binPath)/valet"
|
return "\(binPath)/valet"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var brew: String {
|
public static var brew: String {
|
||||||
return "\(binPath)/brew"
|
return "\(binPath)/brew"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var php: String {
|
public static var php: String {
|
||||||
return "\(binPath)/php"
|
return "\(binPath)/php"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var phpConfig: String {
|
public static var phpConfig: String {
|
||||||
return "\(binPath)/php-config"
|
return "\(binPath)/php-config"
|
||||||
}
|
}
|
||||||
|
|
||||||
// - MARK: Detected Binaries
|
// - MARK: Detected Binaries
|
||||||
|
|
||||||
/** The path to the Composer binary. Can be in multiple locations, so is detected instead. */
|
/** The path to the Composer binary. Can be in multiple locations, so is detected instead. */
|
||||||
public static var composer: String? = nil
|
public static var composer: String?
|
||||||
|
|
||||||
// - MARK: Paths
|
// - MARK: Paths
|
||||||
|
|
||||||
public static var whoami: String {
|
public static var whoami: String {
|
||||||
return shared.userName
|
return shared.userName
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var cellarPath: String {
|
public static var cellarPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/Cellar"
|
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"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var optPath: String {
|
public static var optPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/opt"
|
return "\(shared.baseDir.rawValue)/opt"
|
||||||
}
|
}
|
||||||
|
|
||||||
public static var etcPath: String {
|
public static var etcPath: String {
|
||||||
return "\(shared.baseDir.rawValue)/etc"
|
return "\(shared.baseDir.rawValue)/etc"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Flexible Binaries
|
// MARK: - Flexible Binaries
|
||||||
// (these can be in multiple locations, so we scan common places because)
|
// (these can be in multiple locations, so we scan common places because)
|
||||||
// (PHP Monitor will not use the user's own PATH)
|
// (PHP Monitor will not use the user's own PATH)
|
||||||
|
|
||||||
private func detectComposerBinary() {
|
private func detectComposerBinary() {
|
||||||
if Filesystem.fileExists("/usr/local/bin/composer") {
|
if Filesystem.fileExists("/usr/local/bin/composer") {
|
||||||
Paths.composer = "/usr/local/bin/composer"
|
Paths.composer = "/usr/local/bin/composer"
|
||||||
@@ -87,12 +87,12 @@ public class Paths {
|
|||||||
Log.warn("Composer was not found.")
|
Log.warn("Composer was not found.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Enum
|
// MARK: - Enum
|
||||||
|
|
||||||
public enum HomebrewDir: String {
|
public enum HomebrewDir: String {
|
||||||
case opt = "/opt/homebrew"
|
case opt = "/opt/homebrew"
|
||||||
case usr = "/usr/local"
|
case usr = "/usr/local"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension Process {
|
extension Process {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When a process is running in the background, it can send content to standard
|
When a process is running in the background, it can send content to standard
|
||||||
output or standard error, just like it would in a terminal. Using `listen`
|
output or standard error, just like it would in a terminal. Using `listen`
|
||||||
@@ -22,10 +22,10 @@ extension Process {
|
|||||||
) {
|
) {
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
let errorPipe = Pipe()
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
self.standardOutput = outputPipe
|
self.standardOutput = outputPipe
|
||||||
self.standardError = errorPipe
|
self.standardError = errorPipe
|
||||||
|
|
||||||
[
|
[
|
||||||
(outputPipe, didReceiveStandardOutputData),
|
(outputPipe, didReceiveStandardOutputData),
|
||||||
(errorPipe, didReceiveStandardErrorData)
|
(errorPipe, didReceiveStandardErrorData)
|
||||||
@@ -35,15 +35,18 @@ extension Process {
|
|||||||
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
||||||
object: pipe.fileHandleForReading,
|
object: pipe.fileHandleForReading,
|
||||||
queue: nil
|
queue: nil
|
||||||
) { notification in
|
) { _ in
|
||||||
if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) {
|
if let outputString = String(
|
||||||
|
data: pipe.fileHandleForReading.availableData,
|
||||||
|
encoding: String.Encoding.utf8
|
||||||
|
) {
|
||||||
callback(outputString)
|
callback(outputString)
|
||||||
}
|
}
|
||||||
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
After the process is done running, you'll want to stop listening.
|
After the process is done running, you'll want to stop listening.
|
||||||
*/
|
*/
|
||||||
@@ -55,5 +58,5 @@ extension Process {
|
|||||||
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,41 @@
|
|||||||
// Shell.swift
|
// Shell.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
public class Shell {
|
public class Shell {
|
||||||
|
|
||||||
// MARK: - Invoke static functions
|
// MARK: - Invoke static functions
|
||||||
|
|
||||||
public static func run(
|
public static func run(
|
||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false
|
requiresPath: Bool = false
|
||||||
) {
|
) {
|
||||||
Shell.user.run(command, requiresPath: requiresPath)
|
Shell.user.run(command, requiresPath: requiresPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func pipe(
|
public static func pipe(
|
||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false
|
requiresPath: Bool = false
|
||||||
) -> String {
|
) -> String {
|
||||||
return Shell.user.pipe(command, requiresPath: requiresPath)
|
return Shell.user.pipe(command, requiresPath: requiresPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Singleton
|
// MARK: - Singleton
|
||||||
|
|
||||||
/**
|
/**
|
||||||
We now require macOS 11, so no need to detect which terminal to use.
|
We now require macOS 11, so no need to detect which terminal to use.
|
||||||
*/
|
*/
|
||||||
public var shell: String = "/bin/sh"
|
public var shell: String = "/bin/sh"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Singleton to access a user shell (with --login)
|
Singleton to access a user shell (with --login)
|
||||||
*/
|
*/
|
||||||
public static let user = Shell()
|
public static let user = Shell()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs a shell command without using the output.
|
Runs a shell command without using the output.
|
||||||
Uses the default shell.
|
Uses the default shell.
|
||||||
@@ -51,7 +51,7 @@ public class Shell {
|
|||||||
// Equivalent of piping to /dev/null; don't do anything with the string
|
// Equivalent of piping to /dev/null; don't do anything with the string
|
||||||
_ = Shell.pipe(command, requiresPath: requiresPath)
|
_ = Shell.pipe(command, requiresPath: requiresPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs a shell command and returns the output.
|
Runs a shell command and returns the output.
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ public class Shell {
|
|||||||
)
|
)
|
||||||
return !hasError ? shellOutput.standardOutput : shellOutput.errorOutput
|
return !hasError ? shellOutput.standardOutput : shellOutput.errorOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs the command and returns a `ShellOutput` object, which contains info about the process.
|
Runs the command and returns a `ShellOutput` object, which contains info about the process.
|
||||||
|
|
||||||
@@ -81,17 +81,17 @@ public class Shell {
|
|||||||
_ command: String,
|
_ command: String,
|
||||||
requiresPath: Bool = false
|
requiresPath: Bool = false
|
||||||
) -> Shell.Output {
|
) -> Shell.Output {
|
||||||
|
|
||||||
let outputPipe = Pipe()
|
let outputPipe = Pipe()
|
||||||
let errorPipe = Pipe()
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
let task = self.createTask(for: command, requiresPath: requiresPath)
|
let task = self.createTask(for: command, requiresPath: requiresPath)
|
||||||
task.standardOutput = outputPipe
|
task.standardOutput = outputPipe
|
||||||
task.standardError = errorPipe
|
task.standardError = errorPipe
|
||||||
task.launch()
|
task.launch()
|
||||||
task.waitUntilExit()
|
task.waitUntilExit()
|
||||||
|
|
||||||
return Shell.Output(
|
let output = Shell.Output(
|
||||||
standardOutput: String(
|
standardOutput: String(
|
||||||
data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
|
data: outputPipe.fileHandleForReading.readDataToEndOfFile(),
|
||||||
encoding: .utf8
|
encoding: .utf8
|
||||||
@@ -102,8 +102,14 @@ public class Shell {
|
|||||||
)!,
|
)!,
|
||||||
task: task
|
task: task
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if CommandLine.arguments.contains("--v") {
|
||||||
|
log(task: task, output: output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a new process with the correct PATH and shell.
|
Creates a new process with the correct PATH and shell.
|
||||||
*/
|
*/
|
||||||
@@ -111,19 +117,36 @@ public class Shell {
|
|||||||
let tailoredCommand = requiresPath
|
let tailoredCommand = requiresPath
|
||||||
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
? "export PATH=\(Paths.binPath):$PATH && \(command)"
|
||||||
: command
|
: command
|
||||||
|
|
||||||
let task = Process()
|
let task = Process()
|
||||||
task.launchPath = self.shell
|
task.launchPath = self.shell
|
||||||
task.arguments = ["--noprofile", "-norc", "--login", "-c", tailoredCommand]
|
task.arguments = ["--noprofile", "-norc", "--login", "-c", tailoredCommand]
|
||||||
|
|
||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Verbose logging for PHP Monitor's synchronous shell output.
|
||||||
|
*/
|
||||||
|
private func log(task: Process, output: Output) {
|
||||||
|
Log.info("")
|
||||||
|
Log.info("==== COMMAND ====")
|
||||||
|
Log.info("")
|
||||||
|
Log.info("\(self.shell) \(task.arguments?.joined(separator: " ") ?? "")")
|
||||||
|
Log.info("")
|
||||||
|
Log.info("==== OUTPUT ====")
|
||||||
|
Log.info("")
|
||||||
|
dump(output)
|
||||||
|
Log.info("")
|
||||||
|
Log.info("==== END OUTPUT ====")
|
||||||
|
Log.info("")
|
||||||
|
}
|
||||||
|
|
||||||
public class Output {
|
public class Output {
|
||||||
public let standardOutput: String
|
public let standardOutput: String
|
||||||
public let errorOutput: String
|
public let errorOutput: String
|
||||||
public let task: Process
|
public let task: Process
|
||||||
|
|
||||||
init(standardOutput: String,
|
init(standardOutput: String,
|
||||||
errorOutput: String,
|
errorOutput: String,
|
||||||
task: Process) {
|
task: Process) {
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ struct HomebrewPermissionError: Error, AlertableError {
|
|||||||
enum Kind: String {
|
enum Kind: String {
|
||||||
case applescriptNilError = "homebrew_permissions.applescript_returned_nil"
|
case applescriptNilError = "homebrew_permissions.applescript_returned_nil"
|
||||||
}
|
}
|
||||||
|
|
||||||
let kind: Kind
|
let kind: Kind
|
||||||
|
|
||||||
func getErrorMessageKey() -> String {
|
func getErrorMessageKey() -> String {
|
||||||
return "alert.errors.\(self.kind.rawValue)"
|
return "alert.errors.\(self.kind.rawValue)"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
// Date.swift
|
// Date.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension Date {
|
extension Date {
|
||||||
|
|
||||||
func toString() -> String {
|
func toString() -> String {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
||||||
return dateFormatter.string(from: self)
|
return dateFormatter.string(from: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,27 +3,27 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 14/04/2021.
|
// Created by Nico Verbruggen on 14/04/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension NSMenu {
|
extension NSMenu {
|
||||||
|
|
||||||
open func addItem(_ newItem: NSMenuItem, withKeyModifier modifier: NSEvent.ModifierFlags) {
|
open func addItem(_ newItem: NSMenuItem, withKeyModifier modifier: NSEvent.ModifierFlags) {
|
||||||
newItem.keyEquivalentModifierMask = modifier
|
newItem.keyEquivalentModifierMask = modifier
|
||||||
self.addItem(newItem)
|
self.addItem(newItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBDesignable class LocalizedMenuItem: NSMenuItem {
|
@IBDesignable class LocalizedMenuItem: NSMenuItem {
|
||||||
|
|
||||||
@IBInspectable
|
@IBInspectable
|
||||||
var localizationKey: String? {
|
var localizationKey: String? {
|
||||||
didSet {
|
didSet {
|
||||||
self.title = localizationKey?.localized ?? self.title
|
self.title = localizationKey?.localized ?? self.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,29 +10,29 @@ import Foundation
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension NSWindow {
|
extension NSWindow {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Shakes a window. Inspired by: http://blog.ericd.net/2016/09/30/shaking-a-macos-window/
|
Shakes a window. Inspired by: http://blog.ericd.net/2016/09/30/shaking-a-macos-window/
|
||||||
*/
|
*/
|
||||||
func shake(){
|
func shake() {
|
||||||
let numberOfShakes = 3, durationOfShake = 0.2, vigourOfShake: CGFloat = 0.03
|
let numberOfShakes = 3, durationOfShake = 0.2, vigourOfShake: CGFloat = 0.03
|
||||||
|
|
||||||
let frame: CGRect = self.frame
|
let frame: CGRect = self.frame
|
||||||
let shakeAnimation :CAKeyframeAnimation = CAKeyframeAnimation()
|
let shakeAnimation: CAKeyframeAnimation = CAKeyframeAnimation()
|
||||||
|
|
||||||
let shakePath = CGMutablePath()
|
let shakePath = CGMutablePath()
|
||||||
shakePath.move( to: CGPoint(x:NSMinX(frame), y:NSMinY(frame)))
|
shakePath.move( to: CGPoint(x: frame.minX, y: frame.minY))
|
||||||
|
|
||||||
for _ in 0...numberOfShakes-1 {
|
for _ in 0...numberOfShakes-1 {
|
||||||
shakePath.addLine(to: CGPoint(x:NSMinX(frame) - frame.size.width * vigourOfShake, y:NSMinY(frame)))
|
shakePath.addLine(to: CGPoint(x: frame.minX - frame.size.width * vigourOfShake, y: frame.minY))
|
||||||
shakePath.addLine(to: CGPoint(x:NSMinX(frame) + frame.size.width * vigourOfShake, y:NSMinY(frame)))
|
shakePath.addLine(to: CGPoint(x: frame.minX + frame.size.width * vigourOfShake, y: frame.minY))
|
||||||
}
|
}
|
||||||
|
|
||||||
shakePath.closeSubpath()
|
shakePath.closeSubpath()
|
||||||
shakeAnimation.path = shakePath
|
shakeAnimation.path = shakePath
|
||||||
shakeAnimation.duration = durationOfShake
|
shakeAnimation.duration = durationOfShake
|
||||||
|
|
||||||
self.animations = ["frameOrigin":shakeAnimation]
|
self.animations = ["frameOrigin": shakeAnimation]
|
||||||
self.animator().setFrameOrigin(self.frame.origin)
|
self.animator().setFrameOrigin(self.frame.origin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,33 +2,33 @@
|
|||||||
// StringExtension.swift
|
// StringExtension.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
var localized: String {
|
var localized: String {
|
||||||
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func localized(_ args: CVarArg...) -> String {
|
func localized(_ args: CVarArg...) -> String {
|
||||||
String(format: self.localized, arguments: args)
|
String(format: self.localized, arguments: args)
|
||||||
}
|
}
|
||||||
|
|
||||||
func countInstances(of stringToFind: String) -> Int {
|
func countInstances(of stringToFind: String) -> Int {
|
||||||
if (stringToFind.isEmpty) {
|
if stringToFind.isEmpty {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
var searchRange: Range<String.Index>?
|
var searchRange: Range<String.Index>?
|
||||||
|
|
||||||
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
|
while let foundRange = range(of: stringToFind, options: [], range: searchRange) {
|
||||||
count += 1
|
count += 1
|
||||||
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
|
searchRange = Range(uncheckedBounds: (lower: foundRange.upperBound, upper: endIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ extension String {
|
|||||||
let end = r.upperBound
|
let end = r.upperBound
|
||||||
return String(self[start ..< end])
|
return String(self[start ..< end])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code taken from: https://sarunw.com/posts/how-to-compare-two-app-version-strings-in-swift/
|
// Code taken from: https://sarunw.com/posts/how-to-compare-two-app-version-strings-in-swift/
|
||||||
/*
|
/*
|
||||||
<1> We split the version by period (.).
|
<1> We split the version by period (.).
|
||||||
@@ -50,12 +50,12 @@ extension String {
|
|||||||
*/
|
*/
|
||||||
func versionCompare(_ otherVersion: String) -> ComparisonResult {
|
func versionCompare(_ otherVersion: String) -> ComparisonResult {
|
||||||
let versionDelimiter = "."
|
let versionDelimiter = "."
|
||||||
|
|
||||||
var versionComponents = self.components(separatedBy: versionDelimiter) // <1>
|
var versionComponents = self.components(separatedBy: versionDelimiter) // <1>
|
||||||
var otherVersionComponents = otherVersion.components(separatedBy: versionDelimiter)
|
var otherVersionComponents = otherVersion.components(separatedBy: versionDelimiter)
|
||||||
|
|
||||||
let zeroDiff = versionComponents.count - otherVersionComponents.count // <2>
|
let zeroDiff = versionComponents.count - otherVersionComponents.count // <2>
|
||||||
|
|
||||||
if zeroDiff == 0 { // <3>
|
if zeroDiff == 0 { // <3>
|
||||||
// Same format, compare normally
|
// Same format, compare normally
|
||||||
return self.compare(otherVersion, options: .numeric)
|
return self.compare(otherVersion, options: .numeric)
|
||||||
@@ -70,5 +70,5 @@ extension String {
|
|||||||
.compare(otherVersionComponents.joined(separator: versionDelimiter), options: .numeric) // <6>
|
.compare(otherVersionComponents.joined(separator: versionDelimiter), options: .numeric) // <6>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 04/02/2021.
|
// Created by Nico Verbruggen on 04/02/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -12,25 +12,25 @@ import Cocoa
|
|||||||
// Adapted from: https://stackoverflow.com/a/46268778
|
// Adapted from: https://stackoverflow.com/a/46268778
|
||||||
|
|
||||||
protocol XibLoadable {
|
protocol XibLoadable {
|
||||||
|
|
||||||
static var xibName: String? { get }
|
static var xibName: String? { get }
|
||||||
static func createFromXib(in bundle: Bundle) -> Self?
|
static func createFromXib(in bundle: Bundle) -> Self?
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension XibLoadable where Self: NSView {
|
extension XibLoadable where Self: NSView {
|
||||||
|
|
||||||
static var xibName: String? {
|
static var xibName: String? {
|
||||||
return String(describing: Self.self)
|
return String(describing: Self.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func createFromXib(in bundle: Bundle = Bundle.main) -> Self? {
|
static func createFromXib(in bundle: Bundle = Bundle.main) -> Self? {
|
||||||
guard let xibName = xibName else { return nil }
|
guard let xibName = xibName else { return nil }
|
||||||
var topLevelArray: NSArray? = nil
|
var topLevelArray: NSArray?
|
||||||
bundle.loadNibNamed(NSNib.Name(xibName), owner: self, topLevelObjects: &topLevelArray)
|
bundle.loadNibNamed(NSNib.Name(xibName), owner: self, topLevelObjects: &topLevelArray)
|
||||||
guard let results = topLevelArray else { return nil }
|
guard let results = topLevelArray else { return nil }
|
||||||
let views = Array<Any>(results).filter { $0 is Self }
|
let views = [Any](results).filter { $0 is Self }
|
||||||
return views.last as? Self
|
return views.last as? Self
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@
|
|||||||
// Alert.swift
|
// Alert.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Alert {
|
class Alert {
|
||||||
|
|
||||||
public static func confirm(
|
public static func confirm(
|
||||||
onWindow window: NSWindow,
|
onWindow window: NSWindow,
|
||||||
messageText: String,
|
messageText: String,
|
||||||
@@ -21,13 +21,13 @@ class Alert {
|
|||||||
if !Thread.isMainThread {
|
if !Thread.isMainThread {
|
||||||
fatalError("You should always present alerts on the main thread!")
|
fatalError("You should always present alerts on the main thread!")
|
||||||
}
|
}
|
||||||
|
|
||||||
let alert = NSAlert.init()
|
let alert = NSAlert.init()
|
||||||
alert.alertStyle = style
|
alert.alertStyle = style
|
||||||
alert.messageText = messageText
|
alert.messageText = messageText
|
||||||
alert.informativeText = informativeText
|
alert.informativeText = informativeText
|
||||||
alert.addButton(withTitle: buttonTitle)
|
alert.addButton(withTitle: buttonTitle)
|
||||||
if (!secondButtonTitle.isEmpty) {
|
if !secondButtonTitle.isEmpty {
|
||||||
alert.addButton(withTitle: secondButtonTitle)
|
alert.addButton(withTitle: secondButtonTitle)
|
||||||
}
|
}
|
||||||
alert.beginSheetModal(for: window) { response in
|
alert.beginSheetModal(for: window) { response in
|
||||||
@@ -36,5 +36,5 @@ class Alert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 07/12/2021.
|
// Created by Nico Verbruggen on 07/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -12,23 +12,23 @@ import Foundation
|
|||||||
/// In most cases this is going to be a code editor, but it could also be another application
|
/// In most cases this is going to be a code editor, but it could also be another application
|
||||||
/// that supports opening those directories, like a visual Git client or a terminal app.
|
/// that supports opening those directories, like a visual Git client or a terminal app.
|
||||||
class Application {
|
class Application {
|
||||||
|
|
||||||
enum AppType {
|
enum AppType {
|
||||||
case editor, browser, git_gui, terminal, user_supplied
|
case editor, browser, git_gui, terminal, user_supplied
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Name of the app. Used for display purposes and to determine `name.app` exists.
|
/// Name of the app. Used for display purposes and to determine `name.app` exists.
|
||||||
let name: String
|
let name: String
|
||||||
|
|
||||||
/// Application type. Depending on the type, a different action might occur.
|
/// Application type. Depending on the type, a different action might occur.
|
||||||
let type: AppType
|
let type: AppType
|
||||||
|
|
||||||
/// Initializer. Used to detect a specific app of a specific type.
|
/// Initializer. Used to detect a specific app of a specific type.
|
||||||
init(_ name: String, _ type: AppType) {
|
init(_ name: String, _ type: AppType) {
|
||||||
self.name = name
|
self.name = name
|
||||||
self.type = type
|
self.type = type
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Attempt to open a specific directory in the app of choice.
|
Attempt to open a specific directory in the app of choice.
|
||||||
(This will open the app if it isn't open yet.)
|
(This will open the app if it isn't open yet.)
|
||||||
@@ -36,7 +36,7 @@ class Application {
|
|||||||
@objc public func openDirectory(file: String) {
|
@objc public func openDirectory(file: String) {
|
||||||
return Shell.run("/usr/bin/open -a \"\(name)\" \"\(file)\"")
|
return Shell.run("/usr/bin/open -a \"\(name)\" \"\(file)\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Checks if the app is installed. */
|
/** Checks if the app is installed. */
|
||||||
func isInstalled() -> Bool {
|
func isInstalled() -> Bool {
|
||||||
// If this script does not complain, the app exists!
|
// If this script does not complain, the app exists!
|
||||||
@@ -45,7 +45,7 @@ class Application {
|
|||||||
requiresPath: false
|
requiresPath: false
|
||||||
).task.terminationStatus == 0
|
).task.terminationStatus == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Detect which apps are available to open a specific directory.
|
Detect which apps are available to open a specific directory.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 07/12/2021.
|
// Created by Nico Verbruggen on 07/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Filesystem {
|
class Filesystem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if a file exists at the provided path.
|
Checks if a file exists at the provided path.
|
||||||
Uses `FileManager`.
|
Uses `FileManager`.
|
||||||
@@ -19,5 +19,5 @@ class Filesystem {
|
|||||||
atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
atPath: path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,26 +2,26 @@
|
|||||||
// LocalNotification.swift
|
// LocalNotification.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
class LocalNotification {
|
class LocalNotification {
|
||||||
|
|
||||||
public static func send(title: String, subtitle: String) {
|
public static func send(title: String, subtitle: String) {
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = title
|
content.title = title
|
||||||
content.body = subtitle
|
content.body = subtitle
|
||||||
|
|
||||||
let uuidString = UUID().uuidString
|
let uuidString = UUID().uuidString
|
||||||
let request = UNNotificationRequest(
|
let request = UNNotificationRequest(
|
||||||
identifier: uuidString,
|
identifier: uuidString,
|
||||||
content: content,
|
content: content,
|
||||||
trigger: nil
|
trigger: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
let notificationCenter = UNUserNotificationCenter.current()
|
let notificationCenter = UNUserNotificationCenter.current()
|
||||||
notificationCenter.add(request) { (error) in
|
notificationCenter.add(request) { (error) in
|
||||||
if error != nil {
|
if error != nil {
|
||||||
@@ -29,5 +29,5 @@ class LocalNotification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,46 +2,46 @@
|
|||||||
// ImageGenerator.swift
|
// ImageGenerator.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class MenuBarImageGenerator {
|
class MenuBarImageGenerator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Takes a string and converts it to an image that can be displayed in the menu bar.
|
Takes a string and converts it to an image that can be displayed in the menu bar.
|
||||||
The width of the NSImage depends on the length of the text.
|
The width of the NSImage depends on the length of the text.
|
||||||
*/
|
*/
|
||||||
public static func textToImage(text: String) -> NSImage {
|
public static func textToImage(text: String) -> NSImage {
|
||||||
|
|
||||||
let font = NSFont.systemFont(ofSize: 14, weight: .medium)
|
let font = NSFont.systemFont(ofSize: 14, weight: .medium)
|
||||||
|
|
||||||
let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
|
let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
|
||||||
let textFontAttributes = [
|
let textFontAttributes = [
|
||||||
NSAttributedString.Key.font: font,
|
NSAttributedString.Key.font: font,
|
||||||
NSAttributedString.Key.foregroundColor: NSColor.black,
|
NSAttributedString.Key.foregroundColor: NSColor.black,
|
||||||
NSAttributedString.Key.paragraphStyle: textStyle
|
NSAttributedString.Key.paragraphStyle: textStyle
|
||||||
]
|
]
|
||||||
|
|
||||||
let padding : CGFloat = 2.0;
|
let padding: CGFloat = 2.0
|
||||||
|
|
||||||
// Create an attributed string so we'll know how wide the item will need to be
|
// Create an attributed string so we'll know how wide the item will need to be
|
||||||
let attributedString = NSAttributedString(string: text, attributes: textFontAttributes)
|
let attributedString = NSAttributedString(string: text, attributes: textFontAttributes)
|
||||||
let textSize = attributedString.size()
|
let textSize = attributedString.size()
|
||||||
|
|
||||||
// Add padding to the width of the menu bar item
|
// Add padding to the width of the menu bar item
|
||||||
let size = NSSize(width: textSize.width + (2 * padding), height: textSize.height)
|
let size = NSSize(width: textSize.width + (2 * padding), height: textSize.height)
|
||||||
let image = NSImage(size: size)
|
let image = NSImage(size: size)
|
||||||
|
|
||||||
// Set the image rect with the appropriate dimensions
|
// Set the image rect with the appropriate dimensions
|
||||||
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
|
let imageRect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
|
||||||
|
|
||||||
// Position the text inside the image rect
|
// Position the text inside the image rect
|
||||||
let textRect = CGRect(x: padding, y: 0.5, width: image.size.width, height: image.size.height)
|
let textRect = CGRect(x: padding, y: 0.5, width: image.size.width, height: image.size.height)
|
||||||
|
|
||||||
let targetImage: NSImage = NSImage(size: image.size)
|
let targetImage: NSImage = NSImage(size: image.size)
|
||||||
|
|
||||||
let representation: NSBitmapImageRep = NSBitmapImageRep(
|
let representation: NSBitmapImageRep = NSBitmapImageRep(
|
||||||
bitmapDataPlanes: nil,
|
bitmapDataPlanes: nil,
|
||||||
pixelsWide: Int(image.size.width),
|
pixelsWide: Int(image.size.width),
|
||||||
@@ -54,40 +54,40 @@ class MenuBarImageGenerator {
|
|||||||
bytesPerRow: 0,
|
bytesPerRow: 0,
|
||||||
bitsPerPixel: 0
|
bitsPerPixel: 0
|
||||||
)!
|
)!
|
||||||
|
|
||||||
targetImage.addRepresentation(representation)
|
targetImage.addRepresentation(representation)
|
||||||
targetImage.lockFocus()
|
targetImage.lockFocus()
|
||||||
|
|
||||||
image.draw(in: imageRect)
|
image.draw(in: imageRect)
|
||||||
text.draw(in: textRect, withAttributes: textFontAttributes)
|
text.draw(in: textRect, withAttributes: textFontAttributes)
|
||||||
|
|
||||||
targetImage.unlockFocus()
|
targetImage.unlockFocus()
|
||||||
return targetImage
|
return targetImage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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 {
|
||||||
|
|
||||||
// We'll start out with the image containing the text
|
// We'll start out with the image containing the text
|
||||||
let textImage = self.textToImage(text: text)
|
let textImage = self.textToImage(text: text)
|
||||||
|
|
||||||
// Then we'll fetch the image we want on the left
|
// Then we'll fetch the image we want on the left
|
||||||
var iconType = Preferences.preferences[.iconTypeToDisplay] as? String
|
var iconType = Preferences.preferences[.iconTypeToDisplay] as? String
|
||||||
if iconType == nil {
|
if iconType == nil {
|
||||||
Log.warn("Invalid icon type found, using the default")
|
Log.warn("Invalid icon type found, using the default")
|
||||||
iconType = MenuBarIcon.iconPhp.rawValue
|
iconType = MenuBarIcon.iconPhp.rawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconImage = NSImage(named: "MenuBar_\(iconType!)")!
|
let iconImage = NSImage(named: "MenuBar_\(iconType!)")!
|
||||||
|
|
||||||
// We'll need to reference the width of the icon a bunch of times
|
// We'll need to reference the width of the icon a bunch of times
|
||||||
let iconWidthSize = iconImage.size.width
|
let iconWidthSize = iconImage.size.width
|
||||||
|
|
||||||
// There will also be an additional divider between the image and the text (image)
|
// There will also be an additional divider between the image and the text (image)
|
||||||
let divider: CGFloat = 3
|
let divider: CGFloat = 3
|
||||||
|
|
||||||
// Use a fixed size for the height of the menu bar (18pt)
|
// Use a fixed size for the height of the menu bar (18pt)
|
||||||
let imageRect = CGRect(
|
let imageRect = CGRect(
|
||||||
x: 0,
|
x: 0,
|
||||||
@@ -95,14 +95,14 @@ class MenuBarImageGenerator {
|
|||||||
width: textImage.size.width + iconWidthSize + divider,
|
width: textImage.size.width + iconWidthSize + divider,
|
||||||
height: 18
|
height: 18
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create a new image, we'll draw the text and our icon in there
|
// 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()
|
||||||
|
|
||||||
// Calculate the offset between the image and the text
|
// Calculate the offset between the image and the text
|
||||||
let offset = imageRect.size.width - textImage.size.width
|
let offset = imageRect.size.width - textImage.size.width
|
||||||
|
|
||||||
// Draw the text with a negative x offset (so there is room on the left for the icon)
|
// Draw the text with a negative x offset (so there is room on the left for the icon)
|
||||||
textImage.draw(
|
textImage.draw(
|
||||||
in: imageRect,
|
in: imageRect,
|
||||||
@@ -115,7 +115,7 @@ class MenuBarImageGenerator {
|
|||||||
operation: .overlay,
|
operation: .overlay,
|
||||||
fraction: 1
|
fraction: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// Draw the icon directly in the left of the imageRect (where we left space)
|
// Draw the icon directly in the left of the imageRect (where we left space)
|
||||||
iconImage.draw(
|
iconImage.draw(
|
||||||
in: imageRect,
|
in: imageRect,
|
||||||
@@ -128,11 +128,11 @@ class MenuBarImageGenerator {
|
|||||||
operation: .overlay,
|
operation: .overlay,
|
||||||
fraction: 1
|
fraction: 1
|
||||||
)
|
)
|
||||||
|
|
||||||
// We're done with this image
|
// We're done with this image
|
||||||
image.unlockFocus()
|
image.unlockFocus()
|
||||||
|
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 05/12/2021.
|
// Created by Nico Verbruggen on 05/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
@@ -15,32 +15,32 @@ import Cocoa
|
|||||||
- Note: This class does make a simple assumption: each window controller corresponds to a single view.
|
- Note: This class does make a simple assumption: each window controller corresponds to a single view.
|
||||||
*/
|
*/
|
||||||
class PMWindowController: NSWindowController, NSWindowDelegate {
|
class PMWindowController: NSWindowController, NSWindowDelegate {
|
||||||
|
|
||||||
public var windowName: String {
|
public var windowName: String {
|
||||||
fatalError("Please specify a window name")
|
fatalError("Please specify a window name")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func showWindow(_ sender: Any?) {
|
override func showWindow(_ sender: Any?) {
|
||||||
super.showWindow(sender)
|
super.showWindow(sender)
|
||||||
App.shared.register(window: windowName)
|
App.shared.register(window: windowName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func windowWillClose(_ notification: Notification) {
|
func windowWillClose(_ notification: Notification) {
|
||||||
App.shared.remove(window: windowName)
|
App.shared.remove(window: windowName)
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
Log.perf("Window controller '\(windowName)' was deinitialized")
|
Log.perf("Window controller '\(windowName)' was deinitialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NSWindowController {
|
extension NSWindowController {
|
||||||
|
|
||||||
public func positionWindowInTopLeftCorner() {
|
public func positionWindowInTopLeftCorner() {
|
||||||
guard let frame = NSScreen.main?.frame else { return }
|
guard let frame = NSScreen.main?.frame else { return }
|
||||||
guard let window = self.window else { return }
|
guard let window = self.window else { return }
|
||||||
|
|
||||||
window.setFrame(NSRect(
|
window.setFrame(NSRect(
|
||||||
x: frame.size.width - window.frame.size.width - 20,
|
x: frame.size.width - window.frame.size.width - 20,
|
||||||
y: frame.size.height - window.frame.size.height - 40,
|
y: frame.size.height - window.frame.size.height - 40,
|
||||||
@@ -48,5 +48,5 @@ extension NSWindowController {
|
|||||||
height: window.frame.height
|
height: window.frame.height
|
||||||
), display: true)
|
), display: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 16/12/2021.
|
// Created by Nico Verbruggen on 16/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class VersionExtractor {
|
class VersionExtractor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This attempts to extract the version number from any given string.
|
This attempts to extract the version number from any given string.
|
||||||
*/
|
*/
|
||||||
@@ -19,26 +19,26 @@ class VersionExtractor {
|
|||||||
pattern: #"(?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
pattern: #"(?<version>(\d+)(.)(\d+)((.)(\d+))?)"#,
|
||||||
options: []
|
options: []
|
||||||
)
|
)
|
||||||
|
|
||||||
let match = regex.matches(
|
let match = regex.matches(
|
||||||
in: string,
|
in: string,
|
||||||
options: [],
|
options: [],
|
||||||
range: NSMakeRange(0, string.count)
|
range: NSRange(location: 0, length: string.count)
|
||||||
).first
|
).first
|
||||||
|
|
||||||
guard let match = match else {
|
guard let match = match else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let range = Range(
|
let range = Range(
|
||||||
match.range(withName: "version"),
|
match.range(withName: "version"),
|
||||||
in: string
|
in: string
|
||||||
)!
|
)!
|
||||||
|
|
||||||
return String(string[range])
|
return String(string[range])
|
||||||
} catch {
|
} catch {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// ActivePhpInstallation.swift
|
// ActivePhpInstallation.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -21,78 +21,78 @@ class ActivePhpInstallation {
|
|||||||
var version: Version!
|
var version: Version!
|
||||||
var limits: Limits!
|
var limits: Limits!
|
||||||
var extensions: [PhpExtension]!
|
var extensions: [PhpExtension]!
|
||||||
|
|
||||||
// MARK: - Computed
|
// MARK: - Computed
|
||||||
|
|
||||||
var formula: String {
|
var formula: String {
|
||||||
return (version.short == PhpEnv.brewPhpVersion) ? "php" : "php@\(version.short)"
|
return (version.short == PhpEnv.brewPhpVersion) ? "php" : "php@\(version.short)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
// Show information about the current version
|
// Show information about the current version
|
||||||
getVersion()
|
getVersion()
|
||||||
|
|
||||||
// If an error occurred, exit early
|
// If an error occurred, exit early
|
||||||
if (version.error) {
|
if version.error {
|
||||||
limits = Limits()
|
limits = Limits()
|
||||||
extensions = []
|
extensions = []
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load extension information
|
// Load extension information
|
||||||
let path = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
|
let path = URL(fileURLWithPath: "\(Paths.etcPath)/php/\(version.short)/php.ini")
|
||||||
extensions = PhpExtension.load(from: path)
|
extensions = PhpExtension.load(from: path)
|
||||||
|
|
||||||
// Get configuration values
|
// Get configuration values
|
||||||
limits = Limits(
|
limits = Limits(
|
||||||
memory_limit: getByteCount(key: "memory_limit"),
|
memory_limit: getByteCount(key: "memory_limit"),
|
||||||
upload_max_filesize: getByteCount(key: "upload_max_filesize"),
|
upload_max_filesize: getByteCount(key: "upload_max_filesize"),
|
||||||
post_max_size: getByteCount(key: "post_max_size")
|
post_max_size: getByteCount(key: "post_max_size")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Return a list of .ini files parsed after php.ini
|
// Return a list of .ini files parsed after php.ini
|
||||||
let paths = Command.execute(path: Paths.php, arguments: ["-r", "echo php_ini_scanned_files();"])
|
let paths = Command.execute(path: Paths.php, arguments: ["-r", "echo php_ini_scanned_files();"])
|
||||||
.replacingOccurrences(of: "\n", with: "")
|
.replacingOccurrences(of: "\n", with: "")
|
||||||
.split(separator: ",")
|
.split(separator: ",")
|
||||||
.map { String($0) }
|
.map { String($0) }
|
||||||
|
|
||||||
// See if any extensions are present in said .ini files
|
// See if any extensions are present in said .ini files
|
||||||
paths.forEach { (iniFilePath) in
|
paths.forEach { (iniFilePath) in
|
||||||
let exts = PhpExtension.load(from: URL(fileURLWithPath: iniFilePath))
|
let loadedExtensions = PhpExtension.load(from: URL(fileURLWithPath: iniFilePath))
|
||||||
if exts.count > 0 {
|
if !loadedExtensions.isEmpty {
|
||||||
extensions.append(contentsOf: exts)
|
extensions.append(contentsOf: loadedExtensions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When the app tries to retrieve the version, the installation is considered broken if the output is nothing,
|
When the app tries to retrieve the version, the installation is considered broken if the output is nothing,
|
||||||
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
|
_or_ if the output contains the word "Warning" or "Error". In normal situations this should not be the case.
|
||||||
*/
|
*/
|
||||||
private func getVersion() -> Void {
|
private func getVersion() {
|
||||||
self.version = Version()
|
self.version = Version()
|
||||||
|
|
||||||
let version = Command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
let version = Command.execute(path: Paths.phpConfig, arguments: ["--version"], trimNewlines: true)
|
||||||
|
|
||||||
if (version == "" || version.contains("Warning") || version.contains("Error")) {
|
if version == "" || version.contains("Warning") || version.contains("Error") {
|
||||||
self.version.short = "💩 BROKEN"
|
self.version.short = "💩 BROKEN"
|
||||||
self.version.long = ""
|
self.version.long = ""
|
||||||
self.version.error = true
|
self.version.error = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// That's the long version
|
// That's the long version
|
||||||
self.version.long = version
|
self.version.long = version
|
||||||
|
|
||||||
// Next up, let's strip away the minor version number
|
// Next up, let's strip away the minor version number
|
||||||
let segments = self.version.long.components(separatedBy: ".")
|
let segments = self.version.long.components(separatedBy: ".")
|
||||||
|
|
||||||
// Get the first two elements
|
// Get the first two elements
|
||||||
self.version.short = segments[0...1].joined(separator: ".")
|
self.version.short = segments[0...1].joined(separator: ".")
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Retrieves the display value for a specific key in the `.ini` file.
|
Retrieves the display value for a specific key in the `.ini` file.
|
||||||
|
|
||||||
@@ -110,18 +110,18 @@ class ActivePhpInstallation {
|
|||||||
*/
|
*/
|
||||||
private func getByteCount(key: String) -> String {
|
private func getByteCount(key: String) -> String {
|
||||||
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"])
|
let value = Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('\(key)');"])
|
||||||
|
|
||||||
// Check if the value is unlimited
|
// Check if the value is unlimited
|
||||||
if (value == "-1") {
|
if value == "-1" {
|
||||||
return "∞"
|
return "∞"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the syntax is valid otherwise
|
// Check if the syntax is valid otherwise
|
||||||
let regex = try! NSRegularExpression(pattern: #"^([0-9]*)(K|M|G|)$"#, options: [])
|
let regex = try! NSRegularExpression(pattern: #"^([0-9]*)(K|M|G|)$"#, options: [])
|
||||||
let match = regex.matches(in: value, options: [], range: NSMakeRange(0, value.count)).first
|
let match = regex.matches(in: value, options: [], range: NSRange(location: 0, length: value.count)).first
|
||||||
return (match == nil) ? "⚠️" : "\(value)B"
|
return (match == nil) ? "⚠️" : "\(value)B"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Determine if PHP-FPM is configured correctly.
|
Determine if PHP-FPM is configured correctly.
|
||||||
|
|
||||||
@@ -135,11 +135,11 @@ class ActivePhpInstallation {
|
|||||||
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
|
let fileName = "\(Paths.etcPath)/php/5.6/php-fpm.conf"
|
||||||
return Shell.pipe("cat \(fileName)").contains("valet.sock")
|
return Shell.pipe("cat \(fileName)").contains("valet.sock")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
||||||
return Filesystem.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
|
return Filesystem.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Structs
|
// MARK: - Structs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -153,7 +153,7 @@ class ActivePhpInstallation {
|
|||||||
var long = "???"
|
var long = "???"
|
||||||
var error = false
|
var error = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Struct containing information about the limits of the current PHP installation.
|
Struct containing information about the limits of the current PHP installation.
|
||||||
Includes: memory limit, max upload size and max post size.
|
Includes: memory limit, max upload size and max post size.
|
||||||
@@ -163,5 +163,5 @@ class ActivePhpInstallation {
|
|||||||
var upload_max_filesize = "???"
|
var upload_max_filesize = "???"
|
||||||
var post_max_size = "???"
|
var post_max_size = "???"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
phpmon/Common/PHP/Extensions/Xdebug.swift
Normal file
33
phpmon/Common/PHP/Extensions/Xdebug.swift
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Xdebug.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 01/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class Xdebug {
|
||||||
|
|
||||||
|
public static var enabled: Bool {
|
||||||
|
return !self.mode.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var mode: String {
|
||||||
|
return Command.execute(path: Paths.php, arguments: ["-r", "echo ini_get('xdebug.mode');"])
|
||||||
|
}
|
||||||
|
|
||||||
|
public static var modes: [String] {
|
||||||
|
return [
|
||||||
|
"off",
|
||||||
|
"develop",
|
||||||
|
"coverage",
|
||||||
|
"debug",
|
||||||
|
"gcstats",
|
||||||
|
"profile",
|
||||||
|
"trace"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,24 +2,24 @@
|
|||||||
// HomebrewPackage.swift
|
// HomebrewPackage.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct HomebrewPackage: Decodable {
|
struct HomebrewPackage: Decodable {
|
||||||
|
|
||||||
let name: String
|
let name: String
|
||||||
let full_name: String
|
let full_name: String
|
||||||
let aliases: [String]
|
let aliases: [String]
|
||||||
let installed: [HomebrewInstalled]
|
let installed: [HomebrewInstalled]
|
||||||
let linked_keg: String?
|
let linked_keg: String?
|
||||||
|
|
||||||
public var version: String {
|
public var version: String {
|
||||||
return aliases.first!
|
return aliases.first!
|
||||||
.replacingOccurrences(of: "php@", with: "")
|
.replacingOccurrences(of: "php@", with: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HomebrewInstalled: Decodable {
|
struct HomebrewInstalled: Decodable {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ struct HomebrewService: Decodable, Equatable {
|
|||||||
let status: String?
|
let status: String?
|
||||||
let log_path: String?
|
let log_path: String?
|
||||||
let error_log_path: String?
|
let error_log_path: String?
|
||||||
|
|
||||||
public static func loadAll(
|
public static func loadAll(
|
||||||
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"],
|
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"],
|
||||||
completion: @escaping ([HomebrewService]) -> Void
|
completion: @escaping ([HomebrewService]) -> Void
|
||||||
@@ -27,11 +27,11 @@ struct HomebrewService: Decodable, Equatable {
|
|||||||
let data = Shell
|
let data = Shell
|
||||||
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
||||||
.data(using: .utf8)!
|
.data(using: .utf8)!
|
||||||
|
|
||||||
let services = try! JSONDecoder()
|
let services = try! JSONDecoder()
|
||||||
.decode([HomebrewService].self, from: data)
|
.decode([HomebrewService].self, from: data)
|
||||||
.filter({ return filter.contains($0.name) })
|
.filter({ return filter.contains($0.name) })
|
||||||
|
|
||||||
completion(services)
|
completion(services)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,48 +3,48 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 21/12/2021.
|
// Created by Nico Verbruggen on 21/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PhpEnv {
|
class PhpEnv {
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.currentInstall = ActivePhpInstallation()
|
self.currentInstall = ActivePhpInstallation()
|
||||||
|
|
||||||
let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json");
|
let brewPhpAlias = Shell.pipe("\(Paths.brew) info php --json")
|
||||||
|
|
||||||
self.homebrewPackage = try! JSONDecoder().decode(
|
self.homebrewPackage = try! JSONDecoder().decode(
|
||||||
[HomebrewPackage].self,
|
[HomebrewPackage].self,
|
||||||
from: brewPhpAlias.data(using: .utf8)!
|
from: brewPhpAlias.data(using: .utf8)!
|
||||||
).first!
|
).first!
|
||||||
|
|
||||||
Log.info("When on your system, the `php` formula means version \(homebrewPackage.version)!")
|
Log.info("When on your system, the `php` formula means version \(homebrewPackage.version)!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Properties
|
// MARK: - Properties
|
||||||
|
|
||||||
/** The delegate that is informed of updates. */
|
/** The delegate that is informed of updates. */
|
||||||
weak var delegate: PhpSwitcherDelegate?
|
weak var delegate: PhpSwitcherDelegate?
|
||||||
|
|
||||||
/** The static app instance. Accessible at any time. */
|
/** The static app instance. Accessible at any time. */
|
||||||
static let shared = PhpEnv()
|
static let shared = PhpEnv()
|
||||||
|
|
||||||
/** Whether the switcher is busy performing any actions. */
|
/** Whether the switcher is busy performing any actions. */
|
||||||
var isBusy: Bool = false
|
var isBusy: Bool = false
|
||||||
|
|
||||||
/** All available versions of PHP. */
|
/** All available versions of PHP. */
|
||||||
var availablePhpVersions: [String] = []
|
var availablePhpVersions: [String] = []
|
||||||
|
|
||||||
/** Cached information about the PHP installations. */
|
/** Cached information about the PHP installations. */
|
||||||
var cachedPhpInstallations: [String: PhpInstallation] = [:]
|
var cachedPhpInstallations: [String: PhpInstallation] = [:]
|
||||||
|
|
||||||
/** Information about the currently linked PHP installation. */
|
/** Information about the currently linked PHP installation. */
|
||||||
var currentInstall: ActivePhpInstallation
|
var currentInstall: ActivePhpInstallation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The version that the `php` formula via Brew is aliased to on the current system.
|
The version that the `php` formula via Brew is aliased to on the current system.
|
||||||
|
|
||||||
@@ -57,63 +57,62 @@ class PhpEnv {
|
|||||||
static var brewPhpVersion: String {
|
static var brewPhpVersion: String {
|
||||||
return Self.shared.homebrewPackage.version
|
return Self.shared.homebrewPackage.version
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The currently linked and active PHP installation.
|
The currently linked and active PHP installation.
|
||||||
*/
|
*/
|
||||||
static var phpInstall: ActivePhpInstallation {
|
static var phpInstall: ActivePhpInstallation {
|
||||||
return Self.shared.currentInstall
|
return Self.shared.currentInstall
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Information we were able to discern from the Homebrew info command.
|
Information we were able to discern from the Homebrew info command.
|
||||||
*/
|
*/
|
||||||
var homebrewPackage: HomebrewPackage! = nil
|
var homebrewPackage: HomebrewPackage! = nil
|
||||||
|
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
|
|
||||||
public static var switcher: PhpSwitcher {
|
public static var switcher: PhpSwitcher {
|
||||||
return InternalSwitcher()
|
return InternalSwitcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func detectPhpVersions() -> Void {
|
public static func detectPhpVersions() {
|
||||||
_ = Self.shared.detectPhpVersions()
|
_ = Self.shared.detectPhpVersions()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Detects which versions of PHP are installed.
|
Detects which versions of PHP are installed.
|
||||||
*/
|
*/
|
||||||
public func detectPhpVersions() -> [String]
|
public func detectPhpVersions() -> [String] {
|
||||||
{
|
|
||||||
let files = Shell.pipe("ls \(Paths.optPath) | grep php@")
|
let files = Shell.pipe("ls \(Paths.optPath) | grep php@")
|
||||||
|
|
||||||
var versionsOnly = extractPhpVersions(from: files.components(separatedBy: "\n"))
|
var versionsOnly = extractPhpVersions(from: files.components(separatedBy: "\n"))
|
||||||
|
|
||||||
// Make sure the aliased version is detected
|
// Make sure the aliased version is detected
|
||||||
// The user may have `php` installed, but not e.g. `php@8.0`
|
// The user may have `php` installed, but not e.g. `php@8.0`
|
||||||
// We should also detect that as a version that is installed
|
// We should also detect that as a version that is installed
|
||||||
let phpAlias = homebrewPackage.version
|
let phpAlias = homebrewPackage.version
|
||||||
|
|
||||||
// Avoid inserting a duplicate
|
// Avoid inserting a duplicate
|
||||||
if (!versionsOnly.contains(phpAlias) && Filesystem.fileExists("\(Paths.optPath)/php/bin/php")) {
|
if !versionsOnly.contains(phpAlias) && Filesystem.fileExists("\(Paths.optPath)/php/bin/php") {
|
||||||
versionsOnly.append(phpAlias)
|
versionsOnly.append(phpAlias)
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info("The PHP versions that were detected are: \(versionsOnly)")
|
Log.info("The PHP versions that were detected are: \(versionsOnly)")
|
||||||
|
|
||||||
availablePhpVersions = versionsOnly
|
availablePhpVersions = versionsOnly
|
||||||
|
|
||||||
var mappedVersions: [String: PhpInstallation] = [:]
|
var mappedVersions: [String: PhpInstallation] = [:]
|
||||||
|
|
||||||
availablePhpVersions.forEach { version in
|
availablePhpVersions.forEach { version in
|
||||||
mappedVersions[version] = PhpInstallation(version)
|
mappedVersions[version] = PhpInstallation(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedPhpInstallations = mappedVersions
|
cachedPhpInstallations = mappedVersions
|
||||||
|
|
||||||
return versionsOnly
|
return versionsOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Extracts valid PHP versions from an array of strings.
|
Extracts valid PHP versions from an array of strings.
|
||||||
This array of strings is usually retrieved from `grep`.
|
This array of strings is usually retrieved from `grep`.
|
||||||
@@ -126,14 +125,14 @@ class PhpEnv {
|
|||||||
checkBinaries: Bool = true,
|
checkBinaries: Bool = true,
|
||||||
generateHelpers: Bool = true
|
generateHelpers: Bool = true
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
var output : [String] = []
|
var output: [String] = []
|
||||||
|
|
||||||
var supported = Constants.SupportedPhpVersions
|
var supported = Constants.SupportedPhpVersions
|
||||||
|
|
||||||
if !Valet.enabled(feature: .supportForPhp56) {
|
if !Valet.enabled(feature: .supportForPhp56) {
|
||||||
supported.removeAll { $0 == "5.6" }
|
supported.removeAll { $0 == "5.6" }
|
||||||
}
|
}
|
||||||
|
|
||||||
versions.filter { (version) -> Bool in
|
versions.filter { (version) -> Bool in
|
||||||
// Omit everything that doesn't start with php@
|
// Omit everything that doesn't start with php@
|
||||||
// (e.g. something-php@8.0 won't be detected)
|
// (e.g. something-php@8.0 won't be detected)
|
||||||
@@ -144,19 +143,18 @@ class PhpEnv {
|
|||||||
// is supported and where the binary exists (avoids broken installs)
|
// is supported and where the binary exists (avoids broken installs)
|
||||||
if !output.contains(version)
|
if !output.contains(version)
|
||||||
&& supported.contains(version)
|
&& supported.contains(version)
|
||||||
&& (checkBinaries ? Filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
&& (checkBinaries ? Filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true) {
|
||||||
{
|
|
||||||
output.append(version)
|
output.append(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if generateHelpers {
|
if generateHelpers {
|
||||||
output.forEach { PhpHelper.generate(for: $0) }
|
output.forEach { PhpHelper.generate(for: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
public func validVersions(for constraint: String) -> [PhpVersionNumber] {
|
public func validVersions(for constraint: String) -> [PhpVersionNumber] {
|
||||||
constraint.split(separator: "|").flatMap {
|
constraint.split(separator: "|").flatMap {
|
||||||
return PhpVersionNumberCollection
|
return PhpVersionNumberCollection
|
||||||
@@ -164,7 +162,7 @@ class PhpEnv {
|
|||||||
.matching(constraint: $0.trimmingCharacters(in: .whitespacesAndNewlines))
|
.matching(constraint: $0.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Validates whether the currently running version matches the provided version.
|
Validates whether the currently running version matches the provided version.
|
||||||
*/
|
*/
|
||||||
@@ -173,7 +171,7 @@ class PhpEnv {
|
|||||||
Log.info("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
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,27 +9,28 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PhpHelper {
|
class PhpHelper {
|
||||||
|
|
||||||
static let keyPhrase = "This file was automatically generated by PHP Monitor."
|
static let keyPhrase = "This file was automatically generated by PHP Monitor."
|
||||||
|
|
||||||
public static func generate(for version: String) {
|
public static func generate(for version: String) {
|
||||||
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||||
let dotless = version.replacingOccurrences(of: ".", with: "")
|
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let destination = "/usr/local/bin/pm\(dotless)"
|
let destination = "/usr/local/bin/pm\(dotless)"
|
||||||
if FileManager.default.fileExists(atPath: destination) {
|
if FileManager.default.fileExists(atPath: destination) {
|
||||||
let contents = try String(contentsOfFile: destination)
|
let contents = try String(contentsOfFile: destination)
|
||||||
if !contents.contains(keyPhrase) {
|
if !contents.contains(keyPhrase) {
|
||||||
Log.info("The file at '\(destination)' already exists and was not generated by PHP Monitor (or is unreadable). Not updating this file.")
|
Log.info("The file at '\(destination)' already exists and was not generated by PHP Monitor "
|
||||||
|
+ "(or is unreadable). Not updating this file.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's follow the symlink to the PHP binary folder
|
// Let's follow the symlink to the PHP binary folder
|
||||||
let path = URL(fileURLWithPath: "\(Paths.optPath)/php@\(version)/bin")
|
let path = URL(fileURLWithPath: "\(Paths.optPath)/php@\(version)/bin")
|
||||||
.resolvingSymlinksInPath().path
|
.resolvingSymlinksInPath().path
|
||||||
|
|
||||||
// The contents of the script!
|
// The contents of the script!
|
||||||
let script = """
|
let script = """
|
||||||
#!/bin/zsh
|
#!/bin/zsh
|
||||||
@@ -41,14 +42,14 @@ class PhpHelper {
|
|||||||
|| echo "You must run '. pm\(dotless)' (or 'source pm\(dotless)') instead!";
|
|| echo "You must run '. pm\(dotless)' (or 'source pm\(dotless)') instead!";
|
||||||
export PATH=\(path):$PATH
|
export PATH=\(path):$PATH
|
||||||
"""
|
"""
|
||||||
|
|
||||||
// Write to the destination
|
// Write to the destination
|
||||||
try script.write(
|
try script.write(
|
||||||
to: URL(fileURLWithPath: destination),
|
to: URL(fileURLWithPath: destination),
|
||||||
atomically: true,
|
atomically: true,
|
||||||
encoding: String.Encoding.utf8
|
encoding: String.Encoding.utf8
|
||||||
)
|
)
|
||||||
|
|
||||||
// Make sure the file is executable
|
// Make sure the file is executable
|
||||||
Shell.run("chmod +x \(destination)")
|
Shell.run("chmod +x \(destination)")
|
||||||
} catch {
|
} catch {
|
||||||
@@ -56,5 +57,5 @@ class PhpHelper {
|
|||||||
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,21 +10,21 @@ import Foundation
|
|||||||
|
|
||||||
public struct PhpVersionNumberCollection: Equatable {
|
public struct PhpVersionNumberCollection: Equatable {
|
||||||
let versions: [PhpVersionNumber]
|
let versions: [PhpVersionNumber]
|
||||||
|
|
||||||
public static func make(from versions: [String]) -> Self {
|
public static func make(from versions: [String]) -> Self {
|
||||||
return PhpVersionNumberCollection(
|
return PhpVersionNumberCollection(
|
||||||
versions: versions.map { try! PhpVersionNumber.parse($0) }
|
versions: versions.map { try! PhpVersionNumber.parse($0) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var first: PhpVersionNumber? {
|
public var first: PhpVersionNumber? {
|
||||||
return self.versions.first
|
return self.versions.first
|
||||||
}
|
}
|
||||||
|
|
||||||
public var all: [PhpVersionNumber] {
|
public var all: [PhpVersionNumber] {
|
||||||
return self.versions
|
return self.versions
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if any versions of PHP are valid for the constraint provided.
|
Checks if any versions of PHP are valid for the constraint provided.
|
||||||
Due to the complexity of evaluating these, a important test is maintained.
|
Due to the complexity of evaluating these, a important test is maintained.
|
||||||
@@ -61,13 +61,13 @@ public struct PhpVersionNumberCollection: Equatable {
|
|||||||
// Strict constraint (e.g. "7.0") -> returns specific version
|
// Strict constraint (e.g. "7.0") -> returns specific version
|
||||||
return self.versions.filter { $0.isSameAs(version, strict) }
|
return self.versions.filter { $0.isSameAs(version, strict) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let version = PhpVersionNumber.make(from: constraint, type: .caretVersionRange) {
|
if let version = PhpVersionNumber.make(from: constraint, type: .caretVersionRange) {
|
||||||
// Caret range means that the major version is never higher but minor version can be higher
|
// Caret range means that the major version is never higher but minor version can be higher
|
||||||
// ^7.2 will be compatible with all versions between 7.2 and 8.0
|
// ^7.2 will be compatible with all versions between 7.2 and 8.0
|
||||||
return self.versions.filter { $0.hasNewerMinorVersionOrPatch(version, strict) }
|
return self.versions.filter { $0.hasNewerMinorVersionOrPatch(version, strict) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let version = PhpVersionNumber.make(from: constraint, type: .tildeVersionRange) {
|
if let version = PhpVersionNumber.make(from: constraint, type: .tildeVersionRange) {
|
||||||
// Tilde range means that most specific digit is used as the basis.
|
// Tilde range means that most specific digit is used as the basis.
|
||||||
return self.versions.filter {
|
return self.versions.filter {
|
||||||
@@ -78,11 +78,11 @@ public struct PhpVersionNumberCollection: Equatable {
|
|||||||
: $0.hasSameMajorButNewerOrSameMinor(version, strict)
|
: $0.hasSameMajorButNewerOrSameMinor(version, strict)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let version = PhpVersionNumber.make(from: constraint, type: .greaterThanOrEqual) {
|
if let version = PhpVersionNumber.make(from: constraint, type: .greaterThanOrEqual) {
|
||||||
return self.versions.filter { $0.isSameAs(version, strict) || $0.isNewerThan(version, strict) }
|
return self.versions.filter { $0.isSameAs(version, strict) || $0.isNewerThan(version, strict) }
|
||||||
}
|
}
|
||||||
|
|
||||||
if let version = PhpVersionNumber.make(from: constraint, type: .greaterThan) {
|
if let version = PhpVersionNumber.make(from: constraint, type: .greaterThan) {
|
||||||
return self.versions.filter { $0.isNewerThan(version, strict) }
|
return self.versions.filter { $0.isNewerThan(version, strict) }
|
||||||
}
|
}
|
||||||
@@ -95,47 +95,52 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
let major: Int
|
let major: Int
|
||||||
let minor: Int
|
let minor: Int
|
||||||
let patch: Int?
|
let patch: Int?
|
||||||
|
|
||||||
public func toString() -> String {
|
public func toString() -> String {
|
||||||
return self.patch == nil
|
return self.patch == nil
|
||||||
? "\(major).\(minor)"
|
? "\(major).\(minor)"
|
||||||
: "\(major).\(minor).\(patch!)"
|
: "\(major).\(minor).\(patch!)"
|
||||||
}
|
}
|
||||||
|
|
||||||
public func patch(_ strictFallback: Bool = true, _ constraint: PhpVersionNumber? = nil) -> Int {
|
public func patch(_ strictFallback: Bool = true, _ constraint: PhpVersionNumber? = nil) -> Int {
|
||||||
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
||||||
}
|
}
|
||||||
|
|
||||||
public var homebrewVersion: String {
|
public var homebrewVersion: String {
|
||||||
return "\(major).\(minor)"
|
return "\(major).\(minor)"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MatchType: String {
|
public enum MatchType: String {
|
||||||
case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case versionOnly = #"^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case caretVersionRange = #"^\^(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case tildeVersionRange = #"^~(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
|
|
||||||
// TODO: (6.0) Handle these cases (even though I suspect these are uncommon)
|
// TODO: (6.0) Handle these cases (even though I suspect these are uncommon)
|
||||||
/*
|
/*
|
||||||
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func parse(_ text: String) throws -> Self {
|
public static func parse(_ text: String) throws -> Self {
|
||||||
guard let versionText = VersionExtractor.from(text) else {
|
guard let versionText = VersionExtractor.from(text) else {
|
||||||
throw VersionParseError()
|
throw VersionParseError()
|
||||||
}
|
}
|
||||||
|
|
||||||
return Self.make(from: versionText)!
|
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: NSRange(location: 0, length: versionString.count)
|
||||||
|
).first
|
||||||
|
|
||||||
if match != nil {
|
if match != nil {
|
||||||
let major = Int(
|
let major = Int(
|
||||||
versionString[Range(match!.range(withName: "major"), in: versionString)!]
|
versionString[Range(match!.range(withName: "major"), in: versionString)!]
|
||||||
@@ -143,24 +148,24 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
let minor = Int(
|
let minor = Int(
|
||||||
versionString[Range(match!.range(withName: "minor"), in: versionString)!]
|
versionString[Range(match!.range(withName: "minor"), in: versionString)!]
|
||||||
)!
|
)!
|
||||||
var patch: Int? = nil
|
var patch: Int?
|
||||||
if let minorRange = Range(match!.range(withName: "patch"), in: versionString) {
|
if let minorRange = Range(match!.range(withName: "patch"), in: versionString) {
|
||||||
patch = Int(versionString[minorRange])
|
patch = Int(versionString[minorRange])
|
||||||
}
|
}
|
||||||
return Self(major: major, minor: minor, patch: patch)
|
return Self(major: major, minor: minor, patch: patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Comparison Logic
|
// MARK: Comparison Logic
|
||||||
|
|
||||||
internal func isSameAs(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
internal func isSameAs(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
||||||
return self.major == version.major
|
return self.major == version.major
|
||||||
&& self.minor == version.minor
|
&& self.minor == version.minor
|
||||||
&& (strict ? self.patch(strict, version) == version.patch(strict) : true)
|
&& (strict ? self.patch(strict, version) == version.patch(strict) : true)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func isNewerThan(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
internal func isNewerThan(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
||||||
return (
|
return (
|
||||||
self.major > version.major ||
|
self.major > version.major ||
|
||||||
@@ -169,7 +174,7 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
&& self.patch(strict) > version.patch(strict)
|
&& self.patch(strict) > version.patch(strict)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func hasNewerMinorVersionOrPatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
internal func hasNewerMinorVersionOrPatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
||||||
return self.major == version.major &&
|
return self.major == version.major &&
|
||||||
(
|
(
|
||||||
@@ -177,12 +182,12 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
|| self.minor > version.minor
|
|| self.minor > version.minor
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func hasSameMajorAndMinorButNewerOrSamePatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
internal func hasSameMajorAndMinorButNewerOrSamePatch(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
||||||
return self.major == version.major && self.minor == version.minor
|
return self.major == version.major && self.minor == version.minor
|
||||||
&& self.patch(strict, version) >= version.patch(strict)
|
&& self.patch(strict, version) >= version.patch(strict)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal func hasSameMajorButNewerOrSameMinor(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
internal func hasSameMajorButNewerOrSameMinor(_ version: PhpVersionNumber, _ strict: Bool) -> Bool {
|
||||||
return self.major == version.major
|
return self.major == version.major
|
||||||
&& self.minor >= version.minor
|
&& self.minor >= version.minor
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 31/01/2021.
|
// Created by Nico Verbruggen on 31/01/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -16,24 +16,26 @@ import Foundation
|
|||||||
instances. You can find more information here: https://nshipster.com/swift-regular-expressions/
|
instances. You can find more information here: https://nshipster.com/swift-regular-expressions/
|
||||||
*/
|
*/
|
||||||
class PhpExtension {
|
class PhpExtension {
|
||||||
|
|
||||||
/// The file where this extension was located.
|
/// The file where this extension was located.
|
||||||
var file: String
|
var file: String
|
||||||
|
|
||||||
/// The original string that was used to determine this extension is active.
|
/// The original string that was used to determine this extension is active.
|
||||||
var line: String
|
var line: String
|
||||||
|
|
||||||
/// The name of the extension. This is always identical to the name found in the original string. If you want to display this name, capitalize this.
|
/// The name of the extension. This is always identical to the name found in the original string.
|
||||||
|
/// If you want to display this name, capitalize this.
|
||||||
var name: String
|
var name: String
|
||||||
|
|
||||||
/// Whether the extension has been enabled.
|
/// Whether the extension has been enabled.
|
||||||
var enabled: Bool
|
var enabled: Bool
|
||||||
|
|
||||||
/// The file where this extension was located, but only the filename, not the full path to the .ini file.
|
/// The file where this extension was located, but only the filename, not the full path to the .ini file.
|
||||||
var fileNameOnly: String {
|
var fileNameOnly: String {
|
||||||
return String(file.split(separator: "/").last ?? "php.ini")
|
return String(file.split(separator: "/").last ?? "php.ini")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable line_length
|
||||||
/**
|
/**
|
||||||
This regular expression will allow us to identify lines which activate an extension.
|
This regular expression will allow us to identify lines which activate an extension.
|
||||||
|
|
||||||
@@ -47,29 +49,31 @@ class PhpExtension {
|
|||||||
- Note: Extensions that are disabled in a different way will not be detected. This is intentional.
|
- Note: Extensions that are disabled in a different way will not be detected. This is intentional.
|
||||||
*/
|
*/
|
||||||
static let extensionRegex = #"^(extension|zend_extension|;(\s?)extension|;(\s?)zend_extension)(\s?)(=)(\s?)(?<name>["]?(?:\/?.\/?)+(?:\.so)"?)$"#
|
static let extensionRegex = #"^(extension|zend_extension|;(\s?)extension|;(\s?)zend_extension)(\s?)(=)(\s?)(?<name>["]?(?:\/?.\/?)+(?:\.so)"?)$"#
|
||||||
|
// swiftlint:enable line_length
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When registering an extension, we do that based on the line found inside the .ini file.
|
When registering an extension, we do that based on the line found inside the .ini file.
|
||||||
*/
|
*/
|
||||||
init(_ line: String, file: String) {
|
init(_ line: String, file: String) {
|
||||||
let regex = try! NSRegularExpression(pattern: Self.extensionRegex, options: [])
|
let regex = try! NSRegularExpression(pattern: Self.extensionRegex, options: [])
|
||||||
let match = regex.matches(in: line, options: [], range: NSMakeRange(0, line.count)).first
|
let match = regex.matches(in: line, options: [], range: NSRange(location: 0, length: line.count)).first
|
||||||
let range = Range(match!.range(withName: "name"), in: line)!
|
let range = Range(match!.range(withName: "name"), in: line)!
|
||||||
|
|
||||||
self.line = line
|
self.line = line
|
||||||
|
|
||||||
let fullPath = String(line[range])
|
let fullPath = String(line[range])
|
||||||
.replacingOccurrences(of: "\"", with: "") // replace excess "
|
.replacingOccurrences(of: "\"", with: "") // replace excess "
|
||||||
.replacingOccurrences(of: ".so", with: "") // replace excess .so
|
.replacingOccurrences(of: ".so", with: "") // replace excess .so
|
||||||
|
|
||||||
self.name = String(fullPath.split(separator: "/").last!) // take last segment
|
self.name = String(fullPath.split(separator: "/").last!) // take last segment
|
||||||
|
|
||||||
self.enabled = !line.contains(";")
|
self.enabled = !line.contains(";")
|
||||||
self.file = file
|
self.file = file
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This simply toggles the extension in the .ini file. You may need to restart the other services in order for this change to apply.
|
This simply toggles the extension in the .ini file.
|
||||||
|
You may need to restart the other services in order for this change to apply.
|
||||||
*/
|
*/
|
||||||
func toggle() {
|
func toggle() {
|
||||||
let newLine = enabled
|
let newLine = enabled
|
||||||
@@ -77,25 +81,25 @@ class PhpExtension {
|
|||||||
? "; \(line)"
|
? "; \(line)"
|
||||||
// ENABLED: Line where the comment delimiter (;) is removed
|
// ENABLED: Line where the comment delimiter (;) is removed
|
||||||
: line.replacingOccurrences(of: "; ", with: "")
|
: line.replacingOccurrences(of: "; ", with: "")
|
||||||
|
|
||||||
sed(file: file, original: line, replacement: newLine)
|
sed(file: file, original: line, replacement: newLine)
|
||||||
|
|
||||||
enabled.toggle()
|
enabled.toggle()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Static Methods
|
// MARK: - Static Methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This method will attempt to identify all extensions in the .ini file at a certain URL.
|
This method will attempt to identify all extensions in the .ini file at a certain URL.
|
||||||
*/
|
*/
|
||||||
static func load(from path: URL) -> [PhpExtension] {
|
static func load(from path: URL) -> [PhpExtension] {
|
||||||
let file = try? String(contentsOf: path, encoding: .utf8)
|
let file = try? String(contentsOf: path, encoding: .utf8)
|
||||||
|
|
||||||
if (file == nil) {
|
if file == nil {
|
||||||
Log.err("There was an issue reading the file. Assuming no extensions were found.")
|
Log.err("There was an issue reading the file. Assuming no extensions were found.")
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return file!.components(separatedBy: "\n")
|
return file!.components(separatedBy: "\n")
|
||||||
.filter {
|
.filter {
|
||||||
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
return $0.range(of: Self.extensionRegex, options: .regularExpression) != nil
|
||||||
@@ -104,5 +108,5 @@ class PhpExtension {
|
|||||||
return PhpExtension($0, file: path.path)
|
return PhpExtension($0, file: path.path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,34 +3,34 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 28/11/2021.
|
// Created by Nico Verbruggen on 28/11/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class PhpInstallation {
|
class PhpInstallation {
|
||||||
|
|
||||||
var versionNumber: PhpVersionNumber
|
var versionNumber: PhpVersionNumber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
||||||
in the relevant directory.
|
in the relevant directory.
|
||||||
*/
|
*/
|
||||||
init(_ version: String) {
|
init(_ version: String) {
|
||||||
|
|
||||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||||
self.versionNumber = PhpVersionNumber.make(from: version)!
|
self.versionNumber = PhpVersionNumber.make(from: version)!
|
||||||
|
|
||||||
if Filesystem.fileExists(phpConfigExecutablePath) {
|
if Filesystem.fileExists(phpConfigExecutablePath) {
|
||||||
let longVersionString = Command.execute(
|
let longVersionString = Command.execute(
|
||||||
path: phpConfigExecutablePath,
|
path: phpConfigExecutablePath,
|
||||||
arguments: ["--version"]
|
arguments: ["--version"]
|
||||||
).trimmingCharacters(in: .whitespacesAndNewlines)
|
).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||||
|
|
||||||
// The parser should always work, or the string has to be very unusual.
|
// The parser should always work, or the string has to be very unusual.
|
||||||
// If so, the app SHOULD crash, so that the users report what's up.
|
// If so, the app SHOULD crash, so that the users report what's up.
|
||||||
self.versionNumber = try! PhpVersionNumber.parse(longVersionString)
|
self.versionNumber = try! PhpVersionNumber.parse(longVersionString)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 24/12/2021.
|
// Created by Nico Verbruggen on 24/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class InternalSwitcher: PhpSwitcher {
|
class InternalSwitcher: PhpSwitcher {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Switching to a new PHP version involves:
|
Switching to a new PHP version involves:
|
||||||
- unlinking the current version
|
- unlinking the current version
|
||||||
@@ -20,50 +20,49 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
the version that is switched to may or may not be identical to `php`
|
the version that is switched to may or may not be identical to `php`
|
||||||
(without @version).
|
(without @version).
|
||||||
*/
|
*/
|
||||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
func performSwitch(to version: String, completion: @escaping () -> Void) {
|
||||||
{
|
|
||||||
Log.info("Switching to \(version), unlinking all versions...")
|
Log.info("Switching to \(version), unlinking all versions...")
|
||||||
|
|
||||||
let isolated = Valet.shared.sites.filter { site in
|
let isolated = Valet.shared.sites.filter { site in
|
||||||
site.isolatedPhpVersion != nil
|
site.isolatedPhpVersion != nil
|
||||||
}.map { site in
|
}.map { site in
|
||||||
return site.isolatedPhpVersion!.versionNumber.homebrewVersion
|
return site.isolatedPhpVersion!.versionNumber.homebrewVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
var versions: Set<String> = [version]
|
var versions: Set<String> = [version]
|
||||||
|
|
||||||
if (Valet.enabled(feature: .isolatedSites)) {
|
if Valet.enabled(feature: .isolatedSites) {
|
||||||
versions = versions.union(isolated)
|
versions = versions.union(isolated)
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
self.disableDefaultPhpFpmPool(available)
|
self.disableDefaultPhpFpmPool(available)
|
||||||
self.stopPhpVersion(available)
|
self.stopPhpVersion(available)
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
group.notify(queue: .global(qos: .userInitiated)) {
|
group.notify(queue: .global(qos: .userInitiated)) {
|
||||||
Log.info("All versions have been unlinked!")
|
Log.info("All versions have been unlinked!")
|
||||||
Log.info("Linking the new version!")
|
Log.info("Linking the new version!")
|
||||||
|
|
||||||
for formula in versions {
|
for formula in versions {
|
||||||
self.startPhpVersion(formula, primary: (version == formula))
|
self.startPhpVersion(formula, primary: (version == formula))
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info("Restarting nginx, just to be sure!")
|
Log.info("Restarting nginx, just to be sure!")
|
||||||
brew("services restart nginx", sudo: true)
|
brew("services restart nginx", sudo: true)
|
||||||
|
|
||||||
Log.info("The new version(s) have been linked!")
|
Log.info("The new version(s) have been linked!")
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func disableDefaultPhpFpmPool(_ version: String) {
|
private func disableDefaultPhpFpmPool(_ version: String) {
|
||||||
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||||
if FileManager.default.fileExists(atPath: pool) {
|
if FileManager.default.fileExists(atPath: pool) {
|
||||||
@@ -71,8 +70,9 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
let existing = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf")!
|
let existing = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf")!
|
||||||
let new = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf.disabled-by-phpmon")!
|
let new = URL(string: "file://\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf.disabled-by-phpmon")!
|
||||||
do {
|
do {
|
||||||
if (FileManager.default.fileExists(atPath: new.path)) {
|
if FileManager.default.fileExists(atPath: new.path) {
|
||||||
Log.info("A moved `www.conf.disabled-by-phpmon` file was found for PHP \(version), cleaning up so the newer `www.conf` can be moved again.")
|
Log.info("A moved `www.conf.disabled-by-phpmon` file was found for PHP \(version), "
|
||||||
|
+ "cleaning up so the newer `www.conf` can be moved again.")
|
||||||
try FileManager.default.removeItem(at: new)
|
try FileManager.default.removeItem(at: new)
|
||||||
}
|
}
|
||||||
try FileManager.default.moveItem(at: existing, to: new)
|
try FileManager.default.moveItem(at: existing, to: new)
|
||||||
@@ -82,26 +82,26 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func stopPhpVersion(_ version: String) {
|
private func stopPhpVersion(_ version: String) {
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
brew("unlink \(formula)")
|
brew("unlink \(formula)")
|
||||||
brew("services stop \(formula)", sudo: true)
|
brew("services stop \(formula)", sudo: true)
|
||||||
Log.info("Unlinked and stopped services for \(formula)")
|
Log.info("Unlinked and stopped services for \(formula)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func startPhpVersion(_ version: String, primary: Bool) {
|
private func startPhpVersion(_ version: String, primary: Bool) {
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
|
||||||
if (primary) {
|
if primary {
|
||||||
Log.info("\(formula) is the primary formula, linking and starting services...")
|
Log.info("\(formula) is the primary formula, linking and starting services...")
|
||||||
brew("link \(formula) --overwrite --force")
|
brew("link \(formula) --overwrite --force")
|
||||||
} else {
|
} else {
|
||||||
Log.info("\(formula) is an isolated PHP version, starting services only...")
|
Log.info("\(formula) is an isolated PHP version, starting services only...")
|
||||||
}
|
}
|
||||||
|
|
||||||
brew("services start \(formula)", sudo: true)
|
brew("services start \(formula)", sudo: true)
|
||||||
|
|
||||||
if Valet.enabled(feature: .isolatedSites) && primary {
|
if Valet.enabled(feature: .isolatedSites) && primary {
|
||||||
let socketVersion = version.replacingOccurrences(of: ".", with: "")
|
let socketVersion = version.replacingOccurrences(of: ".", with: "")
|
||||||
Shell.run("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock")
|
Shell.run("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock")
|
||||||
@@ -109,5 +109,5 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,21 +3,21 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 24/12/2021.
|
// Created by Nico Verbruggen on 24/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol PhpSwitcherDelegate: AnyObject {
|
protocol PhpSwitcherDelegate: AnyObject {
|
||||||
|
|
||||||
func switcherDidStartSwitching(to version: String)
|
func switcherDidStartSwitching(to version: String)
|
||||||
|
|
||||||
func switcherDidCompleteSwitch(to version: String)
|
func switcherDidCompleteSwitch(to version: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol PhpSwitcher {
|
protocol PhpSwitcher {
|
||||||
|
|
||||||
func performSwitch(to version: String, completion: @escaping () -> Void)
|
func performSwitch(to version: String, completion: @escaping () -> Void)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 05/12/2021.
|
// Created by Nico Verbruggen on 05/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension App {
|
extension App {
|
||||||
|
|
||||||
// MARK: - Application State
|
// MARK: - Application State
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Registers a window as currently open.
|
Registers a window as currently open.
|
||||||
*/
|
*/
|
||||||
@@ -22,7 +22,7 @@ extension App {
|
|||||||
}
|
}
|
||||||
updateActivationPolicy()
|
updateActivationPolicy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Removes a window, assuming it was closed.
|
Removes a window, assuming it was closed.
|
||||||
*/
|
*/
|
||||||
@@ -32,13 +32,13 @@ extension App {
|
|||||||
}
|
}
|
||||||
updateActivationPolicy()
|
updateActivationPolicy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
If there are any open windows, the app will be a regular app.
|
If there are any open windows, the app will be a regular app.
|
||||||
If there are no windows open, the app will be an accessory (toolbar) app.
|
If there are no windows open, the app will be an accessory (toolbar) app.
|
||||||
*/
|
*/
|
||||||
public func updateActivationPolicy() {
|
public func updateActivationPolicy() {
|
||||||
NSApp.setActivationPolicy(openWindows.count > 0 ? .regular : .accessory)
|
NSApp.setActivationPolicy(!openWindows.isEmpty ? .regular : .accessory)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 05/12/2021.
|
// Created by Nico Verbruggen on 05/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension App {
|
extension App {
|
||||||
|
|
||||||
// MARK: - Methods
|
// MARK: - Methods
|
||||||
|
|
||||||
/**
|
/**
|
||||||
On startup, the preferences should be loaded from the .plist,
|
On startup, the preferences should be loaded from the .plist,
|
||||||
and we'll enable the shortcut if it is set.
|
and we'll enable the shortcut if it is set.
|
||||||
@@ -22,20 +22,20 @@ extension App {
|
|||||||
Log.info("No global hotkey was saved in preferences. None set.")
|
Log.info("No global hotkey was saved in preferences. None set.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can parse the JSON into the desired format
|
// Make sure we can parse the JSON into the desired format
|
||||||
guard let keybindPref = GlobalKeybindPreference.fromJson(hotkey) else {
|
guard let keybindPref = GlobalKeybindPreference.fromJson(hotkey) else {
|
||||||
Log.err("No global hotkey loaded, could not be parsed!")
|
Log.err("No global hotkey loaded, could not be parsed!")
|
||||||
shortcutHotkey = nil
|
shortcutHotkey = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
shortcutHotkey = HotKey(keyCombo: KeyCombo(
|
shortcutHotkey = HotKey(keyCombo: KeyCombo(
|
||||||
carbonKeyCode: keybindPref.keyCode,
|
carbonKeyCode: keybindPref.keyCode,
|
||||||
carbonModifiers: keybindPref.carbonFlags
|
carbonModifiers: keybindPref.carbonFlags
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets up the action that needs to occur when the shortcut key is pressed
|
Sets up the action that needs to occur when the shortcut key is pressed
|
||||||
(opens the menu).
|
(opens the menu).
|
||||||
@@ -44,11 +44,11 @@ extension App {
|
|||||||
guard let hotkey = shortcutHotkey else {
|
guard let hotkey = shortcutHotkey else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hotkey.keyDownHandler = {
|
hotkey.keyDownHandler = {
|
||||||
MainMenu.shared.statusItem.button?.performClick(nil)
|
MainMenu.shared.statusItem.button?.performClick(nil)
|
||||||
NSApplication.shared.activate(ignoringOtherApps: true)
|
NSApplication.shared.activate(ignoringOtherApps: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,25 +2,35 @@
|
|||||||
// StateManager.swift
|
// StateManager.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
|
|
||||||
// MARK: Static Vars
|
// MARK: Static Vars
|
||||||
|
|
||||||
/** The static app instance. Accessible at any time. */
|
/** The static app instance. Accessible at any time. */
|
||||||
static let shared = App()
|
static let shared = App()
|
||||||
|
|
||||||
/** Retrieve the version number from the main info dictionary, Info.plist. */
|
/** Retrieve the version number from the main info dictionary, Info.plist. */
|
||||||
static var version: String {
|
static var version: String {
|
||||||
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
|
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
|
||||||
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as! String
|
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as! String
|
||||||
return "\(version) (\(build))"
|
return "\(version) (\(build))"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Just the bundle version (build). */
|
||||||
|
static var bundleVersion: String {
|
||||||
|
return Bundle.main.infoDictionary?["CFBundleVersion"] as! String
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Just the version number. */
|
||||||
|
static var shortVersion: String {
|
||||||
|
return Bundle.main.infoDictionary?["CFBundleShortVersionString"] as! String
|
||||||
|
}
|
||||||
|
|
||||||
static var architecture: String {
|
static var architecture: String {
|
||||||
var systeminfo = utsname()
|
var systeminfo = utsname()
|
||||||
uname(&systeminfo)
|
uname(&systeminfo)
|
||||||
@@ -34,37 +44,37 @@ class App {
|
|||||||
}
|
}
|
||||||
return machine
|
return machine
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Variables
|
// MARK: Variables
|
||||||
|
|
||||||
/** The list of preferences that are currently active. */
|
/** The list of preferences that are currently active. */
|
||||||
var preferences: [PreferenceName: Bool]!
|
var preferences: [PreferenceName: Bool]!
|
||||||
|
|
||||||
/** The window controller of the currently active preferences window. */
|
/** The window controller of the currently active preferences window. */
|
||||||
var preferencesWindowController: PrefsWC? = nil
|
var preferencesWindowController: PrefsWC?
|
||||||
|
|
||||||
/** The window controller of the currently active site list window. */
|
/** The window controller of the currently active site list window. */
|
||||||
var siteListWindowController: SiteListWC? = nil
|
var domainListWindowController: DomainListWC?
|
||||||
|
|
||||||
/** List of detected (installed) applications that PHP Monitor can work with. */
|
/** List of detected (installed) applications that PHP Monitor can work with. */
|
||||||
var detectedApplications: [Application] = []
|
var detectedApplications: [Application] = []
|
||||||
|
|
||||||
/** Timer that will periodically reload info about the user's PHP installation. */
|
/** Timer that will periodically reload info about the user's PHP installation. */
|
||||||
var timer: Timer?
|
var timer: Timer?
|
||||||
|
|
||||||
// MARK: - Global Hotkey
|
// MARK: - Global Hotkey
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The shortcut the user has requested.
|
The shortcut the user has requested.
|
||||||
*/
|
*/
|
||||||
var shortcutHotkey: HotKey? = nil {
|
var shortcutHotkey: HotKey? {
|
||||||
didSet {
|
didSet {
|
||||||
setupGlobalHotkeyListener()
|
setupGlobalHotkeyListener()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Activation Policy
|
// MARK: - Activation Policy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Variable that keeps track of which windows are currently open.
|
Variable that keeps track of which windows are currently open.
|
||||||
(Please note that window controllers remain open in memory once opened.)
|
(Please note that window controllers remain open in memory once opened.)
|
||||||
@@ -74,9 +84,9 @@ class App {
|
|||||||
(as a normal app or as a toolbar app).
|
(as a normal app or as a toolbar app).
|
||||||
*/
|
*/
|
||||||
var openWindows: [String] = []
|
var openWindows: [String] = []
|
||||||
|
|
||||||
// MARK: - App Watchers
|
// MARK: - App Watchers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 20/12/2021.
|
// Created by Nico Verbruggen on 20/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This is an entry point for future development for integrating with the PHP Monitor
|
This is an entry point for future development for integrating with the PHP Monitor
|
||||||
application URL. You can use the `phpmon://` protocol to communicate with the app.
|
application URL. You can use the `phpmon://` protocol to communicate with the app.
|
||||||
@@ -21,20 +21,20 @@ extension AppDelegate {
|
|||||||
Please note that PHP Monitor needs to be running in the background for this to work.
|
Please note that PHP Monitor needs to be running in the background for this to work.
|
||||||
*/
|
*/
|
||||||
func application(_ application: NSApplication, open urls: [URL]) {
|
func application(_ application: NSApplication, open urls: [URL]) {
|
||||||
|
|
||||||
if !Preferences.isEnabled(.allowProtocolForIntegrations) {
|
if !Preferences.isEnabled(.allowProtocolForIntegrations) {
|
||||||
Log.info("Acting on commands via phpmon:// has been disabled.")
|
Log.info("Acting on commands via phpmon:// has been disabled.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let url = urls.first else { return }
|
guard let url = urls.first else { return }
|
||||||
|
|
||||||
self.interpretCommand(
|
self.interpretCommand(
|
||||||
url.absoluteString.replacingOccurrences(of: "phpmon://", with: ""),
|
url.absoluteString.replacingOccurrences(of: "phpmon://", with: ""),
|
||||||
commands: InterApp.getCommands()
|
commands: InterApp.getCommands()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func interpretCommand(_ command: String, commands: [InterApp.Action]) {
|
private func interpretCommand(_ command: String, commands: [InterApp.Action]) {
|
||||||
commands.forEach { action in
|
commands.forEach { action in
|
||||||
if command.starts(with: action.command) {
|
if command.starts(with: action.command) {
|
||||||
@@ -44,4 +44,3 @@ extension AppDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 05/12/2021.
|
// Created by Nico Verbruggen on 05/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -22,34 +22,34 @@ import AppKit
|
|||||||
For more information about this, please see the ActivationPolicy-related extension.
|
For more information about this, please see the ActivationPolicy-related extension.
|
||||||
*/
|
*/
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
|
|
||||||
// MARK: - Menu Interactions
|
// MARK: - Menu Interactions
|
||||||
|
|
||||||
@IBAction func addSiteLinkPressed(_ sender: Any) {
|
@IBAction func addSiteLinkPressed(_ sender: Any) {
|
||||||
SiteListVC.show()
|
DomainListVC.show()
|
||||||
|
|
||||||
guard let windowController = App.shared.siteListWindowController else { return }
|
guard let windowController = App.shared.domainListWindowController else { return }
|
||||||
windowController.pressedAddLink(nil)
|
windowController.pressedAddLink(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func reloadSiteListPressed(_ sender: Any) {
|
@IBAction func reloadDomainListPressed(_ sender: Any) {
|
||||||
let vc = App.shared.siteListWindowController?
|
let vc = App.shared.domainListWindowController?
|
||||||
.window?.contentViewController as? SiteListVC
|
.window?.contentViewController as? DomainListVC
|
||||||
|
|
||||||
if vc != nil {
|
if vc != nil {
|
||||||
// If the view exists, directly reload the list of sites
|
// If the view exists, directly reload the list of sites
|
||||||
vc!.reloadSites()
|
vc!.reloadDomains()
|
||||||
} else {
|
} else {
|
||||||
// If the view does not exist, reload the cached data that was populated when the app initially launched.
|
// If the view does not exist, reload the cached data that was populated when the app initially launched.
|
||||||
Valet.shared.reloadSites()
|
Valet.shared.reloadSites()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func focusSearchField(_ sender: Any) {
|
@IBAction func focusSearchField(_ sender: Any) {
|
||||||
SiteListVC.show()
|
DomainListVC.show()
|
||||||
|
|
||||||
guard let windowController = App.shared.siteListWindowController else { return }
|
guard let windowController = App.shared.domainListWindowController else { return }
|
||||||
windowController.searchToolbarItem.searchField.becomeFirstResponder()
|
windowController.searchToolbarItem.searchField.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 06/12/2021.
|
// Created by Nico Verbruggen on 06/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
|
|
||||||
// MARK: - Notifications
|
// MARK: - Notifications
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Sets up notifications. That does mean we need to ask for permission first.
|
Sets up notifications. That does mean we need to ask for permission first.
|
||||||
If we cannot get permission, we should log this.
|
If we cannot get permission, we should log this.
|
||||||
@@ -30,7 +30,7 @@ extension AppDelegate {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Ensure that the application displays notifications even when the app is active.
|
Ensure that the application displays notifications even when the app is active.
|
||||||
*/
|
*/
|
||||||
@@ -42,5 +42,5 @@ extension AppDelegate {
|
|||||||
) {
|
) {
|
||||||
completionHandler([.banner])
|
completionHandler([.banner])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// AppDelegate.swift
|
// AppDelegate.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
@@ -10,55 +10,55 @@ import UserNotifications
|
|||||||
|
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate {
|
||||||
|
|
||||||
// MARK: - Variables
|
// MARK: - Variables
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The Shell singleton that keeps track of the history of all
|
The Shell singleton that keeps track of the history of all
|
||||||
(invoked by PHP Monitor) shell commands. It is used to
|
(invoked by PHP Monitor) shell commands. It is used to
|
||||||
invoke all commands in this application.
|
invoke all commands in this application.
|
||||||
*/
|
*/
|
||||||
let sharedShell: Shell
|
let sharedShell: Shell
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The App singleton contains information about the state of
|
The App singleton contains information about the state of
|
||||||
the application and global variables.
|
the application and global variables.
|
||||||
*/
|
*/
|
||||||
let state: App
|
let state: App
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The MainMenu singleton is responsible for rendering the
|
The MainMenu singleton is responsible for rendering the
|
||||||
menu bar item and its menu, as well as its actions.
|
menu bar item and its menu, as well as its actions.
|
||||||
*/
|
*/
|
||||||
let menu: MainMenu
|
let menu: MainMenu
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The paths singleton that determines where Homebrew is installed,
|
The paths singleton that determines where Homebrew is installed,
|
||||||
and where to look for binaries.
|
and where to look for binaries.
|
||||||
*/
|
*/
|
||||||
let paths: Paths
|
let paths: Paths
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The Valet singleton that determines all information
|
The Valet singleton that determines all information
|
||||||
about Valet and its current configuration.
|
about Valet and its current configuration.
|
||||||
*/
|
*/
|
||||||
let valet: Valet
|
let valet: Valet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The PhpEnv 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 phpEnvironment: PhpEnv! = nil
|
var phpEnvironment: PhpEnv! = nil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The logger is responsible for different levels of logging.
|
The logger is responsible for different levels of logging.
|
||||||
You can tweak the verbosity in the `init` method here.
|
You can tweak the verbosity in the `init` method here.
|
||||||
*/
|
*/
|
||||||
var logger = Log.shared
|
var logger = Log.shared
|
||||||
|
|
||||||
// MARK: - Initializer
|
// MARK: - Initializer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When the application initializes, create all singletons.
|
When the application initializes, create all singletons.
|
||||||
*/
|
*/
|
||||||
@@ -67,6 +67,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
logger.verbosity = .performance
|
logger.verbosity = .performance
|
||||||
#endif
|
#endif
|
||||||
|
if CommandLine.arguments.contains("--v") {
|
||||||
|
logger.verbosity = .performance
|
||||||
|
Log.info("Extra verbose mode has been activated.")
|
||||||
|
}
|
||||||
Log.separator(as: .info)
|
Log.separator(as: .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)")
|
||||||
@@ -78,13 +82,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
self.valet = Valet.shared
|
self.valet = Valet.shared
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func initializeSwitcher() {
|
func initializeSwitcher() {
|
||||||
self.phpEnvironment = PhpEnv.shared
|
self.phpEnvironment = PhpEnv.shared
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
/**
|
/**
|
||||||
When the application has finished launching, we'll want to set up
|
When the application has finished launching, we'll want to set up
|
||||||
the user notification center permissions, and kickoff the menu
|
the user notification center permissions, and kickoff the menu
|
||||||
@@ -96,5 +100,5 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
// Make sure the menu performs its initial checks
|
// Make sure the menu performs its initial checks
|
||||||
Task { await menu.startup() }
|
Task { await menu.startup() }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
181
phpmon/Domain/App/AppUpdateChecker.swift
Normal file
181
phpmon/Domain/App/AppUpdateChecker.swift
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
//
|
||||||
|
// Updater.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 09/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class AppUpdateChecker {
|
||||||
|
|
||||||
|
public static var enabled: Bool = {
|
||||||
|
return Preferences.isEnabled(.automaticBackgroundUpdateCheck)
|
||||||
|
}()
|
||||||
|
|
||||||
|
public static var isDev: Bool = {
|
||||||
|
return App.version.contains("-dev")
|
||||||
|
}()
|
||||||
|
|
||||||
|
public static func retrieveVersionFromCask(
|
||||||
|
_ initiatedFromBackground: Bool = true
|
||||||
|
) -> String {
|
||||||
|
let caskFile = App.version.contains("-dev")
|
||||||
|
? Constants.Urls.DevBuildCaskFile.absoluteString
|
||||||
|
: Constants.Urls.StableBuildCaskFile.absoluteString
|
||||||
|
|
||||||
|
var command = "curl -s"
|
||||||
|
|
||||||
|
if initiatedFromBackground {
|
||||||
|
command = "curl -s --max-time 5"
|
||||||
|
}
|
||||||
|
|
||||||
|
return Shell.pipe(
|
||||||
|
"\(command) '\(caskFile)' | grep version"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func checkIfNewerVersionIsAvailable(
|
||||||
|
initiatedFromBackground: Bool = true
|
||||||
|
) {
|
||||||
|
if initiatedFromBackground {
|
||||||
|
if !Preferences.isEnabled(.automaticBackgroundUpdateCheck) {
|
||||||
|
Log.info("Automatic updates are disabled. No check will be performed.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("Automatic updates are enabled, a check will be performed.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let versionString = retrieveVersionFromCask(initiatedFromBackground)
|
||||||
|
|
||||||
|
guard let onlineVersion = AppVersion.from(versionString) else {
|
||||||
|
Log.err("We couldn't check for updates!")
|
||||||
|
|
||||||
|
// Only notify about connection issues if the request to check for updates was explicit
|
||||||
|
if !initiatedFromBackground {
|
||||||
|
notifyAboutConnectionIssue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentVersion = AppVersion.fromCurrentVersion()
|
||||||
|
|
||||||
|
handleVersionComparison(
|
||||||
|
currentVersion,
|
||||||
|
onlineVersion,
|
||||||
|
initiatedFromBackground
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func handleVersionComparison(
|
||||||
|
_ currentVersion: AppVersion,
|
||||||
|
_ onlineVersion: AppVersion,
|
||||||
|
_ background: Bool
|
||||||
|
) {
|
||||||
|
switch onlineVersion.version.versionCompare(currentVersion.version) {
|
||||||
|
case .orderedAscending:
|
||||||
|
Log.info("You are running a newer version of PHP Monitor "
|
||||||
|
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
|
||||||
|
if !background { notifyVersionDoesNotNeedUpgrade() }
|
||||||
|
case .orderedDescending:
|
||||||
|
Log.info("There is a newer version (\(onlineVersion)) available! "
|
||||||
|
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
|
||||||
|
notifyAboutNewerVersion(version: onlineVersion)
|
||||||
|
case .orderedSame:
|
||||||
|
if currentVersion.build != nil
|
||||||
|
&& onlineVersion.build != nil
|
||||||
|
&& buildDiffers(currentVersion, onlineVersion, background) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info("The installed version (\(currentVersion.computerReadable)) matches the latest release "
|
||||||
|
+ "(\(onlineVersion.computerReadable)).")
|
||||||
|
if !background { notifyVersionDoesNotNeedUpgrade() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func buildDiffers(
|
||||||
|
_ currentVersion: AppVersion,
|
||||||
|
_ onlineVersion: AppVersion,
|
||||||
|
_ background: Bool
|
||||||
|
) -> Bool {
|
||||||
|
if Int(onlineVersion.build!)! > Int(currentVersion.build!)! {
|
||||||
|
Log.info("There is a newer build of PHP Monitor available! "
|
||||||
|
+ "(\(onlineVersion.computerReadable) > \(currentVersion.computerReadable))")
|
||||||
|
notifyAboutNewerVersion(version: onlineVersion)
|
||||||
|
return true
|
||||||
|
} else if Int(onlineVersion.build!)! < Int(currentVersion.build!)! {
|
||||||
|
Log.info("You are running a newer build of PHP Monitor "
|
||||||
|
+ "(\(currentVersion.computerReadable) > \(onlineVersion.computerReadable)).")
|
||||||
|
if !background { notifyVersionDoesNotNeedUpgrade() }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func notifyVersionDoesNotNeedUpgrade() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "updater.alerts.is_latest_version.title".localized,
|
||||||
|
subtitle: "updater.alerts.is_latest_version.subtitle".localized(App.shortVersion),
|
||||||
|
description: ""
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func notifyAboutNewerVersion(version: AppVersion) {
|
||||||
|
let devSuffix = isDev ? "-dev" : ""
|
||||||
|
let command = isDev ? "brew upgrade phpmon-dev" : "brew upgrade phpmon"
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "updater.alerts.newer_version_available.title".localized(version.humanReadable),
|
||||||
|
subtitle: "updater.alerts.newer_version_available.subtitle".localized,
|
||||||
|
description: HomebrewDiagnostics.customCaskInstalled
|
||||||
|
? "updater.installation_source.brew".localized(command)
|
||||||
|
: "updater.installation_source.direct".localized
|
||||||
|
)
|
||||||
|
.withPrimary(
|
||||||
|
text: "updater.alerts.buttons.release_notes".localized,
|
||||||
|
action: { vc in
|
||||||
|
vc.close(with: .OK)
|
||||||
|
NSWorkspace.shared.open(
|
||||||
|
Constants.Urls.GitHubReleases.appendingPathComponent("/tag/v\(version.version)\(devSuffix)")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withTertiary(text: "Dismiss", action: { vc in
|
||||||
|
vc.close(with: .OK)
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func notifyAboutConnectionIssue() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "updater.errors.cannot_check_for_update.title".localized,
|
||||||
|
subtitle: "updater.errors.cannot_check_for_update.subtitle".localized,
|
||||||
|
description: "updater.errors.cannot_check_for_update.description".localized(
|
||||||
|
App.version
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.withTertiary(
|
||||||
|
text: "updater.errors.buttons.releases_on_github".localized,
|
||||||
|
action: { _ in
|
||||||
|
NSWorkspace.shared.open(Constants.Urls.GitHubReleases)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
77
phpmon/Domain/App/AppVersion.swift
Normal file
77
phpmon/Domain/App/AppVersion.swift
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// AppVersion.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 10/05/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class AppVersion {
|
||||||
|
var version: String
|
||||||
|
var build: String?
|
||||||
|
var suffix: String?
|
||||||
|
|
||||||
|
init(version: String, build: String?, suffix: String? = nil) {
|
||||||
|
self.version = version
|
||||||
|
self.build = build
|
||||||
|
self.suffix = suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func from(_ string: String) -> AppVersion? {
|
||||||
|
do {
|
||||||
|
let regex = try NSRegularExpression(
|
||||||
|
pattern: #"(?<version>(\d+)[.](\d+)([.](\d+))?)(-(?<suffix>[a-z]+)){0,1}((,|_)(?<build>\d+)){0,1}"#,
|
||||||
|
options: []
|
||||||
|
)
|
||||||
|
|
||||||
|
let match = regex.matches(
|
||||||
|
in: string,
|
||||||
|
options: [],
|
||||||
|
range: NSRange(location: 0, length: string.count)
|
||||||
|
).first
|
||||||
|
|
||||||
|
guard let match = match else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var version: String = ""
|
||||||
|
var build: String?
|
||||||
|
var suffix: String?
|
||||||
|
|
||||||
|
if let versionRange = Range(match.range(withName: "version"), in: string) {
|
||||||
|
version = String(string[versionRange])
|
||||||
|
}
|
||||||
|
|
||||||
|
if let buildRange = Range(match.range(withName: "build"), in: string) {
|
||||||
|
build = String(string[buildRange])
|
||||||
|
}
|
||||||
|
|
||||||
|
if let suffixRange = Range(match.range(withName: "suffix"), in: string) {
|
||||||
|
suffix = String(string[suffixRange])
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppVersion(
|
||||||
|
version: version,
|
||||||
|
build: build,
|
||||||
|
suffix: suffix
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fromCurrentVersion() -> AppVersion {
|
||||||
|
return AppVersion.from("\(App.shortVersion)_\(App.bundleVersion)")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var computerReadable: String {
|
||||||
|
return "\(version)_\(build ?? "0")"
|
||||||
|
}
|
||||||
|
|
||||||
|
var humanReadable: String {
|
||||||
|
return "\(version) (\(build ?? "???"))"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -60,16 +60,16 @@
|
|||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="reload-list" keyEquivalent="r" id="Ema-AU-Nbr" customClass="LocalizedMenuItem" customModule="PHP_Monitor" customModuleProvider="target">
|
<menuItem title="reload-list" keyEquivalent="r" id="Ema-AU-Nbr" customClass="LocalizedMenuItem" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="mm_reload_site_list"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="mm_reload_domain_list"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="reloadSiteListPressed:" target="Voe-Tx-rLC" id="geC-Ld-haX"/>
|
<action selector="reloadDomainListPressed:" target="Voe-Tx-rLC" id="geC-Ld-haX"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="2ux-8Q-UjK"/>
|
<menuItem isSeparatorItem="YES" id="2ux-8Q-UjK"/>
|
||||||
<menuItem title="focus-find" keyEquivalent="f" id="I95-fb-EL7" customClass="LocalizedMenuItem" customModule="PHP_Monitor" customModuleProvider="target">
|
<menuItem title="focus-find" keyEquivalent="f" id="I95-fb-EL7" customClass="LocalizedMenuItem" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<userDefinedRuntimeAttributes>
|
<userDefinedRuntimeAttributes>
|
||||||
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="mm_find_in_site_list"/>
|
<userDefinedRuntimeAttribute type="string" keyPath="localizationKey" value="mm_find_in_domain_list"/>
|
||||||
</userDefinedRuntimeAttributes>
|
</userDefinedRuntimeAttributes>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="focusSearchField:" target="Voe-Tx-rLC" id="O8j-1B-hll"/>
|
<action selector="focusSearchField:" target="Voe-Tx-rLC" id="O8j-1B-hll"/>
|
||||||
@@ -319,7 +319,7 @@
|
|||||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="PHP_Monitor" customModuleProvider="target"/>
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="PHP_Monitor" customModuleProvider="target"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-495" y="-44"/>
|
<point key="canvasLocation" x="-360" y="-94"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="PQa-AT-b2a">
|
<scene sceneID="PQa-AT-b2a">
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="OF0-qs-3Oh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-374" y="327"/>
|
<point key="canvasLocation" x="-374" y="238"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Preferences-->
|
<!--Preferences-->
|
||||||
<scene sceneID="iyi-IS-7Ps">
|
<scene sceneID="iyi-IS-7Ps">
|
||||||
@@ -378,13 +378,13 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="251" y="205"/>
|
<point key="canvasLocation" x="260" y="217"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="4XS-kY-YIS">
|
<scene sceneID="4XS-kY-YIS">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController storyboardIdentifier="siteListWindow" id="8Ec-9q-82s" customClass="SiteListWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController storyboardIdentifier="domainListWindow" id="8Ec-9q-82s" customClass="DomainListWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" title="Domains" subtitle="Linked & Parked" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
<window key="window" separatorStyle="line" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="425" y="461" width="600" height="263"/>
|
<rect key="contentRect" x="425" y="461" width="600" height="263"/>
|
||||||
@@ -437,7 +437,7 @@
|
|||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="VCP-dF-cqM" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="VCP-dF-cqM" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-374" y="746"/>
|
<point key="canvasLocation" x="-374" y="745.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="HTI-x5-rOp">
|
<scene sceneID="HTI-x5-rOp">
|
||||||
@@ -462,7 +462,7 @@
|
|||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="d2k-57-mLZ" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="d2k-57-mLZ" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-409" y="1137"/>
|
<point key="canvasLocation" x="-374" y="1137"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="BD0-La-ygq">
|
<scene sceneID="BD0-La-ygq">
|
||||||
@@ -486,7 +486,7 @@
|
|||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="i3j-z8-nxv" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="i3j-z8-nxv" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="-575" y="1624"/>
|
<point key="canvasLocation" x="-374" y="2267"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Better AlertVC-->
|
<!--Better AlertVC-->
|
||||||
<scene sceneID="y9E-bB-wIG">
|
<scene sceneID="y9E-bB-wIG">
|
||||||
@@ -632,27 +632,27 @@ Gw
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="5Ts-EZ-bJh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="5Ts-EZ-bJh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="38" y="1624"/>
|
<point key="canvasLocation" x="230" y="2267"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Add SiteVC-->
|
<!--Add SiteVC-->
|
||||||
<scene sceneID="6JC-H6-u4K">
|
<scene sceneID="6JC-H6-u4K">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="newSiteLink" id="glS-wF-sEU" customClass="AddSiteVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController storyboardIdentifier="newSiteLink" id="glS-wF-sEU" customClass="AddSiteVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="JJJ-T9-Yuv">
|
<view key="view" id="JJJ-T9-Yuv">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="251"/>
|
<rect key="frame" x="0.0" y="0.0" width="480" height="245"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<box boxType="custom" borderWidth="0.0" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="js9-OW-xzC">
|
<box boxType="custom" borderWidth="0.0" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="js9-OW-xzC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="251"/>
|
<rect key="frame" x="0.0" y="0.0" width="480" height="245"/>
|
||||||
<view key="contentView" id="HRC-RT-LxR">
|
<view key="contentView" id="HRC-RT-LxR">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="480" height="251"/>
|
<rect key="frame" x="0.0" y="0.0" width="480" height="245"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
</view>
|
</view>
|
||||||
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</box>
|
</box>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
||||||
<rect key="frame" x="363" y="13" width="104" height="32"/>
|
<rect key="frame" x="325" y="13" width="142" height="32"/>
|
||||||
<buttonCell key="cell" type="push" title="Create Link" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WwW-Wv-I8s">
|
<buttonCell key="cell" type="push" title="[i18n] Create Link" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WwW-Wv-I8s">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
@@ -664,11 +664,11 @@ DQ
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SwS-o8-pbl">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SwS-o8-pbl">
|
||||||
<rect key="frame" x="13" y="13" width="94" height="32"/>
|
<rect key="frame" x="13" y="13" width="114" height="32"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="qCP-Sp-gxm"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="qCP-Sp-gxm"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WHE-HW-jwp">
|
<buttonCell key="cell" type="push" title="[i18n] Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="WHE-HW-jwp">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<string key="keyEquivalent" base64-UTF8="YES">
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
@@ -680,8 +680,8 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZX9-s1-23i">
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZX9-s1-23i">
|
||||||
<rect key="frame" x="20" y="156" width="440" height="21"/>
|
<rect key="frame" x="20" y="150" width="440" height="21"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Enter a potential domain name here." drawsBackground="YES" id="NFa-1D-Bi4">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Enter a domain name here." drawsBackground="YES" id="NFa-1D-Bi4">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -691,16 +691,16 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VzR-5a-cmT">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="VzR-5a-cmT">
|
||||||
<rect key="frame" x="18" y="134" width="444" height="14"/>
|
<rect key="frame" x="18" y="128" width="444" height="14"/>
|
||||||
<textFieldCell key="cell" title="FOLDER_AVAILABLE" id="bJr-s6-tdP">
|
<textFieldCell key="cell" title="[i18n] Preview text here" id="bJr-s6-tdP">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KZf-b0-9cm">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KZf-b0-9cm">
|
||||||
<rect key="frame" x="18" y="101" width="227" height="18"/>
|
<rect key="frame" x="18" y="95" width="266" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Secure this domain after creation" bezelStyle="regularSquare" imagePosition="left" inset="2" id="vFv-Of-2yZ">
|
<buttonCell key="cell" type="check" title="[i18n] Secure this domain after creation" bezelStyle="regularSquare" imagePosition="left" inset="2" id="vFv-Of-2yZ">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
@@ -709,31 +709,31 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mmQ-7e-dlb">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mmQ-7e-dlb">
|
||||||
<rect key="frame" x="18" y="66" width="444" height="28"/>
|
<rect key="frame" x="18" y="60" width="444" height="28"/>
|
||||||
<textFieldCell key="cell" title="Securing a site requires administrative privileges.
You will be prompted for your password or Touch ID." id="4gd-KM-5Fu">
|
<textFieldCell key="cell" title="[i18n] Securing a domain requires administrative privileges.
You may be prompted for your password or Touch ID." id="4gd-KM-5Fu">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<pathControl verticalHuggingPriority="750" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6JT-Vt-3q0">
|
<pathControl verticalHuggingPriority="750" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6JT-Vt-3q0">
|
||||||
<rect key="frame" x="20" y="185" width="440" height="22"/>
|
<rect key="frame" x="20" y="179" width="440" height="22"/>
|
||||||
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="m8d-XF-kh9">
|
<pathCell key="cell" selectable="YES" refusesFirstResponder="YES" alignment="left" id="m8d-XF-kh9">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<url key="url" string="file:///Users/"/>
|
<url key="url" string="file:///Users/"/>
|
||||||
</pathCell>
|
</pathCell>
|
||||||
</pathControl>
|
</pathControl>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P0B-Ht-R8n">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="P0B-Ht-R8n">
|
||||||
<rect key="frame" x="18" y="215" width="87" height="16"/>
|
<rect key="frame" x="18" y="209" width="128" height="16"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Link a Folder" id="S4j-ZC-ddT">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[i18n] Link a Folder" id="S4j-ZC-ddT">
|
||||||
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
|
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="900-Z2-tID">
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="900-Z2-tID">
|
||||||
<rect key="frame" x="229" y="23" width="128" height="14"/>
|
<rect key="frame" x="139" y="23" width="180" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="That link already exists." id="jOt-n6-TQf">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="That domain name already exists." id="jOt-n6-TQf">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -752,6 +752,7 @@ Gw
|
|||||||
<constraint firstItem="900-Z2-tID" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="SwS-o8-pbl" secondAttribute="trailing" constant="8" symbolic="YES" id="IMv-ZD-VXf"/>
|
<constraint firstItem="900-Z2-tID" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="SwS-o8-pbl" secondAttribute="trailing" constant="8" symbolic="YES" id="IMv-ZD-VXf"/>
|
||||||
<constraint firstItem="js9-OW-xzC" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" id="IpM-ot-dBG"/>
|
<constraint firstItem="js9-OW-xzC" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" id="IpM-ot-dBG"/>
|
||||||
<constraint firstItem="VzR-5a-cmT" firstAttribute="leading" secondItem="ZX9-s1-23i" secondAttribute="leading" id="UPN-Ad-j3X"/>
|
<constraint firstItem="VzR-5a-cmT" firstAttribute="leading" secondItem="ZX9-s1-23i" secondAttribute="leading" id="UPN-Ad-j3X"/>
|
||||||
|
<constraint firstItem="SwS-o8-pbl" firstAttribute="top" secondItem="mmQ-7e-dlb" secondAttribute="bottom" constant="20" id="VNW-fB-2Xj"/>
|
||||||
<constraint firstItem="KZf-b0-9cm" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="Vab-wq-9Nc"/>
|
<constraint firstItem="KZf-b0-9cm" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="Vab-wq-9Nc"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="PVw-cM-qAB" secondAttribute="bottom" constant="20" symbolic="YES" id="VsP-Q0-zRW"/>
|
<constraint firstAttribute="bottom" secondItem="PVw-cM-qAB" secondAttribute="bottom" constant="20" symbolic="YES" id="VsP-Q0-zRW"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="PVw-cM-qAB" secondAttribute="trailing" constant="20" symbolic="YES" id="X5z-G4-CBv"/>
|
<constraint firstAttribute="trailing" secondItem="PVw-cM-qAB" secondAttribute="trailing" constant="20" symbolic="YES" id="X5z-G4-CBv"/>
|
||||||
@@ -778,7 +779,7 @@ Gw
|
|||||||
<outlet property="buttonCancel" destination="SwS-o8-pbl" id="N1v-uy-2Mi"/>
|
<outlet property="buttonCancel" destination="SwS-o8-pbl" id="N1v-uy-2Mi"/>
|
||||||
<outlet property="buttonCreateLink" destination="PVw-cM-qAB" id="0Oo-xW-He7"/>
|
<outlet property="buttonCreateLink" destination="PVw-cM-qAB" id="0Oo-xW-He7"/>
|
||||||
<outlet property="buttonSecure" destination="KZf-b0-9cm" id="5A7-Bn-NB7"/>
|
<outlet property="buttonSecure" destination="KZf-b0-9cm" id="5A7-Bn-NB7"/>
|
||||||
<outlet property="linkName" destination="ZX9-s1-23i" id="yT6-80-Zr1"/>
|
<outlet property="inputDomainName" destination="ZX9-s1-23i" id="yT6-80-Zr1"/>
|
||||||
<outlet property="pathControl" destination="6JT-Vt-3q0" id="f5K-8h-VOd"/>
|
<outlet property="pathControl" destination="6JT-Vt-3q0" id="f5K-8h-VOd"/>
|
||||||
<outlet property="previewText" destination="VzR-5a-cmT" id="qwd-wX-645"/>
|
<outlet property="previewText" destination="VzR-5a-cmT" id="qwd-wX-645"/>
|
||||||
<outlet property="textFieldError" destination="900-Z2-tID" id="qUk-FE-IKW"/>
|
<outlet property="textFieldError" destination="900-Z2-tID" id="qUk-FE-IKW"/>
|
||||||
@@ -788,12 +789,12 @@ Gw
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="6XV-bG-0N1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="6XV-bG-0N1" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="191" y="1098.5"/>
|
<point key="canvasLocation" x="210" y="1128"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Site ListVC-->
|
<!--Domain ListVC-->
|
||||||
<scene sceneID="aZt-6w-TFl">
|
<scene sceneID="aZt-6w-TFl">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController identifier="siteList" storyboardIdentifier="siteList" id="JZI-Vd-9oq" customClass="SiteListVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController identifier="domainList" storyboardIdentifier="domainList" id="JZI-Vd-9oq" customClass="DomainListVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="rIZ-4U-bhj">
|
<view key="view" id="rIZ-4U-bhj">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
@@ -805,7 +806,7 @@ Gw
|
|||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj">
|
<tableView verticalHuggingPriority="750" ambiguous="YES" allowsExpansionToolTips="YES" multipleSelection="NO" autosaveName="phpmon-sitelist-columns" rowHeight="54" headerView="xUg-Mq-OSh" viewBased="YES" id="cp3-34-pQj">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="662" height="281"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="281"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<size key="intercellSpacing" width="17" height="0.0"/>
|
<size key="intercellSpacing" width="17" height="0.0"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -825,7 +826,7 @@ Gw
|
|||||||
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Secure"/>
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Secure"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteListTLSCell" id="hft-M4-nWb" customClass="SiteListTLSCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="domainListTLSCell" id="hft-M4-nWb" customClass="DomainListTLSCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="18" y="0.0" width="34" height="55"/>
|
<rect key="frame" x="18" y="0.0" width="34" height="55"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
@@ -848,7 +849,7 @@ Gw
|
|||||||
</tableCellView>
|
</tableCellView>
|
||||||
</prototypeCellViews>
|
</prototypeCellViews>
|
||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="DOMAIN" width="290" minWidth="250" maxWidth="10000" id="oeH-B2-0rA">
|
<tableColumn identifier="DOMAIN" width="200" minWidth="200" maxWidth="10000" id="oeH-B2-0rA">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Domain">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Domain">
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
@@ -861,8 +862,8 @@ Gw
|
|||||||
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Domain"/>
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Domain"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteListNameCell" wantsLayer="YES" id="5GY-nN-BWd" customClass="SiteListNameCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="domainListNameCell" wantsLayer="YES" id="5GY-nN-BWd" customClass="DomainListNameCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="69" y="0.0" width="290" height="54"/>
|
<rect key="frame" x="69" y="0.0" width="200" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="XJL-Uw-frD">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="XJL-Uw-frD">
|
||||||
@@ -910,8 +911,8 @@ Gw
|
|||||||
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="PHP"/>
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="PHP"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteListPhpCell" wantsLayer="YES" id="T49-0U-d58" customClass="SiteListPhpCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="domainListPhpCell" wantsLayer="YES" id="T49-0U-d58" customClass="DomainListPhpCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="376" y="0.0" width="100" height="54"/>
|
<rect key="frame" x="286" y="0.0" width="100" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZXQ-bg-Xba">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZXQ-bg-Xba">
|
||||||
@@ -965,8 +966,8 @@ Gw
|
|||||||
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Kind"/>
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Kind"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteListKindCell" wantsLayer="YES" id="AhT-xR-16a" customClass="SiteListKindCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="domainListKindCell" wantsLayer="YES" id="AhT-xR-16a" customClass="DomainListKindCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="493" y="0.0" width="36" height="54"/>
|
<rect key="frame" x="403" y="0.0" width="36" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sYR-vb-OW1">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sYR-vb-OW1">
|
||||||
@@ -1002,8 +1003,8 @@ Gw
|
|||||||
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Type"/>
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Type"/>
|
||||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
<prototypeCellViews>
|
<prototypeCellViews>
|
||||||
<tableCellView identifier="siteListTypeCell" wantsLayer="YES" id="ntU-Rl-ciP" customClass="SiteListTypeCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="domainListTypeCell" wantsLayer="YES" id="ntU-Rl-ciP" customClass="DomainListTypeCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="546" y="0.0" width="97" height="54"/>
|
<rect key="frame" x="456" y="0.0" width="97" height="54"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ljl-8B-key">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ljl-8B-key">
|
||||||
@@ -1060,7 +1061,7 @@ Gw
|
|||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<tableHeaderView key="headerView" wantsLayer="YES" id="xUg-Mq-OSh">
|
<tableHeaderView key="headerView" wantsLayer="YES" id="xUg-Mq-OSh">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="662" height="28"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="28"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</tableHeaderView>
|
</tableHeaderView>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
@@ -1088,12 +1089,376 @@ Gw
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="388" y="715.5"/>
|
<point key="canvasLocation" x="323" y="723"/>
|
||||||
|
</scene>
|
||||||
|
<!--Add ProxyVC-->
|
||||||
|
<scene sceneID="g8z-pE-RL9">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="newProxyLink" id="dwh-CF-6iv" customClass="AddProxyVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="U5U-QR-YXS">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="286"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<box boxType="custom" borderWidth="0.0" title="Box" translatesAutoresizingMaskIntoConstraints="NO" id="kkd-UV-SnA">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="286"/>
|
||||||
|
<view key="contentView" id="IXW-35-8NJ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="286"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QCK-Z9-w7g">
|
||||||
|
<rect key="frame" x="20" y="196" width="440" height="21"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="http://127.0.0.1:80" placeholderString="http://127.0.0.1:80" drawsBackground="YES" id="muS-8M-KSy">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="dwh-CF-6iv" id="lNE-OI-G93"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Uib-vA-HRc">
|
||||||
|
<rect key="frame" x="18" y="221" width="325" height="14"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[i18n] Proxy subject (usually: protocol, IP address and port)" id="G1Z-3f-BhL">
|
||||||
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
|
<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="mlA-Zt-Hu8">
|
||||||
|
<rect key="frame" x="18" y="172" width="112" height="14"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[i18n] Domain name" id="dQs-oZ-80e">
|
||||||
|
<font key="font" metaFont="systemMedium" size="11"/>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SNw-oQ-bnb">
|
||||||
|
<rect key="frame" x="20" y="147" width="440" height="21"/>
|
||||||
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Enter a domain name here." drawsBackground="YES" id="gTQ-Y2-Y9w">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="dwh-CF-6iv" id="e9n-PM-7s8"/>
|
||||||
|
</connections>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="SNw-oQ-bnb" secondAttribute="trailing" constant="20" id="2ui-Jg-BUV"/>
|
||||||
|
<constraint firstItem="mlA-Zt-Hu8" firstAttribute="top" secondItem="QCK-Z9-w7g" secondAttribute="bottom" constant="10" id="8sn-dT-SW6"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Uib-vA-HRc" secondAttribute="trailing" constant="20" symbolic="YES" id="Cue-3e-doM"/>
|
||||||
|
<constraint firstItem="QCK-Z9-w7g" firstAttribute="leading" secondItem="SNw-oQ-bnb" secondAttribute="leading" id="N1K-69-wLz"/>
|
||||||
|
<constraint firstItem="mlA-Zt-Hu8" firstAttribute="leading" secondItem="QCK-Z9-w7g" secondAttribute="leading" id="R74-k0-96U"/>
|
||||||
|
<constraint firstItem="SNw-oQ-bnb" firstAttribute="leading" secondItem="IXW-35-8NJ" secondAttribute="leading" constant="20" id="WZR-f8-mgf"/>
|
||||||
|
<constraint firstItem="SNw-oQ-bnb" firstAttribute="top" secondItem="mlA-Zt-Hu8" secondAttribute="bottom" constant="4" id="XDn-h9-dgp"/>
|
||||||
|
<constraint firstItem="QCK-Z9-w7g" firstAttribute="top" secondItem="Uib-vA-HRc" secondAttribute="bottom" constant="4" id="fGU-al-B0w"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="mlA-Zt-Hu8" secondAttribute="trailing" constant="20" symbolic="YES" id="uFE-cU-KOg"/>
|
||||||
|
<constraint firstItem="QCK-Z9-w7g" firstAttribute="trailing" secondItem="SNw-oQ-bnb" secondAttribute="trailing" id="xQE-yY-gPd"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Vi-cN-ude">
|
||||||
|
<rect key="frame" x="317" y="13" width="150" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="[i18n] Create Proxy" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="H2Z-c5-5Vk">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
DQ
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedCreateProxy:" target="dwh-CF-6iv" id="wFW-Aw-FOR"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nC0-dk-QaF">
|
||||||
|
<rect key="frame" x="13" y="13" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="uCc-fF-wS2"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="[i18n] Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="D8g-GE-7TU">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedCancel:" target="dwh-CF-6iv" id="J2T-Zj-A0j"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JSZ-x8-Pqi">
|
||||||
|
<rect key="frame" x="18" y="128" width="444" height="14"/>
|
||||||
|
<textFieldCell key="cell" title="[i18n] Preview text here" id="ISE-9R-ncQ">
|
||||||
|
<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>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rJa-yg-nCn">
|
||||||
|
<rect key="frame" x="18" y="95" width="170" height="18"/>
|
||||||
|
<buttonCell key="cell" type="check" title="[i18n] Secure this proxy" bezelStyle="regularSquare" imagePosition="left" inset="2" id="5LI-lt-Asl">
|
||||||
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedSecure:" target="dwh-CF-6iv" id="b74-8T-AzO"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5x7-ll-2f7">
|
||||||
|
<rect key="frame" x="18" y="60" width="444" height="28"/>
|
||||||
|
<textFieldCell key="cell" title="[i18n] Securing a domain requires administrative privileges.
You may be prompted for your password or Touch ID." id="IMB-O5-ZOy">
|
||||||
|
<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="DAh-br-Dfx">
|
||||||
|
<rect key="frame" x="18" y="250" width="123" height="16"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="[i18n] Add a Proxy" id="AZ1-04-kUl">
|
||||||
|
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField hidden="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="w0k-CK-0u4">
|
||||||
|
<rect key="frame" x="131" y="23" width="180" height="14"/>
|
||||||
|
<textFieldCell key="cell" lineBreakMode="clipping" title="That domain name already exists." id="4sH-94-UJl">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="nC0-dk-QaF" secondAttribute="bottom" constant="20" symbolic="YES" id="3Kk-fY-SB7"/>
|
||||||
|
<constraint firstItem="JSZ-x8-Pqi" firstAttribute="trailing" secondItem="SNw-oQ-bnb" secondAttribute="trailing" id="3So-Wu-1cz"/>
|
||||||
|
<constraint firstItem="DAh-br-Dfx" firstAttribute="top" secondItem="U5U-QR-YXS" secondAttribute="top" constant="20" symbolic="YES" id="3im-Qd-loW"/>
|
||||||
|
<constraint firstItem="kkd-UV-SnA" firstAttribute="leading" secondItem="U5U-QR-YXS" secondAttribute="leading" id="6iw-dd-hTX"/>
|
||||||
|
<constraint firstItem="Uib-vA-HRc" firstAttribute="leading" secondItem="DAh-br-Dfx" secondAttribute="leading" id="6jA-Kj-Q7l"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="kkd-UV-SnA" secondAttribute="trailing" id="8YX-CO-sY2"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="5x7-ll-2f7" secondAttribute="trailing" constant="20" symbolic="YES" id="8jr-cl-x78"/>
|
||||||
|
<constraint firstItem="kkd-UV-SnA" firstAttribute="top" secondItem="U5U-QR-YXS" secondAttribute="top" id="Afh-Ur-QgJ"/>
|
||||||
|
<constraint firstItem="4Vi-cN-ude" firstAttribute="leading" secondItem="w0k-CK-0u4" secondAttribute="trailing" constant="15" id="D3C-co-B10"/>
|
||||||
|
<constraint firstItem="w0k-CK-0u4" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="nC0-dk-QaF" secondAttribute="trailing" constant="8" symbolic="YES" id="FGk-wm-1Mu"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="rJa-yg-nCn" secondAttribute="trailing" constant="20" symbolic="YES" id="Fa7-Rc-1lj"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="4Vi-cN-ude" secondAttribute="trailing" constant="20" symbolic="YES" id="Fbg-C8-v6E"/>
|
||||||
|
<constraint firstItem="5x7-ll-2f7" firstAttribute="leading" secondItem="U5U-QR-YXS" secondAttribute="leading" constant="20" symbolic="YES" id="Fd0-zd-od8"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="4Vi-cN-ude" secondAttribute="bottom" constant="20" symbolic="YES" id="GyL-uL-sjW"/>
|
||||||
|
<constraint firstItem="w0k-CK-0u4" firstAttribute="centerY" secondItem="4Vi-cN-ude" secondAttribute="centerY" id="HcL-wb-0s6"/>
|
||||||
|
<constraint firstItem="rJa-yg-nCn" firstAttribute="leading" secondItem="U5U-QR-YXS" secondAttribute="leading" constant="20" symbolic="YES" id="IEg-SN-bHB"/>
|
||||||
|
<constraint firstItem="rJa-yg-nCn" firstAttribute="top" secondItem="JSZ-x8-Pqi" secondAttribute="bottom" constant="16" id="IW3-MX-3Kh"/>
|
||||||
|
<constraint firstItem="DAh-br-Dfx" firstAttribute="leading" secondItem="U5U-QR-YXS" secondAttribute="leading" constant="20" symbolic="YES" id="LY1-r0-viF"/>
|
||||||
|
<constraint firstItem="nC0-dk-QaF" firstAttribute="top" secondItem="5x7-ll-2f7" secondAttribute="bottom" constant="20" id="OjY-dM-dOG"/>
|
||||||
|
<constraint firstItem="nC0-dk-QaF" firstAttribute="leading" secondItem="U5U-QR-YXS" secondAttribute="leading" constant="20" symbolic="YES" id="V6L-YR-ufX"/>
|
||||||
|
<constraint firstItem="JSZ-x8-Pqi" firstAttribute="leading" secondItem="SNw-oQ-bnb" secondAttribute="leading" id="dpc-5M-0Cq"/>
|
||||||
|
<constraint firstItem="5x7-ll-2f7" firstAttribute="top" secondItem="rJa-yg-nCn" secondAttribute="bottom" constant="8" symbolic="YES" id="dzE-Ob-SVG"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="4Vi-cN-ude" secondAttribute="bottom" constant="20" symbolic="YES" id="ny2-RO-bEI"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="kkd-UV-SnA" secondAttribute="bottom" id="oCP-dn-6dx"/>
|
||||||
|
<constraint firstItem="JSZ-x8-Pqi" firstAttribute="top" secondItem="SNw-oQ-bnb" secondAttribute="bottom" constant="5" id="sX3-MK-14k"/>
|
||||||
|
<constraint firstItem="Uib-vA-HRc" firstAttribute="top" secondItem="DAh-br-Dfx" secondAttribute="bottom" constant="15" id="tWI-S8-17J"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="DAh-br-Dfx" secondAttribute="trailing" constant="20" symbolic="YES" id="vDR-5D-1eN"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="buttonCancel" destination="nC0-dk-QaF" id="n5Q-jg-UCe"/>
|
||||||
|
<outlet property="buttonCreateProxy" destination="4Vi-cN-ude" id="rdK-xc-N7F"/>
|
||||||
|
<outlet property="buttonSecure" destination="rJa-yg-nCn" id="WIs-zt-f3a"/>
|
||||||
|
<outlet property="inputDomainName" destination="SNw-oQ-bnb" id="ELH-63-cAe"/>
|
||||||
|
<outlet property="inputProxySubject" destination="QCK-Z9-w7g" id="76U-te-Jzt"/>
|
||||||
|
<outlet property="previewText" destination="JSZ-x8-Pqi" id="Mve-6W-Owd"/>
|
||||||
|
<outlet property="textFieldDomainName" destination="mlA-Zt-Hu8" id="cHL-Yu-Yvx"/>
|
||||||
|
<outlet property="textFieldError" destination="w0k-CK-0u4" id="28h-bn-igB"/>
|
||||||
|
<outlet property="textFieldProxySubject" destination="Uib-vA-HRc" id="5tV-3l-Wbw"/>
|
||||||
|
<outlet property="textFieldSecure" destination="5x7-ll-2f7" id="NlV-g8-rYP"/>
|
||||||
|
<outlet property="textFieldTitle" destination="DAh-br-Dfx" id="8SA-EW-wcq"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="VaP-ZM-OcY" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="210" y="1524"/>
|
||||||
|
</scene>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="5Gf-7O-tdA">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="addProxyWindow" id="ogq-ok-UVi" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="SMz-Va-x2z">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="480" height="270"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<view key="contentView" id="HsN-qQ-BhO">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="ogq-ok-UVi" id="9CA-sB-ZTD"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="dwh-CF-6iv" kind="relationship" relationship="window.shadowedContentViewController" id="My6-qb-eRg"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="5qP-qX-rbc" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-374" y="1530"/>
|
||||||
|
</scene>
|
||||||
|
<!--SelectionVC-->
|
||||||
|
<scene sceneID="UXm-Ci-yEB">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="addDomainChoice" id="gOD-Gu-zDG" customClass="SelectionVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="ysc-sm-sli">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="540" height="177"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<visualEffectView blendingMode="behindWindow" material="toolTip" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="F37-zt-gM3">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="540" height="177"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="FhN-AM-SkI">
|
||||||
|
<rect key="frame" x="13" y="13" width="114" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="Zhu-D8-cLK"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="[i18n] Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="LxP-t4-H2W">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent" base64-UTF8="YES">
|
||||||
|
Gw
|
||||||
|
</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedCancel:" target="gOD-Gu-zDG" id="wMp-sM-0A4"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<stackView distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pYe-Qu-qnK">
|
||||||
|
<rect key="frame" x="187" y="20" width="333" height="20"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="L5n-Gw-J27">
|
||||||
|
<rect key="frame" x="-7" y="-7" width="172" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="[i18n] Create a Link" bezelStyle="rounded" image="IconLinked" imagePosition="left" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="8UP-Sw-TP6">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent">l</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedCreateLink:" target="gOD-Gu-zDG" id="77M-Ip-GMi"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="01Z-IV-hv1">
|
||||||
|
<rect key="frame" x="159" y="-7" width="181" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="[i18n] Create a Proxy" bezelStyle="rounded" image="IconProxy" imagePosition="left" alignment="center" borderStyle="border" imageScaling="proportionallyUpOrDown" inset="2" id="bJ4-q8-1Ej">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<string key="keyEquivalent">p</string>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedCreateProxy:" target="gOD-Gu-zDG" id="UDf-lD-KCS"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<visibilityPriorities>
|
||||||
|
<integer value="1000"/>
|
||||||
|
<integer value="1000"/>
|
||||||
|
</visibilityPriorities>
|
||||||
|
<customSpacing>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
<real value="3.4028234663852886e+38"/>
|
||||||
|
</customSpacing>
|
||||||
|
</stackView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fJK-Ke-IK3">
|
||||||
|
<rect key="frame" x="18" y="138" width="504" height="19"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="left" title="[i18n] What kind of domain would you like to set up?" id="agk-Nj-FLd">
|
||||||
|
<font key="font" metaFont="systemBold" size="15"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<textField wantsLayer="YES" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="urj-Xq-TrJ">
|
||||||
|
<rect key="frame" x="18" y="60" width="504" height="70"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="tbl-AV-4qB"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" selectable="YES" alignment="left" id="3i9-RG-Ift">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<mutableString key="title">[i18n] Links are used to directly serve projects. If you have a Laravel, Symfony, WordPress, etc. folder with code, you'll want to create a link and choose the folder where your code lives.
If you are in need of a proxy, you can proxy e.g. a container to a particular domain name. This can be useful in combination with Docker, for example.</mutableString>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="FhN-AM-SkI" firstAttribute="leading" secondItem="F37-zt-gM3" secondAttribute="leading" constant="20" symbolic="YES" id="3dg-JM-MDr"/>
|
||||||
|
<constraint firstItem="fJK-Ke-IK3" firstAttribute="top" secondItem="F37-zt-gM3" secondAttribute="top" constant="20" symbolic="YES" id="FbX-Le-O7Q"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="pYe-Qu-qnK" secondAttribute="trailing" constant="20" symbolic="YES" id="IJA-vN-Rbv"/>
|
||||||
|
<constraint firstItem="urj-Xq-TrJ" firstAttribute="leading" secondItem="fJK-Ke-IK3" secondAttribute="leading" id="JcY-ae-6ZH"/>
|
||||||
|
<constraint firstItem="urj-Xq-TrJ" firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" id="ZBI-pN-kOz"/>
|
||||||
|
<constraint firstItem="fJK-Ke-IK3" firstAttribute="leading" secondItem="F37-zt-gM3" secondAttribute="leading" constant="20" symbolic="YES" id="d4o-6b-Dho"/>
|
||||||
|
<constraint firstItem="urj-Xq-TrJ" firstAttribute="top" secondItem="fJK-Ke-IK3" secondAttribute="bottom" constant="8" symbolic="YES" id="hOk-eL-Eg0"/>
|
||||||
|
<constraint firstItem="FhN-AM-SkI" firstAttribute="top" secondItem="urj-Xq-TrJ" secondAttribute="bottom" constant="20" id="kCc-Vp-Gvq"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="pYe-Qu-qnK" secondAttribute="bottom" constant="20" id="lPX-ZF-XZN"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="fJK-Ke-IK3" secondAttribute="trailing" constant="20" symbolic="YES" id="spl-Bn-xtw"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="FhN-AM-SkI" secondAttribute="bottom" constant="20" symbolic="YES" id="t5w-aL-tOa"/>
|
||||||
|
</constraints>
|
||||||
|
</visualEffectView>
|
||||||
|
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cNh-Wc-ADk">
|
||||||
|
<rect key="frame" x="200" y="109" width="0.0" height="48"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="OQ5-hX-qai">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="F37-zt-gM3" secondAttribute="trailing" id="ZRD-3j-s4x"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="F37-zt-gM3" secondAttribute="bottom" id="et1-At-Rgj"/>
|
||||||
|
<constraint firstItem="F37-zt-gM3" firstAttribute="top" secondItem="ysc-sm-sli" secondAttribute="top" id="jp3-eE-mOy"/>
|
||||||
|
<constraint firstItem="F37-zt-gM3" firstAttribute="leading" secondItem="ysc-sm-sli" secondAttribute="leading" id="wIo-zP-KId"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="buttonCancel" destination="FhN-AM-SkI" id="iqV-2E-q7e"/>
|
||||||
|
<outlet property="buttonCreateLink" destination="L5n-Gw-J27" id="SHV-4l-Red"/>
|
||||||
|
<outlet property="buttonCreateProxy" destination="01Z-IV-hv1" id="J1v-7J-4fx"/>
|
||||||
|
<outlet property="textFieldDescription" destination="urj-Xq-TrJ" id="u1w-O0-kI3"/>
|
||||||
|
<outlet property="textFieldTitle" destination="fJK-Ke-IK3" id="x8p-qx-HX4"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="bZa-dD-d4J" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="250" y="1900"/>
|
||||||
|
</scene>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="HW6-nV-trE">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="showSelectionWindow" id="t4x-Mh-iya" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="IeW-fo-4yK">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
|
<rect key="contentRect" x="425" y="462" width="480" height="270"/>
|
||||||
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
|
<view key="contentView" id="Oe0-yv-Jcy">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="t4x-Mh-iya" id="4oO-gI-bd2"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="gOD-Gu-zDG" kind="relationship" relationship="window.shadowedContentViewController" id="KRt-OH-8uc"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="hBK-Bw-dwa" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-374" y="1909"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="Checkmark" width="512" height="512"/>
|
<image name="Checkmark" width="512" height="512"/>
|
||||||
<image name="IconLinked" width="25" height="25"/>
|
<image name="IconLinked" width="25" height="25"/>
|
||||||
|
<image name="IconProxy" 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"/>
|
||||||
|
|||||||
@@ -9,21 +9,21 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class InterApp {
|
class InterApp {
|
||||||
|
|
||||||
public static var bindings: [Action] = []
|
public static var bindings: [Action] = []
|
||||||
|
|
||||||
public static func register(_ action: Action) {
|
public static func register(_ action: Action) {
|
||||||
self.bindings.append(action)
|
self.bindings.append(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Action {
|
public struct Action {
|
||||||
let command: String
|
let command: String
|
||||||
let action: (String) -> Void
|
let action: (String) -> Void
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getCommands() -> [InterApp.Action] { return [
|
static func getCommands() -> [InterApp.Action] { return [
|
||||||
InterApp.Action(command: "list", action: { _ in
|
InterApp.Action(command: "list", action: { _ in
|
||||||
SiteListVC.show()
|
DomainListVC.show()
|
||||||
}),
|
}),
|
||||||
InterApp.Action(command: "services/stop", action: { _ in
|
InterApp.Action(command: "services/stop", action: { _ in
|
||||||
MainMenu.shared.stopAllServices()
|
MainMenu.shared.stopAllServices()
|
||||||
@@ -61,7 +61,7 @@ class InterApp {
|
|||||||
subtitle: "PHP Monitor can't switch to PHP \(version), as it may not be installed or available."
|
subtitle: "PHP Monitor can't switch to PHP \(version), as it may not be installed or available."
|
||||||
).withPrimary(text: "OK").show()
|
).withPrimary(text: "OK").show()
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
]}
|
]}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
// Environment.swift
|
// Environment.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class Startup {
|
class Startup {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks the user's environment and checks if PHP Monitor can be used properly.
|
Checks the user's environment and checks if PHP Monitor can be used properly.
|
||||||
This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more.
|
This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more.
|
||||||
@@ -17,30 +17,29 @@ class Startup {
|
|||||||
If this method returns false, there was a failed check and an alert was displayed.
|
If this method returns false, there was a failed check and an alert was displayed.
|
||||||
If this method returns true, then all checks succeeded and the app can continue.
|
If this method returns true, then all checks succeeded and the app can continue.
|
||||||
*/
|
*/
|
||||||
func checkEnvironment() async -> Bool
|
func checkEnvironment() async -> Bool {
|
||||||
{
|
|
||||||
// Do the important system setup checks
|
// Do the important system setup checks
|
||||||
Log.info("[ARCH] The user is running PHP Monitor with the architecture: \(App.architecture)")
|
Log.info("[ARCH] The user is running PHP Monitor with the architecture: \(App.architecture)")
|
||||||
|
|
||||||
for check in self.checks {
|
for check in self.checks {
|
||||||
if await check.succeeds() {
|
if await check.succeeds() {
|
||||||
Log.info("[OK] \(check.name)")
|
Log.info("[OK] \(check.name)")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, something's gone wrong and the check has failed...
|
// If we get here, something's gone wrong and the check has failed...
|
||||||
Log.info("[FAIL] \(check.name)")
|
Log.info("[FAIL] \(check.name)")
|
||||||
showAlert(for: check)
|
showAlert(for: check)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we get here, nothing has gone wrong. That's what we want!
|
// If we get here, nothing has gone wrong. That's what we want!
|
||||||
initializeSwitcher()
|
initializeSwitcher()
|
||||||
Log.separator(as: .info)
|
Log.separator(as: .info)
|
||||||
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Displays an alert for a particular check. There are two types of alerts:
|
Displays an alert for a particular check. There are two types of alerts:
|
||||||
- ones that require an app restart, which prompt the user to exit the app
|
- ones that require an app restart, which prompt the user to exit the app
|
||||||
@@ -59,7 +58,7 @@ class Startup {
|
|||||||
exit(1)
|
exit(1)
|
||||||
}).show()
|
}).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: check.titleText,
|
title: check.titleText,
|
||||||
@@ -70,7 +69,7 @@ class Startup {
|
|||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Because the Switcher requires various environment guarantees, the switcher is only
|
Because the Switcher requires various environment guarantees, the switcher is only
|
||||||
initialized when it is done working. The switcher must be initialized on the main thread.
|
initialized when it is done working. The switcher must be initialized on the main thread.
|
||||||
@@ -81,9 +80,9 @@ class Startup {
|
|||||||
appDelegate.initializeSwitcher()
|
appDelegate.initializeSwitcher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Check (List)
|
// MARK: - Check (List)
|
||||||
|
|
||||||
public var checks: [EnvironmentCheck] = [
|
public var checks: [EnvironmentCheck] = [
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// The Homebrew binary must exist.
|
// The Homebrew binary must exist.
|
||||||
@@ -196,9 +195,9 @@ class Startup {
|
|||||||
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
// MARK: - EnvironmentCheck struct
|
// MARK: - EnvironmentCheck struct
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
||||||
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
||||||
@@ -211,7 +210,7 @@ class Startup {
|
|||||||
let descriptionText: String
|
let descriptionText: String
|
||||||
let buttonText: String
|
let buttonText: String
|
||||||
let requiresAppRestart: Bool
|
let requiresAppRestart: Bool
|
||||||
|
|
||||||
init(
|
init(
|
||||||
command: @escaping () async -> Bool,
|
command: @escaping () async -> Bool,
|
||||||
name: String,
|
name: String,
|
||||||
@@ -229,7 +228,7 @@ class Startup {
|
|||||||
self.buttonText = buttonText
|
self.buttonText = buttonText
|
||||||
self.requiresAppRestart = requiresAppRestart
|
self.requiresAppRestart = requiresAppRestart
|
||||||
}
|
}
|
||||||
|
|
||||||
public func succeeds() async -> Bool {
|
public func succeeds() async -> Bool {
|
||||||
return await !self.command()
|
return await !self.command()
|
||||||
}
|
}
|
||||||
|
|||||||
169
phpmon/Domain/DomainList/AddProxyVC.swift
Normal file
169
phpmon/Domain/DomainList/AddProxyVC.swift
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
//
|
||||||
|
// AddSiteVC.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 24/01/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class AddProxyVC: NSViewController, NSTextFieldDelegate {
|
||||||
|
|
||||||
|
// MARK: - Outlets
|
||||||
|
|
||||||
|
@IBOutlet weak var textFieldTitle: NSTextField!
|
||||||
|
@IBOutlet weak var textFieldProxySubject: NSTextField!
|
||||||
|
@IBOutlet weak var textFieldDomainName: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet weak var inputProxySubject: NSTextField!
|
||||||
|
@IBOutlet weak var inputDomainName: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet weak var previewText: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet weak var buttonSecure: NSButton!
|
||||||
|
@IBOutlet weak var buttonCreateProxy: NSButton!
|
||||||
|
@IBOutlet weak var buttonCancel: NSButton!
|
||||||
|
|
||||||
|
@IBOutlet weak var textFieldSecure: NSTextField!
|
||||||
|
@IBOutlet weak var textFieldError: NSTextField!
|
||||||
|
|
||||||
|
// MARK: - View Lifecycle
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
loadStaticLocalisedStrings()
|
||||||
|
|
||||||
|
buttonCreateProxy.isEnabled = false
|
||||||
|
updatePreview()
|
||||||
|
validate()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissView(outcome: NSApplication.ModalResponse) {
|
||||||
|
guard let window = view.window, let parent = window.sheetParent else { return }
|
||||||
|
parent.endSheet(window, returnCode: outcome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Localisation
|
||||||
|
|
||||||
|
func loadStaticLocalisedStrings() {
|
||||||
|
textFieldTitle.stringValue = "domain_list.add.set_up_proxy".localized
|
||||||
|
textFieldProxySubject.stringValue = "domain_list.add.proxy_subject".localized
|
||||||
|
textFieldDomainName.stringValue = "domain_list.add.domain_name".localized
|
||||||
|
textFieldSecure.stringValue = "domain_list.add.secure_description".localized
|
||||||
|
buttonCancel.title = "domain_list.add.cancel".localized
|
||||||
|
buttonCreateProxy.title = "domain_list.add.create_proxy".localized
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Outlet Interactions
|
||||||
|
|
||||||
|
@IBAction func pressedSecure(_ sender: Any) {
|
||||||
|
updatePreview()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func pressedCreateProxy(_ sender: Any) {
|
||||||
|
let domain = self.inputDomainName.stringValue
|
||||||
|
let proxyName = self.inputProxySubject.stringValue
|
||||||
|
let secure = self.buttonSecure.state == .on ? " --secure" : ""
|
||||||
|
|
||||||
|
dismissView(outcome: .OK)
|
||||||
|
|
||||||
|
App.shared.domainListWindowController?.contentVC.setUIBusy()
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
|
Shell.run("\(Paths.valet) proxy \(domain) \(proxyName)\(secure)", requiresPath: true)
|
||||||
|
Actions.restartNginx()
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
App.shared.domainListWindowController?.contentVC.setUINotBusy()
|
||||||
|
App.shared.domainListWindowController?.pressedReload(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func pressedCancel(_ sender: Any) {
|
||||||
|
dismissView(outcome: .cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Text Field Delegate
|
||||||
|
|
||||||
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
|
updateTextField()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Helper Methods
|
||||||
|
|
||||||
|
private func validate() {
|
||||||
|
_ = validate(
|
||||||
|
domain: inputDomainName.stringValue,
|
||||||
|
proxy: inputProxySubject.stringValue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func validate(domain: String, proxy: String) -> Bool {
|
||||||
|
if proxy.isEmpty {
|
||||||
|
textFieldError.isHidden = false
|
||||||
|
textFieldError.stringValue = "domain_list.add.errors.empty_proxy".localized
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if proxy.range(of: #"(http:\/\/|https:\/\/)(.+)(:)(\d+)$"#, options: .regularExpression) == nil {
|
||||||
|
textFieldError.isHidden = false
|
||||||
|
textFieldError.stringValue = "domain_list.add.errors.subject_invalid".localized
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if domain.isEmpty {
|
||||||
|
textFieldError.isHidden = false
|
||||||
|
textFieldError.stringValue = "domain_list.add.errors.empty".localized
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if Valet.shared.sites.contains(where: { $0.name == domain }) {
|
||||||
|
textFieldError.isHidden = false
|
||||||
|
textFieldError.stringValue = "domain_list.add.errors.already_exists".localized
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
textFieldError.isHidden = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTextField() {
|
||||||
|
inputDomainName.stringValue = inputDomainName.stringValue
|
||||||
|
.replacingOccurrences(of: " ", with: "-")
|
||||||
|
|
||||||
|
inputProxySubject.stringValue = inputProxySubject.stringValue
|
||||||
|
.replacingOccurrences(of: " ", with: "-")
|
||||||
|
|
||||||
|
buttonCreateProxy.isEnabled = validate(
|
||||||
|
domain: inputDomainName.stringValue,
|
||||||
|
proxy: inputProxySubject.stringValue
|
||||||
|
)
|
||||||
|
|
||||||
|
updatePreview()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updatePreview() {
|
||||||
|
buttonSecure.title = "domain_list.add.secure_after_creation"
|
||||||
|
.localized(
|
||||||
|
inputDomainName.stringValue,
|
||||||
|
Valet.shared.config.tld
|
||||||
|
)
|
||||||
|
|
||||||
|
if inputProxySubject.stringValue.isEmpty || inputDomainName.stringValue.isEmpty {
|
||||||
|
previewText.stringValue = "domain_list.add.empty_fields".localized
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
previewText.stringValue = "domain_list.add.proxy_available"
|
||||||
|
.localized(
|
||||||
|
inputProxySubject.stringValue,
|
||||||
|
buttonSecure.state == .on ? "https" : "http",
|
||||||
|
inputDomainName.stringValue,
|
||||||
|
Valet.shared.config.tld
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -11,130 +11,141 @@ import Cocoa
|
|||||||
|
|
||||||
class AddSiteVC: NSViewController, NSTextFieldDelegate {
|
class AddSiteVC: NSViewController, NSTextFieldDelegate {
|
||||||
|
|
||||||
|
// MARK: - Outlets
|
||||||
|
|
||||||
|
@IBOutlet weak var textFieldTitle: NSTextField!
|
||||||
|
|
||||||
@IBOutlet weak var pathControl: NSPathControl!
|
@IBOutlet weak var pathControl: NSPathControl!
|
||||||
@IBOutlet weak var linkName: NSTextField!
|
@IBOutlet weak var inputDomainName: NSTextField!
|
||||||
|
|
||||||
@IBOutlet weak var previewText: NSTextField!
|
@IBOutlet weak var previewText: NSTextField!
|
||||||
|
|
||||||
@IBOutlet weak var buttonSecure: NSButton!
|
@IBOutlet weak var buttonSecure: NSButton!
|
||||||
@IBOutlet weak var buttonCreateLink: NSButton!
|
@IBOutlet weak var buttonCreateLink: NSButton!
|
||||||
@IBOutlet weak var buttonCancel: NSButton!
|
@IBOutlet weak var buttonCancel: NSButton!
|
||||||
|
|
||||||
@IBOutlet weak var textFieldTitle: NSTextField!
|
|
||||||
@IBOutlet weak var textFieldSecure: NSTextField!
|
@IBOutlet weak var textFieldSecure: NSTextField!
|
||||||
@IBOutlet weak var textFieldError: NSTextField!
|
@IBOutlet weak var textFieldError: NSTextField!
|
||||||
|
|
||||||
// MARK: - View Lifecycle
|
// MARK: - View Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
loadStaticLocalisedStrings()
|
loadStaticLocalisedStrings()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func dismissView(outcome: NSApplication.ModalResponse) {
|
private func dismissView(outcome: NSApplication.ModalResponse) {
|
||||||
guard let window = self.view.window, let parent = window.sheetParent else { return }
|
guard let window = self.view.window, let parent = window.sheetParent else { return }
|
||||||
parent.endSheet(window, returnCode: outcome)
|
parent.endSheet(window, returnCode: outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Localisation
|
// MARK: - Localisation
|
||||||
|
|
||||||
func loadStaticLocalisedStrings() {
|
func loadStaticLocalisedStrings() {
|
||||||
textFieldTitle.stringValue = "site_list.add.link_folder".localized
|
textFieldTitle.stringValue = "domain_list.add.link_folder".localized
|
||||||
linkName.placeholderString = "site_list.add.domain_name_placeholder".localized
|
inputDomainName.placeholderString = "domain_list.add.domain_name_placeholder".localized
|
||||||
textFieldSecure.stringValue = "site_list.add.secure_description".localized
|
textFieldSecure.stringValue = "domain_list.add.secure_description".localized
|
||||||
buttonCancel.stringValue = "site_list.add.cancel".localized
|
buttonCancel.title = "domain_list.add.cancel".localized
|
||||||
|
buttonCreateLink.title = "domain_list.add.create_link".localized
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Outlet Interactions
|
// MARK: - Outlet Interactions
|
||||||
|
|
||||||
@IBAction func pressedCreateLink(_ sender: Any) {
|
@IBAction func pressedCreateLink(_ sender: Any) {
|
||||||
let path = self.pathControl.url!.path
|
let path = pathControl.url!.path
|
||||||
let name = self.linkName.stringValue
|
let name = inputDomainName.stringValue
|
||||||
|
|
||||||
if !FileManager.default.fileExists(atPath: path) {
|
if !FileManager.default.fileExists(atPath: path) {
|
||||||
Alert.confirm(
|
Alert.confirm(
|
||||||
onWindow: self.view.window!,
|
onWindow: view.window!,
|
||||||
messageText: "site_list.alert.folder_missing.title".localized,
|
messageText: "domain_list.alert.folder_missing.title".localized,
|
||||||
informativeText: "site_list.alert.folder_missing.desc".localized,
|
informativeText: "domain_list.alert.folder_missing.desc".localized,
|
||||||
buttonTitle: "site_list.alert.folder_missing.cancel".localized,
|
buttonTitle: "domain_list.alert.folder_missing.cancel".localized,
|
||||||
secondButtonTitle: "site_list.alert.folder_missing.return".localized,
|
secondButtonTitle: "domain_list.alert.folder_missing.return".localized,
|
||||||
onFirstButtonPressed: {
|
onFirstButtonPressed: { [self] in
|
||||||
self.dismissView(outcome: .cancel)
|
dismissView(outcome: .cancel)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adding `valet links` is a workaround for Valet malforming the config.json file
|
// Adding `valet links` is a workaround for Valet malforming the config.json file
|
||||||
// TODO: I will have to investigate and report this behaviour if possible
|
// TODO: I will have to investigate and report this behaviour if possible
|
||||||
Shell.run("cd '\(path)' && \(Paths.valet) link '\(name)' && valet links", requiresPath: true)
|
Shell.run("cd '\(path)' && \(Paths.valet) link '\(name)' && valet links", requiresPath: true)
|
||||||
|
|
||||||
self.dismissView(outcome: .OK)
|
dismissView(outcome: .OK)
|
||||||
|
|
||||||
// Reset search
|
// Reset search
|
||||||
App.shared.siteListWindowController?
|
App.shared.domainListWindowController?
|
||||||
.searchToolbarItem
|
.searchToolbarItem
|
||||||
.searchField.stringValue = ""
|
.searchField.stringValue = ""
|
||||||
|
|
||||||
// Add the new item and scrolls to it
|
// Add the new item and scrolls to it
|
||||||
App.shared.siteListWindowController?
|
App.shared.domainListWindowController?
|
||||||
.contentVC
|
.contentVC
|
||||||
.addedNewSite(
|
.addedNewSite(
|
||||||
name: name,
|
name: name,
|
||||||
secure: buttonSecure.state == .on
|
secure: buttonSecure.state == .on
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func pressedCancel(_ sender: Any) {
|
@IBAction func pressedCancel(_ sender: Any) {
|
||||||
self.dismissView(outcome: .cancel)
|
dismissView(outcome: .cancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func pressedSecure(_ sender: Any) {
|
@IBAction func pressedSecure(_ sender: Any) {
|
||||||
updatePreview()
|
updatePreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Text Field Delegate
|
// MARK: - Text Field Delegate
|
||||||
|
|
||||||
func controlTextDidChange(_ obj: Notification) {
|
func controlTextDidChange(_ obj: Notification) {
|
||||||
updateTextField()
|
updateTextField()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helper Methods
|
// MARK: - Helper Methods
|
||||||
|
|
||||||
private func isValidLinkName(_ name: String) -> Bool {
|
private func isValidLinkName(_ name: String) -> Bool {
|
||||||
if self.linkName.stringValue.isEmpty {
|
if name.isEmpty {
|
||||||
self.textFieldError.isHidden = false
|
textFieldError.isHidden = false
|
||||||
self.textFieldError.stringValue = "site_list.add.errors.empty".localized
|
textFieldError.stringValue = "domain_list.add.errors.empty".localized
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if Valet.shared.sites.contains(where: { $0.name == name }) {
|
if Valet.shared.sites.contains(where: { $0.name == name }) {
|
||||||
self.textFieldError.isHidden = false
|
textFieldError.isHidden = false
|
||||||
self.textFieldError.stringValue = "site_list.add.errors.already_exists".localized
|
textFieldError.stringValue = "domain_list.add.errors.already_exists".localized
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
self.textFieldError.isHidden = true
|
textFieldError.isHidden = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTextField() {
|
func updateTextField() {
|
||||||
self.linkName.stringValue = self.linkName.stringValue
|
inputDomainName.stringValue = inputDomainName.stringValue
|
||||||
.replacingOccurrences(of: " ", with: "-")
|
.replacingOccurrences(of: " ", with: "-")
|
||||||
|
|
||||||
buttonCreateLink.isEnabled = isValidLinkName(self.linkName.stringValue)
|
buttonCreateLink.isEnabled = isValidLinkName(inputDomainName.stringValue)
|
||||||
self.updatePreview()
|
updatePreview()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePreview() {
|
func updatePreview() {
|
||||||
buttonSecure.title = "site_list.add.secure_after_creation"
|
buttonSecure.title = "domain_list.add.secure_after_creation"
|
||||||
.localized(
|
.localized(
|
||||||
self.linkName.stringValue,
|
inputDomainName.stringValue,
|
||||||
Valet.shared.config.tld
|
Valet.shared.config.tld
|
||||||
)
|
)
|
||||||
|
|
||||||
previewText.stringValue = "site_list.add.folder_available"
|
if inputDomainName.stringValue.isEmpty {
|
||||||
|
previewText.stringValue = "domain_list.add.empty_fields".localized
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
previewText.stringValue = "domain_list.add.folder_available"
|
||||||
.localized(
|
.localized(
|
||||||
self.buttonSecure.state == .on ? "https" : "http",
|
buttonSecure.state == .on ? "https" : "http",
|
||||||
self.linkName.stringValue,
|
inputDomainName.stringValue,
|
||||||
Valet.shared.config.tld
|
Valet.shared.config.tld
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
15
phpmon/Domain/DomainList/Cells/DomainListCellProtocol.swift
Normal file
15
phpmon/Domain/DomainList/Cells/DomainListCellProtocol.swift
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// DomainListCellProtocol.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 03/12/2021.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
protocol DomainListCellProtocol {
|
||||||
|
func populateCell(with site: ValetSite)
|
||||||
|
func populateCell(with proxy: ValetProxy)
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// SiteListTypeCell.swift
|
// DomainListTypeCell.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 16/03/2022.
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
@@ -9,12 +9,11 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class SiteListKindCell: NSTableCellView, SiteListCellProtocol
|
class DomainListKindCell: NSTableCellView, DomainListCellProtocol {
|
||||||
{
|
static let reusableName = "domainListKindCell"
|
||||||
static let reusableName = "siteListKindCell"
|
|
||||||
|
|
||||||
@IBOutlet weak var imageViewType: NSImageView!
|
@IBOutlet weak var imageViewType: NSImageView!
|
||||||
|
|
||||||
func populateCell(with site: ValetSite) {
|
func populateCell(with site: ValetSite) {
|
||||||
// If the `aliasPath` is nil, we're dealing with a parked site (otherwise: linked).
|
// If the `aliasPath` is nil, we're dealing with a parked site (otherwise: linked).
|
||||||
imageViewType.image = NSImage(
|
imageViewType.image = NSImage(
|
||||||
@@ -22,12 +21,16 @@ class SiteListKindCell: NSTableCellView, SiteListCellProtocol
|
|||||||
? "IconParked"
|
? "IconParked"
|
||||||
: "IconLinked"
|
: "IconLinked"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unless, of course, this is a default site
|
// Unless, of course, this is a default site
|
||||||
if site.absolutePath == Valet.shared.config.defaultSite {
|
if site.absolutePath == Valet.shared.config.defaultSite {
|
||||||
imageViewType.image = NSImage(named: "IconDefault")
|
imageViewType.image = NSImage(named: "IconDefault")
|
||||||
}
|
}
|
||||||
|
|
||||||
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
imageViewType.contentTintColor = NSColor.tertiaryLabelColor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateCell(with proxy: ValetProxy) {
|
||||||
|
imageViewType.image = NSImage(named: "IconProxy")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
27
phpmon/Domain/DomainList/Cells/DomainListNameCell.swift
Normal file
27
phpmon/Domain/DomainList/Cells/DomainListNameCell.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// DomainListNameCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class DomainListNameCell: NSTableCellView, DomainListCellProtocol {
|
||||||
|
static let reusableName = "domainListNameCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var labelSiteName: NSTextField!
|
||||||
|
@IBOutlet weak var labelPathName: NSTextField!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
labelSiteName.stringValue = "\(site.name).\(site.tld)"
|
||||||
|
labelPathName.stringValue = site.absolutePathRelative
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateCell(with proxy: ValetProxy) {
|
||||||
|
labelSiteName.stringValue = "\(proxy.domain).\(proxy.tld)"
|
||||||
|
labelPathName.stringValue = proxy.target
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// SiteListPhpCell.swift
|
// DomainListPhpCell.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 16/03/2022.
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
@@ -9,57 +9,69 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
|
class DomainListPhpCell: NSTableCellView, DomainListCellProtocol {
|
||||||
{
|
static let reusableName = "domainListPhpCell"
|
||||||
static let reusableName = "siteListPhpCell"
|
|
||||||
|
var site: ValetSite?
|
||||||
var site: ValetSite? = nil
|
|
||||||
|
|
||||||
@IBOutlet weak var buttonPhpVersion: NSButton!
|
@IBOutlet weak var buttonPhpVersion: NSButton!
|
||||||
@IBOutlet weak var imageViewPhpVersionOK: NSImageView!
|
@IBOutlet weak var imageViewPhpVersionOK: NSImageView!
|
||||||
|
|
||||||
func populateCell(with site: ValetSite) {
|
func populateCell(with site: ValetSite) {
|
||||||
self.site = site
|
self.site = site
|
||||||
|
|
||||||
buttonPhpVersion.title = " PHP \(site.servingPhpVersion)"
|
buttonPhpVersion.title = " PHP \(site.servingPhpVersion)"
|
||||||
|
|
||||||
|
imageViewPhpVersionOK.toolTip = nil
|
||||||
|
|
||||||
if site.isolatedPhpVersion != nil {
|
if site.isolatedPhpVersion != nil {
|
||||||
imageViewPhpVersionOK.isHidden = false
|
imageViewPhpVersionOK.isHidden = false
|
||||||
imageViewPhpVersionOK.image = NSImage(named: "Isolated")
|
imageViewPhpVersionOK.image = NSImage(named: "Isolated")
|
||||||
|
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.isolated".localized(site.servingPhpVersion)
|
||||||
} else {
|
} else {
|
||||||
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
imageViewPhpVersionOK.isHidden = (site.composerPhp == "???" || !site.composerPhpCompatibleWithLinked)
|
||||||
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
|
imageViewPhpVersionOK.image = NSImage(named: "Checkmark")
|
||||||
|
imageViewPhpVersionOK.toolTip = "domain_list.tooltips.checkmark".localized(site.composerPhp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buttonPhpVersion.isHidden = false
|
||||||
|
imageViewPhpVersionOK.isHidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateCell(with proxy: ValetProxy) {
|
||||||
|
buttonPhpVersion.isHidden = true
|
||||||
|
imageViewPhpVersionOK.isHidden = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func pressedPhpVersion(_ sender: Any) {
|
@IBAction func pressedPhpVersion(_ sender: Any) {
|
||||||
guard let site = self.site else { return }
|
guard let site = self.site else { return }
|
||||||
|
|
||||||
let alert = NSAlert.init()
|
let alert = NSAlert.init()
|
||||||
alert.alertStyle = .informational
|
alert.alertStyle = .informational
|
||||||
|
|
||||||
var information = ""
|
var information = ""
|
||||||
|
|
||||||
if (self.site?.isolatedPhpVersion != nil) {
|
if self.site?.isolatedPhpVersion != nil {
|
||||||
information += "alert.composer_php_isolated.desc".localized(
|
information += "alert.composer_php_isolated.desc".localized(
|
||||||
self.site!.isolatedPhpVersion!.versionNumber.homebrewVersion,
|
self.site!.isolatedPhpVersion!.versionNumber.homebrewVersion,
|
||||||
PhpEnv.phpInstall.version.short
|
PhpEnv.phpInstall.version.short
|
||||||
)
|
)
|
||||||
information += "\n\n"
|
information += "\n\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
|
information += "alert.composer_php_requirement.type.\(site.composerPhpSource.rawValue)"
|
||||||
.localized
|
.localized
|
||||||
|
|
||||||
alert.messageText = "alert.composer_php_requirement.title"
|
alert.messageText = "alert.composer_php_requirement.title"
|
||||||
.localized("\(site.name).\(Valet.shared.config.tld)", site.composerPhp)
|
.localized("\(site.name).\(Valet.shared.config.tld)", site.composerPhp)
|
||||||
alert.informativeText = information
|
alert.informativeText = information
|
||||||
|
|
||||||
alert.addButton(withTitle: "site_link.close".localized)
|
alert.addButton(withTitle: "site_link.close".localized)
|
||||||
|
|
||||||
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
|
var mapIndex: Int = NSApplication.ModalResponse.alertSecondButtonReturn.rawValue
|
||||||
var map: [Int: String] = [:]
|
var map: [Int: String] = [:]
|
||||||
|
|
||||||
if site.isolatedPhpVersion == nil {
|
if site.isolatedPhpVersion == nil {
|
||||||
// Determine which installed versions would be ideal to switch to,
|
// Determine which installed versions would be ideal to switch to,
|
||||||
// but make sure to exclude the currently linked version
|
// but make sure to exclude the currently linked version
|
||||||
@@ -70,9 +82,9 @@ class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
|
|||||||
map[mapIndex] = version.homebrewVersion
|
map[mapIndex] = version.homebrewVersion
|
||||||
mapIndex += 1
|
mapIndex += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Site is not isolated, show options to switch global PHP version
|
// Site is not isolated, show options to switch global PHP version
|
||||||
alert.beginSheetModal(for: App.shared.siteListWindowController!.window!) { response in
|
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!) { response in
|
||||||
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]!
|
||||||
@@ -83,8 +95,8 @@ class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Site is isolated, do not show any options to switch
|
// Site is isolated, do not show any options to switch
|
||||||
alert.beginSheetModal(for: App.shared.siteListWindowController!.window!)
|
alert.beginSheetModal(for: App.shared.domainListWindowController!.window!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
28
phpmon/Domain/DomainList/Cells/DomainListTLSCell.swift
Normal file
28
phpmon/Domain/DomainList/Cells/DomainListTLSCell.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// DomainListNameCell.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class DomainListTLSCell: NSTableCellView, DomainListCellProtocol {
|
||||||
|
static let reusableName = "domainListTLSCell"
|
||||||
|
|
||||||
|
@IBOutlet weak var imageViewLock: NSImageView!
|
||||||
|
|
||||||
|
func populateCell(with site: ValetSite) {
|
||||||
|
imageViewLock.contentTintColor = site.secured
|
||||||
|
? NSColor(named: "IconColorGreen")
|
||||||
|
: NSColor(named: "IconColorRed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateCell(with proxy: ValetProxy) {
|
||||||
|
imageViewLock.contentTintColor = proxy.secured
|
||||||
|
? NSColor(named: "IconColorGreen")
|
||||||
|
: NSColor(named: "IconColorRed")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// SiteListTypeCell.swift
|
// DomainListTypeCell.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 16/03/2022.
|
// Created by Nico Verbruggen on 16/03/2022.
|
||||||
@@ -9,23 +9,28 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
class SiteListTypeCell: NSTableCellView, SiteListCellProtocol
|
class DomainListTypeCell: NSTableCellView, DomainListCellProtocol {
|
||||||
{
|
static let reusableName = "domainListTypeCell"
|
||||||
static let reusableName = "siteListTypeCell"
|
|
||||||
|
|
||||||
@IBOutlet weak var labelDriver: NSTextField!
|
@IBOutlet weak var labelDriver: NSTextField!
|
||||||
@IBOutlet weak var labelPhpVersion: NSTextField!
|
@IBOutlet weak var labelPhpVersion: NSTextField!
|
||||||
|
|
||||||
func populateCell(with site: ValetSite) {
|
func populateCell(with site: ValetSite) {
|
||||||
labelDriver.stringValue = site.driver ?? "driver.not_detected".localized
|
labelDriver.stringValue = site.driver ?? "driver.not_detected".localized
|
||||||
|
|
||||||
// Determine the Laravel version
|
// Determine the Laravel version
|
||||||
if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") {
|
if site.driver == "Laravel" && site.notableComposerDependencies.keys.contains("laravel/framework") {
|
||||||
let constraint = site.notableComposerDependencies["laravel/framework"]!
|
let constraint = site.notableComposerDependencies["laravel/framework"]!
|
||||||
labelDriver.stringValue = "Laravel (\(constraint))"
|
labelDriver.stringValue = "Laravel (\(constraint))"
|
||||||
}
|
}
|
||||||
|
|
||||||
// PHP version
|
// PHP version
|
||||||
labelPhpVersion.stringValue = site.composerPhp == "???" ? "Any PHP" : "PHP \(site.composerPhp)"
|
labelPhpVersion.stringValue = site.composerPhp == "???" ? "Any PHP" : "PHP \(site.composerPhp)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateCell(with proxy: ValetProxy) {
|
||||||
|
labelDriver.stringValue = "Proxy"
|
||||||
|
labelPhpVersion.stringValue = "Active"
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,54 @@
|
|||||||
//
|
//
|
||||||
// SiteListVC+Actions.swift
|
// DomainListVC+Actions.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 23/12/2021.
|
// Created by Nico Verbruggen on 23/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension SiteListVC {
|
extension DomainListVC {
|
||||||
|
|
||||||
@objc func toggleSecure() {
|
@objc func toggleSecure() {
|
||||||
|
if selected is ValetSite {
|
||||||
|
toggleSecureForSite()
|
||||||
|
} else {
|
||||||
|
toggleSecureForProxy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleSecureForProxy() {
|
||||||
|
let originalSecureStatus = selectedProxy!.secured
|
||||||
|
let selectedProxy = selectedProxy!
|
||||||
|
|
||||||
|
self.waitAndExecute {
|
||||||
|
// 1. Remove the original proxy
|
||||||
|
Shell.run("\(Paths.valet) unproxy \(selectedProxy.domain)", requiresPath: true)
|
||||||
|
|
||||||
|
// 2. Add a new proxy, which is either secured/unsecured
|
||||||
|
let secure = originalSecureStatus ? "" : " --secure"
|
||||||
|
Shell.run("\(Paths.valet) proxy \(selectedProxy.domain) \(selectedProxy.target)\(secure)",
|
||||||
|
requiresPath: true)
|
||||||
|
|
||||||
|
// 3. Restart nginx
|
||||||
|
Actions.restartNginx()
|
||||||
|
|
||||||
|
// 4. Reload site list
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
App.shared.domainListWindowController?.pressedReload(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleSecureForSite() {
|
||||||
let rowToReload = tableView.selectedRow
|
let rowToReload = tableView.selectedRow
|
||||||
let originalSecureStatus = selectedSite!.secured
|
let originalSecureStatus = selectedSite!.secured
|
||||||
let action = selectedSite!.secured ? "unsecure" : "secure"
|
let action = selectedSite!.secured ? "unsecure" : "secure"
|
||||||
let selectedSite = selectedSite!
|
let selectedSite = selectedSite!
|
||||||
let command = "cd '\(selectedSite.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
let command = "cd '\(selectedSite.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
|
||||||
|
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Shell.run(command, requiresPath: true)
|
Shell.run(command, requiresPath: true)
|
||||||
} completion: { [self] in
|
} completion: { [self] in
|
||||||
@@ -25,109 +56,137 @@ extension SiteListVC {
|
|||||||
if selectedSite.secured == originalSecureStatus {
|
if selectedSite.secured == originalSecureStatus {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "site_list.alerts_status_not_changed.title".localized,
|
title: "domain_list.alerts_status_not_changed.title".localized,
|
||||||
subtitle: "site_list.alerts_status_not_changed.desc".localized(command)
|
subtitle: "domain_list.alerts_status_not_changed.desc".localized(command)
|
||||||
)
|
)
|
||||||
.withPrimary(text: "OK")
|
.withPrimary(text: "OK")
|
||||||
.show()
|
.show()
|
||||||
} else {
|
} else {
|
||||||
let newState = selectedSite.secured ? "secured" : "unsecured"
|
let newState = selectedSite.secured ? "secured" : "unsecured"
|
||||||
LocalNotification.send(
|
LocalNotification.send(
|
||||||
title: "site_list.alerts_status_changed.title".localized,
|
title: "domain_list.alerts_status_changed.title".localized,
|
||||||
subtitle: "site_list.alerts_status_changed.desc"
|
subtitle: "domain_list.alerts_status_changed.desc"
|
||||||
.localized(
|
.localized(
|
||||||
"\(selectedSite.name).\(Valet.shared.config.tld)",
|
"\(selectedSite.name).\(Valet.shared.config.tld)",
|
||||||
newState
|
newState
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
|
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
|
||||||
tableView.deselectRow(rowToReload)
|
tableView.deselectRow(rowToReload)
|
||||||
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openInBrowser() {
|
@objc func openInBrowser() {
|
||||||
let prefix = selectedSite!.secured ? "https://" : "http://"
|
guard let selected = self.selected else {
|
||||||
let url = URL(string: "\(prefix)\(selectedSite!.name).\(Valet.shared.config.tld)")
|
return
|
||||||
if url != nil {
|
}
|
||||||
NSWorkspace.shared.open(url!)
|
|
||||||
} else {
|
guard let url = selected.getListableUrl() else {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "site_list.alert.invalid_folder_name".localized,
|
title: "domain_list.alert.invalid_folder_name".localized,
|
||||||
subtitle: "site_list.alert.invalid_folder_name_desc".localized
|
subtitle: "domain_list.alert.invalid_folder_name_desc".localized
|
||||||
)
|
)
|
||||||
.withPrimary(text: "OK")
|
.withPrimary(text: "OK")
|
||||||
.show()
|
.show()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSWorkspace.shared.open(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openInFinder() {
|
@objc func openInFinder() {
|
||||||
Shell.run("open '\(selectedSite!.absolutePath)'")
|
Shell.run("open '\(selectedSite!.absolutePath)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openInTerminal() {
|
@objc func openInTerminal() {
|
||||||
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath)'")
|
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath)'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||||
guard let editor = sender.editor else { return }
|
guard let editor = sender.editor else { return }
|
||||||
editor.openDirectory(file: selectedSite!.absolutePath)
|
editor.openDirectory(file: selectedSite!.absolutePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func isolateSite(sender: PhpMenuItem) {
|
@objc func isolateSite(sender: PhpMenuItem) {
|
||||||
let command = "sudo \(Paths.valet) isolate php@\(sender.version) --site '\(self.selectedSite!.name)' && exit;"
|
let command = "sudo \(Paths.valet) isolate php@\(sender.version) --site '\(self.selectedSite!.name)' && exit;"
|
||||||
|
|
||||||
self.performAction(command: command) {
|
self.performAction(command: command) {
|
||||||
self.selectedSite!.determineIsolated()
|
self.selectedSite!.determineIsolated()
|
||||||
|
|
||||||
if self.selectedSite!.isolatedPhpVersion == nil {
|
if self.selectedSite!.isolatedPhpVersion == nil {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
title: "site_list.alerts_isolation_failed.title".localized,
|
title: "domain_list.alerts_isolation_failed.title".localized,
|
||||||
subtitle: "site_list.alerts_isolation_failed.subtitle".localized,
|
subtitle: "domain_list.alerts_isolation_failed.subtitle".localized,
|
||||||
description: "site_list.alerts_isolation_failed.desc".localized(command)
|
description: "domain_list.alerts_isolation_failed.desc".localized(command)
|
||||||
)
|
)
|
||||||
.withPrimary(text: "OK")
|
.withPrimary(text: "OK")
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func removeIsolatedSite() {
|
@objc func removeIsolatedSite() {
|
||||||
self.performAction(command: "sudo \(Paths.valet) unisolate --site '\(self.selectedSite!.name)' && exit;") {
|
self.performAction(command: "sudo \(Paths.valet) unisolate --site '\(self.selectedSite!.name)' && exit;") {
|
||||||
self.selectedSite!.isolatedPhpVersion = nil
|
self.selectedSite!.isolatedPhpVersion = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func unlinkSite() {
|
@objc func unlinkSite() {
|
||||||
guard let site = selectedSite else {
|
guard let site = selectedSite else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if site.aliasPath == nil {
|
if site.aliasPath == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Alert.confirm(
|
Alert.confirm(
|
||||||
onWindow: view.window!,
|
onWindow: view.window!,
|
||||||
messageText: "site_list.confirm_unlink".localized(site.name),
|
messageText: "domain_list.confirm_unlink".localized(site.name),
|
||||||
informativeText: "site_link.confirm_link".localized,
|
informativeText: "domain_list.confirm_unlink_desc".localized,
|
||||||
buttonTitle: "site_list.unlink".localized,
|
buttonTitle: "domain_list.unlink".localized,
|
||||||
secondButtonTitle: "Cancel",
|
secondButtonTitle: "Cancel",
|
||||||
style: .critical,
|
style: .critical,
|
||||||
onFirstButtonPressed: {
|
onFirstButtonPressed: {
|
||||||
Shell.run("valet unlink '\(site.name)'", requiresPath: true)
|
self.waitAndExecute {
|
||||||
self.reloadSites()
|
Shell.run("valet unlink '\(site.name)'", requiresPath: true)
|
||||||
|
} completion: {
|
||||||
|
self.reloadDomains()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func removeProxy() {
|
||||||
|
guard let proxy = selectedProxy else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Alert.confirm(
|
||||||
|
onWindow: view.window!,
|
||||||
|
messageText: "domain_list.confirm_unproxy".localized("\(proxy.domain).\(proxy.tld)"),
|
||||||
|
informativeText: "domain_list.confirm_unproxy_desc".localized,
|
||||||
|
buttonTitle: "domain_list.unproxy".localized,
|
||||||
|
secondButtonTitle: "Cancel",
|
||||||
|
style: .critical,
|
||||||
|
onFirstButtonPressed: {
|
||||||
|
self.waitAndExecute {
|
||||||
|
Shell.run("valet unproxy '\(proxy.domain)'", requiresPath: true)
|
||||||
|
} completion: {
|
||||||
|
self.reloadDomains()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func performAction(command: String, beforeCellReload: @escaping () -> Void) {
|
private func performAction(command: String, beforeCellReload: @escaping () -> Void) {
|
||||||
let rowToReload = tableView.selectedRow
|
let rowToReload = tableView.selectedRow
|
||||||
|
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Shell.run(command, requiresPath: true)
|
Shell.run(command, requiresPath: true)
|
||||||
} completion: { [self] in
|
} completion: { [self] in
|
||||||
@@ -137,5 +196,5 @@ extension SiteListVC {
|
|||||||
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,65 +1,78 @@
|
|||||||
//
|
//
|
||||||
// SiteListVC+ContextMenu.swift
|
// DomainListVC+ContextMenu.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 10/12/2021.
|
// Created by Nico Verbruggen on 10/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension SiteListVC {
|
extension DomainListVC {
|
||||||
|
|
||||||
internal func reloadContextMenu() {
|
internal func reloadContextMenu() {
|
||||||
guard let site = selectedSite else {
|
guard let selected = selected else {
|
||||||
tableView.menu = nil
|
tableView.menu = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let selected = selected as? ValetSite {
|
||||||
|
addMenuItemsForSite(selected)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let selected = selected as? ValetProxy {
|
||||||
|
addMenuItemsForProxy(selected)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Menu Items for Site
|
||||||
|
|
||||||
|
private func addMenuItemsForSite(_ site: ValetSite) {
|
||||||
let menu = NSMenu()
|
let menu = NSMenu()
|
||||||
|
|
||||||
addSystemApps(to: menu)
|
addSystemApps(to: menu)
|
||||||
addSeparator(to: menu)
|
addSeparator(to: menu)
|
||||||
addDetectedApps(to: menu)
|
addDetectedApps(to: menu)
|
||||||
addSeparator(to: menu)
|
addSeparator(to: menu)
|
||||||
|
|
||||||
if Valet.enabled(feature: .isolatedSites) {
|
if Valet.enabled(feature: .isolatedSites) {
|
||||||
addIsolate(to: menu, with: site)
|
addIsolate(to: menu, with: site)
|
||||||
} else {
|
} else {
|
||||||
addDisabledIsolation(to: menu)
|
addDisabledIsolation(to: menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
addUnlink(to: menu, with: site)
|
addUnlink(to: menu, with: site)
|
||||||
addToggleSecure(to: menu, with: site)
|
addToggleSecure(to: menu, secured: site.secured)
|
||||||
|
|
||||||
tableView.menu = menu
|
tableView.menu = menu
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addSystemApps(to menu: NSMenu) {
|
private func addSystemApps(to menu: NSMenu) {
|
||||||
menu.addItem(withTitle: "site_list.system_apps".localized, action: nil, keyEquivalent: "")
|
menu.addItem(withTitle: "domain_list.system_apps".localized, action: nil, keyEquivalent: "")
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.open_in_finder".localized,
|
withTitle: "domain_list.open_in_finder".localized,
|
||||||
action: #selector(self.openInFinder),
|
action: #selector(self.openInFinder),
|
||||||
keyEquivalent: "F"
|
keyEquivalent: "F"
|
||||||
)
|
)
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.open_in_terminal".localized,
|
withTitle: "domain_list.open_in_terminal".localized,
|
||||||
action: #selector(self.openInTerminal),
|
action: #selector(self.openInTerminal),
|
||||||
keyEquivalent: "T"
|
keyEquivalent: "T"
|
||||||
)
|
)
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.open_in_browser".localized,
|
withTitle: "domain_list.open_in_browser".localized,
|
||||||
action: #selector(self.openInBrowser),
|
action: #selector(self.openInBrowser),
|
||||||
keyEquivalent: "B"
|
keyEquivalent: "B"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addDetectedApps(to menu: NSMenu) {
|
private func addDetectedApps(to menu: NSMenu) {
|
||||||
if (applications.count > 0) {
|
if !applications.isEmpty {
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
menu.addItem(withTitle: "site_list.detected_apps".localized, action: nil, keyEquivalent: "")
|
menu.addItem(withTitle: "domain_list.detected_apps".localized, action: nil, keyEquivalent: "")
|
||||||
|
|
||||||
for (_, editor) in applications.enumerated() {
|
for editor in applications {
|
||||||
let editorMenuItem = EditorMenuItem(
|
let editorMenuItem = EditorMenuItem(
|
||||||
title: "Open with \(editor.name)",
|
title: "Open with \(editor.name)",
|
||||||
action: #selector(self.openWithEditor(sender:)),
|
action: #selector(self.openWithEditor(sender:)),
|
||||||
@@ -70,61 +83,94 @@ extension SiteListVC {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addUnlink(to menu: NSMenu, with site: ValetSite) {
|
private func addUnlink(to menu: NSMenu, with site: ValetSite) {
|
||||||
if (site.aliasPath != nil) {
|
if site.aliasPath != nil {
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.unlink".localized,
|
withTitle: "domain_list.unlink".localized,
|
||||||
action: #selector(self.unlinkSite),
|
action: #selector(self.unlinkSite),
|
||||||
keyEquivalent: ""
|
keyEquivalent: ""
|
||||||
)
|
)
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addDisabledIsolation(to menu: NSMenu) {
|
private func addDisabledIsolation(to menu: NSMenu) {
|
||||||
menu.addItem(withTitle: "site_list.isolation_unavailable".localized, action: nil, keyEquivalent: "")
|
menu.addItem(withTitle: "domain_list.isolation_unavailable".localized, action: nil, keyEquivalent: "")
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addIsolate(to menu: NSMenu, with site: ValetSite) {
|
private func addIsolate(to menu: NSMenu, with site: ValetSite) {
|
||||||
if site.isolatedPhpVersion == nil {
|
if site.isolatedPhpVersion == nil {
|
||||||
// ISOLATION POSSIBLE
|
// ISOLATION POSSIBLE
|
||||||
let isolationMenuItem = NSMenuItem(title:"site_list.isolate".localized, action: nil, keyEquivalent: "")
|
let isolationMenuItem = NSMenuItem(title: "domain_list.isolate".localized, action: nil, keyEquivalent: "")
|
||||||
let submenu = NSMenu()
|
let submenu = NSMenu()
|
||||||
submenu.addItem(withTitle: "Choose a PHP version", action: nil, keyEquivalent: "")
|
submenu.addItem(withTitle: "Choose a PHP version", action: nil, keyEquivalent: "")
|
||||||
for version in PhpEnv.shared.availablePhpVersions.reversed() {
|
for version in PhpEnv.shared.availablePhpVersions.reversed() {
|
||||||
let item = PhpMenuItem(title: "Always use PHP \(version)", action: #selector(self.isolateSite), keyEquivalent: "")
|
let item = PhpMenuItem(
|
||||||
|
title: "Always use PHP \(version)",
|
||||||
|
action: #selector(self.isolateSite),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
item.version = version
|
item.version = version
|
||||||
submenu.addItem(item)
|
submenu.addItem(item)
|
||||||
}
|
}
|
||||||
menu.setSubmenu(submenu, for: isolationMenuItem)
|
menu.setSubmenu(submenu, for: isolationMenuItem)
|
||||||
|
|
||||||
menu.addItem(isolationMenuItem)
|
menu.addItem(isolationMenuItem)
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
} else {
|
} else {
|
||||||
// REMOVE ISOLATION POSSIBLE
|
// REMOVE ISOLATION POSSIBLE
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: "site_list.remove_isolation".localized,
|
withTitle: "domain_list.remove_isolation".localized,
|
||||||
action: #selector(self.removeIsolatedSite),
|
action: #selector(self.removeIsolatedSite),
|
||||||
keyEquivalent: ""
|
keyEquivalent: ""
|
||||||
)
|
)
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addToggleSecure(to menu: NSMenu, with site: ValetSite) {
|
private func addToggleSecure(to menu: NSMenu, secured: Bool) {
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
withTitle: site.secured
|
withTitle: secured
|
||||||
? "site_list.unsecure".localized
|
? "domain_list.unsecure".localized
|
||||||
: "site_list.secure".localized,
|
: "domain_list.secure".localized,
|
||||||
action: #selector(toggleSecure),
|
action: #selector(toggleSecure),
|
||||||
keyEquivalent: ""
|
keyEquivalent: ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Menu Items for Proxy
|
||||||
|
|
||||||
|
private func addMenuItemsForProxy(_ proxy: ValetProxy) {
|
||||||
|
let menu = NSMenu()
|
||||||
|
addOpenProxyInBrowser(to: menu)
|
||||||
|
addSeparator(to: menu)
|
||||||
|
addToggleSecure(to: menu, secured: proxy.secured)
|
||||||
|
addRemoveProxy(to: menu)
|
||||||
|
tableView.menu = menu
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addOpenProxyInBrowser(to menu: NSMenu) {
|
||||||
|
menu.addItem(
|
||||||
|
withTitle: "domain_list.open_in_browser".localized,
|
||||||
|
action: #selector(self.openInBrowser),
|
||||||
|
keyEquivalent: "B"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addRemoveProxy(to menu: NSMenu) {
|
||||||
|
menu.addItem(
|
||||||
|
withTitle: "domain_list.unproxy".localized,
|
||||||
|
action: #selector(self.removeProxy),
|
||||||
|
keyEquivalent: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Shared
|
||||||
|
|
||||||
private func addSeparator(to menu: NSMenu) {
|
private func addSeparator(to menu: NSMenu) {
|
||||||
menu.addItem(NSMenuItem.separator())
|
menu.addItem(NSMenuItem.separator())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,119 +1,134 @@
|
|||||||
//
|
//
|
||||||
// SiteListVC.swift
|
// DomainListVC.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 30/03/2021.
|
// Created by Nico Verbruggen on 30/03/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import Carbon
|
import Carbon
|
||||||
|
|
||||||
class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
class DomainListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||||
|
|
||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
|
|
||||||
@IBOutlet weak var tableView: NSTableView!
|
@IBOutlet weak var tableView: NSTableView!
|
||||||
@IBOutlet weak var progressIndicator: NSProgressIndicator!
|
@IBOutlet weak var progressIndicator: NSProgressIndicator!
|
||||||
|
|
||||||
// MARK: - Variables
|
// MARK: - Variables
|
||||||
|
|
||||||
/// List of sites that will be displayed in this view. Originates from the `Valet` object.
|
/// List of sites that will be displayed in this view. Originates from the `Valet` object.
|
||||||
var sites: [ValetSite] = []
|
var domains: [DomainListable] = []
|
||||||
|
|
||||||
/// Array that contains various apps that might open a particular site directory.
|
/// Array that contains various apps that might open a particular site directory.
|
||||||
var applications: [Application] {
|
var applications: [Application] {
|
||||||
return App.shared.detectedApplications
|
return App.shared.detectedApplications
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The last sort descriptor used.
|
/// The last sort descriptor used.
|
||||||
var sortDescriptor: NSSortDescriptor? = nil
|
var sortDescriptor: NSSortDescriptor?
|
||||||
|
|
||||||
/// String that was last searched for. Empty by default.
|
/// String that was last searched for. Empty by default.
|
||||||
var lastSearchedFor = ""
|
var lastSearchedFor = ""
|
||||||
|
|
||||||
// MARK: - Helper Variables
|
// MARK: - Helper Variables
|
||||||
|
|
||||||
var selectedSite: ValetSite? {
|
var selectedSite: ValetSite? {
|
||||||
if tableView.selectedRow == -1 {
|
if tableView.selectedRow == -1 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return sites[tableView.selectedRow]
|
return domains[tableView.selectedRow] as? ValetSite
|
||||||
}
|
}
|
||||||
|
|
||||||
var timer: Timer? = nil
|
var selectedProxy: ValetProxy? {
|
||||||
|
if tableView.selectedRow == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return domains[tableView.selectedRow] as? ValetProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected: DomainListable? {
|
||||||
|
if tableView.selectedRow == -1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return domains[tableView.selectedRow]
|
||||||
|
}
|
||||||
|
|
||||||
|
var timer: Timer?
|
||||||
|
|
||||||
// MARK: - Display
|
// MARK: - Display
|
||||||
|
|
||||||
public static func create(delegate: NSWindowDelegate?) {
|
public static func create(delegate: NSWindowDelegate?) {
|
||||||
let storyboard = NSStoryboard(name: "Main" , bundle : nil)
|
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
let windowController = storyboard.instantiateController(
|
let windowController = storyboard.instantiateController(
|
||||||
withIdentifier: "siteListWindow"
|
withIdentifier: "domainListWindow"
|
||||||
) as! SiteListWC
|
) as! DomainListWC
|
||||||
|
|
||||||
windowController.window!.title = "site_list.title".localized
|
windowController.window!.title = "domain_list.title".localized
|
||||||
windowController.window!.subtitle = "site_list.subtitle".localized
|
windowController.window!.subtitle = "domain_list.subtitle".localized
|
||||||
windowController.window!.delegate = delegate
|
windowController.window!.delegate = delegate
|
||||||
windowController.window!.styleMask = [
|
windowController.window!.styleMask = [
|
||||||
.titled, .closable, .resizable, .miniaturizable
|
.titled, .closable, .resizable, .miniaturizable
|
||||||
]
|
]
|
||||||
windowController.window!.minSize = NSSize(width: 550, height: 200)
|
windowController.window!.minSize = NSSize(width: 550, height: 200)
|
||||||
windowController.window!.delegate = windowController
|
windowController.window!.delegate = windowController
|
||||||
windowController.window!.setFrameAutosaveName("siteListWindow")
|
windowController.window!.setFrameAutosaveName("domainListWindow")
|
||||||
|
|
||||||
App.shared.siteListWindowController = windowController
|
App.shared.domainListWindowController = windowController
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func show(delegate: NSWindowDelegate? = nil) {
|
public static func show(delegate: NSWindowDelegate? = nil) {
|
||||||
if (App.shared.siteListWindowController == nil) {
|
if App.shared.domainListWindowController == nil {
|
||||||
Self.create(delegate: delegate)
|
Self.create(delegate: delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
App.shared.siteListWindowController!.showWindow(self)
|
App.shared.domainListWindowController!.showWindow(self)
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
tableView.doubleAction = #selector(self.doubleClicked(sender:))
|
tableView.doubleAction = #selector(self.doubleClicked(sender:))
|
||||||
|
|
||||||
if !Valet.shared.sites.isEmpty {
|
if !Valet.shared.sites.isEmpty {
|
||||||
// Preloaded list
|
// Preloaded list
|
||||||
sites = Valet.shared.sites
|
domains = Valet.getDomainListable()
|
||||||
searchedFor(text: lastSearchedFor)
|
searchedFor(text: lastSearchedFor)
|
||||||
} else {
|
} else {
|
||||||
reloadSites()
|
reloadDomains()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Async Operations
|
// MARK: - Async Operations
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Disables the UI so the user cannot interact with it.
|
Disables the UI so the user cannot interact with it.
|
||||||
Also shows a spinner to indicate that we're busy.
|
Also shows a spinner to indicate that we're busy.
|
||||||
*/
|
*/
|
||||||
private func setUIBusy() {
|
public func setUIBusy() {
|
||||||
// If it takes more than 0.5s to set the UI to not busy, show a spinner
|
// 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
|
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { _ in
|
||||||
self.progressIndicator.startAnimation(true)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
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() {
|
public func setUINotBusy() {
|
||||||
timer?.invalidate()
|
timer?.invalidate()
|
||||||
progressIndicator.stopAnimation(nil)
|
progressIndicator.stopAnimation(nil)
|
||||||
tableView.alphaValue = 1.0
|
tableView.alphaValue = 1.0
|
||||||
tableView.isEnabled = true
|
tableView.isEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Executes a specific callback and fires the completion callback,
|
Executes a specific callback and fires the completion callback,
|
||||||
while updating the UI as required. As long as the completion callback
|
while updating the UI as required. As long as the completion callback
|
||||||
@@ -122,12 +137,11 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
- Parameter execute: Callback of the work that needs to happen.
|
- Parameter execute: Callback of the work that needs to happen.
|
||||||
- Parameter completion: Callback that is fired when the work is done.
|
- Parameter completion: Callback that is fired when the work is done.
|
||||||
*/
|
*/
|
||||||
internal func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
internal func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {}) {
|
||||||
{
|
|
||||||
setUIBusy()
|
setUIBusy()
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||||
execute()
|
execute()
|
||||||
|
|
||||||
// For a smoother animation, expect at least a 0.2 second delay
|
// For a smoother animation, expect at least a 0.2 second delay
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { [self] in
|
||||||
completion()
|
completion()
|
||||||
@@ -135,40 +149,35 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Site Data Loading
|
// MARK: - Site Data Loading
|
||||||
|
|
||||||
func reloadSites() {
|
func reloadDomains() {
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Valet.shared.reloadSites()
|
Valet.shared.reloadSites()
|
||||||
} completion: { [self] in
|
} completion: { [self] in
|
||||||
sites = Valet.shared.sites
|
domains = Valet.shared.sites
|
||||||
searchedFor(text: lastSearchedFor)
|
searchedFor(text: lastSearchedFor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func applySortDescriptor(_ descriptor: NSSortDescriptor) {
|
func applySortDescriptor(_ descriptor: NSSortDescriptor) {
|
||||||
sortDescriptor = descriptor
|
sortDescriptor = descriptor
|
||||||
|
|
||||||
var sorted = self.sites
|
var sorted = self.domains
|
||||||
|
|
||||||
switch descriptor.key {
|
switch descriptor.key {
|
||||||
case "Secure":
|
case "Secure": sorted = self.domains.sorted { $0.getListableSecured() && !$1.getListableSecured() }
|
||||||
sorted = self.sites.sorted { $0.secured && !$1.secured }; break
|
case "Domain": sorted = self.domains.sorted { $0.getListableAbsolutePath() < $1.getListableAbsolutePath() }
|
||||||
case "Domain":
|
case "PHP": sorted = self.domains.sorted { $0.getListablePhpVersion() < $1.getListablePhpVersion() }
|
||||||
sorted = self.sites.sorted { $0.absolutePath < $1.absolutePath }; break
|
case "Kind": sorted = self.domains.sorted { $0.getListableKind() < $1.getListableKind() }
|
||||||
case "PHP":
|
case "Type": sorted = self.domains.sorted { $0.getListableType() < $1.getListableType() }
|
||||||
sorted = self.sites.sorted { $0.servingPhpVersion < $1.servingPhpVersion }; break
|
default: break
|
||||||
case "Kind":
|
|
||||||
sorted = self.sites.sorted { ($0.aliasPath == nil) && !($1.aliasPath == nil) }; break
|
|
||||||
case "Type":
|
|
||||||
sorted = self.sites.sorted { $0.driver ?? "ZZZ" < $1.driver ?? "ZZZ" }; break
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sites = descriptor.ascending ? sorted.reversed() : sorted
|
self.domains = descriptor.ascending ? sorted.reversed() : sorted
|
||||||
}
|
}
|
||||||
|
|
||||||
func addedNewSite(name: String, secure: Bool) {
|
func addedNewSite(name: String, secure: Bool) {
|
||||||
waitAndExecute {
|
waitAndExecute {
|
||||||
Valet.shared.reloadSites()
|
Valet.shared.reloadSites()
|
||||||
@@ -176,27 +185,27 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
find(name, secure)
|
find(name, secure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func find(_ name: String, _ secure: Bool = false) {
|
private func find(_ name: String, _ secure: Bool = false) {
|
||||||
sites = Valet.shared.sites
|
domains = Valet.getDomainListable()
|
||||||
searchedFor(text: "")
|
searchedFor(text: "")
|
||||||
if let site = sites.enumerated().first(where: { $0.element.name == name }) {
|
if let site = domains.enumerated().first(where: { $0.element.getListableName() == name }) {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.selectRowIndexes([site.offset], byExtendingSelection: false)
|
self.tableView.selectRowIndexes([site.offset], byExtendingSelection: false)
|
||||||
self.tableView.scrollRowToVisible(site.offset)
|
self.tableView.scrollRowToVisible(site.offset)
|
||||||
if (secure && !site.element.secured) {
|
if secure && !site.element.getListableSecured() {
|
||||||
self.toggleSecure()
|
self.toggleSecure()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Table View Delegate
|
// MARK: - Table View Delegate
|
||||||
|
|
||||||
func numberOfRows(in tableView: NSTableView) -> Int {
|
func numberOfRows(in tableView: NSTableView) -> Int {
|
||||||
return sites.count
|
return domains.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
|
func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
|
||||||
guard let sortDescriptor = tableView.sortDescriptors.first else { return }
|
guard let sortDescriptor = tableView.sortDescriptors.first else { return }
|
||||||
// Kinda scuffed way of applying sort descriptors here, but it works.
|
// Kinda scuffed way of applying sort descriptors here, but it works.
|
||||||
@@ -204,82 +213,86 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
|||||||
applySortDescriptor(sortDescriptor)
|
applySortDescriptor(sortDescriptor)
|
||||||
searchedFor(text: lastSearchedFor)
|
searchedFor(text: lastSearchedFor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||||
let mapping: [String: String] = [
|
let mapping: [String: String] = [
|
||||||
"TLS": SiteListTLSCell.reusableName,
|
"TLS": DomainListTLSCell.reusableName,
|
||||||
"DOMAIN": SiteListNameCell.reusableName,
|
"DOMAIN": DomainListNameCell.reusableName,
|
||||||
"ENVIRONMENT": SiteListPhpCell.reusableName,
|
"ENVIRONMENT": DomainListPhpCell.reusableName,
|
||||||
"KIND": SiteListKindCell.reusableName,
|
"KIND": DomainListKindCell.reusableName,
|
||||||
"TYPE": SiteListTypeCell.reusableName,
|
"TYPE": DomainListTypeCell.reusableName
|
||||||
]
|
]
|
||||||
|
|
||||||
let columnName = tableColumn!.identifier.rawValue
|
let columnName = tableColumn!.identifier.rawValue
|
||||||
let identifier = NSUserInterfaceItemIdentifier(rawValue: mapping[columnName]!)
|
let identifier = NSUserInterfaceItemIdentifier(rawValue: mapping[columnName]!)
|
||||||
|
|
||||||
guard let userCell = tableView.makeView(withIdentifier: identifier, owner: self)
|
guard let userCell = tableView.makeView(withIdentifier: identifier, owner: self)
|
||||||
as? SiteListCellProtocol else { return nil }
|
as? DomainListCellProtocol else { return nil }
|
||||||
|
|
||||||
userCell.populateCell(with: sites[row])
|
if let site = domains[row] as? ValetSite {
|
||||||
|
userCell.populateCell(with: site)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let proxy = domains[row] as? ValetProxy {
|
||||||
|
userCell.populateCell(with: proxy)
|
||||||
|
}
|
||||||
|
|
||||||
return userCell as? NSView
|
return userCell as? NSView
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||||
reloadContextMenu()
|
reloadContextMenu()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func doubleClicked(sender: Any) {
|
@objc func doubleClicked(sender: Any) {
|
||||||
guard self.selectedSite != nil else {
|
guard self.selected != nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.openInBrowser()
|
self.openInBrowser()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - (Search) Text Field Delegate
|
// MARK: - (Search) Text Field Delegate
|
||||||
|
|
||||||
func searchedFor(text: String) {
|
func reloadTable() {
|
||||||
lastSearchedFor = text
|
|
||||||
|
|
||||||
let searchString = text.lowercased()
|
|
||||||
|
|
||||||
if searchString.isEmpty {
|
|
||||||
sites = Valet.shared.sites
|
|
||||||
|
|
||||||
if let sortDescriptor = sortDescriptor {
|
|
||||||
self.applySortDescriptor(sortDescriptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.tableView.reloadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let splitSearchString: [String] = searchString
|
|
||||||
.split(separator: " ")
|
|
||||||
.map { return String($0) }
|
|
||||||
|
|
||||||
sites = Valet.shared.sites.filter({ site in
|
|
||||||
return !splitSearchString.map { searchString in
|
|
||||||
return site.name.lowercased().contains(searchString)
|
|
||||||
}.contains(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
if let sortDescriptor = sortDescriptor {
|
if let sortDescriptor = sortDescriptor {
|
||||||
self.applySortDescriptor(sortDescriptor)
|
self.applySortDescriptor(sortDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func searchedFor(text: String) {
|
||||||
|
lastSearchedFor = text
|
||||||
|
|
||||||
|
let searchString = text.lowercased()
|
||||||
|
|
||||||
|
if searchString.isEmpty {
|
||||||
|
domains = Valet.getDomainListable()
|
||||||
|
|
||||||
|
reloadTable()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let splitSearchString: [String] = searchString
|
||||||
|
.split(separator: " ")
|
||||||
|
.map { return String($0) }
|
||||||
|
|
||||||
|
domains = Valet.getDomainListable().filter({ site in
|
||||||
|
return !splitSearchString.map { searchString in
|
||||||
|
return site.getListableName().lowercased().contains(searchString)
|
||||||
|
}.contains(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
reloadTable()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Deinitialization
|
// MARK: - Deinitialization
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
Log.perf("SiteListVC deallocated")
|
Log.perf("DomainListVC deallocated")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,68 +1,93 @@
|
|||||||
//
|
//
|
||||||
// SiteListWC.swift
|
// DomainListWC.swift
|
||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 03/12/2021.
|
// Created by Nico Verbruggen on 03/12/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
|
class DomainListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
|
||||||
|
|
||||||
// MARK: - Window Identifier
|
// MARK: - Window Identifier
|
||||||
|
|
||||||
override var windowName: String {
|
override var windowName: String {
|
||||||
return "SiteList"
|
return "DomainList"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Outlets
|
// MARK: - Outlets
|
||||||
|
|
||||||
@IBOutlet weak var searchToolbarItem: NSSearchToolbarItem!
|
@IBOutlet weak var searchToolbarItem: NSSearchToolbarItem!
|
||||||
|
|
||||||
// MARK: - Window Lifecycle
|
// MARK: - Window Lifecycle
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
self.searchToolbarItem.searchField.delegate = self
|
self.searchToolbarItem.searchField.delegate = self
|
||||||
self.searchToolbarItem.searchField.becomeFirstResponder()
|
self.searchToolbarItem.searchField.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Search functionality
|
// MARK: - Search functionality
|
||||||
|
|
||||||
var contentVC: SiteListVC {
|
var contentVC: DomainListVC {
|
||||||
return self.contentViewController as! SiteListVC
|
return self.contentViewController as! DomainListVC
|
||||||
}
|
}
|
||||||
|
|
||||||
var searchTimer: Timer?
|
var searchTimer: Timer?
|
||||||
|
|
||||||
func controlTextDidChange(_ notification: Notification) {
|
func controlTextDidChange(_ notification: Notification) {
|
||||||
guard let searchField = notification.object as? NSSearchField else {
|
guard let searchField = notification.object as? NSSearchField else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self.searchTimer?.invalidate()
|
self.searchTimer?.invalidate()
|
||||||
|
|
||||||
searchTimer = Timer.scheduledTimer(withTimeInterval: 0.15, repeats: false, block: { _ in
|
searchTimer = Timer.scheduledTimer(withTimeInterval: 0.15, repeats: false, block: { _ in
|
||||||
self.contentVC.searchedFor(text: searchField.stringValue)
|
self.contentVC.searchedFor(text: searchField.stringValue)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Reload functionality
|
// MARK: - Reload functionality
|
||||||
|
|
||||||
@IBAction func pressedReload(_ sender: Any?) {
|
@IBAction func pressedReload(_ sender: Any?) {
|
||||||
contentVC.reloadSites()
|
contentVC.reloadDomains()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func pressedAddLink(_ sender: Any?) {
|
@IBAction func pressedAddLink(_ sender: Any?) {
|
||||||
selectFolder()
|
showSelectionWindow()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Add a new site
|
// MARK: - Add a new site
|
||||||
|
|
||||||
func selectFolder() {
|
func showSelectionWindow() {
|
||||||
|
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
|
let windowController = storyboard.instantiateController(
|
||||||
|
withIdentifier: "showSelectionWindow"
|
||||||
|
) as! NSWindowController
|
||||||
|
|
||||||
|
let viewController = windowController.window!
|
||||||
|
.contentViewController as! SelectionVC
|
||||||
|
|
||||||
|
viewController.domainListWC = self
|
||||||
|
|
||||||
|
self.window?.beginSheet(windowController.window!)
|
||||||
|
}
|
||||||
|
|
||||||
|
func startCreateLinkFlow() {
|
||||||
|
self.showFolderSelectionForLink()
|
||||||
|
}
|
||||||
|
|
||||||
|
func startCreateProxyFlow() {
|
||||||
|
self.showProxyPopup()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Popups
|
||||||
|
|
||||||
|
private func showFolderSelectionForLink() {
|
||||||
let dialog = NSOpenPanel()
|
let dialog = NSOpenPanel()
|
||||||
dialog.message = "site_list.add.modal_description".localized
|
dialog.message = "domain_list.add.modal_description".localized
|
||||||
dialog.showsResizeIndicator = true
|
dialog.showsResizeIndicator = true
|
||||||
dialog.showsHiddenFiles = false
|
dialog.showsHiddenFiles = false
|
||||||
dialog.allowsMultipleSelection = false
|
dialog.allowsMultipleSelection = false
|
||||||
@@ -70,25 +95,37 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
|
|||||||
dialog.canChooseFiles = false
|
dialog.canChooseFiles = false
|
||||||
dialog.beginSheetModal(for: self.window!) { response in
|
dialog.beginSheetModal(for: self.window!) { response in
|
||||||
let result = dialog.url
|
let result = dialog.url
|
||||||
if (result != nil && response == .OK) {
|
if result != nil && response == .OK {
|
||||||
let path: String = result!.path
|
let path: String = result!.path
|
||||||
self.showSitePopup(path)
|
self.showLinkPopup(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showSitePopup(_ folder: String) {
|
private func showLinkPopup(_ folder: String) {
|
||||||
let storyboard = NSStoryboard(name: "Main", bundle : nil)
|
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
let windowController = storyboard.instantiateController(
|
let windowController = storyboard.instantiateController(
|
||||||
withIdentifier: "addSiteWindow"
|
withIdentifier: "addSiteWindow"
|
||||||
) as! NSWindowController
|
) as! NSWindowController
|
||||||
|
|
||||||
let viewController = windowController.window!.contentViewController as! AddSiteVC
|
let viewController = windowController.window!.contentViewController as! AddSiteVC
|
||||||
viewController.pathControl.url = URL(fileURLWithPath: folder)
|
viewController.pathControl.url = URL(fileURLWithPath: folder)
|
||||||
viewController.linkName.stringValue = String(folder.split(separator: "/").last!)
|
viewController.inputDomainName.stringValue = String(folder.split(separator: "/").last!)
|
||||||
viewController.updateTextField()
|
viewController.updateTextField()
|
||||||
|
|
||||||
|
self.window?.beginSheet(windowController.window!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showProxyPopup() {
|
||||||
|
let storyboard = NSStoryboard(name: "Main", bundle: nil)
|
||||||
|
|
||||||
|
let windowController = storyboard.instantiateController(
|
||||||
|
withIdentifier: "addProxyWindow"
|
||||||
|
) as! NSWindowController
|
||||||
|
|
||||||
|
// let viewController = windowController.window!.contentViewController as! AddSiteVC
|
||||||
|
|
||||||
self.window?.beginSheet(windowController.window!)
|
self.window?.beginSheet(windowController.window!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
62
phpmon/Domain/DomainList/SelectionVC.swift
Normal file
62
phpmon/Domain/DomainList/SelectionVC.swift
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// SelectionVC.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 14/04/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class SelectionVC: NSViewController {
|
||||||
|
|
||||||
|
weak var domainListWC: DomainListWC?
|
||||||
|
|
||||||
|
@IBOutlet weak var textFieldTitle: NSTextField!
|
||||||
|
@IBOutlet weak var textFieldDescription: NSTextField!
|
||||||
|
@IBOutlet weak var buttonCreateLink: NSButton!
|
||||||
|
@IBOutlet weak var buttonCreateProxy: NSButton!
|
||||||
|
@IBOutlet weak var buttonCancel: NSButton!
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
loadStaticLocalisedStrings()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
view.window?.makeFirstResponder(buttonCreateLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissView(outcome: NSApplication.ModalResponse) {
|
||||||
|
guard let window = self.view.window, let parent = window.sheetParent else { return }
|
||||||
|
parent.endSheet(window, returnCode: outcome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Localisation
|
||||||
|
|
||||||
|
func loadStaticLocalisedStrings() {
|
||||||
|
textFieldTitle.stringValue = "selection.title".localized
|
||||||
|
textFieldDescription.stringValue = "selection.description".localized
|
||||||
|
buttonCancel.title = "selection.cancel".localized
|
||||||
|
buttonCreateLink.title = "selection.create_link".localized
|
||||||
|
buttonCreateProxy.title = "selection.create_proxy".localized
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Outlet Interactions
|
||||||
|
|
||||||
|
@IBAction func pressedCreateLink(_ sender: Any) {
|
||||||
|
self.dismissView(outcome: .continue)
|
||||||
|
domainListWC?.startCreateLinkFlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func pressedCreateProxy(_ sender: Any) {
|
||||||
|
self.dismissView(outcome: .continue)
|
||||||
|
domainListWC?.startCreateProxyFlow()
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func pressedCancel(_ sender: Any) {
|
||||||
|
self.dismissView(outcome: .cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,59 +13,58 @@ import Foundation
|
|||||||
to this object.
|
to this object.
|
||||||
*/
|
*/
|
||||||
struct ComposerJson: Decodable {
|
struct ComposerJson: Decodable {
|
||||||
|
|
||||||
// MARK: - JSON structure
|
// MARK: - JSON structure
|
||||||
|
|
||||||
let dependencies: Dictionary<String, String>?
|
let dependencies: [String: String]?
|
||||||
let devDependencies: Dictionary<String, String>?
|
let devDependencies: [String: String]?
|
||||||
let configuration: Config?
|
let configuration: Config?
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case dependencies = "require"
|
case dependencies = "require"
|
||||||
case devDependencies = "require-dev"
|
case devDependencies = "require-dev"
|
||||||
case configuration = "config"
|
case configuration = "config"
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Config: Decodable {
|
struct Config: Decodable {
|
||||||
let platform: Platform?
|
let platform: Platform?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Platform: Decodable {
|
struct Platform: Decodable {
|
||||||
let php: String?
|
let php: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks what the PHP version constraint is.
|
Checks what the PHP version constraint is.
|
||||||
Returns a tuple (constraint, location of constraint).
|
Returns a tuple (constraint, location of constraint).
|
||||||
*/
|
*/
|
||||||
public func getPhpVersion() -> (String, ValetSite.VersionSource)
|
public func getPhpVersion() -> (String, ValetSite.VersionSource) {
|
||||||
{
|
|
||||||
// Check if in platform
|
// Check if in platform
|
||||||
if configuration?.platform?.php != nil {
|
if configuration?.platform?.php != nil {
|
||||||
return (configuration!.platform!.php!, .platform)
|
return (configuration!.platform!.php!, .platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if in dependencies
|
// Check if in dependencies
|
||||||
if dependencies?["php"] != nil {
|
if dependencies?["php"] != nil {
|
||||||
return (dependencies!["php"]!, .require)
|
return (dependencies!["php"]!, .require)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown!
|
// Unknown!
|
||||||
return ("???", .unknown)
|
return ("???", .unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks if any notable dependencies can be resolved.
|
Checks if any notable dependencies can be resolved.
|
||||||
Only notable dependencies are saved.
|
Only notable dependencies are saved.
|
||||||
*/
|
*/
|
||||||
public func getNotableDependencies() -> [String: String] {
|
public func getNotableDependencies() -> [String: String] {
|
||||||
var notable: [String: String] = [:]
|
var notable: [String: String] = [:]
|
||||||
|
|
||||||
var scan = Array(PhpFrameworks.DependencyList.keys)
|
var scan = Array(PhpFrameworks.DependencyList.keys)
|
||||||
scan.append("php")
|
scan.append("php")
|
||||||
|
|
||||||
scan.forEach { dependency in
|
scan.forEach { dependency in
|
||||||
if dependencies?[dependency] != nil {
|
if dependencies?[dependency] != nil {
|
||||||
notable[dependency] = dependencies![dependency]
|
notable[dependency] = dependencies![dependency]
|
||||||
@@ -74,7 +73,5 @@ struct ComposerJson: Decodable {
|
|||||||
|
|
||||||
return notable
|
return notable
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ import Foundation
|
|||||||
|
|
||||||
class ComposerWindow {
|
class ComposerWindow {
|
||||||
|
|
||||||
private var menu: MainMenu? = nil
|
private var menu: MainMenu?
|
||||||
private var shouldNotify: Bool! = nil
|
private var shouldNotify: Bool! = nil
|
||||||
private var completion: ((Bool) -> Void)! = nil
|
private var completion: ((Bool) -> Void)! = nil
|
||||||
private var window: ProgressWindowController? = nil
|
private var window: ProgressWindowController?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Updates the global dependencies and runs the completion callback when done.
|
Updates the global dependencies and runs the completion callback when done.
|
||||||
*/
|
*/
|
||||||
@@ -22,33 +22,33 @@ class ComposerWindow {
|
|||||||
self.menu = MainMenu.shared
|
self.menu = MainMenu.shared
|
||||||
self.shouldNotify = notify
|
self.shouldNotify = notify
|
||||||
self.completion = completion
|
self.completion = completion
|
||||||
|
|
||||||
Paths.shared.detectBinaryPaths()
|
Paths.shared.detectBinaryPaths()
|
||||||
if Paths.composer == nil {
|
if Paths.composer == nil {
|
||||||
presentMissingAlert()
|
presentMissingAlert()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
PhpEnv.shared.isBusy = true
|
PhpEnv.shared.isBusy = true
|
||||||
menu?.setBusyImage()
|
menu?.setBusyImage()
|
||||||
menu?.rebuild()
|
menu?.rebuild()
|
||||||
|
|
||||||
window = ProgressWindowController.display(
|
window = ProgressWindowController.display(
|
||||||
title: "alert.composer_progress.title".localized,
|
title: "alert.composer_progress.title".localized,
|
||||||
description: "alert.composer_progress.info".localized
|
description: "alert.composer_progress.info".localized
|
||||||
)
|
)
|
||||||
|
|
||||||
window?.setType(info: true)
|
window?.setType(info: true)
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||||
let task = Shell.user.createTask(
|
let task = Shell.user.createTask(
|
||||||
for: "\(Paths.composer!) global update", requiresPath: true
|
for: "\(Paths.composer!) global update", requiresPath: true
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.window?.addToConsole("\(Paths.composer!) global update\n")
|
self.window?.addToConsole("\(Paths.composer!) global update\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
task.listen(
|
task.listen(
|
||||||
didReceiveStandardOutputData: { string in
|
didReceiveStandardOutputData: { string in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@@ -63,11 +63,11 @@ class ComposerWindow {
|
|||||||
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
task.launch()
|
task.launch()
|
||||||
task.waitUntilExit()
|
task.waitUntilExit()
|
||||||
task.haltListening()
|
task.haltListening()
|
||||||
|
|
||||||
if task.terminationStatus <= 0 {
|
if task.terminationStatus <= 0 {
|
||||||
composerUpdateSucceeded()
|
composerUpdateSucceeded()
|
||||||
} else {
|
} else {
|
||||||
@@ -75,12 +75,12 @@ class ComposerWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func composerUpdateSucceeded() {
|
private func composerUpdateSucceeded() {
|
||||||
// Closing the window should happen after a slight delay
|
// Closing the window should happen after a slight delay
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [self] in
|
||||||
window?.close()
|
window?.close()
|
||||||
if (shouldNotify) {
|
if shouldNotify {
|
||||||
LocalNotification.send(
|
LocalNotification.send(
|
||||||
title: "alert.composer_success.title".localized,
|
title: "alert.composer_success.title".localized,
|
||||||
subtitle: "alert.composer_success.info".localized
|
subtitle: "alert.composer_success.info".localized
|
||||||
@@ -91,7 +91,7 @@ class ComposerWindow {
|
|||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func composerUpdateFailed() {
|
private func composerUpdateFailed() {
|
||||||
// Showing that something failed should be shown immediately
|
// Showing that something failed should be shown immediately
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
@@ -103,18 +103,18 @@ class ComposerWindow {
|
|||||||
completion(false)
|
completion(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Main Menu Update
|
// MARK: Main Menu Update
|
||||||
|
|
||||||
private func removeBusyStatus() {
|
private func removeBusyStatus() {
|
||||||
PhpEnv.shared.isBusy = false
|
PhpEnv.shared.isBusy = false
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
menu?.updatePhpVersionInStatusBar()
|
menu?.updatePhpVersionInStatusBar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Alert
|
// MARK: Alert
|
||||||
|
|
||||||
private func presentMissingAlert() {
|
private func presentMissingAlert() {
|
||||||
BetterAlert()
|
BetterAlert()
|
||||||
.withInformation(
|
.withInformation(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct PhpFrameworks {
|
struct PhpFrameworks {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This list should probably be reversed when checked, because some of these
|
This list should probably be reversed when checked, because some of these
|
||||||
will also require either `laravel/framework` or `symfony/symfony`.
|
will also require either `laravel/framework` or `symfony/symfony`.
|
||||||
@@ -17,10 +17,10 @@ struct PhpFrameworks {
|
|||||||
public static let DependencyList = [
|
public static let DependencyList = [
|
||||||
|
|
||||||
// COMMON FRAMEWORKS
|
// COMMON FRAMEWORKS
|
||||||
"laravel/framework" : "Laravel",
|
"laravel/framework": "Laravel",
|
||||||
"symfony/symfony": "Symfony",
|
"symfony/symfony": "Symfony",
|
||||||
"laravel/lumen": "Lumen",
|
"laravel/lumen": "Lumen",
|
||||||
|
|
||||||
// VARIOUS CMS
|
// VARIOUS CMS
|
||||||
"roots/bedrock": "Bedrock",
|
"roots/bedrock": "Bedrock",
|
||||||
"cakephp/app": "CakePHP",
|
"cakephp/app": "CakePHP",
|
||||||
@@ -37,15 +37,15 @@ struct PhpFrameworks {
|
|||||||
"johnpbloch/wordpress-core": "WordPress",
|
"johnpbloch/wordpress-core": "WordPress",
|
||||||
"zendframework/zendframework": "Zend",
|
"zendframework/zendframework": "Zend",
|
||||||
"zendframework/zend-mvc": "Zend",
|
"zendframework/zend-mvc": "Zend",
|
||||||
"typo3/cms-core": "Typo3",
|
"typo3/cms-core": "Typo3"
|
||||||
|
|
||||||
// TODO (5.1): Handle these in v5.1
|
// TODO (6.0): Handle these in v6.0
|
||||||
// "magento/*": "Magento",
|
// "magento/*": "Magento",
|
||||||
// "concrete5/*": "Concrete5",
|
// "concrete5/*": "Concrete5",
|
||||||
// "contao/*": "Contao",
|
// "contao/*": "Contao",
|
||||||
// "slim/*": "Slim",
|
// "slim/*": "Slim",
|
||||||
]
|
]
|
||||||
|
|
||||||
public static let FileMapping: [String: [String]] = [
|
public static let FileMapping: [String: [String]] = [
|
||||||
"Drupal": [
|
"Drupal": [
|
||||||
// Legacy installations
|
// Legacy installations
|
||||||
@@ -61,10 +61,10 @@ struct PhpFrameworks {
|
|||||||
],
|
],
|
||||||
"Typo3": [
|
"Typo3": [
|
||||||
"/typo3",
|
"/typo3",
|
||||||
"/public/typo3",
|
"/public/typo3"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
There are two cases where users are unlikely to use `composer`,
|
There are two cases where users are unlikely to use `composer`,
|
||||||
when setting up a Drupal or a WordPress project. For performance
|
when setting up a Drupal or a WordPress project. For performance
|
||||||
@@ -75,13 +75,13 @@ struct PhpFrameworks {
|
|||||||
let found = entry.value
|
let found = entry.value
|
||||||
.map { path in return Filesystem.fileExists(basePath + path) }
|
.map { path in return Filesystem.fileExists(basePath + path) }
|
||||||
.contains(true)
|
.contains(true)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
return entry.key
|
return entry.key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,64 +3,106 @@
|
|||||||
// PHP Monitor
|
// PHP Monitor
|
||||||
//
|
//
|
||||||
// Created by Nico Verbruggen on 28/11/2021.
|
// Created by Nico Verbruggen on 28/11/2021.
|
||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class HomebrewDiagnostics {
|
class HomebrewDiagnostics {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines the Homebrew taps the user has installed.
|
||||||
|
*/
|
||||||
|
public static var installedTaps: [String] = {
|
||||||
|
return Shell
|
||||||
|
.pipe("\(Paths.brew) tap")
|
||||||
|
.split(separator: "\n")
|
||||||
|
.map { string in
|
||||||
|
return String(string)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether the PHP Monitor Cask is installed.
|
||||||
|
*/
|
||||||
|
public static var customCaskInstalled: Bool = {
|
||||||
|
return installedTaps.contains("nicoverbruggen/cask")
|
||||||
|
}()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
It is possible to have the `shivammathur/php` tap installed, and for the core homebrew information to be outdated.
|
It is possible to have the `shivammathur/php` tap installed, and for the core homebrew information to be outdated.
|
||||||
This will then result in two different aliases claiming to point to the same formula (`php`).
|
This will then result in two different aliases claiming to point to the same formula (`php`).
|
||||||
This will break all linking functionality in PHP Monitor, and the user needs to be informed of this.
|
This will break all linking functionality in PHP Monitor, and the user needs to be informed of this.
|
||||||
|
|
||||||
This check only needs to be performed if the `shivammathur/php` tap is active.
|
This check only needs to be performed if the `shivammathur/php` tap is active.
|
||||||
*/
|
*/
|
||||||
public static func hasAliasConflict() -> Bool
|
public static func checkForCaskConflict() {
|
||||||
{
|
if hasAliasConflict() {
|
||||||
|
presentAlertAboutConflict()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if the alias conflict as documented in `checkForCaskConflict` actually occurred.
|
||||||
|
*/
|
||||||
|
private static func hasAliasConflict() -> Bool {
|
||||||
let tapAlias = Shell.pipe("\(Paths.brew) info shivammathur/php/php --json")
|
let tapAlias = Shell.pipe("\(Paths.brew) info shivammathur/php/php --json")
|
||||||
|
|
||||||
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") {
|
if tapAlias.contains("brew tap shivammathur/php") || tapAlias.contains("Error") {
|
||||||
Log.info("The user does not appear to have tapped: shivammathur/php")
|
Log.info("The user does not appear to have tapped: shivammathur/php")
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
Log.info("The user DOES have the following tapped: shivammathur/php")
|
Log.info("The user DOES have the following tapped: shivammathur/php")
|
||||||
Log.info("Checking for `php` formula conflicts...")
|
Log.info("Checking for `php` formula conflicts...")
|
||||||
|
|
||||||
let tapPhp = try! JSONDecoder().decode(
|
let tapPhp = try! JSONDecoder().decode(
|
||||||
[HomebrewPackage].self,
|
[HomebrewPackage].self,
|
||||||
from: tapAlias.data(using: .utf8)!
|
from: tapAlias.data(using: .utf8)!
|
||||||
).first!
|
).first!
|
||||||
|
|
||||||
if tapPhp.version != PhpEnv.brewPhpVersion {
|
if tapPhp.version != PhpEnv.brewPhpVersion {
|
||||||
Log.warn("The `php` formula alias seems to be the different between the tap and core. This could be a problem!")
|
Log.warn("The `php` formula alias seems to be the different between the tap and core. "
|
||||||
|
+ "This could be a problem!")
|
||||||
Log.info("Determining whether both of these versions are installed...")
|
Log.info("Determining whether both of these versions are installed...")
|
||||||
|
|
||||||
let bothInstalled = PhpEnv.shared.availablePhpVersions.contains(tapPhp.version)
|
let bothInstalled = PhpEnv.shared.availablePhpVersions.contains(tapPhp.version)
|
||||||
&& PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion)
|
&& PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion)
|
||||||
|
|
||||||
if bothInstalled {
|
if bothInstalled {
|
||||||
Log.warn("Both conflicting aliases seem to be installed, warning the user!")
|
Log.warn("Both conflicting aliases seem to be installed, warning the user!")
|
||||||
} else {
|
} else {
|
||||||
Log.info("Conflicting aliases are not both installed, seems fine!")
|
Log.info("Conflicting aliases are not both installed, seems fine!")
|
||||||
}
|
}
|
||||||
|
|
||||||
return bothInstalled
|
return bothInstalled
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.info("All seems to be OK. No conflicts, both are PHP \(tapPhp.version).")
|
Log.info("All seems to be OK. No conflicts, both are PHP \(tapPhp.version).")
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Show this alert in case the tapped Cask does cause issues because of the conflict.
|
||||||
|
*/
|
||||||
|
private static func presentAlertAboutConflict() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.php_alias_conflict.title".localized,
|
||||||
|
subtitle: "alert.php_alias_conflict.info".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
In order to see if we support the --json syntax, we'll query nginx.
|
In order to see if we support the --json syntax, we'll query nginx.
|
||||||
If the JSON response cannot be parsed, Homebrew is probably out of date.
|
If the JSON response cannot be parsed, Homebrew is probably out of date.
|
||||||
*/
|
*/
|
||||||
public static func cannotLoadService(_ name: String = "nginx") -> Bool
|
public static func cannotLoadService(_ name: String = "nginx") -> Bool {
|
||||||
{
|
|
||||||
let serviceInfo = try? JSONDecoder().decode(
|
let serviceInfo = try? JSONDecoder().decode(
|
||||||
[HomebrewService].self,
|
[HomebrewService].self,
|
||||||
from: Shell.pipe(
|
from: Shell.pipe(
|
||||||
@@ -68,7 +110,7 @@ class HomebrewDiagnostics {
|
|||||||
requiresPath: true
|
requiresPath: true
|
||||||
).data(using: .utf8)!
|
).data(using: .utf8)!
|
||||||
)
|
)
|
||||||
|
|
||||||
return serviceInfo == nil
|
return serviceInfo == nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
phpmon/Domain/Integrations/Nginx/NginxConfiguration.swift
Normal file
82
phpmon/Domain/Integrations/Nginx/NginxConfiguration.swift
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
//
|
||||||
|
// NginxConfiguration.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 15/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class NginxConfiguration {
|
||||||
|
|
||||||
|
/** Contents of the Nginx file in question, as a string. */
|
||||||
|
var contents: String!
|
||||||
|
|
||||||
|
/** The name of the domain, usually derived from the name of the file. */
|
||||||
|
var domain: String
|
||||||
|
|
||||||
|
/** The TLD of the domain, usually derived from the name of the file. */
|
||||||
|
var tld: String
|
||||||
|
|
||||||
|
static func from(filePath: String) -> NginxConfiguration? {
|
||||||
|
let path = filePath.replacingOccurrences(
|
||||||
|
of: "~",
|
||||||
|
with: "/Users/\(Paths.whoami)"
|
||||||
|
)
|
||||||
|
|
||||||
|
do {
|
||||||
|
let fileContents = try String(contentsOfFile: path)
|
||||||
|
return NginxConfiguration.init(
|
||||||
|
path: path,
|
||||||
|
contents: fileContents
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
Log.warn("Could not read the nginx configuration file at: `\(filePath)`")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(path: String, contents: String) {
|
||||||
|
let domain = String(path.split(separator: "/").last!)
|
||||||
|
let tld = String(domain.split(separator: ".").last!)
|
||||||
|
|
||||||
|
self.contents = contents
|
||||||
|
self.domain = domain.replacingOccurrences(of: ".\(tld)", with: "")
|
||||||
|
self.tld = tld
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieves what address this domain is proxying.
|
||||||
|
*/
|
||||||
|
lazy var proxy: String? = {
|
||||||
|
let regex = try! NSRegularExpression(
|
||||||
|
pattern: #"proxy_pass (?<proxy>.*:\d*)(\/*);"#,
|
||||||
|
options: []
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let match = regex.firstMatch(in: contents, range: NSRange(location: 0, length: contents.count))
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
return contents[Range(match.range(withName: "proxy"), in: contents)!]
|
||||||
|
}()
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieves which isolated version is active for this domain (if applicable).
|
||||||
|
*/
|
||||||
|
lazy var isolatedVersion: String? = {
|
||||||
|
let regex = try! NSRegularExpression(
|
||||||
|
// PHP versions have (so far) never needed multiple digits for version numbers
|
||||||
|
pattern: #"(ISOLATED_PHP_VERSION=(php)?(@)?)((?<major>\d)(.)?(?<minor>\d))"#,
|
||||||
|
options: []
|
||||||
|
)
|
||||||
|
|
||||||
|
guard let match = regex.firstMatch(in: contents, range: NSRange(location: 0, length: contents.count))
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
let major: String = contents[Range(match.range(withName: "major"), in: contents)!],
|
||||||
|
minor: String = contents[Range(match.range(withName: "minor"), in: contents)!]
|
||||||
|
|
||||||
|
return "\(major).\(minor)"
|
||||||
|
}()
|
||||||
|
}
|
||||||
27
phpmon/Domain/Integrations/Valet/DomainListable.swift
Normal file
27
phpmon/Domain/Integrations/Valet/DomainListable.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// DomainListable.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 12/04/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol DomainListable {
|
||||||
|
|
||||||
|
func getListableName() -> String
|
||||||
|
|
||||||
|
func getListableSecured() -> Bool
|
||||||
|
|
||||||
|
func getListableAbsolutePath() -> String
|
||||||
|
|
||||||
|
func getListablePhpVersion() -> String
|
||||||
|
|
||||||
|
func getListableKind() -> String
|
||||||
|
|
||||||
|
func getListableType() -> String
|
||||||
|
|
||||||
|
func getListableUrl() -> URL?
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// NginxConfigParser.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 15/03/2022.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
class NginxConfigParser {
|
|
||||||
|
|
||||||
var contents: String!
|
|
||||||
|
|
||||||
init(filePath: String) {
|
|
||||||
self.contents = try! String(contentsOfFile: filePath
|
|
||||||
.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy var isolatedVersion: String? = {
|
|
||||||
let regex = try! NSRegularExpression(
|
|
||||||
// PHP versions have (so far) never needed multiple digits for version numbers
|
|
||||||
pattern: #"(ISOLATED_PHP_VERSION=(php)?(@)?)((?<major>\d)(.)?(?<minor>\d))"#,
|
|
||||||
options: []
|
|
||||||
)
|
|
||||||
|
|
||||||
let match = regex.firstMatch(in: contents, range: NSMakeRange(0, contents.count))
|
|
||||||
|
|
||||||
if match == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let major: String = contents[Range(match!.range(withName: "major"), in: contents)!]
|
|
||||||
let minor: String = contents[Range(match!.range(withName: "minor"), in: contents)!]
|
|
||||||
|
|
||||||
return "\(major).\(minor)"
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// ProxyScanner.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 02/04/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol ProxyScanner {
|
||||||
|
|
||||||
|
func resolveProxies(directoryPath: String) -> [ValetProxy]
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
//
|
||||||
|
// ValetProxyScanner.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 11/04/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ValetProxyScanner: ProxyScanner {
|
||||||
|
func resolveProxies(directoryPath: String) -> [ValetProxy] {
|
||||||
|
return try! FileManager
|
||||||
|
.default
|
||||||
|
.contentsOfDirectory(atPath: directoryPath)
|
||||||
|
.compactMap {
|
||||||
|
return NginxConfiguration.from(filePath: "\(directoryPath)/\($0)")
|
||||||
|
}
|
||||||
|
.filter {
|
||||||
|
return $0.proxy != nil
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
return ValetProxy($0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
//
|
||||||
|
// ValetProxy+Fake.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 02/04/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ValetProxy {
|
||||||
|
|
||||||
|
}
|
||||||
53
phpmon/Domain/Integrations/Valet/Proxies/ValetProxy.swift
Normal file
53
phpmon/Domain/Integrations/Valet/Proxies/ValetProxy.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// ValetProxy.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 30/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ValetProxy: DomainListable {
|
||||||
|
var domain: String
|
||||||
|
var tld: String
|
||||||
|
var target: String
|
||||||
|
var secured: Bool = false
|
||||||
|
|
||||||
|
init(_ configuration: NginxConfiguration) {
|
||||||
|
self.domain = configuration.domain
|
||||||
|
self.tld = configuration.tld
|
||||||
|
self.target = configuration.proxy!
|
||||||
|
self.secured = Filesystem.fileExists("~/.config/valet/Certificates/\(self.domain).\(self.tld).key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - DomainListable Protocol
|
||||||
|
|
||||||
|
func getListableName() -> String {
|
||||||
|
return self.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListableSecured() -> Bool {
|
||||||
|
return self.secured
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListableAbsolutePath() -> String {
|
||||||
|
return self.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListablePhpVersion() -> String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListableKind() -> String {
|
||||||
|
return "proxy"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListableType() -> String {
|
||||||
|
return "proxy"
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListableUrl() -> URL? {
|
||||||
|
return URL(string: "\(self.secured ? "https://" : "http://")\(self.domain).\(self.tld)")
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user