mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2025-08-08 04:20:07 +02:00
🔀 Merge branch 'main' into dev/5.x
This commit is contained in:
@ -52,6 +52,8 @@
|
||||
C41C1B3E22B0098000E7CF16 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C41C1B3C22B0098000E7CF16 /* Main.storyboard */; };
|
||||
C41C1B4922B00A9800E7CF16 /* MenuBarImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */; };
|
||||
C41C1B4B22B019FF00E7CF16 /* ActivePhpInstallation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */; };
|
||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */; };
|
||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */; };
|
||||
C41CD0292628D8EE0065BBED /* GlobalKeybindPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */; };
|
||||
C41E871A2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; };
|
||||
C41E871B2763D42300161EE0 /* SiteListVC+ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */; };
|
||||
@ -208,6 +210,7 @@
|
||||
C41C1B4022B0098000E7CF16 /* phpmon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = phpmon.entitlements; sourceTree = "<group>"; };
|
||||
C41C1B4822B00A9800E7CF16 /* MenuBarImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuBarImageGenerator.swift; sourceTree = "<group>"; };
|
||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivePhpInstallation.swift; sourceTree = "<group>"; };
|
||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+Actions.swift"; sourceTree = "<group>"; };
|
||||
C41CD0282628D8EE0065BBED /* GlobalKeybindPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalKeybindPreference.swift; sourceTree = "<group>"; };
|
||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SiteListVC+ContextMenu.swift"; sourceTree = "<group>"; };
|
||||
C4232EE42612526500158FC6 /* Credits.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = Credits.html; sourceTree = "<group>"; };
|
||||
@ -314,6 +317,7 @@
|
||||
C412E5FB25700D5300A1FB67 /* HomebrewPackage.swift */,
|
||||
C40C7F1D2772136000DDDCDC /* PhpSwitcher.swift */,
|
||||
C41C1B4A22B019FF00E7CF16 /* ActivePhpInstallation.swift */,
|
||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */,
|
||||
C4F2E4392752F7D00020E974 /* PhpInstallation.swift */,
|
||||
C4ACA38E25C754C100060C66 /* PhpExtension.swift */,
|
||||
);
|
||||
@ -363,14 +367,6 @@
|
||||
path = Core;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C40C7F2C2772204700DDDCDC /* PHP */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C40C7F2727721FF600DDDCDC /* ActivePhpInstallation+Checks.swift */,
|
||||
);
|
||||
path = PHP;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C415D3D72770F341005EF286 /* phpmon-cli */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -433,7 +429,6 @@
|
||||
children = (
|
||||
C4AF9F6B275445D300D44ED0 /* Integrations */,
|
||||
C4B13B1D25C4915000548C3A /* Core */,
|
||||
C40C7F2C2772204700DDDCDC /* PHP */,
|
||||
C47331A0247093AC009A0597 /* Menu */,
|
||||
C464ADAA275A7A25003FCD53 /* SiteList */,
|
||||
5420395726135DB800FB00FA /* Preferences */,
|
||||
@ -461,6 +456,7 @@
|
||||
C464ADAB275A7A3F003FCD53 /* SiteListWC.swift */,
|
||||
C464ADAE275A7A69003FCD53 /* SiteListVC.swift */,
|
||||
C41E87192763D42300161EE0 /* SiteListVC+ContextMenu.swift */,
|
||||
C41CA5EC2774F8EE00A2C80E /* SiteListVC+Actions.swift */,
|
||||
C464ADB1275A87CA003FCD53 /* SiteListCell.swift */,
|
||||
);
|
||||
path = SiteList;
|
||||
@ -793,6 +789,7 @@
|
||||
54FCFD2A276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||
C4811D2A22D70F9A00B5F6B3 /* MainMenu.swift in Sources */,
|
||||
C40C7F3027722E8D00DDDCDC /* Logger.swift in Sources */,
|
||||
C41CA5ED2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||
C412E5FC25700D5300A1FB67 /* HomebrewPackage.swift in Sources */,
|
||||
54AB03262763858F00A29D5F /* Timer.swift in Sources */,
|
||||
C4C8E81B276F54E5003AC782 /* PhpConfigWatcher.swift in Sources */,
|
||||
@ -824,6 +821,7 @@
|
||||
files = (
|
||||
54EAC806262F212B0092D14E /* GlobalKeybindPreference.swift in Sources */,
|
||||
C4EE55AE27708B9E001DF387 /* PMStatsView.swift in Sources */,
|
||||
C41CA5EE2774F8EE00A2C80E /* SiteListVC+Actions.swift in Sources */,
|
||||
C4F780C425D80B75000DBC97 /* MainMenu.swift in Sources */,
|
||||
54AB03272763858F00A29D5F /* Timer.swift in Sources */,
|
||||
54FCFD2B276C8AA4004CE748 /* CheckboxPreferenceView.swift in Sources */,
|
||||
|
@ -51,6 +51,4 @@ class Constants {
|
||||
"8.2"
|
||||
]
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -109,7 +109,8 @@ public class Shell {
|
||||
Uses `/bin/echo` instead of the `builtin` (which does not support `-n`).
|
||||
*/
|
||||
public static func fileExists(_ path: String) -> Bool {
|
||||
return Shell.pipe("if [ -f \(path) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||
let escapedPath = path.replacingOccurrences(of: " ", with: "\\ ")
|
||||
return Shell.pipe("if [ -f \(escapedPath) ]; then /bin/echo -n \"0\"; fi") == "0"
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -500,13 +500,35 @@
|
||||
<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 translatesAutoresizingMaskIntoConstraints="NO" id="ypa-iv-wLD">
|
||||
<rect key="frame" x="211" y="18" width="18" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="18" id="jKJ-Xn-BPA"/>
|
||||
<constraint firstAttribute="height" constant="18" id="lSH-of-WzD"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="NSCaution" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="9XB-KO-aSI">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="150" translatesAutoresizingMaskIntoConstraints="NO" id="MD8-ef-Ht8">
|
||||
<rect key="frame" x="235" y="16" width="182" height="22"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Warning: This is a warning message. Please take this into account." id="iub-KH-clf">
|
||||
<font key="font" metaFont="miniSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="0NQ-ZD-CqD" firstAttribute="leading" secondItem="MD8-ef-Ht8" secondAttribute="trailing" constant="20" id="1Rb-Or-Nnn"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TbX-e2-3QL" secondAttribute="trailing" constant="20" symbolic="YES" id="3vE-LR-S7N"/>
|
||||
<constraint firstItem="TbX-e2-3QL" firstAttribute="leading" secondItem="0NQ-ZD-CqD" secondAttribute="trailing" constant="8" symbolic="YES" id="4cb-D9-8d1"/>
|
||||
<constraint firstItem="XJL-Uw-frD" firstAttribute="leading" secondItem="QPX-eu-eV8" secondAttribute="trailing" constant="10" id="55y-3V-RYt"/>
|
||||
<constraint firstItem="syz-LF-l6P" firstAttribute="leading" secondItem="5GY-nN-BWd" secondAttribute="leading" id="8QK-nf-Fiw"/>
|
||||
<constraint firstItem="QPX-eu-eV8" firstAttribute="top" secondItem="XJL-Uw-frD" secondAttribute="top" id="9QB-jZ-k1V"/>
|
||||
<constraint firstItem="ypa-iv-wLD" firstAttribute="centerY" secondItem="5GY-nN-BWd" secondAttribute="centerY" id="9d8-P2-iSk"/>
|
||||
<constraint firstItem="MD8-ef-Ht8" firstAttribute="leading" secondItem="ypa-iv-wLD" secondAttribute="trailing" constant="8" symbolic="YES" id="C90-wQ-3Gf"/>
|
||||
<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"/>
|
||||
@ -518,15 +540,19 @@
|
||||
<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="ypa-iv-wLD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="XJL-Uw-frD" secondAttribute="trailing" constant="30" id="koV-Sj-tO8"/>
|
||||
<constraint firstItem="MD8-ef-Ht8" firstAttribute="centerY" secondItem="ypa-iv-wLD" secondAttribute="centerY" id="lIN-pm-mCo"/>
|
||||
<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="jKi-Ls-7FZ" firstAttribute="leading" secondItem="TbX-e2-3QL" secondAttribute="leading" id="zjN-s3-2Ww"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="buttonWarning" destination="ypa-iv-wLD" id="NwX-H3-8um"/>
|
||||
<outlet property="imageViewLock" destination="QPX-eu-eV8" id="Nnh-kB-adG"/>
|
||||
<outlet property="imageViewType" destination="0NQ-ZD-CqD" id="Cph-FN-LaY"/>
|
||||
<outlet property="labelDriver" destination="TbX-e2-3QL" id="qJh-Ak-Dge"/>
|
||||
<outlet property="labelPathName" destination="CXK-Q9-CpO" id="iVZ-cL-azB"/>
|
||||
<outlet property="labelSiteName" destination="XJL-Uw-frD" id="f0t-vd-W68"/>
|
||||
<outlet property="labelWarning" destination="MD8-ef-Ht8" id="Faw-CY-9R5"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
@ -576,12 +602,13 @@
|
||||
</viewController>
|
||||
<customObject id="HgD-aB-bQb" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="288" y="765"/>
|
||||
<point key="canvasLocation" x="288" y="764.5"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="IconLinked" width="512" height="512"/>
|
||||
<image name="Lock" width="30" height="30"/>
|
||||
<image name="NSCaution" width="32" height="32"/>
|
||||
<image name="arrow.clockwise" catalog="system" width="14" height="16"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -34,7 +34,7 @@ class Application {
|
||||
(This will open the app if it isn't open yet.)
|
||||
*/
|
||||
@objc public func openDirectory(file: String) {
|
||||
return Shell.run("/usr/bin/open -a \"\(name)\" \(file)")
|
||||
return Shell.run("/usr/bin/open -a \"\(name)\" \"\(file)\"")
|
||||
}
|
||||
|
||||
/** Checks if the app is installed. */
|
||||
|
@ -37,10 +37,14 @@ class Valet {
|
||||
}
|
||||
|
||||
public func startPreloadingSites() {
|
||||
if self.sites.count <= 10 {
|
||||
let maximumPreload = 10
|
||||
let foundSites = self.countPaths()
|
||||
if foundSites <= maximumPreload {
|
||||
// Preload the sites and their drivers
|
||||
Log.info("Fewer than or 11 sites found, preloading list of sites...")
|
||||
Log.info("Fewer than or \(maximumPreload) sites found, preloading list of sites...")
|
||||
self.reloadSites()
|
||||
} else {
|
||||
Log.info("\(foundSites) sites found, exceeds \(maximumPreload) for preload at launch!")
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +68,25 @@ class Valet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns a count of how many sites are linked and parked.
|
||||
*/
|
||||
private func countPaths() -> Int {
|
||||
var count = 0
|
||||
for path in 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.
|
||||
*/
|
||||
private func resolvePaths(tld: String) {
|
||||
sites = []
|
||||
|
||||
@ -75,6 +98,28 @@ class Valet {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
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
|
||||
|
||||
@ -84,6 +129,12 @@ class Valet {
|
||||
// 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 == "" {
|
||||
print("Warning: could not parse the site: \(siteDir), skipping!")
|
||||
return
|
||||
}
|
||||
|
||||
if type == FileAttributeType.typeSymbolicLink {
|
||||
sites.append(Site(aliasPath: siteDir, tld: tld))
|
||||
} else if type == FileAttributeType.typeDirectory {
|
||||
@ -114,7 +165,7 @@ class Valet {
|
||||
convenience init(absolutePath: String, tld: String) {
|
||||
self.init()
|
||||
self.absolutePath = absolutePath
|
||||
self.name = URL(string: absolutePath)!.lastPathComponent
|
||||
self.name = URL(fileURLWithPath: absolutePath).lastPathComponent
|
||||
self.aliasPath = nil
|
||||
determineSecured(tld)
|
||||
determineDriver()
|
||||
@ -123,7 +174,7 @@ class Valet {
|
||||
convenience init(aliasPath: String, tld: String) {
|
||||
self.init()
|
||||
self.absolutePath = try! FileManager.default.destinationOfSymbolicLink(atPath: aliasPath)
|
||||
self.name = URL(string: aliasPath)!.lastPathComponent
|
||||
self.name = URL(fileURLWithPath: aliasPath).lastPathComponent
|
||||
self.aliasPath = aliasPath
|
||||
determineSecured(tld)
|
||||
determineDriver()
|
||||
@ -134,7 +185,7 @@ class Valet {
|
||||
}
|
||||
|
||||
public func determineDriver() {
|
||||
let driver = Shell.pipe("cd \(absolutePath!) && valet which", requiresPath: true)
|
||||
let driver = Shell.pipe("cd '\(absolutePath!)' && valet which", requiresPath: true)
|
||||
if driver.contains("This site is served by") {
|
||||
self.driver = driver
|
||||
// TODO: Use a regular expression to retrieve the driver instead?
|
||||
|
@ -19,6 +19,9 @@ class SiteListCell: NSTableCellView
|
||||
|
||||
@IBOutlet weak var labelDriver: NSTextField!
|
||||
|
||||
@IBOutlet weak var buttonWarning: NSButton!
|
||||
@IBOutlet weak var labelWarning: NSTextField!
|
||||
|
||||
override func draw(_ dirtyRect: NSRect) {
|
||||
super.draw(dirtyRect)
|
||||
}
|
||||
@ -27,6 +30,11 @@ class SiteListCell: NSTableCellView
|
||||
// Make sure to show the TLD
|
||||
labelSiteName.stringValue = "\(site.name!).\(Valet.shared.config.tld)"
|
||||
|
||||
let isProblematic = site.name.contains(" ")
|
||||
buttonWarning.isHidden = !isProblematic
|
||||
labelWarning.isHidden = !isProblematic
|
||||
labelWarning.stringValue = "site_list.warning.spaces".localized
|
||||
|
||||
// Show the absolute path, except make sure to replace the /Users/username segment with ~ for readability
|
||||
labelPathName.stringValue = site.absolutePath
|
||||
.replacingOccurrences(of: "/Users/\(Paths.whoami)", with: "~")
|
||||
|
97
phpmon/Domain/SiteList/SiteListVC+Actions.swift
Normal file
97
phpmon/Domain/SiteList/SiteListVC+Actions.swift
Normal file
@ -0,0 +1,97 @@
|
||||
//
|
||||
// SiteListVC+Actions.swift
|
||||
// PHP Monitor
|
||||
//
|
||||
// Created by Nico Verbruggen on 23/12/2021.
|
||||
// Copyright © 2021 Nico Verbruggen. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Cocoa
|
||||
|
||||
extension SiteListVC {
|
||||
|
||||
@objc func toggleSecure() {
|
||||
let rowToReload = tableView.selectedRow
|
||||
let originalSecureStatus = selectedSite!.secured
|
||||
let action = selectedSite!.secured ? "unsecure" : "secure"
|
||||
let selectedSite = selectedSite!
|
||||
let command = "cd '\(selectedSite.absolutePath!)' && sudo \(Paths.valet) \(action) && exit;"
|
||||
|
||||
waitAndExecute {
|
||||
Shell.run(command, requiresPath: true)
|
||||
} completion: { [self] in
|
||||
selectedSite.determineSecured(Valet.shared.config.tld)
|
||||
if selectedSite.secured == originalSecureStatus {
|
||||
Alert.notify(
|
||||
message: "site_list.alerts_status_not_changed.title".localized,
|
||||
info: "site_list.alerts_status_not_changed.desc".localized(command)
|
||||
)
|
||||
} else {
|
||||
let newState = selectedSite.secured ? "secured" : "unsecured"
|
||||
LocalNotification.send(
|
||||
title: "site_list.alerts_status_changed.title".localized,
|
||||
subtitle: "site_list.alerts_status_changed.desc"
|
||||
.localized(
|
||||
"\(selectedSite.name!).\(Valet.shared.config.tld)",
|
||||
newState
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0])
|
||||
tableView.deselectRow(rowToReload)
|
||||
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openInBrowser() {
|
||||
let prefix = selectedSite!.secured ? "https://" : "http://"
|
||||
let url = URL(string: "\(prefix)\(selectedSite!.name!).\(Valet.shared.config.tld)")
|
||||
if url != nil {
|
||||
NSWorkspace.shared.open(url!)
|
||||
} else {
|
||||
_ = Alert.present(
|
||||
messageText: "site_list.alert.invalid_folder_name".localized,
|
||||
informativeText: "site_list.alert.invalid_folder_name_desc".localized
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func openInFinder() {
|
||||
Shell.run("open '\(selectedSite!.absolutePath!)'")
|
||||
}
|
||||
|
||||
@objc func openInTerminal() {
|
||||
Shell.run("open -b com.apple.terminal '\(selectedSite!.absolutePath!)'")
|
||||
}
|
||||
|
||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||
guard let editor = sender.editor else { return }
|
||||
editor.openDirectory(file: selectedSite!.absolutePath!)
|
||||
}
|
||||
|
||||
@objc func unlinkSite() {
|
||||
guard let site = selectedSite else {
|
||||
return
|
||||
}
|
||||
|
||||
if site.aliasPath == nil {
|
||||
return
|
||||
}
|
||||
|
||||
Alert.confirm(
|
||||
onWindow: view.window!,
|
||||
messageText: "site_list.confirm_unlink".localized(site.name),
|
||||
informativeText: "site_link.confirm_link".localized,
|
||||
buttonTitle: "site_list.unlink".localized,
|
||||
secondButtonTitle: "Cancel",
|
||||
style: .critical,
|
||||
onFirstButtonPressed: {
|
||||
Shell.run("valet unlink '\(site.name!)'", requiresPath: true)
|
||||
self.reloadSites()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
@ -112,12 +112,11 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
- Parameter execute: Callback of the work that needs to happen.
|
||||
- Parameter completion: Callback that is fired when the work is done.
|
||||
*/
|
||||
private func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
||||
internal func waitAndExecute(_ execute: @escaping () -> Void, completion: @escaping () -> Void = {})
|
||||
{
|
||||
setUIBusy()
|
||||
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
|
||||
execute()
|
||||
|
||||
DispatchQueue.main.async { [self] in
|
||||
completion()
|
||||
setUINotBusy()
|
||||
@ -164,81 +163,6 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
self.openInBrowser()
|
||||
}
|
||||
|
||||
// MARK: Secure & Unsecure
|
||||
|
||||
@objc public func toggleSecure() {
|
||||
let rowToReload = tableView.selectedRow
|
||||
let originalSecureStatus = selectedSite!.secured
|
||||
let action = selectedSite!.secured ? "unsecure" : "secure"
|
||||
let selectedSite = selectedSite!
|
||||
let command = "cd \(selectedSite.absolutePath!) && sudo \(Paths.valet) \(action) && exit;"
|
||||
|
||||
waitAndExecute {
|
||||
Shell.run(command, requiresPath: true)
|
||||
} completion: { [self] in
|
||||
selectedSite.determineSecured(Valet.shared.config.tld)
|
||||
if selectedSite.secured == originalSecureStatus {
|
||||
Alert.notify(
|
||||
message: "site_list.alerts_status_not_changed.title".localized,
|
||||
info: "site_list.alerts_status_not_changed.desc".localized(command)
|
||||
)
|
||||
} else {
|
||||
let newState = selectedSite.secured ? "secured" : "unsecured"
|
||||
LocalNotification.send(
|
||||
title: "site_list.alerts_status_changed.title".localized,
|
||||
subtitle: "site_list.alerts_status_changed.desc"
|
||||
.localized(
|
||||
"\(selectedSite.name!).\(Valet.shared.config.tld)",
|
||||
newState
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
tableView.reloadData(forRowIndexes: [rowToReload], columnIndexes: [0])
|
||||
tableView.deselectRow(rowToReload)
|
||||
tableView.selectRowIndexes([rowToReload], byExtendingSelection: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Open in Browser & Finder
|
||||
|
||||
@objc public func openInBrowser() {
|
||||
let prefix = selectedSite!.secured ? "https://" : "http://"
|
||||
let url = "\(prefix)\(selectedSite!.name!).\(Valet.shared.config.tld)"
|
||||
NSWorkspace.shared.open(URL(string: url)!)
|
||||
}
|
||||
|
||||
@objc public func openInFinder() {
|
||||
Shell.run("open \(selectedSite!.absolutePath!)")
|
||||
}
|
||||
|
||||
@objc public func openInTerminal() {
|
||||
Shell.run("open -b com.apple.terminal \(selectedSite!.absolutePath!)")
|
||||
}
|
||||
|
||||
@objc public func unlinkSite() {
|
||||
guard let site = selectedSite else {
|
||||
return
|
||||
}
|
||||
|
||||
if site.aliasPath == nil {
|
||||
return
|
||||
}
|
||||
|
||||
Alert.confirm(
|
||||
onWindow: view.window!,
|
||||
messageText: "site_list.confirm_unlink".localized(site.name),
|
||||
informativeText: "site_link.confirm_link".localized,
|
||||
buttonTitle: "site_list.unlink".localized,
|
||||
secondButtonTitle: "Cancel",
|
||||
style: .critical,
|
||||
onFirstButtonPressed: {
|
||||
Shell.run("valet unlink \(site.name!)", requiresPath: true)
|
||||
self.reloadSites()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - (Search) Text Field Delegate
|
||||
|
||||
func searchedFor(text: String) {
|
||||
@ -259,12 +183,6 @@ class SiteListVC: NSViewController, NSTableViewDelegate, NSTableViewDataSource {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
// MARK: - Context Menu
|
||||
|
||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||
guard let editor = sender.editor else { return }
|
||||
editor.openDirectory(file: selectedSite!.absolutePath!)
|
||||
}
|
||||
// MARK: - Deinitialization
|
||||
|
||||
deinit {
|
||||
|
@ -80,6 +80,11 @@
|
||||
"site_list.detected_apps" = "Detected Applications";
|
||||
"site_list.system_apps" = "System Applications";
|
||||
|
||||
"site_list.warning.spaces" = "Warning! This site has a space in its folder.\nThe site will not be reachable via the browser.";
|
||||
|
||||
"site_list.alert.invalid_folder_name" = "Invalid folder name";
|
||||
"site_list.alert.invalid_folder_name_desc" = "This folder could not be resolved to a valid URL. This is usually because there’s a space in the folder name. Please rename the folder, reload the list of sites, and try again.";
|
||||
|
||||
// EDITORS
|
||||
|
||||
"editors.alert.try_again" = "Try Again";
|
||||
|
Reference in New Issue
Block a user