gitbucket-drawio-plugin からセルフホストの draw.io にアクセスさせたい
前回の記事
クローズド環境で使用したら draw.io 本家にアクセスしようとしていた
ある程度、予想はついていたのだが、viewer.min.js
から、draw.io 本家の複数のスクリプトへアクセスしようとするため、クローズド環境では、一時的にブロッキングが発生する。実際にアクセスしようとしていた URL は以下の通り。
- https://app.diagrams.net/math/es5/startup.js
- https://app.diagrams.net/math/es5/core.js
- https://app.diagrams.net/math/es5/output/svg.js
- https://app.diagrams.net/math/es5/input/tex.js
- https://app.diagrams.net/math/es5/input/asciimath.js
- https://app.diagrams.net/math/es5/ui/safe.js
- https://app.diagrams.net/math/es5/output/svg/fonts/tex.js
修正の参考にしたプラグイン
設定を保存できるサービスの作成
gitbucket-commitgraphs-plugin/src/main/scala/me/huzi/gitbucket/commitgraphs/service/CommitGraphsSettingsService.scalaを参考にgitbucket-drawio-plugin/src/main/scala/DrawioSettingsService.scalaを作成。
import java.io.File
import scala.util.Using
import gitbucket.core.util.Directory._
import gitbucket.core.util.SyntaxSugars._
import DrawioSettingsService._
trait DrawioSettingsService {
val DrawioConf = new File(GitBucketHome, "drawio.conf")
def saveDrawioSettings(settings: DrawioSettings): Unit =
defining(new java.util.Properties()) { props =>
props.setProperty(DrawioBaseUrl, settings.DrawioBaseUrl)
Using.resource(new java.io.FileOutputStream(DrawioConf)) { out =>
props.store(out, null)
}
}
def loadDrawioSettings(): DrawioSettings =
defining(new java.util.Properties()) { props =>
if (DrawioConf.exists) {
Using.resource(new java.io.FileInputStream(DrawioConf)) { in =>
props.load(in)
}
}
DrawioSettings(
getValue[String](props, DrawioBaseUrl, "")
)
}
}
object DrawioSettingsService {
import scala.reflect.ClassTag
case class DrawioSettings(DrawioBaseUrl: String)
private val DrawioBaseUrl = "DrawioBaseUrl"
private def getValue[A: ClassTag](props: java.util.Properties,
key: String,
default: A): A =
defining(props.getProperty(key)) { value =>
if (value == null || value.isEmpty) default
else convertType(value).asInstanceOf[A]
}
private def getOptionValue[A: ClassTag](props: java.util.Properties,
key: String,
default: Option[A]): Option[A] =
defining(props.getProperty(key)) { value =>
if (value == null || value.isEmpty) default
else Some(convertType(value)).asInstanceOf[Option[A]]
}
private def convertType[A: ClassTag](value: String) =
defining(implicitly[ClassTag[A]].runtimeClass) { c =>
if (c == classOf[Boolean]) value.toBoolean
else if (c == classOf[Int]) value.toInt
else value
}
}
GitBucket のホームディレクトリに drawio.conf
というファイルを作成し、以下のように DrawioBaseUrl
という設定値を保存する。
#Sat Jul 26 21:09:46 JST 2025
DrawioBaseUrl=http\://localhost\:8080/draw/
trait は Java の interface に似てるらしい。同じ名前のオブジェクトが定義されているのは、コンパニオンオブジェクトに当たると思われる。
設定画面用の twirl テンプレートの作成
上図のような設定画面を表示するための twirl テンプレートを作成する。
twirl を使用できるようにするため、gitbucket-drawio-plugin/project/plugins.sbtに以下の行を追加する。
addSbtPlugin("org.playframework.twirl" % "sbt-twirl" % "2.0.9")
LATEST
だとプラグインを取得できなかったので少し古いバージョンを指定した。
また、gitbucket-drawio-plugin/build.sbtに以下の行を挿入した。
lazy val root = (project in file(".")).enablePlugins(SbtTwirl)
テンプレートとして、gitbucket-commitgraphs-plugin/src/main/twirl/me/huzi/gitbucket/commitgraphs/settings.scala.htmlを参考にgitbucket-drawio-plugin/src/main/twirl/settings.scala.htmを作成した。
@(DrawioBaseUrl: String, info: Option[Any])(implicit context: gitbucket.core.controller.Context)
@import context._
@import gitbucket.core.html.main
@import gitbucket.core.admin.html.menu
@import gitbucket.core.helper.html.information
@import gitbucket.core.view.helpers._
@import gitbucket.core.util.Directory._
@main("draw.io"){
@menu("draw.io"){
@information(info)
<form action="@path/admin/drawio" method="POST" validate="true">
<div class="panel panel-default">
<div class="panel-heading strong">draw.io Settings</div>
<div class="panel-body">
<fieldset>
<label><span class="strong">draw.io base url</span></label>
<input type="text" style="width: 635px;" name="DrawioBaseUrl" value="@DrawioBaseUrl"/>
<p class="muted">e.g. https://app.diagrams.net/</p>
</fieldset>
</div>
</div>
<fieldset>
<input type="submit" class="btn btn-success" value="Apply changes"/>
</fieldset>
</form>
}
}
冒頭の @(DrawioBaseUrl: String, info: Option[Any])
で外部から、DrawioBaseUrl
と必要により、情報を与えられるようにしている。
作成した settings.scala.htm
には、Scala ソースから、html.settings
でアクセスできるようである。
設定画面を表示するコントローラーの作成
gitbucket-commitgraphs-plugin/src/main/scala/me/huzi/gitbucket/commitgraphs/controller/CommitGraphsController.scalaを参考にgitbucket-drawio-plugin/src/main/scala/DrawioController.scalaを作成した。
import scala.jdk.CollectionConverters._
import scala.sys.process._
import scala.util.Using
import org.eclipse.jgit.api.Git
import gitbucket.core.controller._
import gitbucket.core.service._
import gitbucket.core.util._
import gitbucket.core.util.AdminAuthenticator
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.util.Directory._
import gitbucket.core.util.JGitUtil._
import org.scalatra.forms._
import DrawioSettingsService._
class DrawioController extends DrawioControllerBase
with RepositoryService with AccountService
with ReferrerAuthenticator with AdminAuthenticator
with DrawioSettingsService
trait DrawioControllerBase extends ControllerBase {
self: RepositoryService with AccountService with ReferrerAuthenticator with AdminAuthenticator with DrawioSettingsService =>
val settingsForm: MappingValueType[DrawioSettings] = mapping(
"DrawioBaseUrl" -> text(required, maxlength(200))
)(DrawioSettings.apply)
get("/admin/drawio")(adminOnly {
val settings = loadDrawioSettings()
html.settings(settings.DrawioBaseUrl, None)
}
)
post("/admin/drawio", settingsForm)(adminOnly { form =>
assert(form.DrawioBaseUrl != null)
saveDrawioSettings(form)
html.settings(form.DrawioBaseUrl, Some("Settings Saved"))
})
}
get("/admin/drawio")
で設定画面を表示する処理、post("/admin/drawio", settingsForm)
でApply changes
が押された際の処理を定義している。
System administration のサイドバーに設定画面へのメニューを追加
gitbucket-commitgraphs-plugin/src/main/scala/Plugin.scalaを参考にgitbucket-drawio-plugin/src/main/scala/Plugin.scalaを修正する。
まず、gitbucket.core.plugin.Plugin
から派生させた Plugin
クラスで controllers
を override し前項で作成したコントローラーを追加する。
override val controllers = Seq(
"/*" -> new DrawioController
)
そして、サイドバーにメニューを追加するため、 systemSettingMenus
を override する。
override val systemSettingMenus: Seq[(Context) => Option[Link]] = Seq(
(ctx: Context) => Some(Link("draw.io","draw.io","admin/drawio"))
)
これで下図のように draw.io
というメニューが追加される。
renderer の修正
gitbucket-drawio-plugin/src/main/scala/DrawioRenderer.scala で設定に応じたレンダリングができるように修正した。
def toHtml(content: String)(implicit context: Context): String = {
val path = context.baseUrl
val settings = loadDrawioSettings()
var viewer = "";
var baseUrl = "https://viewer.diagrams.net/"
if (settings.DrawioBaseUrl != "") {
viewer = settings.DrawioBaseUrl + "/js/viewer.min.js"
baseUrl = settings.DrawioBaseUrl
} else {
viewer = path + "/plugin-assets/drawio/viewer.min.js"
}
val data = content.replaceAll("&", "&")
.replaceAll("'", "'")
.replaceAll("`", "`")
.replaceAll("\"", """)
.replaceAll("<", "<")
.replaceAll(">", ">")
s"""
|<link rel="stylesheet" type="text/css" href="$path/plugin-assets/drawio/drawio-renderer.css">
|<script>
|window.DRAWIO_BASE_URL = '$baseUrl';
|window.DRAW_MATH_URL = '$baseUrl' + 'math/es5';
|</script>
|<script src="$viewer"></script>
|<script src="$path/plugin-assets/drawio/drawio-renderer.js"></script>
|<div class="mxgraph" data-diagram-data="$data"></div>
|""".stripMargin
}
DrawioBaseUrl
の設定に応じて、viewer.min.js
のパスや window.DRAWIO_BASE_URL
と window.DRAW_MATH_URL
が変更されるように修正した。
今のところ、window.DRAWIO_LIGHTBOX_URL
は変更していない。
今回の成果物
Release Added support for connecting to self-hosted draw.io · yasumichi/gitbucket-drawio-plugin にビルド済みのプラグインを添付した。(https://github.com/yasumichi/gitbucket-drawio-plugin/releases/download/0.2.0/gitbucket-drawio-plugin-0.2.0.jar)
本家への Pull Request
Added support for connecting to self-hosted draw.io by yasumichi · Pull Request #1 · onukura/gitbucket-drawio-pluginで本家へ Pull Request を送った。
今後の希望
-
DrawioBaseUrl
のチェックが甘いので修正する。- URL として妥当か?
- 最後に
/
がない場合、補完するようにする。
- 可能であれば
- 埋め込みエディタで編集できるようにしたい。
- markdown に埋め込まれた drawio を表示したい。
Discussion