From fd30facd8f561a66123c476f9d252943b7b5a21a Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sun, 16 Apr 2017 20:56:50 +0900 Subject: [PATCH 01/17] Add Release page. (close #607) --- build.sbt | 2 +- .../resources/update/gitbucket-core_4.12.xml | 44 +++++ src/main/scala/ScalatraBootstrap.scala | 1 + .../gitbucket/core/GitBucketCoreModule.scala | 3 + .../controller/FileUploadController.scala | 27 +++- .../core/controller/ReleasesController.scala | 139 ++++++++++++++++ .../gitbucket/core/model/BasicTemplate.scala | 11 ++ .../scala/gitbucket/core/model/Profile.scala | 2 + .../scala/gitbucket/core/model/Release.scala | 53 ++++++ .../gitbucket/core/model/ReleasesAsset.scala | 38 +++++ .../core/service/ActivityService.scala | 13 +- .../core/service/ReleaseService.scala | 129 +++++++++++++++ .../core/service/RepositoryService.scala | 17 +- .../scala/gitbucket/core/util/Directory.scala | 8 +- .../core/helper/activities.scala.html | 1 + src/main/twirl/gitbucket/core/menu.scala.html | 1 + .../gitbucket/core/releases/create.scala.html | 33 ++++ .../core/releases/editrelease.scala.html | 38 +++++ .../gitbucket/core/releases/list.scala.html | 69 ++++++++ .../core/releases/release.scala.html | 151 ++++++++++++++++++ 20 files changed, 767 insertions(+), 13 deletions(-) create mode 100644 src/main/resources/update/gitbucket-core_4.12.xml create mode 100644 src/main/scala/gitbucket/core/controller/ReleasesController.scala create mode 100644 src/main/scala/gitbucket/core/model/Release.scala create mode 100644 src/main/scala/gitbucket/core/model/ReleasesAsset.scala create mode 100644 src/main/scala/gitbucket/core/service/ReleaseService.scala create mode 100644 src/main/twirl/gitbucket/core/releases/create.scala.html create mode 100644 src/main/twirl/gitbucket/core/releases/editrelease.scala.html create mode 100644 src/main/twirl/gitbucket/core/releases/list.scala.html create mode 100644 src/main/twirl/gitbucket/core/releases/release.scala.html diff --git a/build.sbt b/build.sbt index b7c12bce3..0fa50e92b 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ val Organization = "io.github.gitbucket" val Name = "gitbucket" -val GitBucketVersion = "4.11.0" +val GitBucketVersion = "4.12.0-SNAPSHOT" val ScalatraVersion = "2.5.0" val JettyVersion = "9.3.9.v20160517" diff --git a/src/main/resources/update/gitbucket-core_4.12.xml b/src/main/resources/update/gitbucket-core_4.12.xml new file mode 100644 index 000000000..3e503ffbe --- /dev/null +++ b/src/main/resources/update/gitbucket-core_4.12.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/scala/ScalatraBootstrap.scala b/src/main/scala/ScalatraBootstrap.scala index 949bf80d9..3ea8aad25 100644 --- a/src/main/scala/ScalatraBootstrap.scala +++ b/src/main/scala/ScalatraBootstrap.scala @@ -47,6 +47,7 @@ class ScalatraBootstrap extends LifeCycle with SystemSettingsService { context.mount(new MilestonesController, "/*") context.mount(new IssuesController, "/*") context.mount(new PullRequestsController, "/*") + context.mount(new ReleaseController, "/*") context.mount(new RepositorySettingsController, "/*") // Create GITBUCKET_HOME directory if it does not exist diff --git a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala index dd8665d55..2d5211e64 100644 --- a/src/main/scala/gitbucket/core/GitBucketCoreModule.scala +++ b/src/main/scala/gitbucket/core/GitBucketCoreModule.scala @@ -31,5 +31,8 @@ object GitBucketCoreModule extends Module("gitbucket-core", new Version("4.10.0"), new Version("4.11.0", new LiquibaseMigration("update/gitbucket-core_4.11.xml") + ), + new Version("4.12.0", + new LiquibaseMigration("update/gitbucket-core_4.12.xml") ) ) diff --git a/src/main/scala/gitbucket/core/controller/FileUploadController.scala b/src/main/scala/gitbucket/core/controller/FileUploadController.scala index cff4cebb1..eae347910 100644 --- a/src/main/scala/gitbucket/core/controller/FileUploadController.scala +++ b/src/main/scala/gitbucket/core/controller/FileUploadController.scala @@ -2,7 +2,7 @@ package gitbucket.core.controller import gitbucket.core.model.Account import gitbucket.core.service.RepositoryService.RepositoryInfo -import gitbucket.core.service.{AccountService, RepositoryService} +import gitbucket.core.service.{AccountService, RepositoryService, ReleaseService} import gitbucket.core.servlet.Database import gitbucket.core.util._ import gitbucket.core.util.SyntaxSugars._ @@ -20,7 +20,11 @@ import org.apache.commons.io.{FileUtils, IOUtils} * * This servlet saves uploaded file. */ -class FileUploadController extends ScalatraServlet with FileUploadSupport with RepositoryService with AccountService { +class FileUploadController extends ScalatraServlet + with FileUploadSupport + with RepositoryService + with AccountService + with ReleaseService{ configureMultipartHandling(MultipartConfig(maxFileSize = Some(3 * 1024 * 1024))) @@ -78,6 +82,25 @@ class FileUploadController extends ScalatraServlet with FileUploadSupport with R } getOrElse BadRequest() } + post("/release/:owner/:repository/:id"){ + session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account => + val owner = params("owner") + val repository = params("repository") + val releaseId = params("id").toInt + val release = getRelease(owner, repository, releaseId) + 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"/${rel.tag}", + fileId), file.get) + fileName + } + }, (_ => true)) + }.getOrElse(BadRequest()) + } + post("/import") { import JDBCUtil._ session.get(Keys.Session.LoginAccount).collect { case loginAccount: Account if loginAccount.isAdmin => diff --git a/src/main/scala/gitbucket/core/controller/ReleasesController.scala b/src/main/scala/gitbucket/core/controller/ReleasesController.scala new file mode 100644 index 000000000..d13498c8d --- /dev/null +++ b/src/main/scala/gitbucket/core/controller/ReleasesController.scala @@ -0,0 +1,139 @@ +package gitbucket.core.controller + +import gitbucket.core.service.{RepositoryService, AccountService, ReleaseService, ActivityService} +import gitbucket.core.util.{ReferrerAuthenticator, ReadableUsersAuthenticator, WritableUsersAuthenticator, FileUtil, Notifier} +import gitbucket.core.util.Directory._ +import gitbucket.core.util.Implicits._ +import gitbucket.core.util.SyntaxSugars._ +import gitbucket.core.view.Markdown +import io.github.gitbucket.scalatra.forms._ +import gitbucket.core.releases.html + +class ReleaseController extends ReleaseControllerBase + with RepositoryService + with AccountService + with ReleaseService + with ActivityService + with ReadableUsersAuthenticator + with ReferrerAuthenticator + with WritableUsersAuthenticator + +trait ReleaseControllerBase extends ControllerBase { + self: RepositoryService + with AccountService + with ReleaseService + with ReadableUsersAuthenticator + with ReferrerAuthenticator + with WritableUsersAuthenticator + with ActivityService => + + case class ReleaseCreateForm( + name: String, + content: Option[String], + isPrerelease: Boolean + ) + + 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( + "content" -> trim(optional(text())) + )(x => x) + + get("/:owner/:repository/releases")(referrersOnly {repository => + html.list( + repository, + getReleaseTagMap(repository.owner, repository.name), + getReleaseAssetsMap(repository.owner, repository.name), + hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) + }) + + get("/:owner/:repository/releases/:id")(referrersOnly {repository => + val id = params("id") + getRelease(repository.owner, repository.name, id).map{ release => + html.release(release, getReleaseAssets(repository.owner, repository.name, id), hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository) + }.getOrElse(NotFound()) + }) + + 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 java.io.File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.tag}", fileId))) + } + }.getOrElse(NotFound()) + }) + + get("/:owner/:repository/releases/:tag/create")(writableUsersOnly {repository => + val tag = params("tag") + defining(repository.owner, repository.name){ case (owner, name) => + html.create(repository, tag) + } + }) + + post("/:owner/:repository/releases/:tag/create", releaseCreateForm)(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) + + redirect(s"/${release.userName}/${release.repositoryName}/releases/${release.releaseId}") + }) + + get("/:owner/:repository/release/delete/:id")(writableUsersOnly { repository => + deleteRelease(repository.owner, repository.name, params("id")) + + redirect(s"/${repository.owner}/${repository.name}/releases") + }) + + 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() + } + }) + + 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() + } + }) + + 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 + ) + ) + ) + } + } getOrElse NotFound() + }) +} diff --git a/src/main/scala/gitbucket/core/model/BasicTemplate.scala b/src/main/scala/gitbucket/core/model/BasicTemplate.scala index c3b8e1b36..a33d96f66 100644 --- a/src/main/scala/gitbucket/core/model/BasicTemplate.scala +++ b/src/main/scala/gitbucket/core/model/BasicTemplate.scala @@ -63,4 +63,15 @@ protected[model] trait TemplateComponent { self: Profile => def byBranch(owner: String, repository: String, branchName: String) = byRepository(owner, repository) && (branch === branchName.bind) def byBranch(owner: Rep[String], repository: Rep[String], branchName: Rep[String]) = byRepository(owner, repository) && (this.branch === branchName) } + + trait ReleaseTemplate extends BasicTemplate { self: Table[_] => + val releaseId = column[Int]("RELEASE_ID") + + def byRelease(owner: String, repository: String, releaseId: Int) = + byRepository(owner, repository) && (this.releaseId === releaseId.bind) + + def byRelease(userName: Rep[String], repositoryName: Rep[String], releaseId: Rep[Int]) = + byRepository(userName, repositoryName) && (this.releaseId === releaseId) + } + } diff --git a/src/main/scala/gitbucket/core/model/Profile.scala b/src/main/scala/gitbucket/core/model/Profile.scala index 332e7ea30..76d64fc89 100644 --- a/src/main/scala/gitbucket/core/model/Profile.scala +++ b/src/main/scala/gitbucket/core/model/Profile.scala @@ -55,5 +55,7 @@ trait CoreProfile extends ProfileProvider with Profile with WebHookEventComponent with ProtectedBranchComponent with DeployKeyComponent + with ReleaseComponent + with ReleaseAssetComponent object Profile extends CoreProfile diff --git a/src/main/scala/gitbucket/core/model/Release.scala b/src/main/scala/gitbucket/core/model/Release.scala new file mode 100644 index 000000000..3d3b02f9b --- /dev/null +++ b/src/main/scala/gitbucket/core/model/Release.scala @@ -0,0 +1,53 @@ +package gitbucket.core.model + +trait ReleaseComponent extends TemplateComponent { + self: Profile => + + import profile.api._ + import self._ + + lazy val ReleaseId = TableQuery[ReleaseId] + lazy val Releases = TableQuery[Releases] + + class ReleaseId(tag: Tag) extends Table[(String, String, Int)](tag, "RELEASE_ID") with ReleaseTemplate { + def * = (userName, repositoryName, releaseId) + + def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository) + } + + class Releases(tag_ : Tag) extends Table[Release](tag_, "RELEASE") with ReleaseTemplate { + val name = column[String]("NAME") + 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 byPrimaryKey(owner: String, repository: String, releaseId: Int) = byRelease(owner, repository, releaseId) + + def byTag(owner: String, repository: String, tag: String) = + byRepository(owner, repository) && (this.tag === tag.bind) + + def byTag(userName: Rep[String], repositoryName: Rep[String], tag: Rep[String]) = + byRepository(userName, repositoryName) && (this.tag === tag) + } + +} + +case class Release( + userName: String, + repositoryName: String, + releaseId: Int = 0, + name: String, + tag: String, + author: String, + content: Option[String], + isDraft: Boolean, + isPrerelease: Boolean, + registeredDate: java.util.Date, + updatedDate: java.util.Date +) diff --git a/src/main/scala/gitbucket/core/model/ReleasesAsset.scala b/src/main/scala/gitbucket/core/model/ReleasesAsset.scala new file mode 100644 index 000000000..812534839 --- /dev/null +++ b/src/main/scala/gitbucket/core/model/ReleasesAsset.scala @@ -0,0 +1,38 @@ +package gitbucket.core.model + +import java.util.Date + +trait ReleaseAssetComponent extends TemplateComponent { + self: Profile => + + import profile.api._ + import self._ + + lazy val ReleaseAssets = TableQuery[ReleaseAssets] + + class ReleaseAssets(tag : Tag) extends Table[ReleaseAsset](tag, "RELEASE_ASSET") with ReleaseTemplate { + val fileName = column[String]("FILE_NAME") + val label = column[String]("LABEL") + val size = column[Long]("SIZE") + val uploader = column[String]("UPLOADER") + val registeredDate = column[Date]("REGISTERED_DATE") + val updatedDate = column[Date]("UPDATED_DATE") + + def * = (userName, repositoryName, releaseId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply) + + def byPrimaryKey(owner: String, repository: String, releaseId: Int, fileName: String) = byRelease(owner, repository, releaseId) && (this.fileName === fileName.bind) + } + +} + +case class ReleaseAsset( + userName: String, + repositoryName: String, + releaseId: Int, + fileName: String, + label: String, + size: Long, + uploader: String, + registeredDate: Date, + updatedDate: Date +) diff --git a/src/main/scala/gitbucket/core/service/ActivityService.scala b/src/main/scala/gitbucket/core/service/ActivityService.scala index 433909d77..75b73edd5 100644 --- a/src/main/scala/gitbucket/core/service/ActivityService.scala +++ b/src/main/scala/gitbucket/core/service/ActivityService.scala @@ -59,7 +59,7 @@ trait ActivityService { Activities insert Activity(userName, repositoryName, activityUserName, "open_issue", s"[user:${activityUserName}] opened issue [issue:${userName}/${repositoryName}#${issueId}]", - Some(title), + Some(title), currentDate) def recordCloseIssueActivity(userName: String, repositoryName: String, activityUserName: String, issueId: Int, title: String) @@ -135,7 +135,7 @@ trait ActivityService { Some(commits.map { commit => commit.id + ":" + commit.shortMessage }.mkString("\n")), currentDate) - def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String, + def recordCreateTagActivity(userName: String, repositoryName: String, activityUserName: String, tagName: String, commits: List[JGitUtil.CommitInfo])(implicit s: Session): Unit = Activities insert Activity(userName, repositoryName, activityUserName, "create_tag", @@ -167,7 +167,7 @@ trait ActivityService { None, currentDate) - def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(implicit s: Session): Unit = + def recordForkActivity(userName: String, repositoryName: String, activityUserName: String, forkedUserName: String)(implicit s: Session): Unit = Activities insert Activity(userName, repositoryName, activityUserName, "fork", s"[user:${activityUserName}] forked [repo:${userName}/${repositoryName}] to [repo:${forkedUserName}/${repositoryName}]", @@ -190,6 +190,13 @@ trait ActivityService { Some(message), currentDate) + def recordReleaseActivity(userName: String, repositoryName: String, activityUserName: String, releaseId: Int, name: String)(implicit s: Session): Unit = + Activities insert Activity(userName, repositoryName, activityUserName, + "release", + s"[user:${activityUserName}] released ${name} at [repo:${userName}/${repositoryName}]", + None, + currentDate) + private def cut(value: String, length: Int): String = if(value.length > length) value.substring(0, length) + "..." else value } diff --git a/src/main/scala/gitbucket/core/service/ReleaseService.scala b/src/main/scala/gitbucket/core/service/ReleaseService.scala new file mode 100644 index 000000000..0ca68b614 --- /dev/null +++ b/src/main/scala/gitbucket/core/service/ReleaseService.scala @@ -0,0 +1,129 @@ +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( + owner, + repository, + releaseId, + fileName, + label, + size, + loginAccount.userName, + currentDate, + currentDate + ) + } + + def getReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s: Session): List[ReleaseAsset] = { + ReleaseAssets.filter(x => x.byRelease(owner, repository, releaseId)).list + } + + def getReleaseAssets(owner: String, repository: String, releaseId: String)(implicit s: Session): List[ReleaseAsset] = { + if (isInteger(releaseId)) + getReleaseAssets(owner, repository, releaseId.toInt) + else + List.empty + } + + def getReleaseAssetsMap(owner: String, repository: String)(implicit s: Session): Map[Release, List[ReleaseAsset]] = { + val releases = getReleases(owner, repository) + releases.map(rel => (rel -> getReleaseAssets(owner, repository, rel.releaseId))).toMap + } + + def getReleaseAsset(owner: String, repository: String, releaseId: String, fileId: String)(implicit s: Session): Option[ReleaseAsset] = { + if (isInteger(releaseId)) + ReleaseAssets.filter(x => x.byPrimaryKey(owner, repository, releaseId.toInt, fileId)) firstOption + else None + } + + def deleteReleaseAssets(owner: String, repository: String, releaseId: Int)(implicit s: Session): Unit = { + ReleaseAssets.filter(x => x.byRelease(owner, repository, releaseId)) delete + } + + def createRelease(repository: RepositoryInfo, name: String, content:Option[String], tag: String, + isDraft: Boolean, isPrerelease: Boolean, loginAccount: Account)(implicit context: Context, s: Session): Release = { + val releaseId = insertRelease(repository.owner, repository.name, loginAccount.userName, name, tag, + content, isDraft, isPrerelease) + val release = getRelease(repository.owner, repository.name, releaseId.toString).get + release + } + + def getReleases(owner: String, repository: String)(implicit s: Session): List[Release] = { + Releases.filter(x => x.byRepository(owner, repository)).list + } + + def getRelease(owner: String, repository: String, releaseId: Int)(implicit s: Session): Option[Release] = { + Releases filter (_.byPrimaryKey(owner, repository, releaseId)) firstOption + } + + def getRelease(owner: String, repository: String, releaseId: String)(implicit s: Session): Option[Release] = { + if (isInteger(releaseId)) + getRelease(owner, repository, releaseId.toInt) + else None + } + + def getReleaseTagMap(owner: String, repository: String)(implicit s: Session): Map[String, Release] = { + val releases = getReleases(owner, repository) + releases.map(rel => (rel.tag -> rel)).toMap + } + + def insertRelease(owner: String, repository: String, loginUser: String, name: String, tag: String, + content: Option[String], isDraft: Boolean, isPrerelease: Boolean)(implicit s: Session): Int = { + // next id number + val id = sql"SELECT RELEASE_ID + 1 FROM RELEASE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int] + .firstOption.getOrElse(1) + Releases insert Release( + owner, + repository, + id, + name, + tag, + loginUser, + content, + isDraft, + isPrerelease, + currentDate, + currentDate + ) + + // increment issue id + if (id > 1){ + ReleaseId + .filter(_.byPrimaryKey(owner, repository)) + .map(_.releaseId) + .update(id) > 0 + }else{ + ReleaseId.insert(owner, repository, id) + } + + id + } + + def updateRelease(owner: String, repository: String, releaseId: Int, title: String, content: Option[String])(implicit s: Session): Int = { + Releases + .filter (_.byPrimaryKey(owner, repository, releaseId)) + .map { t => (t.name, t.content, t.updatedDate) } + .update (title, content, currentDate) + } + + def deleteRelease(owner: String, repository: String, releaseId: String)(implicit s: Session): Unit = { + if (isInteger(releaseId)){ + val relId = releaseId.toInt + deleteReleaseAssets(owner, repository, relId) + Releases filter (_.byPrimaryKey(owner, repository, relId)) delete + } + } +} diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala index 3640f1c68..cc3159d53 100644 --- a/src/main/scala/gitbucket/core/service/RepositoryService.scala +++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala @@ -3,7 +3,7 @@ package gitbucket.core.service import gitbucket.core.controller.Context import gitbucket.core.util._ import gitbucket.core.util.SyntaxSugars._ -import gitbucket.core.model.{Account, Collaborator, Repository, RepositoryOptions, Role} +import gitbucket.core.model.{Account, Collaborator, Repository, RepositoryOptions, Role, Release} import gitbucket.core.model.Profile._ import gitbucket.core.model.Profile.profile.blockingApi._ import gitbucket.core.model.Profile.dateColumnType @@ -216,6 +216,10 @@ trait RepositoryService { self: AccountService => t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind) }.map(_.pullRequest).list + val releases = Releases.filter { t => + t.byRepository(repository.userName, repository.repositoryName) + }.list + new RepositoryInfo( JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName), repository, @@ -225,6 +229,7 @@ trait RepositoryService { self: AccountService => repository.originUserName.getOrElse(repository.userName), repository.originRepositoryName.getOrElse(repository.repositoryName) ), + releases.length, getRepositoryManagers(repository.userName)) } } @@ -458,20 +463,20 @@ trait RepositoryService { self: AccountService => object RepositoryService { case class RepositoryInfo(owner: String, name: String, repository: Repository, - issueCount: Int, pullCount: Int, forkedCount: Int, + issueCount: Int, pullCount: Int, forkedCount: Int, releaseCount: Int, branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]) { /** * Creates instance with issue count and pull request count. */ - def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, model, issueCount, pullCount, forkedCount, repo.branchList, repo.tags, managers) + def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, releaseCount: Int, managers: Seq[String]) = + this(repo.owner, repo.name, model, issueCount, pullCount, forkedCount, releaseCount, repo.branchList, repo.tags, managers) /** - * Creates instance without issue count and pull request count. + * Creates instance without issue, pull request and release count. */ def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, model, 0, 0, forkedCount, repo.branchList, repo.tags, managers) + this(repo.owner, repo.name, model, 0, 0, forkedCount, 0, repo.branchList, repo.tags, managers) def httpUrl(implicit context: Context): String = RepositoryService.httpUrl(owner, name) def sshUrl(implicit context: Context): Option[String] = RepositoryService.sshUrl(owner, name) diff --git a/src/main/scala/gitbucket/core/util/Directory.scala b/src/main/scala/gitbucket/core/util/Directory.scala index e54a7862a..b30b45541 100644 --- a/src/main/scala/gitbucket/core/util/Directory.scala +++ b/src/main/scala/gitbucket/core/util/Directory.scala @@ -54,6 +54,12 @@ object Directory { def getAttachedDir(owner: String, repository: String): File = new File(getRepositoryFilesDir(owner, repository), "comments") + /** + * Directory for released files + */ + def getReleaseFilesDir(owner: String, repository: String): File = + new File(getRepositoryFilesDir(owner, repository), "releases") + /** * Directory for files which are attached to issue. */ @@ -90,4 +96,4 @@ object Directory { def getWikiRepositoryDir(owner: String, repository: String): File = new File(s"${RepositoryHome}/${owner}/${repository}.wiki.git") -} \ No newline at end of file +} diff --git a/src/main/twirl/gitbucket/core/helper/activities.scala.html b/src/main/twirl/gitbucket/core/helper/activities.scala.html index 8d01d0dce..2b95fbcb8 100644 --- a/src/main/twirl/gitbucket/core/helper/activities.scala.html +++ b/src/main/twirl/gitbucket/core/helper/activities.scala.html @@ -14,6 +14,7 @@ case "reopen_issue" => detailActivity(activity, "issue-reopened") case "open_pullreq" => detailActivity(activity, "git-pull-request") case "merge_pullreq" => detailActivity(activity, "git-merge") + case "release" => detailActivity(activity, "package") case "create_repository" => simpleActivity(activity, "repo") case "create_branch" => simpleActivity(activity, "git-branch") case "delete_branch" => simpleActivity(activity, "circle-slash") diff --git a/src/main/twirl/gitbucket/core/menu.scala.html b/src/main/twirl/gitbucket/core/menu.scala.html index e75d7f81e..e27e23c22 100644 --- a/src/main/twirl/gitbucket/core/menu.scala.html +++ b/src/main/twirl/gitbucket/core/menu.scala.html @@ -27,6 +27,7 @@ @menuitem("/branches", "branches", "Branches", "git-branch", repository.branchList.length) @menuitem("/tags", "tags", "Tags", "tag", repository.tags.length) } + @menuitem("/releases", "releases", "Releases", "package", repository.releaseCount) @if(repository.repository.options.issuesOption != "DISABLE") { @menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount) @menuitem("/pulls", "pulls", "Pull requests", "git-pull-request", repository.pullCount) diff --git a/src/main/twirl/gitbucket/core/releases/create.scala.html b/src/main/twirl/gitbucket/core/releases/create.scala.html new file mode 100644 index 000000000..396ae9c0d --- /dev/null +++ b/src/main/twirl/gitbucket/core/releases/create.scala.html @@ -0,0 +1,33 @@ +@(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){ +
+
+
+

New release for @tag

+ + + @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" + ) + + +
+ +
+
+
+
+} +} diff --git a/src/main/twirl/gitbucket/core/releases/editrelease.scala.html b/src/main/twirl/gitbucket/core/releases/editrelease.scala.html new file mode 100644 index 000000000..f7a3aef24 --- /dev/null +++ b/src/main/twirl/gitbucket/core/releases/editrelease.scala.html @@ -0,0 +1,38 @@ +@(content: Option[String], releaseId: Int, +repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit context: gitbucket.core.controller.Context) +@gitbucket.core.helper.html.attached(repository, "releases"){ + +} +
+ + +
+ diff --git a/src/main/twirl/gitbucket/core/releases/list.scala.html b/src/main/twirl/gitbucket/core/releases/list.scala.html new file mode 100644 index 000000000..11e5b5b34 --- /dev/null +++ b/src/main/twirl/gitbucket/core/releases/list.scala.html @@ -0,0 +1,69 @@ +@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo, +tagReleaseMap: Map[String, gitbucket.core.model.Release], +releaseAssetsMap: Map[gitbucket.core.model.Release, List[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){ + + + + + + @repository.tags.reverse.map{ tag => + + + + } + +
@tagReleaseMap.count(_ => true) releases
+ @tagReleaseMap.get(tag.name).map{rel => + +
+

@rel.name

+
+
+ + @helpers.avatar(rel.author, 20) + @helpers.user(rel.author, styleClass="username strong") + released this @gitbucket.core.helper.html.datetimeago(rel.registeredDate) + +
+
+ @helpers.markdown( + markdown = rel.content getOrElse "No description provided.", + repository = repository, + enableWikiLink = false, + enableRefsLink = true, + enableLineBreaks = true, + enableTaskList = true, + hasWritePermission = hasWritePermission + ) +
+

Downloads

+
    + @releaseAssetsMap(rel).map{ asset => +
  • @asset.label
  • + } +
+ +
+
+ }.getOrElse{ +
+ @gitbucket.core.helper.html.datetimeago(tag.time) +
+
+ @tag.name + @if(hasWritePermission){ + + } +
+ } +
+}} diff --git a/src/main/twirl/gitbucket/core/releases/release.scala.html b/src/main/twirl/gitbucket/core/releases/release.scala.html new file mode 100644 index 000000000..3cadc3a7f --- /dev/null +++ b/src/main/twirl/gitbucket/core/releases/release.scala.html @@ -0,0 +1,151 @@ +@(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) +@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){ +
+ +
+
+
+ @if(hasWritePermission){ + Edit title + Delete + } +
+ +

+ + @release.name + + +

+
+
+
+ + @helpers.avatar(release.author, 20) + @helpers.user(release.author, styleClass="username strong") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate) + + + @if(hasWritePermission){ +   + } + +
+
+ @helpers.markdown( + markdown = release.content getOrElse "No description provided.", + repository = repository, + enableWikiLink = false, + enableRefsLink = true, + enableLineBreaks = true, + enableTaskList = true, + hasWritePermission = hasWritePermission + ) +
+
+

Downloads

+ @if(hasWritePermission){ +
+
Attach release files by dragging & dropping, or selecting them.
+
+ } +
    + @assets.map{ asset => +
  • + + @asset.label + @helpers.readableSize(Some(asset.size)) +
  • + } +
+ +
+
+
+
+ +} +} From 0f70e5b1d6025cd3351484cfd338b3ecf47148e9 Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Tue, 18 Apr 2017 23:04:56 +0900 Subject: [PATCH 02/17] Change release files direcoty to releaseId from tagname. --- .../scala/gitbucket/core/controller/FileUploadController.scala | 2 +- .../scala/gitbucket/core/controller/ReleasesController.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/FileUploadController.scala b/src/main/scala/gitbucket/core/controller/FileUploadController.scala index eae347910..987d39694 100644 --- a/src/main/scala/gitbucket/core/controller/FileUploadController.scala +++ b/src/main/scala/gitbucket/core/controller/FileUploadController.scala @@ -93,7 +93,7 @@ class FileUploadController extends ScalatraServlet release.map { rel => createReleaseAsset(owner, repository, releaseId, fileId, fileName, file.size, loginAccount) FileUtils.writeByteArrayToFile(new java.io.File( - getReleaseFilesDir(owner, repository) + s"/${rel.tag}", + getReleaseFilesDir(owner, repository) + s"/${releaseId}", fileId), file.get) fileName } diff --git a/src/main/scala/gitbucket/core/controller/ReleasesController.scala b/src/main/scala/gitbucket/core/controller/ReleasesController.scala index d13498c8d..ad827979b 100644 --- a/src/main/scala/gitbucket/core/controller/ReleasesController.scala +++ b/src/main/scala/gitbucket/core/controller/ReleasesController.scala @@ -68,7 +68,7 @@ trait ReleaseControllerBase extends ControllerBase { 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 java.io.File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.tag}", fileId))) + Some(RawData(FileUtil.getMimeType(asset.label), new java.io.File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}", fileId))) } }.getOrElse(NotFound()) }) From 981b228a88e4c6d142ceb9e3da814f4710ff118f Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Wed, 19 Apr 2017 19:59:51 +0900 Subject: [PATCH 03/17] Add RELEASE_ASSET_ID --- src/main/resources/update/gitbucket-core_4.12.xml | 1 + src/main/scala/gitbucket/core/model/ReleasesAsset.scala | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/resources/update/gitbucket-core_4.12.xml b/src/main/resources/update/gitbucket-core_4.12.xml index 3e503ffbe..1f4b6d92f 100644 --- a/src/main/resources/update/gitbucket-core_4.12.xml +++ b/src/main/resources/update/gitbucket-core_4.12.xml @@ -31,6 +31,7 @@ + diff --git a/src/main/scala/gitbucket/core/model/ReleasesAsset.scala b/src/main/scala/gitbucket/core/model/ReleasesAsset.scala index 812534839..166474df7 100644 --- a/src/main/scala/gitbucket/core/model/ReleasesAsset.scala +++ b/src/main/scala/gitbucket/core/model/ReleasesAsset.scala @@ -11,6 +11,7 @@ trait ReleaseAssetComponent extends TemplateComponent { lazy val ReleaseAssets = TableQuery[ReleaseAssets] class ReleaseAssets(tag : Tag) extends Table[ReleaseAsset](tag, "RELEASE_ASSET") with ReleaseTemplate { + val releaseAssetId = column[Int]("RELEASE_ASSET_ID", O AutoInc) val fileName = column[String]("FILE_NAME") val label = column[String]("LABEL") val size = column[Long]("SIZE") @@ -18,7 +19,7 @@ trait ReleaseAssetComponent extends TemplateComponent { val registeredDate = column[Date]("REGISTERED_DATE") val updatedDate = column[Date]("UPDATED_DATE") - def * = (userName, repositoryName, releaseId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply) + def * = (userName, repositoryName, releaseId, releaseAssetId, fileName, label, size, uploader, registeredDate, updatedDate) <> (ReleaseAsset.tupled, ReleaseAsset.unapply) def byPrimaryKey(owner: String, repository: String, releaseId: Int, fileName: String) = byRelease(owner, repository, releaseId) && (this.fileName === fileName.bind) } @@ -29,6 +30,7 @@ case class ReleaseAsset( userName: String, repositoryName: String, releaseId: Int, + releaseAssetId: Int = 0, fileName: String, label: String, size: Long, From 239c7371a82b5e7867d78a34204911e5a1bdf39d Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Wed, 19 Apr 2017 21:13:43 +0900 Subject: [PATCH 04/17] Add moveDirectory for releases directory. --- .../RepositorySettingsController.scala | 12 ++++++++++++ .../core/service/ReleaseService.scala | 18 +++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala index c6f580f9e..eb35623c6 100644 --- a/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositorySettingsController.scala @@ -139,6 +139,12 @@ trait RepositorySettingsControllerBase extends ControllerBase { FileUtils.moveDirectory(dir, getLfsDir(repository.owner, form.repositoryName)) } } + // Move release directory + defining(getReleaseFilesDir(repository.owner, repository.name)){ dir => + if(dir.isDirectory) { + FileUtils.moveDirectory(dir, getReleaseFilesDir(repository.owner, form.repositoryName)) + } + } // Delete parent directory FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name)) @@ -352,6 +358,12 @@ trait RepositorySettingsControllerBase extends ControllerBase { FileUtils.moveDirectory(dir, getLfsDir(form.newOwner, repository.name)) } } + // Move release directory + defining(getReleaseFilesDir(repository.owner, repository.name)){ dir => + if(dir.isDirectory) { + FileUtils.moveDirectory(dir, getReleaseFilesDir(form.newOwner, repository.name)) + } + } // Delere parent directory FileUtil.deleteDirectoryIfEmpty(getRepositoryFilesDir(repository.owner, repository.name)) diff --git a/src/main/scala/gitbucket/core/service/ReleaseService.scala b/src/main/scala/gitbucket/core/service/ReleaseService.scala index 0ca68b614..dae1eecc2 100644 --- a/src/main/scala/gitbucket/core/service/ReleaseService.scala +++ b/src/main/scala/gitbucket/core/service/ReleaseService.scala @@ -15,15 +15,15 @@ trait ReleaseService { def createReleaseAsset(owner: String, repository: String, releaseId: Int, fileName: String, label: String, size: Long, loginAccount: Account)(implicit s: Session): Unit = { ReleaseAssets insert ReleaseAsset( - owner, - repository, - releaseId, - fileName, - label, - size, - loginAccount.userName, - currentDate, - currentDate + userName = owner, + repositoryName = repository, + releaseId = releaseId, + fileName = fileName, + label = label, + size = size, + uploader = loginAccount.userName, + registeredDate = currentDate, + updatedDate = currentDate ) } From fb0cd272ce96baacf3226aa914d209e886f66a4d Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 22 Apr 2017 08:55:15 +0900 Subject: [PATCH 05/17] change to col-md-12 https://github.com/gitbucket/gitbucket/pull/1543#discussion_r112392040 --- src/main/twirl/gitbucket/core/releases/create.scala.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/twirl/gitbucket/core/releases/create.scala.html b/src/main/twirl/gitbucket/core/releases/create.scala.html index 396ae9c0d..fb4fbee30 100644 --- a/src/main/twirl/gitbucket/core/releases/create.scala.html +++ b/src/main/twirl/gitbucket/core/releases/create.scala.html @@ -4,7 +4,7 @@ @gitbucket.core.html.menu("releases", repository){
-
+

New release for @tag

From 64f7db65854f308a4eebee80a2e666e16f0b0918 Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 22 Apr 2017 09:19:20 +0900 Subject: [PATCH 06/17] change repository-wide release Id numbering to system-wide numbering. --- .../resources/update/gitbucket-core_4.12.xml | 11 +--- .../scala/gitbucket/core/model/Release.scala | 7 --- .../core/service/ReleaseService.scala | 53 ++++++------------- 3 files changed, 18 insertions(+), 53 deletions(-) diff --git a/src/main/resources/update/gitbucket-core_4.12.xml b/src/main/resources/update/gitbucket-core_4.12.xml index 1f4b6d92f..df5e463a9 100644 --- a/src/main/resources/update/gitbucket-core_4.12.xml +++ b/src/main/resources/update/gitbucket-core_4.12.xml @@ -3,7 +3,7 @@ - + @@ -18,15 +18,6 @@ - - - - - - - - - diff --git a/src/main/scala/gitbucket/core/model/Release.scala b/src/main/scala/gitbucket/core/model/Release.scala index 3d3b02f9b..a46afffcf 100644 --- a/src/main/scala/gitbucket/core/model/Release.scala +++ b/src/main/scala/gitbucket/core/model/Release.scala @@ -6,15 +6,8 @@ trait ReleaseComponent extends TemplateComponent { import profile.api._ import self._ - lazy val ReleaseId = TableQuery[ReleaseId] lazy val Releases = TableQuery[Releases] - class ReleaseId(tag: Tag) extends Table[(String, String, Int)](tag, "RELEASE_ID") with ReleaseTemplate { - def * = (userName, repositoryName, releaseId) - - def byPrimaryKey(owner: String, repository: String) = byRepository(owner, repository) - } - class Releases(tag_ : Tag) extends Table[Release](tag_, "RELEASE") with ReleaseTemplate { val name = column[String]("NAME") val tag = column[String]("TAG") diff --git a/src/main/scala/gitbucket/core/service/ReleaseService.scala b/src/main/scala/gitbucket/core/service/ReleaseService.scala index dae1eecc2..f70c1c0a3 100644 --- a/src/main/scala/gitbucket/core/service/ReleaseService.scala +++ b/src/main/scala/gitbucket/core/service/ReleaseService.scala @@ -55,10 +55,19 @@ trait ReleaseService { def createRelease(repository: RepositoryInfo, name: String, content:Option[String], tag: String, isDraft: Boolean, isPrerelease: Boolean, loginAccount: Account)(implicit context: Context, s: Session): Release = { - val releaseId = insertRelease(repository.owner, repository.name, loginAccount.userName, name, tag, - content, isDraft, isPrerelease) - val release = getRelease(repository.owner, repository.name, releaseId.toString).get - release + Releases insert Release( + userName = repository.owner, + repositoryName = repository.name, + name = name, + tag = tag, + author = loginAccount.userName, + content = content, + isDraft = isDraft, + isPrerelease = isPrerelease, + registeredDate = currentDate, + updatedDate = currentDate + ) + getReleaseByTag(repository.owner, repository.name, tag).get } def getReleases(owner: String, repository: String)(implicit s: Session): List[Release] = { @@ -69,6 +78,10 @@ trait ReleaseService { Releases filter (_.byPrimaryKey(owner, repository, releaseId)) firstOption } + def getReleaseByTag(owner: String, repository: String, tag: String)(implicit s: Session): Option[Release] = { + Releases filter (_.byTag(owner, repository, tag)) firstOption + } + def getRelease(owner: String, repository: String, releaseId: String)(implicit s: Session): Option[Release] = { if (isInteger(releaseId)) getRelease(owner, repository, releaseId.toInt) @@ -80,38 +93,6 @@ trait ReleaseService { releases.map(rel => (rel.tag -> rel)).toMap } - def insertRelease(owner: String, repository: String, loginUser: String, name: String, tag: String, - content: Option[String], isDraft: Boolean, isPrerelease: Boolean)(implicit s: Session): Int = { - // next id number - val id = sql"SELECT RELEASE_ID + 1 FROM RELEASE_ID WHERE USER_NAME = $owner AND REPOSITORY_NAME = $repository FOR UPDATE".as[Int] - .firstOption.getOrElse(1) - Releases insert Release( - owner, - repository, - id, - name, - tag, - loginUser, - content, - isDraft, - isPrerelease, - currentDate, - currentDate - ) - - // increment issue id - if (id > 1){ - ReleaseId - .filter(_.byPrimaryKey(owner, repository)) - .map(_.releaseId) - .update(id) > 0 - }else{ - ReleaseId.insert(owner, repository, id) - } - - id - } - def updateRelease(owner: String, repository: String, releaseId: Int, title: String, content: Option[String])(implicit s: Session): Int = { Releases .filter (_.byPrimaryKey(owner, repository, releaseId)) From 44d2918deee765aff13b2362eff6242ee7e26d1e Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 22 Apr 2017 09:19:43 +0900 Subject: [PATCH 07/17] error-title to error-name. --- src/main/twirl/gitbucket/core/releases/create.scala.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/twirl/gitbucket/core/releases/create.scala.html b/src/main/twirl/gitbucket/core/releases/create.scala.html index fb4fbee30..b23658f7a 100644 --- a/src/main/twirl/gitbucket/core/releases/create.scala.html +++ b/src/main/twirl/gitbucket/core/releases/create.scala.html @@ -6,7 +6,7 @@

New release for @tag

- + @gitbucket.core.helper.html.preview( repository = repository, From 806a5aecef5ce0019e80c828d6cd022f8bc4b31c Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 22 Apr 2017 09:23:43 +0900 Subject: [PATCH 08/17] fix delete release action. --- .../gitbucket/core/controller/ReleasesController.scala | 5 ++--- src/main/twirl/gitbucket/core/releases/release.scala.html | 8 ++------ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/ReleasesController.scala b/src/main/scala/gitbucket/core/controller/ReleasesController.scala index ad827979b..6690e31b4 100644 --- a/src/main/scala/gitbucket/core/controller/ReleasesController.scala +++ b/src/main/scala/gitbucket/core/controller/ReleasesController.scala @@ -88,10 +88,9 @@ trait ReleaseControllerBase extends ControllerBase { redirect(s"/${release.userName}/${release.repositoryName}/releases/${release.releaseId}") }) - get("/:owner/:repository/release/delete/:id")(writableUsersOnly { repository => + ajaxPost("/:owner/:repository/releases/delete/:id")(writableUsersOnly { repository => deleteRelease(repository.owner, repository.name, params("id")) - - redirect(s"/${repository.owner}/${repository.name}/releases") + org.json4s.jackson.Serialization.write(Map("message" -> "ok")) }) ajaxPost("/:owner/:repository/releases/edit_title/:id", releaseTitleEditForm)(writableUsersOnly { (title, repository) => diff --git a/src/main/twirl/gitbucket/core/releases/release.scala.html b/src/main/twirl/gitbucket/core/releases/release.scala.html index 3cadc3a7f..e3504f5c8 100644 --- a/src/main/twirl/gitbucket/core/releases/release.scala.html +++ b/src/main/twirl/gitbucket/core/releases/release.scala.html @@ -125,13 +125,9 @@ $(function(){ }); $('#delete-release').click(function(){ if(confirm('Are you sure you want to delete this?')) { - var id = $(this).closest('a').data('comment-id'); - $.post('@helpers.url(repository)/issue_comments/delete/' + id, + $.post('@helpers.url(repository)/releases/delete/' + @release.releaseId, function(data){ - if(data > 0) { - $('#comment-' + id).prev('div.issue-avatar-image').remove(); - $('#comment-' + id).remove(); - } + location.href = "@helpers.url(repository)/releases"; }); } return false; From 3a8b93d44a5c6bbc1003c704972ce377e7ea0b6d Mon Sep 17 00:00:00 2001 From: KOUNOIKE Yuusuke Date: Sat, 22 Apr 2017 10:33:30 +0900 Subject: [PATCH 09/17] Delete asset entry and files. --- .../core/controller/ReleasesController.scala | 28 +++++++++++++++++-- .../core/service/ReleaseService.scala | 6 +++- .../gitbucket/core/releases/list.scala.html | 24 +++++++++------- .../core/releases/release.scala.html | 13 +++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/src/main/scala/gitbucket/core/controller/ReleasesController.scala b/src/main/scala/gitbucket/core/controller/ReleasesController.scala index 6690e31b4..c1784cbcb 100644 --- a/src/main/scala/gitbucket/core/controller/ReleasesController.scala +++ b/src/main/scala/gitbucket/core/controller/ReleasesController.scala @@ -1,13 +1,16 @@ package gitbucket.core.controller -import gitbucket.core.service.{RepositoryService, AccountService, ReleaseService, ActivityService} -import gitbucket.core.util.{ReferrerAuthenticator, ReadableUsersAuthenticator, WritableUsersAuthenticator, FileUtil, Notifier} +import java.io.File + +import gitbucket.core.service.{AccountService, ActivityService, ReleaseService, RepositoryService} +import gitbucket.core.util.{FileUtil, Notifier, ReadableUsersAuthenticator, ReferrerAuthenticator, WritableUsersAuthenticator} import gitbucket.core.util.Directory._ import gitbucket.core.util.Implicits._ import gitbucket.core.util.SyntaxSugars._ import gitbucket.core.view.Markdown import io.github.gitbucket.scalatra.forms._ import gitbucket.core.releases.html +import org.apache.commons.io.FileUtils class ReleaseController extends ReleaseControllerBase with RepositoryService @@ -68,11 +71,25 @@ trait ReleaseControllerBase extends ControllerBase { 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 java.io.File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}", fileId))) + Some(RawData(FileUtil.getMimeType(asset.label), new File(getReleaseFilesDir(repository.owner, repository.name) + s"/${release.releaseId}", 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) => @@ -89,6 +106,11 @@ trait ReleaseControllerBase extends ControllerBase { }) 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")) }) diff --git a/src/main/scala/gitbucket/core/service/ReleaseService.scala b/src/main/scala/gitbucket/core/service/ReleaseService.scala index f70c1c0a3..8d556ecdf 100644 --- a/src/main/scala/gitbucket/core/service/ReleaseService.scala +++ b/src/main/scala/gitbucket/core/service/ReleaseService.scala @@ -49,10 +49,14 @@ 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 = { Releases insert Release( diff --git a/src/main/twirl/gitbucket/core/releases/list.scala.html b/src/main/twirl/gitbucket/core/releases/list.scala.html index 11e5b5b34..7f2ed3167 100644 --- a/src/main/twirl/gitbucket/core/releases/list.scala.html +++ b/src/main/twirl/gitbucket/core/releases/list.scala.html @@ -13,21 +13,21 @@ hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context @repository.tags.reverse.map{ tag => - @tagReleaseMap.get(tag.name).map{rel => + @tagReleaseMap.get(tag.name).map{release =>
-

@rel.name

+

@release.name

- @helpers.avatar(rel.author, 20) - @helpers.user(rel.author, styleClass="username strong") - released this @gitbucket.core.helper.html.datetimeago(rel.registeredDate) + @helpers.avatar(release.author, 20) + @helpers.user(release.author, styleClass="username strong") + released this @gitbucket.core.helper.html.datetimeago(release.registeredDate)
@helpers.markdown( - markdown = rel.content getOrElse "No description provided.", + markdown = release.content getOrElse "No description provided.", repository = repository, enableWikiLink = false, enableRefsLink = true, @@ -38,13 +38,17 @@ hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context

Downloads

    - @releaseAssetsMap(rel).map{ asset => -
  • @asset.label
  • + @releaseAssetsMap(release).map{ asset => +
  • + + @asset.label + @helpers.readableSize(Some(asset.size)) +
  • }
diff --git a/src/main/twirl/gitbucket/core/releases/release.scala.html b/src/main/twirl/gitbucket/core/releases/release.scala.html index e3504f5c8..505a666cf 100644 --- a/src/main/twirl/gitbucket/core/releases/release.scala.html +++ b/src/main/twirl/gitbucket/core/releases/release.scala.html @@ -66,6 +66,9 @@ repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit co @asset.label @helpers.readableSize(Some(asset.size)) + @if(hasWritePermission){ + Delete + } } @@ -79,6 +82,16 @@ repository: gitbucket.core.service.RepositoryService.RepositoryInfo)(implicit co
diff --git a/src/main/twirl/gitbucket/core/releases/form.scala.html b/src/main/twirl/gitbucket/core/releases/form.scala.html new file mode 100644 index 000000000..7dacfabd1 --- /dev/null +++ b/src/main/twirl/gitbucket/core/releases/form.scala.html @@ -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){ + +
+
+ @if(release.isEmpty){ +

New release for @tag

+ } else { +

Update release for @tag

+ } + + + @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" + ) +
    + @release.map { case (release, assets) => + @assets.map { asset => +
  • + @asset.label + (remove) + +
  • + } + } +
+
+
Attach release files by dragging & dropping, or selecting them.
+
+
+ @if(release.isEmpty){ + + } else { + + } +
+
+
+ + } +} + diff --git a/src/main/twirl/gitbucket/core/releases/list.scala.html b/src/main/twirl/gitbucket/core/releases/list.scala.html index 95f42bf04..ed8f4a2b8 100644 --- a/src/main/twirl/gitbucket/core/releases/list.scala.html +++ b/src/main/twirl/gitbucket/core/releases/list.scala.html @@ -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){ - - - - - - @releases.map{ release => - - -
@releases.length releases
- -
-

@release.name

-
-
- - @helpers.avatar(release.author, 20) - @helpers.user(release.author, styleClass="username strong") - released this @gitbucket.core.helper.html.datetimeago(release.registeredDate) - -
-
- @helpers.markdown( - markdown = release.content getOrElse "No description provided.", - repository = repository, - enableWikiLink = false, - enableRefsLink = true, - enableLineBreaks = true, - enableTaskList = true, - hasWritePermission = hasWritePermission - ) -
-

Downloads

-
    - @releaseAssetsMap(release).map{ asset => -
  • - - @asset.label - @helpers.readableSize(Some(asset.size)) -
  • - } -
- -
-
+ @gitbucket.core.html.menu("releases", repository){ + + + + + + @releases.map { case (tag, release) => + + + } - - + +
@releases.length releases
+
+ @tag.name
+ @tag.id.substring(0, 7)
+ @gitbucket.core.helper.html.datetimeago(tag.time) +
+
+
+ @release.map { case (release, assets) => +

@release.name

+

+ @helpers.avatar(release.author, 20) @helpers.user(release.author, styleClass="username") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate) +

+ @helpers.markdown( + markdown = release.content getOrElse "No description provided.", + repository = repository, + enableWikiLink = false, + enableRefsLink = true, + enableLineBreaks = true, + enableTaskList = true, + hasWritePermission = hasWritePermission + ) + }.getOrElse { + @if(hasWritePermission){ + + } + } +

Downloads

+ +
+
+
} -
} diff --git a/src/main/twirl/gitbucket/core/releases/release.scala.html b/src/main/twirl/gitbucket/core/releases/release.scala.html index a3db25274..369ee507b 100644 --- a/src/main/twirl/gitbucket/core/releases/release.scala.html +++ b/src/main/twirl/gitbucket/core/releases/release.scala.html @@ -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){ -
- -
-
-
- @if(hasWritePermission){ - Edit title - Delete - } -
- -

- - @release.name - - -

-
-
-
- - @helpers.avatar(release.author, 20) - @helpers.user(release.author, styleClass="username strong") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate) - - - @if(hasWritePermission){ -   + @gitbucket.core.html.menu("releases", repository){ +
+
+ @defining(repository.tags.find(_.name == release.tag)){ tag => + @tag.map { tag => + @tag.name
+ @tag.id.substring(0, 7)
+ @gitbucket.core.helper.html.datetimeago(tag.time) } - -
-
- @helpers.markdown( - markdown = release.content getOrElse "No description provided.", - repository = repository, - enableWikiLink = false, - enableRefsLink = true, - enableLineBreaks = true, - enableTaskList = true, - hasWritePermission = hasWritePermission - ) -
-
-

Downloads

- @if(hasWritePermission){ -
-
Attach release files by dragging & dropping, or selecting them.
-
} -
    - @assets.map{ asset => -
  • - - @asset.label - @helpers.readableSize(Some(asset.size)) +
+
+
+

+ @release.name @if(hasWritePermission){ - Delete +
+
+ Edit + +
+
} - - } - - +

+

+ @helpers.avatar(release.author, 20) @helpers.user(release.author, styleClass="username") released this @gitbucket.core.helper.html.datetimeago(release.registeredDate) +

+ @helpers.markdown( + markdown = release.content getOrElse "No description provided.", + repository = repository, + enableWikiLink = false, + enableRefsLink = true, + enableLineBreaks = true, + enableTaskList = true, + hasWritePermission = hasWritePermission + ) +

Downloads

+ +
-
-
+ } +} -} -} diff --git a/src/main/twirl/gitbucket/core/repo/tags.scala.html b/src/main/twirl/gitbucket/core/repo/tags.scala.html index 16e4d3422..7dd88f8a5 100644 --- a/src/main/twirl/gitbucket/core/repo/tags.scala.html +++ b/src/main/twirl/gitbucket/core/repo/tags.scala.html @@ -27,11 +27,11 @@ Release }.getOrElse{ @if(hasWritePermission){ - - }else{ -
+ + } else { +
} } From e80da63515a7df2e2818efab243b865688e8a57a Mon Sep 17 00:00:00 2001 From: Naoki Takezoe Date: Sat, 13 Jan 2018 16:19:02 +0900 Subject: [PATCH 17/17] Merge tags and releases --- .../RepositoryViewerController.scala | 2 +- .../core/service/RepositoryService.scala | 22 ++++------ src/main/twirl/gitbucket/core/menu.scala.html | 3 +- .../twirl/gitbucket/core/repo/tags.scala.html | 43 ------------------- 4 files changed, 11 insertions(+), 59 deletions(-) delete mode 100644 src/main/twirl/gitbucket/core/repo/tags.scala.html diff --git a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala index 2361b9188..3a3b3b039 100644 --- a/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala +++ b/src/main/scala/gitbucket/core/controller/RepositoryViewerController.scala @@ -628,7 +628,7 @@ trait RepositoryViewerControllerBase extends ControllerBase { * Displays tags. */ get("/:owner/:repository/tags")(referrersOnly { repository => - html.tags(repository, hasDeveloperRole(repository.owner, repository.name, context.loginAccount)) + redirect(s"${repository.owner}/${repository.name}/releases") }) /** diff --git a/src/main/scala/gitbucket/core/service/RepositoryService.scala b/src/main/scala/gitbucket/core/service/RepositoryService.scala index 7b44eb58c..20448c8ab 100644 --- a/src/main/scala/gitbucket/core/service/RepositoryService.scala +++ b/src/main/scala/gitbucket/core/service/RepositoryService.scala @@ -229,10 +229,6 @@ trait RepositoryService { self: AccountService => t.byRepository(repository.userName, repository.repositoryName) && (t.closed === false.bind) }.map(_.pullRequest).list - val releases = Releases.filter { t => - t.byRepository(repository.userName, repository.repositoryName) - }.list - new RepositoryInfo( JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName), repository, @@ -242,7 +238,6 @@ trait RepositoryService { self: AccountService => repository.originUserName.getOrElse(repository.userName), repository.originRepositoryName.getOrElse(repository.repositoryName) ), - releases.length, getRepositoryManagers(repository.userName)) } } @@ -515,20 +510,20 @@ trait RepositoryService { self: AccountService => object RepositoryService { case class RepositoryInfo(owner: String, name: String, repository: Repository, - issueCount: Int, pullCount: Int, forkedCount: Int, releaseCount: Int, + issueCount: Int, pullCount: Int, forkedCount: Int, branchList: Seq[String], tags: Seq[JGitUtil.TagInfo], managers: Seq[String]) { /** * Creates instance with issue count and pull request count. */ - def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, releaseCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, model, issueCount, pullCount, forkedCount, releaseCount, repo.branchList, repo.tags, managers) + def this(repo: JGitUtil.RepositoryInfo, model: Repository, issueCount: Int, pullCount: Int, forkedCount: Int, managers: Seq[String]) = + this(repo.owner, repo.name, model, issueCount, pullCount, forkedCount, repo.branchList, repo.tags, managers) /** - * Creates instance without issue, pull request and release count. + * Creates instance without issue and pull request count. */ def this(repo: JGitUtil.RepositoryInfo, model: Repository, forkedCount: Int, managers: Seq[String]) = - this(repo.owner, repo.name, model, 0, 0, forkedCount, 0, repo.branchList, repo.tags, managers) + this(repo.owner, repo.name, model, 0, 0, forkedCount, repo.branchList, repo.tags, managers) def httpUrl(implicit context: Context): String = RepositoryService.httpUrl(owner, name) def sshUrl(implicit context: Context): Option[String] = RepositoryService.sshUrl(owner, name) @@ -542,9 +537,10 @@ object RepositoryService { (id, path.substring(id.length).stripPrefix("/")) } - def getReleaseByTag(tag: String)(implicit s: Session): Option[Release] = { - Releases filter (_.byTag(owner, name, tag)) firstOption - } + +// def getReleaseByTag(tag: String)(implicit s: Session): Option[Release] = { +// Releases filter (_.byTag(owner, name, tag)) firstOption +// } } def httpUrl(owner: String, name: String)(implicit context: Context): String = s"${context.baseUrl}/git/${owner}/${name}.git" diff --git a/src/main/twirl/gitbucket/core/menu.scala.html b/src/main/twirl/gitbucket/core/menu.scala.html index 5fc9f78e5..04d9de031 100644 --- a/src/main/twirl/gitbucket/core/menu.scala.html +++ b/src/main/twirl/gitbucket/core/menu.scala.html @@ -33,9 +33,8 @@ @menuitem("", "files", "Files", "code") @if(repository.branchList.nonEmpty) { @menuitem("/branches", "branches", "Branches", "git-branch", repository.branchList.length) - @menuitem("/tags", "tags", "Tags", "tag", repository.tags.length) } - @menuitem("/releases", "releases", "Releases", "gift", repository.releaseCount) + @menuitem("/releases", "releases", "Releases", "tag", repository.tags.length) @if(repository.repository.options.issuesOption != "DISABLE") { @menuitem("/issues", "issues", "Issues", "issue-opened", repository.issueCount) @menuitem("/pulls", "pulls", "Pull requests", "git-pull-request", repository.pullCount) diff --git a/src/main/twirl/gitbucket/core/repo/tags.scala.html b/src/main/twirl/gitbucket/core/repo/tags.scala.html deleted file mode 100644 index 7dd88f8a5..000000000 --- a/src/main/twirl/gitbucket/core/repo/tags.scala.html +++ /dev/null @@ -1,43 +0,0 @@ -@(repository: gitbucket.core.service.RepositoryService.RepositoryInfo, hasWritePermission: Boolean)(implicit context: gitbucket.core.controller.Context, s: gitbucket.core.model.Profile.profile.blockingApi.Session) -@import gitbucket.core.view.helpers -@gitbucket.core.html.main(s"${repository.owner}/${repository.name}", Some(repository)) { - @gitbucket.core.html.menu("tags", repository){ - - - - - - - - - - - - @repository.tags.reverseMap { tag => - - - - - - - - } - -
TagDateCommitDownloadRelease
@tag.name@gitbucket.core.helper.html.datetimeago(tag.time, false)@tag.id.substring(0, 10) - ZIP - TAR.GZ - - @repository.getReleaseByTag(tag.name).map{ release => - Release - }.getOrElse{ - @if(hasWritePermission){ - - } else { -
- } - } -
- } -}