├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ └── build.yml ├── .gitignore ├── .scalafmt.conf ├── LICENSE ├── README.md ├── build.sbt ├── project ├── build.properties └── plugins.sbt └── src └── main ├── resources └── update │ ├── gitbucket-page_1.1.sql │ └── gitbucket-page_1.1.xml ├── scala ├── Plugin.scala └── gitbucket │ └── plugin │ ├── model │ ├── PageProfile.scala │ └── Pages.scala │ ├── pages │ ├── PagesHook.scala │ └── pages.scala │ └── service │ └── PagesService.scala └── twirl └── gitbucket └── pages └── options.scala.html /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @takezoe @xuwei-k 2 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | java: [11] 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Cache 14 | uses: actions/cache@v4 15 | env: 16 | cache-name: cache-sbt-libs 17 | with: 18 | path: | 19 | ~/.ivy2/cache 20 | ~/.sbt 21 | ~/.coursier 22 | key: build-${{ env.cache-name }}-${{ hashFiles('build.sbt') }} 23 | - name: Set up JDK 24 | uses: actions/setup-java@v4 25 | with: 26 | distribution: temurin 27 | java-version: ${{ matrix.java }} 28 | - uses: sbt/setup-sbt@v1 29 | - name: Run tests 30 | run: | 31 | git clone https://github.com/gitbucket/gitbucket.git 32 | cd gitbucket 33 | sbt publishLocal 34 | cd ../ 35 | sbt scalafmtSbtCheck scalafmtCheckAll test 36 | - name: Assembly 37 | run: sbt assembly 38 | - name: Upload artifacts 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: gitbucket-pages-plugin-java${{ matrix.java }}-${{ github.sha }} 42 | path: ./target/scala-2.13/*.jar 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | dist/* 6 | target/ 7 | lib_managed/ 8 | src_managed/ 9 | project/boot/ 10 | project/plugins/project/ 11 | 12 | # Scala-IDE specific 13 | .scala_dependencies 14 | .classpath 15 | .project 16 | .cache 17 | .settings 18 | 19 | # IntelliJ specific 20 | .idea/ 21 | .idea_modules/ 22 | 23 | # Ensime 24 | .ensime 25 | .ensime_cache/ 26 | 27 | # Metals 28 | .bloop/ 29 | .metals/ 30 | .vscode/ 31 | **/metals.sbt 32 | .bsp 33 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = "3.9.7" 2 | project.git = true 3 | 4 | maxColumn = 120 5 | 6 | align.tokens = ["%", "%%", {code = "=>", owner = "Case"}] 7 | align.openParenCallSite = false 8 | align.openParenDefnSite = false 9 | continuationIndent.callSite = 2 10 | continuationIndent.defnSite = 2 11 | danglingParentheses.preset = true 12 | runner.dialect = scala213source3 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015 Yan Su (tsu@yaroot.net) 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Gitbucket-Pages-Plugin [![Gitter](https://img.shields.io/gitter/room/gitbucket/gitbucket.js.svg)](https://gitter.im/gitbucket/gitbucket) [![build](https://github.com/gitbucket/gitbucket-pages-plugin/workflows/build/badge.svg?branch=master)](https://github.com/gitbucket/gitbucket-pages-plugin/actions?query=workflow%3Abuild+branch%3Amaster) 3 | 4 | This plugin provides *Project Pages* functionality for 5 | [GitBucket](https://github.com/gitbucket/gitbucket) based repositories. 6 | 7 | ## User guide 8 | 9 | This plugin serves static files directly from one of the following 10 | places: 11 | 12 | - `gb-pages` branch (with fallback to `gh-pages` to be compatible with 13 | github, this is the default) 14 | - `master` branch 15 | - `docs` folder under `master` branch 16 | 17 | ### Quick start 18 | 19 | - create a directory or branch if necessary (eg. create an orphan branch called `gb-pages`: `git checkout --orphan gb-pages && git rm -f $(git ls-files)`) 20 | - create a static site under this branch. E.g. `echo '

hello, world

' > index.html` to create a simple file. 21 | - commit && push to gitbucket this orphan branch 22 | - open the browser and point to `/pages` 23 | 24 | **Note**: This plugin won't render markdown content. To render markdown content, use the GitBucket Wiki functionality, or use one of the many static site generators (e.g. [jekyll](http://jekyllrb.com/), [hugo](https://gohugo.io/)) 25 | 26 | ## Installation 27 | 28 | **This plugin is bundled with newer version of GitBucket, for older 29 | version please follow the instruction below** 30 | 31 | ### Install manually 32 | 33 | - download from [releases](https://github.com/gitbucket/gitbucket-pages-plugin/releases) 34 | - copy the jar file to `/plugins/` (`GITBUCKET_HOME` defaults to `~/.gitbucket`) 35 | - enable it in plugin settings (you may need to restart gitbucket) 36 | 37 | ## Versions 38 | 39 | | pages version | gitbucket version | 40 | | :---: | :---: | 41 | | 1.10.0 | 4.36.0 | 42 | | 1.9.0 | 4.35.0 | 43 | | 1.8.0 | 4.32.0 | 44 | | 1.7.0 | 4.23.0 | 45 | | 1.6.0 | 4.19.0 | 46 | | 1.5.0 | 4.15.0 | 47 | | 1.3 | 4.14.1 | 48 | | 1.2 | 4.13 | 49 | | 1.1 | 4.11 | 50 | | 1.0 | 4.10 | 51 | | 0.9 | 4.9 | 52 | | 0.8 | 4.6 | 53 | | 0.7 | 4.3 ~ 4.6 | 54 | | 0.6 | 4.2.x | 55 | | 0.5 | 4.0, 4.1 | 56 | | 0.4 | 3.13 | 57 | | 0.3 | 3.12 | 58 | | 0.2 | 3.11 | 59 | | 0.1 | 3.9, 3.10 | 60 | 61 | 62 | ## Security (panic mode) 63 | 64 | To prevent XSS, one must use two different domains to host the pages and 65 | Gitbucket itself. Below is a working example of nginx configuration to achieve that. 66 | 67 | ``` 68 | server { 69 | listen 80; 70 | server_name git.local; 71 | 72 | location ~ ^/([^/]+)/([^/]+)/pages/(.*)$ { 73 | rewrite ^/([^/]+)/([^/]+)/pages/(.*)$ http://doc.local/$1/$2/pages/$3 redirect; 74 | } 75 | 76 | location / { 77 | proxy_pass http://127.0.0.1:8080; 78 | } 79 | } 80 | 81 | server { 82 | listen 80; 83 | server_name doc.local; 84 | 85 | location ~ ^/([^/]+)/([^/]+)/pages/(.*)$ { 86 | proxy_pass http://127.0.0.1:8080; 87 | } 88 | 89 | location / { 90 | return 403; 91 | } 92 | } 93 | ``` 94 | 95 | ## CI 96 | 97 | - build by [GitHub Actions](https://github.com/gitbucket/gitbucket-pages-plugin/actions) 98 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | organization := "gitbucket" 2 | name := "gitbucket-pages-plugin" 3 | scalaVersion := "2.13.16" 4 | version := "1.10.0" 5 | gitbucketVersion := "4.42.1" 6 | scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8", "-feature") 7 | 8 | scalafmtOnCompile := true 9 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.2 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4") 2 | addSbtPlugin("io.github.gitbucket" % "sbt-gitbucket-plugin" % "1.6.0") 3 | -------------------------------------------------------------------------------- /src/main/resources/update/gitbucket-page_1.1.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO PAGES 2 | (USER_NAME, REPOSITORY_NAME, SOURCE) 3 | SELECT 4 | A.USER_NAME 5 | , A.REPOSITORY_NAME 6 | , 'gh-pages' 7 | FROM 8 | REPOSITORY A; 9 | -------------------------------------------------------------------------------- /src/main/resources/update/gitbucket-page_1.1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/scala/Plugin.scala: -------------------------------------------------------------------------------- 1 | import gitbucket.core.controller.Context 2 | import gitbucket.core.controller.ControllerBase 3 | import gitbucket.core.plugin.Link 4 | import gitbucket.core.service.RepositoryService.RepositoryInfo 5 | import gitbucket.plugin.pages.{PagesController, PagesHook} 6 | import io.github.gitbucket.solidbase.migration.{SqlMigration, LiquibaseMigration} 7 | import io.github.gitbucket.solidbase.model.Version 8 | 9 | class Plugin extends gitbucket.core.plugin.Plugin { 10 | override val pluginId = "pages" 11 | override val pluginName = "Pages Plugin" 12 | override val description = "Project pages for GitBucket" 13 | override val versions = List( 14 | new Version("0.1"), 15 | new Version("0.2"), 16 | new Version("0.3"), 17 | new Version("0.4"), 18 | new Version("0.5"), 19 | new Version("0.6"), 20 | new Version("0.7"), 21 | new Version("0.8"), 22 | new Version("0.9"), 23 | new Version("1.0"), 24 | new Version( 25 | "1.1", 26 | new LiquibaseMigration("update/gitbucket-page_1.1.xml"), 27 | new SqlMigration("update/gitbucket-page_1.1.sql") 28 | ), 29 | new Version("1.2"), 30 | new Version("1.3"), 31 | new Version("1.4.0"), 32 | new Version("1.5.0"), 33 | new Version("1.6.0"), 34 | new Version("1.7.0"), 35 | new Version("1.7.1"), 36 | new Version("1.8.0"), 37 | new Version("1.9.0"), 38 | new Version("1.10.0") 39 | ) 40 | 41 | override val controllers: Seq[(String, ControllerBase)] = Seq("/*" -> new PagesController) 42 | 43 | override val repositorySettingTabs = 44 | Seq((repository: RepositoryInfo, context: Context) => Some(Link("pages", "Pages", s"settings/pages"))) 45 | 46 | override val repositoryHooks = Seq(new PagesHook) 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/gitbucket/plugin/model/PageProfile.scala: -------------------------------------------------------------------------------- 1 | package gitbucket.plugin.model 2 | 3 | import gitbucket.core.model._ 4 | 5 | object Profile extends CoreProfile with PagesComponent 6 | -------------------------------------------------------------------------------- /src/main/scala/gitbucket/plugin/model/Pages.scala: -------------------------------------------------------------------------------- 1 | package gitbucket.plugin.model 2 | 3 | trait PagesComponent { self: gitbucket.core.model.Profile => 4 | import profile.api._ 5 | 6 | implicit val psColumnType = 7 | MappedColumnType.base[PageSourceType, String](ps => ps.code, code => PageSourceType.valueOf(code)) 8 | 9 | lazy val Pages = TableQuery[Pages] 10 | 11 | class Pages(tag: Tag) extends Table[Page](tag, "PAGES") { 12 | val userName = column[String]("USER_NAME") 13 | val repositoryName = column[String]("REPOSITORY_NAME") 14 | val source = column[PageSourceType]("SOURCE") 15 | def * = (userName, repositoryName, source).<>((Page.apply _).tupled, Page.unapply) 16 | } 17 | } 18 | 19 | abstract sealed case class PageSourceType(code: String) 20 | 21 | object PageSourceType { 22 | object NONE extends PageSourceType("none") 23 | object MASTER_DOCS extends PageSourceType("master /docs") 24 | object MASTER extends PageSourceType("master") 25 | object GH_PAGES extends PageSourceType("gh-pages") 26 | 27 | val values: Vector[PageSourceType] = Vector(NONE, MASTER_DOCS, MASTER, GH_PAGES) 28 | 29 | private val map: Map[String, PageSourceType] = values.map(enum => enum.code -> enum).toMap 30 | 31 | def apply(code: String): PageSourceType = map(code) 32 | 33 | def valueOf(code: String): PageSourceType = map(code) 34 | def valueOpt(code: String): Option[PageSourceType] = map.get(code) 35 | } 36 | 37 | case class Page(userName: String, repositoryName: String, source: PageSourceType) 38 | -------------------------------------------------------------------------------- /src/main/scala/gitbucket/plugin/pages/PagesHook.scala: -------------------------------------------------------------------------------- 1 | package gitbucket.plugin.pages 2 | 3 | import gitbucket.core.plugin.RepositoryHook 4 | import gitbucket.plugin.model.PageSourceType 5 | import gitbucket.plugin.model.Profile.profile.blockingApi._ 6 | import gitbucket.plugin.service.PagesService 7 | 8 | class PagesHook extends PagesHookBase with PagesService 9 | 10 | trait PagesHookBase extends RepositoryHook { 11 | self: PagesService => 12 | 13 | override def created(owner: String, repository: String)(implicit session: Session): Unit = 14 | registerPageOptions(owner, repository, PageSourceType.GH_PAGES) 15 | 16 | override def deleted(owner: String, repository: String)(implicit session: Session): Unit = 17 | deletePageOptions(owner, repository) 18 | 19 | override def renamed(owner: String, oldRepository: String, newRepository: String)(implicit session: Session): Unit = 20 | renameRepository(owner, oldRepository, newRepository) 21 | 22 | override def transferred(oldOwner: String, newOwner: String, repository: String)(implicit session: Session): Unit = 23 | renameUserName(oldOwner, newOwner, repository) 24 | 25 | override def forked(owner: String, newOwner: String, repository: String)(implicit session: Session): Unit = { 26 | val source = getPageSource(owner, repository) 27 | registerPageOptions(newOwner, repository, source) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/scala/gitbucket/plugin/pages/pages.scala: -------------------------------------------------------------------------------- 1 | package gitbucket.plugin.pages 2 | 3 | import gitbucket.core.controller.ControllerBase 4 | import gitbucket.core.service.RepositoryService.RepositoryInfo 5 | import gitbucket.core.service.{AccountService, RepositoryService} 6 | import gitbucket.core.util.Implicits._ 7 | import gitbucket.core.util.{Directory, JGitUtil, OwnerAuthenticator, ReferrerAuthenticator} 8 | import gitbucket.pages.html 9 | import gitbucket.plugin.model.PageSourceType 10 | import gitbucket.plugin.service.PagesService 11 | import org.scalatra.forms._ 12 | import org.eclipse.jgit.api.Git 13 | import org.eclipse.jgit.lib.ObjectId 14 | import org.eclipse.jgit.revwalk.RevCommit 15 | import org.scalatra.i18n.Messages 16 | 17 | import javax.servlet.http.HttpServletRequest 18 | import scala.util.Using 19 | import scala.annotation.tailrec 20 | import scala.language.implicitConversions 21 | 22 | class PagesController 23 | extends PagesControllerBase 24 | with AccountService 25 | with OwnerAuthenticator 26 | with PagesService 27 | with RepositoryService 28 | with ReferrerAuthenticator 29 | 30 | trait PagesControllerBase extends ControllerBase { 31 | self: AccountService with RepositoryService with PagesService with ReferrerAuthenticator with OwnerAuthenticator => 32 | import PagesControllerBase._ 33 | 34 | val optionsForm: MappingValueType[OptionsForm] = mapping( 35 | "source" -> trim(label("Pages Source", text(required, pagesOption))) 36 | )(source => OptionsForm(PageSourceType.valueOf(source))) 37 | 38 | val PAGES_BRANCHES = List("gb-pages", "gh-pages") 39 | 40 | def endsWithSlash(): Boolean = request.getServletPath.endsWith("/") 41 | 42 | get("/:owner/:repository/pages/*")(referrersOnly { repository => 43 | renderPage(repository, params("splat")) 44 | }) 45 | 46 | get("/:owner/:repository/pages")(referrersOnly { repository => 47 | if (endsWithSlash()) { 48 | renderPage(repository, "") 49 | } else { 50 | redirect(s"/${repository.owner}/${repository.name}/pages/") 51 | } 52 | }) 53 | 54 | private def renderPage(repository: RepositoryInfo, path: String) = { 55 | val defaultBranch = repository.repository.defaultBranch 56 | Using.resource(Git.open(Directory.getRepositoryDir(repository.owner, repository.name))) { git => 57 | getPageSource(repository.owner, repository.name) match { 58 | case PageSourceType.GH_PAGES => 59 | renderFromBranch(repository, git, path, PAGES_BRANCHES.collectFirst(Function.unlift(resolveBranch(git, _)))) 60 | case PageSourceType.MASTER => 61 | renderFromBranch(repository, git, path, resolveBranch(git, defaultBranch)) 62 | case PageSourceType.MASTER_DOCS => 63 | renderFromBranch(repository, git, joinPath("docs", path), resolveBranch(git, defaultBranch)) 64 | case PageSourceType.NONE => 65 | NotFound() 66 | } 67 | } 68 | } 69 | 70 | get("/:owner/:repository/settings/pages")(ownerOnly { repository => 71 | val source = getPageSource(repository.owner, repository.name) 72 | val defaultBranch = repository.repository.defaultBranch 73 | html.options(repository, source, defaultBranch, flash.get("info")) 74 | }) 75 | 76 | post("/:owner/:repository/settings/pages", optionsForm)(ownerOnly { (form, repository) => 77 | updatePageOptions(repository.owner, repository.name, form.source) 78 | flash.update("info", "Pages source saved") 79 | redirect(s"/${repository.owner}/${repository.name}/settings/pages") 80 | }) 81 | 82 | def renderPageObject(git: Git, path: String, obj: ObjectId): Unit = { 83 | JGitUtil.getObjectLoaderFromId(git, obj) { loader => 84 | contentType = guessContentType(path) 85 | response.setContentLength(loader.getSize.toInt) 86 | loader.copyTo(response.getOutputStream) 87 | } 88 | } 89 | 90 | def renderFromBranch( 91 | repository: RepositoryService.RepositoryInfo, 92 | git: Git, 93 | path: String, 94 | branchObject: Option[ObjectId] 95 | ): Any = { 96 | val pagePair = branchObject 97 | .map(JGitUtil.getRevCommitFromId(git, _)) 98 | .flatMap(getPageObjectId(git, path, _)) 99 | 100 | pagePair match { 101 | case Some((realPath, _)) if shouldRedirect(path, realPath) => 102 | redirect(s"/${repository.owner}/${repository.name}/pages/$path/") 103 | case Some((realPath, pageObject)) => 104 | renderPageObject(git, realPath, pageObject) 105 | case None => 106 | NotFound() 107 | } 108 | } 109 | 110 | def resolveBranch(git: Git, name: String): Option[ObjectId] = Option(git.getRepository.resolve(name)) 111 | 112 | // redirect [owner/repo/pages/path] -> [owner/repo/pages/path/] 113 | def shouldRedirect(path: String, path0: String): Boolean = 114 | !isRoot(path) && path0 != path && path0.startsWith(path) && !endsWithSlash() 115 | 116 | def getPageObjectId(git: Git, path: String, revCommit: RevCommit): Option[(String, ObjectId)] = { 117 | listProbablePages(path).collectFirst(Function.unlift(getPathObjectIdPair(git, _, revCommit))) 118 | } 119 | 120 | def listProbablePages(path: String): List[String] = { 121 | path :: List("index.html", "index.htm").map(joinPath(path, _)) 122 | } 123 | 124 | def getPathObjectIdPair(git: Git, path: String, revCommit: RevCommit): Option[(String, ObjectId)] = { 125 | getPathObjectId(git, path, revCommit).map(path -> _) 126 | } 127 | 128 | def joinPath(base: String, suffix: String): String = { 129 | val sfx = suffix.stripPrefix("/") 130 | if (isRoot(base)) sfx 131 | else base.stripSuffix("/") + "/" + sfx 132 | } 133 | 134 | def isRoot(path: String): Boolean = path == "" 135 | 136 | def guessContentType(path: String): String = { 137 | Option(servletContext.getMimeType(path)).getOrElse("application/octet-stream") 138 | } 139 | 140 | } 141 | 142 | object PagesControllerBase { 143 | case class OptionsForm(source: PageSourceType) 144 | 145 | def pagesOption: Constraint = new Constraint() { 146 | override def validate(name: String, value: String, messages: Messages): Option[String] = 147 | PageSourceType.valueOpt(value) match { 148 | case Some(_) => None 149 | case None => Some("Pages source is invalid.") 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/scala/gitbucket/plugin/service/PagesService.scala: -------------------------------------------------------------------------------- 1 | package gitbucket.plugin.service 2 | 3 | import gitbucket.plugin.model.{Page, PageSourceType} 4 | import gitbucket.plugin.model.Profile._ 5 | import gitbucket.plugin.model.Profile.profile.blockingApi._ 6 | 7 | trait PagesService { 8 | 9 | def getPageSource(userName: String, repositoryName: String)(implicit s: Session): PageSourceType = 10 | getPageOptions(userName, repositoryName).map(_.source).getOrElse(PageSourceType.GH_PAGES) 11 | 12 | def getPageOptions(userName: String, repositoryName: String)(implicit s: Session): Option[Page] = 13 | Pages.filter(t => (t.userName === userName.bind) && (t.repositoryName === repositoryName.bind)).firstOption 14 | 15 | def registerPageOptions(userName: String, repositoryName: String, source: PageSourceType)(implicit s: Session): Unit = 16 | Pages.insert(Page(userName, repositoryName, source)) 17 | 18 | def updatePageOptions(userName: String, repositoryName: String, source: PageSourceType)(implicit s: Session): Unit = 19 | Pages 20 | .filter(t => (t.userName === userName.bind) && (t.repositoryName === repositoryName.bind)) 21 | .map(t => t.source) 22 | .update(source) 23 | 24 | def renameRepository(userName: String, oldRepositoryName: String, newRepositoryName: String)(implicit 25 | s: Session 26 | ): Unit = 27 | Pages 28 | .filter(t => (t.userName === userName.bind) && (t.repositoryName === oldRepositoryName.bind)) 29 | .map(t => t.repositoryName) 30 | .update(newRepositoryName) 31 | 32 | def renameUserName(oldUserName: String, newUserName: String, repositoryName: String)(implicit s: Session): Unit = 33 | Pages 34 | .filter(t => (t.userName === oldUserName.bind) && (t.repositoryName === repositoryName.bind)) 35 | .map(t => t.userName) 36 | .update(newUserName) 37 | 38 | def deletePageOptions(userName: String, repositoryName: String)(implicit s: Session): Unit = { 39 | Pages.filter(t => (t.userName === userName.bind) && (t.repositoryName === repositoryName.bind)).delete 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/twirl/gitbucket/pages/options.scala.html: -------------------------------------------------------------------------------- 1 | @import gitbucket.plugin.model.PageSourceType 2 | @(repository: gitbucket.core.service.RepositoryService.RepositoryInfo, 3 | source: PageSourceType, 4 | defaultBranch: String, 5 | info: Option[Any])(implicit context: gitbucket.core.controller.Context) 6 | @import gitbucket.core.view.helpers 7 | @gitbucket.core.html.main("Pages", Some(repository)){ 8 | @gitbucket.core.html.menu("settings", repository){ 9 | @gitbucket.core.settings.html.menu("pages", repository){ 10 | @gitbucket.core.helper.html.information(info) 11 |
12 |
13 |
Source
14 |
15 |
16 |
17 | 24 |
25 |
26 | 33 |
34 |
35 | 42 |
43 |
44 | 51 |
52 |
53 |
54 |
55 |
56 | 57 |
58 |
59 | } 60 | } 61 | } 62 | --------------------------------------------------------------------------------