mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-09 23:15:49 +01:00
Implement displaying result as a scrollable table
This commit is contained in:
@@ -44,24 +44,10 @@ abstract class ControllerBase extends ScalatraFilter
|
|||||||
|
|
||||||
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = try {
|
override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = try {
|
||||||
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
val httpRequest = request.asInstanceOf[HttpServletRequest]
|
||||||
val httpResponse = response.asInstanceOf[HttpServletResponse]
|
|
||||||
val context = request.getServletContext.getContextPath
|
val context = request.getServletContext.getContextPath
|
||||||
val path = httpRequest.getRequestURI.substring(context.length)
|
val path = httpRequest.getRequestURI.substring(context.length)
|
||||||
|
|
||||||
if(path.startsWith("/console/")){
|
if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
|
||||||
val account = httpRequest.getSession.getAttribute(Keys.Session.LoginAccount).asInstanceOf[Account]
|
|
||||||
val baseUrl = this.baseUrl(httpRequest)
|
|
||||||
if(account == null){
|
|
||||||
// Redirect to login form
|
|
||||||
httpResponse.sendRedirect(baseUrl + "/signin?redirect=" + StringUtil.urlEncode(path))
|
|
||||||
} else if(account.isAdmin){
|
|
||||||
// H2 Console (administrators only)
|
|
||||||
chain.doFilter(request, response)
|
|
||||||
} else {
|
|
||||||
// Redirect to dashboard
|
|
||||||
httpResponse.sendRedirect(baseUrl + "/")
|
|
||||||
}
|
|
||||||
} else if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
|
|
||||||
// Git repository
|
// Git repository
|
||||||
chain.doFilter(request, response)
|
chain.doFilter(request, response)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import org.apache.commons.io.IOUtils
|
|||||||
import org.scalatra.i18n.Messages
|
import org.scalatra.i18n.Messages
|
||||||
import com.github.zafarkhaja.semver.{Version => Semver}
|
import com.github.zafarkhaja.semver.{Version => Semver}
|
||||||
import gitbucket.core.GitBucketCoreModule
|
import gitbucket.core.GitBucketCoreModule
|
||||||
|
import org.scalatra._
|
||||||
|
import org.json4s.jackson.Serialization
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import scala.collection.mutable.ListBuffer
|
import scala.collection.mutable.ListBuffer
|
||||||
@@ -176,6 +178,42 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
|
|||||||
html.dbviewer(tables)
|
html.dbviewer(tables)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
post("/admin/dbviewer/_query")(adminOnly {
|
||||||
|
contentType = formats("json")
|
||||||
|
params.get("query").collectFirst { case query if query.trim.nonEmpty =>
|
||||||
|
val trimmedQuery = query.trim
|
||||||
|
if(trimmedQuery.nonEmpty){
|
||||||
|
try {
|
||||||
|
val conn = request2Session(request).conn
|
||||||
|
using(conn.prepareStatement(query)){ stmt =>
|
||||||
|
if(trimmedQuery.toUpperCase.startsWith("SELECT")){
|
||||||
|
using(stmt.executeQuery()){ rs =>
|
||||||
|
val meta = rs.getMetaData
|
||||||
|
val columns = for(i <- 1 to meta.getColumnCount) yield {
|
||||||
|
meta.getColumnName(i)
|
||||||
|
}
|
||||||
|
val result = ListBuffer[Map[String, String]]()
|
||||||
|
while(rs.next()){
|
||||||
|
val row = columns.map { columnName =>
|
||||||
|
columnName -> Option(rs.getObject(columnName)).map(_.toString).getOrElse("<NULL>")
|
||||||
|
}.toMap
|
||||||
|
result += row
|
||||||
|
}
|
||||||
|
Ok(Serialization.write(Map("type" -> "query", "columns" -> columns, "rows" -> result)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val rows = stmt.executeUpdate()
|
||||||
|
Ok(Serialization.write(Map("type" -> "update", "rows" -> rows)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
Ok(Serialization.write(Map("type" -> "error", "message" -> e.toString)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} getOrElse Ok(Serialization.write(Map("type" -> "error", "message" -> "query is empty")))
|
||||||
|
})
|
||||||
|
|
||||||
get("/admin/system")(adminOnly {
|
get("/admin/system")(adminOnly {
|
||||||
html.system(flash.get("info"))
|
html.system(flash.get("info"))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class CompositeScalatraFilter extends Filter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
|
if(!checkPath.startsWith("/upload/") && !checkPath.startsWith("/git/") && !checkPath.startsWith("/git-lfs/") &&
|
||||||
!checkPath.startsWith("/plugin-assets/") && !checkPath.startsWith("/console/")){
|
!checkPath.startsWith("/plugin-assets/")){
|
||||||
filters
|
filters
|
||||||
.filter { case (_, path) =>
|
.filter { case (_, path) =>
|
||||||
val start = path.replaceFirst("/\\*$", "/")
|
val start = path.replaceFirst("/\\*$", "/")
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class TransactionFilter extends Filter {
|
|||||||
|
|
||||||
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
|
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
|
||||||
val servletPath = req.asInstanceOf[HttpServletRequest].getServletPath()
|
val servletPath = req.asInstanceOf[HttpServletRequest].getServletPath()
|
||||||
if(servletPath.startsWith("/assets/") || servletPath == "/console" || servletPath == "/git" || servletPath == "/git-lfs"){
|
if(servletPath.startsWith("/assets/") || servletPath == "/git" || servletPath == "/git-lfs"){
|
||||||
// assets and git-lfs don't need transaction
|
// assets and git-lfs don't need transaction
|
||||||
chain.doFilter(req, res)
|
chain.doFilter(req, res)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
@(tables: Seq[gitbucket.core.controller.Table])(implicit context: gitbucket.core.controller.Context)
|
@(tables: Seq[gitbucket.core.controller.Table])(implicit context: gitbucket.core.controller.Context)
|
||||||
|
@import gitbucket.core.view.helpers
|
||||||
@gitbucket.core.html.main("Database viewer") {
|
@gitbucket.core.html.main("Database viewer") {
|
||||||
@gitbucket.core.admin.html.menu("dbviewer") {
|
@gitbucket.core.admin.html.menu("dbviewer") {
|
||||||
<div>
|
<div class="container">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div id="table-tree">
|
<div id="table-tree">
|
||||||
<ul>
|
<ul>
|
||||||
@tables.map { table =>
|
@tables.map { table =>
|
||||||
<li>@table.name
|
<li data-jstree='{"icon":"@context.path/assets/common/images/table.gif"}'><a href="javascript:void(0);" class="table-link">@table.name</a>
|
||||||
<ul>
|
<ul>
|
||||||
@table.columns.map { column =>
|
@table.columns.map { column =>
|
||||||
<li>@column.name</li>
|
<li data-jstree='{"icon":"@context.path/assets/common/images/column.gif"}'>@column.name</li>
|
||||||
}
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -27,19 +28,60 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script src="@gitbucket.core.view.helpers.assets("/vendors/ace/ace.js")" type="text/javascript" charset="utf-8"></script>
|
<script src="@helpers.assets("/vendors/ace/ace.js")" type="text/javascript" charset="utf-8"></script>
|
||||||
<script src="@gitbucket.core.view.helpers.assets("/vendors/vakata-jstree-3.3.4/jstree.min.js")" type="text/javascript" charset="utf-8"></script>
|
<script src="@helpers.assets("/vendors/vakata-jstree-3.3.4/jstree.min.js")" type="text/javascript" charset="utf-8"></script>
|
||||||
<link rel="stylesheet" href="@gitbucket.core.view.helpers.assets("/vendors/vakata-jstree-3.3.4/themes/default/style.min.css")" />
|
<link rel="stylesheet" href="@helpers.assets("/vendors/vakata-jstree-3.3.4/themes/default/style.min.css")" />
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$('#editor').text($('#initial').val());
|
$('#editor').text($('#initial').val());
|
||||||
var editor = ace.edit("editor");
|
var editor = ace.edit("editor");
|
||||||
editor.setTheme("ace/theme/monokai");
|
editor.setTheme("ace/theme/monokai");
|
||||||
|
editor.getSession().setMode("ace/mode/sql");
|
||||||
|
|
||||||
|
|
||||||
$('#table-tree').jstree();
|
$('#table-tree').jstree();
|
||||||
|
|
||||||
|
$('.table-link').click(function(e){
|
||||||
|
var query = editor.getValue();
|
||||||
|
if(query != ''){
|
||||||
|
query = query + '\n';
|
||||||
|
}
|
||||||
|
console.log(e);
|
||||||
|
editor.setValue(query + 'SELECT * FROM ' + $(e.target).text());
|
||||||
|
});
|
||||||
|
|
||||||
$('#run-query').click(function(){
|
$('#run-query').click(function(){
|
||||||
console.log(editor.getValue());
|
console.log(editor.getValue());
|
||||||
|
$.post('@context.path/admin/dbviewer/_query', { query: editor.getValue() }, function(data){
|
||||||
|
if(data.type == "query"){
|
||||||
|
var table = $('<table class="table table-bordered table-hover table-scroll">');
|
||||||
|
|
||||||
|
var header = $('<tr>');
|
||||||
|
$.each(data.columns, function(i, column){
|
||||||
|
header.append($('<th>').text(column));
|
||||||
|
});
|
||||||
|
table.append($('<thead>').append(header));
|
||||||
|
|
||||||
|
var body = $('<tbody>');
|
||||||
|
$.each(data.rows, function(i, rs){
|
||||||
|
var row = $('<tr>');
|
||||||
|
$.each(data.columns, function(i, column){
|
||||||
|
row.append($('<td>').text(rs[column]));
|
||||||
|
});
|
||||||
|
body.append(row);
|
||||||
|
});
|
||||||
|
|
||||||
|
table.append(body);
|
||||||
|
$('#result').empty().append(table);
|
||||||
|
|
||||||
|
} else if(data.type == "update"){
|
||||||
|
$('#result').empty().append($('<span>').text('Updated ' + data.rows + ' rows.'));
|
||||||
|
|
||||||
|
} else if(data.type == "error"){
|
||||||
|
$('#result').empty().append($('<span class="error">').text(data.message));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -70,30 +70,6 @@
|
|||||||
<url-pattern>/plugin-assets/*</url-pattern>
|
<url-pattern>/plugin-assets/*</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
<!-- ===================================================================== -->
|
|
||||||
<!-- H2 console configuration -->
|
|
||||||
<!-- ===================================================================== -->
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>H2Console</servlet-name>
|
|
||||||
<servlet-class>org.h2.server.web.WebServlet</servlet-class>
|
|
||||||
<init-param>
|
|
||||||
<param-name>webAllowOthers</param-name>
|
|
||||||
<param-value></param-value>
|
|
||||||
</init-param>
|
|
||||||
<!--
|
|
||||||
<init-param>
|
|
||||||
<param-name>trace</param-name>
|
|
||||||
<param-value></param-value>
|
|
||||||
</init-param>
|
|
||||||
-->
|
|
||||||
<load-on-startup>1</load-on-startup>
|
|
||||||
</servlet>
|
|
||||||
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>H2Console</servlet-name>
|
|
||||||
<url-pattern>/console/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
<!-- Session timeout -->
|
<!-- Session timeout -->
|
||||||
<!-- ===================================================================== -->
|
<!-- ===================================================================== -->
|
||||||
|
|||||||
@@ -124,6 +124,12 @@ div.content-wrapper {
|
|||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table-scroll {
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
/* Global Header */
|
/* Global Header */
|
||||||
/* ======================================================================== */
|
/* ======================================================================== */
|
||||||
|
|||||||
BIN
src/main/webapp/assets/common/images/column.gif
Normal file
BIN
src/main/webapp/assets/common/images/column.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 317 B |
BIN
src/main/webapp/assets/common/images/table.gif
Normal file
BIN
src/main/webapp/assets/common/images/table.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 343 B |
Reference in New Issue
Block a user