Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
4eea13f059 | |||
3cff2d6469 | |||
df506e4128 | |||
f3b1172d0e | |||
32278533bf | |||
39908f7fc5 | |||
347d79c88d | |||
9bc117e9f5 | |||
ba5fbed9be | |||
211556d5ce | |||
066d7bc217 | |||
f072ceae37 | |||
273dac1ca7 | |||
f3b170ba14 | |||
78d8030ed6 | |||
a300d2f4cf | |||
96a658073e | |||
8395ba407d | |||
f80e3fed2b | |||
b48edf7409 | |||
e0a0eb089d | |||
60b126333d | |||
649a3f4fb5 | |||
9a326928f3 | |||
52f87ca18a | |||
ad2d2cb57f | |||
22c0021ada | |||
2cfc5731fb | |||
1d74e1536c | |||
e6b2ddf2ad | |||
62fda6224e | |||
083f8ebec8 | |||
aec8fb1168 | |||
d5d9b38ed6 | |||
9a6975e3d9 | |||
d1c40f2eb5 | |||
ad58661449 | |||
1d6cfd419e | |||
15182ea15a | |||
c43e00c88d | |||
25c7004368 | |||
02ba57cd64 | |||
c2dc6302c0 | |||
af9f30a123 | |||
28c5754800 | |||
48c1d48573 | |||
582bf0e12c | |||
46b30bbff4 | |||
372011ca08 | |||
7255792910 | |||
0c96b11b05 | |||
ea4da12d3b | |||
8419ebad10 | |||
09a5cf836a | |||
1a1a53b472 | |||
a8bad8447a | |||
ca8f5a8fbe | |||
a0e7aec228 | |||
26badc759e | |||
e21c2168ea | |||
589ab3664e | |||
48b4f9b160 | |||
139e416c3b | |||
ba4ed3b365 | |||
06a8022265 | |||
3b297e07dc | |||
68fa8e523e | |||
768bf06a9d | |||
6a8d66758a | |||
078e6e6f23 | |||
3f80bfb641 | |||
a34389c3a9 | |||
692d3c143f | |||
bc86a45925 | |||
2a412b794a | |||
bf673263d8 | |||
ce498d3019 | |||
e398f089af | |||
e8ba24e48b | |||
e0f40be188 | |||
42848764cf | |||
46ac0c339c | |||
a0fe68f3ab | |||
c952c3d031 | |||
c05cdeda72 | |||
ae7b285eb0 | |||
6b3c562af2 | |||
e3ae878cae | |||
dd43c94e6e | |||
0e8fe1fcfb | |||
293b7f564e | |||
634ffb4c57 | |||
9468a2e9f8 | |||
921ecd99d6 | |||
d06f92051d | |||
97d68f89f1 | |||
115863f1ee | |||
bc27a4d25a | |||
5a0b2f319b | |||
50f003a2bd | |||
bc0b50f5bf | |||
dd20b25900 | |||
d0469467ac | |||
61427ec505 | |||
6cd1d78572 | |||
0ad5049785 | |||
b5c1960260 | |||
e6fbe7c4a4 | |||
537536d443 | |||
f702d14749 | |||
74bb544f3c | |||
dae47e3779 | |||
dc91d0e00c | |||
6187eb3e4e | |||
0fa6d337f2 | |||
a10e1cad11 | |||
7c252deede | |||
224c5c4fe2 | |||
e945b5fe94 | |||
23e534c5c9 | |||
dcddf74830 | |||
9baf69394e |
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Something going wrong? File a bug report!
|
||||||
|
title: ''
|
||||||
|
labels: bug
|
||||||
|
assignees: nicoverbruggen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
**Required information**
|
||||||
|
- Did you consult the FAQ in the README? [yes/no]
|
||||||
|
- Did you try "Fix My Valet"? [yes/no]
|
||||||
|
- OS: [e.g. macOS Monterey]
|
||||||
|
- PHP Monitor version [e.g. v5.0.1]
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an enhancement.
|
||||||
|
title: ''
|
||||||
|
labels: enhancement
|
||||||
|
assignees: nicoverbruggen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Enhancement requests that are not immediately approved will be moved to a discussion instead._
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
14
DEVELOPER.md
@ -13,6 +13,20 @@ Once you have downloaded this repository, open `PHP Monitor.xcodeproj`, and you
|
|||||||
|
|
||||||
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
If you'd like to create a production build, choose "Any Mac" as the target and select Product > Archive.
|
||||||
|
|
||||||
|
## 🚀 Release procedure
|
||||||
|
|
||||||
|
1. Merge into `main`
|
||||||
|
2. Create tag
|
||||||
|
3. Add changes to changelog + update security document
|
||||||
|
4. Archive
|
||||||
|
5. Notarize and prepare for own distribution
|
||||||
|
6. After notarization, export .app
|
||||||
|
7. Create zipped version
|
||||||
|
8. Calculate SHA256: `openssl dgst -sha256 phpmon.zip`
|
||||||
|
9. Upload to GitHub and add to tagged release
|
||||||
|
10. Update Cask with new version + hash
|
||||||
|
11. Check new version can be installed via Cask
|
||||||
|
|
||||||
## 🐛 Symbolication of crashes
|
## 🐛 Symbolication of crashes
|
||||||
|
|
||||||
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
If you have an archived build of the app and exported the DSYM, it is possible to symbolicate .ips crash logs.
|
||||||
|
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2019 Nico Verbruggen
|
Copyright (c) 2019-2022 Nico Verbruggen
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 52;
|
objectVersion = 50;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@ -11,6 +11,16 @@
|
|||||||
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
5420395F2613607600FB00FA /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5420395E2613607600FB00FA /* Preferences.swift */; };
|
||||||
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B5F275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
54B48B60275F66AE006D90C5 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B48B5E275F66AE006D90C5 /* Application.swift */; };
|
||||||
|
54D9E0B227E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
||||||
|
54D9E0B327E4F51E003B9AD9 /* HotKeysController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */; };
|
||||||
|
54D9E0B427E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; };
|
||||||
|
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AD27E4F51E003B9AD9 /* Key.swift */; };
|
||||||
|
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; };
|
||||||
|
54D9E0B727E4F51E003B9AD9 /* HotKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */; };
|
||||||
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; };
|
||||||
|
54D9E0B927E4F51E003B9AD9 /* KeyCombo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */; };
|
||||||
|
54D9E0BA27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; };
|
||||||
|
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */; };
|
||||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||||
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
54FCFD26276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
54FCFD27276C883F004CE748 /* SelectPreferenceView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */; };
|
||||||
@ -28,10 +38,13 @@
|
|||||||
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
C4068CA827B07A1300544CD5 /* SelectPreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */; };
|
||||||
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
C4068CAA27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||||
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
C4068CAB27B0890D00544CD5 /* MenuBarIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */; };
|
||||||
|
C4080FF627BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||||
|
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */; };
|
||||||
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||||
|
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */; };
|
||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
C40B24F427A310830018C7D2 /* StatusMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C47331A1247093B7009A0597 /* StatusMenu.swift */; };
|
||||||
C40B24F527A3108B0018C7D2 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
|
||||||
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1E2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F1D2772136000DDDCDC /* PhpEnv.swift */; };
|
||||||
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
C40C7F2827721FF600DDDCDC /* ActivePhpInstallation+Checks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */; };
|
||||||
@ -49,6 +62,10 @@
|
|||||||
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
C417DC75277614690015E6EE /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = C417DC73277614690015E6EE /* Helpers.swift */; };
|
||||||
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C4188989275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
C418898A275FE8CB001EF227 /* Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4188988275FE8CB001EF227 /* Filesystem.swift */; };
|
||||||
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A527E60D7A009F26CB /* SiteScanner.swift */; };
|
||||||
|
C41C02A927E61A65009F26CB /* ValetSite+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */; };
|
||||||
|
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A527E60D7A009F26CB /* SiteScanner.swift */; };
|
||||||
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */; };
|
||||||
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
C41C1B3722B0097F00E7CF16 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */; };
|
||||||
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; };
|
C41C1B3B22B0098000E7CF16 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3A22B0098000E7CF16 /* Assets.xcassets */; };
|
||||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||||
@ -62,11 +79,24 @@
|
|||||||
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
C4232EE52612526500158FC6 /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = C4232EE42612526500158FC6 /* Credits.html */; };
|
||||||
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 */; };
|
||||||
|
C42CFB1627DFDE7900862737 /* nicoverbruggen.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1527DFDE7900862737 /* nicoverbruggen.test */; };
|
||||||
|
C42CFB1827DFDFDC00862737 /* nicoverbruggen_isolated.test in Resources */ = {isa = PBXBuildFile; fileRef = C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */; };
|
||||||
|
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */; };
|
||||||
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.json in Resources */ = {isa = PBXBuildFile; fileRef = C43A8A1F25D9D1D700591B77 /* brew.json */; };
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; };
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */; };
|
||||||
|
C44067F527E2582B0045BD4E /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
||||||
|
C44067F727E258410045BD4E /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
||||||
|
C44067F927E2585E0045BD4E /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
||||||
|
C44067FB27E25FD70045BD4E /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
||||||
|
C449B4F027EE7FB800C47E8A /* SiteListTLSCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */; };
|
||||||
|
C449B4F127EE7FC200C47E8A /* SiteListNameCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F427E2582B0045BD4E /* SiteListNameCell.swift */; };
|
||||||
|
C449B4F227EE7FC400C47E8A /* SiteListPhpCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F627E258410045BD4E /* SiteListPhpCell.swift */; };
|
||||||
|
C449B4F327EE7FC600C47E8A /* SiteListTypeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */; };
|
||||||
|
C449B4F427EE7FC800C47E8A /* SiteListKindCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AC51FB27E27F47008528CA /* SiteListKindCell.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 */; };
|
||||||
@ -79,8 +109,7 @@
|
|||||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */; };
|
||||||
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADAF275A7A69003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||||
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
C464ADB0275A7A6A003FCD53 /* SiteListVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADAE275A7A69003FCD53 /* SiteListVC.swift */; };
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCell.swift */; };
|
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */; };
|
||||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCell.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 */; };
|
||||||
@ -102,17 +131,17 @@
|
|||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4927F0A27B2DFC200C55AFD /* Errors.swift */; };
|
||||||
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084A279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
C493084B279F331F009C240B /* AddSiteVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4930849279F331F009C240B /* AddSiteVC.swift */; };
|
||||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
|
|
||||||
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 */; };
|
||||||
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 /* ValetConfigParserTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.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 /* ValetTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetTest.swift */; };
|
C4AF9F7D275454A900D44ED0 /* ValetVersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */; };
|
||||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; };
|
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; };
|
||||||
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; };
|
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B5635D276AB09000F12CCB /* VersionExtractor.swift */; };
|
||||||
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; };
|
C4B56362276AB0A500F12CCB /* VersionExtractorTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */; };
|
||||||
@ -128,6 +157,9 @@
|
|||||||
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
C4B97B79275CF1B5003F3378 /* App+ActivationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */; };
|
||||||
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 */; };
|
||||||
|
C4C1019B27C65C6F001FACC2 /* 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 */; };
|
||||||
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
C4C3ED4327834C5200AB15D8 /* CustomPrefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C3ED4227834C5200AB15D8 /* CustomPrefs.swift */; };
|
||||||
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
C4C8E818276F54D8003AC782 /* App+ConfigWatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */; };
|
||||||
@ -137,19 +169,27 @@
|
|||||||
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6C275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CCBA6B275C567B008C7055 /* PMWindowController.swift */; };
|
||||||
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
C4CE3BB827B31F2E0086CA49 /* MainMenu+Switcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */; };
|
||||||
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
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 /* MainMenu+Composer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */; };
|
C4CE3BBC27B324250086CA49 /* ComposerWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */; };
|
||||||
|
C4D5CFCA27E0F9CD00035329 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */; };
|
||||||
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D5CFC927E0F9CD00035329 /* NginxConfigParser.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 */; };
|
||||||
|
C4D936CA27E3EB6100BD69FE /* PhpHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D936C827E3EB6100BD69FE /* PhpHelper.swift */; };
|
||||||
|
C4D936CB27E3EE4A00BD69FE /* SiteListCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.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 */; };
|
||||||
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 */; };
|
||||||
|
C4E0F7EE27BEBDA9007475F2 /* NSWindowExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */; };
|
||||||
|
C4E4404627C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||||
|
C4E4404727C56F4700D225E1 /* ValetSite.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4E4404527C56F4700D225E1 /* ValetSite.swift */; };
|
||||||
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
C4EC1E66279DE0380010F296 /* ServicesView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C4EC1E65279DE0380010F296 /* ServicesView.xib */; };
|
||||||
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
C4EC1E68279DE0540010F296 /* ServicesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E67279DE0540010F296 /* ServicesView.swift */; };
|
||||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E6C279DF87A0010F296 /* Async.swift */; };
|
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EC1E72279DFCF40010F296 /* Events.swift */; };
|
||||||
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
C4EE188422D3386B00E126E5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE188322D3386B00E126E5 /* Constants.swift */; };
|
||||||
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
C4EE55A927708B9E001DF387 /* PMHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE55A627708B9E001DF387 /* PMHeaderView.swift */; };
|
||||||
@ -208,6 +248,13 @@
|
|||||||
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>"; };
|
||||||
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>"; };
|
||||||
|
54D9E0AD27E4F51E003B9AD9 /* Key.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Key.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HotKey.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyCombo.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModifierFlagsExtension.swift; sourceTree = "<group>"; };
|
||||||
|
54D9E0BF27E4F5D9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||||
|
54D9E0C027E4F5E9003B9AD9 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
|
||||||
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD25276C883F004CE748 /* SelectPreferenceView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
54FCFD29276C8AA4004CE748 /* CheckboxPreferenceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
54FCFD2C276C8D67004CE748 /* HotkeyPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HotkeyPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
@ -217,6 +264,8 @@
|
|||||||
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
C4068CA327B0780A00544CD5 /* CheckboxPreferenceView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CheckboxPreferenceView.xib; sourceTree = "<group>"; };
|
||||||
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPreferenceView.swift; sourceTree = "<group>"; };
|
C4068CA627B07A1300544CD5 /* SelectPreferenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectPreferenceView.swift; sourceTree = "<group>"; };
|
||||||
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
C4068CA927B0890D00544CD5 /* MenuBarIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarIcons.swift; sourceTree = "<group>"; };
|
||||||
|
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlert.swift; sourceTree = "<group>"; };
|
||||||
|
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BetterAlertVC.swift; sourceTree = "<group>"; };
|
||||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpEnv.swift; sourceTree = "<group>"; };
|
||||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActivePhpInstallation+Checks.swift"; sourceTree = "<group>"; };
|
||||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
C40C7F2F27722E8D00DDDCDC /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
|
||||||
@ -227,6 +276,8 @@
|
|||||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = DEVELOPER.md; sourceTree = "<group>"; };
|
||||||
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
C417DC73277614690015E6EE /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
|
||||||
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
C4188988275FE8CB001EF227 /* Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filesystem.swift; sourceTree = "<group>"; };
|
||||||
|
C41C02A527E60D7A009F26CB /* SiteScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteScanner.swift; sourceTree = "<group>"; };
|
||||||
|
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ValetSite+Fake.swift"; sourceTree = "<group>"; };
|
||||||
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "PHP Monitor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
C41C1B3622B0097F00E7CF16 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
C41C1B3A22B0098000E7CF16 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -240,17 +291,25 @@
|
|||||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+ContextMenu.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>"; };
|
||||||
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>"; };
|
||||||
|
C42CFB1527DFDE7900862737 /* nicoverbruggen.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen.test; sourceTree = "<group>"; };
|
||||||
|
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nicoverbruggen_isolated.test; sourceTree = "<group>"; };
|
||||||
|
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NginxConfigParserTest.swift; 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.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = brew.json; sourceTree = "<group>"; };
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewJsonParserTest.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListNameCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F627E258410045BD4E /* SiteListPhpCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListPhpCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTypeCell.swift; sourceTree = "<group>"; };
|
||||||
|
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SiteListTLSCell.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>"; };
|
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListWC.swift; sourceTree = "<group>"; };
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListVC.swift; sourceTree = "<group>"; };
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCell.swift; sourceTree = "<group>"; };
|
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SiteListCellProtocol.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>"; };
|
||||||
@ -269,11 +328,12 @@
|
|||||||
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>"; };
|
||||||
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 /* ValetConfigParserTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetConfigParserTest.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 /* ValetTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetTest.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>"; };
|
||||||
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionExtractorTest.swift; sourceTree = "<group>"; };
|
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionExtractorTest.swift; sourceTree = "<group>"; };
|
||||||
C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
|
C4B5853B2770FE3900DA4FBE /* Paths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
|
||||||
@ -282,23 +342,27 @@
|
|||||||
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>"; };
|
||||||
|
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>"; };
|
||||||
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
C4C8E817276F54D8003AC782 /* App+ConfigWatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "App+ConfigWatch.swift"; sourceTree = "<group>"; };
|
||||||
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
C4C8E81A276F54E5003AC782 /* PhpConfigWatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhpConfigWatcher.swift; sourceTree = "<group>"; };
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PMWindowController.swift; sourceTree = "<group>"; };
|
||||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Switcher.swift"; sourceTree = "<group>"; };
|
||||||
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MainMenu+Composer.swift"; sourceTree = "<group>"; };
|
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>"; };
|
||||||
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>"; };
|
||||||
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>"; };
|
||||||
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>"; };
|
||||||
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ValetSite.swift; sourceTree = "<group>"; };
|
||||||
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
C4E713562570150F00007428 /* SECURITY.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = SECURITY.md; sourceTree = "<group>"; };
|
||||||
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
|
C4E713572570151400007428 /* docs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = docs; sourceTree = "<group>"; };
|
||||||
C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = "<group>"; };
|
C4EC1E65279DE0380010F296 /* ServicesView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ServicesView.xib; sourceTree = "<group>"; };
|
||||||
C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
|
C4EC1E67279DE0540010F296 /* ServicesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServicesView.swift; sourceTree = "<group>"; };
|
||||||
C4EC1E6C279DF87A0010F296 /* Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = "<group>"; };
|
|
||||||
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
|
C4EC1E72279DFCF40010F296 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
|
||||||
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
C4EE188322D3386B00E126E5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = "<group>"; };
|
C4EE55A627708B9E001DF387 /* PMHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PMHeaderView.swift; sourceTree = "<group>"; };
|
||||||
@ -309,7 +373,6 @@
|
|||||||
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>"; };
|
||||||
C4F7807425D7F7E5000DBC97 /* RELEASE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = RELEASE.md; 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>"; };
|
||||||
@ -325,7 +388,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
C4998F0626175E7200B2526E /* HotKey in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -367,6 +429,27 @@
|
|||||||
path = PHP;
|
path = PHP;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
54D9E0AB27E4F502003B9AD9 /* HotKey */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
54D9E0BF27E4F5D9003B9AD9 /* LICENSE */,
|
||||||
|
54D9E0AE27E4F51E003B9AD9 /* HotKey.swift */,
|
||||||
|
54D9E0AC27E4F51E003B9AD9 /* HotKeysController.swift */,
|
||||||
|
54D9E0AD27E4F51E003B9AD9 /* Key.swift */,
|
||||||
|
54D9E0AF27E4F51E003B9AD9 /* KeyCombo.swift */,
|
||||||
|
54D9E0B027E4F51E003B9AD9 /* ModifierFlagsExtension.swift */,
|
||||||
|
);
|
||||||
|
path = HotKey;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
54D9E0BE27E4F5C0003B9AD9 /* Vendor */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
54D9E0AB27E4F502003B9AD9 /* HotKey */,
|
||||||
|
);
|
||||||
|
path = Vendor;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
54FCFD28276C88C0004CE748 /* Views */ = {
|
54FCFD28276C88C0004CE748 /* Views */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -389,9 +472,20 @@
|
|||||||
path = IAP;
|
path = IAP;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4080FF827BD955900BF2C6B /* Notice */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4080FF527BD8C6400BF2C6B /* BetterAlert.swift */,
|
||||||
|
C4080FF927BD956700BF2C6B /* BetterAlertVC.swift */,
|
||||||
|
);
|
||||||
|
path = Notice;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
C40C7F1C27720E1400DDDCDC /* Test Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C42CFB1527DFDE7900862737 /* nicoverbruggen.test */,
|
||||||
|
C42CFB1727DFDFDC00862737 /* nicoverbruggen_isolated.test */,
|
||||||
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
C4AF9F70275445FF00D44ED0 /* valet-config.json */,
|
||||||
C43A8A1F25D9D1D700591B77 /* brew.json */,
|
C43A8A1F25D9D1D700591B77 /* brew.json */,
|
||||||
C4F30B06278E195800755FCE /* brew-services.json */,
|
C4F30B06278E195800755FCE /* brew-services.json */,
|
||||||
@ -409,6 +503,7 @@
|
|||||||
C4B5853D2770FE3900DA4FBE /* Command.swift */,
|
C4B5853D2770FE3900DA4FBE /* Command.swift */,
|
||||||
C4B5853B2770FE3900DA4FBE /* Paths.swift */,
|
C4B5853B2770FE3900DA4FBE /* Paths.swift */,
|
||||||
C4B5853C2770FE3900DA4FBE /* Shell.swift */,
|
C4B5853C2770FE3900DA4FBE /* Shell.swift */,
|
||||||
|
C4C1019A27C65C6F001FACC2 /* Process.swift */,
|
||||||
C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
|
C40C7F2F27722E8D00DDDCDC /* Logger.swift */,
|
||||||
C417DC73277614690015E6EE /* Helpers.swift */,
|
C417DC73277614690015E6EE /* Helpers.swift */,
|
||||||
);
|
);
|
||||||
@ -420,8 +515,8 @@
|
|||||||
children = (
|
children = (
|
||||||
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
C4F8C0A522D4FA41002EFE61 /* README.md */,
|
||||||
C4E713562570150F00007428 /* SECURITY.md */,
|
C4E713562570150F00007428 /* SECURITY.md */,
|
||||||
C4F7807425D7F7E5000DBC97 /* RELEASE.md */,
|
|
||||||
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
C4168F4427ADB4A3003B6C39 /* DEVELOPER.md */,
|
||||||
|
54D9E0C027E4F5E9003B9AD9 /* LICENSE */,
|
||||||
C4E713572570151400007428 /* docs */,
|
C4E713572570151400007428 /* docs */,
|
||||||
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
C41C1B3522B0097F00E7CF16 /* phpmon */,
|
||||||
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
C4F7807A25D7F84B000DBC97 /* phpmon-tests */,
|
||||||
@ -444,6 +539,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C4B5853A2770FE2500DA4FBE /* Common */,
|
C4B5853A2770FE2500DA4FBE /* Common */,
|
||||||
C41E181722CB61EB0072CF09 /* Domain */,
|
C41E181722CB61EB0072CF09 /* Domain */,
|
||||||
|
54D9E0BE27E4F5C0003B9AD9 /* Vendor */,
|
||||||
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
C41C1B3F22B0098000E7CF16 /* Info.plist */,
|
||||||
C4232EE42612526500158FC6 /* Credits.html */,
|
C4232EE42612526500158FC6 /* Credits.html */,
|
||||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */,
|
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */,
|
||||||
@ -465,6 +561,7 @@
|
|||||||
C41E181722CB61EB0072CF09 /* Domain */ = {
|
C41E181722CB61EB0072CF09 /* Domain */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C4080FF827BD955900BF2C6B /* Notice */,
|
||||||
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
||||||
C4B13B1D25C4915000548C3A /* App */,
|
C4B13B1D25C4915000548C3A /* App */,
|
||||||
C4D9ADBD27761084007277F4 /* PHP */,
|
C4D9ADBD27761084007277F4 /* PHP */,
|
||||||
@ -478,6 +575,19 @@
|
|||||||
path = Domain;
|
path = Domain;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C44067F327E256560045BD4E /* Cells */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C464ADB1275A87CA003FCD53 /* SiteListCellProtocol.swift */,
|
||||||
|
C44067FA27E25FD70045BD4E /* SiteListTLSCell.swift */,
|
||||||
|
C44067F427E2582B0045BD4E /* SiteListNameCell.swift */,
|
||||||
|
C44067F627E258410045BD4E /* SiteListPhpCell.swift */,
|
||||||
|
C44067F827E2585E0045BD4E /* SiteListTypeCell.swift */,
|
||||||
|
C4AC51FB27E27F47008528CA /* SiteListKindCell.swift */,
|
||||||
|
);
|
||||||
|
path = Cells;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C44C198F276E3A380072762D /* Progress */ = {
|
C44C198F276E3A380072762D /* Progress */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -499,12 +609,12 @@
|
|||||||
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
C464ADAA275A7A25003FCD53 /* SiteList */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C44067F327E256560045BD4E /* Cells */,
|
||||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
||||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
||||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
||||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
||||||
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
C4930849279F331F009C240B /* AddSiteVC.swift */,
|
||||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */,
|
|
||||||
);
|
);
|
||||||
path = SiteList;
|
path = SiteList;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -513,10 +623,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
C4811D2922D70F9A00B5F6B3 /* MainMenu.swift */,
|
||||||
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
|
||||||
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
C4C3ED402783497000AB15D8 /* MainMenu+Startup.swift */,
|
||||||
|
C44CCD4827AFF3B700CE40E5 /* MainMenu+Async.swift */,
|
||||||
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
C4CE3BB727B31F2E0086CA49 /* MainMenu+Switcher.swift */,
|
||||||
C4CE3BB927B31F670086CA49 /* MainMenu+Composer.swift */,
|
C42C49DA27C2806F0074ABAC /* MainMenu+FixMyValet.swift */,
|
||||||
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
C47331A1247093B7009A0597 /* StatusMenu.swift */,
|
||||||
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
C48D0C9525CC80B100CC7490 /* HeaderView.swift */,
|
||||||
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
C48D0C9925CC888B00CC7490 /* HeaderView.xib */,
|
||||||
@ -538,7 +648,6 @@
|
|||||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */,
|
||||||
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
C4CCBA6B275C567B008C7055 /* PMWindowController.swift */,
|
||||||
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
|
C4B5635D276AB09000F12CCB /* VersionExtractor.swift */,
|
||||||
C4EC1E6C279DF87A0010F296 /* Async.swift */,
|
|
||||||
);
|
);
|
||||||
path = Helpers;
|
path = Helpers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -548,6 +657,7 @@
|
|||||||
children = (
|
children = (
|
||||||
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
C40C7F1D2772136000DDDCDC /* PhpEnv.swift */,
|
||||||
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */,
|
C48D6C6F279CD2AC00F26D7E /* PhpVersionNumber.swift */,
|
||||||
|
C4D936C827E3EB6100BD69FE /* PhpHelper.swift */,
|
||||||
);
|
);
|
||||||
path = "PHP Version";
|
path = "PHP Version";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -556,6 +666,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
C4AF9F792754499000D44ED0 /* Valet.swift */,
|
||||||
|
C4E4404527C56F4700D225E1 /* ValetSite.swift */,
|
||||||
|
C41C02A827E61A65009F26CB /* ValetSite+Fake.swift */,
|
||||||
|
C41C02A527E60D7A009F26CB /* SiteScanner.swift */,
|
||||||
|
C4D5CFC927E0F9CD00035329 /* NginxConfigParser.swift */,
|
||||||
);
|
);
|
||||||
path = Valet;
|
path = Valet;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -589,8 +703,8 @@
|
|||||||
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
C4811D2322D70A4700B5F6B3 /* App.swift */,
|
||||||
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */,
|
C4B97B77275CF1B5003F3378 /* App+ActivationPolicy.swift */,
|
||||||
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
C4B97B7A275CF20A003F3378 /* App+GlobalHotkey.swift */,
|
||||||
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
|
||||||
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
C4EED88827A48778006D7272 /* InterAppHandler.swift */,
|
||||||
|
C4D8016522B1584700C6DA1B /* Startup.swift */,
|
||||||
);
|
);
|
||||||
path = App;
|
path = App;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -607,6 +721,36 @@
|
|||||||
path = Common;
|
path = Common;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
C4C1019727C65A11001FACC2 /* Parsers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
||||||
|
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
||||||
|
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
||||||
|
C42CFB1927DFE8BD00862737 /* NginxConfigParserTest.swift */,
|
||||||
|
);
|
||||||
|
path = Parsers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C1019827C65A1A001FACC2 /* Versions */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */,
|
||||||
|
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */,
|
||||||
|
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */,
|
||||||
|
C4AF9F7C275454A900D44ED0 /* ValetVersionExtractorTest.swift */,
|
||||||
|
);
|
||||||
|
path = Versions;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
C4C1019927C65A4D001FACC2 /* Commands */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
C4F7809B25D80344000DBC97 /* CommandTest.swift */,
|
||||||
|
);
|
||||||
|
path = Commands;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
C4C8E81D276F5686003AC782 /* Watcher */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -626,6 +770,7 @@
|
|||||||
C4D89BC42783C98800A02B68 /* Composer */ = {
|
C4D89BC42783C98800A02B68 /* Composer */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
C4CE3BB927B31F670086CA49 /* ComposerWindow.swift */,
|
||||||
C4D89BC52783C99400A02B68 /* ComposerJson.swift */,
|
C4D89BC52783C99400A02B68 /* ComposerJson.swift */,
|
||||||
C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */,
|
C415937E27A1B54F00D2E1B7 /* PhpFrameworks.swift */,
|
||||||
);
|
);
|
||||||
@ -673,16 +818,11 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
C4F7807D25D7F84B000DBC97 /* Info.plist */,
|
C4F7807D25D7F84B000DBC97 /* Info.plist */,
|
||||||
C40C7F1C27720E1400DDDCDC /* Test Files */,
|
|
||||||
C4F7809B25D80344000DBC97 /* CommandTest.swift */,
|
|
||||||
C4F780AD25D80B37000DBC97 /* ExtensionParserTest.swift */,
|
|
||||||
C43A8A2325D9D20D00591B77 /* BrewJsonParserTest.swift */,
|
|
||||||
C4FBFC512616485F00CDB8E1 /* PhpVersionDetectionTest.swift */,
|
|
||||||
C48D6C73279CD3E400F26D7E /* PhpVersionNumberTest.swift */,
|
|
||||||
C43A8A1925D9CD1000591B77 /* Utility.swift */,
|
C43A8A1925D9CD1000591B77 /* Utility.swift */,
|
||||||
C4AF9F76275447F100D44ED0 /* ValetConfigParserTest.swift */,
|
C40C7F1C27720E1400DDDCDC /* Test Files */,
|
||||||
C4AF9F7C275454A900D44ED0 /* ValetTest.swift */,
|
C4C1019927C65A4D001FACC2 /* Commands */,
|
||||||
C4B56360276AB0A500F12CCB /* VersionExtractorTest.swift */,
|
C4C1019827C65A1A001FACC2 /* Versions */,
|
||||||
|
C4C1019727C65A11001FACC2 /* Parsers */,
|
||||||
);
|
);
|
||||||
path = "phpmon-tests";
|
path = "phpmon-tests";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -694,6 +834,7 @@
|
|||||||
C46FA23E246C358E00944F05 /* StringExtension.swift */,
|
C46FA23E246C358E00944F05 /* StringExtension.swift */,
|
||||||
C48D0C9225CC804200CC7490 /* XibLoadable.swift */,
|
C48D0C9225CC804200CC7490 /* XibLoadable.swift */,
|
||||||
C42759662627662800093CAE /* NSMenuExtension.swift */,
|
C42759662627662800093CAE /* NSMenuExtension.swift */,
|
||||||
|
C4E0F7EC27BEBDA9007475F2 /* NSWindowExtension.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -715,7 +856,6 @@
|
|||||||
);
|
);
|
||||||
name = "PHP Monitor";
|
name = "PHP Monitor";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
C4998F0526175E7200B2526E /* HotKey */,
|
|
||||||
);
|
);
|
||||||
productName = phpmon;
|
productName = phpmon;
|
||||||
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
|
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
|
||||||
@ -735,6 +875,8 @@
|
|||||||
C4F7807F25D7F84B000DBC97 /* PBXTargetDependency */,
|
C4F7807F25D7F84B000DBC97 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = "phpmon-tests";
|
name = "phpmon-tests";
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
productName = "phpmon-tests";
|
productName = "phpmon-tests";
|
||||||
productReference = C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */;
|
productReference = C4F7807925D7F84B000DBC97 /* phpmon-tests.xctest */;
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
@ -754,7 +896,6 @@
|
|||||||
};
|
};
|
||||||
C4F7807825D7F84B000DBC97 = {
|
C4F7807825D7F84B000DBC97 = {
|
||||||
CreatedOnToolsVersion = 12.4;
|
CreatedOnToolsVersion = 12.4;
|
||||||
TestTargetID = C41C1B3222B0097F00E7CF16;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -768,7 +909,6 @@
|
|||||||
);
|
);
|
||||||
mainGroup = C41C1B2A22B0097F00E7CF16;
|
mainGroup = C41C1B2A22B0097F00E7CF16;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */,
|
|
||||||
);
|
);
|
||||||
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
|
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -809,12 +949,14 @@
|
|||||||
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 */,
|
||||||
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.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 */,
|
||||||
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
C4F30B08278E195800755FCE /* brew-services.json in Resources */,
|
||||||
|
C42CFB1627DFDE7900862737 /* nicoverbruggen.test in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -827,6 +969,7 @@
|
|||||||
files = (
|
files = (
|
||||||
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
|
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
|
||||||
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
|
||||||
|
C42C49DB27C2806F0074ABAC /* MainMenu+FixMyValet.swift in Sources */,
|
||||||
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C70279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
|
C4B585412770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||||
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
|
||||||
@ -835,23 +978,31 @@
|
|||||||
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 */,
|
||||||
|
C4080FF627BD8C6400BF2C6B /* BetterAlert.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 */,
|
||||||
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 */,
|
||||||
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 */,
|
C41E871A2763D42300161EE0 /* SiteListVC+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 */,
|
||||||
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4372752F0870020E974 /* HomebrewDiagnostics.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 */,
|
||||||
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 */,
|
||||||
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
C415D3B72770F294005EF286 /* Actions.swift in Sources */,
|
||||||
|
C4AC51FC27E27F47008528CA /* SiteListKindCell.swift in Sources */,
|
||||||
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
C44C198D276E3A1C0072762D /* ProgressWindow.swift in Sources */,
|
||||||
|
54D9E0B827E4F51E003B9AD9 /* KeyCombo.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 */,
|
||||||
@ -860,9 +1011,11 @@
|
|||||||
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
C4811D2422D70A4700B5F6B3 /* App.swift in Sources */,
|
||||||
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 */,
|
||||||
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 */,
|
||||||
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 /* SiteListVC+Actions.swift in Sources */,
|
||||||
@ -875,9 +1028,10 @@
|
|||||||
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 /* SiteListVC.swift in Sources */,
|
||||||
C4EC1E6D279DF87A0010F296 /* Async.swift in Sources */,
|
|
||||||
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
C44CCD4927AFF3B700CE40E5 /* MainMenu+Async.swift in Sources */,
|
||||||
|
C4C1019B27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
C4EC1E73279DFCF40010F296 /* Events.swift in Sources */,
|
||||||
|
C44067FB27E25FD70045BD4E /* SiteListTLSCell.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 */,
|
||||||
@ -889,16 +1043,23 @@
|
|||||||
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
C476FF9822B0DD830098105B /* Alert.swift in Sources */,
|
||||||
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
C474B00624C0E98C00066A22 /* LocalNotification.swift in Sources */,
|
||||||
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */,
|
||||||
C4CE3BBA27B31F670086CA49 /* MainMenu+Composer.swift in Sources */,
|
C4D5CFCA27E0F9CD00035329 /* NginxConfigParser.swift in Sources */,
|
||||||
|
C4CE3BBA27B31F670086CA49 /* ComposerWindow.swift in Sources */,
|
||||||
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
C4D9ADC8277611A0007277F4 /* InternalSwitcher.swift in Sources */,
|
||||||
|
C4080FFA27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635E276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
|
54D9E0B627E4F51E003B9AD9 /* HotKey.swift in Sources */,
|
||||||
|
C4D936C927E3EB6100BD69FE /* PhpHelper.swift in Sources */,
|
||||||
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
C47331A2247093B7009A0597 /* StatusMenu.swift in Sources */,
|
||||||
|
C44067F927E2585E0045BD4E /* SiteListTypeCell.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 */,
|
||||||
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
C4B97B75275CF08C003F3378 /* AppDelegate+MenuOutlets.swift in Sources */,
|
||||||
|
C41C02A627E60D7A009F26CB /* SiteScanner.swift in Sources */,
|
||||||
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
C464ADAC275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
||||||
C464ADB2275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
C464ADB2275A87CA003FCD53 /* SiteListCellProtocol.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 */,
|
||||||
@ -909,9 +1070,11 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
C449B4F427EE7FC800C47E8A /* SiteListKindCell.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 /* SiteListVC+Actions.swift in Sources */,
|
||||||
|
54D9E0B727E4F51E003B9AD9 /* HotKey.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 */,
|
||||||
@ -919,29 +1082,38 @@
|
|||||||
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 */,
|
||||||
|
C41C02AA27E61CA3009F26CB /* SiteScanner.swift in Sources */,
|
||||||
|
C4080FFB27BD956700BF2C6B /* BetterAlertVC.swift in Sources */,
|
||||||
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
C4F780CC25D80B75000DBC97 /* ActivePhpInstallation.swift in Sources */,
|
||||||
|
54D9E0BB27E4F51E003B9AD9 /* ModifierFlagsExtension.swift in Sources */,
|
||||||
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
C4F780B125D80B4D000DBC97 /* PhpExtension.swift in Sources */,
|
||||||
|
C4D5CFCB27E0F9CD00035329 /* NginxConfigParser.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 */,
|
||||||
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
C4FBFC532616485F00CDB8E1 /* PhpVersionDetectionTest.swift in Sources */,
|
||||||
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
C43A8A2425D9D20D00591B77 /* BrewJsonParserTest.swift in Sources */,
|
||||||
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
C4F780CA25D80B75000DBC97 /* HomebrewPackage.swift in Sources */,
|
||||||
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
C4C8E81C276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||||
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
C4F319C927B034A500AFF46F /* Stats.swift in Sources */,
|
||||||
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
C4F30B04278E16BA00755FCE /* HomebrewService.swift in Sources */,
|
||||||
|
54D9E0B527E4F51E003B9AD9 /* Key.swift in Sources */,
|
||||||
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
C4AF9F7B2754499000D44ED0 /* Valet.swift in Sources */,
|
||||||
|
C4C1019C27C65C6F001FACC2 /* Process.swift in Sources */,
|
||||||
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
C4F780C025D80B6E000DBC97 /* Startup.swift in Sources */,
|
||||||
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
C4CCBA6D275C567B008C7055 /* PMWindowController.swift in Sources */,
|
||||||
C40B24F527A3108B0018C7D2 /* Async.swift in Sources */,
|
|
||||||
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
C4B5635F276AB09000F12CCB /* VersionExtractor.swift in Sources */,
|
||||||
|
C4BF90C127C57C220054E78C /* MainMenu+FixMyValet.swift in Sources */,
|
||||||
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
C4F2E4382752F08D0020E974 /* HomebrewDiagnostics.swift in Sources */,
|
||||||
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
C4F780AE25D80B37000DBC97 /* ExtensionParserTest.swift in Sources */,
|
||||||
C4C8E819276F54D8003AC782 /* App+ConfigWatch.swift in Sources */,
|
C4C8E819276F54D8003AC782 /* App+ConfigWatch.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 */,
|
||||||
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 */,
|
||||||
@ -949,17 +1121,20 @@
|
|||||||
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 */,
|
||||||
C464ADB3275A87CA003FCD53 /* SiteListCell.swift in Sources */,
|
|
||||||
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
C415D3E92770F692005EF286 /* AppDelegate+InterApp.swift in Sources */,
|
||||||
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
C4AF9F78275447F100D44ED0 /* ValetConfigParserTest.swift in Sources */,
|
||||||
C4CE3BBC27B324250086CA49 /* MainMenu+Composer.swift in Sources */,
|
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 */,
|
||||||
|
C4080FF727BD8C6400BF2C6B /* BetterAlert.swift in Sources */,
|
||||||
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.swift in Sources */,
|
C4B97B7C275CF20A003F3378 /* App+GlobalHotkey.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 */,
|
||||||
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 */,
|
||||||
|
C449B4F127EE7FC200C47E8A /* SiteListNameCell.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 */,
|
||||||
@ -969,21 +1144,27 @@
|
|||||||
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 */,
|
||||||
|
C42CFB1A27DFE8BD00862737 /* NginxConfigParserTest.swift in Sources */,
|
||||||
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
C4F30B0B278E203C00755FCE /* MainMenu+Startup.swift in Sources */,
|
||||||
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
C40B24F227A310770018C7D2 /* Events.swift in Sources */,
|
||||||
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
C4F30B0A278E1A1A00755FCE /* ComposerJson.swift in Sources */,
|
||||||
C4AF9F7D275454A900D44ED0 /* ValetTest.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 */,
|
||||||
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
C40B24F127A3106D0018C7D2 /* ServicesView.swift in Sources */,
|
||||||
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
C4F780C525D80B75000DBC97 /* MenuBarImageGenerator.swift in Sources */,
|
||||||
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
C4F780B725D80B5D000DBC97 /* App.swift in Sources */,
|
||||||
C4927F0C27B2DFC200C55AFD /* Errors.swift in Sources */,
|
C4927F0C27B2DFC200C55AFD /* Errors.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 */,
|
||||||
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
C48D6C71279CD2AC00F26D7E /* PhpVersionNumber.swift in Sources */,
|
||||||
|
C41C02AB27E61CB3009F26CB /* ValetSite+Fake.swift in Sources */,
|
||||||
C4F780C925D80B75000DBC97 /* StringExtension.swift in Sources */,
|
C4F780C925D80B75000DBC97 /* StringExtension.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 */,
|
||||||
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
C4B585422770FE3900DA4FBE /* Shell.swift in Sources */,
|
||||||
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
C464ADAD275A7A3F003FCD53 /* SiteListWC.swift in Sources */,
|
||||||
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
C40C7F1F2772136000DDDCDC /* PhpEnv.swift in Sources */,
|
||||||
@ -1143,7 +1324,8 @@
|
|||||||
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 = 610;
|
CURRENT_PROJECT_VERSION = 761;
|
||||||
|
DEBUG = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@ -1152,7 +1334,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.0.1;
|
MARKETING_VERSION = 5.2.2;
|
||||||
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 = "";
|
||||||
@ -1168,7 +1350,8 @@
|
|||||||
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 = 610;
|
CURRENT_PROJECT_VERSION = 761;
|
||||||
|
DEBUG = NO;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
INFOPLIST_FILE = phpmon/Info.plist;
|
INFOPLIST_FILE = phpmon/Info.plist;
|
||||||
@ -1177,7 +1360,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||||
MARKETING_VERSION = 5.0.1;
|
MARKETING_VERSION = 5.2.2;
|
||||||
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 = "";
|
||||||
@ -1188,7 +1371,6 @@
|
|||||||
C4F7808125D7F84B000DBC97 /* Debug */ = {
|
C4F7808125D7F84B000DBC97 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
@ -1202,14 +1384,12 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PHP Monitor.app/Contents/MacOS/PHP Monitor";
|
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
C4F7808225D7F84B000DBC97 /* Release */ = {
|
C4F7808225D7F84B000DBC97 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = 8M54J5J787;
|
DEVELOPMENT_TEAM = 8M54J5J787;
|
||||||
@ -1223,7 +1403,6 @@
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
PRODUCT_BUNDLE_IDENTIFIER = "com.nicoverbruggen.phpmon-tests";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PHP Monitor.app/Contents/MacOS/PHP Monitor";
|
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@ -1258,25 +1437,6 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
|
||||||
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */ = {
|
|
||||||
isa = XCRemoteSwiftPackageReference;
|
|
||||||
repositoryURL = "https://github.com/soffes/HotKey";
|
|
||||||
requirement = {
|
|
||||||
kind = upToNextMinorVersion;
|
|
||||||
minimumVersion = 0.1.3;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
/* End XCRemoteSwiftPackageReference section */
|
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
|
||||||
C4998F0526175E7200B2526E /* HotKey */ = {
|
|
||||||
isa = XCSwiftPackageProductDependency;
|
|
||||||
package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */;
|
|
||||||
productName = HotKey;
|
|
||||||
};
|
|
||||||
/* End XCSwiftPackageProductDependency section */
|
|
||||||
};
|
};
|
||||||
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
|
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,13 @@
|
|||||||
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
ReferencedContainer = "container:PHP Monitor.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<EnvironmentVariables>
|
||||||
|
<EnvironmentVariable
|
||||||
|
key = "PHPMON_MARKETING_MODE"
|
||||||
|
value = "YES"
|
||||||
|
isEnabled = "NO">
|
||||||
|
</EnvironmentVariable>
|
||||||
|
</EnvironmentVariables>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
91
README.md
@ -1,17 +1,12 @@
|
|||||||
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project.
|
> If this software has been useful to you, I ask that you **please star the repository**, that way I know that the software is being used. Also, please consider leaving [a one-time donation](https://nicoverbruggen.be/sponsor) to support the project, as this is something I make in my free time. **Thank you!** ⭐️
|
||||||
> You can also send me [feedback](https://twitter.com/nicoverbruggen) if the app came in handy.<br>**Thank you!** ⭐️
|
|
||||||
|
|
||||||
<h1 align="center"><b>PHP Monitor</b> (phpmon)</h1>
|
<p align="center"><img src="./docs/logo.png" alt="PHP Monitor Logo" width="500px" /></p>
|
||||||
|
|
||||||
<p align="center">
|
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this app</u> (consult the FAQ below with info about how to set up your environment).
|
||||||
<img src="./phpmon/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png" alt="phpmon icon" width="128px" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
**PHP Monitor** (or *phpmon*) is a lightweight macOS utility app that runs on your Mac and displays the active PHP version in your status bar. It's tightly integrated with [Laravel Valet](https://github.com/laravel/valet), so <u>you need to have it set up before you can use this</u>.
|
<img src="./docs/screenshot.jpg" width="1085px" alt="phpmon screenshot (menu bar app)"/>
|
||||||
|
|
||||||
<img src="./docs/screenshot50.jpg" width="1085px" alt="phpmon screenshot (menu bar app)"/>
|
<small><i>Screenshot: Showing the key functionality of PHP Monitor.</i></small>
|
||||||
|
|
||||||
<small><i>Screenshot: Showing the key functionality of PHP Monitor. You can also add new domains as links, manage various services, and perform First Aid to fix all kinds of common PHP link issues.</i></small>
|
|
||||||
|
|
||||||
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
|
It's super convenient to switch between different versions of PHP. You'll even get notifications (only if you choose to opt-in, of course)!
|
||||||
|
|
||||||
@ -19,16 +14,19 @@ It's super convenient to switch between different versions of PHP. You'll even g
|
|||||||
|
|
||||||
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
|
PHP Monitor also gives you quick access to various useful functionality (like accessing configuration files, restarting services, and more).
|
||||||
|
|
||||||
|
You can also add new domains as links, isolate sites, manage various services, and perform First Aid to fix all kinds of common PHP link issues.
|
||||||
|
|
||||||
## 🖥 System requirements
|
## 🖥 System requirements
|
||||||
|
|
||||||
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
|
PHP Monitor is a universal application that runs natively on Apple Silicon **and** Intel-based Macs.
|
||||||
|
|
||||||
|
* Your user account can administer your computer (required for some functionality, e.g. certificate generation)
|
||||||
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
* macOS 11 Big Sur or higher (supports macOS 12 Monterey)
|
||||||
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
* Homebrew is installed in `/usr/local/homebrew` or `/opt/homebrew`
|
||||||
* The brew formula `php` has to be installed (which version is detected)
|
* Homebrew `php` formula is installed
|
||||||
* Laravel Valet 2.16.2 or higher (older versions might be compatible but are not supported)
|
* Laravel Valet 2.16 or newer (supports Valet 3)
|
||||||
|
|
||||||
_You may need to update your Valet installation to keep everything working if a major version update of PHP has been released. You can do this by running `composer global update && valet install`._
|
_You may need to update your Valet installation to keep everything working if a major version update of PHP has been released. You can do this by running `composer global update && valet install`. Some features are not supported when running Valet 2._
|
||||||
|
|
||||||
## 🚀 How to install
|
## 🚀 How to install
|
||||||
|
|
||||||
@ -43,7 +41,13 @@ To upgrade your existing installation, run:
|
|||||||
|
|
||||||
brew upgrade phpmon
|
brew upgrade phpmon
|
||||||
|
|
||||||
(You may need to run `brew update` first in order to update the cask file if you ran a Homebrew operation recently.)
|
(You may need to run `brew update` or `brew update-reset` first in order to update the cask file if you ran a Homebrew operation recently.)
|
||||||
|
|
||||||
|
## ⚡️ Launchers (Alfred, Raycast)
|
||||||
|
|
||||||
|
If you would like to integrate with your launcher of choice, you can also download an [Alfred workflow](https://github.com/nicoverbruggen/phpmon/raw/main/integrations/phpmon.alfredworkflow) or [Raycast extension](https://www.raycast.com/nicoverbruggen/php-monitor) that works with PHP Monitor.
|
||||||
|
|
||||||
|
The app must be running in the background for these to work, and the _Allow third-party integrations_ checkbox must be enabled in Preferences (it is by default).
|
||||||
|
|
||||||
## 🔑 Is the app signed & notarized?
|
## 🔑 Is the app signed & notarized?
|
||||||
|
|
||||||
@ -73,7 +77,7 @@ If you're still having issues, here's a few common questions & answers, as well
|
|||||||
<summary><strong>Which versions of PHP are supported?</strong></summary>
|
<summary><strong>Which versions of PHP are supported?</strong></summary>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>PHP 5.6</li>
|
<li>PHP 5.6 (only if you are running Valet 2)</li>
|
||||||
<li>PHP 7.0</li>
|
<li>PHP 7.0</li>
|
||||||
<li>PHP 7.1</li>
|
<li>PHP 7.1</li>
|
||||||
<li>PHP 7.2</li>
|
<li>PHP 7.2</li>
|
||||||
@ -84,7 +88,7 @@ If you're still having issues, here's a few common questions & answers, as well
|
|||||||
<li>PHP 8.2 (experimental)</li>
|
<li>PHP 8.2 (experimental)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Constants.swift#L16) file to see which versions are supported.
|
For more details, consult the [constants file](https://github.com/nicoverbruggen/phpmon/blob/main/phpmon/Common/Core/Constants.swift#L16) file to see which versions are supported.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
@ -145,6 +149,29 @@ 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>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>
|
||||||
|
|
||||||
|
It's easy to make a mistake here, and end up with an unlinked version of PHP or have versions missing from PHP Monitor.
|
||||||
|
|
||||||
|
Here's what I usually do:
|
||||||
|
|
||||||
|
* Open PHP Monitor and select **First Aid & Services** > **Restore Homebrew Permissions**.
|
||||||
|
* Close PHP Monitor after the pop-up tells you the permissions were restored.
|
||||||
|
* Run `brew update-reset`
|
||||||
|
* Run `brew upgrade`
|
||||||
|
|
||||||
|
If after this, any PHP versions are missing in PHP Monitor, please run the following for the versions that are missing:
|
||||||
|
|
||||||
|
* Run `brew uninstall php@x.x` (where `x.x` is the version)
|
||||||
|
* Run `brew cleanup` (if you get any permission issues you may need to manually clean up the folder)
|
||||||
|
* Run `brew install php@x.x` (where `x.x` is the version)
|
||||||
|
|
||||||
|
You may still need to run `brew link php` after upgrading, too.
|
||||||
|
|
||||||
|
That's it. Now start up PHP Monitor again and you should be golden!
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>PHP Monitor tells me `php` is not installed...</strong></summary>
|
<summary><strong>PHP Monitor tells me `php` is not installed...</strong></summary>
|
||||||
|
|
||||||
@ -197,6 +224,12 @@ You should see an error or a warning here in the output.
|
|||||||
Usually this is a duplicate extension declaration causing issues, or an extension that couldn't be loaded. You'll have to solve that issue yourself (usually by removing the offending extension or reinstalling).
|
Usually this is a duplicate extension declaration causing issues, or an extension that couldn't be loaded. You'll have to solve that issue yourself (usually by removing the offending extension or reinstalling).
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><strong>The option to isolate a site is disabled! What's going on?</strong></summary>
|
||||||
|
|
||||||
|
Make sure you have at least **Valet 3.0** installed, since support for isolation was added in this version of Valet. (Please note that this version of Valet drops support for PHP 5.6.)
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>One of the limits (memory limit, max POST size, max upload size) shows an exclamation mark!</strong></summary>
|
<summary><strong>One of the limits (memory limit, max POST size, max upload size) shows an exclamation mark!</strong></summary>
|
||||||
@ -231,8 +264,15 @@ Since v3.4 all of the loaded .ini files are sourced to determine which extension
|
|||||||
<details>
|
<details>
|
||||||
<summary><strong>I've got two Homebrew installations on my Apple Silicon Mac, can I choose which installation to use with PHP Monitor?</strong></summary>
|
<summary><strong>I've got two Homebrew installations on my Apple Silicon Mac, can I choose which installation to use with PHP Monitor?</strong></summary>
|
||||||
|
|
||||||
Not at this time, no. PHP Monitor will prefer the `/opt/homebrew` installation over the classic installation directory.
|
If you are using PHP Monitor on an Intel machine or on an Apple Silicon machine with Rosetta enabled, PHP Monitor expects the main Homebrew binary in `/usr/local/bin/brew`.
|
||||||
|
|
||||||
|
If you are using PHP Monitor on Apple Silicon without Rosetta, PHP Monitor expects the main Homebrew binary in `/opt/homebrew/bin/brew`.
|
||||||
|
|
||||||
|
If there's an issue here, you'll get an alert at launch.
|
||||||
|
|
||||||
|
Make sure that the version of Homebrew that you are running normally is the same as the one that PHP Monitor expects. If you are on M1 hardware for example, but still using Rosetta for Homebrew, you'll need to run PHP Monitor under Rosetta as well.
|
||||||
|
|
||||||
|
PHP Monitor is a universal app and supports both architectures, so [find out here](https://support.apple.com/en-us/HT211861) how to enable Rosetta with PHP Monitor.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@ -267,9 +307,11 @@ You can put as many apps as you'd like in the `scan_apps` array, and PHP Monitor
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><strong>How can the app integrate with third party tools, like Alfred?</strong></summary>
|
<summary><strong>How can the app integrate with third party tools, like Alfred or Raycast?</strong></summary>
|
||||||
|
|
||||||
There's an Alfred workflow usually included in the release list, you can grab it by going to releases and downloading the asset `phpmon.alfredworkflow`.
|
PHP Monitor supports third party app integrations by default, and this feature is enabled in Preferences unless you disable it.
|
||||||
|
|
||||||
|
You can grab the official [Alfred workflow](https://github.com/nicoverbruggen/phpmon/raw/main/integrations/phpmon.alfredworkflow) or [Raycast extension](https://www.raycast.com/nicoverbruggen/php-monitor).
|
||||||
|
|
||||||
If you'd like to integrate something yourself, all you need to to is use the `phpmon://` protocol and ensure that third party app integrations are enabled in Preferences (in PHP Monitor).
|
If you'd like to integrate something yourself, all you need to to is use the `phpmon://` protocol and ensure that third party app integrations are enabled in Preferences (in PHP Monitor).
|
||||||
|
|
||||||
@ -351,13 +393,14 @@ Donations really help with the Apple Developer Program cost, and keep me motivat
|
|||||||
|
|
||||||
## 😎 Acknowledgements
|
## 😎 Acknowledgements
|
||||||
|
|
||||||
While I did make this application during my own free time, I have been lucky enough to do various experiments during work hours at [DIVE](https://dive.be). I'd also like to shout out the following folks:
|
While I did make this application during my own free time, PHP Monitor started out from various learning experiments during work hours at my employer, DIVE. I'd also like to shout out the following folks:
|
||||||
|
|
||||||
* My colleagues at [DIVE](https://dive.be)
|
* My colleagues at [DIVE](https://dive.be)
|
||||||
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
|
* The [Homebrew](https://brew.sh/) team & [Valet maintainers](https://github.com/laravel/valet/graphs/contributors)
|
||||||
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
|
* Various folks who [reached](https://twitter.com/stauffermatt) [out](https://twitter.com/marcelpociot) when PHP Monitor was still very much a small app with a handful of stars or so
|
||||||
|
* My [GitHub Sponsors](https://github.com/sponsors/nicoverbruggen) and those who have donated
|
||||||
|
* Everyone who has left feedback and reported bugs (appreciate it!)
|
||||||
* Everyone in the Laravel community who shared the app (thanks!)
|
* Everyone in the Laravel community who shared the app (thanks!)
|
||||||
* Everyone who left feedback via issues & who donated to keep the project up and running
|
|
||||||
|
|
||||||
Thank you very much for your contributions, kind words and support.
|
Thank you very much for your contributions, kind words and support.
|
||||||
|
|
||||||
@ -373,7 +416,9 @@ In order to save power, this only happens once every 60 seconds.
|
|||||||
|
|
||||||
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
|
This utility will detect which PHP versions you have installed via Homebrew, and then allows you to switch between them.
|
||||||
|
|
||||||
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be much faster than Valet’s switcher.
|
The switcher will disable all PHP-FPM services not belonging to the version you wish to use, and link the desired version of PHP. Then, it'll restart your desired PHP version's FPM process. This all happens in parallel, so this should be a bit faster than Valet’s switcher.
|
||||||
|
|
||||||
|
If you're using Valet 3, versions of PHP-FPM required to keep isolated sites up and running will also be started or stopped as needed.
|
||||||
|
|
||||||
### Config change detection
|
### Config change detection
|
||||||
|
|
||||||
@ -399,4 +444,4 @@ I have done my best to annotate as much as humanly possible, and have avoided us
|
|||||||
|
|
||||||
I also have a few tests for key parts of the application that I found needed to be tested. In the future, I would like to add even more tests for some of the UI stuff, but for now the tests are more unit tests than feature tests.
|
I also have a few tests for key parts of the application that I found needed to be tested. In the future, I would like to add even more tests for some of the UI stuff, but for now the tests are more unit tests than feature tests.
|
||||||
|
|
||||||
For more detailed information for developers, please see [the documentation file for developers](./DEVS.md).
|
For more detailed information for developers, please see [the documentation file for developers](./DEVELOPER.md).
|
||||||
|
13
RELEASE.md
@ -1,13 +0,0 @@
|
|||||||
# Release Procedure
|
|
||||||
|
|
||||||
1. Merge into `main`
|
|
||||||
2. Create tag
|
|
||||||
3. Add changes to changelog + update security document
|
|
||||||
4. Archive
|
|
||||||
5. Notarize and prepare for own distribution
|
|
||||||
6. After notarization, export .app
|
|
||||||
7. Create zipped version
|
|
||||||
8. Calculate SHA256: `openssl dgst -sha256 phpmon.zip`
|
|
||||||
9. Upload to GitHub and add to tagged release
|
|
||||||
10. Update Cask with new version + hash
|
|
||||||
11. Check new version can be installed via Cask
|
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
Generally speaking, only the latest version of **PHP Monitor** is supported, except during transition periods (for example, when particular system requirements go up):
|
||||||
|
|
||||||
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Minimum Required Valet Version |
|
| Version | Apple Silicon | Supported | Supported macOS | Deployment Target | Detected PHP Versions | Recommended Valet Version |
|
||||||
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
| ------- | ------------- | ------------------ | ----- | ----- | ----- | ----
|
||||||
| 5.0 | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 | 2.16.2 |
|
| 5.x | ✅ Universal binary | ✅ Yes | Big Sur (11.0) and Monterey (12.0) | macOS 11+ | PHP 5.6—PHP 8.2 (*) | 3.0 (2.16.2 minimum) |
|
||||||
|
|
||||||
|
_(*) Support for PHP 5.6 is only included if you are using Valet 2.x, since support for PHP 5.6 was dropped in Valet 3.0._
|
||||||
|
|
||||||
## Legacy versions
|
## Legacy versions
|
||||||
|
|
||||||
|
BIN
assets/affinity/icon.afdesign
Normal file
BIN
assets/affinity/icons.afdesign
Normal file
BIN
docs/logo.png
Normal file
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshot.jpg
Normal file
After Width: | Height: | Size: 345 KiB |
Before Width: | Height: | Size: 370 KiB |
32
phpmon-tests/Parsers/NginxConfigParserTest.swift
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
//
|
||||||
|
// 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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -32,6 +32,7 @@ class ValetConfigParserTest: XCTestCase {
|
|||||||
"/Users/username/.config/valet/Sites",
|
"/Users/username/.config/valet/Sites",
|
||||||
"/Users/username/Sites"
|
"/Users/username/Sites"
|
||||||
])
|
])
|
||||||
|
XCTAssertEqual(config.defaultSite, "/Users/username/default-site")
|
||||||
XCTAssertEqual(config.loopback, "127.0.0.1")
|
XCTAssertEqual(config.loopback, "127.0.0.1")
|
||||||
}
|
}
|
||||||
|
|
93
phpmon-tests/Test Files/nicoverbruggen.test
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.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 nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 512M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.crt";
|
||||||
|
ssl_certificate_key "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.key";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:60;
|
||||||
|
#listen 127.0.0.1:60; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.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/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
94
phpmon-tests/Test Files/nicoverbruggen_isolated.test
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
server {
|
||||||
|
listen 127.0.0.1:80;
|
||||||
|
#listen 127.0.0.1:80; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.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 nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.test;
|
||||||
|
root /;
|
||||||
|
charset utf-8;
|
||||||
|
client_max_body_size 512M;
|
||||||
|
http2_push_preload on;
|
||||||
|
|
||||||
|
location /41c270e4-5535-4daa-b23e-c269744c2f45/ {
|
||||||
|
internal;
|
||||||
|
alias /;
|
||||||
|
try_files $uri $uri/;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_certificate "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.crt";
|
||||||
|
ssl_certificate_key "/Users/nicoverbruggen/.config/valet/Certificates/nicoverbruggen.test.key";
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
# ISOLATED_PHP_VERSION=php@8.1
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet81.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 127.0.0.1:60;
|
||||||
|
#listen 127.0.0.1:60; # valet loopback
|
||||||
|
server_name nicoverbruggen.test www.nicoverbruggen.test *.nicoverbruggen.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/;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
rewrite ^ "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php" last;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /favicon.ico { access_log off; log_not_found off; }
|
||||||
|
location = /robots.txt { access_log off; log_not_found off; }
|
||||||
|
|
||||||
|
access_log off;
|
||||||
|
error_log "/Users/nicoverbruggen/.config/valet/Log/nginx-error.log";
|
||||||
|
|
||||||
|
error_page 404 "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
|
||||||
|
location ~ [^/]\.php(/|$) {
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass "unix:/Users/nicoverbruggen/.config/valet/valet.sock";
|
||||||
|
fastcgi_index "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME "/Users/nicoverbruggen/.composer/vendor/laravel/valet/server.php";
|
||||||
|
fastcgi_param PATH_INFO $fastcgi_path_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,5 +4,6 @@
|
|||||||
"/Users/username/.config/valet/Sites",
|
"/Users/username/.config/valet/Sites",
|
||||||
"/Users/username/Sites"
|
"/Users/username/Sites"
|
||||||
],
|
],
|
||||||
"loopback": "127.0.0.1"
|
"loopback": "127.0.0.1",
|
||||||
|
"default": "/Users/username/default-site"
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ class PhpVersionDetectionTest: XCTestCase {
|
|||||||
"unrelatedphp@1.0", // should be omitted, invalid
|
"unrelatedphp@1.0", // should be omitted, invalid
|
||||||
"php@5.6",
|
"php@5.6",
|
||||||
"php@5.4" // should be omitted, not supported
|
"php@5.4" // should be omitted, not supported
|
||||||
], checkBinaries: false)
|
], checkBinaries: false, generateHelpers: false)
|
||||||
|
|
||||||
XCTAssertEqual(outcome, ["8.0", "7.0", "5.6"])
|
XCTAssertEqual(outcome, ["8.0", "7.0"])
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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(
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
class ValetTest: XCTestCase {
|
class ValetVersionExtractorTest: XCTestCase {
|
||||||
|
|
||||||
func testDetermineValetVersion() {
|
func testDetermineValetVersion() {
|
||||||
let version = valet("--version")
|
let version = valet("--version", sudo: false)
|
||||||
XCTAssert(version.contains("Laravel Valet 2."))
|
XCTAssert(version.contains("Laravel Valet 2") || version.contains("Laravel Valet 3"))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 585 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 134 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_128x128.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_128x128@2x.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_16x16.png
Normal file
After Width: | Height: | Size: 632 B |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_16x16@2x.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_256x256.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_256x256@2x.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_32x32.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_32x32@2x.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_512x512.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
phpmon/Assets.xcassets/AppIconDev.appiconset/icon_512x512@2x.png
Normal file
After Width: | Height: | Size: 163 KiB |
25
phpmon/Assets.xcassets/IconDefault.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Default.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Default@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
phpmon/Assets.xcassets/IconDefault.imageset/Default.png
vendored
Normal file
After Width: | Height: | Size: 861 B |
BIN
phpmon/Assets.xcassets/IconDefault.imageset/Default@2x.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
25
phpmon/Assets.xcassets/Isolated.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "Isolated.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "Isolated@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
BIN
phpmon/Assets.xcassets/Isolated.imageset/Isolated.png
vendored
Normal file
After Width: | Height: | Size: 690 B |
BIN
phpmon/Assets.xcassets/Isolated.imageset/Isolated@2x.png
vendored
Normal file
After Width: | Height: | Size: 827 B |
@ -41,8 +41,8 @@ class Actions {
|
|||||||
"\(Paths.brew) services stop dnsmasq",
|
"\(Paths.brew) services stop dnsmasq",
|
||||||
]
|
]
|
||||||
var cellarCommands = [
|
var cellarCommands = [
|
||||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/nginx",
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/nginx",
|
||||||
"chown -R \(Paths.whoami):staff \(Paths.cellarPath)/dnsmasq"
|
"chown -R \(Paths.whoami):admin \(Paths.cellarPath)/dnsmasq"
|
||||||
]
|
]
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { version in
|
PhpEnv.shared.availablePhpVersions.forEach { version in
|
||||||
@ -50,7 +50,7 @@ class Actions {
|
|||||||
? "php"
|
? "php"
|
||||||
: "php@\(version)"
|
: "php@\(version)"
|
||||||
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
servicesCommands.append("\(Paths.brew) services stop \(formula)")
|
||||||
cellarCommands.append("chown -R \(Paths.whoami):staff \(Paths.cellarPath)/\(formula)")
|
cellarCommands.append("chown -R \(Paths.whoami):admin \(Paths.cellarPath)/\(formula)")
|
||||||
}
|
}
|
||||||
|
|
||||||
let script =
|
let script =
|
||||||
@ -116,31 +116,21 @@ class Actions {
|
|||||||
Detects all currently available PHP versions,
|
Detects all currently available PHP versions,
|
||||||
and unlinks each and every one of them.
|
and unlinks each and every one of them.
|
||||||
|
|
||||||
|
This all happens in sequence, nothing runs in parallel.
|
||||||
|
|
||||||
After this, the brew services are also stopped,
|
After this, the brew services are also stopped,
|
||||||
the latest PHP version is linked, and php + nginx are restarted.
|
the latest PHP version is linked, and php + nginx are restarted.
|
||||||
|
|
||||||
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()
|
public static func fixMyValet(completed: @escaping () -> Void)
|
||||||
{
|
{
|
||||||
brew("services restart dnsmasq", sudo: true)
|
InternalSwitcher().performSwitch(to: PhpEnv.brewPhpVersion, completion: {
|
||||||
|
brew("services restart dnsmasq", sudo: true)
|
||||||
PhpEnv.shared.detectPhpVersions().forEach { (version) in
|
brew("services restart php", sudo: true)
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
brew("services restart nginx", sudo: true)
|
||||||
brew("unlink php@\(version)")
|
completed()
|
||||||
brew("services stop \(formula)")
|
})
|
||||||
brew("services stop \(formula)", sudo: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
brew("services stop dnsmasq")
|
|
||||||
brew("services stop php")
|
|
||||||
brew("services stop nginx")
|
|
||||||
|
|
||||||
brew("link php --overwrite --force")
|
|
||||||
|
|
||||||
brew("services restart dnsmasq", sudo: true)
|
|
||||||
brew("services restart php", sudo: true)
|
|
||||||
brew("services restart nginx", sudo: true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Constants {
|
struct Constants {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The latest PHP version that is considered to be stable at the time of release.
|
* The latest PHP version that is considered to be stable at the time of release.
|
||||||
@ -34,7 +34,7 @@ class Constants {
|
|||||||
// STABLE RELEASES
|
// STABLE RELEASES
|
||||||
// ====================
|
// ====================
|
||||||
// Versions of PHP that are stable and are supported.
|
// Versions of PHP that are stable and are supported.
|
||||||
"5.6",
|
"5.6", // only supported when Valet 2.x is active
|
||||||
"7.0",
|
"7.0",
|
||||||
"7.1",
|
"7.1",
|
||||||
"7.2",
|
"7.2",
|
||||||
@ -50,10 +50,19 @@ class Constants {
|
|||||||
// dev release. In this case, that means that the version below is detected.
|
// dev release. In this case, that means that the version below is detected.
|
||||||
"8.2"
|
"8.2"
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
struct Urls {
|
||||||
The URL that people can visit if they wish to help support the project.
|
|
||||||
*/
|
static let DonationPayment = URL(
|
||||||
static let DonationUrl = URL(string: "https://nicoverbruggen.be/sponsor#pay-now")!
|
string: "https://nicoverbruggen.be/sponsor#pay-now"
|
||||||
|
)!
|
||||||
|
static let DonationPage = URL(
|
||||||
|
string: "https://nicoverbruggen.be/sponsor"
|
||||||
|
)!
|
||||||
|
static let FrequentlyAskedQuestions = URL(
|
||||||
|
string: "https://github.com/nicoverbruggen/phpmon#%EF%B8%8F-faq--troubleshooting"
|
||||||
|
)!
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
// MARK: Common Shell Commands
|
// MARK: Common Shell Commands
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Runs a `valet` command.
|
Runs a `valet` command. Defaults to running as superuser.
|
||||||
*/
|
*/
|
||||||
func valet(_ command: String) -> String
|
func valet(_ command: String, sudo: Bool = true) -> String
|
||||||
{
|
{
|
||||||
return Shell.pipe("sudo \(Paths.valet) \(command)", requiresPath: true)
|
return Shell.pipe("\(sudo ? "sudo " : "")" + "\(Paths.valet) \(command)", requiresPath: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +35,7 @@ func sed(file: String, original: String, replacement: String)
|
|||||||
|
|
||||||
// Check if gsed exists; it is able to follow symlinks,
|
// Check if gsed exists; it is able to follow symlinks,
|
||||||
// which we want to do to toggle the extension
|
// which we want to do to toggle the extension
|
||||||
if Shell.fileExists("\(Paths.binPath)/gsed") {
|
if Filesystem.fileExists("\(Paths.binPath)/gsed") {
|
||||||
Shell.run("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
Shell.run("\(Paths.binPath)/gsed -i --follow-symlinks 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||||
} else {
|
} else {
|
||||||
Shell.run("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
Shell.run("sed -i '' 's/\(e_original)/\(e_replacement)/g' \(file)")
|
||||||
|
@ -27,25 +27,31 @@ class Log {
|
|||||||
|
|
||||||
static func err(_ item: Any) {
|
static func err(_ item: Any) {
|
||||||
if Verbosity.error.isApplicable() {
|
if Verbosity.error.isApplicable() {
|
||||||
print("[ERR] \(item)")
|
print("[E] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func warn(_ item: Any) {
|
static func warn(_ item: Any) {
|
||||||
if Verbosity.warning.isApplicable() {
|
if Verbosity.warning.isApplicable() {
|
||||||
print("[WRN] \(item)")
|
print("[W] \(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func info(_ item: Any) {
|
static func info(_ item: Any) {
|
||||||
if Verbosity.info.isApplicable() {
|
if Verbosity.info.isApplicable() {
|
||||||
print(item)
|
print("\(item)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func perf(_ item: Any) {
|
static func perf(_ item: Any) {
|
||||||
if Verbosity.performance.isApplicable() {
|
if Verbosity.performance.isApplicable() {
|
||||||
print(item)
|
print("[P] \(item)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func separator(as verbosity: Verbosity = .info) {
|
||||||
|
if verbosity.isApplicable() {
|
||||||
|
print("==================================")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,15 +15,19 @@ public class Paths {
|
|||||||
|
|
||||||
public static let shared = Paths()
|
public static let shared = Paths()
|
||||||
|
|
||||||
private var baseDir : Paths.HomebrewDir
|
internal var baseDir: Paths.HomebrewDir
|
||||||
|
|
||||||
private var userName : String
|
private var userName: String
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
baseDir = FileManager.default.fileExists(atPath: "\(HomebrewDir.opt.rawValue)/bin/brew") ? .opt : .usr
|
baseDir = App.architecture != "x86_64" ? .opt : .usr
|
||||||
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
userName = String(Shell.pipe("whoami").split(separator: "\n")[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func detectBinaryPaths() {
|
||||||
|
detectComposerBinary()
|
||||||
|
}
|
||||||
|
|
||||||
// - MARK: Binaries
|
// - MARK: Binaries
|
||||||
|
|
||||||
public static var valet: String {
|
public static var valet: String {
|
||||||
@ -42,6 +46,11 @@ public class Paths {
|
|||||||
return "\(binPath)/php-config"
|
return "\(binPath)/php-config"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - MARK: Detected Binaries
|
||||||
|
|
||||||
|
/** The path to the Composer binary. Can be in multiple locations, so is detected instead. */
|
||||||
|
public static var composer: String? = nil
|
||||||
|
|
||||||
// - MARK: Paths
|
// - MARK: Paths
|
||||||
|
|
||||||
public static var whoami: String {
|
public static var whoami: String {
|
||||||
@ -64,6 +73,21 @@ public class Paths {
|
|||||||
return "\(shared.baseDir.rawValue)/etc"
|
return "\(shared.baseDir.rawValue)/etc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Flexible Binaries
|
||||||
|
// (these can be in multiple locations, so we scan common places because)
|
||||||
|
// (PHP Monitor will not use the user's own PATH)
|
||||||
|
|
||||||
|
private func detectComposerBinary() {
|
||||||
|
if Filesystem.fileExists("/usr/local/bin/composer") {
|
||||||
|
Paths.composer = "/usr/local/bin/composer"
|
||||||
|
} else if Filesystem.fileExists("/opt/homebrew/bin/composer") {
|
||||||
|
Paths.composer = "/opt/homebrew/bin/composer"
|
||||||
|
} else {
|
||||||
|
Paths.composer = nil
|
||||||
|
Log.warn("Composer was not found.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Enum
|
// MARK: - Enum
|
||||||
|
|
||||||
public enum HomebrewDir: String {
|
public enum HomebrewDir: String {
|
||||||
|
59
phpmon/Common/Core/Process.swift
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
// Process.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 23/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Process {
|
||||||
|
|
||||||
|
/**
|
||||||
|
When a process is running in the background, it can send content to standard
|
||||||
|
output or standard error, just like it would in a terminal. Using `listen`
|
||||||
|
allows us to react whenever data is received by running a particular closure,
|
||||||
|
depending on which type of data is received.
|
||||||
|
*/
|
||||||
|
public func listen(
|
||||||
|
didReceiveStandardOutputData: @escaping (String) -> Void,
|
||||||
|
didReceiveStandardErrorData: @escaping (String) -> Void
|
||||||
|
) {
|
||||||
|
let outputPipe = Pipe()
|
||||||
|
let errorPipe = Pipe()
|
||||||
|
|
||||||
|
self.standardOutput = outputPipe
|
||||||
|
self.standardError = errorPipe
|
||||||
|
|
||||||
|
[
|
||||||
|
(outputPipe, didReceiveStandardOutputData),
|
||||||
|
(errorPipe, didReceiveStandardErrorData)
|
||||||
|
].forEach { (pipe: Pipe, callback: @escaping (String) -> Void) in
|
||||||
|
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
||||||
|
object: pipe.fileHandleForReading,
|
||||||
|
queue: nil
|
||||||
|
) { notification in
|
||||||
|
if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) {
|
||||||
|
callback(outputString)
|
||||||
|
}
|
||||||
|
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
After the process is done running, you'll want to stop listening.
|
||||||
|
*/
|
||||||
|
public func haltListening() {
|
||||||
|
if let pipe = self.standardOutput as? Pipe {
|
||||||
|
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
||||||
|
}
|
||||||
|
if let pipe = self.standardError as? Pipe {
|
||||||
|
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -104,15 +104,6 @@ public class Shell {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if a file exists at a certain path.
|
|
||||||
Used to be done with a shell command, now uses the native FileManager class instead.
|
|
||||||
*/
|
|
||||||
public static func fileExists(_ path: String) -> Bool {
|
|
||||||
let fullPath = path.replacingOccurrences(of: "~", with: "/Users/\(Paths.whoami)")
|
|
||||||
return FileManager.default.fileExists(atPath: fullPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Creates a new process with the correct PATH and shell.
|
Creates a new process with the correct PATH and shell.
|
||||||
*/
|
*/
|
||||||
@ -128,42 +119,6 @@ public class Shell {
|
|||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func captureOutput(
|
|
||||||
_ task: Process,
|
|
||||||
didReceiveStdOutData: @escaping (String) -> Void,
|
|
||||||
didReceiveStdErrData: @escaping (String) -> Void
|
|
||||||
) {
|
|
||||||
let outputPipe = Pipe()
|
|
||||||
let errorPipe = Pipe()
|
|
||||||
|
|
||||||
task.standardOutput = outputPipe
|
|
||||||
task.standardError = errorPipe
|
|
||||||
|
|
||||||
[(outputPipe, didReceiveStdOutData), (errorPipe, didReceiveStdErrData)].forEach {
|
|
||||||
(pipe: Pipe, callback: @escaping (String) -> Void) in
|
|
||||||
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
forName: NSNotification.Name.NSFileHandleDataAvailable,
|
|
||||||
object: pipe.fileHandleForReading,
|
|
||||||
queue: nil
|
|
||||||
) { notification in
|
|
||||||
if let outputString = String(data: pipe.fileHandleForReading.availableData, encoding: String.Encoding.utf8) {
|
|
||||||
callback(outputString)
|
|
||||||
}
|
|
||||||
pipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func haltCapturingOutput(_ task: Process) {
|
|
||||||
if let pipe = task.standardOutput as? Pipe {
|
|
||||||
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
|
||||||
}
|
|
||||||
if let pipe = task.standardError as? Pipe {
|
|
||||||
NotificationCenter.default.removeObserver(pipe.fileHandleForReading)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Output {
|
public class Output {
|
||||||
public let standardOutput: String
|
public let standardOutput: String
|
||||||
public let errorOutput: String
|
public let errorOutput: String
|
||||||
|
38
phpmon/Common/Extensions/NSWindowExtension.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// NSWindowExtension.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 17/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
extension NSWindow {
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shakes a window. Inspired by: http://blog.ericd.net/2016/09/30/shaking-a-macos-window/
|
||||||
|
*/
|
||||||
|
func shake(){
|
||||||
|
let numberOfShakes = 3, durationOfShake = 0.2, vigourOfShake: CGFloat = 0.03
|
||||||
|
|
||||||
|
let frame: CGRect = self.frame
|
||||||
|
let shakeAnimation :CAKeyframeAnimation = CAKeyframeAnimation()
|
||||||
|
|
||||||
|
let shakePath = CGMutablePath()
|
||||||
|
shakePath.move( to: CGPoint(x:NSMinX(frame), y:NSMinY(frame)))
|
||||||
|
|
||||||
|
for _ in 0...numberOfShakes-1 {
|
||||||
|
shakePath.addLine(to: CGPoint(x:NSMinX(frame) - frame.size.width * vigourOfShake, y:NSMinY(frame)))
|
||||||
|
shakePath.addLine(to: CGPoint(x:NSMinX(frame) + frame.size.width * vigourOfShake, y:NSMinY(frame)))
|
||||||
|
}
|
||||||
|
|
||||||
|
shakePath.closeSubpath()
|
||||||
|
shakeAnimation.path = shakePath
|
||||||
|
shakeAnimation.duration = durationOfShake
|
||||||
|
|
||||||
|
self.animations = ["frameOrigin":shakeAnimation]
|
||||||
|
self.animator().setFrameOrigin(self.frame.origin)
|
||||||
|
}
|
||||||
|
}
|
@ -9,24 +9,6 @@ import Cocoa
|
|||||||
|
|
||||||
class Alert {
|
class Alert {
|
||||||
|
|
||||||
public static func present(
|
|
||||||
messageText: String,
|
|
||||||
informativeText: String,
|
|
||||||
buttonTitle: String = "OK",
|
|
||||||
secondButtonTitle: String = "",
|
|
||||||
style: NSAlert.Style = .informational
|
|
||||||
) -> Bool {
|
|
||||||
let alert = NSAlert.init()
|
|
||||||
alert.alertStyle = style
|
|
||||||
alert.messageText = messageText
|
|
||||||
alert.informativeText = informativeText
|
|
||||||
alert.addButton(withTitle: buttonTitle)
|
|
||||||
if (!secondButtonTitle.isEmpty) {
|
|
||||||
alert.addButton(withTitle: secondButtonTitle)
|
|
||||||
}
|
|
||||||
return alert.runModal() == .alertFirstButtonReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func confirm(
|
public static func confirm(
|
||||||
onWindow window: NSWindow,
|
onWindow window: NSWindow,
|
||||||
messageText: String,
|
messageText: String,
|
||||||
@ -36,6 +18,10 @@ class Alert {
|
|||||||
style: NSAlert.Style = .warning,
|
style: NSAlert.Style = .warning,
|
||||||
onFirstButtonPressed: @escaping (() -> Void)
|
onFirstButtonPressed: @escaping (() -> Void)
|
||||||
) {
|
) {
|
||||||
|
if !Thread.isMainThread {
|
||||||
|
fatalError("You should always present alerts on the main thread!")
|
||||||
|
}
|
||||||
|
|
||||||
let alert = NSAlert.init()
|
let alert = NSAlert.init()
|
||||||
alert.alertStyle = style
|
alert.alertStyle = style
|
||||||
alert.messageText = messageText
|
alert.messageText = messageText
|
||||||
@ -51,31 +37,4 @@ class Alert {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Notify the user about something by showing an alert.
|
|
||||||
*/
|
|
||||||
public static func notify(message: String, info: String, style: NSAlert.Style = .informational) {
|
|
||||||
_ = present(
|
|
||||||
messageText: message,
|
|
||||||
informativeText: info,
|
|
||||||
buttonTitle: "OK",
|
|
||||||
secondButtonTitle: "",
|
|
||||||
style: style
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Notify the user about a particular error (which must be `Alertable`)
|
|
||||||
by showing an alert.
|
|
||||||
*/
|
|
||||||
public static func notify(about error: Error & AlertableError) {
|
|
||||||
let key = error.getErrorMessageKey()
|
|
||||||
_ = present(
|
|
||||||
messageText: "\(key).title".localized,
|
|
||||||
informativeText: "\(key).description".localized,
|
|
||||||
buttonTitle: "OK",
|
|
||||||
secondButtonTitle: "",
|
|
||||||
style: .critical
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// Async.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 23/01/2022.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/**
|
|
||||||
This generic async helper is something I'd like to use in more places.
|
|
||||||
|
|
||||||
The `DispatchQueue.global` into `DispatchQueue.main.async` logic is common in the app.
|
|
||||||
|
|
||||||
I could also use try `async` support which was introduced in Swift but that would
|
|
||||||
require too much refactoring at this time to consider. I also need to read up on async
|
|
||||||
in order to properly grasp all the gotchas. Looking into that later at some point.
|
|
||||||
*/
|
|
||||||
public func runAsync(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
|
||||||
{
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
execute()
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
completion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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 {
|
||||||
|
@ -137,7 +137,7 @@ class ActivePhpInstallation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
// Make sure to check if valet-fpm.conf exists. If it does, we should be fine :)
|
||||||
return Shell.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
|
return Filesystem.fileExists("\(Paths.etcPath)/php/\(self.version.short)/php-fpm.d/valet-fpm.conf")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Structs
|
// MARK: - Structs
|
||||||
|
@ -18,4 +18,21 @@ struct HomebrewService: Decodable, Equatable {
|
|||||||
let status: String?
|
let status: String?
|
||||||
let log_path: String?
|
let log_path: String?
|
||||||
let error_log_path: String?
|
let error_log_path: String?
|
||||||
|
|
||||||
|
public static func loadAll(
|
||||||
|
filter: [String] = [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"],
|
||||||
|
completion: @escaping ([HomebrewService]) -> Void
|
||||||
|
) {
|
||||||
|
DispatchQueue.global(qos: .background).async {
|
||||||
|
let data = Shell
|
||||||
|
.pipe("sudo \(Paths.brew) services info --all --json", requiresPath: true)
|
||||||
|
.data(using: .utf8)!
|
||||||
|
|
||||||
|
let services = try! JSONDecoder()
|
||||||
|
.decode([HomebrewService].self, from: data)
|
||||||
|
.filter({ return filter.contains($0.name) })
|
||||||
|
|
||||||
|
completion(services)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ class PhpEnv {
|
|||||||
let phpAlias = homebrewPackage.version
|
let phpAlias = homebrewPackage.version
|
||||||
|
|
||||||
// Avoid inserting a duplicate
|
// Avoid inserting a duplicate
|
||||||
if (!versionsOnly.contains(phpAlias) && Shell.fileExists("\(Paths.optPath)/php/bin/php")) {
|
if (!versionsOnly.contains(phpAlias) && Filesystem.fileExists("\(Paths.optPath)/php/bin/php")) {
|
||||||
versionsOnly.append(phpAlias)
|
versionsOnly.append(phpAlias)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,13 +117,23 @@ class PhpEnv {
|
|||||||
/**
|
/**
|
||||||
Extracts valid PHP versions from an array of strings.
|
Extracts valid PHP versions from an array of strings.
|
||||||
This array of strings is usually retrieved from `grep`.
|
This array of strings is usually retrieved from `grep`.
|
||||||
|
|
||||||
|
If `generateHelpers` is set to true, after detecting
|
||||||
|
all versions, helper scripts are generated as well.
|
||||||
*/
|
*/
|
||||||
public func extractPhpVersions(
|
public func extractPhpVersions(
|
||||||
from versions: [String],
|
from versions: [String],
|
||||||
checkBinaries: Bool = true
|
checkBinaries: Bool = true,
|
||||||
|
generateHelpers: Bool = true
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
var output : [String] = []
|
var output : [String] = []
|
||||||
|
|
||||||
|
var supported = Constants.SupportedPhpVersions
|
||||||
|
|
||||||
|
if !Valet.enabled(feature: .supportForPhp56) {
|
||||||
|
supported.removeAll { $0 == "5.6" }
|
||||||
|
}
|
||||||
|
|
||||||
versions.filter { (version) -> Bool in
|
versions.filter { (version) -> Bool in
|
||||||
// Omit everything that doesn't start with php@
|
// Omit everything that doesn't start with php@
|
||||||
// (e.g. something-php@8.0 won't be detected)
|
// (e.g. something-php@8.0 won't be detected)
|
||||||
@ -133,13 +143,17 @@ class PhpEnv {
|
|||||||
// Only append the version if it doesn't already exist (avoid dupes),
|
// Only append the version if it doesn't already exist (avoid dupes),
|
||||||
// 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)
|
||||||
&& Constants.SupportedPhpVersions.contains(version)
|
&& supported.contains(version)
|
||||||
&& (checkBinaries ? Shell.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
&& (checkBinaries ? Filesystem.fileExists("\(Paths.optPath)/php@\(version)/bin/php") : true)
|
||||||
{
|
{
|
||||||
output.append(version)
|
output.append(version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if generateHelpers {
|
||||||
|
output.forEach { PhpHelper.generate(for: $0) }
|
||||||
|
}
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
phpmon/Common/PHP/PHP Version/PhpHelper.swift
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// PhpHelper.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 17/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class PhpHelper {
|
||||||
|
|
||||||
|
static let keyPhrase = "This file was automatically generated by PHP Monitor."
|
||||||
|
|
||||||
|
public static func generate(for version: String) {
|
||||||
|
// Take the PHP version (e.g. "7.2") and generate a dotless version
|
||||||
|
let dotless = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
|
||||||
|
do {
|
||||||
|
let destination = "/usr/local/bin/pm\(dotless)"
|
||||||
|
if FileManager.default.fileExists(atPath: destination) {
|
||||||
|
let contents = try String(contentsOfFile: destination)
|
||||||
|
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.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's follow the symlink to the PHP binary folder
|
||||||
|
let path = URL(fileURLWithPath: "\(Paths.optPath)/php@\(version)/bin")
|
||||||
|
.resolvingSymlinksInPath().path
|
||||||
|
|
||||||
|
// The contents of the script!
|
||||||
|
let script = """
|
||||||
|
#!/bin/zsh
|
||||||
|
# \(keyPhrase)
|
||||||
|
# It reflects the location of PHP \(version)'s binaries on your system.
|
||||||
|
# Usage: . pm\(dotless)
|
||||||
|
[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] \\
|
||||||
|
&& echo "PHP Monitor has enabled this terminal to use PHP \(version)." \\
|
||||||
|
|| echo "You must run '. pm\(dotless)' (or 'source pm\(dotless)') instead!";
|
||||||
|
export PATH=\(path):$PATH
|
||||||
|
"""
|
||||||
|
|
||||||
|
// Write to the destination
|
||||||
|
try script.write(
|
||||||
|
to: URL(fileURLWithPath: destination),
|
||||||
|
atomically: true,
|
||||||
|
encoding: String.Encoding.utf8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Make sure the file is executable
|
||||||
|
Shell.run("chmod +x \(destination)")
|
||||||
|
} catch {
|
||||||
|
print(error)
|
||||||
|
Log.err("Could not write PHP Monitor helper for PHP \(version) to /usr/local/bin/pm\(dotless)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +96,12 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
let minor: Int
|
let minor: Int
|
||||||
let patch: Int?
|
let patch: Int?
|
||||||
|
|
||||||
|
public func toString() -> String {
|
||||||
|
return self.patch == nil
|
||||||
|
? "\(major).\(minor)"
|
||||||
|
: "\(major).\(minor).\(patch!)"
|
||||||
|
}
|
||||||
|
|
||||||
public func patch(_ strictFallback: Bool = true, _ constraint: PhpVersionNumber? = nil) -> Int {
|
public func patch(_ strictFallback: Bool = true, _ constraint: PhpVersionNumber? = nil) -> Int {
|
||||||
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
return patch ?? (strictFallback ? 0 : constraint?.patch ?? 999)
|
||||||
}
|
}
|
||||||
@ -111,7 +117,7 @@ public struct PhpVersionNumber: Equatable {
|
|||||||
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case greaterThanOrEqual = #"^>=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case greaterThan = #"^>(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
|
|
||||||
// TODO: (5.1) Handle these cases (even though I suspect these are uncommon)
|
// TODO: (6.0) Handle these cases (even though I suspect these are uncommon)
|
||||||
/*
|
/*
|
||||||
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case smallerThanOrEqual = #"^<=(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
case smallerThan = #"^<(?<major>\d+).(?<minor>\d+).?(?<patch>\d+)?\z"#
|
||||||
|
@ -10,7 +10,7 @@ import Foundation
|
|||||||
|
|
||||||
class PhpInstallation {
|
class PhpInstallation {
|
||||||
|
|
||||||
var longVersion: PhpVersionNumber
|
var versionNumber: PhpVersionNumber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
In order to determine details about a PHP installation, we’ll simply run `php-config --version`
|
||||||
@ -19,9 +19,9 @@ class PhpInstallation {
|
|||||||
init(_ version: String) {
|
init(_ version: String) {
|
||||||
|
|
||||||
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
let phpConfigExecutablePath = "\(Paths.optPath)/php@\(version)/bin/php-config"
|
||||||
self.longVersion = PhpVersionNumber.make(from: version)!
|
self.versionNumber = PhpVersionNumber.make(from: version)!
|
||||||
|
|
||||||
if Shell.fileExists(phpConfigExecutablePath) {
|
if Filesystem.fileExists(phpConfigExecutablePath) {
|
||||||
let longVersionString = Command.execute(
|
let longVersionString = Command.execute(
|
||||||
path: phpConfigExecutablePath,
|
path: phpConfigExecutablePath,
|
||||||
arguments: ["--version"]
|
arguments: ["--version"]
|
||||||
@ -29,8 +29,7 @@ class PhpInstallation {
|
|||||||
|
|
||||||
// The parser should always work, or the string has to be very unusual.
|
// The parser should always work, or the string has to be very unusual.
|
||||||
// If so, the app SHOULD crash, so that the users report what's up.
|
// If so, the app SHOULD crash, so that the users report what's up.
|
||||||
// TODO: Alert the user that the version number could not be parsed.
|
self.versionNumber = try! PhpVersionNumber.parse(longVersionString)
|
||||||
self.longVersion = try! PhpVersionNumber.parse(longVersionString)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,20 +24,26 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
{
|
{
|
||||||
Log.info("Switching to \(version), unlinking all versions...")
|
Log.info("Switching to \(version), unlinking all versions...")
|
||||||
|
|
||||||
|
let isolated = Valet.shared.sites.filter { site in
|
||||||
|
site.isolatedPhpVersion != nil
|
||||||
|
}.map { site in
|
||||||
|
return site.isolatedPhpVersion!.versionNumber.homebrewVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
var versions: Set<String> = [version]
|
||||||
|
|
||||||
|
if (Valet.enabled(feature: .isolatedSites)) {
|
||||||
|
versions = versions.union(isolated)
|
||||||
|
}
|
||||||
|
|
||||||
let group = DispatchGroup()
|
let group = DispatchGroup()
|
||||||
|
|
||||||
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
PhpEnv.shared.availablePhpVersions.forEach { (available) in
|
||||||
group.enter()
|
group.enter()
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
DispatchQueue.global(qos: .userInitiated).async {
|
||||||
let formula = (available == PhpEnv.brewPhpVersion)
|
self.disableDefaultPhpFpmPool(available)
|
||||||
? "php" : "php@\(available)"
|
self.stopPhpVersion(available)
|
||||||
|
|
||||||
brew("unlink \(formula)")
|
|
||||||
brew("services stop \(formula)", sudo: true)
|
|
||||||
|
|
||||||
Log.perf("Unlinked and stopped services for \(formula)")
|
|
||||||
|
|
||||||
group.leave()
|
group.leave()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -46,16 +52,62 @@ class InternalSwitcher: PhpSwitcher {
|
|||||||
Log.info("All versions have been unlinked!")
|
Log.info("All versions have been unlinked!")
|
||||||
Log.info("Linking the new version!")
|
Log.info("Linking the new version!")
|
||||||
|
|
||||||
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
for formula in versions {
|
||||||
brew("link \(formula) --overwrite --force")
|
self.startPhpVersion(formula, primary: (version == formula))
|
||||||
brew("services start \(formula)", sudo: true)
|
}
|
||||||
|
|
||||||
Log.info("Restarting nginx, just to be sure!")
|
Log.info("Restarting nginx, just to be sure!")
|
||||||
brew("services restart nginx", sudo: true)
|
brew("services restart nginx", sudo: true)
|
||||||
|
|
||||||
Log.info("The new version has been linked!")
|
Log.info("The new version(s) have been linked!")
|
||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func disableDefaultPhpFpmPool(_ version: String) {
|
||||||
|
let pool = "\(Paths.etcPath)/php/\(version)/php-fpm.d/www.conf"
|
||||||
|
if FileManager.default.fileExists(atPath: pool) {
|
||||||
|
Log.info("A default `www.conf` file was found in the php-fpm.d directory for PHP \(version).")
|
||||||
|
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")!
|
||||||
|
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)
|
||||||
|
Log.info("Success: A default `www.conf` file was disabled for PHP \(version).")
|
||||||
|
} catch {
|
||||||
|
Log.err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopPhpVersion(_ version: String) {
|
||||||
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
brew("unlink \(formula)")
|
||||||
|
brew("services stop \(formula)", sudo: true)
|
||||||
|
Log.info("Unlinked and stopped services for \(formula)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startPhpVersion(_ version: String, primary: Bool) {
|
||||||
|
let formula = (version == PhpEnv.brewPhpVersion) ? "php" : "php@\(version)"
|
||||||
|
|
||||||
|
if (primary) {
|
||||||
|
Log.info("\(formula) is the primary formula, linking and starting services...")
|
||||||
|
brew("link \(formula) --overwrite --force")
|
||||||
|
} else {
|
||||||
|
Log.info("\(formula) is an isolated PHP version, starting services only...")
|
||||||
|
}
|
||||||
|
|
||||||
|
brew("services start \(formula)", sudo: true)
|
||||||
|
|
||||||
|
if Valet.enabled(feature: .isolatedSites) && primary {
|
||||||
|
let socketVersion = version.replacingOccurrences(of: ".", with: "")
|
||||||
|
Shell.run("ln -sF ~/.config/valet/valet\(socketVersion).sock ~/.config/valet/valet.sock")
|
||||||
|
Log.info("Symlinked new socket version (valet\(socketVersion).sock → valet.sock).")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,9 @@ import Foundation
|
|||||||
|
|
||||||
protocol PhpSwitcherDelegate: AnyObject {
|
protocol PhpSwitcherDelegate: AnyObject {
|
||||||
|
|
||||||
func switcherDidStartSwitching(to: String)
|
func switcherDidStartSwitching(to version: String)
|
||||||
|
|
||||||
func switcherDidCompleteSwitch(to: String)
|
func switcherDidCompleteSwitch(to version: String)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import HotKey
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
extension App {
|
extension App {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
|
|
||||||
@ -22,9 +21,18 @@ class App {
|
|||||||
return "\(version) (\(build))"
|
return "\(version) (\(build))"
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the app is busy doing something. Used to determine what UI to display. */
|
static var architecture: String {
|
||||||
static var busy: Bool {
|
var systeminfo = utsname()
|
||||||
return PhpEnv.shared.isBusy
|
uname(&systeminfo)
|
||||||
|
let machine = withUnsafeBytes(of: &systeminfo.machine) {bufPtr->String in
|
||||||
|
let data = Data(bufPtr)
|
||||||
|
if let lastIndex = data.lastIndex(where: {$0 != 0}) {
|
||||||
|
return String(data: data[0...lastIndex], encoding: .isoLatin1)!
|
||||||
|
} else {
|
||||||
|
return String(data: data, encoding: .isoLatin1)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return machine
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Variables
|
// MARK: Variables
|
||||||
|
@ -64,10 +64,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
*/
|
*/
|
||||||
override init() {
|
override init() {
|
||||||
logger.verbosity = .info
|
logger.verbosity = .info
|
||||||
Log.info("==================================")
|
#if DEBUG
|
||||||
|
logger.verbosity = .performance
|
||||||
|
#endif
|
||||||
|
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)")
|
||||||
Log.info("==================================")
|
Log.separator(as: .info)
|
||||||
self.sharedShell = Shell.user
|
self.sharedShell = Shell.user
|
||||||
self.state = App.shared
|
self.state = App.shared
|
||||||
self.menu = MainMenu.shared
|
self.menu = MainMenu.shared
|
||||||
@ -91,7 +94,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||||||
// Make sure notifications will work
|
// Make sure notifications will work
|
||||||
setupNotifications()
|
setupNotifications()
|
||||||
// Make sure the menu performs its initial checks
|
// Make sure the menu performs its initial checks
|
||||||
menu.startup()
|
Task { await menu.startup() }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
<capability name="Image references" minToolsVersion="12.0"/>
|
<capability name="Image references" minToolsVersion="12.0"/>
|
||||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||||
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
|
<capability name="Search Toolbar Item" minToolsVersion="12.0" minSystemVersion="11.0"/>
|
||||||
@ -387,10 +387,10 @@
|
|||||||
<window key="window" title="Domains" subtitle="Linked & Parked" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="raw-02-3Q1">
|
<window key="window" title="Domains" subtitle="Linked & Parked" 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="550" height="263"/>
|
<rect key="contentRect" x="425" y="461" width="600" height="263"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||||
<view key="contentView" id="uVx-Da-x4I">
|
<view key="contentView" id="uVx-Da-x4I">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="550" height="263"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="263"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</view>
|
</view>
|
||||||
<toolbar key="toolbar" implicitIdentifier="594015E3-8428-4926-9341-4B8CE4C7E373" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="OOz-oZ-vlN">
|
<toolbar key="toolbar" implicitIdentifier="594015E3-8428-4926-9341-4B8CE4C7E373" autosavesConfiguration="NO" allowsUserCustomization="NO" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="OOz-oZ-vlN">
|
||||||
@ -407,12 +407,12 @@
|
|||||||
<action selector="pressedReload:" target="8Ec-9q-82s" id="fLc-bD-oYQ"/>
|
<action selector="pressedReload:" target="8Ec-9q-82s" id="fLc-bD-oYQ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
<searchToolbarItem implicitItemIdentifier="629F0782-3C5F-4CD0-9396-3A054A422180" label="Search" paletteLabel="Search" visibilityPriority="1001" id="Q7Z-fw-lB9">
|
<searchToolbarItem implicitItemIdentifier="7C834FBE-7118-4082-A09F-7CBECEC1356A" label="Search" paletteLabel="Search" visibilityPriority="1001" id="G2g-jS-RVc">
|
||||||
<nil key="toolTip"/>
|
<nil key="toolTip"/>
|
||||||
<searchField key="view" verticalHuggingPriority="750" textCompletion="NO" id="oWA-TH-Pm7">
|
<searchField key="view" verticalHuggingPriority="750" textCompletion="NO" id="0gE-Yr-MLy">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="100" height="21"/>
|
<rect key="frame" x="0.0" y="0.0" width="100" height="21"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsSearchStringImmediately="YES" id="3NO-6x-aLc">
|
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" sendsSearchStringImmediately="YES" id="vp9-vH-goQ">
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
<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"/>
|
||||||
@ -423,7 +423,7 @@
|
|||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="GsC-ra-40U"/>
|
<toolbarItem reference="GsC-ra-40U"/>
|
||||||
<toolbarItem reference="YtK-vM-5y7"/>
|
<toolbarItem reference="YtK-vM-5y7"/>
|
||||||
<searchToolbarItem reference="Q7Z-fw-lB9"/>
|
<searchToolbarItem reference="G2g-jS-RVc"/>
|
||||||
</defaultToolbarItems>
|
</defaultToolbarItems>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
<connections>
|
<connections>
|
||||||
@ -431,13 +431,13 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="searchToolbarItem" destination="Q7Z-fw-lB9" id="J5o-oh-VhO"/>
|
<outlet property="searchToolbarItem" destination="G2g-jS-RVc" id="xlc-qe-k7e"/>
|
||||||
<segue destination="JZI-Vd-9oq" kind="relationship" relationship="window.shadowedContentViewController" id="9Gy-Gw-hPH"/>
|
<segue destination="JZI-Vd-9oq" kind="relationship" relationship="window.shadowedContentViewController" id="9Gy-Gw-hPH"/>
|
||||||
</connections>
|
</connections>
|
||||||
</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="758"/>
|
<point key="canvasLocation" x="-374" y="746"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="HTI-x5-rOp">
|
<scene sceneID="HTI-x5-rOp">
|
||||||
@ -462,7 +462,177 @@
|
|||||||
</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="-339" y="1147"/>
|
<point key="canvasLocation" x="-409" y="1137"/>
|
||||||
|
</scene>
|
||||||
|
<!--Window Controller-->
|
||||||
|
<scene sceneID="BD0-La-ygq">
|
||||||
|
<objects>
|
||||||
|
<windowController storyboardIdentifier="noticeWindow" id="nfT-AN-9ZW" sceneMemberID="viewController">
|
||||||
|
<window key="window" title="Notice" separatorStyle="none" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="alertPanel" frameAutosaveName="" titlebarAppearsTransparent="YES" toolbarStyle="unified" titleVisibility="hidden" id="AoF-SN-xB0">
|
||||||
|
<windowStyleMask key="styleMask" titled="YES" fullSizeContentView="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="Src-7L-4Z4">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="270"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="delegate" destination="nfT-AN-9ZW" id="8dd-JR-bQG"/>
|
||||||
|
</connections>
|
||||||
|
</window>
|
||||||
|
<connections>
|
||||||
|
<segue destination="hkw-9V-NxP" kind="relationship" relationship="window.shadowedContentViewController" id="eob-YS-ACy"/>
|
||||||
|
</connections>
|
||||||
|
</windowController>
|
||||||
|
<customObject id="i3j-z8-nxv" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="-575" y="1624"/>
|
||||||
|
</scene>
|
||||||
|
<!--Better AlertVC-->
|
||||||
|
<scene sceneID="y9E-bB-wIG">
|
||||||
|
<objects>
|
||||||
|
<viewController storyboardIdentifier="noticeVC" id="hkw-9V-NxP" customClass="BetterAlertVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<view key="view" id="UPH-hV-Naz">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="500" height="212"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<visualEffectView blendingMode="behindWindow" material="popover" state="followsWindowActiveState" translatesAutoresizingMaskIntoConstraints="NO" id="JVG-5w-fPd">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="500" height="212"/>
|
||||||
|
<subviews>
|
||||||
|
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="8zu-cF-KCX">
|
||||||
|
<rect key="frame" x="383" y="13" width="104" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="90" id="4Uf-fh-jWJ"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="Primary" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="F26-vf-hFH">
|
||||||
|
<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="primaryButtonAction:" target="hkw-9V-NxP" id="W7d-3b-pZT"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TCp-nS-HN2">
|
||||||
|
<rect key="frame" x="281" y="13" width="104" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="90" id="QWZ-BA-0g9"/>
|
||||||
|
</constraints>
|
||||||
|
<buttonCell key="cell" type="push" title="Secondary" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="eCk-FC-9Zr">
|
||||||
|
<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="secondaryButtonAction:" target="hkw-9V-NxP" id="YJs-Hu-lFP"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<button wantsLayer="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n5T-nn-k3j">
|
||||||
|
<rect key="frame" x="13" y="13" width="82" height="32"/>
|
||||||
|
<buttonCell key="cell" type="push" title="Tertiary" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="mzA-Uu-gyf">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="tertiaryButtonAction:" target="hkw-9V-NxP" id="o1C-av-ifx"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="8zu-cF-KCX" secondAttribute="trailing" constant="20" symbolic="YES" id="0DC-rd-xbu"/>
|
||||||
|
<constraint firstItem="8zu-cF-KCX" firstAttribute="leading" secondItem="TCp-nS-HN2" secondAttribute="trailing" constant="12" symbolic="YES" id="8tH-KO-fjY"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="8zu-cF-KCX" secondAttribute="bottom" constant="20" symbolic="YES" id="L6V-KQ-nj3"/>
|
||||||
|
<constraint firstItem="n5T-nn-k3j" firstAttribute="leading" secondItem="JVG-5w-fPd" secondAttribute="leading" constant="20" symbolic="YES" id="QLS-fE-1PM"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="8zu-cF-KCX" secondAttribute="trailing" constant="20" symbolic="YES" id="RRS-WO-6KO"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="n5T-nn-k3j" secondAttribute="bottom" constant="20" symbolic="YES" id="Scj-z1-AdN"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="TCp-nS-HN2" secondAttribute="bottom" constant="20" symbolic="YES" id="fYa-HG-gmL"/>
|
||||||
|
<constraint firstItem="n5T-nn-k3j" firstAttribute="bottom" secondItem="TCp-nS-HN2" secondAttribute="bottom" id="lOI-ZI-wCd"/>
|
||||||
|
<constraint firstItem="TCp-nS-HN2" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="n5T-nn-k3j" secondAttribute="trailing" constant="12" symbolic="YES" id="rUC-0t-9H9"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="8zu-cF-KCX" secondAttribute="bottom" constant="20" symbolic="YES" id="wIl-uw-y3p"/>
|
||||||
|
</constraints>
|
||||||
|
</visualEffectView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="U1c-qS-cIm">
|
||||||
|
<rect key="frame" x="98" y="153" width="384" height="19"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="380" id="WgB-hj-d4P"/>
|
||||||
|
</constraints>
|
||||||
|
<textFieldCell key="cell" selectable="YES" title="This is the title of the notice window." id="bzW-MI-jXb">
|
||||||
|
<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 horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yI6-qf-htf">
|
||||||
|
<rect key="frame" x="98" y="127" width="384" height="16"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" title="This is a slightly more expanded explanation." id="rY3-Nd-Iit">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0rX-Ss-3Xd">
|
||||||
|
<rect key="frame" x="12" y="144" width="48" 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="Uib-R1-GEx">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
</buttonCell>
|
||||||
|
</button>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QkM-5D-ZEQ">
|
||||||
|
<rect key="frame" x="20" y="111" width="64" height="64"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="64" id="VhJ-fI-IKC"/>
|
||||||
|
<constraint firstAttribute="width" constant="64" id="a2d-Gn-Oor"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="7eT-Hw-EL9"/>
|
||||||
|
</imageView>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="hml-dl-Cah">
|
||||||
|
<rect key="frame" x="98" y="70" width="384" height="42"/>
|
||||||
|
<textFieldCell key="cell" selectable="YES" id="7iW-Lc-DqO">
|
||||||
|
<font key="font" metaFont="smallSystem"/>
|
||||||
|
<string key="title">Sometimes you need a really long explanation and in that case you can get a really, really long description here, along with, for example, various steps you can take. This allows for a lot of text to be displayed, yay!</string>
|
||||||
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
</textField>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="hml-dl-Cah" firstAttribute="leading" secondItem="yI6-qf-htf" secondAttribute="leading" id="1Lh-ve-rwK"/>
|
||||||
|
<constraint firstItem="U1c-qS-cIm" firstAttribute="leading" secondItem="QkM-5D-ZEQ" secondAttribute="trailing" constant="16" id="2xX-Ma-iQZ"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="U1c-qS-cIm" secondAttribute="trailing" constant="20" symbolic="YES" id="39u-Uk-TDI"/>
|
||||||
|
<constraint firstItem="8zu-cF-KCX" firstAttribute="top" secondItem="hml-dl-Cah" secondAttribute="bottom" constant="30" id="7fT-EZ-cAO"/>
|
||||||
|
<constraint firstItem="JVG-5w-fPd" firstAttribute="top" secondItem="UPH-hV-Naz" secondAttribute="top" id="CE7-54-G09"/>
|
||||||
|
<constraint firstItem="hml-dl-Cah" firstAttribute="trailing" secondItem="yI6-qf-htf" secondAttribute="trailing" id="DBa-7d-sS3"/>
|
||||||
|
<constraint firstItem="yI6-qf-htf" firstAttribute="trailing" secondItem="U1c-qS-cIm" secondAttribute="trailing" id="NvJ-vf-wEl"/>
|
||||||
|
<constraint firstItem="hml-dl-Cah" firstAttribute="top" secondItem="yI6-qf-htf" secondAttribute="bottom" constant="15" id="Pmz-I9-2up"/>
|
||||||
|
<constraint firstItem="U1c-qS-cIm" firstAttribute="top" secondItem="UPH-hV-Naz" secondAttribute="top" constant="40" id="Uqt-sc-vxn"/>
|
||||||
|
<constraint firstItem="QkM-5D-ZEQ" firstAttribute="top" secondItem="U1c-qS-cIm" secondAttribute="top" constant="-3" id="WAj-rw-srg"/>
|
||||||
|
<constraint firstItem="yI6-qf-htf" firstAttribute="leading" secondItem="U1c-qS-cIm" secondAttribute="leading" id="bng-pH-jSG"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="JVG-5w-fPd" secondAttribute="trailing" id="dRt-Pq-6n0"/>
|
||||||
|
<constraint firstItem="JVG-5w-fPd" firstAttribute="leading" secondItem="UPH-hV-Naz" secondAttribute="leading" id="ejC-of-zjN"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="JVG-5w-fPd" secondAttribute="bottom" id="hGp-DD-cKr"/>
|
||||||
|
<constraint firstItem="QkM-5D-ZEQ" firstAttribute="leading" secondItem="UPH-hV-Naz" secondAttribute="leading" constant="20" symbolic="YES" id="jG8-dt-l4x"/>
|
||||||
|
<constraint firstItem="yI6-qf-htf" firstAttribute="top" secondItem="U1c-qS-cIm" secondAttribute="bottom" constant="10" id="kqR-yg-zdG"/>
|
||||||
|
</constraints>
|
||||||
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="buttonPrimary" destination="8zu-cF-KCX" id="MT5-Px-K97"/>
|
||||||
|
<outlet property="buttonSecondary" destination="TCp-nS-HN2" id="nPn-OX-Z4m"/>
|
||||||
|
<outlet property="buttonTertiary" destination="n5T-nn-k3j" id="UnB-8x-s3D"/>
|
||||||
|
<outlet property="imageView" destination="QkM-5D-ZEQ" id="zsW-l7-eH0"/>
|
||||||
|
<outlet property="labelDescription" destination="hml-dl-Cah" id="ehw-zs-EPc"/>
|
||||||
|
<outlet property="labelSubtitle" destination="yI6-qf-htf" id="m7A-bX-HE8"/>
|
||||||
|
<outlet property="labelTitle" destination="U1c-qS-cIm" id="oM3-kl-PL8"/>
|
||||||
|
<outlet property="primaryButtonTopMargin" destination="7fT-EZ-cAO" id="r6u-1l-dbl"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<customObject id="5Ts-EZ-bJh" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="38" y="1624"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Add SiteVC-->
|
<!--Add SiteVC-->
|
||||||
<scene sceneID="6JC-H6-u4K">
|
<scene sceneID="6JC-H6-u4K">
|
||||||
@ -472,8 +642,16 @@
|
|||||||
<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="251"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
|
<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"/>
|
||||||
|
<view key="contentView" id="HRC-RT-LxR">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="480" height="251"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
</view>
|
||||||
|
<color key="fillColor" name="windowBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</box>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PVw-cM-qAB">
|
||||||
<rect key="frame" x="13" y="13" width="103" height="32"/>
|
<rect key="frame" x="363" y="13" width="104" 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="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"/>
|
||||||
@ -486,7 +664,10 @@ 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="391" y="13" width="76" height="32"/>
|
<rect key="frame" x="13" y="13" width="94" height="32"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="80" id="qCP-Sp-gxm"/>
|
||||||
|
</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="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"/>
|
||||||
@ -539,7 +720,7 @@ Gw
|
|||||||
<rect key="frame" x="20" y="185" width="440" height="22"/>
|
<rect key="frame" x="20" y="185" 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/nicoverbruggen/Code/nicoverbruggen.be/"/>
|
<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">
|
||||||
@ -551,7 +732,7 @@ Gw
|
|||||||
</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="115" y="23" width="128" height="14"/>
|
<rect key="frame" x="229" y="23" width="128" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="That link already exists." id="jOt-n6-TQf">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="That link 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"/>
|
||||||
@ -562,27 +743,32 @@ Gw
|
|||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="VzR-5a-cmT" firstAttribute="trailing" secondItem="ZX9-s1-23i" secondAttribute="trailing" id="06B-dj-IBU"/>
|
<constraint firstItem="VzR-5a-cmT" firstAttribute="trailing" secondItem="ZX9-s1-23i" secondAttribute="trailing" id="06B-dj-IBU"/>
|
||||||
<constraint firstItem="ZX9-s1-23i" firstAttribute="top" secondItem="6JT-Vt-3q0" secondAttribute="bottom" constant="8" symbolic="YES" id="0QU-nI-sYv"/>
|
<constraint firstItem="ZX9-s1-23i" firstAttribute="top" secondItem="6JT-Vt-3q0" secondAttribute="bottom" constant="8" symbolic="YES" id="0QU-nI-sYv"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="SwS-o8-pbl" secondAttribute="bottom" constant="20" symbolic="YES" id="24Z-vC-4E8"/>
|
<constraint firstAttribute="bottom" secondItem="SwS-o8-pbl" secondAttribute="bottom" constant="20" symbolic="YES" id="2pB-nW-NVx"/>
|
||||||
<constraint firstItem="900-Z2-tID" firstAttribute="centerY" secondItem="PVw-cM-qAB" secondAttribute="centerY" id="578-2f-4x8"/>
|
<constraint firstItem="900-Z2-tID" firstAttribute="centerY" secondItem="PVw-cM-qAB" secondAttribute="centerY" id="578-2f-4x8"/>
|
||||||
<constraint firstItem="ZX9-s1-23i" firstAttribute="leading" secondItem="6JT-Vt-3q0" secondAttribute="trailing" constant="-440" id="6eF-GS-Xcn"/>
|
<constraint firstItem="ZX9-s1-23i" firstAttribute="leading" secondItem="6JT-Vt-3q0" secondAttribute="trailing" constant="-440" id="6eF-GS-Xcn"/>
|
||||||
<constraint firstItem="SwS-o8-pbl" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="900-Z2-tID" secondAttribute="trailing" constant="10" id="9uc-R7-CZk"/>
|
|
||||||
<constraint firstItem="6JT-Vt-3q0" firstAttribute="top" secondItem="P0B-Ht-R8n" secondAttribute="bottom" constant="8" symbolic="YES" id="DGN-4k-X0h"/>
|
<constraint firstItem="6JT-Vt-3q0" firstAttribute="top" secondItem="P0B-Ht-R8n" secondAttribute="bottom" constant="8" symbolic="YES" id="DGN-4k-X0h"/>
|
||||||
<constraint firstItem="P0B-Ht-R8n" firstAttribute="top" secondItem="JJJ-T9-Yuv" secondAttribute="top" constant="20" symbolic="YES" id="F2r-6E-qxh"/>
|
<constraint firstItem="P0B-Ht-R8n" firstAttribute="top" secondItem="JJJ-T9-Yuv" secondAttribute="top" constant="20" symbolic="YES" id="F2r-6E-qxh"/>
|
||||||
<constraint firstItem="mmQ-7e-dlb" firstAttribute="top" secondItem="KZf-b0-9cm" secondAttribute="bottom" constant="8" symbolic="YES" id="G21-Vd-tgl"/>
|
<constraint firstItem="mmQ-7e-dlb" firstAttribute="top" secondItem="KZf-b0-9cm" secondAttribute="bottom" constant="8" symbolic="YES" id="G21-Vd-tgl"/>
|
||||||
<constraint firstItem="900-Z2-tID" firstAttribute="leading" secondItem="PVw-cM-qAB" secondAttribute="trailing" constant="8" symbolic="YES" id="QzV-vP-fbq"/>
|
<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="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="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 firstItem="ZX9-s1-23i" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="bJ4-Yr-4ah"/>
|
<constraint firstItem="ZX9-s1-23i" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="bJ4-Yr-4ah"/>
|
||||||
<constraint firstItem="KZf-b0-9cm" firstAttribute="top" secondItem="VzR-5a-cmT" secondAttribute="bottom" constant="16" id="bdw-P7-FLz"/>
|
<constraint firstItem="KZf-b0-9cm" firstAttribute="top" secondItem="VzR-5a-cmT" secondAttribute="bottom" constant="16" id="bdw-P7-FLz"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="SwS-o8-pbl" secondAttribute="trailing" constant="20" symbolic="YES" id="bkx-g2-WCM"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="6JT-Vt-3q0" secondAttribute="trailing" constant="20" symbolic="YES" id="ctg-Gt-34Y"/>
|
<constraint firstAttribute="trailing" secondItem="6JT-Vt-3q0" secondAttribute="trailing" constant="20" symbolic="YES" id="ctg-Gt-34Y"/>
|
||||||
<constraint firstItem="PVw-cM-qAB" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="fE5-T7-e8z"/>
|
<constraint firstItem="PVw-cM-qAB" firstAttribute="leading" secondItem="900-Z2-tID" secondAttribute="trailing" constant="15" id="cx5-Gi-XS7"/>
|
||||||
<constraint firstItem="mmQ-7e-dlb" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="fKH-1r-MIf"/>
|
<constraint firstItem="mmQ-7e-dlb" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="fKH-1r-MIf"/>
|
||||||
|
<constraint firstItem="js9-OW-xzC" firstAttribute="top" secondItem="JJJ-T9-Yuv" secondAttribute="top" id="ffu-hT-qSK"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="js9-OW-xzC" secondAttribute="bottom" id="hLd-Kd-y6k"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="mmQ-7e-dlb" secondAttribute="trailing" constant="20" symbolic="YES" id="hjv-Xq-cxV"/>
|
<constraint firstAttribute="trailing" secondItem="mmQ-7e-dlb" secondAttribute="trailing" constant="20" symbolic="YES" id="hjv-Xq-cxV"/>
|
||||||
|
<constraint firstItem="SwS-o8-pbl" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="jkg-UC-GPr"/>
|
||||||
<constraint firstItem="6JT-Vt-3q0" firstAttribute="leading" secondItem="P0B-Ht-R8n" secondAttribute="leading" id="jxP-vM-eA9"/>
|
<constraint firstItem="6JT-Vt-3q0" firstAttribute="leading" secondItem="P0B-Ht-R8n" secondAttribute="leading" id="jxP-vM-eA9"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="PVw-cM-qAB" secondAttribute="bottom" constant="20" symbolic="YES" id="kGp-mI-1Ic"/>
|
||||||
<constraint firstItem="P0B-Ht-R8n" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="msC-eG-Fop"/>
|
<constraint firstItem="P0B-Ht-R8n" firstAttribute="leading" secondItem="JJJ-T9-Yuv" secondAttribute="leading" constant="20" symbolic="YES" id="msC-eG-Fop"/>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="P0B-Ht-R8n" secondAttribute="trailing" constant="20" symbolic="YES" id="nvj-Ij-dcd"/>
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="P0B-Ht-R8n" secondAttribute="trailing" constant="20" symbolic="YES" id="nvj-Ij-dcd"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="js9-OW-xzC" secondAttribute="trailing" id="rc3-XI-7CY"/>
|
||||||
<constraint firstItem="VzR-5a-cmT" firstAttribute="top" secondItem="ZX9-s1-23i" secondAttribute="bottom" constant="8" symbolic="YES" id="sVP-EV-07F"/>
|
<constraint firstItem="VzR-5a-cmT" firstAttribute="top" secondItem="ZX9-s1-23i" secondAttribute="bottom" constant="8" symbolic="YES" id="sVP-EV-07F"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="ZX9-s1-23i" secondAttribute="trailing" constant="20" symbolic="YES" id="tZ3-2X-JC9"/>
|
<constraint firstAttribute="trailing" secondItem="ZX9-s1-23i" secondAttribute="trailing" constant="20" symbolic="YES" id="tZ3-2X-JC9"/>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="KZf-b0-9cm" secondAttribute="trailing" constant="20" symbolic="YES" id="zq0-Ce-sCs"/>
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="KZf-b0-9cm" secondAttribute="trailing" constant="20" symbolic="YES" id="zq0-Ce-sCs"/>
|
||||||
@ -602,35 +788,68 @@ 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="277" y="1137.5"/>
|
<point key="canvasLocation" x="191" y="1098.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Site ListVC-->
|
<!--Site 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="siteList" storyboardIdentifier="siteList" id="JZI-Vd-9oq" customClass="SiteListVC" 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="600" height="309"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<customView id="j65-Lf-0lG">
|
<scrollView borderType="none" horizontalLineScroll="54" horizontalPageScroll="10" verticalLineScroll="54" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p0j-eB-I2i">
|
||||||
<rect key="frame" x="9" y="0.0" width="581" height="203"/>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="6IL-DW-37w">
|
||||||
</customView>
|
<rect key="frame" x="0.0" y="0.0" width="626" height="309"/>
|
||||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="54" horizontalPageScroll="10" verticalLineScroll="54" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p0j-eB-I2i">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="309"/>
|
|
||||||
<clipView key="contentView" id="6IL-DW-37w">
|
|
||||||
<rect key="frame" x="1" y="1" width="598" height="307"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" multipleSelection="NO" autosaveColumns="NO" rowHeight="54" rowSizeStyle="automatic" 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="598" height="307"/>
|
<rect key="frame" x="0.0" y="0.0" width="662" 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"/>
|
||||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||||
|
<color key="gridColor" name="quaternaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<tableColumns>
|
<tableColumns>
|
||||||
<tableColumn width="586" minWidth="40" maxWidth="10000" id="oeH-B2-0rA">
|
<tableColumn identifier="TLS" width="36" minWidth="36" maxWidth="36" id="z6X-Ni-Eev">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="TLS">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="OsS-YW-O4s">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Secure"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListTLSCell" id="hft-M4-nWb" customClass="SiteListTLSCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="18" y="0.0" width="34" height="55"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Wel-Ho-Kpp">
|
||||||
|
<rect key="frame" x="7" y="18" width="20" height="20"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="20" id="7mC-me-Nse"/>
|
||||||
|
<constraint firstAttribute="height" constant="20" id="AjD-xX-suI"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Lock" id="gK0-Mh-K9Y"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="Wel-Ho-Kpp" firstAttribute="centerY" secondItem="hft-M4-nWb" secondAttribute="centerY" id="L6B-iA-BCQ"/>
|
||||||
|
<constraint firstItem="Wel-Ho-Kpp" firstAttribute="centerX" secondItem="hft-M4-nWb" secondAttribute="centerX" id="jAF-AV-EeX"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageViewLock" destination="Wel-Ho-Kpp" id="iji-uw-8we"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="DOMAIN" width="290" minWidth="250" maxWidth="10000" id="oeH-B2-0rA">
|
||||||
|
<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"/>
|
||||||
</tableHeaderCell>
|
</tableHeaderCell>
|
||||||
@ -639,14 +858,15 @@ Gw
|
|||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
|
<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="siteItem" wantsLayer="YES" id="5GY-nN-BWd" customClass="SiteListCell" customModule="PHP_Monitor" customModuleProvider="target">
|
<tableCellView identifier="siteListNameCell" wantsLayer="YES" id="5GY-nN-BWd" customClass="SiteListNameCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
<rect key="frame" x="8" y="0.0" width="581" height="54"/>
|
<rect key="frame" x="69" y="0.0" width="290" 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">
|
||||||
<rect key="frame" x="38" y="26" width="145" height="16"/>
|
<rect key="frame" x="3" y="26" width="145" height="16"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="my-domain-name.test" id="SGC-Gm-Mxd">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="my-domain-name.test" id="SGC-Gm-Mxd">
|
||||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -654,106 +874,166 @@ Gw
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="CXK-Q9-CpO">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="CXK-Q9-CpO">
|
||||||
<rect key="frame" x="38" y="12" width="75" height="14"/>
|
<rect key="frame" x="3" y="12" width="75" height="14"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="~/path/to/site" id="fe7-Ha-mR9">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="~/path/to/site" id="fe7-Ha-mR9">
|
||||||
<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>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QPX-eu-eV8">
|
</subviews>
|
||||||
<rect key="frame" x="10" y="22" width="20" height="20"/>
|
<constraints>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="XJL-Uw-frD" secondAttribute="trailing" constant="20" symbolic="YES" id="62d-gz-2lX"/>
|
||||||
|
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="CXK-Q9-CpO" secondAttribute="trailing" constant="20" symbolic="YES" id="Agn-Ag-QYd"/>
|
||||||
|
<constraint firstItem="CXK-Q9-CpO" firstAttribute="leading" secondItem="XJL-Uw-frD" secondAttribute="leading" id="Ojw-VZ-3EG"/>
|
||||||
|
<constraint firstItem="XJL-Uw-frD" firstAttribute="top" secondItem="5GY-nN-BWd" secondAttribute="top" constant="12" id="QeE-c7-I9U"/>
|
||||||
|
<constraint firstItem="CXK-Q9-CpO" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="bottom" id="VKg-Vq-sYa"/>
|
||||||
|
<constraint firstItem="XJL-Uw-frD" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" constant="5" id="u1q-b0-iKq"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="labelPathName" destination="CXK-Q9-CpO" id="iVZ-cL-azB"/>
|
||||||
|
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="ENVIRONMENT" width="100" minWidth="100" maxWidth="150" id="hzb-XI-Out">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Active">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="ryK-6j-qWW">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="PHP"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListPhpCell" wantsLayer="YES" id="T49-0U-d58" customClass="SiteListPhpCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="376" y="0.0" width="100" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZXQ-bg-Xba">
|
||||||
|
<rect key="frame" x="27" y="18" width="70" height="18"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="20" id="Bmk-CN-Yyn"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="70" id="MBa-bB-DTB"/>
|
||||||
<constraint firstAttribute="height" constant="20" id="d4z-lb-Ww0"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Lock" id="aJ0-ia-YrZ"/>
|
<buttonCell key="cell" type="inline" title="PHP X.X" bezelStyle="inline" alignment="center" borderStyle="border" inset="2" id="Tfk-YR-L4B">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="smallSystemBold"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="pressedPhpVersion:" target="T49-0U-d58" id="jVO-TS-F6d"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Yq0-qk-bFt">
|
||||||
|
<rect key="frame" x="1" y="18" width="18" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="18" id="5fd-EQ-BgV"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="nP7-13-SSn"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="S66-ow-Jo1">
|
||||||
|
<imageReference key="image" image="Checkmark" symbolScale="default"/>
|
||||||
|
</imageCell>
|
||||||
|
<color key="contentTintColor" name="IconColorGreen"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jKi-Ls-7FZ">
|
</subviews>
|
||||||
<rect key="frame" x="474" y="28" width="64" height="11"/>
|
<constraints>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="DRIVER TYPE" id="fjd-eb-itv">
|
<constraint firstItem="Yq0-qk-bFt" firstAttribute="centerY" secondItem="T49-0U-d58" secondAttribute="centerY" id="4Kz-lX-kOP"/>
|
||||||
<font key="font" metaFont="miniSystem"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="leading" secondItem="Yq0-qk-bFt" secondAttribute="trailing" constant="8" symbolic="YES" id="6jM-Qf-SG3"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="centerY" secondItem="T49-0U-d58" secondAttribute="centerY" id="FFK-4B-qIb"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<constraint firstItem="ZXQ-bg-Xba" firstAttribute="centerX" secondItem="T49-0U-d58" secondAttribute="centerX" constant="12" id="zjr-UQ-Dd7"/>
|
||||||
</textFieldCell>
|
</constraints>
|
||||||
</textField>
|
<connections>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TbX-e2-3QL">
|
<outlet property="buttonPhpVersion" destination="ZXQ-bg-Xba" id="vxL-if-CCC"/>
|
||||||
<rect key="frame" x="474" y="15" width="36" height="14"/>
|
<outlet property="imageViewPhpVersionOK" destination="Yq0-qk-bFt" id="0hT-cZ-9NI"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Driver" id="GMt-SG-vFl">
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="KIND" width="36" minWidth="36" maxWidth="36" id="7EV-ZL-92u">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Kind">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="kAe-u7-lN6">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Kind"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListKindCell" wantsLayer="YES" id="AhT-xR-16a" customClass="SiteListKindCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="493" y="0.0" width="36" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="sYR-vb-OW1">
|
||||||
|
<rect key="frame" x="9" y="18" width="18" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="18" id="XcB-uw-szU"/>
|
||||||
|
<constraint firstAttribute="height" constant="18" id="bGN-Vh-Sh0"/>
|
||||||
|
</constraints>
|
||||||
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="IconLinked" id="T6e-IU-aZy"/>
|
||||||
|
<color key="contentTintColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</imageView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="sYR-vb-OW1" firstAttribute="centerY" secondItem="AhT-xR-16a" secondAttribute="centerY" id="4mB-oS-T6I"/>
|
||||||
|
<constraint firstItem="sYR-vb-OW1" firstAttribute="centerX" secondItem="AhT-xR-16a" secondAttribute="centerX" id="LyQ-XZ-J3u"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="imageViewType" destination="sYR-vb-OW1" id="txH-es-roq"/>
|
||||||
|
</connections>
|
||||||
|
</tableCellView>
|
||||||
|
</prototypeCellViews>
|
||||||
|
</tableColumn>
|
||||||
|
<tableColumn identifier="TYPE" width="100" minWidth="100" maxWidth="100" id="ncU-Ge-cyW">
|
||||||
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="center" title="Project Type">
|
||||||
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
</tableHeaderCell>
|
||||||
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="qHO-1M-TT2">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</textFieldCell>
|
||||||
|
<sortDescriptor key="sortDescriptorPrototype" selector="compare:" sortKey="Type"/>
|
||||||
|
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||||
|
<prototypeCellViews>
|
||||||
|
<tableCellView identifier="siteListTypeCell" wantsLayer="YES" id="ntU-Rl-ciP" customClass="SiteListTypeCell" customModule="PHP_Monitor" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="546" y="0.0" width="97" height="54"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ljl-8B-key">
|
||||||
|
<rect key="frame" x="6" y="26" width="93" height="14"/>
|
||||||
|
<textFieldCell key="cell" alignment="left" title="Laravel" id="0lu-L6-oKr">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<font key="font" metaFont="smallSystem"/>
|
||||||
<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>
|
||||||
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="syz-LF-l6P">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="aPK-Xc-J4B">
|
||||||
<rect key="frame" x="0.0" y="-2" width="581" height="5"/>
|
<rect key="frame" x="6" y="15" width="93" height="11"/>
|
||||||
</box>
|
<textFieldCell key="cell" alignment="left" title="PHP 8.0" id="puf-Jh-ham">
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="0NQ-ZD-CqD">
|
<font key="font" metaFont="miniSystem"/>
|
||||||
<rect key="frame" x="450" y="18" width="18" height="18"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<constraints>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
<constraint firstAttribute="width" constant="18" id="Suw-gm-AEi"/>
|
</textFieldCell>
|
||||||
<constraint firstAttribute="height" constant="18" id="qO6-vg-5nC"/>
|
</textField>
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="IconLinked" id="2ng-pK-kvv"/>
|
|
||||||
<color key="contentTintColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</imageView>
|
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3xt-wC-hUJ">
|
|
||||||
<rect key="frame" x="363" y="18" width="75" height="18"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="75" id="VI8-MP-7Hv"/>
|
|
||||||
</constraints>
|
|
||||||
<buttonCell key="cell" type="inline" title=" PHP X.X" bezelStyle="inline" alignment="center" borderStyle="border" inset="2" id="anZ-hP-G0R">
|
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
|
||||||
<font key="font" metaFont="smallSystemBold"/>
|
|
||||||
</buttonCell>
|
|
||||||
<color key="contentTintColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="pressedPhpVersion:" target="5GY-nN-BWd" id="mB5-WD-aZy"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5aN-ZI-D7U">
|
|
||||||
<rect key="frame" x="341" y="20" width="14" height="14"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="14" id="NKD-Pc-okU"/>
|
|
||||||
<constraint firstAttribute="width" constant="14" id="wrl-lJ-3eN"/>
|
|
||||||
</constraints>
|
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Checkmark" id="R5o-Cd-a91"/>
|
|
||||||
<color key="contentTintColor" name="IconColorGreen"/>
|
|
||||||
</imageView>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" secondItem="3xt-wC-hUJ" secondAttribute="trailing" constant="12" id="2G8-Ow-FTu"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="top" secondItem="Ljl-8B-key" secondAttribute="bottom" id="0Ta-4x-l8E"/>
|
||||||
<constraint firstItem="3xt-wC-hUJ" firstAttribute="leading" secondItem="5aN-ZI-D7U" secondAttribute="trailing" constant="8" symbolic="YES" id="39Z-nB-kXx"/>
|
<constraint firstItem="Ljl-8B-key" firstAttribute="centerY" secondItem="ntU-Rl-ciP" secondAttribute="centerY" constant="-6" id="9Lh-QK-qnR"/>
|
||||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TbX-e2-3QL" secondAttribute="trailing" constant="20" symbolic="YES" id="3vE-LR-S7N"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="leading" secondItem="Ljl-8B-key" secondAttribute="leading" id="PNb-yh-XOn"/>
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="leading" secondItem="0NQ-ZD-CqD" secondAttribute="trailing" constant="8" symbolic="YES" id="4cb-D9-8d1"/>
|
<constraint firstItem="aPK-Xc-J4B" firstAttribute="trailing" secondItem="Ljl-8B-key" secondAttribute="trailing" id="T28-JT-Zkq"/>
|
||||||
<constraint firstItem="XJL-Uw-frD" firstAttribute="leading" secondItem="QPX-eu-eV8" secondAttribute="trailing" constant="10" id="55y-3V-RYt"/>
|
<constraint firstAttribute="trailing" secondItem="Ljl-8B-key" secondAttribute="trailing" id="Y7O-lc-fqb"/>
|
||||||
<constraint firstItem="syz-LF-l6P" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" id="8QK-nf-Fiw"/>
|
<constraint firstItem="Ljl-8B-key" firstAttribute="leading" secondItem="ntU-Rl-ciP" secondAttribute="leading" constant="8" id="idV-Vu-YeP"/>
|
||||||
<constraint firstItem="QPX-eu-eV8" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="top" id="9QB-jZ-k1V"/>
|
|
||||||
<constraint firstItem="QPX-eu-eV8" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" constant="10" id="GOj-sw-ZlZ"/>
|
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="top" secondItem="jKi-Ls-7FZ" secondAttribute="bottom" constant="-1" id="J29-wT-Uex"/>
|
|
||||||
<constraint firstItem="CXK-Q9-CpO" firstAttribute="leading" secondItem="XJL-Uw-frD" secondAttribute="leading" id="Ojw-VZ-3EG"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="syz-LF-l6P" secondAttribute="trailing" id="PWd-5k-AlD"/>
|
|
||||||
<constraint firstItem="XJL-Uw-frD" firstAttribute="top" secondItem="5GY-nN-BWd" secondAttribute="top" constant="12" id="QeE-c7-I9U"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" id="Utr-aa-tqX"/>
|
|
||||||
<constraint firstItem="CXK-Q9-CpO" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="bottom" id="VKg-Vq-sYa"/>
|
|
||||||
<constraint firstItem="5aN-ZI-D7U" firstAttribute="centerY" secondItem="3xt-wC-hUJ" secondAttribute="centerY" id="a6n-E2-i2x"/>
|
|
||||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" constant="5" id="cN8-zO-fnc"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="syz-LF-l6P" secondAttribute="bottom" id="gj7-cJ-Lle"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="CXK-Q9-CpO" secondAttribute="trailing" constant="8" symbolic="YES" id="iEd-Y3-zhp"/>
|
|
||||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="XJL-Uw-frD" secondAttribute="trailing" constant="8" symbolic="YES" id="lLA-Jx-Q4W"/>
|
|
||||||
<constraint firstItem="3xt-wC-hUJ" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" id="vhb-WC-3NC"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="jKi-Ls-7FZ" secondAttribute="trailing" constant="45" id="vwD-Sg-Lzc"/>
|
|
||||||
<constraint firstItem="jKi-Ls-7FZ" firstAttribute="leading" secondItem="TbX-e2-3QL" secondAttribute="leading" id="zjN-s3-2Ww"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="buttonPhpVersion" destination="3xt-wC-hUJ" id="LpB-7n-qUr"/>
|
<outlet property="labelDriver" destination="Ljl-8B-key" id="82M-LT-pHT"/>
|
||||||
<outlet property="imageViewLock" destination="QPX-eu-eV8" id="Nnh-kB-adG"/>
|
<outlet property="labelPhpVersion" destination="aPK-Xc-J4B" id="TdR-xE-xhX"/>
|
||||||
<outlet property="imageViewPhpVersionOK" destination="5aN-ZI-D7U" id="ePz-Cb-dWk"/>
|
|
||||||
<outlet property="imageViewType" destination="0NQ-ZD-CqD" id="Cph-FN-LaY"/>
|
|
||||||
<outlet property="labelDriver" destination="TbX-e2-3QL" id="qJh-Ak-Dge"/>
|
|
||||||
<outlet property="labelDriverType" destination="jKi-Ls-7FZ" id="ZTq-pP-qUC"/>
|
|
||||||
<outlet property="labelPathName" destination="CXK-Q9-CpO" id="iVZ-cL-azB"/>
|
|
||||||
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</tableCellView>
|
</tableCellView>
|
||||||
</prototypeCellViews>
|
</prototypeCellViews>
|
||||||
@ -765,22 +1045,27 @@ Gw
|
|||||||
</connections>
|
</connections>
|
||||||
</tableView>
|
</tableView>
|
||||||
</subviews>
|
</subviews>
|
||||||
|
<nil key="backgroundColor"/>
|
||||||
</clipView>
|
</clipView>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="R3Z-g3-tYQ"/>
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="300" id="R3Z-g3-tYQ"/>
|
||||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="600" id="iRQ-sz-oyv"/>
|
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="620" id="iRQ-sz-oyv"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
<scroller key="horizontalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="TDE-ff-DQT">
|
||||||
<rect key="frame" x="1" y="292" width="598" height="16"/>
|
<rect key="frame" x="0.0" y="293" width="626" height="16"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="wFn-93-f10">
|
||||||
<rect key="frame" x="558" y="29" width="15" height="225"/>
|
<rect key="frame" x="610" y="28" width="16" height="281"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
|
<tableHeaderView key="headerView" wantsLayer="YES" id="xUg-Mq-OSh">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="662" height="28"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</tableHeaderView>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
<progressIndicator maxValue="100" displayedWhenStopped="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="ZiS-Gq-TLQ">
|
||||||
<rect key="frame" x="285" y="150" width="30" height="30"/>
|
<rect key="frame" x="298" y="150" width="30" height="30"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
<constraint firstAttribute="width" constant="30" id="XK3-AR-Oc0"/>
|
||||||
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
<constraint firstAttribute="height" constant="30" id="lfW-dB-Eu3"/>
|
||||||
@ -803,7 +1088,7 @@ 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="251" y="741.5"/>
|
<point key="canvasLocation" x="388" y="715.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -56,7 +56,10 @@ class InterApp {
|
|||||||
if PhpEnv.shared.availablePhpVersions.contains(version) {
|
if PhpEnv.shared.availablePhpVersions.contains(version) {
|
||||||
MainMenu.shared.switchToPhpVersion(version)
|
MainMenu.shared.switchToPhpVersion(version)
|
||||||
} else {
|
} else {
|
||||||
Alert.notify(message: "Unsupported version", info: "PHP Monitor can't switch to PHP \(version), as it may not be installed or available.")
|
BetterAlert().withInformation(
|
||||||
|
title: "Unsupported version",
|
||||||
|
subtitle: "PHP Monitor can't switch to PHP \(version), as it may not be installed or available."
|
||||||
|
).withPrimary(text: "OK").show()
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
]}
|
]}
|
||||||
|
@ -10,88 +10,70 @@ import AppKit
|
|||||||
|
|
||||||
class Startup {
|
class Startup {
|
||||||
|
|
||||||
public var failed : Bool = false
|
|
||||||
public var failureCallback = {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Checks the user's environment and checks if PHP Monitor can be used properly.
|
Checks the user's environment and checks if PHP Monitor can be used properly.
|
||||||
This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more.
|
This checks if PHP is installed, Valet is running, the appropriate permissions are set, and more.
|
||||||
|
|
||||||
- Parameter success: Callback that is fired if the application can proceed with launch
|
If this method returns false, there was a failed check and an alert was displayed.
|
||||||
- Parameter failure: Callback that is fired if the application must retry launch
|
If this method returns true, then all checks succeeded and the app can continue.
|
||||||
*/
|
*/
|
||||||
func checkEnvironment(success: () -> Void, failure: @escaping () -> Void)
|
func checkEnvironment() async -> Bool
|
||||||
{
|
{
|
||||||
failureCallback = failure
|
// Do the important system setup checks
|
||||||
|
Log.info("[ARCH] The user is running PHP Monitor with the architecture: \(App.architecture)")
|
||||||
|
|
||||||
performEnvironmentCheck(
|
for check in self.checks {
|
||||||
!Shell.fileExists("\(Paths.binPath)/php"),
|
if await check.succeeds() {
|
||||||
messageText: "startup.errors.php_binary.title".localized,
|
Log.info("[OK] \(check.name)")
|
||||||
informativeText: "startup.errors.php_binary.desc".localized,
|
continue
|
||||||
breaking: true
|
}
|
||||||
)
|
|
||||||
|
// If we get here, something's gone wrong and the check has failed...
|
||||||
|
Log.info("[FAIL] \(check.name)")
|
||||||
|
showAlert(for: check)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
performEnvironmentCheck(
|
// If we get here, nothing has gone wrong. That's what we want!
|
||||||
!Shell.pipe("ls \(Paths.optPath) | grep php").contains("php"),
|
initializeSwitcher()
|
||||||
messageText: "startup.errors.php_opt.title".localized,
|
Log.separator(as: .info)
|
||||||
informativeText: "startup.errors.php_opt.desc".localized,
|
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
||||||
breaking: true
|
return true
|
||||||
)
|
}
|
||||||
|
|
||||||
performEnvironmentCheck(
|
/**
|
||||||
// Check for Valet; it can be symlinked or in .composer/vendor/bin
|
Displays an alert for a particular check. There are two types of alerts:
|
||||||
!(Shell.fileExists("/usr/local/bin/valet")
|
- ones that require an app restart, which prompt the user to exit the app
|
||||||
|| Shell.fileExists("/opt/homebrew/bin/valet")
|
- ones that allow the app to continue, which allow the user to retry
|
||||||
|| Shell.fileExists("~/.composer/vendor/bin/valet")
|
*/
|
||||||
),
|
private func showAlert(for check: EnvironmentCheck) {
|
||||||
messageText: "startup.errors.valet_executable.title".localized,
|
DispatchQueue.main.async {
|
||||||
informativeText: "startup.errors.valet_executable.desc".localized,
|
if check.requiresAppRestart {
|
||||||
breaking: true
|
BetterAlert()
|
||||||
)
|
.withInformation(
|
||||||
|
title: check.titleText,
|
||||||
performEnvironmentCheck(
|
subtitle: check.subtitleText,
|
||||||
HomebrewDiagnostics.cannotLoadService(),
|
description: check.descriptionText
|
||||||
messageText: "startup.errors.services_json_error.title".localized,
|
)
|
||||||
informativeText: "startup.errors.services_json_error.desc".localized,
|
.withPrimary(text: check.buttonText, action: { _ in
|
||||||
breaking: true
|
exit(1)
|
||||||
)
|
}).show()
|
||||||
|
}
|
||||||
performEnvironmentCheck(
|
|
||||||
!Shell.pipe("cat /private/etc/sudoers.d/brew").contains("\(Paths.binPath)/brew"),
|
BetterAlert()
|
||||||
messageText: "startup.errors.sudoers_brew.title".localized,
|
.withInformation(
|
||||||
informativeText: "startup.errors.sudoers_brew.desc".localized,
|
title: check.titleText,
|
||||||
breaking: true
|
subtitle: check.subtitleText,
|
||||||
)
|
description: check.descriptionText
|
||||||
|
)
|
||||||
performEnvironmentCheck(
|
.withPrimary(text: "OK")
|
||||||
// Check for Valet; it MUST be symlinked thanks to sudoers
|
.show()
|
||||||
!(Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/usr/local/bin/valet")
|
|
||||||
|| Shell.pipe("cat /private/etc/sudoers.d/valet").contains("/opt/homebrew/bin/valet")
|
|
||||||
),
|
|
||||||
messageText: "startup.errors.sudoers_valet.title".localized,
|
|
||||||
informativeText: "startup.errors.sudoers_valet.desc".localized,
|
|
||||||
breaking: true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Determine the Valet version only AFTER confirming the correct permission is in place
|
|
||||||
Valet.shared.version = VersionExtractor.from(valet("--version"))
|
|
||||||
performEnvironmentCheck(
|
|
||||||
Valet.shared.version == nil,
|
|
||||||
messageText: "startup.errors.valet_version_unknown.title".localized,
|
|
||||||
informativeText: "startup.errors.valet_version_unknown.desc".localized,
|
|
||||||
breaking: true
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!failed) {
|
|
||||||
initializeSwitcher()
|
|
||||||
Log.info("PHP Monitor has determined the application has successfully passed all checks.")
|
|
||||||
success()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Because the Switcher requires various environment guarantees, the switcher is only
|
Because the Switcher requires various environment guarantees, the switcher is only
|
||||||
initialized when it is done working.
|
initialized when it is done working. The switcher must be initialized on the main thread.
|
||||||
*/
|
*/
|
||||||
private func initializeSwitcher() {
|
private func initializeSwitcher() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
@ -99,35 +81,157 @@ class Startup {
|
|||||||
appDelegate.initializeSwitcher()
|
appDelegate.initializeSwitcher()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// MARK: - Check (List)
|
||||||
Perform an environment check. Will cause the application to terminate, if `breaking` is set to true.
|
|
||||||
|
public var checks: [EnvironmentCheck] = [
|
||||||
- Parameter condition: Fail condition to check for; if this returns `true`, the alert will be shown
|
// =================================================================================
|
||||||
- Parameter messageText: Short description of what is wrong
|
// The Homebrew binary must exist.
|
||||||
- Parameter informativeText: Expanded description of the environment check that failed
|
// =================================================================================
|
||||||
- Parameter breaking: If the application should terminate afterwards
|
EnvironmentCheck(
|
||||||
*/
|
command: { return !FileManager.default.fileExists(atPath: Paths.brew) },
|
||||||
private func performEnvironmentCheck(
|
name: "`\(Paths.brew)` exists",
|
||||||
_ condition: Bool,
|
titleText: "alert.homebrew_missing.title".localized,
|
||||||
messageText: String,
|
subtitleText: "alert.homebrew_missing.subtitle".localized,
|
||||||
informativeText: String,
|
descriptionText: "alert.homebrew_missing.info".localized(
|
||||||
breaking: Bool
|
App.architecture
|
||||||
) {
|
.replacingOccurrences(of: "x86_64", with: "Intel")
|
||||||
if (!condition) { return }
|
.replacingOccurrences(of: "arm64", with: "Apple Silicon"),
|
||||||
|
Paths.brew
|
||||||
failed = breaking
|
),
|
||||||
|
buttonText: "alert.homebrew_missing.quit".localized,
|
||||||
DispatchQueue.main.async { [self] in
|
requiresAppRestart: true
|
||||||
// Present the information to the user
|
),
|
||||||
Alert.notify(
|
// =================================================================================
|
||||||
message: messageText,
|
// The PHP binary must exist.
|
||||||
info: informativeText,
|
// =================================================================================
|
||||||
style: breaking ? .critical : .warning
|
EnvironmentCheck(
|
||||||
|
command: { return !Filesystem.fileExists(Paths.php) },
|
||||||
|
name: "`\(Paths.php)` exists",
|
||||||
|
titleText: "startup.errors.php_binary.title".localized,
|
||||||
|
subtitleText: "startup.errors.php_binary.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.php_binary.desc".localized(Paths.php)
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// Make sure we can detect one or more PHP installations.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: { return !Shell.pipe("ls \(Paths.optPath) | grep php").contains("php") },
|
||||||
|
name: "`ls \(Paths.optPath) | grep php` returned php result",
|
||||||
|
titleText: "startup.errors.php_opt.title".localized,
|
||||||
|
subtitleText: "startup.errors.php_opt.subtitle".localized(
|
||||||
|
Paths.optPath
|
||||||
|
),
|
||||||
|
descriptionText: "startup.errors.php_opt.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// The Valet binary must exist.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
return !(Filesystem.fileExists(Paths.valet) || Filesystem.fileExists("~/.composer/vendor/bin/valet"))
|
||||||
|
},
|
||||||
|
name: "`valet` binary exists",
|
||||||
|
titleText: "startup.errors.valet_executable.title".localized,
|
||||||
|
subtitleText: "startup.errors.valet_executable.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.valet_executable.desc".localized(
|
||||||
|
Paths.valet
|
||||||
)
|
)
|
||||||
// Only breaking issues will throw the extra retry modal
|
),
|
||||||
breaking ? failureCallback() : ()
|
// =================================================================================
|
||||||
|
// Check if Valet and Homebrew need manual password intervention. If they do, then
|
||||||
|
// PHP Monitor will be unable to run these commands, which prevents PHP Monitor from
|
||||||
|
// functioning correctly. Let the user know that they need to run `valet trust`.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: { return !Shell.pipe("cat /private/etc/sudoers.d/brew").contains(Paths.brew) },
|
||||||
|
name: "`/private/etc/sudoers.d/brew` contains brew",
|
||||||
|
titleText: "startup.errors.sudoers_brew.title".localized,
|
||||||
|
subtitleText: "startup.errors.sudoers_brew.subtitle".localized
|
||||||
|
),
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: { return !Shell.pipe("cat /private/etc/sudoers.d/valet").contains(Paths.valet) },
|
||||||
|
name: "`/private/etc/sudoers.d/valet` contains valet",
|
||||||
|
titleText: "startup.errors.sudoers_valet.title".localized,
|
||||||
|
subtitleText: "startup.errors.sudoers_valet.subtitle".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// Verify if the Homebrew services are running (as root).
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: { return HomebrewDiagnostics.cannotLoadService() },
|
||||||
|
name: "`sudo \(Paths.brew) services info` JSON loaded",
|
||||||
|
titleText: "startup.errors.services_json_error.title".localized,
|
||||||
|
subtitleText: "startup.errors.services_json_error.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.services_json_error.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// Determine that the Valet configuration JSON file is valid.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
// Detect additional binaries (e.g. Composer)
|
||||||
|
Paths.shared.detectBinaryPaths()
|
||||||
|
// Load the configuration file (config.json)
|
||||||
|
Valet.shared.loadConfiguration()
|
||||||
|
// This check fails when the config is nil
|
||||||
|
return Valet.shared.config == nil
|
||||||
|
},
|
||||||
|
name: "`config.json` was valid",
|
||||||
|
titleText: "startup.errors.valet_json_invalid.title".localized,
|
||||||
|
subtitleText: "startup.errors.valet_json_invalid.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.valet_json_invalid.desc".localized
|
||||||
|
),
|
||||||
|
// =================================================================================
|
||||||
|
// Determine the Valet version and ensure it isn't unknown.
|
||||||
|
// =================================================================================
|
||||||
|
EnvironmentCheck(
|
||||||
|
command: {
|
||||||
|
Valet.shared.version = VersionExtractor.from(valet("--version", sudo: false))
|
||||||
|
return Valet.shared.version == nil
|
||||||
|
},
|
||||||
|
name: "`valet --version` was loaded",
|
||||||
|
titleText: "startup.errors.valet_version_unknown.title".localized,
|
||||||
|
subtitleText: "startup.errors.valet_version_unknown.subtitle".localized,
|
||||||
|
descriptionText: "startup.errors.valet_version_unknown.desc".localized
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
// MARK: - EnvironmentCheck struct
|
||||||
|
|
||||||
|
/**
|
||||||
|
The `EnvironmentCheck` is used to defer the execution of all of these commands until necessary.
|
||||||
|
Checks that require an app restart will always lead to an alert and app termination shortly after.
|
||||||
|
*/
|
||||||
|
struct EnvironmentCheck {
|
||||||
|
let command: () async -> Bool
|
||||||
|
let name: String
|
||||||
|
let titleText: String
|
||||||
|
let subtitleText: String
|
||||||
|
let descriptionText: String
|
||||||
|
let buttonText: String
|
||||||
|
let requiresAppRestart: Bool
|
||||||
|
|
||||||
|
init(
|
||||||
|
command: @escaping () async -> Bool,
|
||||||
|
name: String,
|
||||||
|
titleText: String,
|
||||||
|
subtitleText: String,
|
||||||
|
descriptionText: String = "",
|
||||||
|
buttonText: String = "OK",
|
||||||
|
requiresAppRestart: Bool = false
|
||||||
|
) {
|
||||||
|
self.command = command
|
||||||
|
self.name = name
|
||||||
|
self.titleText = titleText
|
||||||
|
self.subtitleText = subtitleText
|
||||||
|
self.descriptionText = descriptionText
|
||||||
|
self.buttonText = buttonText
|
||||||
|
self.requiresAppRestart = requiresAppRestart
|
||||||
|
}
|
||||||
|
|
||||||
|
public func succeeds() async -> Bool {
|
||||||
|
return await !self.command()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ struct ComposerJson: Decodable {
|
|||||||
struct Config: Decodable {
|
struct Config: Decodable {
|
||||||
let platform: Platform?
|
let platform: Platform?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Platform: Decodable {
|
struct Platform: Decodable {
|
||||||
let php: String?
|
let php: String?
|
||||||
}
|
}
|
||||||
@ -39,19 +40,20 @@ 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, String) {
|
public func getPhpVersion() -> (String, ValetSite.VersionSource)
|
||||||
|
{
|
||||||
// Check if in platform
|
// Check if in platform
|
||||||
if configuration?.platform?.php != nil {
|
if configuration?.platform?.php != nil {
|
||||||
return (configuration!.platform!.php!, "platform")
|
return (configuration!.platform!.php!, .platform)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if in dependencies
|
// Check if in dependencies
|
||||||
if dependencies?["php"] != nil {
|
if dependencies?["php"] != nil {
|
||||||
return (dependencies!["php"]!, "require")
|
return (dependencies!["php"]!, .require)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown!
|
// Unknown!
|
||||||
return ("???", "unknown")
|
return ("???", .unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
128
phpmon/Domain/Integrations/Composer/ComposerWindow.swift
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+Composer.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 08/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ComposerWindow {
|
||||||
|
|
||||||
|
private var menu: MainMenu? = nil
|
||||||
|
private var shouldNotify: Bool! = nil
|
||||||
|
private var completion: ((Bool) -> Void)! = nil
|
||||||
|
private var window: ProgressWindowController? = nil
|
||||||
|
|
||||||
|
/**
|
||||||
|
Updates the global dependencies and runs the completion callback when done.
|
||||||
|
*/
|
||||||
|
func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
||||||
|
self.menu = MainMenu.shared
|
||||||
|
self.shouldNotify = notify
|
||||||
|
self.completion = completion
|
||||||
|
|
||||||
|
Paths.shared.detectBinaryPaths()
|
||||||
|
if Paths.composer == nil {
|
||||||
|
presentMissingAlert()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PhpEnv.shared.isBusy = true
|
||||||
|
menu?.setBusyImage()
|
||||||
|
menu?.rebuild()
|
||||||
|
|
||||||
|
window = ProgressWindowController.display(
|
||||||
|
title: "alert.composer_progress.title".localized,
|
||||||
|
description: "alert.composer_progress.info".localized
|
||||||
|
)
|
||||||
|
|
||||||
|
window?.setType(info: true)
|
||||||
|
|
||||||
|
DispatchQueue.global(qos: .userInitiated).async { [self] in
|
||||||
|
let task = Shell.user.createTask(
|
||||||
|
for: "\(Paths.composer!) global update", requiresPath: true
|
||||||
|
)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.window?.addToConsole("\(Paths.composer!) global update\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
task.listen(
|
||||||
|
didReceiveStandardOutputData: { string in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.window?.addToConsole(string)
|
||||||
|
}
|
||||||
|
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||||
|
},
|
||||||
|
didReceiveStandardErrorData: { string in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.window?.addToConsole(string)
|
||||||
|
}
|
||||||
|
// Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
task.launch()
|
||||||
|
task.waitUntilExit()
|
||||||
|
task.haltListening()
|
||||||
|
|
||||||
|
if task.terminationStatus <= 0 {
|
||||||
|
composerUpdateSucceeded()
|
||||||
|
} else {
|
||||||
|
composerUpdateFailed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func composerUpdateSucceeded() {
|
||||||
|
// Closing the window should happen after a slight delay
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [self] in
|
||||||
|
window?.close()
|
||||||
|
if (shouldNotify) {
|
||||||
|
LocalNotification.send(
|
||||||
|
title: "alert.composer_success.title".localized,
|
||||||
|
subtitle: "alert.composer_success.info".localized
|
||||||
|
)
|
||||||
|
}
|
||||||
|
window = nil
|
||||||
|
removeBusyStatus()
|
||||||
|
completion(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func composerUpdateFailed() {
|
||||||
|
// Showing that something failed should be shown immediately
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
window?.setType(info: false)
|
||||||
|
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
||||||
|
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
||||||
|
window = nil
|
||||||
|
removeBusyStatus()
|
||||||
|
completion(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Main Menu Update
|
||||||
|
|
||||||
|
private func removeBusyStatus() {
|
||||||
|
PhpEnv.shared.isBusy = false
|
||||||
|
DispatchQueue.main.async { [self] in
|
||||||
|
menu?.updatePhpVersionInStatusBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Alert
|
||||||
|
|
||||||
|
private func presentMissingAlert() {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.composer_missing.title".localized,
|
||||||
|
subtitle: "alert.composer_missing.subtitle".localized,
|
||||||
|
description: "alert.composer_missing.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
@ -36,7 +36,8 @@ struct PhpFrameworks {
|
|||||||
"statamic/cms": "Statamic",
|
"statamic/cms": "Statamic",
|
||||||
"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",
|
||||||
|
|
||||||
// TODO (5.1): Handle these in v5.1
|
// TODO (5.1): Handle these in v5.1
|
||||||
// "magento/*": "Magento",
|
// "magento/*": "Magento",
|
||||||
@ -58,6 +59,10 @@ struct PhpFrameworks {
|
|||||||
"/wp-config.php",
|
"/wp-config.php",
|
||||||
"/wp-config-sample.php"
|
"/wp-config-sample.php"
|
||||||
],
|
],
|
||||||
|
"Typo3": [
|
||||||
|
"/typo3",
|
||||||
|
"/public/typo3",
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
39
phpmon/Domain/Integrations/Valet/NginxConfigParser.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// 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)"
|
||||||
|
}()
|
||||||
|
}
|
134
phpmon/Domain/Integrations/Valet/SiteScanner.swift
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// 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?
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return paths.map { path in
|
||||||
|
|
||||||
|
let entries = try! FileManager.default
|
||||||
|
.contentsOfDirectory(atPath: path)
|
||||||
|
|
||||||
|
return entries
|
||||||
|
.map { self.isSite($0, forPath: path) }
|
||||||
|
.filter{ $0 == true}
|
||||||
|
.count
|
||||||
|
|
||||||
|
}.reduce(0, +)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resolveSitesFrom(paths: [String]) -> [ValetSite] {
|
||||||
|
var sites: [ValetSite] = []
|
||||||
|
|
||||||
|
paths.forEach { path in
|
||||||
|
let entries = try! FileManager.default
|
||||||
|
.contentsOfDirectory(atPath: path)
|
||||||
|
|
||||||
|
return entries.forEach {
|
||||||
|
if let site = self.resolveSite(path: "\(path)/\($0)") {
|
||||||
|
sites.append(site)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sites
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether the site can be resolved as a symbolic link or as a directory.
|
||||||
|
Regular files are ignored, and the site is added to Valet's list of sites.
|
||||||
|
*/
|
||||||
|
func resolveSite(path: String) -> ValetSite? {
|
||||||
|
// Get the TLD from the global Valet object
|
||||||
|
let tld = Valet.shared.config.tld
|
||||||
|
|
||||||
|
// See if the file is a symlink, if so, resolve it
|
||||||
|
guard let attrs = try? FileManager.default.attributesOfItem(atPath: path) else {
|
||||||
|
Log.warn("Could not parse the site: \(path), skipping!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can also determine whether the thing at the path is a directory, too
|
||||||
|
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
||||||
|
|
||||||
|
// We should also check that we can interpret the path correctly
|
||||||
|
if URL(fileURLWithPath: path).lastPathComponent == "" {
|
||||||
|
Log.warn("Could not parse the site: \(path), skipping!")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == FileAttributeType.typeSymbolicLink {
|
||||||
|
return ValetSite(aliasPath: path, tld: tld)
|
||||||
|
} else if type == FileAttributeType.typeDirectory {
|
||||||
|
return ValetSite(absolutePath: path, tld: tld)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determines whether the site can be resolved as a symbolic link or as a directory.
|
||||||
|
Regular files are ignored. Returns true if the path can be parsed.
|
||||||
|
*/
|
||||||
|
private func isSite(_ entry: String, forPath path: String) -> Bool {
|
||||||
|
let siteDir = path + "/" + entry
|
||||||
|
|
||||||
|
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
||||||
|
|
||||||
|
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
||||||
|
|
||||||
|
if type == FileAttributeType.typeSymbolicLink || type == FileAttributeType.typeDirectory {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
@ -10,6 +10,11 @@ import Foundation
|
|||||||
|
|
||||||
class Valet {
|
class Valet {
|
||||||
|
|
||||||
|
enum FeatureFlag {
|
||||||
|
case isolatedSites,
|
||||||
|
supportForPhp56
|
||||||
|
}
|
||||||
|
|
||||||
static let shared = Valet()
|
static let shared = Valet()
|
||||||
|
|
||||||
/// The version of Valet that was detected.
|
/// The version of Valet that was detected.
|
||||||
@ -19,33 +24,61 @@ class Valet {
|
|||||||
var config: Valet.Configuration!
|
var config: Valet.Configuration!
|
||||||
|
|
||||||
/// A cached list of sites that were detected after analyzing the paths set up for Valet.
|
/// A cached list of sites that were detected after analyzing the paths set up for Valet.
|
||||||
var sites: [Site] = []
|
var sites: [ValetSite] = []
|
||||||
|
|
||||||
/// Whether we're busy with some blocking operation.
|
/// Whether we're busy with some blocking operation.
|
||||||
var isBusy: Bool = false
|
var isBusy: Bool = false
|
||||||
|
|
||||||
/// When initialising the Valet singleton, extract the Valet version and assume no sites loaded.
|
/// Various feature flags. Enabled based on the installed Valet version.
|
||||||
|
var features: [FeatureFlag] = []
|
||||||
|
|
||||||
|
/// When initialising the Valet singleton assume no sites loaded. We will load the version later.
|
||||||
init() {
|
init() {
|
||||||
self.version = nil
|
self.version = nil
|
||||||
self.sites = []
|
self.sites = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
If marketing mode is enabled, show a list of sites that are used for promotional screenshots.
|
||||||
|
This can be done by swapping out the real Valet scanner with one that always returns a fixed
|
||||||
|
list of fake sites. You should not interact with these sites!
|
||||||
|
*/
|
||||||
|
static var siteScanner: SiteScanner {
|
||||||
|
if ProcessInfo.processInfo.environment["PHPMON_MARKETING_MODE"] != nil {
|
||||||
|
return FakeSiteScanner()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValetSiteScanner()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if a particular feature is enabled.
|
||||||
|
*/
|
||||||
|
public static func enabled(feature: FeatureFlag) -> Bool {
|
||||||
|
return self.shared.features.contains(feature)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
We don't want to load the initial config.json file as soon as the class is initialised.
|
We don't want to load the initial config.json file as soon as the class is initialised.
|
||||||
|
|
||||||
Instead, we'll defer the loading of the configuration file once the initial app checks
|
Instead, we'll defer the loading of the configuration file once the initial app checks
|
||||||
have passed: if the user does not have Valet installed, we'll crash the app because we
|
have passed: otherwise the file might not exist, leading to a crash.
|
||||||
force unwrap the file. Currently, this does also mean that if the JSON is invalid or
|
|
||||||
incompatible with the `Decodable` `Valet.Configuration` class, that the app will crash.
|
Since version 5.2, it is no longer possible for an invalid file to crash the app.
|
||||||
|
If the JSON is invalid when the app launches, an alert will be presented, however.
|
||||||
*/
|
*/
|
||||||
public func loadConfiguration() {
|
public func loadConfiguration() {
|
||||||
let file = FileManager.default.homeDirectoryForCurrentUser
|
let file = FileManager.default.homeDirectoryForCurrentUser
|
||||||
.appendingPathComponent(".config/valet/config.json")
|
.appendingPathComponent(".config/valet/config.json")
|
||||||
|
|
||||||
// TODO: (5.1) Fix loading of invalid JSON: do not crash the app
|
do {
|
||||||
config = try! JSONDecoder().decode(
|
config = try JSONDecoder().decode(
|
||||||
Valet.Configuration.self,
|
Valet.Configuration.self,
|
||||||
from: try! String(contentsOf: file, encoding: .utf8).data(using: .utf8)!
|
from: try String(contentsOf: file, encoding: .utf8).data(using: .utf8)!
|
||||||
)
|
)
|
||||||
|
} catch {
|
||||||
|
Log.err(error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +87,7 @@ class Valet {
|
|||||||
(This is done to keep the startup speed as fast as possible.)
|
(This is done to keep the startup speed as fast as possible.)
|
||||||
*/
|
*/
|
||||||
public func startPreloadingSites() {
|
public func startPreloadingSites() {
|
||||||
let maximumPreload = 30
|
let maximumPreload = 50
|
||||||
let foundSites = self.countPaths()
|
let foundSites = self.countPaths()
|
||||||
if foundSites <= maximumPreload {
|
if foundSites <= maximumPreload {
|
||||||
// Preload the sites and their drivers
|
// Preload the sites and their drivers
|
||||||
@ -67,14 +100,34 @@ class Valet {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Reloads the list of sites, assuming that the list isn't being reloaded at the time.
|
Reloads the list of sites, assuming that the list isn't being reloaded at the time.
|
||||||
We don't want to do duplicate or parallel work!
|
(We don't want to do duplicate or parallel work!)
|
||||||
*/
|
*/
|
||||||
public func reloadSites() {
|
public func reloadSites() {
|
||||||
|
loadConfiguration()
|
||||||
|
|
||||||
if (isBusy) {
|
if (isBusy) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePaths(tld: config.tld)
|
resolvePaths()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Depending on the version of Valet that is active, the feature set of PHP Monitor will change.
|
||||||
|
|
||||||
|
In version 6.0, support for Valet 2.x will be dropped, but until then features are evaluated by using the helper
|
||||||
|
`enabled(feature)`, which contains information about the feature set of the version of Valet that is currently
|
||||||
|
in use. This allows PHP Monitor to do different things when Valet 3.0 is enabled.
|
||||||
|
*/
|
||||||
|
public func evaluateFeatureSupport() -> Void {
|
||||||
|
let isOlderThanVersionThree = version.versionCompare("3.0") == .orderedAscending
|
||||||
|
|
||||||
|
if isOlderThanVersionThree {
|
||||||
|
self.features.append(.supportForPhp56)
|
||||||
|
} else {
|
||||||
|
Log.info("This version of Valet supports isolation.")
|
||||||
|
self.features.append(.isolatedSites)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,11 +136,21 @@ class Valet {
|
|||||||
installed is not recent enough.
|
installed is not recent enough.
|
||||||
*/
|
*/
|
||||||
public func validateVersion() -> Void {
|
public func validateVersion() -> Void {
|
||||||
|
// 1. Evaluate feature support
|
||||||
|
Valet.shared.evaluateFeatureSupport()
|
||||||
|
|
||||||
|
// 2. Notify user if the version is too old
|
||||||
if version.versionCompare(Constants.MinimumRecommendedValetVersion) == .orderedAscending {
|
if version.versionCompare(Constants.MinimumRecommendedValetVersion) == .orderedAscending {
|
||||||
let version = version
|
let version = version
|
||||||
Log.warn("Valet version \(version!) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
Log.warn("Valet version \(version!) is too old! (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Alert.notify(message: "alert.min_valet_version.title".localized, info: "alert.min_valet_version.info".localized(version!, Constants.MinimumRecommendedValetVersion))
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.min_valet_version.title".localized,
|
||||||
|
subtitle:"alert.min_valet_version.info".localized(version!, Constants.MinimumRecommendedValetVersion)
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.info("Valet version \(version!) is recent enough, OK (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
Log.info("Valet version \(version!) is recent enough, OK (recommended: \(Constants.MinimumRecommendedValetVersion))")
|
||||||
@ -98,235 +161,28 @@ class Valet {
|
|||||||
Returns a count of how many sites are linked and parked.
|
Returns a count of how many sites are linked and parked.
|
||||||
*/
|
*/
|
||||||
private func countPaths() -> Int {
|
private func countPaths() -> Int {
|
||||||
var count = 0
|
return Self.siteScanner
|
||||||
for path in config.paths {
|
.resolveSiteCount(paths: config.paths)
|
||||||
let entries = try! FileManager.default.contentsOfDirectory(atPath: path)
|
|
||||||
for entry in entries {
|
|
||||||
if resolveSite(entry, forPath: path) {
|
|
||||||
count += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Resolves all paths and creates linked or parked site instances that can be referenced later.
|
Resolves all paths and creates linked or parked site instances that can be referenced later.
|
||||||
*/
|
*/
|
||||||
private func resolvePaths(tld: String) {
|
private func resolvePaths() {
|
||||||
isBusy = true
|
isBusy = true
|
||||||
|
|
||||||
sites = []
|
sites = Self.siteScanner
|
||||||
|
.resolveSitesFrom(paths: config.paths)
|
||||||
|
.sorted { $0.absolutePath < $1.absolutePath }
|
||||||
|
|
||||||
for path in config.paths {
|
if let defaultPath = Valet.shared.config.defaultSite,
|
||||||
let entries = try! FileManager.default.contentsOfDirectory(atPath: path)
|
let site = ValetSiteScanner().resolveSite(path: defaultPath) {
|
||||||
for entry in entries {
|
sites.insert(site, at: 0)
|
||||||
resolvePath(entry, forPath: path, tld: tld)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sites = sites.sorted { $0.absolutePath < $1.absolutePath }
|
|
||||||
|
|
||||||
isBusy = false
|
isBusy = false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
Determines whether the site can be resolved as a symbolic link or as a directory.
|
|
||||||
Regular files are ignored. Returns true if the path can be parsed.
|
|
||||||
*/
|
|
||||||
private func resolveSite(_ entry: String, forPath path: String) -> Bool {
|
|
||||||
let siteDir = path + "/" + entry
|
|
||||||
|
|
||||||
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
|
||||||
|
|
||||||
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
|
||||||
|
|
||||||
if type == FileAttributeType.typeSymbolicLink || type == FileAttributeType.typeDirectory {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determines whether the site can be resolved as a symbolic link or as a directory.
|
|
||||||
Regular files are ignored, and the site is added to Valet's list of sites.
|
|
||||||
*/
|
|
||||||
private func resolvePath(_ entry: String, forPath path: String, tld: String) {
|
|
||||||
let siteDir = path + "/" + entry
|
|
||||||
|
|
||||||
// See if the file is a symlink, if so, resolve it
|
|
||||||
let attrs = try! FileManager.default.attributesOfItem(atPath: siteDir)
|
|
||||||
|
|
||||||
// We can also determine whether the thing at the path is a directory, too
|
|
||||||
let type = attrs[FileAttributeKey.type] as! FileAttributeType
|
|
||||||
|
|
||||||
// We should also check that we can interpret the path correctly
|
|
||||||
if URL(fileURLWithPath: siteDir).lastPathComponent == "" {
|
|
||||||
Log.warn("Could not parse the site: \(siteDir), skipping!")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if type == FileAttributeType.typeSymbolicLink {
|
|
||||||
sites.append(Site(aliasPath: siteDir, tld: tld))
|
|
||||||
} else if type == FileAttributeType.typeDirectory {
|
|
||||||
sites.append(Site(absolutePath: siteDir, tld: tld))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Structs
|
|
||||||
|
|
||||||
class Site {
|
|
||||||
/// Name of the site. Does not include the TLD.
|
|
||||||
var name: String!
|
|
||||||
|
|
||||||
/// The absolute path to the directory that is served.
|
|
||||||
var absolutePath: String!
|
|
||||||
|
|
||||||
/// The absolute path to the directory that is served,
|
|
||||||
/// replacing the user's home folder with ~.
|
|
||||||
lazy var absolutePathRelative: String = {
|
|
||||||
return self.absolutePath
|
|
||||||
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
|
||||||
}()
|
|
||||||
|
|
||||||
/// Location of the alias. If set, this is a linked domain.
|
|
||||||
var aliasPath: String?
|
|
||||||
|
|
||||||
/// Whether the site has been secured.
|
|
||||||
var secured: Bool!
|
|
||||||
|
|
||||||
/// What driver is currently in use. If not detected, defaults to nil.
|
|
||||||
var driver: String? = nil
|
|
||||||
|
|
||||||
/// Whether the driver was determined by checking the Composer file.
|
|
||||||
var driverDeterminedByComposer: Bool = false
|
|
||||||
|
|
||||||
/// A list of notable Composer dependencies.
|
|
||||||
var notableComposerDependencies: [String: String] = [:]
|
|
||||||
|
|
||||||
/// The PHP version as discovered in `composer.json`.
|
|
||||||
var composerPhp: String = "???"
|
|
||||||
|
|
||||||
/// Check whether the PHP version is valid for the currently linked version.
|
|
||||||
var composerPhpCompatibleWithLinked: Bool = false
|
|
||||||
|
|
||||||
/// How the PHP version was determined.
|
|
||||||
var composerPhpSource: String = "unknown"
|
|
||||||
|
|
||||||
init() {}
|
|
||||||
|
|
||||||
convenience init(absolutePath: String, tld: String) {
|
|
||||||
self.init()
|
|
||||||
self.absolutePath = absolutePath
|
|
||||||
self.name = URL(fileURLWithPath: absolutePath).lastPathComponent
|
|
||||||
self.aliasPath = nil
|
|
||||||
determineSecured(tld)
|
|
||||||
determineComposerPhpVersion()
|
|
||||||
determineDriver()
|
|
||||||
}
|
|
||||||
|
|
||||||
convenience init(aliasPath: String, tld: String) {
|
|
||||||
self.init()
|
|
||||||
self.absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath)
|
|
||||||
self.name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
|
||||||
self.aliasPath = aliasPath
|
|
||||||
determineSecured(tld)
|
|
||||||
determineComposerPhpVersion()
|
|
||||||
determineDriver()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if a certificate file can be found in the `valet/Certificates` directory.
|
|
||||||
- Note: The file is not validated, only its presence is checked.
|
|
||||||
*/
|
|
||||||
public func determineSecured(_ tld: String) {
|
|
||||||
secured = Shell.fileExists("~/.config/valet/Certificates/\(self.name!).\(tld).key")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Checks if `composer.json` exists in the folder, and extracts notable information:
|
|
||||||
|
|
||||||
- The PHP version required (the constraint, so it could be `^8.0`, for example)
|
|
||||||
- Where the PHP version was found (`require` or `platform`)
|
|
||||||
- Notable PHP dependencies (determined via `PhpFrameworks.DependencyList`)
|
|
||||||
|
|
||||||
The method then also checks if the determined constraint (if found) is compatible
|
|
||||||
with the currently linked version of PHP (see `composerPhpMatchesSystem`).
|
|
||||||
*/
|
|
||||||
public func determineComposerPhpVersion() {
|
|
||||||
let path = "\(absolutePath!)/composer.json"
|
|
||||||
|
|
||||||
do {
|
|
||||||
if Filesystem.fileExists(path) {
|
|
||||||
let decoded = try JSONDecoder().decode(
|
|
||||||
ComposerJson.self,
|
|
||||||
from: String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8).data(using: .utf8)!
|
|
||||||
)
|
|
||||||
|
|
||||||
(self.composerPhp, self.composerPhpSource) = decoded.getPhpVersion()
|
|
||||||
self.notableComposerDependencies = decoded.getNotableDependencies()
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Log.err("Something went wrong reading the composer JSON file.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.composerPhp == "???" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split the composer list (on "|") to evaluate multiple constraints
|
|
||||||
// For example, for Laravel 8 projects the value is "^7.3|^8.0"
|
|
||||||
self.composerPhpCompatibleWithLinked =
|
|
||||||
self.composerPhp.split(separator: "|").map { string in
|
|
||||||
return PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
|
||||||
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
|
||||||
.count > 0
|
|
||||||
}.contains(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Determine the driver to be displayed in the list of sites. In v5.0, this has been changed
|
|
||||||
to load the "framework" or "project type" instead.
|
|
||||||
*/
|
|
||||||
public func determineDriver() {
|
|
||||||
self.determineDriverViaComposer()
|
|
||||||
|
|
||||||
if self.driver == nil {
|
|
||||||
self.driver = PhpFrameworks.detectFallbackDependency(self.absolutePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Check the dependency list and see if a particular dependency can't be found.
|
|
||||||
We'll revert the dependency list so that Laravel and Symfony are detected last.
|
|
||||||
|
|
||||||
(Some other frameworks might use Laravel, so if we found it first the detection would be incorrect:
|
|
||||||
this would happen with Statamic, for example.)
|
|
||||||
*/
|
|
||||||
private func determineDriverViaComposer() {
|
|
||||||
self.driverDeterminedByComposer = true
|
|
||||||
|
|
||||||
PhpFrameworks.DependencyList.reversed().forEach { (key: String, value: String) in
|
|
||||||
if self.notableComposerDependencies.keys.contains(key) {
|
|
||||||
self.driver = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, deprecated, renamed: "determineDriver")
|
|
||||||
private func determineDriverViaValet() {
|
|
||||||
let driver = Shell.pipe("cd '\(absolutePath!)' && valet which", requiresPath: true)
|
|
||||||
if driver.contains("This site is served by") {
|
|
||||||
self.driver = driver
|
|
||||||
.replacingOccurrences(of: "This site is served by [", with: "")
|
|
||||||
.replacingOccurrences(of: "ValetDriver].\n", with: "")
|
|
||||||
} else {
|
|
||||||
self.driver = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Configuration: Decodable {
|
struct Configuration: Decodable {
|
||||||
/// Top level domain suffix. Usually "test" but can be set to something else.
|
/// Top level domain suffix. Usually "test" but can be set to something else.
|
||||||
/// - Important: Does not include the actual dot. ("test", not ".test"!)
|
/// - Important: Does not include the actual dot. ("test", not ".test"!)
|
||||||
|
44
phpmon/Domain/Integrations/Valet/ValetSite+Fake.swift
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// ValetSite+Fake.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 19/03/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ValetSite {
|
||||||
|
|
||||||
|
convenience init(
|
||||||
|
fakeWithName name: String,
|
||||||
|
tld: String,
|
||||||
|
secure: Bool,
|
||||||
|
path: String,
|
||||||
|
linked: Bool,
|
||||||
|
driver: String = "Laravel (^9.0)",
|
||||||
|
constraint: String = "^8.1",
|
||||||
|
isolated: String? = nil
|
||||||
|
) {
|
||||||
|
self.init(name: name, tld: tld, absolutePath: path, aliasPath: nil, makeDeterminations: false)
|
||||||
|
self.secured = secure
|
||||||
|
self.composerPhp = constraint
|
||||||
|
|
||||||
|
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
||||||
|
.map { string in
|
||||||
|
return PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
||||||
|
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
.count > 0
|
||||||
|
}.contains(true)
|
||||||
|
|
||||||
|
self.driver = driver
|
||||||
|
self.driverDeterminedByComposer = true
|
||||||
|
if linked {
|
||||||
|
self.aliasPath = self.absolutePath
|
||||||
|
}
|
||||||
|
if let isolated = isolated {
|
||||||
|
self.isolatedPhpVersion = PhpInstallation(isolated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
234
phpmon/Domain/Integrations/Valet/ValetSite.swift
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
//
|
||||||
|
// Valet+Subclasses.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 22/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class ValetSite {
|
||||||
|
|
||||||
|
/// Name of the site. Does not include the TLD.
|
||||||
|
var name: String
|
||||||
|
|
||||||
|
/// The absolute path to the directory that is served.
|
||||||
|
var absolutePath: String
|
||||||
|
|
||||||
|
/// The absolute path to the directory that is served,
|
||||||
|
/// replacing the user's home folder with ~.
|
||||||
|
lazy var absolutePathRelative: String = {
|
||||||
|
return self.absolutePath
|
||||||
|
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
||||||
|
}()
|
||||||
|
|
||||||
|
/// The TLD used to locate this site.
|
||||||
|
var tld: String = "test"
|
||||||
|
|
||||||
|
/// The PHP version that is being used to serve this site specifically (if not global).
|
||||||
|
var isolatedPhpVersion: PhpInstallation?
|
||||||
|
|
||||||
|
/// Location of the alias. If set, this is a linked domain.
|
||||||
|
var aliasPath: String?
|
||||||
|
|
||||||
|
/// Whether the site has been secured.
|
||||||
|
var secured: Bool!
|
||||||
|
|
||||||
|
/// What driver is currently in use. If not detected, defaults to nil.
|
||||||
|
var driver: String? = nil
|
||||||
|
|
||||||
|
/// Whether the driver was determined by checking the Composer file.
|
||||||
|
var driverDeterminedByComposer: Bool = false
|
||||||
|
|
||||||
|
/// A list of notable Composer dependencies.
|
||||||
|
var notableComposerDependencies: [String: String] = [:]
|
||||||
|
|
||||||
|
/// The PHP version as discovered in `composer.json` or in .valetphprc.
|
||||||
|
var composerPhp: String = "???"
|
||||||
|
|
||||||
|
/// Check whether the PHP version is valid for the currently linked version.
|
||||||
|
var composerPhpCompatibleWithLinked: Bool = false
|
||||||
|
|
||||||
|
/// How the PHP version was determined.
|
||||||
|
var composerPhpSource: VersionSource = .unknown
|
||||||
|
|
||||||
|
/// Which version of PHP is actually used to serve this site.
|
||||||
|
var servingPhpVersion: String {
|
||||||
|
return self.isolatedPhpVersion?.versionNumber.homebrewVersion
|
||||||
|
?? PhpEnv.phpInstall.version.short
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VersionSource: String {
|
||||||
|
case unknown = "unknown"
|
||||||
|
case require = "require"
|
||||||
|
case platform = "platform"
|
||||||
|
case valetphprc = "valetphprc"
|
||||||
|
}
|
||||||
|
|
||||||
|
init(
|
||||||
|
name: String,
|
||||||
|
tld: String,
|
||||||
|
absolutePath: String,
|
||||||
|
aliasPath: String? = nil,
|
||||||
|
makeDeterminations: Bool = true
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.tld = tld
|
||||||
|
self.absolutePath = absolutePath
|
||||||
|
self.aliasPath = aliasPath
|
||||||
|
self.secured = false
|
||||||
|
|
||||||
|
if makeDeterminations {
|
||||||
|
determineSecured()
|
||||||
|
determineComposerPhpVersion()
|
||||||
|
determineDriver()
|
||||||
|
determineIsolated()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(absolutePath: String, tld: String) {
|
||||||
|
let name = URL(fileURLWithPath: absolutePath).lastPathComponent
|
||||||
|
self.init(name: name, tld: tld, absolutePath: absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
convenience init(aliasPath: String, tld: String) {
|
||||||
|
let name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
||||||
|
let absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath)
|
||||||
|
self.init(name: name, tld: tld, absolutePath: absolutePath, aliasPath: aliasPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determine whether a site is isolated.
|
||||||
|
*/
|
||||||
|
public func determineIsolated() {
|
||||||
|
if let version = ValetSite.isolatedVersion("~/.config/valet/Nginx/\(self.name).\(self.tld)") {
|
||||||
|
if (!PhpEnv.shared.cachedPhpInstallations.keys.contains(version)) {
|
||||||
|
Log.err("The PHP version \(version) is isolated for the site \(self.name) but that PHP version is unavailable.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.isolatedPhpVersion = PhpEnv.shared.cachedPhpInstallations[version]
|
||||||
|
} else {
|
||||||
|
self.isolatedPhpVersion = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if a certificate file can be found in the `valet/Certificates` directory.
|
||||||
|
- Note: The file is not validated, only its presence is checked.
|
||||||
|
*/
|
||||||
|
public func determineSecured() {
|
||||||
|
secured = Filesystem.fileExists("~/.config/valet/Certificates/\(self.name).\(self.tld).key")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if `composer.json` exists in the folder, and extracts notable information:
|
||||||
|
|
||||||
|
- The PHP version required (the constraint, so it could be `^8.0`, for example)
|
||||||
|
- Where the PHP version was found (`require` or `platform` or via .valetphprc)
|
||||||
|
- Notable PHP dependencies (determined via `PhpFrameworks.DependencyList`)
|
||||||
|
|
||||||
|
The method then also checks if the determined constraint (if found) is compatible
|
||||||
|
with the currently linked version of PHP (see `composerPhpMatchesSystem`).
|
||||||
|
*/
|
||||||
|
public func determineComposerPhpVersion() {
|
||||||
|
|
||||||
|
self.determineComposerInformation()
|
||||||
|
self.determineValetPhpFileInfo()
|
||||||
|
|
||||||
|
if self.composerPhp == "???" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the composer list (on "|") to evaluate multiple constraints
|
||||||
|
// For example, for Laravel 8 projects the value is "^7.3|^8.0"
|
||||||
|
self.composerPhpCompatibleWithLinked = self.composerPhp.split(separator: "|")
|
||||||
|
.map { string in
|
||||||
|
return PhpVersionNumberCollection.make(from: [PhpEnv.phpInstall.version.long])
|
||||||
|
.matching(constraint: string.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
|
.count > 0
|
||||||
|
}.contains(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Determine the driver to be displayed in the list of sites. In v5.0, this has been changed
|
||||||
|
to load the "framework" or "project type" instead.
|
||||||
|
*/
|
||||||
|
public func determineDriver() {
|
||||||
|
self.determineDriverViaComposer()
|
||||||
|
|
||||||
|
if self.driver == nil {
|
||||||
|
self.driver = PhpFrameworks.detectFallbackDependency(self.absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check the dependency list and see if a particular dependency can't be found.
|
||||||
|
We'll revert the dependency list so that Laravel and Symfony are detected last.
|
||||||
|
|
||||||
|
(Some other frameworks might use Laravel, so if we found it first the detection would be incorrect:
|
||||||
|
this would happen with Statamic, for example.)
|
||||||
|
*/
|
||||||
|
private func determineDriverViaComposer() {
|
||||||
|
self.driverDeterminedByComposer = true
|
||||||
|
|
||||||
|
PhpFrameworks.DependencyList.reversed().forEach { (key: String, value: String) in
|
||||||
|
if self.notableComposerDependencies.keys.contains(key) {
|
||||||
|
self.driver = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks the contents of the composer.json file and determine the notable dependencies,
|
||||||
|
as well as the requested PHP version. If no composer.json file is found, nothing happens.
|
||||||
|
*/
|
||||||
|
private func determineComposerInformation() {
|
||||||
|
let path = "\(absolutePath)/composer.json"
|
||||||
|
|
||||||
|
do {
|
||||||
|
if Filesystem.fileExists(path) {
|
||||||
|
let decoded = try JSONDecoder().decode(
|
||||||
|
ComposerJson.self,
|
||||||
|
from: String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8).data(using: .utf8)!
|
||||||
|
)
|
||||||
|
|
||||||
|
(self.composerPhp, self.composerPhpSource) = decoded.getPhpVersion()
|
||||||
|
self.notableComposerDependencies = decoded.getNotableDependencies()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Log.err("Something went wrong reading the Composer JSON file.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks the contents of the .valetphprc file and determine the version, if possible.
|
||||||
|
*/
|
||||||
|
private func determineValetPhpFileInfo() {
|
||||||
|
let path = "\(absolutePath)/.valetphprc"
|
||||||
|
|
||||||
|
do {
|
||||||
|
if Filesystem.fileExists(path) {
|
||||||
|
let contents = try String(contentsOf: URL(fileURLWithPath: path), encoding: .utf8)
|
||||||
|
if let version = VersionExtractor.from(contents) {
|
||||||
|
self.composerPhp = version
|
||||||
|
self.composerPhpSource = .valetphprc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Log.err("Something went wrong parsing the .valetphprc file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: File Parsing
|
||||||
|
|
||||||
|
public static func isolatedVersion(_ filePath: String) -> String? {
|
||||||
|
if Filesystem.fileExists(filePath) {
|
||||||
|
return NginxConfigParser
|
||||||
|
.init(filePath: filePath)
|
||||||
|
.isolatedVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -1,98 +0,0 @@
|
|||||||
//
|
|
||||||
// MainMenu+Composer.swift
|
|
||||||
// PHP Monitor
|
|
||||||
//
|
|
||||||
// Created by Nico Verbruggen on 08/02/2022.
|
|
||||||
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension MainMenu {
|
|
||||||
|
|
||||||
/**
|
|
||||||
Updates the global dependencies and runs the completion callback when done.
|
|
||||||
This method should probably be broken up into several smaller methods at some point.
|
|
||||||
*/
|
|
||||||
func updateGlobalDependencies(notify: Bool, completion: @escaping (Bool) -> Void) {
|
|
||||||
if !Shell.fileExists("/usr/local/bin/composer") {
|
|
||||||
Alert.notify(
|
|
||||||
message: "alert.composer_missing.title".localized,
|
|
||||||
info: "alert.composer_missing.info".localized
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
PhpEnv.shared.isBusy = true
|
|
||||||
setBusyImage()
|
|
||||||
self.rebuild()
|
|
||||||
|
|
||||||
let noLongerBusy = {
|
|
||||||
PhpEnv.shared.isBusy = false
|
|
||||||
DispatchQueue.main.async { [self] in
|
|
||||||
self.updatePhpVersionInStatusBar()
|
|
||||||
self.rebuild()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var window: ProgressWindowController? = ProgressWindowController.display(
|
|
||||||
title: "alert.composer_progress.title".localized,
|
|
||||||
description: "alert.composer_progress.info".localized
|
|
||||||
)
|
|
||||||
window?.setType(info: true)
|
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInitiated).async {
|
|
||||||
let task = Shell.user.createTask(
|
|
||||||
for: "/usr/local/bin/composer global update", requiresPath: true
|
|
||||||
)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole("/usr/local/bin/composer global update\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
Shell.captureOutput(
|
|
||||||
task,
|
|
||||||
didReceiveStdOutData: { string in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole(string)
|
|
||||||
}
|
|
||||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
|
||||||
},
|
|
||||||
didReceiveStdErrData: { string in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
window?.addToConsole(string)
|
|
||||||
}
|
|
||||||
Log.perf("\(string.trimmingCharacters(in: .newlines))")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
task.launch()
|
|
||||||
task.waitUntilExit()
|
|
||||||
Shell.haltCapturingOutput(task)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if task.terminationStatus <= 0 {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
|
||||||
window?.close()
|
|
||||||
if (notify) {
|
|
||||||
LocalNotification.send(
|
|
||||||
title: "alert.composer_success.title".localized,
|
|
||||||
subtitle: "alert.composer_success.info".localized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
window = nil
|
|
||||||
noLongerBusy()
|
|
||||||
completion(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
window?.setType(info: false)
|
|
||||||
window?.progressView?.labelTitle.stringValue = "alert.composer_failure.title".localized
|
|
||||||
window?.progressView?.labelDescription.stringValue = "alert.composer_failure.info".localized
|
|
||||||
window = nil
|
|
||||||
noLongerBusy()
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
85
phpmon/Domain/Menu/MainMenu+FixMyValet.swift
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// MainMenu+FixMyValet.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 20/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
extension MainMenu {
|
||||||
|
|
||||||
|
@objc func fixMyValet() {
|
||||||
|
let previousVersion = PhpEnv.phpInstall.version.short
|
||||||
|
|
||||||
|
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
||||||
|
presentAlertForMissingFormula()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.fix_my_valet.title".localized,
|
||||||
|
subtitle: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion)
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.fix_my_valet.ok".localized)
|
||||||
|
.withSecondary(text: "alert.fix_my_valet.cancel".localized)
|
||||||
|
.didSelectPrimary()
|
||||||
|
{
|
||||||
|
Log.info("The user has chosen to abort Fix My Valet")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Actions.fixMyValet {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if previousVersion == PhpEnv.brewPhpVersion {
|
||||||
|
self.presentAlertForSameVersion()
|
||||||
|
} else {
|
||||||
|
self.presentAlertForDifferentVersion(version: previousVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentAlertForMissingFormula() {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.php_formula_missing.title".localized,
|
||||||
|
subtitle: "alert.php_formula_missing.info".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentAlertForSameVersion() {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.fix_my_valet_done.title".localized,
|
||||||
|
subtitle: "alert.fix_my_valet_done.subtitle".localized,
|
||||||
|
description: "alert.fix_my_valet_done.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentAlertForDifferentVersion(version: String) {
|
||||||
|
BetterAlert()
|
||||||
|
.withInformation(
|
||||||
|
title: "alert.fix_my_valet_done.title".localized,
|
||||||
|
subtitle: "alert.fix_my_valet_done.subtitle".localized,
|
||||||
|
description: "alert.fix_my_valet_done.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.fix_my_valet_done.switch_back".localized(version), action: { alert in
|
||||||
|
alert.close(with: .alertSecondButtonReturn)
|
||||||
|
MainMenu.shared.switchToPhpVersion(version)
|
||||||
|
})
|
||||||
|
.withSecondary(text: "alert.fix_my_valet_done.stay".localized(PhpEnv.brewPhpVersion))
|
||||||
|
.withTertiary(text: "", action: { alert in
|
||||||
|
NSWorkspace.shared.open(Constants.Urls.FrequentlyAskedQuestions)
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,15 +12,16 @@ extension MainMenu {
|
|||||||
/**
|
/**
|
||||||
Kick off the startup of the rendering of the main menu.
|
Kick off the startup of the rendering of the main menu.
|
||||||
*/
|
*/
|
||||||
func startup() {
|
func startup() async {
|
||||||
// Start with the icon
|
// Start with the icon
|
||||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
DispatchQueue.main.async {
|
||||||
|
self.setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||||
|
}
|
||||||
|
|
||||||
// Perform environment boot checks
|
if await Startup().checkEnvironment() {
|
||||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
self.onEnvironmentPass()
|
||||||
Startup().checkEnvironment(success: { onEnvironmentPass() },
|
} else {
|
||||||
failure: { onEnvironmentFail() }
|
self.onEnvironmentFail()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,15 +29,27 @@ extension MainMenu {
|
|||||||
When the environment is all clear and the app can run, let's go.
|
When the environment is all clear and the app can run, let's go.
|
||||||
*/
|
*/
|
||||||
private func onEnvironmentPass() {
|
private func onEnvironmentPass() {
|
||||||
|
// Attempt to find out more info about Valet
|
||||||
|
if Valet.shared.version != nil {
|
||||||
|
Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version!)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate the version (this will enforce which versions of PHP are supported)
|
||||||
|
Valet.shared.validateVersion()
|
||||||
|
|
||||||
|
// Actually detect the PHP versions
|
||||||
PhpEnv.detectPhpVersions()
|
PhpEnv.detectPhpVersions()
|
||||||
|
|
||||||
|
// Check for an alias conflict
|
||||||
if HomebrewDiagnostics.hasAliasConflict() {
|
if HomebrewDiagnostics.hasAliasConflict() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Alert.notify(
|
BetterAlert()
|
||||||
message: "alert.php_alias_conflict.title".localized,
|
.withInformation(
|
||||||
info: "alert.php_alias_conflict.info".localized,
|
title: "alert.php_alias_conflict.title".localized,
|
||||||
style: .critical
|
subtitle: "alert.php_alias_conflict.info".localized
|
||||||
)
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +81,19 @@ extension MainMenu {
|
|||||||
// Load the global hotkey
|
// Load the global hotkey
|
||||||
App.shared.loadGlobalHotkey()
|
App.shared.loadGlobalHotkey()
|
||||||
|
|
||||||
// Attempt to find out more info about Valet
|
// Preload sites
|
||||||
if Valet.shared.version != nil {
|
|
||||||
Log.info("PHP Monitor has extracted the version number of Valet: \(Valet.shared.version!)")
|
|
||||||
}
|
|
||||||
|
|
||||||
Valet.shared.loadConfiguration()
|
|
||||||
Valet.shared.validateVersion()
|
|
||||||
Valet.shared.startPreloadingSites()
|
Valet.shared.startPreloadingSites()
|
||||||
|
|
||||||
|
if (Valet.shared.config.tld != "test") {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
BetterAlert().withInformation(
|
||||||
|
title: "alert.warnings.tld_issue.title".localized,
|
||||||
|
subtitle: "alert.warnings.tld_issue.subtitle".localized,
|
||||||
|
description: "alert.warnings.tld_issue.description".localized
|
||||||
|
).withPrimary(text: "OK").show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||||
|
|
||||||
Log.info("PHP Monitor is ready to serve!")
|
Log.info("PHP Monitor is ready to serve!")
|
||||||
@ -101,18 +118,21 @@ extension MainMenu {
|
|||||||
*/
|
*/
|
||||||
private func onEnvironmentFail() {
|
private func onEnvironmentFail() {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
let close = Alert.present(
|
|
||||||
messageText: "alert.cannot_start.title".localized,
|
|
||||||
informativeText: "alert.cannot_start.info".localized,
|
|
||||||
buttonTitle: "alert.cannot_start.close".localized,
|
|
||||||
secondButtonTitle: "alert.cannot_start.retry".localized
|
|
||||||
)
|
|
||||||
|
|
||||||
if (close) {
|
BetterAlert()
|
||||||
exit(1)
|
.withInformation(
|
||||||
}
|
title: "alert.cannot_start.title".localized,
|
||||||
|
subtitle: "alert.cannot_start.subtitle".localized,
|
||||||
|
description: "alert.cannot_start.description".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "alert.cannot_start.retry".localized)
|
||||||
|
.withSecondary(text: "alert.cannot_start.close".localized, action: { vc in
|
||||||
|
vc.close(with: .alertSecondButtonReturn)
|
||||||
|
exit(1)
|
||||||
|
})
|
||||||
|
.show()
|
||||||
|
|
||||||
startup()
|
Task { await startup() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,13 @@ extension MainMenu {
|
|||||||
|
|
||||||
// Run composer updates
|
// Run composer updates
|
||||||
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
if Preferences.isEnabled(.autoComposerGlobalUpdateAfterSwitch) {
|
||||||
self.updateGlobalDependencies(notify: false, completion: { _ in
|
ComposerWindow().updateGlobalDependencies(
|
||||||
self.notifyAboutVersionChange(to: version)
|
notify: false,
|
||||||
})
|
completion: { _ in
|
||||||
|
self.notifyAboutVersionChange(to: version)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
self.notifyAboutVersionChange(to: version)
|
self.notifyAboutVersionChange(to: version)
|
||||||
}
|
}
|
||||||
@ -52,12 +56,15 @@ extension MainMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func suggestFixMyValet(failed version: String) {
|
@MainActor private func suggestFixMyValet(failed version: String) {
|
||||||
let outcome = Alert.present(
|
let outcome = BetterAlert()
|
||||||
messageText: "alert.php_switch_failed.title".localized(version),
|
.withInformation(
|
||||||
informativeText: "alert.php_switch_failed.info".localized(version),
|
title: "alert.php_switch_failed.title".localized(version),
|
||||||
buttonTitle: "alert.php_switch_failed.confirm".localized,
|
subtitle: "alert.php_switch_failed.info".localized(version)
|
||||||
secondButtonTitle: "alert.php_switch_failed.cancel".localized, style: .informational)
|
)
|
||||||
|
.withPrimary(text: "alert.php_switch_failed.confirm".localized)
|
||||||
|
.withSecondary(text: "alert.php_switch_failed.cancel".localized)
|
||||||
|
.didSelectPrimary()
|
||||||
if outcome {
|
if outcome {
|
||||||
MainMenu.shared.fixMyValet()
|
MainMenu.shared.fixMyValet()
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
/** Refreshes the icon with the PHP version. */
|
/** Refreshes the icon with the PHP version. */
|
||||||
@objc func refreshIcon() {
|
@objc func refreshIcon() {
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
if (App.busy) {
|
if (PhpEnv.shared.isBusy) {
|
||||||
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
|
||||||
} else {
|
} else {
|
||||||
if Preferences.preferences[.shouldDisplayDynamicIcon] as! Bool == false {
|
if Preferences.preferences[.shouldDisplayDynamicIcon] as! Bool == false {
|
||||||
@ -170,26 +170,31 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
// MARK: - Actions
|
// MARK: - Actions
|
||||||
|
|
||||||
@objc func fixHomebrewPermissions() {
|
@objc func fixHomebrewPermissions() {
|
||||||
if !Alert.present(
|
if !BetterAlert()
|
||||||
messageText: "alert.fix_homebrew_permissions.title".localized,
|
.withInformation(
|
||||||
informativeText: "alert.fix_homebrew_permissions.info".localized,
|
title: "alert.fix_homebrew_permissions.title".localized,
|
||||||
buttonTitle: "alert.fix_homebrew_permissions.ok".localized,
|
subtitle: "alert.fix_homebrew_permissions.subtitle".localized,
|
||||||
secondButtonTitle: "alert.fix_homebrew_permissions.cancel".localized,
|
description: "alert.fix_homebrew_permissions.desc".localized
|
||||||
style: .warning
|
)
|
||||||
) {
|
.withPrimary(text: "alert.fix_homebrew_permissions.ok".localized)
|
||||||
|
.withSecondary(text: "alert.fix_homebrew_permissions.cancel".localized)
|
||||||
|
.didSelectPrimary() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
asyncExecution {
|
asyncExecution {
|
||||||
try Actions.fixHomebrewPermissions()
|
try Actions.fixHomebrewPermissions()
|
||||||
} success: {
|
} success: {
|
||||||
Alert.notify(
|
BetterAlert()
|
||||||
message: "alert.fix_homebrew_permissions_done.title".localized,
|
.withInformation(
|
||||||
info: "alert.fix_homebrew_permissions_done.info".localized,
|
title: "alert.fix_homebrew_permissions_done.title".localized,
|
||||||
style: .warning
|
subtitle: "alert.fix_homebrew_permissions_done.subtitle".localized,
|
||||||
)
|
description: "alert.fix_homebrew_permissions_done.desc".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
} failure: { error in
|
} failure: { error in
|
||||||
Alert.notify(about: error as! HomebrewPermissionError)
|
BetterAlert.show(for: error as! HomebrewPermissionError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,30 +264,11 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func fixMyValet() {
|
|
||||||
if !Alert.present(
|
|
||||||
messageText: "alert.fix_my_valet.title".localized,
|
|
||||||
informativeText: "alert.fix_my_valet.info".localized(PhpEnv.brewPhpVersion),
|
|
||||||
buttonTitle: "alert.fix_my_valet.ok".localized,
|
|
||||||
secondButtonTitle: "alert.fix_my_valet.cancel".localized,
|
|
||||||
style: .warning
|
|
||||||
) {
|
|
||||||
Log.info("The user has chosen to abort Fix My Valet")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
asyncExecution {
|
|
||||||
Actions.fixMyValet()
|
|
||||||
} success: {
|
|
||||||
Alert.notify(
|
|
||||||
message: "alert.fix_my_valet_done.title".localized,
|
|
||||||
info: "alert.fix_my_valet_done.info".localized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateGlobalComposerDependencies() {
|
@objc func updateGlobalComposerDependencies() {
|
||||||
self.updateGlobalDependencies(notify: true, completion: { _ in })
|
ComposerWindow().updateGlobalDependencies(
|
||||||
|
notify: true,
|
||||||
|
completion: { _ in }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func openActiveConfigFolder() {
|
@objc func openActiveConfigFolder() {
|
||||||
@ -342,7 +328,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func openDonate() {
|
@objc func openDonate() {
|
||||||
NSWorkspace.shared.open(Constants.DonationUrl)
|
NSWorkspace.shared.open(Constants.Urls.DonationPage)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func terminateApp() {
|
@objc func terminateApp() {
|
||||||
@ -354,6 +340,7 @@ class MainMenu: NSObject, NSWindowDelegate, NSMenuDelegate, PhpSwitcherDelegate
|
|||||||
func menuWillOpen(_ menu: NSMenu) {
|
func menuWillOpen(_ menu: NSMenu) {
|
||||||
// Make sure the shortcut key does not trigger this when the menu is open
|
// Make sure the shortcut key does not trigger this when the menu is open
|
||||||
App.shared.shortcutHotkey?.isPaused = true
|
App.shared.shortcutHotkey?.isPaused = true
|
||||||
|
NotificationCenter.default.post(name: Events.ServicesUpdated, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func menuDidClose(_ menu: NSMenu) {
|
func menuDidClose(_ menu: NSMenu) {
|
||||||
|
@ -44,36 +44,15 @@ class ServicesView: NSView, XibLoadable {
|
|||||||
)
|
)
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDraw() {
|
|
||||||
super.viewWillDraw()
|
|
||||||
self.loadData()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateInformation() {
|
@objc func updateInformation() {
|
||||||
self.loadData()
|
self.loadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: (5.1) Move data fetching, caching & retrieval somewhere else
|
|
||||||
func loadData() {
|
func loadData() {
|
||||||
// Use stale data
|
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
self.applyAllInfoFieldsFromCachedValue()
|
||||||
|
HomebrewService.loadAll { services in
|
||||||
// Re-fetch services
|
ServicesView.services = Dictionary(uniqueKeysWithValues: services.map{ ($0.name, $0) })
|
||||||
runAsync {
|
|
||||||
let servicesList = try! JSONDecoder().decode(
|
|
||||||
[HomebrewService].self,
|
|
||||||
from: Shell.pipe(
|
|
||||||
"sudo \(Paths.brew) services info --all --json",
|
|
||||||
requiresPath: true
|
|
||||||
).data(using: .utf8)!
|
|
||||||
).filter({ service in
|
|
||||||
return [PhpEnv.phpInstall.formula, "nginx", "dnsmasq"].contains(service.name)
|
|
||||||
})
|
|
||||||
|
|
||||||
ServicesView.services = Dictionary(uniqueKeysWithValues: servicesList.map{ ($0.name, $0) })
|
|
||||||
} completion: {
|
|
||||||
// Use fresh data
|
|
||||||
self.applyAllInfoFieldsFromCachedValue()
|
self.applyAllInfoFieldsFromCachedValue()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ class StatusMenu : NSMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addPhpActionMenuItems() {
|
func addPhpActionMenuItems() {
|
||||||
if App.busy {
|
if PhpEnv.shared.isBusy {
|
||||||
addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: ""))
|
addItem(NSMenuItem(title: "mi_busy".localized, action: nil, keyEquivalent: ""))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -97,26 +97,20 @@ class StatusMenu : NSMenu {
|
|||||||
func addFirstAidAndServicesMenuItems() {
|
func addFirstAidAndServicesMenuItems() {
|
||||||
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
let services = NSMenuItem(title: "mi_other".localized, action: nil, keyEquivalent: "")
|
||||||
let servicesMenu = NSMenu()
|
let servicesMenu = NSMenu()
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_first_aid".localized))
|
|
||||||
|
|
||||||
if !PhpEnv.shared.availablePhpVersions.contains(PhpEnv.brewPhpVersion) {
|
let fixMyValetMenuItem = NSMenuItem(
|
||||||
servicesMenu.addItem(NSMenuItem(
|
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
||||||
title: "mi_fix_my_valet_unavailable".localized(PhpEnv.brewPhpVersion),
|
action: #selector(MainMenu.fixMyValet), keyEquivalent: ""
|
||||||
action: nil, keyEquivalent: "f")
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
servicesMenu.addItem(NSMenuItem(
|
|
||||||
title: "mi_fix_my_valet".localized(PhpEnv.brewPhpVersion),
|
|
||||||
action: #selector(MainMenu.fixMyValet), keyEquivalent: "")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (disabled until v5.1 and further tweaking)
|
|
||||||
servicesMenu.addItem(NSMenuItem(
|
|
||||||
title: "mi_fix_brew_permissions".localized(),
|
|
||||||
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: "")
|
|
||||||
)
|
)
|
||||||
*/
|
fixMyValetMenuItem.toolTip = "mi_fix_my_valet_tooltip".localized
|
||||||
|
servicesMenu.addItem(fixMyValetMenuItem)
|
||||||
|
|
||||||
|
let fixHomebrewMenuItem = NSMenuItem(
|
||||||
|
title: "mi_fix_brew_permissions".localized(),
|
||||||
|
action: #selector(MainMenu.fixHomebrewPermissions), keyEquivalent: ""
|
||||||
|
)
|
||||||
|
fixHomebrewMenuItem.toolTip = "mi_fix_brew_permissions_tooltip".localized
|
||||||
|
servicesMenu.addItem(fixHomebrewMenuItem)
|
||||||
|
|
||||||
servicesMenu.addItem(NSMenuItem.separator())
|
servicesMenu.addItem(NSMenuItem.separator())
|
||||||
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
servicesMenu.addItem(HeaderView.asMenuItem(text: "mi_services".localized))
|
||||||
@ -150,10 +144,10 @@ class StatusMenu : NSMenu {
|
|||||||
|
|
||||||
// Get the short and long version
|
// Get the short and long version
|
||||||
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
|
let shortVersion = PhpEnv.shared.availablePhpVersions[index]
|
||||||
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.longVersion
|
let longVersion = PhpEnv.shared.cachedPhpInstallations[shortVersion]!.versionNumber
|
||||||
|
|
||||||
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
let long = Preferences.preferences[.fullPhpVersionDynamicIcon] as! Bool
|
||||||
let versionString = long ? longVersion.homebrewVersion : shortVersion
|
let versionString = long ? longVersion.toString() : shortVersion
|
||||||
|
|
||||||
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
let action = #selector(MainMenu.switchToPhpVersion(sender:))
|
||||||
let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)"
|
let brew = (shortVersion == PhpEnv.brewPhpVersion) ? "php" : "php@\(shortVersion)"
|
||||||
|
119
phpmon/Domain/Notice/BetterAlert.swift
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
//
|
||||||
|
// Notice.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class BetterAlert {
|
||||||
|
|
||||||
|
var windowController: NSWindowController!
|
||||||
|
|
||||||
|
var noticeVC: BetterAlertVC {
|
||||||
|
return self.windowController.contentViewController as! BetterAlertVC
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
let storyboard = NSStoryboard(name: "Main" , bundle : nil)
|
||||||
|
|
||||||
|
self.windowController = storyboard.instantiateController(
|
||||||
|
withIdentifier: "noticeWindow"
|
||||||
|
) as? NSWindowController
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func make() -> BetterAlert {
|
||||||
|
return BetterAlert()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withPrimary(
|
||||||
|
text: String,
|
||||||
|
action: @escaping (BetterAlertVC) -> Void = {
|
||||||
|
vc in vc.close(with: .alertFirstButtonReturn)
|
||||||
|
}
|
||||||
|
) -> Self {
|
||||||
|
self.noticeVC.buttonPrimary.title = text
|
||||||
|
self.noticeVC.actionPrimary = action
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withSecondary(
|
||||||
|
text: String,
|
||||||
|
action: ((BetterAlertVC) -> Void)? = {
|
||||||
|
vc in vc.close(with: .alertSecondButtonReturn)
|
||||||
|
}
|
||||||
|
) -> Self {
|
||||||
|
self.noticeVC.buttonSecondary.title = text
|
||||||
|
self.noticeVC.actionSecondary = action
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withTertiary(
|
||||||
|
text: String = "",
|
||||||
|
action: ((BetterAlertVC) -> Void)? = nil
|
||||||
|
) -> Self {
|
||||||
|
if text == "" {
|
||||||
|
self.noticeVC.buttonTertiary.bezelStyle = .helpButton
|
||||||
|
}
|
||||||
|
self.noticeVC.buttonTertiary.title = text
|
||||||
|
self.noticeVC.actionTertiary = action
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
public func withInformation(
|
||||||
|
title: String,
|
||||||
|
subtitle: String,
|
||||||
|
description: String = ""
|
||||||
|
) -> Self {
|
||||||
|
self.noticeVC.labelTitle.stringValue = title
|
||||||
|
self.noticeVC.labelSubtitle.stringValue = subtitle
|
||||||
|
self.noticeVC.labelDescription.stringValue = description
|
||||||
|
|
||||||
|
// If the description is missing, handle the excess space and change the top margin
|
||||||
|
if (description == "") {
|
||||||
|
self.noticeVC.labelDescription.isHidden = true
|
||||||
|
self.noticeVC.primaryButtonTopMargin.constant = 0
|
||||||
|
}
|
||||||
|
return self
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows the modal and returns a ModalResponse.
|
||||||
|
If you wish to simply show the alert and disregard the outcome, use `show`.
|
||||||
|
*/
|
||||||
|
public func runModal() -> NSApplication.ModalResponse {
|
||||||
|
if !Thread.isMainThread {
|
||||||
|
fatalError("You should always present alerts on the main thread!")
|
||||||
|
}
|
||||||
|
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
windowController.window?.makeKeyAndOrderFront(nil)
|
||||||
|
return NSApplication.shared.runModal(for: windowController.window!)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Shows the modal and returns true if the user pressed the primary button. */
|
||||||
|
public func didSelectPrimary() -> Bool {
|
||||||
|
return self.runModal() == .alertFirstButtonReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows the modal and does not return anything.
|
||||||
|
*/
|
||||||
|
public func show() {
|
||||||
|
_ = self.runModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Shows the modal for a particular error.
|
||||||
|
*/
|
||||||
|
public static func show(for error: Error & AlertableError) {
|
||||||
|
let key = error.getErrorMessageKey()
|
||||||
|
return BetterAlert().withInformation(
|
||||||
|
title: "\(key).title".localized,
|
||||||
|
subtitle: "\(key).description".localized
|
||||||
|
).withPrimary(text: "OK").show()
|
||||||
|
}
|
||||||
|
}
|
79
phpmon/Domain/Notice/BetterAlertVC.swift
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// NoticeVC.swift
|
||||||
|
// PHP Monitor
|
||||||
|
//
|
||||||
|
// Created by Nico Verbruggen on 16/02/2022.
|
||||||
|
// Copyright © 2022 Nico Verbruggen. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class BetterAlertVC: NSViewController {
|
||||||
|
|
||||||
|
// MARK: - Outlets
|
||||||
|
|
||||||
|
@IBOutlet weak var labelTitle: NSTextField!
|
||||||
|
@IBOutlet weak var labelSubtitle: NSTextField!
|
||||||
|
@IBOutlet weak var labelDescription: NSTextField!
|
||||||
|
|
||||||
|
@IBOutlet weak var buttonPrimary: NSButton!
|
||||||
|
@IBOutlet weak var buttonSecondary: NSButton!
|
||||||
|
@IBOutlet weak var buttonTertiary: NSButton!
|
||||||
|
|
||||||
|
var actionPrimary: (BetterAlertVC) -> Void = { _ in }
|
||||||
|
var actionSecondary: ((BetterAlertVC) -> Void)?
|
||||||
|
var actionTertiary: ((BetterAlertVC) -> Void)?
|
||||||
|
|
||||||
|
@IBOutlet weak var imageView: NSImageView!
|
||||||
|
|
||||||
|
@IBOutlet weak var primaryButtonTopMargin: NSLayoutConstraint!
|
||||||
|
|
||||||
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
|
override func viewWillAppear() {
|
||||||
|
imageView.image = NSApp.applicationIconImage
|
||||||
|
|
||||||
|
if actionSecondary == nil {
|
||||||
|
buttonSecondary.isHidden = true
|
||||||
|
}
|
||||||
|
if actionTertiary == nil {
|
||||||
|
buttonTertiary.isHidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
view.window?.makeFirstResponder(buttonPrimary)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
Log.perf("A BetterAlert has been deinitialized.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Outlet Actions
|
||||||
|
|
||||||
|
@IBAction func primaryButtonAction(_ sender: Any) {
|
||||||
|
self.actionPrimary(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func secondaryButtonAction(_ sender: Any) {
|
||||||
|
if self.actionSecondary != nil {
|
||||||
|
self.actionSecondary!(self)
|
||||||
|
} else {
|
||||||
|
self.close(with: .alertSecondButtonReturn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func tertiaryButtonAction(_ sender: Any) {
|
||||||
|
if self.actionSecondary != nil {
|
||||||
|
self.actionTertiary!(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func close(with code: NSApplication.ModalResponse) {
|
||||||
|
self.view.window?.close()
|
||||||
|
NSApplication.shared.stopModal(withCode: code)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -21,11 +21,14 @@ extension ActivePhpInstallation {
|
|||||||
public func notifyAboutBrokenPhpFpm() {
|
public func notifyAboutBrokenPhpFpm() {
|
||||||
if !self.checkPhpFpmStatus() {
|
if !self.checkPhpFpmStatus() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Alert.notify(
|
BetterAlert()
|
||||||
message: "alert.php_fpm_broken.title".localized,
|
.withInformation(
|
||||||
info: "alert.php_fpm_broken.info".localized,
|
title: "alert.php_fpm_broken.title".localized,
|
||||||
style: .critical
|
subtitle: "alert.php_fpm_broken.info".localized,
|
||||||
)
|
description: "alert.php_fpm_broken.description".localized
|
||||||
|
)
|
||||||
|
.withPrimary(text: "OK")
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import HotKey
|
|
||||||
import Carbon
|
import Carbon
|
||||||
|
|
||||||
class PrefsVC: NSViewController {
|
class PrefsVC: NSViewController {
|
||||||
|
@ -86,6 +86,7 @@ class Stats {
|
|||||||
(see `didSeeSponsorEncouragement`)
|
(see `didSeeSponsorEncouragement`)
|
||||||
*/
|
*/
|
||||||
public static func evaluateSponsorMessageShouldBeDisplayed() {
|
public static func evaluateSponsorMessageShouldBeDisplayed() {
|
||||||
|
|
||||||
if Bundle.main.bundleIdentifier?.contains("beta") ?? false {
|
if Bundle.main.bundleIdentifier?.contains("beta") ?? false {
|
||||||
return Log.info("Sponsor messages never apply to beta builds.")
|
return Log.info("Sponsor messages never apply to beta builds.")
|
||||||
}
|
}
|
||||||
@ -99,16 +100,24 @@ class Stats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let donate = Alert.present(
|
let donate = BetterAlert()
|
||||||
messageText: "startup.sponsor_encouragement.title".localized,
|
.withInformation(
|
||||||
informativeText: "startup.sponsor_encouragement.desc".localized,
|
title: "startup.sponsor_encouragement.title".localized,
|
||||||
buttonTitle: "startup.sponsor_encouragement.accept".localized,
|
subtitle: "startup.sponsor_encouragement.subtitle".localized,
|
||||||
secondButtonTitle: "startup.sponsor_encouragement.skip".localized,
|
description: "startup.sponsor_encouragement.desc".localized
|
||||||
style: .informational)
|
)
|
||||||
|
.withPrimary(text: "startup.sponsor_encouragement.accept".localized)
|
||||||
|
.withSecondary(text: "startup.sponsor_encouragement.skip".localized)
|
||||||
|
.withTertiary(text: "", action: { vc in
|
||||||
|
vc.close(with: .alertThirdButtonReturn)
|
||||||
|
NSWorkspace.shared.open(Constants.Urls.DonationPage)
|
||||||
|
}).didSelectPrimary()
|
||||||
|
|
||||||
if donate {
|
if donate {
|
||||||
Log.info("The user is an absolute badass for choosing this option. Thank you.")
|
Log.info("The user is an absolute badass for choosing this option. Thank you.")
|
||||||
NSWorkspace.shared.open(Constants.DonationUrl)
|
NSWorkspace.shared.open(Constants.Urls.DonationPayment)
|
||||||
}
|
}
|
||||||
|
|
||||||
UserDefaults.standard.set(true, forKey: InternalStats.didSeeSponsorEncouragement.rawValue)
|
UserDefaults.standard.set(true, forKey: InternalStats.didSeeSponsorEncouragement.rawValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import HotKey
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class HotkeyPreferenceView: NSView, XibLoadable {
|
class HotkeyPreferenceView: NSView, XibLoadable {
|
||||||
|