🦔

gitbucket-drawio-plugin からセルフホストの draw.io にアクセスさせたい

に公開

前回の記事

https://zenn.dev/yasumichi/articles/04d09995388959

クローズド環境で使用したら draw.io 本家にアクセスしようとしていた

ある程度、予想はついていたのだが、viewer.min.js から、draw.io 本家の複数のスクリプトへアクセスしようとするため、クローズド環境では、一時的にブロッキングが発生する。実際にアクセスしようとしていた URL は以下の通り。

修正の参考にしたプラグイン

gitbucket-commitgraphs-plugin

設定を保存できるサービスの作成

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("&", "&amp;")
                      .replaceAll("'", "&#x27;")
                      .replaceAll("`", "&#x60;")
                      .replaceAll("\"", "&quot;")
                      .replaceAll("<", "&lt;")
                      .replaceAll(">", "&gt;")

    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_URLwindow.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 を表示したい。
GitHubで編集を提案

Discussion