使用 NSOpenPanel 限制对某些文件夹的访问

发布于 2024-11-01 20:17:13 字数 320 浏览 1 评论 0原文

我使用 NSOpenPanel 允许用户选择一个文件夹来保存文档。我想限制它们可以保存到哪个文件夹(就层次结构而言)。本质上,我想阻止他们选择上面的任何文件夹:

/用户/用户名/

所以文件夹

/用户/用户名/猫/

是可以接受的,但是

/用户/用户名/

/应用程序/猫/

不允许 。我想知道如何实施这个限制。

谢谢。

I'm using NSOpenPanel to allow a user to select a folder to save documents into. I would like to restrict what folder (in terms of hierarchy) they can save into. Essentially, I want to prevent them from choosing any folder above:

/Users/username/

So the folder

/Users/username/cats/

would be acceptable but

/Users/username/

/Applications/cats/

would not be allowed. I was wondering how to implement this restriction.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

美羊羊 2024-11-08 20:17:13

请注意 NSOpenPanel 继承自 NSSavePanel,它又定义了委托和相应的委托协议NSOpenSavePanelDelegate。您可以使用委托来扩展打开面板的行为,以便包含您在问题中列出的限制。

例如,假设应用程序委托实现了打开面板限制,请使其符合 NSOpenSavePanelDelegate 协议:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSOpenSavePanelDelegate>
@end

在应用程序委托的实现中,告诉打开面板应用程序委托充当打开面板委托:

NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setDirectory:NSHomeDirectory()];
[openPanel setCanChooseDirectories:NO];
[openPanel setDelegate:self];
[openPanel runModal];

并实现以下委托方法:

- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    return [path hasPrefix:homeDir] && ! [path isEqualToString:homeDir];
}

- (void)panel:(id)sender didChangeToDirectoryURL:(NSURL *)url {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    // If the user has changed to a non home directory, send him back home!
    if (! [path hasPrefix:homeDir]) [sender setDirectory:homeDir];
}

- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    if (![path hasPrefix:homeDir]) {
        if (outError)
           *outError = ; // create an appropriate NSError instance

        return NO;    
    }
    return YES;
}

Note that NSOpenPanel inherits from NSSavePanel, which in turn defines a delegate and a corresponding delegate protocol NSOpenSavePanelDelegate. You can use the delegate to extend the behaviour of the open panel so as to include the restriction you’ve listed in your question.

For instance, assuming the application delegate implements the open panel restriction, make it conform to the NSOpenSavePanelDelegate protocol:

@interface AppDelegate : NSObject <NSApplicationDelegate, NSOpenSavePanelDelegate>
@end

In the implementation of your application delegate, tell the open panel that the application delegate acts as the open panel delegate:

NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setDirectory:NSHomeDirectory()];
[openPanel setCanChooseDirectories:NO];
[openPanel setDelegate:self];
[openPanel runModal];

And implement the following delegate methods:

- (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    return [path hasPrefix:homeDir] && ! [path isEqualToString:homeDir];
}

- (void)panel:(id)sender didChangeToDirectoryURL:(NSURL *)url {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    // If the user has changed to a non home directory, send him back home!
    if (! [path hasPrefix:homeDir]) [sender setDirectory:homeDir];
}

- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError {
    NSString *path = [url path];
    NSString *homeDir = NSHomeDirectory();

    if (![path hasPrefix:homeDir]) {
        if (outError)
           *outError = ; // create an appropriate NSError instance

        return NO;    
    }
    return YES;
}
薄凉少年不暖心 2024-11-08 20:17:13

因此,我尝试将其更新为 Swift 5.5

我将所有委托方法都包含在内,以便让任何偶然发现此问题的人清楚地了解。

class Utility {
    var homeDirectory: URL?

    func openPanel(url: URL, sender: Any) -> URL? {
        let openPanel = NSOpenPanel()
        openPanel.canChooseDirectories = true
        openPanel.canChooseFiles = false
        openPanel.delegate = self
        appHomeDirectory = url
        openPanel.directoryURL = homeDirectory
        openPanel.showsHiddenFiles = false
        openPanel.canCreateDirectories = true
        switch openPanel.runModal() {
            case .OK:
                print("OK")
            case .cancel:
                print("Cancel")
            case .abort:
                print("Abort")
            case .continue:
                print("Continue")
            case .stop:
                print("Stop")
            default:
                print("Unknown Response")
        }
        return nil
    }

    func panel(_ sender: Any, didChangeToDirectoryURL url: URL?) {
        guard let _url = url else {
            return
        }
        print("didChangeToDirectoryURL")
        print("url: \(_url)")
    }

    func panel(_ sender: Any, shouldEnable url: URL) -> Bool {
        guard let homeDirectory = self.homeDirectory else {
            // Since homeDirectory cannot be set
            return false
        }
        print("shouldEnable")
        print("url path: \(url.path)")
        print("homeDirectory path: \(homeDirectory.path)")
        print("url.path.hasSuffix(homeDirectory.path): \(url.path.hasSuffix(homeDirectory.path))")
        // Removing the last path component of the sent URL
        print("url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path): \(url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path))")
        if url == homeDirectory {
            // This ensures the user can get back into the homeDirectory if
            // they navigated above the homeDirectory.
            return true
        } else {
            // Delete the last path component and then compare if the suffix of the url
            // path is the same as the homeDirectory path and return result.
            return (url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path))
        }
    }

    func panel(_ sender: Any, validate url: URL) throws {
        print("validate")
        print("url: \(url)")
    }

    func panel(_ sender: Any, willExpand expanding: Bool) {
        print("willExpand")
        print("expanding: \(expanding)")
    }

    func panelSelectionDidChange(_ sender: Any?) {
        print("panelSelectionDidChange")
    }

}

在我的 ViewController 中,我有一个“Utility”实例作为“utility”,还有一个 imageButton 的 IBAction,其 Storyboard 标识为“fileBrowseImageButton”。

用法:

@IBAction func fileBrowseImageButtonClicked(sender: Any?) {
    guard let url = utility.openPanel(url: url, sender: sender as! NSButton) else {
        return
    }
    // Do whatever needed with the returned url.
}

So, I took a stab at updating this for Swift 5.5.

I am including all the delegate methods for clarity to anyone who stumbles across this.

class Utility {
    var homeDirectory: URL?

    func openPanel(url: URL, sender: Any) -> URL? {
        let openPanel = NSOpenPanel()
        openPanel.canChooseDirectories = true
        openPanel.canChooseFiles = false
        openPanel.delegate = self
        appHomeDirectory = url
        openPanel.directoryURL = homeDirectory
        openPanel.showsHiddenFiles = false
        openPanel.canCreateDirectories = true
        switch openPanel.runModal() {
            case .OK:
                print("OK")
            case .cancel:
                print("Cancel")
            case .abort:
                print("Abort")
            case .continue:
                print("Continue")
            case .stop:
                print("Stop")
            default:
                print("Unknown Response")
        }
        return nil
    }

    func panel(_ sender: Any, didChangeToDirectoryURL url: URL?) {
        guard let _url = url else {
            return
        }
        print("didChangeToDirectoryURL")
        print("url: \(_url)")
    }

    func panel(_ sender: Any, shouldEnable url: URL) -> Bool {
        guard let homeDirectory = self.homeDirectory else {
            // Since homeDirectory cannot be set
            return false
        }
        print("shouldEnable")
        print("url path: \(url.path)")
        print("homeDirectory path: \(homeDirectory.path)")
        print("url.path.hasSuffix(homeDirectory.path): \(url.path.hasSuffix(homeDirectory.path))")
        // Removing the last path component of the sent URL
        print("url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path): \(url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path))")
        if url == homeDirectory {
            // This ensures the user can get back into the homeDirectory if
            // they navigated above the homeDirectory.
            return true
        } else {
            // Delete the last path component and then compare if the suffix of the url
            // path is the same as the homeDirectory path and return result.
            return (url.deletingLastPathComponent().path.hasSuffix(homeDirectory.path))
        }
    }

    func panel(_ sender: Any, validate url: URL) throws {
        print("validate")
        print("url: \(url)")
    }

    func panel(_ sender: Any, willExpand expanding: Bool) {
        print("willExpand")
        print("expanding: \(expanding)")
    }

    func panelSelectionDidChange(_ sender: Any?) {
        print("panelSelectionDidChange")
    }

}

In my ViewController I have an instance of “Utility“ as “utility“ and an IBAction for an imageButton that has a Storyboard identity of “fileBrowseImageButton“.

Usage:

@IBAction func fileBrowseImageButtonClicked(sender: Any?) {
    guard let url = utility.openPanel(url: url, sender: sender as! NSButton) else {
        return
    }
    // Do whatever needed with the returned url.
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文