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

Compare commits

..

64 Commits
v5.2 ... v5.3.1

Author SHA1 Message Date
507d4785aa 🚀 Version 5.3.1 2022-06-08 18:36:24 +02:00
c645bb7610 🚀 Version 5.3
Merge branch 'dev/5.3'
2022-05-13 16:33:48 +02:00
e6574966da 📝 Clarify network requests 2022-05-13 00:38:42 +02:00
1e9cfff05e 📝 Updated README 2022-05-13 00:34:57 +02:00
bd34c2b255 👌 Improved nginx file parsing 2022-05-13 00:24:54 +02:00
c040ac3200 🔧 Prepare for release build 2022-05-10 19:20:47 +02:00
6c6888c9cb 👌 Bump version number for new beta build 2022-05-10 19:02:37 +02:00
78cb6922b3 🐛 Fix issue with version parser 2022-05-10 19:01:37 +02:00
c16377c688 👌 Improved updater 2022-05-10 18:52:48 +02:00
540ea5c310 👌 Changes to updater 2022-05-10 18:34:34 +02:00
4ba2b25f18 👌 App version parsing 2022-05-10 18:09:22 +02:00
81b75dcaa8 👌 Async unlink and unproxy to prevent main thread hang 2022-05-10 10:44:24 +02:00
884784d024 🐛 Handle trailing semicolon (#170) 2022-05-10 10:26:48 +02:00
e81ff2870d Add test and prepare for new prerelease 2022-05-10 01:00:18 +02:00
7c631099b2 👌 Fix regular expression 2022-05-10 00:43:17 +02:00
f7a98b88a7 👌 Improve proxy subject validation 2022-05-10 00:38:13 +02:00
3fc21fff2a ♻️ Cleanup 2022-05-10 00:18:45 +02:00
0306c2b726 ♻️ Clarify parameter name 2022-05-10 00:16:47 +02:00
9d822df54e Check for updates 2022-05-10 00:14:48 +02:00
f413b84a45 🏗 WIP: Check for updates 2022-05-09 23:41:52 +02:00
b82811e6bf 🐛 Fix issue with tertiary action 2022-05-09 23:01:36 +02:00
af922664ab 🏗 WIP: Check for updates 2022-05-09 17:28:35 +02:00
8b73e69495 🐛 Fix issue with listing extensions 2022-05-09 15:27:55 +02:00
29a9e14741 🐛 Fix crash issue with .DS_Store 2022-05-09 15:27:43 +02:00
997fb27596 👌 Update copyright, verbose logging tweak 2022-05-07 18:43:36 +02:00
c171df0a93 Add additional verbosity option (#169) 2022-05-07 13:32:45 +02:00
1c15a4e07f Add (un)secure option for proxies 2022-05-06 18:27:03 +02:00
5067c7b87f 🏗 WIP: Add secure/unsecure option for proxy 2022-05-05 21:29:03 +02:00
2987464da8 📝 Added information about linter 2022-05-03 18:20:11 +02:00
4d04275c57 Added linting 2022-05-03 18:16:26 +02:00
790f63e8c9 🔧 Disable Xdebug item for 5.3 2022-05-02 18:26:02 +02:00
86b49812c3 ♻️ Cleanup menu item generation (#168) 2022-05-02 18:24:44 +02:00
ef9e0fd916 Begin work on Xdebug mode switcher (#168) 2022-05-01 22:07:18 +02:00
af8807f799 🔀 Merge bugfixes from 5.2 into 5.3 2022-04-23 12:25:18 +02:00
4eea13f059 🚀 Version 5.2.2
Merge branch 'dev/5.2'
2022-04-22 19:50:57 +02:00
ba93ed93e4 Allow opening of proxies in browser 2022-04-21 19:18:05 +02:00
932a0fe176 Add 'Remove Proxy' to right-click menu 2022-04-21 19:08:40 +02:00
3cff2d6469 🐛 Fix issue w/ flag evaluation order (#165) 2022-04-20 22:31:20 +02:00
df506e4128 🐛 Fix various minor issues (discovered via #162)
- Renaming the configuration files from `www.conf` to the backup
  (`disabled-by-phpmon`) will now succeed if the `disabled-by-phpmon`
  file already exists. This would fail if the `disabled-by-phpmon` file
  already existed in previous builds.

- The PHP-FPM alert when there's an issue with a missing socket
  configuration file has been tweaked and now contains a workaround if
  you want to run a newer version of PHP (e.g. PHP 8.2) that is not
  officially supported by Valet yet.

- When attempting to list the PHP version numbers, the `parse()` method
  is now used, as opposed to `PhpVersionNumber.make()`, which couldn't
  correctly handle pre-release versions of PHP.

- Updated tests to reflect these changes to `PhpVersionNumber`.
2022-04-20 12:19:02 +02:00
eb80214785 ✏️ Updated copy 2022-04-19 20:39:51 +02:00
80a4e361a4 🔧 Prepare for new DEV build 2022-04-19 20:33:19 +02:00
2af88b2bee ✏️ Update copy about non-standard TLDs 2022-04-19 20:26:44 +02:00
5048ccab8c 👌 Update various TODO items 2022-04-18 12:00:54 +02:00
66d13c92d5 Run the valet proxy command (#105) 2022-04-18 11:59:14 +02:00
836b076da9 👌 Cleanup, ensure dynamic form works correctly 2022-04-17 14:33:59 +02:00
1a75838a3b Set up proxy view strings and outlets 2022-04-17 14:02:44 +02:00
a18b7962a7 ♻️ Fix link 2022-04-16 23:21:29 +02:00
84548634ec Add proxy view 2022-04-16 23:19:13 +02:00
419ebe61f7 Add selection view 2022-04-14 14:56:51 +02:00
c45817b127 Added new UI for proxies to storyboard 2022-04-13 19:14:08 +02:00
2c0c0c5a11 Correctly detect secured proxies 2022-04-12 20:43:57 +02:00
1b8d6311ba ♻️ Refactor displaying domains 2022-04-12 17:36:18 +02:00
f0f7a3f7d6 👌 Scan proxies (#105) 2022-04-11 22:56:40 +02:00
8304d774c3 ♻️ Refactoring of files and tests 2022-04-02 15:48:21 +02:00
faeea4e866 Ensure normal nginx file does not have proxy 2022-03-31 18:28:47 +02:00
6470daf7d3 Added test to parse the proxy address 2022-03-31 18:27:26 +02:00
94139a3669 🚚 Moved test files into separate directories 2022-03-31 18:09:50 +02:00
f3b1172d0e 🚀 Version 5.2.1
This is a bugfix release that fixes the isolation command (#158).
2022-03-31 13:51:46 +02:00
8057019898 🐛 Fix isolation command (#158) 2022-03-31 13:35:23 +02:00
9b59fc5dae 👌 Cleanup proxies 2022-03-31 13:34:56 +02:00
75f4377de8 Added tooltips next to PHP version 2022-03-31 13:29:04 +02:00
d3657716c4 ♻️ Added ValetProxy struct 2022-03-30 21:26:53 +02:00
a13990b96f ♻️ Rename SiteList to DomainList 2022-03-30 19:19:36 +02:00
4c7aa7fead Add UI for displaying proxies (#105) 2022-03-29 21:40:10 +02:00
142 changed files with 4486 additions and 2203 deletions

15
.swiftlint.yml Normal file
View 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

View File

@@ -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"/>

View File

@@ -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 = 756; CURRENT_PROJECT_VERSION = 787;
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; MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -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 = 756; CURRENT_PROJECT_VERSION = 787;
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; MARKETING_VERSION = 5.3.1;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon; PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -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"

View File

@@ -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.

View File

@@ -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

View File

@@ -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 {
@@ -64,9 +64,9 @@ class BrewJsonParserTest: XCTestCase {
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)
} }

View File

@@ -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
)
}
}

View 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)
}
}

View File

@@ -3,12 +3,12 @@
// 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")!
@@ -69,4 +69,9 @@ class ExtensionParserTest: XCTestCase {
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")
}
} }

View File

@@ -3,12 +3,12 @@
// 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(

View 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;
}
}

View 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 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;
}
}

View 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;
}
}

View File

@@ -3,7 +3,7 @@
// 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

View 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)
}
}

View 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)
}
}

View File

@@ -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

View File

@@ -11,20 +11,24 @@ import XCTest
class PhpVersionNumberTest: XCTestCase { class PhpVersionNumberTest: XCTestCase {
func testCanDeconstructPhpVersion() throws { func testCanDeconstructPhpVersion() throws {
XCTAssertEqual(
try! PhpVersionNumber.parse("PHP 8.2.0-dev"),
PhpVersionNumber(major: 8, minor: 2, patch: 0)
)
XCTAssertEqual( XCTAssertEqual(
try! PhpVersionNumber.parse("PHP 8.1.0RC5-dev"), try! PhpVersionNumber.parse("PHP 8.1.0RC5-dev"),
PhpVersionNumber(major: 8, minor: 1, patch: 0) PhpVersionNumber(major: 8, minor: 1, patch: 0)
) )
XCTAssertEqual( XCTAssertEqual(
PhpVersionNumber.make(from: "8.0.11"), try! PhpVersionNumber.parse("8.0.11"),
PhpVersionNumber(major: 8, minor: 0, patch: 11) PhpVersionNumber(major: 8, minor: 0, patch: 11)
) )
XCTAssertEqual( XCTAssertEqual(
PhpVersionNumber.make(from: "7.4.2"), try! PhpVersionNumber.parse("7.4.2"),
PhpVersionNumber(major: 7, minor: 4, patch: 2) PhpVersionNumber(major: 7, minor: 4, patch: 2)
) )
XCTAssertEqual( XCTAssertEqual(
PhpVersionNumber.make(from: "7.4"), try! PhpVersionNumber.parse("7.4"),
PhpVersionNumber(major: 7, minor: 4, patch: nil) PhpVersionNumber(major: 7, minor: 4, patch: nil)
) )
XCTAssertEqual( XCTAssertEqual(

View File

@@ -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

View File

@@ -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

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 935 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -2,7 +2,7 @@
// 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
@@ -12,33 +12,28 @@ 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",
@@ -64,34 +59,30 @@ class Actions {
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])
@@ -99,8 +90,7 @@ class Actions {
// 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)
@@ -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)

View File

@@ -2,7 +2,7 @@
// 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
@@ -28,7 +28,7 @@ public class Command {
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")

View File

@@ -2,7 +2,7 @@
// 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
@@ -56,13 +56,27 @@ struct Constants {
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"
)!
} }
} }

View File

@@ -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,24 +11,21 @@
/** /**
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: "\\/")
@@ -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"
""") """)

View File

@@ -3,7 +3,7 @@
// 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

View File

@@ -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
@@ -49,7 +49,7 @@ public class Paths {
// - 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

View File

@@ -35,8 +35,11 @@ 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()

View File

@@ -2,7 +2,7 @@
// 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
@@ -91,7 +91,7 @@ public class Shell {
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,6 +102,12 @@ public class Shell {
)!, )!,
task: task task: task
) )
if CommandLine.arguments.contains("--v") {
log(task: task, output: output)
}
return output
} }
/** /**
@@ -119,6 +125,23 @@ public class Shell {
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

View File

@@ -2,7 +2,7 @@
// 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

View File

@@ -3,7 +3,7 @@
// 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

View File

@@ -14,25 +14,25 @@ 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)
} }
} }

View File

@@ -2,7 +2,7 @@
// 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
@@ -17,7 +17,7 @@ extension String {
} }
func countInstances(of stringToFind: String) -> Int { func countInstances(of stringToFind: String) -> Int {
if (stringToFind.isEmpty) { if stringToFind.isEmpty {
return 0 return 0
} }

View File

@@ -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
@@ -26,10 +26,10 @@ extension XibLoadable where Self: NSView {
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
} }

View File

@@ -2,7 +2,7 @@
// 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
@@ -27,7 +27,7 @@ class Alert {
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

View File

@@ -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

View File

@@ -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 Cocoa import Cocoa

View File

@@ -2,7 +2,7 @@
// 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

View File

@@ -2,7 +2,7 @@
// 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
@@ -24,7 +24,7 @@ class MenuBarImageGenerator {
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)

View File

@@ -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

View File

@@ -3,7 +3,7 @@
// 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
@@ -11,7 +11,7 @@ import Foundation
class VersionExtractor { class VersionExtractor {
/** /**
This attempts to extract the version number from the command line output of Valet. This attempts to extract the version number from any given string.
*/ */
public static func from(_ string: String) -> String? { public static func from(_ string: String) -> String? {
do { do {
@@ -23,7 +23,7 @@ class VersionExtractor {
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 {

View File

@@ -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
@@ -35,7 +35,7 @@ class ActivePhpInstallation {
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
@@ -60,9 +60,9 @@ class ActivePhpInstallation {
// 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)
} }
} }
} }
@@ -71,12 +71,12 @@ class ActivePhpInstallation {
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
@@ -112,13 +112,13 @@ class ActivePhpInstallation {
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"
} }

View 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"
]
}
}

View File

@@ -2,7 +2,7 @@
// 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

View File

@@ -3,7 +3,7 @@
// 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
@@ -15,7 +15,7 @@ class PhpEnv {
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,
@@ -76,15 +76,14 @@ class PhpEnv {
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"))
@@ -95,7 +94,7 @@ class PhpEnv {
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)
} }
@@ -126,7 +125,7 @@ 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
@@ -144,8 +143,7 @@ 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)
} }
} }

View File

@@ -21,7 +21,8 @@ class PhpHelper {
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
} }
} }

View File

@@ -13,7 +13,7 @@ public struct PhpVersionNumberCollection: Equatable {
public static func make(from versions: [String]) -> Self { public static func make(from versions: [String]) -> Self {
return PhpVersionNumberCollection( return PhpVersionNumberCollection(
versions: versions.map { PhpVersionNumber.make(from: $0)! } versions: versions.map { try! PhpVersionNumber.parse($0) }
) )
} }
@@ -134,7 +134,12 @@ public struct PhpVersionNumber: Equatable {
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(
@@ -143,7 +148,7 @@ 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])
} }

View File

@@ -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
@@ -23,7 +23,8 @@ class PhpExtension {
/// 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.
@@ -34,6 +35,7 @@ class PhpExtension {
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,13 +49,14 @@ 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
@@ -69,7 +72,8 @@ class PhpExtension {
} }
/** /**
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
@@ -91,7 +95,7 @@ class PhpExtension {
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 []
} }

View File

@@ -3,7 +3,7 @@
// 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

View File

@@ -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.
// //
import Foundation import Foundation
@@ -20,8 +20,7 @@ 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
@@ -32,7 +31,7 @@ class InternalSwitcher: PhpSwitcher {
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)
} }
@@ -71,6 +70,11 @@ 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) {
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.moveItem(at: existing, to: new) try FileManager.default.moveItem(at: existing, to: new)
Log.info("Success: A default `www.conf` file was disabled for PHP \(version).") Log.info("Success: A default `www.conf` file was disabled for PHP \(version).")
} catch { } catch {
@@ -89,7 +93,7 @@ class InternalSwitcher: PhpSwitcher {
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 {

View File

@@ -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.
// //
import Foundation import Foundation

View File

@@ -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
@@ -38,7 +38,7 @@ extension 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)
} }
} }

View File

@@ -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

View File

@@ -2,7 +2,7 @@
// 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
@@ -21,6 +21,16 @@ class App {
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)
@@ -41,10 +51,10 @@ class App {
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] = []
@@ -57,7 +67,7 @@ class App {
/** /**
The shortcut the user has requested. The shortcut the user has requested.
*/ */
var shortcutHotkey: HotKey? = nil { var shortcutHotkey: HotKey? {
didSet { didSet {
setupGlobalHotkeyListener() setupGlobalHotkeyListener()
} }

View File

@@ -3,7 +3,7 @@
// 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
@@ -44,4 +44,3 @@ extension AppDelegate {
} }
} }
} }

View File

@@ -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
@@ -26,19 +26,19 @@ 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()
@@ -46,9 +46,9 @@ extension AppDelegate {
} }
@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()
} }

View File

@@ -3,7 +3,7 @@
// 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

View File

@@ -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
@@ -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)")

View 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()
}
}
}

View 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 ?? "???"))"
}
}

View File

@@ -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 &amp; 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"/>

View File

@@ -23,7 +23,7 @@ class InterApp {
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()
} }
}), })
]} ]}
} }

View File

@@ -2,7 +2,7 @@
// 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
@@ -17,8 +17,7 @@ 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)")

View 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
)
}
}

View File

@@ -11,14 +11,19 @@ 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!
@@ -37,27 +42,28 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
// 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
@@ -67,15 +73,15 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
// 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,
@@ -84,7 +90,7 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
} }
@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) {
@@ -100,41 +106,46 @@ class AddSiteVC: NSViewController, NSTextFieldDelegate {
// 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
) )
} }

View 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)
}

View File

@@ -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,9 +9,8 @@
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!
@@ -30,4 +29,8 @@ class SiteListKindCell: NSTableCellView, SiteListCellProtocol
imageViewType.contentTintColor = NSColor.tertiaryLabelColor imageViewType.contentTintColor = NSColor.tertiaryLabelColor
} }
func populateCell(with proxy: ValetProxy) {
imageViewType.image = NSImage(named: "IconProxy")
}
} }

View 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
}
}

View File

@@ -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,11 +9,10 @@
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? = nil var site: ValetSite?
@IBOutlet weak var buttonPhpVersion: NSButton! @IBOutlet weak var buttonPhpVersion: NSButton!
@IBOutlet weak var imageViewPhpVersionOK: NSImageView! @IBOutlet weak var imageViewPhpVersionOK: NSImageView!
@@ -21,17 +20,30 @@ class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
func populateCell(with site: ValetSite) { func populateCell(with site: ValetSite) {
self.site = site self.site = site
buttonPhpVersion.isHidden = false
imageViewPhpVersionOK.isHidden = false
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)
} }
} }
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 }
@@ -40,7 +52,7 @@ class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
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
@@ -72,7 +84,7 @@ class SiteListPhpCell: NSTableCellView, SiteListCellProtocol
} }
// 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,7 +95,7 @@ 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!)
} }
} }

View 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")
}
}

View File

@@ -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,9 +9,8 @@
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!
@@ -28,4 +27,10 @@ class SiteListTypeCell: NSTableCellView, SiteListCellProtocol
// 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
}
} }

View File

@@ -0,0 +1,200 @@
//
// DomainListVC+Actions.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 23/12/2021.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
import Cocoa
extension DomainListVC {
@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 originalSecureStatus = selectedSite!.secured
let action = selectedSite!.secured ? "unsecure" : "secure"
let selectedSite = selectedSite!
let command = "cd '\(selectedSite.absolutePath)' && sudo \(Paths.valet) \(action) && exit;"
waitAndExecute {
Shell.run(command, requiresPath: true)
} completion: { [self] in
selectedSite.determineSecured()
if selectedSite.secured == originalSecureStatus {
BetterAlert()
.withInformation(
title: "domain_list.alerts_status_not_changed.title".localized,
subtitle: "domain_list.alerts_status_not_changed.desc".localized(command)
)
.withPrimary(text: "OK")
.show()
} else {
let newState = selectedSite.secured ? "secured" : "unsecured"
LocalNotification.send(
title: "domain_list.alerts_status_changed.title".localized,
subtitle: "domain_list.alerts_status_changed.desc"
.localized(
"\(selectedSite.name).\(Valet.shared.config.tld)",
newState
)
)
}
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
tableView.deselectRow(rowToReload)
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
}
}
@objc func openInBrowser() {
guard let selected = self.selected else {
return
}
guard let url = selected.getListableUrl() else {
BetterAlert()
.withInformation(
title: "domain_list.alert.invalid_folder_name".localized,
subtitle: "domain_list.alert.invalid_folder_name_desc".localized
)
.withPrimary(text: "OK")
.show()
return
}
NSWorkspace.shared.open(url)
}
@objc func openInFinder() {
Shell.run("open '\(selectedSite!.absolutePath)'")
}
@objc func openInTerminal() {
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath)'")
}
@objc func openWithEditor(sender: EditorMenuItem) {
guard let editor = sender.editor else { return }
editor.openDirectory(file: selectedSite!.absolutePath)
}
@objc func isolateSite(sender: PhpMenuItem) {
let command = "sudo \(Paths.valet) isolate php@\(sender.version) --site '\(self.selectedSite!.name)' && exit;"
self.performAction(command: command) {
self.selectedSite!.determineIsolated()
if self.selectedSite!.isolatedPhpVersion == nil {
BetterAlert()
.withInformation(
title: "domain_list.alerts_isolation_failed.title".localized,
subtitle: "domain_list.alerts_isolation_failed.subtitle".localized,
description: "domain_list.alerts_isolation_failed.desc".localized(command)
)
.withPrimary(text: "OK")
.show()
}
}
}
@objc func removeIsolatedSite() {
self.performAction(command: "sudo \(Paths.valet) unisolate --site '\(self.selectedSite!.name)' && exit;") {
self.selectedSite!.isolatedPhpVersion = nil
}
}
@objc func unlinkSite() {
guard let site = selectedSite else {
return
}
if site.aliasPath == nil {
return
}
Alert.confirm(
onWindow: view.window!,
messageText: "domain_list.confirm_unlink".localized(site.name),
informativeText: "domain_list.confirm_unlink_desc".localized,
buttonTitle: "domain_list.unlink".localized,
secondButtonTitle: "Cancel",
style: .critical,
onFirstButtonPressed: {
self.waitAndExecute {
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) {
let rowToReload = tableView.selectedRow
waitAndExecute {
Shell.run(command, requiresPath: true)
} completion: { [self] in
beforeCellReload()
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0, 1, 2, 3, 4])
tableView.deselectRow(rowToReload)
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
}
}
}

View File

@@ -1,21 +1,34 @@
// //
// 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)
@@ -30,36 +43,36 @@ extension SiteListVC {
} }
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:)),
@@ -72,9 +85,9 @@ 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: ""
) )
@@ -83,18 +96,22 @@ extension SiteListVC {
} }
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)
} }
@@ -105,7 +122,7 @@ extension SiteListVC {
} 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: ""
) )
@@ -113,16 +130,45 @@ extension SiteListVC {
} }
} }
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())
} }

View File

@@ -1,15 +1,15 @@
// //
// 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
@@ -19,7 +19,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
// 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] {
@@ -27,7 +27,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
} }
/// 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 = ""
@@ -38,39 +38,53 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
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)
} }
@@ -78,12 +92,13 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
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()
} }
} }
@@ -93,7 +108,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
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)
@@ -107,7 +122,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
/** /**
Re-enables the UI so the user can interact with it. Re-enables the UI so the user can interact with it.
*/ */
private func setUINotBusy() { public func setUINotBusy() {
timer?.invalidate() timer?.invalidate()
progressIndicator.stopAnimation(nil) progressIndicator.stopAnimation(nil)
tableView.alphaValue = 1.0 tableView.alphaValue = 1.0
@@ -122,8 +137,7 @@ 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()
@@ -138,11 +152,11 @@ 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)
} }
} }
@@ -150,23 +164,18 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
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) {
@@ -178,13 +187,13 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
} }
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()
} }
} }
@@ -194,7 +203,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
// 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]) {
@@ -207,20 +216,26 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
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
} }
@@ -230,7 +245,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
} }
@objc func doubleClicked(sender: Any) { @objc func doubleClicked(sender: Any) {
guard self.selectedSite != nil else { guard self.selected != nil else {
return return
} }
@@ -239,35 +254,7 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
// 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)
} }
@@ -277,9 +264,35 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
} }
} }
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")
} }
} }

View File

@@ -1,19 +1,19 @@
// //
// 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
@@ -30,8 +30,8 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
// 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?
@@ -51,18 +51,43 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
// 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,15 +95,15 @@ 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"
@@ -86,9 +111,21 @@ class SiteListWC: PMWindowController, NSSearchFieldDelegate, NSToolbarDelegate {
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!) 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!)
}
} }

View 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)
}
}

View File

@@ -16,8 +16,8 @@ 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 {
@@ -40,8 +40,7 @@ struct ComposerJson: Decodable {
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)
@@ -76,5 +75,3 @@ struct ComposerJson: Decodable {
} }
} }

View File

@@ -10,10 +10,10 @@ 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.
@@ -80,7 +80,7 @@ class ComposerWindow {
// 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

View File

@@ -17,7 +17,7 @@ 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",
@@ -37,9 +37,9 @@ 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",
@@ -61,7 +61,7 @@ struct PhpFrameworks {
], ],
"Typo3": [ "Typo3": [
"/typo3", "/typo3",
"/public/typo3", "/public/typo3"
] ]
] ]

View File

@@ -3,13 +3,32 @@
// 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`).
@@ -17,8 +36,16 @@ class HomebrewDiagnostics {
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") {
@@ -34,7 +61,8 @@ class HomebrewDiagnostics {
).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)
@@ -55,12 +83,26 @@ class HomebrewDiagnostics {
} }
} }
/**
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(

View 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)"
}()
}

View 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?
}

View File

@@ -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)"
}()
}

View File

@@ -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]
}

View File

@@ -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)
}
}
}

View File

@@ -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 {
}

View 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)")
}
}

View File

@@ -0,0 +1,41 @@
//
// FakeSiteScanner.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 02/04/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
class FakeSiteScanner: SiteScanner {
let fakes = [
ValetSite(fakeWithName: "laravel", tld: "test", secure: true,
path: "~/Code/laravel/framework", linked: true),
ValetSite(fakeWithName: "tailwind", tld: "test", secure: true,
path: "~/Code/tailwind/site", linked: true, constraint: "8.0"),
ValetSite(fakeWithName: "forge", tld: "test", secure: true,
path: "~/Code/laravel/forge", linked: true),
ValetSite(fakeWithName: "concord", tld: "test", secure: false,
path: "~/Code/concord", linked: true, driver: "Laravel (^8.0)", constraint: "^7.4", isolated: "7.4"),
ValetSite(fakeWithName: "drupal", tld: "test", secure: false,
path: "~/Sites/drupal", linked: false, driver: "Drupal", constraint: "^7.4", isolated: "7.4"),
ValetSite(fakeWithName: "wordpress", tld: "test", secure: false,
path: "~/Sites/wordpress", linked: false, driver: "WordPress", constraint: "^7.4", isolated: "7.4")
]
func resolveSiteCount(paths: [String]) -> Int {
return fakes.count
}
func resolveSitesFrom(paths: [String]) -> [ValetSite] {
return fakes
}
func resolveSite(path: String) -> ValetSite? {
return nil
}
}

View File

@@ -0,0 +1,17 @@
//
// ValetSiteScanner.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 19/03/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved.
//
import Foundation
protocol SiteScanner {
func resolveSiteCount(paths: [String]) -> Int
func resolveSitesFrom(paths: [String]) -> [ValetSite]
func resolveSite(path: String) -> ValetSite?
}

View File

@@ -2,55 +2,13 @@
// ValetSiteScanner.swift // ValetSiteScanner.swift
// PHP Monitor // PHP Monitor
// //
// Created by Nico Verbruggen on 19/03/2022. // Created by Nico Verbruggen on 02/04/2022.
// Copyright © 2022 Nico Verbruggen. All rights reserved. // Copyright © 2022 Nico Verbruggen. All rights reserved.
// //
import Foundation import Foundation
protocol SiteScanner class ValetSiteScanner: SiteScanner {
{
func resolveSiteCount(paths: [String]) -> Int
func resolveSitesFrom(paths: [String]) -> [ValetSite]
func resolveSite(path: String) -> ValetSite?
}
class FakeSiteScanner: SiteScanner
{
let fakes = [
ValetSite(fakeWithName: "laravel", tld: "test", secure: true, path: "~/Code/laravel/framework", linked: true),
ValetSite(fakeWithName: "tailwind", tld: "test", secure: true, path: "~/Code/tailwind/site", linked: true, constraint: "8.0"),
ValetSite(fakeWithName: "forge", tld: "test", secure: true, path: "~/Code/laravel/forge", linked: true),
ValetSite(fakeWithName: "concord", tld: "test", secure: false,
path: "~/Code/concord", linked: true, driver: "Laravel (^8.0)", constraint: "^7.4", isolated: "7.4"),
ValetSite(fakeWithName: "drupal", tld: "test", secure: false,
path: "~/Sites/drupal", linked: false, driver: "Drupal", constraint: "^7.4", isolated: "7.4"),
ValetSite(fakeWithName: "wordpress", tld: "test", secure: false,
path: "~/Sites/wordpress", linked: false, driver: "WordPress", constraint: "^7.4", isolated: "7.4")
]
func resolveSiteCount(paths: [String]) -> Int {
return fakes.count
}
func resolveSitesFrom(paths: [String]) -> [ValetSite] {
return fakes
}
func resolveSite(path: String) -> ValetSite? {
return nil
}
}
class ValetSiteScanner: SiteScanner
{
func resolveSiteCount(paths: [String]) -> Int { func resolveSiteCount(paths: [String]) -> Int {
return paths.map { path in return paths.map { path in
@@ -59,7 +17,7 @@ class ValetSiteScanner: SiteScanner
return entries return entries
.map { self.isSite($0, forPath: path) } .map { self.isSite($0, forPath: path) }
.filter{ $0 == true} .filter { $0 == true}
.count .count
}.reduce(0, +) }.reduce(0, +)

Some files were not shown because too many files have changed in this diff Show More