mirror of
https://github.com/gitbucket/gitbucket.git
synced 2026-01-06 15:42:20 +01:00
@@ -57,7 +57,7 @@ abstract class ControllerBase extends ScalatraFilter
|
||||
// Redirect to dashboard
|
||||
httpResponse.sendRedirect(baseUrl + "/")
|
||||
}
|
||||
} else if(path.startsWith("/git/")){
|
||||
} else if(path.startsWith("/git/") || path.startsWith("/git-lfs/")){
|
||||
// Git repository
|
||||
chain.doFilter(request, response)
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gitbucket.core.controller
|
||||
|
||||
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||
import java.io.FileInputStream
|
||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||
|
||||
import gitbucket.core.plugin.PluginRegistry
|
||||
import gitbucket.core.repo.html
|
||||
@@ -16,9 +17,8 @@ import gitbucket.core.model.{Account, WebHook}
|
||||
import gitbucket.core.service.WebHookService._
|
||||
import gitbucket.core.view
|
||||
import gitbucket.core.view.helpers
|
||||
|
||||
import io.github.gitbucket.scalatra.forms._
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
import org.eclipse.jgit.api.{ArchiveCommand, Git}
|
||||
import org.eclipse.jgit.archive.{TgzFormat, ZipFormat}
|
||||
import org.eclipse.jgit.dircache.DirCache
|
||||
@@ -255,13 +255,9 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
val (id, path) = repository.splitPath(multiParams("splat").head)
|
||||
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
|
||||
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
|
||||
getPathObjectId(git, path, revCommit).flatMap { objectId =>
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
|
||||
contentType = FileUtil.getMimeType(path)
|
||||
response.setContentLength(loader.getSize.toInt)
|
||||
loader.copyTo(response.outputStream)
|
||||
()
|
||||
}
|
||||
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
responseRawFile(git, objectId, path, repository)
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
@@ -277,23 +273,62 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
||||
getPathObjectId(git, path, revCommit).map { objectId =>
|
||||
if(raw){
|
||||
// Download (This route is left for backword compatibility)
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
|
||||
contentType = FileUtil.getMimeType(path)
|
||||
response.setContentLength(loader.getSize.toInt)
|
||||
loader.copyTo(response.outputStream)
|
||||
()
|
||||
} getOrElse NotFound()
|
||||
responseRawFile(git, objectId, path, repository)
|
||||
} else {
|
||||
html.blob(id, repository, path.split("/").toList,
|
||||
JGitUtil.getContentInfo(git, path, objectId),
|
||||
new JGitUtil.CommitInfo(JGitUtil.getLastModifiedCommit(git, revCommit, path)),
|
||||
hasDeveloperRole(repository.owner, repository.name, context.loginAccount),
|
||||
request.paths(2) == "blame")
|
||||
request.paths(2) == "blame",
|
||||
isLfsFile(git, objectId))
|
||||
}
|
||||
} getOrElse NotFound()
|
||||
}
|
||||
})
|
||||
|
||||
private def isLfsFile(git: Git, objectId: ObjectId): Boolean = {
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
|
||||
if(loader.isLarge){
|
||||
false
|
||||
} else {
|
||||
new String(loader.getCachedBytes, "UTF-8").startsWith("version https://git-lfs.github.com/spec/v1")
|
||||
}
|
||||
}.getOrElse(false)
|
||||
}
|
||||
|
||||
private def responseRawFile(git: Git, objectId: ObjectId, path: String,
|
||||
repository: RepositoryService.RepositoryInfo): Unit = {
|
||||
JGitUtil.getObjectLoaderFromId(git, objectId){ loader =>
|
||||
contentType = FileUtil.getMimeType(path)
|
||||
|
||||
if(loader.isLarge){
|
||||
response.setContentLength(loader.getSize.toInt)
|
||||
loader.copyTo(response.outputStream)
|
||||
} else {
|
||||
val bytes = loader.getCachedBytes
|
||||
val text = new String(bytes, "UTF-8")
|
||||
|
||||
if(text.startsWith("version https://git-lfs.github.com/spec/v1")){
|
||||
// LFS objects
|
||||
val attrs = text.split("\n").map { line =>
|
||||
val dim = line.split(" ")
|
||||
dim(0) -> dim(1)
|
||||
}.toMap
|
||||
|
||||
response.setContentLength(attrs("size").toInt)
|
||||
val oid = attrs("oid").split(":")(1)
|
||||
|
||||
using(new FileInputStream(FileUtil.getLfsFilePath(repository.owner, repository.name, oid))){ in =>
|
||||
IOUtils.copy(in, response.getOutputStream)
|
||||
}
|
||||
} else {
|
||||
response.setContentLength(loader.getSize.toInt)
|
||||
response.getOutputStream.write(bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get("/:owner/:repository/blame/*"){
|
||||
blobRoute.action()
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ trait AccessTokenService {
|
||||
def tokenToHash(token: String): String = StringUtil.sha1(token)
|
||||
|
||||
/**
|
||||
* @retuen (TokenId, Token)
|
||||
* @return (TokenId, Token)
|
||||
*/
|
||||
def generateAccessToken(userName: String, note: String)(implicit s: Session): (Int, String) = {
|
||||
var token: String = null
|
||||
|
||||
@@ -176,6 +176,9 @@ object SystemSettingsService {
|
||||
port:Int,
|
||||
genericUser:String)
|
||||
|
||||
case class Lfs(
|
||||
serverUrl: Option[String])
|
||||
|
||||
val DefaultSshPort = 29418
|
||||
val DefaultSmtpPort = 25
|
||||
val DefaultLdapPort = 389
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package gitbucket.core.servlet
|
||||
|
||||
import java.io.{File, FileInputStream, FileOutputStream}
|
||||
import java.text.MessageFormat
|
||||
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
|
||||
|
||||
import gitbucket.core.util.{Directory, FileUtil, StringUtil}
|
||||
import org.apache.commons.io.{FileUtils, IOUtils}
|
||||
import org.json4s.jackson.Serialization._
|
||||
import org.apache.http.HttpStatus
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
|
||||
/**
|
||||
* Provides GitLFS Transfer API
|
||||
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/basic-transfers.md
|
||||
*/
|
||||
class GitLfsTransferServlet extends HttpServlet {
|
||||
|
||||
private implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
|
||||
private val LongObjectIdLength = 32
|
||||
private val LongObjectIdStringLength = LongObjectIdLength * 2
|
||||
|
||||
override protected def doGet(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
for {
|
||||
(owner, repository, oid) <- getPathInfo(req, res) if checkToken(req, oid)
|
||||
} yield {
|
||||
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
||||
if(file.exists()){
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
res.setContentType("application/octet-stream")
|
||||
res.setContentLength(file.length.toInt)
|
||||
using(new FileInputStream(file), res.getOutputStream){ (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
out.flush()
|
||||
}
|
||||
} else {
|
||||
sendError(res, HttpStatus.SC_NOT_FOUND,
|
||||
MessageFormat.format("Object ''{0}'' not found", oid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override protected def doPut(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
for {
|
||||
(owner, repository, oid) <- getPathInfo(req, res) if checkToken(req, oid)
|
||||
} yield {
|
||||
val file = new File(FileUtil.getLfsFilePath(owner, repository, oid))
|
||||
FileUtils.forceMkdir(file.getParentFile)
|
||||
using(req.getInputStream, new FileOutputStream(file)){ (in, out) =>
|
||||
IOUtils.copy(in, out)
|
||||
}
|
||||
res.setStatus(HttpStatus.SC_OK)
|
||||
}
|
||||
}
|
||||
|
||||
private def checkToken(req: HttpServletRequest, oid: String): Boolean = {
|
||||
val token = req.getHeader("Authorization")
|
||||
if(token != null){
|
||||
val Array(expireAt, targetOid) = StringUtil.decodeBlowfish(token).split(" ")
|
||||
oid == targetOid && expireAt.toLong > System.currentTimeMillis
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private def getPathInfo(req: HttpServletRequest, res: HttpServletResponse): Option[(String, String, String)] = {
|
||||
req.getRequestURI.substring(1).split("/") match {
|
||||
case Array(_, owner, repository, oid) => Some((owner, repository, oid))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
private def sendError(res: HttpServletResponse, status: Int, message: String): Unit = {
|
||||
res.setStatus(status)
|
||||
using(res.getWriter()){ out =>
|
||||
out.write(write(GitLfs.Error(message)))
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package gitbucket.core.servlet
|
||||
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
|
||||
import gitbucket.core.api
|
||||
import gitbucket.core.model.{Session, WebHook}
|
||||
@@ -11,16 +12,16 @@ import gitbucket.core.service._
|
||||
import gitbucket.core.util.ControlUtil._
|
||||
import gitbucket.core.util.Implicits._
|
||||
import gitbucket.core.util._
|
||||
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.http.server.GitServlet
|
||||
import org.eclipse.jgit.lib._
|
||||
import org.eclipse.jgit.transport._
|
||||
import org.eclipse.jgit.transport.resolver._
|
||||
import org.slf4j.LoggerFactory
|
||||
|
||||
import javax.servlet.ServletConfig
|
||||
import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
|
||||
|
||||
import org.json4s.jackson.Serialization._
|
||||
|
||||
|
||||
/**
|
||||
@@ -32,7 +33,8 @@ import javax.servlet.http.{HttpServletResponse, HttpServletRequest}
|
||||
class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
|
||||
private val logger = LoggerFactory.getLogger(classOf[GitRepositoryServlet])
|
||||
|
||||
private implicit val jsonFormats = gitbucket.core.api.JsonFormat.jsonFormats
|
||||
|
||||
override def init(config: ServletConfig): Unit = {
|
||||
setReceivePackFactory(new GitBucketReceivePackFactory())
|
||||
|
||||
@@ -45,15 +47,73 @@ class GitRepositoryServlet extends GitServlet with SystemSettingsService {
|
||||
override def service(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
val agent = req.getHeader("USER-AGENT")
|
||||
val index = req.getRequestURI.indexOf(".git")
|
||||
if(index >= 0 && (agent == null || agent.toLowerCase.indexOf("git/") < 0)){
|
||||
if(index >= 0 && (agent == null || agent.toLowerCase.indexOf("git") < 0)){
|
||||
// redirect for browsers
|
||||
val paths = req.getRequestURI.substring(0, index).split("/")
|
||||
res.sendRedirect(baseUrl(req) + "/" + paths.dropRight(1).last + "/" + paths.last)
|
||||
|
||||
} else if(req.getMethod.toUpperCase == "POST" && req.getRequestURI.endsWith("/info/lfs/objects/batch")){
|
||||
serviceGitLfsBatchAPI(req, res)
|
||||
|
||||
} else {
|
||||
// response for git client
|
||||
super.service(req, res)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides GitLFS Batch API
|
||||
* https://github.com/git-lfs/git-lfs/blob/master/docs/api/batch.md
|
||||
*/
|
||||
protected def serviceGitLfsBatchAPI(req: HttpServletRequest, res: HttpServletResponse): Unit = {
|
||||
val batchRequest = read[GitLfs.BatchRequest](req.getInputStream)
|
||||
val settings = loadSystemSettings()
|
||||
|
||||
settings.baseUrl match {
|
||||
case None => {
|
||||
throw new IllegalStateException("lfs.server_url is not configured.")
|
||||
}
|
||||
case Some(baseUrl) => {
|
||||
req.getRequestURI.substring(1).replace(".git/", "/").split("/") match {
|
||||
case Array(_, owner, repository, _*) => {
|
||||
val timeout = System.currentTimeMillis + (60000 * 10) // 10 min.
|
||||
val batchResponse = batchRequest.operation match {
|
||||
case "upload" =>
|
||||
GitLfs.BatchUploadResponse("basic", batchRequest.objects.map { requestObject =>
|
||||
GitLfs.BatchResponseObject(requestObject.oid, requestObject.size, true,
|
||||
GitLfs.Actions(
|
||||
upload = Some(GitLfs.Action(
|
||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||
header = Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
||||
expires_at = new Date(timeout)
|
||||
))
|
||||
)
|
||||
)
|
||||
})
|
||||
case "download" =>
|
||||
GitLfs.BatchUploadResponse("basic", batchRequest.objects.map { requestObject =>
|
||||
GitLfs.BatchResponseObject(requestObject.oid, requestObject.size, true,
|
||||
GitLfs.Actions(
|
||||
download = Some(GitLfs.Action(
|
||||
href = baseUrl + "/git-lfs/" + owner + "/" + repository + "/" + requestObject.oid,
|
||||
header = Map("Authorization" -> StringUtil.encodeBlowfish(timeout + " " + requestObject.oid)),
|
||||
expires_at = new Date(timeout)
|
||||
))
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
res.setContentType("application/vnd.git-lfs+json")
|
||||
using(res.getWriter){ out =>
|
||||
out.print(write(batchResponse))
|
||||
out.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GitBucketRepositoryResolver(parent: FileResolver[HttpServletRequest]) extends RepositoryResolver[HttpServletRequest] {
|
||||
@@ -232,3 +292,45 @@ class CommitLogHook(owner: String, repository: String, pusher: String, baseUrl:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object GitLfs {
|
||||
|
||||
case class BatchRequest(
|
||||
operation: String,
|
||||
transfers: Seq[String],
|
||||
objects: Seq[BatchRequestObject]
|
||||
)
|
||||
|
||||
case class BatchRequestObject(
|
||||
oid: String,
|
||||
size: Long
|
||||
)
|
||||
|
||||
case class BatchUploadResponse(
|
||||
transfer: String,
|
||||
objects: Seq[BatchResponseObject]
|
||||
)
|
||||
|
||||
case class BatchResponseObject(
|
||||
oid: String,
|
||||
size: Long,
|
||||
authenticated: Boolean,
|
||||
actions: Actions
|
||||
)
|
||||
|
||||
case class Actions(
|
||||
download: Option[Action] = None,
|
||||
upload: Option[Action] = None
|
||||
)
|
||||
|
||||
case class Action(
|
||||
href: String,
|
||||
header: Map[String, String] = Map.empty,
|
||||
expires_at: Date
|
||||
)
|
||||
|
||||
case class Error(
|
||||
message: String
|
||||
)
|
||||
|
||||
}
|
||||
@@ -21,8 +21,9 @@ class TransactionFilter extends Filter {
|
||||
def destroy(): Unit = {}
|
||||
|
||||
def doFilter(req: ServletRequest, res: ServletResponse, chain: FilterChain): Unit = {
|
||||
if(req.asInstanceOf[HttpServletRequest].getServletPath().startsWith("/assets/")){
|
||||
// assets don't need transaction
|
||||
val servletPath = req.asInstanceOf[HttpServletRequest].getServletPath()
|
||||
if(servletPath.startsWith("/assets/") || servletPath.startsWith("/git-lfs")){
|
||||
// assets and git-lfs don't need transaction
|
||||
chain.doFilter(req, res)
|
||||
} else {
|
||||
Database() withTransaction { session =>
|
||||
|
||||
@@ -20,6 +20,20 @@ object ControlUtil {
|
||||
}
|
||||
}
|
||||
|
||||
def using[A <% { def close(): Unit }, B <% { def close(): Unit }, C](resource1: A, resource2: B)(f: (A, B) => C): C =
|
||||
try f(resource1, resource2) finally {
|
||||
if(resource1 != null){
|
||||
ignoring(classOf[Throwable]) {
|
||||
resource1.close()
|
||||
}
|
||||
}
|
||||
if(resource2 != null){
|
||||
ignoring(classOf[Throwable]) {
|
||||
resource2.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def using[T](git: Git)(f: Git => T): T =
|
||||
try f(git) finally git.getRepository.close()
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
package gitbucket.core.util
|
||||
|
||||
import java.io.File
|
||||
import ControlUtil._
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
/**
|
||||
* Provides directories used by GitBucket.
|
||||
* Provides directory locations used by GitBucket.
|
||||
*/
|
||||
object Directory {
|
||||
|
||||
@@ -50,6 +48,12 @@ object Directory {
|
||||
def getAttachedDir(owner: String, repository: String): File =
|
||||
new File(s"${RepositoryHome}/${owner}/${repository}/comments")
|
||||
|
||||
/**
|
||||
* Directory for files which are attached to issue.
|
||||
*/
|
||||
def getLfsDir(owner: String, repository: String): File =
|
||||
new File(s"${RepositoryHome}/${owner}/${repository}/lfs")
|
||||
|
||||
/**
|
||||
* Directory for uploaded files by the specified user.
|
||||
*/
|
||||
|
||||
@@ -62,4 +62,8 @@ object FileUtil {
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"text/plain")
|
||||
|
||||
def getLfsFilePath(owner: String, repository: String, oid: String): String =
|
||||
Directory.getLfsDir(owner, repository) + "/" + oid
|
||||
|
||||
}
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
package gitbucket.core.util
|
||||
|
||||
import java.net.{URLDecoder, URLEncoder}
|
||||
|
||||
import org.mozilla.universalchardet.UniversalDetector
|
||||
import ControlUtil._
|
||||
import org.apache.commons.io.input.BOMInputStream
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.codec.binary.{Base64, StringUtils}
|
||||
|
||||
import scala.util.control.Exception._
|
||||
|
||||
object StringUtil {
|
||||
|
||||
private lazy val BlowfishKey = {
|
||||
// last 4 numbers in current timestamp
|
||||
val time = System.currentTimeMillis.toString
|
||||
time.substring(time.length - 4)
|
||||
}
|
||||
|
||||
def sha1(value: String): String =
|
||||
defining(java.security.MessageDigest.getInstance("SHA-1")){ md =>
|
||||
md.update(value.getBytes)
|
||||
@@ -21,6 +30,20 @@ object StringUtil {
|
||||
md.digest.map(b => "%02x".format(b)).mkString
|
||||
}
|
||||
|
||||
def encodeBlowfish(value: String): String = {
|
||||
val spec = new javax.crypto.spec.SecretKeySpec(BlowfishKey.getBytes(), "Blowfish")
|
||||
val cipher = javax.crypto.Cipher.getInstance("Blowfish")
|
||||
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, spec)
|
||||
new String(Base64.encodeBase64(cipher.doFinal(value.getBytes("UTF-8"))), "UTF-8")
|
||||
}
|
||||
|
||||
def decodeBlowfish(value: String): String = {
|
||||
val spec = new javax.crypto.spec.SecretKeySpec(BlowfishKey.getBytes(), "Blowfish")
|
||||
val cipher = javax.crypto.Cipher.getInstance("Blowfish")
|
||||
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, spec)
|
||||
new String(cipher.doFinal(Base64.decodeBase64(value)), "UTF-8")
|
||||
}
|
||||
|
||||
def urlEncode(value: String): String = URLEncoder.encode(value, "UTF-8").replace("+", "%20")
|
||||
|
||||
def urlDecode(value: String): String = URLDecoder.decode(value, "UTF-8")
|
||||
|
||||
@@ -123,27 +123,25 @@
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="ssh" name="ssh"@if(context.settings.ssh){ checked}/>
|
||||
Enable SSH access to git repository
|
||||
<span class="muted normal">(Both of SSH host and Base URL are required if SSH access is enabled)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="ssh">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="sshHost">SSH Host</label>
|
||||
<label class="control-label col-md-3" for="sshHost">SSH host</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="sshHost" name="sshHost" class="form-control" value="@context.settings.sshHost"/>
|
||||
<span id="error-sshHost" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="sshPort">SSH Port</label>
|
||||
<label class="control-label col-md-3" for="sshPort">SSH port</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="sshPort" name="sshPort" class="form-control" value="@context.settings.sshPort"/>
|
||||
<span id="error-sshPort" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="muted">
|
||||
Both of SSH host and Base URL are required if SSH access is enabled.
|
||||
</p>
|
||||
<!--====================================================================-->
|
||||
<!-- Authentication -->
|
||||
<!--====================================================================-->
|
||||
@@ -157,14 +155,14 @@
|
||||
</fieldset>
|
||||
<div class="ldap">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="ldapHost">LDAP Host</label>
|
||||
<label class="control-label col-md-3" for="ldapHost">LDAP host</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="ldapHost" name="ldap.host" class="form-control" value="@context.settings.ldap.map(_.host)"/>
|
||||
<span id="error-ldap_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="ldapPort">LDAP Port</label>
|
||||
<label class="control-label col-md-3" for="ldapPort">LDAP port</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="ldapPort" name="ldap.port" class="form-control input-mini" value="@context.settings.ldap.map(_.port)"/>
|
||||
<span id="error-ldap_port" class="error"></span>
|
||||
@@ -178,7 +176,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="ldapBindPassword">Bind Password</label>
|
||||
<label class="control-label col-md-3" for="ldapBindPassword">Bind password</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" id="ldapBindPassword" name="ldap.bindPassword" class="form-control" value="@context.settings.ldap.map(_.bindPassword)"/>
|
||||
<span id="error-ldap_bindPassword" class="error"></span>
|
||||
@@ -259,31 +257,32 @@
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" id="useSMTP" name="useSMTP" @if(context.settings.useSMTP){ checked}/>
|
||||
SMTP
|
||||
<span class="muted normal">(Enable notification not only SMTP configuration if you want to send notification email)</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="useSMTP">
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="smtpHost">SMTP Host</label>
|
||||
<label class="control-label col-md-3" for="smtpHost">SMTP host</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="smtpHost" name="smtp.host" class="form-control" value="@context.settings.smtp.map(_.host)"/>
|
||||
<span id="error-smtp_host" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="smtpPort">SMTP Port</label>
|
||||
<label class="control-label col-md-3" for="smtpPort">SMTP port</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="smtpPort" name="smtp.port" class="form-control input-mini" value="@context.settings.smtp.map(_.port)"/>
|
||||
<span id="error-smtp_port" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="smtpUser">SMTP User</label>
|
||||
<label class="control-label col-md-3" for="smtpUser">SMTP user</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="smtpUser" name="smtp.user" class="form-control" value="@context.settings.smtp.map(_.user)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="smtpPassword">SMTP Password</label>
|
||||
<label class="control-label col-md-3" for="smtpPassword">SMTP password</label>
|
||||
<div class="col-md-9">
|
||||
<input type="password" id="smtpPassword" name="smtp.password" class="form-control" value="@context.settings.smtp.map(_.password)"/>
|
||||
</div>
|
||||
@@ -295,13 +294,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="fromAddress">FROM Address</label>
|
||||
<label class="control-label col-md-3" for="fromAddress">FROM address</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="fromAddress" name="smtp.fromAddress" class="form-control" value="@context.settings.smtp.map(_.fromAddress)"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="fromName">FROM Name</label>
|
||||
<label class="control-label col-md-3" for="fromName">FROM name</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="fromName" name="smtp.fromName" class="form-control" value="@context.settings.smtp.map(_.fromName)"/>
|
||||
</div>
|
||||
@@ -311,11 +310,23 @@
|
||||
<input type="text" id="testAddress" size="30"/>
|
||||
<input type="button" id="sendTestMail" value="Send"/>
|
||||
</div>
|
||||
|
||||
<p class="muted">
|
||||
Enable notification not only SMTP configuration if you want to send notification email.
|
||||
</p>
|
||||
</div>
|
||||
<!--====================================================================-->
|
||||
<!-- GitLFS -->
|
||||
<!--====================================================================-->
|
||||
@*
|
||||
<hr>
|
||||
<label class="strong">
|
||||
GitLFS <span class="muted normal">(Put LFS server url to enable GitLFS support)</span>
|
||||
</label>
|
||||
<div class="form-group">
|
||||
<label class="control-label col-md-3" for="smtpHost">LFS server url</label>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="lfsServerUrl" name="lfs.serverUrl" class="form-control" value="@context.settings.lfs.serverUrl"/>
|
||||
<span id="error-lfs_serverUrl" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
*@
|
||||
</div>
|
||||
</div>
|
||||
<div class="align-right" style="margin-top: 20px;">
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
content: gitbucket.core.util.JGitUtil.ContentInfo,
|
||||
latestCommit: gitbucket.core.util.JGitUtil.CommitInfo,
|
||||
hasWritePermission: Boolean,
|
||||
isBlame: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
isBlame: Boolean,
|
||||
isLfsFile: Boolean)(implicit context: gitbucket.core.controller.Context)
|
||||
@import gitbucket.core.view.helpers
|
||||
@gitbucket.core.html.main(s"${(repository.name :: pathList).mkString("/")} at ${helpers.encodeRefName(branch)} - ${repository.owner}/${repository.name}", Some(repository)) {
|
||||
@gitbucket.core.html.menu("files", repository){
|
||||
@@ -45,6 +46,9 @@
|
||||
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
|
||||
}
|
||||
}
|
||||
@if(isLfsFile){
|
||||
<span class="label label-info">LFS</span>
|
||||
}
|
||||
</div>
|
||||
<div class="box-header">
|
||||
@helpers.avatar(latestCommit, 28)
|
||||
|
||||
@@ -46,6 +46,17 @@
|
||||
<url-pattern>/git/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>GitLfsTransferServlet</servlet-name>
|
||||
<servlet-class>gitbucket.core.servlet.GitLfsTransferServlet</servlet-class>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>GitLfsTransferServlet</servlet-name>
|
||||
<url-pattern>/git-lfs/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
|
||||
<!-- ===================================================================== -->
|
||||
<!-- Supply assets which are provided by plugins -->
|
||||
<!-- ===================================================================== -->
|
||||
|
||||
@@ -5,7 +5,7 @@ import java.util.Date
|
||||
import gitbucket.core.model.Account
|
||||
import gitbucket.core.service.{RequestCache, SystemSettingsService}
|
||||
import gitbucket.core.controller.Context
|
||||
import SystemSettingsService.SystemSettings
|
||||
import SystemSettingsService.{Lfs, SystemSettings}
|
||||
import javax.servlet.http.{HttpServletRequest, HttpSession}
|
||||
|
||||
import play.twirl.api.Html
|
||||
|
||||
Reference in New Issue
Block a user