From 327609c8400498dc616e23be3cd5216b8d4ec49c Mon Sep 17 00:00:00 2001 From: qinyuhang Date: Mon, 12 Sep 2016 17:05:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20for=20Swift=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 已解决冲突问题,请Merge --- ShadowsocksX-NG/AppDelegate.swift | 24 +++- ShadowsocksX-NG/Base.lproj/MainMenu.xib | 26 ++++ ShadowsocksX-NG/ServerProfileManager.swift | 112 +++++++++++++++++- ShadowsocksX-NG/example-gui-config.json | 44 +++++++ .../zh-Hans.lproj/MainMenu.strings | 8 ++ 5 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 ShadowsocksX-NG/example-gui-config.json diff --git a/ShadowsocksX-NG/AppDelegate.swift b/ShadowsocksX-NG/AppDelegate.swift index 60796e8..d91db56 100755 --- a/ShadowsocksX-NG/AppDelegate.swift +++ b/ShadowsocksX-NG/AppDelegate.swift @@ -35,6 +35,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele @IBOutlet weak var serversMenuItem: NSMenuItem! @IBOutlet var showQRCodeMenuItem: NSMenuItem! @IBOutlet var scanQRCodeMenuItem: NSMenuItem! + @IBOutlet var showBunchJsonExampleFileItem: NSMenuItem! + @IBOutlet var importBunchJsonFileItem: NSMenuItem! + @IBOutlet var exportAllServerProfileItem: NSMenuItem! @IBOutlet var serversPreferencesMenuItem: NSMenuItem! @IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem! @@ -259,7 +262,20 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele ScanQRCodeOnScreen() } - @IBAction func toggleLaunghAtLogin(_ sender: NSMenuItem) { + @IBAction func showBunchJsonExampleFile(sender: NSMenuItem) { + ServerProfileManager.showExampleConfigFile() + } + + @IBAction func importBunchJsonFile(sender: NSMenuItem) { + ServerProfileManager.instance.importConfigFile() + //updateServersMenu()//not working + } + + @IBAction func exportAllServerProfile(sender: NSMenuItem) { + ServerProfileManager.instance.exportConfigFile() + } + + @IBAction func toggleLaunghAtLogin(sender: NSMenuItem) { launchAtLoginController.launchAtLogin = !launchAtLoginController.launchAtLogin; updateLaunchAtLoginMenu() } @@ -460,6 +476,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele let showQRItem = showQRCodeMenuItem let scanQRItem = scanQRCodeMenuItem let preferencesItem = serversPreferencesMenuItem + let showBunch = showBunchJsonExampleFileItem + let importBuntch = importBunchJsonFileItem + let exportAllServer = exportAllServerProfileItem var i = 0 for p in mgr.profiles { @@ -486,6 +505,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele } serversMenuItem.submenu?.addItem(showQRItem!) serversMenuItem.submenu?.addItem(scanQRItem!) + serversMenuItem.submenu?.addItem(showBunch!) + serversMenuItem.submenu?.addItem(importBuntch!) + serversMenuItem.submenu?.addItem(exportAllServer!) serversMenuItem.submenu?.addItem(NSMenuItem.separator()) serversMenuItem.submenu?.addItem(preferencesItem!) } diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 92b450c..19883ba 100755 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -1,5 +1,10 @@ +<<<<<<< HEAD +======= + + +>>>>>>> 8dfa5e4... 批量导入导出配置文件功能 @@ -16,7 +21,9 @@ + + @@ -24,6 +31,7 @@ + @@ -110,6 +118,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/ShadowsocksX-NG/ServerProfileManager.swift b/ShadowsocksX-NG/ServerProfileManager.swift index 61b62c7..900503e 100644 --- a/ShadowsocksX-NG/ServerProfileManager.swift +++ b/ShadowsocksX-NG/ServerProfileManager.swift @@ -2,7 +2,7 @@ // ServerProfileManager.swift // ShadowsocksX-NG // -// Created by 邱宇舟 on 16/6/6. +// Created by 邱宇舟 on 16/6/6. Modified by 秦宇航 16/9/12 // Copyright © 2016年 qiuyuzhou. All rights reserved. // @@ -70,4 +70,114 @@ class ServerProfileManager: NSObject { return nil } } + + func importConfigFile() { + let openPanel = NSOpenPanel() + openPanel.title = "Choose Config Json File".localized + openPanel.allowsMultipleSelection = false + openPanel.canChooseDirectories = false + openPanel.canCreateDirectories = false + openPanel.canChooseFiles = true + openPanel.becomeKey() + openPanel.begin { (result) -> Void in + if (result == NSFileHandlingPanelOKButton && (openPanel.url) != nil) { + let fileManager = FileManager.default + let filePath:String = (openPanel.url?.path)! + if (fileManager.fileExists(atPath: filePath) && filePath.hasSuffix("json")) { + let data = fileManager.contents(atPath: filePath) + let readString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)! + let readStringData = readString.data(using: String.Encoding.utf8.rawValue) + + let jsonArr1 = try! JSONSerialization.jsonObject(with: readStringData!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary + + for item in jsonArr1.object(forKey: "configs") as! [[String: AnyObject]]{ + let profile = ServerProfile() + profile.serverHost = item["server"] as! String + profile.serverPort = UInt16((item["server_port"]?.integerValue)!) + profile.method = item["method"] as! String + profile.password = item["password"] as! String + profile.remark = item["remarks"] as! String + self.profiles.append(profile) + self.save() + NotificationCenter.default.post(name: NSNotification.Name(rawValue: NOTIFY_SERVER_PROFILES_CHANGED), object: nil) + } + let configsCount = (jsonArr1.object(forKey: "configs") as! [[String: AnyObject]]).count + let notification = NSUserNotification() + notification.title = "Import Server Profile succeed!".localized + notification.informativeText = "Successful import \(configsCount) items".localized + NSUserNotificationCenter.default + .deliver(notification) + }else{ + let notification = NSUserNotification() + notification.title = "Import Server Profile failed!".localized + notification.informativeText = "Invalid config file!".localized + NSUserNotificationCenter.default + .deliver(notification) + return + } + } + } + } + + func exportConfigFile() { + //读取example文件,删掉configs里面的配置,再用NSDictionary填充到configs里面 + let fileManager = FileManager.default + + let filePath:String = Bundle.main.bundlePath + "/Contents/Resources/example-gui-config.json" + let data = fileManager.contents(atPath: filePath) + let readString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)! + let readStringData = readString.data(using: String.Encoding.utf8.rawValue) + let jsonArr1 = try! JSONSerialization.jsonObject(with: readStringData!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary + + let configsArray:NSMutableArray = [] //not using var? + + for profile in profiles{ + let configProfile:NSMutableDictionary = [:] //not using var? + //standard ss profile + configProfile.setValue(true, forKey: "enable") + configProfile.setValue(profile.serverHost, forKey: "server") + configProfile.setValue(NSNumber(value:profile.serverPort), forKey: "server_port")//not work + configProfile.setValue(profile.password, forKey: "password") + configProfile.setValue(profile.method, forKey: "method") + configProfile.setValue(profile.remark, forKey: "remarks") + configProfile.setValue(profile.remark.data(using: String.Encoding.utf8)?.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)), forKey: "remarks_base64") + configsArray.add(configProfile) + } + jsonArr1.setValue(configsArray, forKey: "configs") + let jsonData = try! JSONSerialization.data(withJSONObject: jsonArr1, options: JSONSerialization.WritingOptions.prettyPrinted) + let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String + let savePanel = NSSavePanel() + savePanel.title = "Export Config Json File".localized + savePanel.canCreateDirectories = true + savePanel.allowedFileTypes = ["json"] + savePanel.nameFieldStringValue = "export.json" + savePanel.becomeKey() + savePanel.begin { (result) -> Void in + if (result == NSFileHandlingPanelOKButton && (savePanel.url) != nil) { + //write jsonArr1 back to file + try! jsonString.write(toFile: (savePanel.url?.path)!, atomically: true, encoding: String.Encoding.utf8) + NSWorkspace.shared().selectFile((savePanel.url?.path)!, inFileViewerRootedAtPath: (savePanel.directoryURL?.path)!) + let notification = NSUserNotification() + notification.title = "Export Server Profile succeed!".localized + notification.informativeText = "Successful Export \(self.profiles.count) items".localized + NSUserNotificationCenter.default + .deliver(notification) + } + } + } + + class func showExampleConfigFile() { + //copy file to ~/Downloads folder + let filePath:String = Bundle.main.bundlePath + "/Contents/Resources/example-gui-config.json" + let fileMgr = FileManager.default + let dataPath = NSHomeDirectory() + "/Downloads" + let destPath = dataPath + "/example-gui-config.json" + //检测文件是否已经存在,如果存在直接用sharedWorkspace显示 + if fileMgr.fileExists(atPath: destPath) { + NSWorkspace.shared().selectFile(destPath, inFileViewerRootedAtPath: dataPath) + }else{ + try! fileMgr.copyItem(atPath: filePath, toPath: destPath) + NSWorkspace.shared().selectFile(destPath, inFileViewerRootedAtPath: dataPath) + } + } } diff --git a/ShadowsocksX-NG/example-gui-config.json b/ShadowsocksX-NG/example-gui-config.json new file mode 100644 index 0000000..937e1e3 --- /dev/null +++ b/ShadowsocksX-NG/example-gui-config.json @@ -0,0 +1,44 @@ +{ + "index": 0, + "random": false, + "global": false, + "enabled": true, + "shareOverLan": false, + "isDefault": false, + "localPort": 1080, + "pacUrl": null, + "useOnlinePac": false, + "reconnectTimes": 3, + "randomAlgorithm": 0, + "TTL": 0, + "proxyEnable": false, + "proxyType": 0, + "proxyHost": null, + "proxyPort": 0, + "proxyAuthUser": null, + "proxyAuthPass": null, + "authUser": null, + "authPass": null, + "autoban": false, + "configs": [{ + "remarks": "example", + "server": "abc.xyz", + "server_port": 1234, + "method": "rc4-md5", + "remarks_base64": "ZXhhbXBsZQ==", + "password": "passwd", + "tcp_over_udp": false, + "udp_over_tcp": false, + "enable": true + },{ + "remarks": "example2", + "server": "xyz.xyz", + "server_port": 1234, + "method": "rc4-md5", + "remarks_base64": "ZXhhbXBsZTI=", + "password": "passwd", + "tcp_over_udp": false, + "udp_over_tcp": false, + "enable": true + }] +} \ No newline at end of file diff --git a/ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings b/ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings index 99145f2..655994d 100644 --- a/ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings +++ b/ShadowsocksX-NG/zh-Hans.lproj/MainMenu.strings @@ -80,3 +80,11 @@ /* Class = "NSMenuItem"; title = "HTTP Proxy Preference ..."; ObjectID = "uEp-Gz-cu0"; */ "uEp-Gz-cu0.title" = "HTTP代理设置..."; +/* Class = "NSMenuItem"; title = "Show Bunch Json Example File..."; ObjectID = "pdy-JE-50Q"; */ +"pdy-JE-50Q.title" = "显示示例服务器配置文件..."; + +/* Class = "NSMenuItem"; title = "Import Bunch Json File..."; ObjectID = "T9g-gy-gvv"; */ +"T9g-gy-gvv.title" = "导入服务器配置文件..."; + +/* Class = "NSMenuItem"; title = "Export All Server To Json..."; ObjectID = "6k0-gn-DQv"; */ +"6k0-gn-DQv.title" = "导出全部服务器配置..."; From 52a32a68af90140f219a441aceb4c5dfe93644ba Mon Sep 17 00:00:00 2001 From: qinyuhang Date: Tue, 18 Oct 2016 00:20:13 +0800 Subject: [PATCH 2/3] UI fix --- ShadowsocksX-NG/Base.lproj/MainMenu.xib | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 19883ba..8d01395 100755 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -1,10 +1,5 @@ -<<<<<<< HEAD -======= - - ->>>>>>> 8dfa5e4... 批量导入导出配置文件功能 From 15193a978d3ec5320cb9d04f7f5e339e6f23fa2a Mon Sep 17 00:00:00 2001 From: qinyuhang Date: Thu, 20 Oct 2016 00:30:33 +0800 Subject: [PATCH 3/3] fix storyboard merge problem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 那三个按钮是灰色的大概是因为merge的时候storyboard 文件冲突 --- ShadowsocksX-NG/Base.lproj/MainMenu.xib | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ShadowsocksX-NG/Base.lproj/MainMenu.xib b/ShadowsocksX-NG/Base.lproj/MainMenu.xib index 8d01395..d2f0979 100755 --- a/ShadowsocksX-NG/Base.lproj/MainMenu.xib +++ b/ShadowsocksX-NG/Base.lproj/MainMenu.xib @@ -1,5 +1,5 @@ - + @@ -16,17 +16,17 @@ - + - - + + - + @@ -116,19 +116,19 @@ - + - + - + @@ -156,7 +156,7 @@ - +