mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-06 13:35:50 +01:00
(refs #4)Add 'Public Activity' tab to the account information page.
This commit is contained in:
12
src/main/resources/update/1_2.sql
Normal file
12
src/main/resources/update/1_2.sql
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
CREATE TABLE ACTIVITY(
|
||||||
|
ACTIVITY_ID INT AUTO_INCREMENT,
|
||||||
|
USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
||||||
|
ACTIVITY_USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
MESSAGE TEXT NOT NULL,
|
||||||
|
ACTIVITY_DATE TIMESTAMP NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE ACTIVITY ADD CONSTRAINT IDX_ACTIVITY_PK PRIMARY KEY (ACTIVITY_ID);
|
||||||
|
ALTER TABLE ACTIVITY ADD CONSTRAINT IDX_ACTIVITY_FK0 FOREIGN KEY (USER_NAME, REPOSITORY_NAME) REFERENCES REPOSITORY (USER_NAME, REPOSITORY_NAME);
|
||||||
|
ALTER TABLE ACTIVITY ADD CONSTRAINT IDX_ACTIVITY_FK1 FOREIGN KEY (ACTIVITY_USER_NAME) REFERENCES ACCOUNT (USER_NAME);
|
||||||
@@ -6,10 +6,12 @@ import util.StringUtil._
|
|||||||
import jp.sf.amateras.scalatra.forms._
|
import jp.sf.amateras.scalatra.forms._
|
||||||
|
|
||||||
class AccountController extends AccountControllerBase
|
class AccountController extends AccountControllerBase
|
||||||
with SystemSettingsService with AccountService with RepositoryService with OneselfAuthenticator
|
with SystemSettingsService with AccountService with RepositoryService with ActivityService
|
||||||
|
with OneselfAuthenticator
|
||||||
|
|
||||||
trait AccountControllerBase extends ControllerBase {
|
trait AccountControllerBase extends ControllerBase {
|
||||||
self: SystemSettingsService with AccountService with RepositoryService with OneselfAuthenticator =>
|
self: SystemSettingsService with AccountService with RepositoryService with ActivityService
|
||||||
|
with OneselfAuthenticator =>
|
||||||
|
|
||||||
case class AccountNewForm(userName: String, password: String,mailAddress: String, url: Option[String])
|
case class AccountNewForm(userName: String, password: String,mailAddress: String, url: Option[String])
|
||||||
|
|
||||||
@@ -33,8 +35,13 @@ trait AccountControllerBase extends ControllerBase {
|
|||||||
*/
|
*/
|
||||||
get("/:userName") {
|
get("/:userName") {
|
||||||
val userName = params("userName")
|
val userName = params("userName")
|
||||||
getAccountByUserName(userName).map {
|
getAccountByUserName(userName).map { x =>
|
||||||
account.html.info(_, getVisibleRepositories(userName, baseUrl, context.loginAccount.map(_.userName)))
|
params.getOrElse("tab", "repositories") match {
|
||||||
|
// Public Activity
|
||||||
|
case "activity" => account.html.activity(x, getActivitiesByUser(userName, true))
|
||||||
|
// Repositories
|
||||||
|
case _ => account.html.repositories(x, getVisibleRepositories(userName, baseUrl, context.loginAccount.map(_.userName)))
|
||||||
|
}
|
||||||
} getOrElse NotFound
|
} getOrElse NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,15 @@ import org.apache.commons.io._
|
|||||||
import jp.sf.amateras.scalatra.forms._
|
import jp.sf.amateras.scalatra.forms._
|
||||||
|
|
||||||
class CreateRepositoryController extends CreateRepositoryControllerBase
|
class CreateRepositoryController extends CreateRepositoryControllerBase
|
||||||
with RepositoryService with AccountService with WikiService with LabelsService with UsersAuthenticator
|
with RepositoryService with AccountService with WikiService with LabelsService with ActivityService
|
||||||
|
with UsersAuthenticator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new repository.
|
* Creates new repository.
|
||||||
*/
|
*/
|
||||||
trait CreateRepositoryControllerBase extends ControllerBase {
|
trait CreateRepositoryControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with WikiService with LabelsService with UsersAuthenticator =>
|
self: RepositoryService with WikiService with LabelsService with ActivityService
|
||||||
|
with UsersAuthenticator =>
|
||||||
|
|
||||||
case class RepositoryCreationForm(name: String, description: Option[String])
|
case class RepositoryCreationForm(name: String, description: Option[String])
|
||||||
|
|
||||||
@@ -36,7 +38,8 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
* Create new repository.
|
* Create new repository.
|
||||||
*/
|
*/
|
||||||
post("/new", form)(usersOnly { form =>
|
post("/new", form)(usersOnly { form =>
|
||||||
val loginUserName = context.loginAccount.get.userName
|
val loginAccount = context.loginAccount.get
|
||||||
|
val loginUserName = loginAccount.userName
|
||||||
|
|
||||||
// Insert to the database at first
|
// Insert to the database at first
|
||||||
createRepository(form.name, loginUserName, form.description)
|
createRepository(form.name, loginUserName, form.description)
|
||||||
@@ -82,7 +85,10 @@ trait CreateRepositoryControllerBase extends ControllerBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create Wiki repository
|
// Create Wiki repository
|
||||||
createWikiRepository(context.loginAccount.get, form.name)
|
createWikiRepository(loginAccount, form.name)
|
||||||
|
|
||||||
|
// Record activity
|
||||||
|
recordCreateRepository(loginUserName, form.name, loginUserName)
|
||||||
|
|
||||||
// redirect to the repository
|
// redirect to the repository
|
||||||
redirect("/%s/%s".format(loginUserName, form.name))
|
redirect("/%s/%s".format(loginUserName, form.name))
|
||||||
|
|||||||
21
src/main/scala/model/Activity.scala
Normal file
21
src/main/scala/model/Activity.scala
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import scala.slick.driver.H2Driver.simple._
|
||||||
|
|
||||||
|
object Activities extends Table[Activity]("ACTIVITY") with BasicTemplate with Functions {
|
||||||
|
def activityId = column[Int]("ACTIVITY_ID", O AutoInc)
|
||||||
|
def activityUserName = column[String]("ACTIVITY_USER_NAME")
|
||||||
|
def message = column[String]("MESSAGE")
|
||||||
|
def activityDate = column[java.util.Date]("ACTIVITY_DATE")
|
||||||
|
def * = activityId ~ userName ~ repositoryName ~ activityUserName ~ message ~ activityDate <> (Activity, Activity.unapply _)
|
||||||
|
def autoInc = userName ~ repositoryName ~ activityUserName ~ message ~ activityDate returning activityId
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Activity(
|
||||||
|
activityId: Int,
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
activityUserName: String,
|
||||||
|
message: String,
|
||||||
|
activityDate: java.util.Date
|
||||||
|
)
|
||||||
30
src/main/scala/service/ActivityService.scala
Normal file
30
src/main/scala/service/ActivityService.scala
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import model._
|
||||||
|
import Activities._
|
||||||
|
import scala.slick.driver.H2Driver.simple._
|
||||||
|
import Database.threadLocalSession
|
||||||
|
|
||||||
|
trait ActivityService {
|
||||||
|
|
||||||
|
def getActivitiesByUser(activityUserName: String, isPublic: Boolean): List[Activity] = {
|
||||||
|
val q = Query(Activities)
|
||||||
|
.innerJoin(Repositories).on((t1, t2) => t1.byRepository(t2.userName, t2.repositoryName))
|
||||||
|
|
||||||
|
(if(isPublic){
|
||||||
|
q filter { case (t1, t2) => (t1.activityUserName is activityUserName.bind) && (t2.isPrivate is false.bind) }
|
||||||
|
} else {
|
||||||
|
q filter { case (t1, t2) => t1.activityUserName is activityUserName.bind }
|
||||||
|
})
|
||||||
|
.sortBy { case (t1, t2) => t1.activityId desc }
|
||||||
|
.map { case (t1, t2) => t1 }
|
||||||
|
.list
|
||||||
|
}
|
||||||
|
|
||||||
|
def recordCreateRepository(userName: String, repositoryName: String, activityUserName: String): Unit = {
|
||||||
|
Activities.autoInc insert(userName, repositoryName, activityUserName,
|
||||||
|
"[[%s]] created [[%s/%s]]".format(activityUserName, userName, repositoryName),
|
||||||
|
currentDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ object AutoUpdate {
|
|||||||
* The history of versions. A head of this sequence is the current BitBucket version.
|
* The history of versions. A head of this sequence is the current BitBucket version.
|
||||||
*/
|
*/
|
||||||
val versions = Seq(
|
val versions = Seq(
|
||||||
|
Version(1, 2),
|
||||||
Version(1, 1),
|
Version(1, 1),
|
||||||
Version(1, 0)
|
Version(1, 0)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ object helpers {
|
|||||||
Html(Markdown.toHtml(value, repository, enableWikiLink, enableCommitLink, enableIssueLink))
|
Html(Markdown.toHtml(value, repository, enableWikiLink, enableCommitLink, enableIssueLink))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def activityMessage(message: String)(implicit context: app.Context): Html = {
|
||||||
|
Html(message
|
||||||
|
.replaceAll("\\[\\[([^\\s]+?)/([^\\s]+?)\\]\\]", "<a href=\"%s/$1/$2\">$1/$2</a>".format(context.path))
|
||||||
|
.replaceAll("\\[\\[([^\\s]+?)\\]\\]", "<a href=\"%s/$1\">$1</a>".format(context.path))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the url to the repository.
|
* Generates the url to the repository.
|
||||||
*/
|
*/
|
||||||
@@ -61,6 +68,9 @@ object helpers {
|
|||||||
// convert commit id to link
|
// convert commit id to link
|
||||||
.replaceAll("(^|\\W)([a-f0-9]{40})(\\W|$)", "$1<a href=\"%s/%s/%s/commit/$2\">$2</a>$3").format(context.path, repository.owner, repository.name))
|
.replaceAll("(^|\\W)([a-f0-9]{40})(\\W|$)", "$1<a href=\"%s/%s/%s/commit/$2\">$2</a>$3").format(context.path, repository.owner, repository.name))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implicit conversion to add mkHtml() to Seq[Html].
|
||||||
|
*/
|
||||||
implicit def extendsHtmlSeq(seq: Seq[Html]) = new {
|
implicit def extendsHtmlSeq(seq: Seq[Html]) = new {
|
||||||
def mkHtml(separator: String) = Html(seq.mkString(separator))
|
def mkHtml(separator: String) = Html(seq.mkString(separator))
|
||||||
def mkHtml(separator: scala.xml.Elem) = Html(seq.mkString(separator.toString))
|
def mkHtml(separator: scala.xml.Elem) = Html(seq.mkString(separator.toString))
|
||||||
|
|||||||
31
src/main/twirl/account/activity.scala.html
Normal file
31
src/main/twirl/account/activity.scala.html
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
@(account: model.Account, activities: List[model.Activity])(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
@html.main(account.userName){
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span4">
|
||||||
|
<div class="block">
|
||||||
|
<div class="block-header">@account.userName</div>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<div><i class="icon-home"></i> <a href="@account.url">@account.url</a></div>
|
||||||
|
<div><i class="icon-time"></i> <span class="muted">Joined on</span> @date(account.registeredDate)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="span8">
|
||||||
|
@tab(account, "activity")
|
||||||
|
@if(activities.isEmpty){
|
||||||
|
No activity
|
||||||
|
} else {
|
||||||
|
@activities.map { activity =>
|
||||||
|
<div class="block">
|
||||||
|
<div class="muted small">@datetime(activity.activityDate)</div>
|
||||||
|
<div>@activityMessage(activity.message)</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -14,19 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
<ul class="nav nav-tabs">
|
@tab(account, "repositories")
|
||||||
<li class="active"><a href="#">Repositories</a></li>
|
|
||||||
<!--
|
|
||||||
<li><a href="#">Activity</a></li>
|
|
||||||
-->
|
|
||||||
@if(loginAccount.isDefined && loginAccount.get.userName == account.userName){
|
|
||||||
<li class="pull-right">
|
|
||||||
<div class="button-group">
|
|
||||||
<a href="@url(account.userName)/_edit" class="btn">Edit Your Profile</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
@if(repositories.isEmpty){
|
@if(repositories.isEmpty){
|
||||||
No repositories
|
No repositories
|
||||||
} else {
|
} else {
|
||||||
14
src/main/twirl/account/tab.scala.html
Normal file
14
src/main/twirl/account/tab.scala.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
@(account: model.Account, active: String)(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
<ul class="nav nav-tabs">
|
||||||
|
<li@if(active == "repositories"){ class="active"}><a href="@url(account.userName)?tab=repositories">Repositories</a></li>
|
||||||
|
<li@if(active == "activity"){ class="active"}><a href="@url(account.userName)?tab=activity">Public Activity</a></li>
|
||||||
|
@if(loginAccount.isDefined && loginAccount.get.userName == account.userName){
|
||||||
|
<li class="pull-right">
|
||||||
|
<div class="button-group">
|
||||||
|
<a href="@url(account.userName)/_edit" class="btn">Edit Your Profile</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
Reference in New Issue
Block a user