Skip to content

Commit

Permalink
Merge pull request #111 from 42Box/110-유저에게-권한을-받는-페이지를-추가합니다
Browse files Browse the repository at this point in the history
feat: 유저에게 권한을 받습니다.
  • Loading branch information
chanhihi authored Aug 30, 2023
2 parents 53de1b6 + ad34298 commit f1e074b
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 36 deletions.
34 changes: 4 additions & 30 deletions Box42/Preferences/Controller/PreferencesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,21 @@ import Cocoa
import SnapKit

class PreferencesViewController: NSViewController {
var prefTableView : NSTableView?
var prefTableView : PreferencesTableView?

override func loadView() {
self.view = NSView()
self.view.wantsLayer = true
self.view.layer?.backgroundColor = NSColor.blue.cgColor

prefTableView = NSTableView(frame: .zero)
// Column 추가
let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Column1"))
column1.width = 100.0
column1.title = "Column 1"
prefTableView?.addTableColumn(column1)
prefTableView = PreferencesTableView(frame: .zero)
prefTableView?.setup()
// prefTableView?.viewModel = viewModel

// delegate와 dataSource 설정
prefTableView?.delegate = self
prefTableView?.dataSource = self

// TableView를 스크롤 뷰에 추가 (일반적으로 NSTableView는 NSScrollView 안에 위치합니다)
let scrollView = NSScrollView()
scrollView.documentView = prefTableView
self.view.addSubview(scrollView)


scrollView.snp.makeConstraints({ make in
make.edges.equalToSuperview()
})
Expand All @@ -42,20 +33,3 @@ class PreferencesViewController: NSViewController {
})
}
}

extension PreferencesViewController: NSTableViewDelegate, NSTableViewDataSource {
func numberOfRows(in tableView: NSTableView) -> Int {
return 10 // 총 로우 수
}

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("MyCell"), owner: self) as? NSTableCellView ?? NSTableCellView()
cell.textField?.stringValue = "Row \(row), Column \(tableColumn?.identifier ?? NSUserInterfaceItemIdentifier(""))"
return cell
}

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
return 44.0 // 셀 높이를 44로 설정
}
}

78 changes: 78 additions & 0 deletions Box42/Preferences/View/PreferencesTableView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// PreferencesTableView.swift
// Box42
//
// Created by Chanhee Kim on 8/31/23.
//

import AppKit
import SnapKit
import Combine

enum PreferencesCellList: Int, CaseIterable {
case requestAccessView = 1
case cpu = 2
case my = 3

var height: CGFloat {
switch self {
case .requestAccessView:
return 100.0
case .cpu:
return 40.0
case .my:
return 50.0
}
}
}

class PreferencesTableView: NSTableView {
let requestAccessView = RequestAccessView()

func setup() {
self.delegate = self
self.dataSource = self

let column1 = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Preferences"))
column1.width = 100.0
column1.title = "Preferences"
self.addTableColumn(column1)
}

}

extension PreferencesTableView: NSTableViewDelegate, NSTableViewDataSource {
func getCellForRow(at row: Int) -> NSView {
let allCases = PreferencesCellList.allCases
if row >= 0 && row < allCases.count {
switch allCases[row] {
case .requestAccessView:
return requestAccessView
case .cpu:
// Return the view for the CPU cell
return NSView() // Placeholder
case .my:
// Return the view for the "my" cell
return NSView() // Placeholder
}
}
return NSView() // Default view if out of bounds or undefined
}

func numberOfRows(in tableView: NSTableView) -> Int {
return PreferencesCellList.allCases.count
}

func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
return getCellForRow(at: row)
}

func tableView(_ tableView: NSTableView, heightOfRow row: Int) -> CGFloat {
let allCases = PreferencesCellList.allCases
if row >= 0 && row < allCases.count {
return allCases[row].height
}

return 44.0 // Default height
}
}
136 changes: 136 additions & 0 deletions Box42/Preferences/View/RequestAccessView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// RequestAccessView.swift
// Box42
//
// Created by Chanhee Kim on 8/31/23.
//

import AppKit
import SnapKit

class RequestAccessView: NSView {
var requestAccessTextField: NSTextField = NSTextField()
var grantAccessButton: NSButton = NSButton()
var revokeAccessButton: NSButton = NSButton()
var directoryNameTextField: NSTextField = NSTextField()

override init(frame frameRect: NSRect) {
super.init(frame: .zero)
self.wantsLayer = true
self.layer?.backgroundColor = NSColor(hex: "#7FFFFFFF").cgColor
self.layer?.cornerRadius = 13

// Add subviews
self.addSubview(requestAccessTextField)
self.addSubview(grantAccessButton)
self.addSubview(revokeAccessButton)
self.addSubview(directoryNameTextField)

// Initialize UI elements
textfieldInit()
buttonInit()
directoryNameTextFieldInit()

// Set constraints
textfieldConstraints()
buttonConstraints()
directoryNameTextFieldConstraints()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func textfieldInit() {
requestAccessTextField.stringValue = "Script 및 기능들을 실행하기 위해서 루트 디렉토리의 권한이 필요합니다."
requestAccessTextField.font = NSFont.systemFont(ofSize: 15)
requestAccessTextField.isEditable = false
requestAccessTextField.isBordered = false
requestAccessTextField.backgroundColor = NSColor.clear
requestAccessTextField.lineBreakMode = .byWordWrapping
requestAccessTextField.maximumNumberOfLines = 3
}

func buttonInit() {
grantAccessButton.title = "권한 부여"
grantAccessButton.target = self
grantAccessButton.action = #selector(requestFolderAccess(_:))

revokeAccessButton.title = "권한 취소"
revokeAccessButton.target = self
revokeAccessButton.action = #selector(revokeFolderAccess(_:))
}

func directoryNameTextFieldInit() {
directoryNameTextField.stringValue = "선택된 디렉터리: 없음"
directoryNameTextField.font = NSFont.systemFont(ofSize: 15)
directoryNameTextField.isEditable = false
directoryNameTextField.isBordered = false
directoryNameTextField.backgroundColor = NSColor.clear
directoryNameTextField.lineBreakMode = .byWordWrapping
}

func directoryNameTextFieldConstraints() {
directoryNameTextField.snp.makeConstraints { make in
make.top.equalTo(revokeAccessButton.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(17)
make.trailing.equalToSuperview().offset(-17)
}
}

func textfieldConstraints() {
requestAccessTextField.snp.makeConstraints { make in
make.top.equalToSuperview().offset(10)
make.leading.equalToSuperview().offset(17)
make.trailing.equalToSuperview().offset(-17)
}
}

func buttonConstraints() {
grantAccessButton.snp.makeConstraints { make in
make.top.equalTo(requestAccessTextField.snp.bottom).offset(10)
make.leading.equalToSuperview().offset(17)
}

revokeAccessButton.snp.makeConstraints { make in
make.top.equalTo(requestAccessTextField.snp.bottom).offset(10)
make.leading.equalTo(grantAccessButton.snp.trailing).offset(10)
}
}

@objc func requestFolderAccess(_ sender: NSButton) {
let openPanel = NSOpenPanel()
openPanel.title = "Choose a folder"
openPanel.showsResizeIndicator = true
openPanel.showsHiddenFiles = false
openPanel.canChooseDirectories = true
openPanel.canCreateDirectories = true
openPanel.allowsMultipleSelection = false
openPanel.canChooseFiles = false

if openPanel.runModal() == NSApplication.ModalResponse.OK {
let result = openPanel.url

if let result = result {
print("Selected folder is \(result.path)")

directoryNameTextField.stringValue = "선택된 디렉터리: \(result.path)"

do {
let bookmarkData = try result.bookmarkData(options: .withSecurityScope, includingResourceValuesForKeys: nil, relativeTo: nil)
UserDefaults.standard.set(bookmarkData, forKey: "bookmarkData")
} catch {
print("Error creating bookmark: \(error)")
}
}
} else {
// User clicked on "Cancel"
return
}
}

@objc func revokeFolderAccess(_ sender: NSButton) {
// TODO: Add code to revoke folder access
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ScriptsLogicController {

@objc func handleButtonTapped(notification: NSNotification) {
if let button = notification.object as? NSButton {
ExcuteScripts.executeShellScript(path: button.associatedString ?? "")
SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: button.associatedString ?? "")
}
}

Expand Down
5 changes: 3 additions & 2 deletions Box42/QuickSlot/ViewModel/QuickSlotViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ class QuickSlotViewModel {
@Published var buttons: [QuickSlotButtonModel] = []

private init() {
let button1 = QuickSlotButtonModel(title: QuickSlotUI.title.clean)
let button1 = QuickSlotButtonModel(title: QuickSlotUI.title.clean,
path: Bundle.main.path(forResource: "cleanCache", ofType: "sh"))
let button2 = QuickSlotButtonModel(title: QuickSlotUI.title.preferences)
let button3 = QuickSlotButtonModel(title: QuickSlotUI.title.scripts)
let button4 = QuickSlotButtonModel(title: QuickSlotUI.title.user)

buttons = [button1, button2, button3, button4]
}

// 퀵슬롯 안에 해당 버튼이 없으면 추가
func addButton(_ button: QuickSlotButtonModel) {
if buttons.count > 7 { return }
if !buttons.contains(where: { $0.id == button.id }) {
buttons.append(button)
}
Expand Down
2 changes: 2 additions & 0 deletions Box42/Resources/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSDocumentsFolderUsageDescription</key>
<string>원활한 앱 구동을 위해 유저 디렉토리의 권한을 요청합니다.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
Expand Down
5 changes: 2 additions & 3 deletions Box42/Scripts/Controller/ScriptsFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ScriptsFileManager {

if let savedURL = savedURL, fileManager.fileExists(atPath: savedURL.path) {
print("File already exists, executing...")
ExcuteScripts.executeShellScript(path: savedURL.path)
SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: savedURL.path)
return
}

Expand All @@ -37,8 +37,7 @@ class ScriptsFileManager {
try fileManager.moveItem(at: location, to: savedURL)

print("Saved URL: ", savedURL)

ExcuteScripts.executeShellScript(path: savedURL.path)
SecurityScopedResourceAccess.accessResourceExecuteShellScript(scriptPath: savedURL.path)

} catch {
print("File error: \(error)")
Expand Down
59 changes: 59 additions & 0 deletions Box42/Shared/SecurityScopedResourceAccess.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// SecurityScopedResourceAccess.swift
// Box42
//
// Created by Chanhee Kim on 8/31/23.
//

import Foundation

class SecurityScopedResourceAccess {
private static let queue = DispatchQueue(label: "com.yourApp.securityAccessQueue", attributes: .concurrent)
private static var isAccessing = false
static var bookmarkData: Data? {
get {
return UserDefaults.standard.data(forKey: "bookmarkData")
}
set {
UserDefaults.standard.set(newValue, forKey: "bookmarkData")
}
}

static func accessResourceExecuteShellScript(scriptPath: String) {
queue.async(flags: .barrier) {
var url: URL? = nil // 이 부분을 추가하여 url 변수의 스코프를 확장합니다.
do {
var staleBookmarkData = false
guard let bookmarkData = self.bookmarkData else {
print("Bookmark data not available.")
return
}

print("Stored bookmark data: \(String(describing: UserDefaults.standard.data(forKey: "bookmarkData")))")


url = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &staleBookmarkData)

if staleBookmarkData {
// Refresh the bookmark data and save it.
}

isAccessing = url?.startAccessingSecurityScopedResource() ?? false

// Perform work here
if isAccessing {
ExecuteScripts.executeShellScript(path: scriptPath)
}

} catch {
print("An error occurred: \(error)")
}

// Cleanup
if isAccessing {
// Make sure to match this with a call to startAccessingSecurityScopedResource()
url?.stopAccessingSecurityScopedResource()
}
}
}
}

0 comments on commit f1e074b

Please sign in to comment.