Newer
Older
GitBucket / src / main / scala / gitbucket / core / controller / ReleasesController.scala
package gitbucket.core.controller

import java.io.File

import gitbucket.core.service.{AccountService, ActivityService, ReleaseService, RepositoryService}
import gitbucket.core.util.{FileUtil, ReadableUsersAuthenticator, ReferrerAuthenticator, WritableUsersAuthenticator}
import gitbucket.core.util.Directory._
import gitbucket.core.util.Implicits._
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
  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 ReleaseForm(
    name: String,
    content: Option[String]
  )

  val releaseForm = mapping(
    "name"    -> trim(text(required)),
    "content" -> trim(optional(text()))
  )(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,
      repository.tags.reverse.map { tag =>
        (tag, releases.find(_.tag == tag.name).map { release => (release, assets(release)) })
      },
      hasDeveloperRole(repository.owner, repository.name, context.loginAccount))
  })

  get("/:owner/:repository/releases/:tag")(referrersOnly { repository =>
    val tag = params("tag")
    getRelease(repository.owner, repository.name, tag).map { release =>
      html.release(release, getReleaseAssets(repository.owner, repository.name, tag), hasDeveloperRole(repository.owner, repository.name, context.loginAccount), repository)
    }.getOrElse(NotFound())
  })

  get("/:owner/:repository/releases/:tag/assets/:fileId")(referrersOnly {repository =>
    val tag = params("tag")
    val fileId = params("fileId")
    (for {
      _     <- repository.tags.find(_.name == tag)
      _     <- getRelease(repository.owner, repository.name, tag)
      asset <- getReleaseAsset(repository.owner, repository.name, tag, fileId)
    } yield {
      response.setHeader("Content-Disposition", s"attachment; filename=${asset.label}")
      RawData(
        FileUtil.getMimeType(asset.label),
        new File(getReleaseFilesDir(repository.owner, repository.name), tag + "/" + fileId)
      )
    }).getOrElse(NotFound())
  })

  get("/:owner/:repository/releases/:tag/create")(writableUsersOnly {repository =>
    html.form(repository, params("tag"), None)
  })

  post("/:owner/:repository/releases/:tag/create", releaseForm)(writableUsersOnly { (form, repository) =>
    val tag = params("tag")
    val loginAccount = context.loginAccount.get

    // Insert into 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, tag, fileId, fileName, size, loginAccount)
    }

    recordReleaseActivity(repository.owner, repository.name, loginAccount.userName, form.name)

    redirect(s"/${repository.owner}/${repository.name}/releases/${tag}")
  })

  get("/:owner/:repository/releases/:tag/edit")(writableUsersOnly {repository =>
    val tag = params("tag")

    getRelease(repository.owner, repository.name, tag).map { release =>
      html.form(repository, release.tag, Some(release, getReleaseAssets(repository.owner, repository.name, tag)))
    }.getOrElse(NotFound())
  })

  post("/:owner/:repository/releases/:tag/edit", releaseForm)(writableUsersOnly { (form, repository) =>
    val tag = params("tag")
    val loginAccount = context.loginAccount.get

    getRelease(repository.owner, repository.name, tag).map { release =>
      // Update RELEASE
      updateRelease(repository.owner, repository.name, tag, form.name, form.content)

      // Delete and Insert RELEASE_ASSET
      val assets = getReleaseAssets(repository.owner, repository.name, tag)
      deleteReleaseAssets(repository.owner, repository.name, tag)

      val fileIds = request.getParameterNames.asScala.filter(_.startsWith("file:")).map { 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, tag, fileId, fileName, size, loginAccount)
        fileId
      }

      assets.foreach { asset =>
        if(!fileIds.contains(asset.fileName)){
          val file = new java.io.File(getReleaseFilesDir(repository.owner, repository.name), release.tag + "/" + asset.fileName)
          FileUtils.forceDelete(file)
        }
      }

      redirect(s"/${release.userName}/${release.repositoryName}/releases/${tag}")
    }.getOrElse(NotFound())
  })

  post("/:owner/:repository/releases/:tag/delete")(writableUsersOnly { repository =>
    val tag = params("tag")
    getRelease(repository.owner, repository.name, tag).foreach { release =>
      FileUtils.deleteDirectory(new File(getReleaseFilesDir(repository.owner, repository.name), release.tag))
    }
    deleteRelease(repository.owner, repository.name, tag)
    redirect(s"/${repository.owner}/${repository.name}/releases")
  })

}