mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-01-25 00:39:20 +01:00
Add uninstall plugin button
This commit is contained in:
@@ -190,6 +190,15 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
post("/admin/plugins/:pluginId/_uninstall")(adminOnly {
|
||||
val pluginId = params("pluginId")
|
||||
PluginRegistry().getPlugins().find(_.pluginId == pluginId).foreach { plugin =>
|
||||
PluginRegistry.uninstall(pluginId, request.getServletContext, loadSystemSettings(), request2Session(request).conn)
|
||||
flash += "info" -> s"${pluginId} was uninstalled."
|
||||
}
|
||||
redirect("/admin/plugins")
|
||||
})
|
||||
|
||||
get("/admin/users")(adminOnly {
|
||||
val includeRemoved = params.get("includeRemoved").map(_.toBoolean).getOrElse(false)
|
||||
val users = getAllUsers(includeRemoved)
|
||||
|
||||
@@ -160,7 +160,7 @@ object PluginRegistry {
|
||||
|
||||
private var instance = new PluginRegistry()
|
||||
|
||||
private var watcher: PluginWatchThread = null
|
||||
// private var watcher: PluginWatchThread = null
|
||||
|
||||
/**
|
||||
* Returns the PluginRegistry singleton instance.
|
||||
@@ -176,6 +176,19 @@ object PluginRegistry {
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall a specified plugin.
|
||||
*/
|
||||
def uninstall(pluginId: String, context: ServletContext, settings: SystemSettings, conn: java.sql.Connection): Unit = synchronized {
|
||||
instance.getPlugins().find(_.pluginId == pluginId).foreach { plugin =>
|
||||
shutdown(context, settings)
|
||||
// TODO kick uninstall action here?
|
||||
plugin.pluginJar.delete()
|
||||
instance = new PluginRegistry()
|
||||
initialize(context, settings, conn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all installed plugins.
|
||||
*/
|
||||
@@ -209,7 +222,9 @@ object PluginRegistry {
|
||||
pluginName = plugin.pluginName,
|
||||
pluginVersion = plugin.versions.last.getVersion,
|
||||
description = plugin.description,
|
||||
pluginClass = plugin
|
||||
pluginClass = plugin,
|
||||
pluginJar = pluginJar,
|
||||
classLoader = classLoader
|
||||
))
|
||||
|
||||
} catch {
|
||||
@@ -220,10 +235,10 @@ object PluginRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
if(watcher == null){
|
||||
watcher = new PluginWatchThread(context)
|
||||
watcher.start()
|
||||
}
|
||||
// if(watcher == null){
|
||||
// watcher = new PluginWatchThread(context)
|
||||
// watcher.start()
|
||||
// }
|
||||
}
|
||||
|
||||
def shutdown(context: ServletContext, settings: SystemSettings): Unit = synchronized {
|
||||
@@ -234,6 +249,8 @@ object PluginRegistry {
|
||||
case e: Exception => {
|
||||
logger.error(s"Error during plugin shutdown", e)
|
||||
}
|
||||
} finally {
|
||||
pluginInfo.classLoader.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,47 +264,50 @@ case class PluginInfo(
|
||||
pluginName: String,
|
||||
pluginVersion: String,
|
||||
description: String,
|
||||
pluginClass: Plugin
|
||||
pluginClass: Plugin,
|
||||
pluginJar: File,
|
||||
classLoader: URLClassLoader
|
||||
)
|
||||
|
||||
class PluginWatchThread(context: ServletContext) extends Thread with SystemSettingsService {
|
||||
import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
||||
|
||||
override def run(): Unit = {
|
||||
val path = Paths.get(PluginHome)
|
||||
val fs = path.getFileSystem
|
||||
val watcher = fs.newWatchService
|
||||
|
||||
val watchKey = path.register(watcher,
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.OVERFLOW)
|
||||
|
||||
logger.info("Start PluginWatchThread: " + path)
|
||||
|
||||
try {
|
||||
while (watchKey.isValid()) {
|
||||
val detectedWatchKey = watcher.take()
|
||||
if(detectedWatchKey != null){
|
||||
val events = detectedWatchKey.pollEvents()
|
||||
events.forEach { event =>
|
||||
logger.info(event.kind + ": " + event.context)
|
||||
}
|
||||
gitbucket.core.servlet.Database() withTransaction { session =>
|
||||
logger.info("Reloading plugins...")
|
||||
PluginRegistry.reload(context, loadSystemSettings(), session.conn)
|
||||
}
|
||||
}
|
||||
detectedWatchKey.reset()
|
||||
}
|
||||
} catch {
|
||||
case _: InterruptedException => ()
|
||||
}
|
||||
|
||||
logger.info("Shutdown PluginWatchThread")
|
||||
}
|
||||
|
||||
}
|
||||
//class PluginWatchThread(context: ServletContext) extends Thread with SystemSettingsService {
|
||||
// import gitbucket.core.model.Profile.profile.blockingApi._
|
||||
//
|
||||
// private val logger = LoggerFactory.getLogger(classOf[PluginWatchThread])
|
||||
//
|
||||
// override def run(): Unit = {
|
||||
// val path = Paths.get(PluginHome)
|
||||
// val fs = path.getFileSystem
|
||||
// val watcher = fs.newWatchService
|
||||
//
|
||||
// val watchKey = path.register(watcher,
|
||||
// StandardWatchEventKinds.ENTRY_CREATE,
|
||||
// StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
// StandardWatchEventKinds.ENTRY_DELETE,
|
||||
// StandardWatchEventKinds.OVERFLOW)
|
||||
//
|
||||
// logger.info("Start PluginWatchThread: " + path)
|
||||
//
|
||||
// try {
|
||||
// while (watchKey.isValid()) {
|
||||
// val detectedWatchKey = watcher.take()
|
||||
// val events = detectedWatchKey.pollEvents()
|
||||
//
|
||||
// events.forEach { event =>
|
||||
// logger.info(event.kind + ": " + event.context)
|
||||
// }
|
||||
//
|
||||
// gitbucket.core.servlet.Database() withTransaction { session =>
|
||||
// logger.info("Reloading plugins...")
|
||||
// PluginRegistry.reload(context, loadSystemSettings(), session.conn)
|
||||
// }
|
||||
//
|
||||
// detectedWatchKey.reset()
|
||||
// }
|
||||
// } catch {
|
||||
// case _: InterruptedException => watchKey.cancel()
|
||||
// }
|
||||
//
|
||||
// logger.info("Shutdown PluginWatchThread")
|
||||
// }
|
||||
//
|
||||
//}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
@gitbucket.core.html.main("Plugins"){
|
||||
@gitbucket.core.admin.html.menu("plugins") {
|
||||
@gitbucket.core.helper.html.information(info)
|
||||
<h1>Installed plugins</h1>
|
||||
<form action="@context.path/admin/plugins/_reload" method="POST" class="pull-right">
|
||||
<input type="submit" value="Reload plugins" class="btn btn-success">
|
||||
</form>
|
||||
<h1>Installed plugins</h1>
|
||||
@if(plugins.size > 0) {
|
||||
<ul>
|
||||
@plugins.map { plugin =>
|
||||
@@ -15,7 +15,12 @@
|
||||
|
||||
@plugins.map { plugin =>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading strong" id="@plugin.pluginId">@plugin.pluginName</div>
|
||||
<div class="panel-heading strong" id="@plugin.pluginId">
|
||||
<form action="@{context.path}/admin/plugins/@{plugin.pluginId}/_uninstall" method="POST" class="pull-right uninstall-form">
|
||||
<input type="submit" value="Uninstall" class="btn btn-danger btn-sm" style="position: relative; top: -5px; left: 10px;" data-name="@plugin.pluginName">
|
||||
</form>
|
||||
@plugin.pluginName
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<label class="col-md-2">Id</label>
|
||||
@@ -41,3 +46,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
<script>
|
||||
$(function(){
|
||||
$('.uninstall-form').click(function(e){
|
||||
var name = $(e.target).data('name');
|
||||
return confirm('Uninstall ' + name + '. Are you sure?');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user