mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-07 14:05:52 +01:00
Add plugin updating capability
This commit is contained in:
@@ -10,7 +10,7 @@ import ssh.SshServer
|
||||
import org.scalatra.Ok
|
||||
import org.apache.commons.io.FileUtils
|
||||
import java.io.FileInputStream
|
||||
import plugin.PluginSystem
|
||||
import plugin.{Plugin, PluginSystem}
|
||||
|
||||
class SystemSettingsController extends SystemSettingsControllerBase
|
||||
with AccountService with AdminAuthenticator
|
||||
@@ -83,36 +83,32 @@ trait SystemSettingsControllerBase extends ControllerBase {
|
||||
})
|
||||
|
||||
get("/admin/plugins")(adminOnly {
|
||||
admin.plugins.html.installed(plugin.PluginSystem.plugins)
|
||||
val installedPlugins = plugin.PluginSystem.plugins
|
||||
val updatablePlugins = getAvailablePlugins(installedPlugins).filter(_.status == "updatable")
|
||||
admin.plugins.html.installed(installedPlugins, updatablePlugins)
|
||||
})
|
||||
|
||||
post("/admin/plugins/_update", pluginForm)(adminOnly { form =>
|
||||
deletePlugins(form.pluginIds)
|
||||
installPlugins(form.pluginIds)
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
post("/admin/plugins/_delete", pluginForm)(adminOnly { form =>
|
||||
form.pluginIds.foreach { pluginId =>
|
||||
plugin.PluginSystem.uninstall(pluginId)
|
||||
val dir = new java.io.File(PluginHome, pluginId)
|
||||
if(dir.exists && dir.isDirectory){
|
||||
FileUtils.deleteQuietly(dir)
|
||||
PluginSystem.uninstall(pluginId)
|
||||
}
|
||||
}
|
||||
deletePlugins(form.pluginIds)
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
get("/admin/plugins/available")(adminOnly {
|
||||
// TODO Do periodical and asynchronous...?
|
||||
PluginSystem.updateAllRepositories()
|
||||
admin.plugins.html.available(getAvailablePlugins())
|
||||
val installedPlugins = plugin.PluginSystem.plugins
|
||||
val availablePlugins = getAvailablePlugins(installedPlugins).filter(_.status == "available")
|
||||
admin.plugins.html.available(availablePlugins)
|
||||
})
|
||||
|
||||
post("/admin/plugins/_install", pluginForm)(adminOnly { form =>
|
||||
val dir = getPluginCacheDir()
|
||||
getAvailablePlugins().filter(x => form.pluginIds.contains(x.id)).foreach { plugin =>
|
||||
val pluginDir = new java.io.File(PluginHome, plugin.id)
|
||||
if(!pluginDir.exists){
|
||||
FileUtils.copyDirectory(new java.io.File(dir, plugin.repository + "/" + plugin.id), pluginDir)
|
||||
}
|
||||
PluginSystem.installPlugin(plugin.id)
|
||||
}
|
||||
installPlugins(form.pluginIds)
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
@@ -126,9 +122,31 @@ trait SystemSettingsControllerBase extends ControllerBase {
|
||||
Ok(result)
|
||||
})
|
||||
|
||||
// TODO Move to PluginSystem or Service?
|
||||
private def getAvailablePlugins(): List[SystemSettingsControllerBase.AvailablePlugin] = {
|
||||
// TODO Move these methods to PluginSystem or Service?
|
||||
private def deletePlugins(pluginIds: List[String]): Unit = {
|
||||
pluginIds.foreach { pluginId =>
|
||||
plugin.PluginSystem.uninstall(pluginId)
|
||||
val dir = new java.io.File(PluginHome, pluginId)
|
||||
if(dir.exists && dir.isDirectory){
|
||||
FileUtils.deleteQuietly(dir)
|
||||
PluginSystem.uninstall(pluginId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def installPlugins(pluginIds: List[String]): Unit = {
|
||||
val dir = getPluginCacheDir()
|
||||
val installedPlugins = plugin.PluginSystem.plugins
|
||||
getAvailablePlugins(installedPlugins).filter(x => pluginIds.contains(x.id)).foreach { plugin =>
|
||||
val pluginDir = new java.io.File(PluginHome, plugin.id)
|
||||
if(!pluginDir.exists){
|
||||
FileUtils.copyDirectory(new java.io.File(dir, plugin.repository + "/" + plugin.id), pluginDir)
|
||||
}
|
||||
PluginSystem.installPlugin(plugin.id)
|
||||
}
|
||||
}
|
||||
|
||||
private def getAvailablePlugins(installedPlugins: List[Plugin]): List[SystemSettingsControllerBase.AvailablePlugin] = {
|
||||
val repositoryRoot = getPluginCacheDir()
|
||||
|
||||
if(repositoryRoot.exists && repositoryRoot.isDirectory){
|
||||
@@ -144,16 +162,20 @@ trait SystemSettingsControllerBase extends ControllerBase {
|
||||
}
|
||||
}
|
||||
SystemSettingsControllerBase.AvailablePlugin(
|
||||
repo.id,
|
||||
properties.getProperty("id"),
|
||||
properties.getProperty("version"),
|
||||
properties.getProperty("author"),
|
||||
properties.getProperty("url"),
|
||||
properties.getProperty("description"),
|
||||
if(installedPlugins.exists(_.id == properties.getProperty("id"))) "installed" else "available")
|
||||
repository = repo.id,
|
||||
id = properties.getProperty("id"),
|
||||
version = properties.getProperty("version"),
|
||||
author = properties.getProperty("author"),
|
||||
url = properties.getProperty("url"),
|
||||
description = properties.getProperty("description"),
|
||||
status = installedPlugins.find(_.id == properties.getProperty("id")) match {
|
||||
case Some(x) if(PluginSystem.isUpdatable(x.version, properties.getProperty("version")))=> "updatable"
|
||||
case Some(x) => "installed"
|
||||
case None => "available"
|
||||
})
|
||||
}
|
||||
} else Nil
|
||||
}.filter(x => !installedPlugins.exists(_.id == x.id))
|
||||
}
|
||||
} else Nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,28 @@ object PluginSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the plugin is updatable.
|
||||
*/
|
||||
def isUpdatable(oldVersion: String, newVersion: String): Boolean = {
|
||||
if(oldVersion == newVersion){
|
||||
false
|
||||
} else {
|
||||
val dim1 = oldVersion.split("\\.").map(_.toInt)
|
||||
val dim2 = newVersion.split("\\.").map(_.toInt)
|
||||
dim1.zip(dim2).foreach { case (a, b) =>
|
||||
if(a < b){
|
||||
return true
|
||||
} else if(a > b){
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO This is a test
|
||||
// addGlobalMenu("Google", "http://www.google.co.jp/", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAEvwAABL8BkeKJvAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAIgSURBVEiJtdZNiI1hFAfw36ORhSFFPgYLszOKJAsWRLGzks1gYyFZKFs7C7K2Y2XDRiwmq9kIJWQjJR9Tk48xRtTIRwjH4p473nm99yLNqdNTz/mf//+555x7ektEmEmbNaPs6OkUKKX0YBmWp6/IE8bwIs8xjEfEt0aiiJBl6sEuXMRLfEf8pX/PnIvJ0TPFWxE4+w+Ef/Kzbd5qDx5l8H8tkku7LG17gH7sxWatevdhEUoXsjda5RnDTZzH6jagtMe0lHIa23AJw3iOiSRZlmJ9mfcyfTzFl2AldmI3rkbEkbrAYKrX7S1eVRyWVnxhQ87eiLjQ+o2/mtyve+PuYy3W4+EfsP2/TVGKTHRI+Iz9Fdx8XOmAnZjGWRMYqoF/4ESW4hpOYk1iZ2WsLjDUTeBYBfgeuyux2XiNT5hXud+DD5W8Y90EtifoSfultfjx7MVtrKzcr8No5m7vJtCLx1hQJ8/4IZzClpyoy5ibsYUYQW81Z9o2jYgPeKr15+poEXE9+1XF9WIkOaasaV2P4k4pZUdDbEm+VEQcjIgtEfGxlLIVd/Gs6TX1MhzQquU3HK1t23f4IsuS94fxNXMO/MbXIDBg+tidw5yMbcCmylSdqWEH/kagYLKWeAt9Fcxi3KhhJuXq6SqQBMO15NDalvswmLWux4cbuToIbMS9BpJOfg8bm7imtmmTlVJWaa3hpnU9nufziBjtyDHTny0/AaA7Qnb4AM4aAAAAAElFTkSuQmCC")
|
||||
// { context => context.loginAccount.isDefined }
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<input type="submit" id="install-plugins" class="btn btn-primary" value="Install selected plugins"/>
|
||||
<input type="submit" id="install-plugins" class="btn btn-success" value="Install selected plugins"/>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
@(plugins: List[plugin.Plugin])(implicit context: app.Context)
|
||||
@(plugins: List[plugin.Plugin],
|
||||
updatablePlugins: List[app.SystemSettingsControllerBase.AvailablePlugin])(implicit context: app.Context)
|
||||
@import context._
|
||||
@import view.helpers._
|
||||
@html.main("Plugins"){
|
||||
@admin.html.menu("plugins"){
|
||||
@tab("installed")
|
||||
<form action="@path/admin/plugins/_delete" method="POST" validate="true">
|
||||
<form method="POST" validate="true">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
@@ -18,18 +19,27 @@
|
||||
<input type="checkbox" name="pluginId[@i]" value="@plugin.id"/>
|
||||
@plugin.id
|
||||
</td>
|
||||
<td>@plugin.version</td>
|
||||
<td>
|
||||
@plugin.version
|
||||
@updatablePlugins.find(_.id == plugin.id).map { x =>
|
||||
(@x.version is available)
|
||||
}
|
||||
</td>
|
||||
<td><a href="@plugin.url">@plugin.author</a></td>
|
||||
<td>@plugin.description</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
<input type="submit" id="delete-plugins" class="btn btn-danger" value="Uninstall selected plugins"/>
|
||||
<input type="submit" id="update-plugins" class="btn btn-success" value="Update selected plugins" formaction="@path/admin/plugins/_update"/>
|
||||
<input type="submit" id="delete-plugins" class="btn btn-danger" value="Uninstall selected plugins" formaction="@path/admin/plugins/_delete"/>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('#update-plugins').click(function(){
|
||||
return confirm('Selected plugin will be updated. Are you sure?');
|
||||
});
|
||||
$('#delete-plugins').click(function(){
|
||||
return confirm('Selected plugin will be removed permanently. Are you sure?');
|
||||
});
|
||||
|
||||
22
src/test/scala/plugin/PluginSystemSpec.scala
Normal file
22
src/test/scala/plugin/PluginSystemSpec.scala
Normal file
@@ -0,0 +1,22 @@
|
||||
package plugin
|
||||
|
||||
import org.specs2.mutable._
|
||||
|
||||
class PluginSystemSpec extends Specification {
|
||||
|
||||
"isUpdatable" should {
|
||||
"return true for updattable plugin" in {
|
||||
PluginSystem.isUpdatable("1.0.0", "1.0.1") must beTrue
|
||||
PluginSystem.isUpdatable("1.0.0", "1.1.0") must beTrue
|
||||
PluginSystem.isUpdatable("1.1.1", "1.2.0") must beTrue
|
||||
PluginSystem.isUpdatable("1.2.1", "2.0.0") must beTrue
|
||||
}
|
||||
"return false for not updattable plugin" in {
|
||||
PluginSystem.isUpdatable("1.0.0", "1.0.0") must beFalse
|
||||
PluginSystem.isUpdatable("1.0.1", "1.0.0") must beFalse
|
||||
PluginSystem.isUpdatable("1.1.1", "1.1.0") must beFalse
|
||||
PluginSystem.isUpdatable("2.0.0", "1.2.1") must beFalse
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user