mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: supports importing hosts from FinalShell (#295)
This commit is contained in:
@@ -16,6 +16,7 @@ import org.apache.commons.csv.CSVParser
|
|||||||
import org.apache.commons.csv.CSVPrinter
|
import org.apache.commons.csv.CSVPrinter
|
||||||
import org.apache.commons.io.FileUtils
|
import org.apache.commons.io.FileUtils
|
||||||
import org.apache.commons.io.FilenameUtils
|
import org.apache.commons.io.FilenameUtils
|
||||||
|
import org.apache.commons.io.filefilter.FileFilterUtils
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||||
import org.ini4j.Ini
|
import org.ini4j.Ini
|
||||||
@@ -365,6 +366,7 @@ class NewHostTree : JXTree() {
|
|||||||
val importMenu = JMenu(I18n.getString("termora.welcome.contextmenu.import"))
|
val importMenu = JMenu(I18n.getString("termora.welcome.contextmenu.import"))
|
||||||
val csvMenu = importMenu.add("CSV")
|
val csvMenu = importMenu.add("CSV")
|
||||||
val xShellMenu = importMenu.add("Xshell")
|
val xShellMenu = importMenu.add("Xshell")
|
||||||
|
val finalShellMenu = importMenu.add("FinalShell")
|
||||||
val windTermMenu = importMenu.add("WindTerm")
|
val windTermMenu = importMenu.add("WindTerm")
|
||||||
val secureCRTMenu = importMenu.add("SecureCRT")
|
val secureCRTMenu = importMenu.add("SecureCRT")
|
||||||
val mobaXtermMenu = importMenu.add("MobaXterm")
|
val mobaXtermMenu = importMenu.add("MobaXterm")
|
||||||
@@ -398,6 +400,7 @@ class NewHostTree : JXTree() {
|
|||||||
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
xShellMenu.addActionListener { importHosts(lastNode, ImportType.Xshell) }
|
||||||
secureCRTMenu.addActionListener { importHosts(lastNode, ImportType.SecureCRT) }
|
secureCRTMenu.addActionListener { importHosts(lastNode, ImportType.SecureCRT) }
|
||||||
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
mobaXtermMenu.addActionListener { importHosts(lastNode, ImportType.MobaXterm) }
|
||||||
|
finalShellMenu.addActionListener { importHosts(lastNode, ImportType.FinalShell) }
|
||||||
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
csvMenu.addActionListener { importHosts(lastNode, ImportType.CSV) }
|
||||||
windTermMenu.addActionListener { importHosts(lastNode, ImportType.WindTerm) }
|
windTermMenu.addActionListener { importHosts(lastNode, ImportType.WindTerm) }
|
||||||
open.addActionListener { openHosts(it, false) }
|
open.addActionListener { openHosts(it, false) }
|
||||||
@@ -656,6 +659,11 @@ class NewHostTree : JXTree() {
|
|||||||
chooser.dialogTitle = "Xshell Sessions"
|
chooser.dialogTitle = "Xshell Sessions"
|
||||||
chooser.isAcceptAllFileFilterUsed = true
|
chooser.isAcceptAllFileFilterUsed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImportType.FinalShell -> {
|
||||||
|
chooser.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY
|
||||||
|
chooser.isAcceptAllFileFilterUsed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val dir = properties.getString("NewHostTree.ImportHosts.defaultDir", StringUtils.EMPTY)
|
val dir = properties.getString("NewHostTree.ImportHosts.defaultDir", StringUtils.EMPTY)
|
||||||
@@ -706,20 +714,22 @@ class NewHostTree : JXTree() {
|
|||||||
// 选择文件
|
// 选择文件
|
||||||
val code = chooser.showOpenDialog(owner)
|
val code = chooser.showOpenDialog(owner)
|
||||||
|
|
||||||
// 记住目录
|
|
||||||
properties.putString("NewHostTree.ImportHosts.defaultDir", chooser.currentDirectory.absolutePath)
|
|
||||||
|
|
||||||
if (code != JFileChooser.APPROVE_OPTION) {
|
if (code != JFileChooser.APPROVE_OPTION) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val file = chooser.selectedFile
|
val file = chooser.selectedFile
|
||||||
|
properties.putString(
|
||||||
|
"NewHostTree.ImportHosts.defaultDir",
|
||||||
|
(if (FileUtils.isDirectory(file)) file else file.parentFile).absolutePath
|
||||||
|
)
|
||||||
|
|
||||||
val nodes = when (type) {
|
val nodes = when (type) {
|
||||||
ImportType.WindTerm -> parseFromWindTerm(folder, file)
|
ImportType.WindTerm -> parseFromWindTerm(folder, file)
|
||||||
ImportType.SecureCRT -> parseFromSecureCRT(folder, file)
|
ImportType.SecureCRT -> parseFromSecureCRT(folder, file)
|
||||||
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
ImportType.MobaXterm -> parseFromMobaXterm(folder, file)
|
||||||
ImportType.Xshell -> parseFromXshell(folder, file)
|
ImportType.Xshell -> parseFromXshell(folder, file)
|
||||||
|
ImportType.FinalShell -> parseFromFinalShell(folder, file)
|
||||||
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
ImportType.CSV -> file.bufferedReader().use { parseFromCSV(folder, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -874,6 +884,45 @@ class NewHostTree : JXTree() {
|
|||||||
return parseFromCSV(folder, StringReader(sw.toString()))
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun parseFromFinalShell(folder: HostTreeNode, dir: File): List<HostTreeNode> {
|
||||||
|
val files = FileUtils.listFiles(
|
||||||
|
dir,
|
||||||
|
FileFilterUtils.suffixFileFilter("_connect_config.json"),
|
||||||
|
FileFilterUtils.trueFileFilter()
|
||||||
|
)
|
||||||
|
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
OptionPane.showMessageDialog(
|
||||||
|
owner,
|
||||||
|
I18n.getString("termora.welcome.contextmenu.import.finalshell-folder-empty")
|
||||||
|
)
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val sw = StringWriter()
|
||||||
|
CSVPrinter(sw, CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).get()).use { printer ->
|
||||||
|
for (file in files) {
|
||||||
|
try {
|
||||||
|
val json = ohMyJson.runCatching { ohMyJson.parseToJsonElement(file.readText()) }
|
||||||
|
.getOrNull()?.jsonObject ?: continue
|
||||||
|
val username = json["user_name"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
val label = json["name"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
val host = json["host"]?.jsonPrimitive?.content ?: StringUtils.EMPTY
|
||||||
|
val port = json["port"]?.jsonPrimitive?.intOrNull ?: 22
|
||||||
|
if (StringUtils.isAllBlank(host, label)) continue
|
||||||
|
val folders = FilenameUtils.separatorsToUnix(file.parentFile.relativeTo(dir).toString())
|
||||||
|
printer.printRecord(folders, StringUtils.defaultIfBlank(label, host), host, port, username, "SSH")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (log.isErrorEnabled) {
|
||||||
|
log.error(file.absolutePath, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseFromCSV(folder, StringReader(sw.toString()))
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseFromCSV(folderNode: HostTreeNode, sr: Reader): List<HostTreeNode> {
|
private fun parseFromCSV(folderNode: HostTreeNode, sr: Reader): List<HostTreeNode> {
|
||||||
val records = CSVParser.builder()
|
val records = CSVParser.builder()
|
||||||
.setFormat(CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).setSkipHeaderRecord(true).get())
|
.setFormat(CSVFormat.EXCEL.builder().setHeader(*CSV_HEADERS).setSkipHeaderRecord(true).get())
|
||||||
@@ -954,7 +1003,8 @@ class NewHostTree : JXTree() {
|
|||||||
CSV,
|
CSV,
|
||||||
Xshell,
|
Xshell,
|
||||||
SecureCRT,
|
SecureCRT,
|
||||||
MobaXterm
|
MobaXterm,
|
||||||
|
FinalShell,
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MoveHostTransferable(val nodes: List<HostTreeNode>) : Transferable {
|
private class MoveHostTransferable(val nodes: List<HostTreeNode>) : Transferable {
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ termora.welcome.contextmenu.import.csv.download-template=Do you want to import o
|
|||||||
termora.welcome.contextmenu.import.csv.download-template-done=Download the template successfully
|
termora.welcome.contextmenu.import.csv.download-template-done=Download the template successfully
|
||||||
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=Download the template successfully, Do you want to open the folder?
|
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=Download the template successfully, Do you want to open the folder?
|
||||||
termora.welcome.contextmenu.import.xshell-folder-empty=The folder does not contain any *.xsh files, Please select the correct Xshell Sessions directory
|
termora.welcome.contextmenu.import.xshell-folder-empty=The folder does not contain any *.xsh files, Please select the correct Xshell Sessions directory
|
||||||
|
termora.welcome.contextmenu.import.finalshell-folder-empty=The folder does not contain any *_connect_config.json files, Please select the correct FinalShell directory
|
||||||
|
|
||||||
# New Host
|
# New Host
|
||||||
termora.new-host.title=Create a new host
|
termora.new-host.title=Create a new host
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ termora.welcome.contextmenu.import.csv.download-template=您要导入还是下
|
|||||||
termora.welcome.contextmenu.import.csv.download-template-done=下载成功
|
termora.welcome.contextmenu.import.csv.download-template-done=下载成功
|
||||||
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=下载成功, 是否需要打开所在文件夹?
|
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=下载成功, 是否需要打开所在文件夹?
|
||||||
termora.welcome.contextmenu.import.xshell-folder-empty=该文件夹不包含 *.xsh 文件,请选择正确的 Xshell 会话目录
|
termora.welcome.contextmenu.import.xshell-folder-empty=该文件夹不包含 *.xsh 文件,请选择正确的 Xshell 会话目录
|
||||||
|
termora.welcome.contextmenu.import.finalshell-folder-empty=该文件夹不包含 *_connect_config.json 文件,请选择正确的 FinalShell 配置目录
|
||||||
|
|
||||||
# New Host
|
# New Host
|
||||||
termora.new-host.title=新建主机
|
termora.new-host.title=新建主机
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ termora.welcome.contextmenu.import.csv.download-template=您要匯入還是下
|
|||||||
termora.welcome.contextmenu.import.csv.download-template-done=下載成功
|
termora.welcome.contextmenu.import.csv.download-template-done=下載成功
|
||||||
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=下載成功, 是否需要開啟所在資料夾?
|
termora.welcome.contextmenu.import.csv.download-template-done-open-folder=下載成功, 是否需要開啟所在資料夾?
|
||||||
termora.welcome.contextmenu.import.xshell-folder-empty=該資料夾不包含 *.xsh 文件,請選擇正確的 Xshell 會話目錄
|
termora.welcome.contextmenu.import.xshell-folder-empty=該資料夾不包含 *.xsh 文件,請選擇正確的 Xshell 會話目錄
|
||||||
|
termora.welcome.contextmenu.import.finalshell-folder-empty=該資料夾不包含 *_connect_config.json 文件,請選擇正確的 FinalShell 設定目錄
|
||||||
|
|
||||||
# New Host
|
# New Host
|
||||||
termora.new-host.title=新主機
|
termora.new-host.title=新主機
|
||||||
|
|||||||
Reference in New Issue
Block a user