Harden and improve "Download Mod from Url" parser (#8924)

This commit is contained in:
SomeTroglodyte
2023-03-16 21:43:16 +01:00
committed by GitHub
parent c5993e0392
commit 5a58d8c39c
3 changed files with 46 additions and 26 deletions

View File

@ -1642,7 +1642,8 @@ Download [modName] =
Update [modName] =
Could not download mod list =
Download mod from URL =
Please enter the mod repository -or- archive zip url: =
Please enter the mod repository -or- archive zip -or- branch url: =
Invalid link! =
Paste from clipboard =
Download =
Done! =

View File

@ -323,36 +323,50 @@ object Github {
//var has_wiki = false // a wiki could mean proper documentation for the mod?
/**
* Initialize `this` with an url, extracting all possible fields from it.
* Initialize `this` with an url, extracting all possible fields from it
* (html_url, author, repoName, branchName).
*
* Allows basic repo url or complete 'zip' url from github's code->download zip menu
* Allow url formats:
* * Basic repo url:
* https://github.com/author/repoName
* * or complete 'zip' url from github's code->download zip menu:
* https://github.com/author/repoName/archive/refs/heads/branchName.zip
* * or the branch url same as one navigates to on github through the "branches" menu:
* https://github.com/author/repoName/tree/branchName
*
* @return `this` to allow chaining
* In the case of the basic repo url, an API query is sent to determine the default branch.
* Other url forms will not go online.
*
* @return `this` to allow chaining, `null` for invalid links or any other failures
*/
fun parseUrl(url: String): Repo {
// Allow url formats
// https://github.com/author/repoName
// or
// https://github.com/author/repoName/archive/refs/heads/branchName.zip
// and extract author, repoName, branchName
fun parseUrl(url: String): Repo? {
fun processMatch(matchResult: MatchResult): Repo {
html_url = matchResult.groups[1]!!.value
owner.login = matchResult.groups[2]!!.value
name = matchResult.groups[3]!!.value
default_branch = matchResult.groups[4]!!.value
return this
}
html_url = url
default_branch = "master"
val matchZip = Regex("""^(.*/(.*)/(.*))/archive/(?:.*/)?([^.]+).zip$""").matchEntire(url)
if (matchZip != null && matchZip.groups.size > 3) {
html_url = matchZip.groups[1]!!.value
owner.login = matchZip.groups[2]!!.value
name = matchZip.groups[3]!!.value
default_branch = matchZip.groups[4]!!.value
} else {
val matchRepo = Regex("""^.*/(.*)/(.*)/?$""").matchEntire(url)
if (matchRepo != null && matchRepo.groups.size > 2) {
val repoString = download("https://api.github.com/repos/${matchRepo.groups[1]!!.value}/${matchRepo.groups[2]!!.value}")!!
.bufferedReader().readText()
return json().fromJson(Repo::class.java, repoString)
}
if (matchZip != null && matchZip.groups.size > 4)
return processMatch(matchZip)
val matchBranch = Regex("""^(.*/(.*)/(.*))/tree/([^/]+)$""").matchEntire(url)
if (matchBranch != null && matchBranch.groups.size > 4)
return processMatch(matchBranch)
val matchRepo = Regex("""^.*//.*/(.+)/(.+)/?$""").matchEntire(url)
if (matchRepo != null && matchRepo.groups.size > 2) {
// Query API if we got the first URL format to get the correct default branch
val response = download("https://api.github.com/repos/${matchRepo.groups[1]!!.value}/${matchRepo.groups[2]!!.value}")
?: return null
val repoString = response.bufferedReader().readText()
return json().fromJson(Repo::class.java, repoString)
}
return this
return null
}
}

View File

@ -400,8 +400,8 @@ class ModManagementScreen(
val downloadButton = "Download mod from URL".toTextButton()
downloadButton.onClick {
val popup = Popup(this)
popup.addGoodSizedLabel("Please enter the mod repository -or- archive zip url:").row()
val textField = UncivTextField.create("")
popup.addGoodSizedLabel("Please enter the mod repository -or- archive zip -or- branch url:").row()
val textField = UncivTextField.create("").apply { maxLength = 666 }
popup.add(textField).width(stage.width / 2).row()
val pasteLinkButton = "Paste from clipboard".toTextButton()
pasteLinkButton.onClick {
@ -412,7 +412,12 @@ class ModManagementScreen(
actualDownloadButton.onClick {
actualDownloadButton.setText("Downloading...".tr())
actualDownloadButton.disable()
downloadMod(Github.Repo().parseUrl(textField.text)) { popup.close() }
val repo = Github.Repo().parseUrl(textField.text)
if (repo == null) {
ToastPopup("Invalid link!", this@ModManagementScreen)
popup.close() // Re-enabling button would be nice, but Toast doesn't work over other Popups
} else
downloadMod(repo) { popup.close() }
}
popup.add(actualDownloadButton).row()
popup.addCloseButton()