Fix user interface of releases

This commit is contained in:
Naoki Takezoe
2018-01-13 04:03:31 +09:00
parent bb188ec948
commit 71cce5b470
11 changed files with 304 additions and 421 deletions

View File

@@ -8,8 +8,6 @@
<column name="TAG" type="varchar(100)" nullable="false"/>
<column name="AUTHOR" type="varchar(100)" nullable="false"/>
<column name="CONTENT" type="text" nullable="true"/>
<column name="IS_DRAFT" type="boolean" nullable="false"/>
<column name="IS_PRERELEASE" type="boolean" nullable="false"/>
<column name="REGISTERED_DATE" type="datetime" nullable="false"/>
<column name="UPDATED_DATE" type="datetime" nullable="false"/>
</createTable>

View File

@@ -2,7 +2,6 @@ package gitbucket.core.controller
import gitbucket.core.model.Account
import gitbucket.core.service.{AccountService, RepositoryService, ReleaseService}
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.servlet.Database
import gitbucket.core.util._
import gitbucket.core.util.SyntaxSugars._
@@ -89,21 +88,16 @@ class FileUploadController extends ScalatraServlet
} getOrElse BadRequest()
}
post("/release/:owner/:repository/:id"){
session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account =>
post("/release/:owner/:repository/:tag"){
session.get(Keys.Session.LoginAccount).collect { case _: Account =>
val owner = params("owner")
val repository = params("repository")
val releaseId = params("id").toInt
val release = getRelease(owner, repository, releaseId)
val tag = params("tag")
execute({ (file, fileId) =>
val fileName = file.getName
release.map { rel =>
createReleaseAsset(owner, repository, releaseId, fileId, fileName, file.size, loginAccount)
FileUtils.writeByteArrayToFile(new java.io.File(
getReleaseFilesDir(owner, repository) + s"/${releaseId}",
fileId), file.get)
fileName
}
FileUtils.writeByteArrayToFile(
new java.io.File(getReleaseFilesDir(owner, repository), tag + "/" + fileId),
file.get
)
}, _ => true)
}.getOrElse(BadRequest())
}

View File

@@ -6,11 +6,10 @@ import gitbucket.core.service.{AccountService, ActivityService, ReleaseService,
import gitbucket.core.util.{FileUtil, ReadableUsersAuthenticator, ReferrerAuthenticator, WritableUsersAuthenticator}
import gitbucket.core.util.Directory._
import gitbucket.core.util.Implicits._
import gitbucket.core.util.SyntaxSugars._
import gitbucket.core.view.Markdown
import org.scalatra.forms._
import gitbucket.core.releases.html
import org.apache.commons.io.FileUtils
import scala.collection.JavaConverters._
class ReleaseController extends ReleaseControllerBase
with RepositoryService
@@ -30,31 +29,25 @@ trait ReleaseControllerBase extends ControllerBase {
with WritableUsersAuthenticator
with ActivityService =>
case class ReleaseCreateForm(
case class ReleaseForm(
name: String,
content: Option[String],
isPrerelease: Boolean
content: Option[String]
)
val releaseCreateForm = mapping(
"name" -> trim(text(required)),
"content" -> trim(optional(text())),
"isprerelease" -> boolean()
)(ReleaseCreateForm.apply)
val releaseTitleEditForm = mapping(
"title" -> trim(label("Title", text(required)))
)(x => x)
val releaseEditForm = mapping(
val releaseForm = mapping(
"name" -> trim(text(required)),
"content" -> trim(optional(text()))
)(x => x)
)(ReleaseForm.apply)
get("/:owner/:repository/releases")(referrersOnly {repository =>
val releases = getReleases(repository.owner, repository.name)
val assets = getReleaseAssetsMap(repository.owner, repository.name)
html.list(
repository,
getReleases(repository.owner, repository.name),
getReleaseAssetsMap(repository.owner, repository.name),
repository.tags.reverse.map { tag =>
(tag, releases.find(_.tag == tag.name).map { release => (release, assets(release)) })
},
hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
})
@@ -68,93 +61,80 @@ trait ReleaseControllerBase extends ControllerBase {
get("/:owner/:repository/releases/:id/assets/:fileId")(referrersOnly {repository =>
val releaseId = params("id")
val fileId = params("fileId")
getRelease(repository.owner, repository.name, releaseId).flatMap{ release =>
getReleaseAsset(repository.owner, repository.name, releaseId, fileId).flatMap{ asset =>
response.setHeader("Content-Disposition", s"attachment; filename=${asset.label}")
Some(RawData(FileUtil.getMimeType(asset.label), new File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}", fileId)))
}
}.getOrElse(NotFound())
(for {
release <- getRelease(repository.owner, repository.name, releaseId)
asset <- getReleaseAsset(repository.owner, repository.name, releaseId, fileId)
} yield {
response.setHeader("Content-Disposition", s"attachment; filename=${asset.label}")
Some(RawData(
FileUtil.getMimeType(asset.label),
new File(getReleaseFilesDir(repository.owner, repository.name), release.tag + "/" + fileId)
))
}).getOrElse(NotFound())
})
ajaxPost("/:owner/:repository/releases/:id/assets/delete/:fileId")(writableUsersOnly { repository =>
val releaseId = params("id")
val fileId = params("fileId")
for(
release <- getRelease(repository.owner, repository.name, releaseId.toInt);
asset <- getReleaseAsset(repository.owner, repository.name, releaseId, fileId)
){
FileUtils.forceDelete(new File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}", fileId))
}
deleteReleaseAsset(repository.owner, repository.name, params("id").toInt, params("fileId"))
org.json4s.jackson.Serialization.write(Map("message" -> "ok"))
})
get("/:owner/:repository/releases/:tag/create")(writableUsersOnly {repository =>
val tag = params("tag")
defining(repository.owner, repository.name){ case (owner, name) =>
html.create(repository, tag)
}
html.form(repository, params("tag"), None)
})
post("/:owner/:repository/releases/:tag/create", releaseCreateForm)(writableUsersOnly { (form, repository) =>
post("/:owner/:repository/releases/:tag/create", releaseForm)(writableUsersOnly { (form, repository) =>
val tag = params("tag")
val release = createRelease(repository, form.name, form.content, tag, false, form.isPrerelease, context.loginAccount.get)
recordReleaseActivity(repository.owner, repository.name, context.loginAccount.get.userName, release.releaseId, release.name)
val loginAccount = context.loginAccount.get
// Insert into RELEASE
val release = createRelease(repository.owner, repository.name, form.name, form.content, tag, loginAccount)
// Insert into RELEASE_ASSET
request.getParameterNames.asScala.filter(_.startsWith("file:")).foreach { paramName =>
val Array(_, fileId) = paramName.split(":")
val fileName = params(paramName)
val size = new java.io.File(getReleaseFilesDir(repository.owner, repository.name), tag + "/" + fileId).length
createReleaseAsset(repository.owner, repository.name, release.releaseId, fileId, fileName, size, loginAccount)
}
recordReleaseActivity(repository.owner, repository.name, loginAccount.userName, release.releaseId, release.name)
redirect(s"/${release.userName}/${release.repositoryName}/releases/${release.releaseId}")
})
ajaxPost("/:owner/:repository/releases/delete/:id")(writableUsersOnly { repository =>
for(
release <- getRelease(repository.owner, repository.name, params("id"))
){
FileUtils.deleteDirectory(new File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}"))
}
deleteRelease(repository.owner, repository.name, params("id"))
org.json4s.jackson.Serialization.write(Map("message" -> "ok"))
get("/:owner/:repository/releases/:id/edit")(writableUsersOnly {repository =>
val releaseId = params("id").toInt
getRelease(repository.owner, repository.name, releaseId).map { release =>
html.form(repository, release.tag, Some(release, getReleaseAssets(repository.owner, repository.name, releaseId)))
}.getOrElse(NotFound())
})
ajaxPost("/:owner/:repository/releases/edit_title/:id", releaseTitleEditForm)(writableUsersOnly { (title, repository) =>
defining(repository.owner, repository.name) { case (owner, name) =>
getRelease(owner, name, params("id")).map { release =>
updateRelease(owner, name, release.releaseId, title, release.content)
redirect(s"/${owner}/${name}/releases/_data/${release.releaseId}")
} getOrElse NotFound()
}
})
post("/:owner/:repository/releases/:id/edit", releaseForm)(writableUsersOnly { (form, repository) =>
val releaseId = params("id").toInt
val loginAccount = context.loginAccount.get
ajaxPost("/:owner/:repository/releases/edit/:id", releaseEditForm)(writableUsersOnly { (content, repository) =>
defining(repository.owner, repository.name){ case (owner, name) =>
getRelease(owner, name, params("id")).map { release =>
updateRelease(owner, name, release.releaseId, release.name, content)
redirect(s"/${owner}/${name}/releases/_data/${release.releaseId}")
} getOrElse NotFound()
}
})
getRelease(repository.owner, repository.name, releaseId).map { release =>
// Update RELEASE
updateRelease(repository.owner, repository.name, releaseId, form.name, form.content)
ajaxGet("/:owner/:repository/releases/_data/:id")(writableUsersOnly { repository =>
getRelease(repository.owner, repository.name, params("id")) map { x =>
params.get("dataType") collect {
case t if t == "html" => html.editrelease(x.content, x.releaseId, repository)
} getOrElse {
contentType = formats("json")
org.json4s.jackson.Serialization.write(
Map(
"title" -> x.name,
"content" -> Markdown.toHtml(
markdown = x.content getOrElse "No description given.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableAnchor = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true
)
)
)
// Delete and Insert RELEASE_ASSET
deleteReleaseAssets(repository.owner, repository.name, releaseId)
request.getParameterNames.asScala.filter(_.startsWith("file:")).foreach { paramName =>
val Array(_, fileId) = paramName.split(":")
val fileName = params(paramName)
val size = new java.io.File(getReleaseFilesDir(repository.owner, repository.name), release.tag + "/" + fileId).length
createReleaseAsset(repository.owner, repository.name, release.releaseId, fileId, fileName, size, loginAccount)
}
} getOrElse NotFound()
redirect(s"/${release.userName}/${release.repositoryName}/releases/${release.releaseId}")
}.getOrElse(NotFound())
})
post("/:owner/:repository/releases/:id/delete")(writableUsersOnly { repository =>
val releaseId = params("id")
getRelease(repository.owner, repository.name, releaseId).foreach { release =>
FileUtils.deleteDirectory(new File(getReleaseFilesDir(repository.owner, repository.name), release.tag))
}
deleteRelease(repository.owner, repository.name, releaseId)
redirect(s"/${repository.owner}/${repository.name}/releases")
})
}

View File

@@ -14,12 +14,10 @@ trait ReleaseComponent extends TemplateComponent {
val tag = column[String]("TAG")
val author = column[String]("AUTHOR")
val content = column[Option[String]]("CONTENT")
val isDraft = column[Boolean]("IS_DRAFT")
val isPrerelease = column[Boolean]("IS_PRERELEASE")
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
val updatedDate = column[java.util.Date]("UPDATED_DATE")
def * = (userName, repositoryName, releaseId, name, tag, author, content, isDraft, isPrerelease, registeredDate, updatedDate) <> (Release.tupled, Release.unapply)
def * = (userName, repositoryName, releaseId, name, tag, author, content, registeredDate, updatedDate) <> (Release.tupled, Release.unapply)
def byPrimaryKey(owner: String, repository: String, releaseId: Int) = byRelease(owner, repository, releaseId)
@@ -46,8 +44,6 @@ case class Release(
tag: String,
author: String,
content: Option[String],
isDraft: Boolean,
isPrerelease: Boolean,
registeredDate: java.util.Date,
updatedDate: java.util.Date
)

View File

@@ -3,42 +3,39 @@ package gitbucket.core.service
import gitbucket.core.controller.Context
import gitbucket.core.model.{Account, Release, ReleaseAsset}
import gitbucket.core.util.StringUtil._
import gitbucket.core.util.Implicits._
import gitbucket.core.model.Profile.profile.blockingApi._
import gitbucket.core.model.Profile._
import gitbucket.core.model.Profile.profile._
import gitbucket.core.model.Profile.dateColumnType
import gitbucket.core.service.RepositoryService.RepositoryInfo
trait ReleaseService {
self: AccountService with RepositoryService =>
def createReleaseAsset(owner: String, repository: String, releaseId: Int, fileName: String, label: String, size: Long, loginAccount: Account)(implicit s: Session): Unit = {
ReleaseAssets insert ReleaseAsset(
userName = owner,
userName = owner,
repositoryName = repository,
releaseId = releaseId,
fileName = fileName,
label = label,
size = size,
uploader = loginAccount.userName,
releaseId = releaseId,
fileName = fileName,
label = label,
size = size,
uploader = loginAccount.userName,
registeredDate = currentDate,
updatedDate = currentDate
updatedDate = currentDate
)
}
def getReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s: Session): List[ReleaseAsset] = {
def getReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s: Session): Seq[ReleaseAsset] = {
ReleaseAssets.filter(x => x.byRelease(owner, repository, releaseId)).list
}
def getReleaseAssets(owner: String, repository: String, releaseId: String)(implicit s: Session): List[ReleaseAsset] = {
def getReleaseAssets(owner: String, repository: String, releaseId: String)(implicit s: Session): Seq[ReleaseAsset] = {
if (isInteger(releaseId))
getReleaseAssets(owner, repository, releaseId.toInt)
else
List.empty
Seq.empty
}
def getReleaseAssetsMap(owner: String, repository: String)(implicit s: Session): Map[Release, List[ReleaseAsset]] = {
def getReleaseAssetsMap(owner: String, repository: String)(implicit s: Session): Map[Release, Seq[ReleaseAsset]] = {
val releases = getReleases(owner, repository)
releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.releaseId))).toMap
}
@@ -49,32 +46,26 @@ trait ReleaseService {
else None
}
def deleteReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s:Session): Unit = {
def deleteReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s: Session): Unit = {
ReleaseAssets.filter(x => x.byRelease(owner, repository, releaseId)) delete
}
def deleteReleaseAsset(owner: String, repository: String, releaseId: Int, fileId: String)(implicit s: Session): Unit = {
ReleaseAssets.filter(x => x.byPrimaryKey(owner, repository, releaseId, fileId)) delete
}
def createRelease(repository: RepositoryInfo, name: String, content:Option[String], tag: String,
isDraft: Boolean, isPrerelease: Boolean, loginAccount: Account)(implicit context: Context, s: Session): Release = {
def createRelease(owner: String, repository: String, name: String, content: Option[String], tag: String,
loginAccount: Account)(implicit context: Context, s: Session): Release = {
Releases insert Release(
userName = repository.owner,
repositoryName = repository.name,
name = name,
tag = tag,
author = loginAccount.userName,
content = content,
isDraft = isDraft,
isPrerelease = isPrerelease,
userName = owner,
repositoryName = repository,
name = name,
tag = tag,
author = loginAccount.userName,
content = content,
registeredDate = currentDate,
updatedDate = currentDate
updatedDate = currentDate
)
getReleaseByTag(repository.owner, repository.name, tag).get
getReleaseByTag(owner, repository, tag).get
}
def getReleases(owner: String, repository: String)(implicit s: Session): List[Release] = {
def getReleases(owner: String, repository: String)(implicit s: Session): Seq[Release] = {
Releases.filter(x => x.byRepository(owner, repository)).list
}

View File

@@ -1,33 +0,0 @@
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo, tag: String)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"New Release - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.html.menu("releases", repository){
<form action="@helpers.url(repository)/releases/@tag/create" method="POST" validate="true" class="form-group">
<div class="row-fluid">
<div class="col-md-12">
<h3>New release for @tag</h3>
<span id="error-name" class="error"></span>
<input type="text" id="release-name" name="name" class="form-control" value="" placeholder="Title" style="margin-bottom: 6px;" autofocus/>
@gitbucket.core.helper.html.preview(
repository = repository,
content = "",
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true,
completionContext = "releases",
style = "height: 200px; max-height: 500px;",
elastic = true,
placeholder = "Describe this release"
)
<input type="checkbox" id="release-isprerelease" name="isprerelease"/>
<label for="release-isprerelease">This is a pre-release</label>
<div class="align-right">
<input type="submit" class="btn btn-success" value="Submit new release"/>
</div>
</div>
</div>
</form>
}
}

View File

@@ -1,38 +0,0 @@
@(content: Option[String], releaseId: Int,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
@gitbucket.core.helper.html.attached(repository, "releases"){
<textarea id="edit-content" class="form-control">@content.getOrElse("")</textarea>
}
<div>
<input type="button" id="cancel-release" class="btn btn-default" value="Cancel"/>
<input type="button" id="update-release" class="btn btn-success pull-right" value="Update comment"/>
</div>
<script>
$(function(){
var callback = function(data){
$('#update, #cancel').removeAttr('disabled');
$('#release-note').empty().html(data.content);
};
$('#update-release').click(function(){
$('#update, #cancel').attr('disabled', 'disabled');
$.ajax({
url: '@context.path/@repository.owner/@repository.name/releases/edit/@releaseId',
type: 'POST',
data: {
content : $('#edit-content').val()
}
}).done(
callback
).fail(function(req) {
$('#update, #cancel').removeAttr('disabled');
});
});
$('#cancel-release').click(function(){
$('#update, #cancel').attr('disabled', 'disabled');
$.get('@context.path/@repository.owner/@repository.name/releases/_data/@releaseId', callback);
return false;
});
});
</script>

View File

@@ -0,0 +1,87 @@
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
tag: String,
release: Option[(gitbucket.core.model.Release, Seq[gitbucket.core.model.ReleaseAsset])])(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@action = {
@release.map { case (release, _) =>
@helpers.url(repository)/releases/@release.releaseId/edit
}.getOrElse {
@helpers.url(repository)/releases/@helpers.encodeRefName(tag)/create
}
}
@gitbucket.core.html.main(s"New Release - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.html.menu("releases", repository){
<form action="@action.toString.trim" method="POST" validate="true" class="form-group">
<div class="row-fluid">
<div class="co`l-md-12">
@if(release.isEmpty){
<h3>New release for @tag</h3>
} else {
<h3>Update release for @tag</h3>
}
<span id="error-name" class="error"></span>
<input type="text" id="release-name" name="name" class="form-control" value="@release.map { case (release, _) => @release.name }.getOrElse(tag)" placeholder="Title" style="margin-bottom: 6px;" autofocus/>
@gitbucket.core.helper.html.preview(
repository = repository,
content = release.flatMap { case (release, _) => release.content }.getOrElse(""),
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = true,
completionContext = "releases",
style = "height: 200px; max-height: 500px;",
elastic = true,
placeholder = "Describe this release"
)
<ul id="assets-list" class="collaborator">
@release.map { case (release, assets) =>
@assets.map { asset =>
<li>
<a href="@context.baseUrl/@repository.owner/@repository.name/_release/@helpers.encodeRefName(tag)/@asset.fileName"><i class="octicon octicon-file"></i>@asset.label</a>
<a href="#" class="remove pull-right" style="padding-top: 0px;">(remove)</a>
<input type="hidden" name="file:@asset.fileName" value="@asset.label"/>
</li>
}
}
</ul>
<div style="border: 1px dashed #ccc; color: gray; background-color: #eee; padding: 4px;">
<div id="drop" class="clickable">Attach release files by dragging &amp; dropping, or selecting them.</div>
</div>
<div class="align-right" style="margin-top: 12px;">
@if(release.isEmpty){
<input type="submit" class="btn btn-success" value="Submit new release"/>
} else {
<input type="submit" class="btn btn-success" value="Update release"/>
}
</div>
</div>
</div>
</form>
}
}
<script>
$(function(){
$(document).on('click', '.remove', function(){
$(this).parent().remove();
});
$("#drop").dropzone({
maxFilesize: @{gitbucket.core.util.FileUtil.MaxFileSize / 1024 / 1024},
url: '@context.path/upload/release/@repository.owner/@repository.name/@helpers.encodeRefName(tag)',
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
success: function(file, id) {
var attach =
'<li><a href="@context.baseUrl/@repository.owner/@repository.name/_release/@helpers.encodeRefName(tag)/' + id + '">' +
'<i class="octicon octicon-file"></i>' + file.name + '</a>' +
'<a href="#" class="remove pull-right" style="padding-top: 0px;">(remove)</a>' +
'<input type="hidden" name="file:' + id + '" value="' + file.name + '"/>' + // TODO escape file.name
'</li>';
$('#assets-list').append(attach);
$(file.previewElement).prevAll('div.dz-preview').addBack().remove();
}
});
});
</script>

View File

@@ -1,60 +1,64 @@
@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo,
releases: List[gitbucket.core.model.Release],
releaseAssetsMap: Map[gitbucket.core.model.Release, List[gitbucket.core.model.ReleaseAsset]],
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
releases: Seq[(gitbucket.core.util.JGitUtil.TagInfo, Option[(gitbucket.core.model.Release, Seq[gitbucket.core.model.ReleaseAsset])])],
hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main("Releases" + s" - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.html.menu("releases", repository){
<table class="table table-bordered table-releases">
<thead>
<tr><th>@releases.length releases</div></th></tr>
</thead>
<tbody>
@releases.map{ release =>
<tr>
<td>
<div class="col-md-1"><a href="@helpers.url(repository)/tree/helpers.encodeRefName(@release.tag)"><i class="octicon octicon-tag"></i>@release.tag</a></div>
<div class="col-md-11" style="border-left: 1px solid #eee">
<h3><a href="@helpers.url(repository)/releases/@release.releaseId">@release.name</a></h3>
<div class="panel panel-default">
<div class="panel-heading">
<span class="muted">
@helpers.avatar(release.author, 20)
@helpers.user(release.author, styleClass="username strong")
released this @gitbucket.core.helper.html.datetimeago(release.registeredDate)
</span>
</div>
<div class="panel-body release-note markdown-body">
@helpers.markdown(
markdown = release.content getOrElse "No description provided.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
</div>
<h2>Downloads</h2>
<ul style="list-style: none">
@releaseAssetsMap(release).map{ asset =>
<li>
<i class="octicon octicon-file"></i>
<a href="@helpers.url(repository)/releases/@release.releaseId/assets/@asset.fileName">@asset.label</a>
<span class="label label-default">@helpers.readableSize(Some(asset.size))</span>
</li>
}
</ul>
<ul style="list-style: none;">
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.zip"><i class="octicon octicon-file-zip"></i>ZIP</a></li>
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.tar.gz"><i class="octicon octicon-file-zip"></i>TAR.GZ</a></li>
</ul>
</div>
</div>
@gitbucket.core.html.menu("releases", repository){
<table class="table table-bordered table-releases">
<thead>
<tr><th>@releases.length releases</th></tr>
</thead>
<tbody>
@releases.map { case (tag, release) =>
<tr>
<td>
<div class="col-md-2 text-right">
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(tag.name)" class="strong"><i class="octicon octicon-tag"></i>@tag.name</a><br>
<a href="@helpers.url(repository)/commit/@tag.id" class="monospace muted"><i class="octicon octicon-git-commit"></i>@tag.id.substring(0, 7)</a><br>
<span class="muted">@gitbucket.core.helper.html.datetimeago(tag.time)</span>
</div>
<div class="col-md-10" style="border-left: 1px solid #eee">
<div class="release-note markdown-body">
@release.map { case (release, assets) =>
<h3><a href="@helpers.url(repository)/releases/@release.releaseId">@release.name</a></h3>
<p class="muted">
@helpers.avatar(release.author, 20) @helpers.user(release.author, styleClass="username") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate)
</p>
@helpers.markdown(
markdown = release.content getOrElse "No description provided.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
}.getOrElse {
@if(hasWritePermission){
<div class="pull-right">
<a class="btn btn-success" href="@helpers.url(repository)/releases/@{helpers.encodeRefName(tag.name)}/create" id="edit">Create release</a>
</div>
}
}
<h4>Downloads</h4>
<ul style="list-style: none; padding-left: 8px;">
@release.map { case (release, assets) =>
@assets.map { asset =>
<li>
<i class="octicon octicon-file"></i><a href="@helpers.url(repository)/releases/@release.releaseId/assets/@asset.fileName">@asset.label</a>
<span class="label label-default">@helpers.readableSize(Some(asset.size))</span>
</li>
}
}
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(tag.name)}.zip"><i class="octicon octicon-file-zip"></i>Source code (zip)</a></li>
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(tag.name)}.tar.gz"><i class="octicon octicon-file-zip"></i>Source code (tar.gz)</a></li>
</ul>
</div>
</div>
</td>
</tr>
}
</td>
</tr>
</tbody>
</table>
}
</tbody>
</table>
}

View File

@@ -1,161 +1,65 @@
@(release: gitbucket.core.model.Release,
assets: List[gitbucket.core.model.ReleaseAsset],
hasWritePermission: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
assets: Seq[gitbucket.core.model.ReleaseAsset],
hasWritePermission: Boolean,
repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context)
@import gitbucket.core.view.helpers
@gitbucket.core.html.main(s"${release.name} - Release ${release.releaseId} - ${repository.owner}/${repository.name}", Some(repository)){
@gitbucket.core.html.menu("releases", repository){
<div class="row">
<div class="col-md-1">
<div><a href="@helpers.url(repository)/tree/@release.tag"><i class="octicon octicon-tag"></i>@release.tag</a></div>
</div>
<div class="col-md-11" style="border-left: 1px solid #eee">
<div>
<div class="show-title pull-right">
@if(hasWritePermission){
<a class="btn btn-default" href="#" id="edit">Edit title</a>
<a class="btn btn-danger" href="#" id="delete-release">Delete</a>
}
</div>
<div class="edit-title pull-right" style="display: none;">
<a class="btn btn-success" href="#" id="update">Save</a> <a class="btn btn-default" href="#" id="cancel">Cancel</a>
</div>
<h1 class="body-title">
<span class="show-title">
<span id="show-title">@release.name</span>
</span>
<span class="edit-title" style="display: none;">
<span id="error-edit-title" class="error"></span>
<input type="text" class="form-control" style="width: 700px;" id="edit-title" value="@release.name"/>
</span>
</h1>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<span class="muted">
@helpers.avatar(release.author, 20)
@helpers.user(release.author, styleClass="username strong") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate)
</span>
<span class="pull-right">
@if(hasWritePermission){
<a href="#"><i class="octicon octicon-pencil" aria-label="Edit" id="edit-notes"></i></a>&nbsp;
@gitbucket.core.html.menu("releases", repository){
<div class="row">
<div class="col-md-2 text-right">
@defining(repository.tags.find(_.name == release.tag)){ tag =>
@tag.map { tag =>
<a href="@helpers.url(repository)/tree/@helpers.encodeRefName(tag.name)" class="strong"><i class="octicon octicon-tag"></i>@tag.name</a><br>
<a href="@helpers.url(repository)/commit/@tag.id" class="monospace muted"><i class="octicon octicon-git-commit"></i>@tag.id.substring(0, 7)</a><br>
<span class="muted">@gitbucket.core.helper.html.datetimeago(tag.time)</span>
}
</span>
</div>
<div class="panel-body markdown-body" id="release-note">
@helpers.markdown(
markdown = release.content getOrElse "No description provided.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
</div>
<div class="panel-body">
<h2>Downloads</h2>
@if(hasWritePermission){
<div style="border: 3px dashed #ccc; background-color: #eee" >
<div id="drop" class="clickable">Attach release files by dragging &amp; dropping, or selecting them.</div>
</div>
}
<ul style="list-style: none;" id="attachedFiles">
@assets.map{ asset =>
<li>
<i class="octicon octicon-file"></i>
<a href="@helpers.url(repository)/releases/@release.releaseId/assets/@asset.fileName">@asset.label</a>
<span class="label label-default">@helpers.readableSize(Some(asset.size))</span>
</div>
<div class="col-md-10" style="border-left: 1px solid #eee">
<div class="markdown-body">
<h3>
@release.name
@if(hasWritePermission){
<a href="#" data-asset-file-name="@asset.fileName" class="label label-danger delete-asset">Delete</a>
<div class="pull-right">
<form method="POST" action="@helpers.url(repository)/releases/@release.releaseId/delete" id="delete-form">
<a class="btn btn-default" href="@helpers.url(repository)/releases/@release.releaseId/edit" id="edit">Edit</a>
<input type="submit" class="btn btn-danger" value="Delete">
</form>
</div>
}
</li>
}
</ul>
<ul style="list-style: none;">
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.zip"><i class="octicon octicon-file-zip"></i>ZIP</a></li>
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.tar.gz"><i class="octicon octicon-file-zip"></i>TAR.GZ</a></li>
</ul>
</h3>
<p class="muted">
@helpers.avatar(release.author, 20) @helpers.user(release.author, styleClass="username") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate)
</p>
@helpers.markdown(
markdown = release.content getOrElse "No description provided.",
repository = repository,
enableWikiLink = false,
enableRefsLink = true,
enableLineBreaks = true,
enableTaskList = true,
hasWritePermission = hasWritePermission
)
<h4>Downloads</h4>
<ul style="list-style: none; padding-left: 8px;" id="attachedFiles">
@assets.map{ asset =>
<li>
<i class="octicon octicon-file"></i><a href="@helpers.url(repository)/releases/@release.releaseId/assets/@asset.fileName">@asset.label</a>
<span class="label label-default">@helpers.readableSize(Some(asset.size))</span>
</li>
}
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.zip"><i class="octicon octicon-file-zip"></i>Source code (zip)</a></li>
<li><a href="@helpers.url(repository)/archive/@{helpers.encodeRefName(release.tag)}.tar.gz"><i class="octicon octicon-file-zip"></i>Source code (tar.gz)</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
}
}
<script>
$(function(){
$('.delete-asset').click(function(){
var name = $(this).data('asset-file-name');
var url = '@helpers.url(repository)/releases/@release.releaseId/assets/delete/' + name;
var li = $(this).closest('li');
$.post(url,
function(data){
li.remove();
});
return false;
});
$('#edit').click(function(){
$('.edit-title').show();
$('.show-title').hide();
return false;
});
$('#update').click(function(){
$(this).attr('disabled', 'disabled');
$.ajax({
url: '@helpers.url(repository)/releases/edit_title/@release.releaseId',
type: 'POST',
data: {
title : $('#edit-title').val()
}
}).done(function(data){
$('#show-title').empty().text(data.title);
$('#cancel').click();
$(this).removeAttr('disabled');
}).fail(function(req){
$(this).removeAttr('disabled');
$('#error-edit-title').text($.parseJSON(req.responseText).title);
});
return false;
});
$('#cancel').click(function(){
$('.edit-title').hide();
$('.show-title').show();
return false;
});
$('#edit-notes').click(function(){
var id = @release.releaseId;
var url = '@helpers.url(repository)/releases/_data/' + id;
var $content = $('#release-note');
$.get(url,
{
dataType : 'html'
},
function(data){
$content.empty().html(data);
});
return false;
});
$('#delete-release').click(function(){
if(confirm('Are you sure you want to delete this?')) {
$.post('@helpers.url(repository)/releases/delete/' + @release.releaseId,
function(data){
location.href = "@helpers.url(repository)/releases";
});
}
return false;
});
$("#drop").dropzone({
maxFilesize: @{gitbucket.core.util.FileUtil.MaxFileSize / 1024 / 1024},
url: '@context.path/upload/release/@repository.owner/@repository.name/@release.releaseId',
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your files...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
success: function(file, id) {
var attach = '<li><a href="@context.baseUrl/@repository.owner/@repository.name/_release/@release.releaseId/' + id + '">'
+ '<i class="octicon octicon-file"></i>' + file.name + '</a></li>';
$('#attachedFiles').append(attach);
}
$('#delete-form').submit(function(){
return confirm('Are you sure you want to delete this release?');
});
});
</script>
}
}

View File

@@ -27,11 +27,11 @@
<a href="@helpers.url(repository)/releases/@release.releaseId">Release</a>
}.getOrElse{
@if(hasWritePermission){
<div class="show-title pull-right">
<a class="btn btn-success" href="@helpers.url(repository)/releases/@{helpers.encodeRefName(tag.name)}/create">Create release</a>
</div>
}else{
<br />
<div class="show-title pull-right">
<a class="btn btn-success btn-min" href="@helpers.url(repository)/releases/@{helpers.encodeRefName(tag.name)}/create">Create release</a>
</div>
} else {
<br />
}
}
</td>