Add plugin updating capability

This commit is contained in:
Naoki Takezoe
2014-06-29 14:26:33 +09:00
parent 0e1d184715
commit 0b3781ec8a
5 changed files with 110 additions and 34 deletions

View File

@@ -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
}
}

View File

@@ -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 }

View File

@@ -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>
}
}

View File

@@ -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?');
});

View 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
}
}
}