mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-11-03 03:55:58 +01:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d56d72611 | ||
|
|
527c91ff9d | ||
|
|
c58c2d6700 | ||
|
|
5518eca952 | ||
|
|
6e2b67ec0b | ||
|
|
837b1e44a7 | ||
|
|
e04c230c6e | ||
|
|
a01b5a4a59 | ||
|
|
427b6ce846 | ||
|
|
b7b5af2b72 | ||
|
|
39fec57f72 | ||
|
|
238dedb6df | ||
|
|
af091117b7 | ||
|
|
ddea4e12f0 | ||
|
|
9767903252 | ||
|
|
bc75f9f8a2 | ||
|
|
63627fc1d0 | ||
|
|
c23985c1a7 | ||
|
|
af58e99dcf | ||
|
|
676670e9e3 |
@@ -23,7 +23,6 @@ The current version of GitBucket provides a basic features below:
|
|||||||
|
|
||||||
Following features are not implemented, but we will make them in the future release!
|
Following features are not implemented, but we will make them in the future release!
|
||||||
|
|
||||||
- Comment for the changeset
|
|
||||||
- Network graph
|
- Network graph
|
||||||
- Statistics
|
- Statistics
|
||||||
- Watch / Star
|
- Watch / Star
|
||||||
@@ -80,6 +79,11 @@ Run the following commands in `Terminal` to
|
|||||||
|
|
||||||
Release Notes
|
Release Notes
|
||||||
--------
|
--------
|
||||||
|
### 2.7 - 29 Dec 2014
|
||||||
|
- Comment for commit and diff
|
||||||
|
- Fix security issue in markdown rendering
|
||||||
|
- Some bug fix and improvements
|
||||||
|
|
||||||
### 2.6 - 24 Nov 2014
|
### 2.6 - 24 Nov 2014
|
||||||
- Search box at issues and pull requests
|
- Search box at issues and pull requests
|
||||||
- Information from administrator
|
- Information from administrator
|
||||||
|
|||||||
15
contrib/linux/redhat/README.md
Normal file
15
contrib/linux/redhat/README.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Contrib Notes #
|
||||||
|
|
||||||
|
RPM spec file and init script for Red Hat Enterprise Linux 6.x.
|
||||||
|
|
||||||
|
To create RPM:
|
||||||
|
1. Edit `../../gitbucket.conf` to suit.
|
||||||
|
2. Edit `gitbucket.init` to suit.
|
||||||
|
3. Edit `gitbucket.spec` to suit.
|
||||||
|
4. Place `gitbucket.spec` to rpm/SPECS/.
|
||||||
|
5. Place `gitbucket.init` and `gitbucket.war` to rpm/SOURCES/.
|
||||||
|
6. Execute `rpmbuild -ba rpm/SPECS/gitbucket.spec`
|
||||||
|
|
||||||
|
This rpm runs gitbucket not as root user but as gitbucket user.
|
||||||
|
This rpm creates user and group named `gitbucket` at installation.
|
||||||
|
This rpm make chkconfig of gitbucket to be on.
|
||||||
108
contrib/linux/redhat/gitbucket.init
Normal file
108
contrib/linux/redhat/gitbucket.init
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# RedHat: /etc/rc.d/init.d/gitbucket
|
||||||
|
#
|
||||||
|
# Starts the GitBucket server
|
||||||
|
#
|
||||||
|
# chkconfig: 345 60 40
|
||||||
|
# description: Run GitBucket server
|
||||||
|
# processname: java
|
||||||
|
|
||||||
|
[ -f /etc/rc.d/init.d/functions ] && source /etc/rc.d/init.d/functions # RedHat
|
||||||
|
|
||||||
|
# Default values
|
||||||
|
GITBUCKET_HOME=/var/lib/gitbucket
|
||||||
|
GITBUCKET_WAR_FILE=/usr/share/gitbucket/lib/gitbucket.war
|
||||||
|
|
||||||
|
# Pull in cq settings
|
||||||
|
[ -f /etc/sysconfig/gitbucket ] && source /etc/sysconfig/gitbucket # RedHat
|
||||||
|
[ -f gitbucket.conf ] && source gitbucket.conf # For all systems
|
||||||
|
|
||||||
|
# Location of the log and PID file
|
||||||
|
LOG_FILE=$GITBUCKET_LOG_DIR/run.log
|
||||||
|
|
||||||
|
RED='\033[1m\E[37;41m'
|
||||||
|
GREEN='\033[1m\E[37;42m'
|
||||||
|
OFF='\E[0m'
|
||||||
|
|
||||||
|
RETVAL=0
|
||||||
|
|
||||||
|
start() {
|
||||||
|
echo -n $"Starting GitBucket server: "
|
||||||
|
|
||||||
|
START_OPTS=
|
||||||
|
if [ $GITBUCKET_PORT ]; then
|
||||||
|
START_OPTS="${START_OPTS} --port=${GITBUCKET_PORT}"
|
||||||
|
fi
|
||||||
|
if [ $GITBUCKET_PREFIX ]; then
|
||||||
|
START_OPTS="${START_OPTS} --prefix=${GITBUCKET_PREFIX}"
|
||||||
|
fi
|
||||||
|
if [ $GITBUCKET_HOST ]; then
|
||||||
|
START_OPTS="${START_OPTS} --host=${GITBUCKET_HOST}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
GITBUCKET_HOME="${GITBUCKET_HOME}" daemon --user=gitbucket java $GITBUCKET_JVM_OPTS -jar $GITBUCKET_WAR_FILE $START_OPTS >>$LOG_FILE 2>&1 &
|
||||||
|
sleep 3
|
||||||
|
pgrep -f $GITBUCKET_WAR_FILE >> $LOG_FILE 2>&1
|
||||||
|
RETVAL=$?
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 0 ] ; then
|
||||||
|
success "Success"
|
||||||
|
else
|
||||||
|
failure "Exit code $RETVAL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
echo -n $"Stopping GitBucket server: "
|
||||||
|
|
||||||
|
# Run the Java process
|
||||||
|
pkill -f $GITBUCKET_WAR_FILE >>$LOG_FILE 2>&1
|
||||||
|
RETVAL=$?
|
||||||
|
|
||||||
|
if [ $RETVAL -eq 0 ] ; then
|
||||||
|
success "GitBucket stopping"
|
||||||
|
else
|
||||||
|
failure "GitBucket stopping"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
return $RETVAL
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
restart() {
|
||||||
|
stop
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
start)
|
||||||
|
start
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
stop
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
restart
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
pgrep -f $GITBUCKET_WAR_FILE >> $LOG_FILE 2>&1
|
||||||
|
RETVAL=$?
|
||||||
|
if [ $RETVAL -eq 0 ]; then
|
||||||
|
echo $"GitBucket is running...."
|
||||||
|
else
|
||||||
|
echo $"GitBucket is stopped"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo $"Usage: $0 [start|stop|restart|status]"
|
||||||
|
RETVAL=2
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit $RETVAL
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: gitbucket
|
Name: gitbucket
|
||||||
Summary: GitHub clone written with Scala.
|
Summary: GitHub clone written with Scala.
|
||||||
Version: 1.7
|
Version: 2.6
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
License: Apache
|
License: Apache
|
||||||
URL: https://github.com/takezoe/gitbucket
|
URL: https://github.com/takezoe/gitbucket
|
||||||
@@ -26,6 +26,25 @@ GitBucket is the easily installable GitHub clone written with Scala.
|
|||||||
%{__install} -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name}
|
%{__install} -m 0644 %{SOURCE2} %{buildroot}%{_sysconfdir}/sysconfig/%{name}
|
||||||
touch %{buildroot}%{_localstatedir}/log/%{name}/run.log
|
touch %{buildroot}%{_localstatedir}/log/%{name}/run.log
|
||||||
|
|
||||||
|
%pre
|
||||||
|
/usr/sbin/groupadd -r gitbucket &> /dev/null || :
|
||||||
|
/usr/sbin/useradd -g gitbucket -s /bin/false -r -c "GitBucket GitHub clone" -d %{_sharedstatedir}/%{name} gitbucket &> /dev/null || :
|
||||||
|
|
||||||
|
%post
|
||||||
|
/sbin/chkconfig --add gitbucket
|
||||||
|
|
||||||
|
%preun
|
||||||
|
if [ "$1" = 0 ]; then
|
||||||
|
/sbin/service gitbucket stop > /dev/null 2>&1
|
||||||
|
/sbin/chkconfig --del gitbucket
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
%postun
|
||||||
|
if [ "$1" -ge 1 ]; then
|
||||||
|
/sbin/service gitbucket restart > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
|
||||||
%clean
|
%clean
|
||||||
[ "%{buildroot}" != / ] && %{__rm} -rf "%{buildroot}"
|
[ "%{buildroot}" != / ] && %{__rm} -rf "%{buildroot}"
|
||||||
@@ -34,12 +53,28 @@ touch %{buildroot}%{_localstatedir}/log/%{name}/run.log
|
|||||||
%files
|
%files
|
||||||
%defattr(-,root,root,-)
|
%defattr(-,root,root,-)
|
||||||
%{_datarootdir}/%{name}/lib/%{name}.war
|
%{_datarootdir}/%{name}/lib/%{name}.war
|
||||||
%{_sysconfdir}/init.d/%{name}
|
%config %{_sysconfdir}/init.d/%{name}
|
||||||
%config %{_sysconfdir}/sysconfig/%{name}
|
%config(noreplace) %{_sysconfdir}/sysconfig/%{name}
|
||||||
%{_localstatedir}/log/%{name}/run.log
|
%attr(0755,gitbucket,gitbucket) %{_sharedstatedir}/%{name}
|
||||||
|
%attr(0750,gitbucket,gitbucket) %{_localstatedir}/log/%{name}
|
||||||
|
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Nov 24 2014 Toru Takahashi <torutk at gmail.com>
|
||||||
|
- Version bump to v2.6
|
||||||
|
|
||||||
|
* Sun Nov 09 2014 Toru Takahashi <torutk at gmail.com>
|
||||||
|
- Version bump to v2.5
|
||||||
|
|
||||||
|
* Sun Oct 26 2014 Toru Takahashi <torutk at gmail.com>
|
||||||
|
- Version bump to v2.4.1
|
||||||
|
|
||||||
|
* Mon Jul 21 2014 Toru Takahashi <torutk at gmail.com>
|
||||||
|
- execute as gitbucket user
|
||||||
|
|
||||||
|
* Sun Jul 20 2014 Toru Takahashi <torutk at gmail.com>
|
||||||
|
- Version bump to v2.1.
|
||||||
|
|
||||||
* Mon Oct 28 2013 Jiri Tyr <jiri_DOT_tyr at gmail.com>
|
* Mon Oct 28 2013 Jiri Tyr <jiri_DOT_tyr at gmail.com>
|
||||||
- Version bump to v1.7.
|
- Version bump to v1.7.
|
||||||
|
|
||||||
|
|||||||
18
src/main/resources/update/2_7.sql
Normal file
18
src/main/resources/update/2_7.sql
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
CREATE TABLE COMMIT_COMMENT (
|
||||||
|
USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
REPOSITORY_NAME VARCHAR(100) NOT NULL,
|
||||||
|
COMMIT_ID VARCHAR(100) NOT NULL,
|
||||||
|
COMMENT_ID INT AUTO_INCREMENT,
|
||||||
|
COMMENTED_USER_NAME VARCHAR(100) NOT NULL,
|
||||||
|
CONTENT TEXT NOT NULL,
|
||||||
|
FILE_NAME NVARCHAR(100),
|
||||||
|
OLD_LINE_NUMBER INT,
|
||||||
|
NEW_LINE_NUMBER INT,
|
||||||
|
REGISTERED_DATE TIMESTAMP NOT NULL,
|
||||||
|
UPDATED_DATE TIMESTAMP NOT NULL,
|
||||||
|
PULL_REQUEST BOOLEAN NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE COMMIT_COMMENT ADD CONSTRAINT IDX_COMMIT_COMMENT_PK PRIMARY KEY (COMMENT_ID);
|
||||||
|
ALTER TABLE COMMIT_COMMENT ADD CONSTRAINT IDX_COMMIT_COMMENT_FK0 FOREIGN KEY (USER_NAME, REPOSITORY_NAME) REFERENCES REPOSITORY (USER_NAME, REPOSITORY_NAME);
|
||||||
|
ALTER TABLE COMMIT_COMMENT ADD CONSTRAINT IDX_COMMIT_COMMENT_1 UNIQUE (USER_NAME, REPOSITORY_NAME, COMMIT_ID, COMMENT_ID);
|
||||||
@@ -12,23 +12,21 @@ import scala.collection.JavaConverters._
|
|||||||
import org.eclipse.jgit.lib.{ObjectId, CommitBuilder, PersonIdent}
|
import org.eclipse.jgit.lib.{ObjectId, CommitBuilder, PersonIdent}
|
||||||
import service.IssuesService._
|
import service.IssuesService._
|
||||||
import service.PullRequestService._
|
import service.PullRequestService._
|
||||||
import util.JGitUtil.DiffInfo
|
|
||||||
import util.JGitUtil.CommitInfo
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.eclipse.jgit.merge.MergeStrategy
|
import org.eclipse.jgit.merge.MergeStrategy
|
||||||
import org.eclipse.jgit.errors.NoMergeBaseException
|
import org.eclipse.jgit.errors.NoMergeBaseException
|
||||||
import service.WebHookService.WebHookPayload
|
import service.WebHookService.WebHookPayload
|
||||||
import util.JGitUtil.DiffInfo
|
import util.JGitUtil.DiffInfo
|
||||||
import scala.Some
|
|
||||||
import util.JGitUtil.CommitInfo
|
import util.JGitUtil.CommitInfo
|
||||||
|
|
||||||
|
|
||||||
class PullRequestsController extends PullRequestsControllerBase
|
class PullRequestsController extends PullRequestsControllerBase
|
||||||
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
|
with RepositoryService with AccountService with IssuesService with PullRequestService with MilestonesService with LabelsService
|
||||||
with ActivityService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with CommitsService with ActivityService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator
|
||||||
|
|
||||||
trait PullRequestsControllerBase extends ControllerBase {
|
trait PullRequestsControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
|
self: RepositoryService with AccountService with IssuesService with MilestonesService with LabelsService
|
||||||
with ActivityService with PullRequestService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
with CommitsService with ActivityService with PullRequestService with WebHookService with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
||||||
|
|
||||||
private val logger = LoggerFactory.getLogger(classOf[PullRequestsControllerBase])
|
private val logger = LoggerFactory.getLogger(classOf[PullRequestsControllerBase])
|
||||||
|
|
||||||
@@ -81,7 +79,8 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
|
|
||||||
pulls.html.pullreq(
|
pulls.html.pullreq(
|
||||||
issue, pullreq,
|
issue, pullreq,
|
||||||
getComments(owner, name, issueId),
|
(commits.flatten.map(commit => getCommitComments(owner, name, commit.id, true)).flatten.toList ::: getComments(owner, name, issueId))
|
||||||
|
.sortWith((a, b) => a.registeredDate before b.registeredDate),
|
||||||
getIssueLabels(owner, name, issueId),
|
getIssueLabels(owner, name, issueId),
|
||||||
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
(getCollaborators(owner, name) ::: (if(getAccountByUserName(owner).get.isGroupAccount) Nil else List(owner))).sorted,
|
||||||
getMilestonesWithIssueCount(owner, name),
|
getMilestonesWithIssueCount(owner, name),
|
||||||
@@ -281,6 +280,7 @@ trait PullRequestsControllerBase extends ControllerBase {
|
|||||||
case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName)
|
case (Some(userName), Some(repositoryName)) => (userName, repositoryName) :: getForkedRepositories(userName, repositoryName)
|
||||||
case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name)
|
case _ => (forkedRepository.owner, forkedRepository.name) :: getForkedRepositories(forkedRepository.owner, forkedRepository.name)
|
||||||
},
|
},
|
||||||
|
commits.flatten.map(commit => getCommitComments(forkedRepository.owner, forkedRepository.name, commit.id, false)).flatten.toList,
|
||||||
originBranch,
|
originBranch,
|
||||||
forkedBranch,
|
forkedBranch,
|
||||||
oldId.getName,
|
oldId.getName,
|
||||||
|
|||||||
@@ -20,16 +20,16 @@ import org.eclipse.jgit.revwalk.RevCommit
|
|||||||
import service.WebHookService.WebHookPayload
|
import service.WebHookService.WebHookPayload
|
||||||
|
|
||||||
class RepositoryViewerController extends RepositoryViewerControllerBase
|
class RepositoryViewerController extends RepositoryViewerControllerBase
|
||||||
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService
|
with RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository viewer.
|
* The repository viewer.
|
||||||
*/
|
*/
|
||||||
trait RepositoryViewerControllerBase extends ControllerBase {
|
trait RepositoryViewerControllerBase extends ControllerBase {
|
||||||
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService
|
self: RepositoryService with AccountService with ActivityService with IssuesService with WebHookService with CommitsService
|
||||||
with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
with ReadableUsersAuthenticator with ReferrerAuthenticator with CollaboratorsAuthenticator =>
|
||||||
|
|
||||||
ArchiveCommand.registerFormat("zip", new ZipFormat)
|
ArchiveCommand.registerFormat("zip", new ZipFormat)
|
||||||
ArchiveCommand.registerFormat("tar.gz", new TgzFormat)
|
ArchiveCommand.registerFormat("tar.gz", new TgzFormat)
|
||||||
@@ -52,6 +52,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
fileName: String
|
fileName: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
case class CommentForm(
|
||||||
|
fileName: Option[String],
|
||||||
|
oldLineNumber: Option[Int],
|
||||||
|
newLineNumber: Option[Int],
|
||||||
|
content: String,
|
||||||
|
pullRequest: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
val editorForm = mapping(
|
val editorForm = mapping(
|
||||||
"branch" -> trim(label("Branch", text(required))),
|
"branch" -> trim(label("Branch", text(required))),
|
||||||
"path" -> trim(label("Path", text())),
|
"path" -> trim(label("Path", text())),
|
||||||
@@ -70,6 +78,14 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
"fileName" -> trim(label("Filename", text(required)))
|
"fileName" -> trim(label("Filename", text(required)))
|
||||||
)(DeleteForm.apply)
|
)(DeleteForm.apply)
|
||||||
|
|
||||||
|
val commentForm = mapping(
|
||||||
|
"fileName" -> trim(label("Filename", optional(text()))),
|
||||||
|
"oldLineNumber" -> trim(label("Old line number", optional(number()))),
|
||||||
|
"newLineNumber" -> trim(label("New line number", optional(number()))),
|
||||||
|
"content" -> trim(label("Content", text(required))),
|
||||||
|
"pullRequest" -> trim(label("In pull request", boolean()))
|
||||||
|
)(CommentForm.apply)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns converted HTML from Markdown for preview.
|
* Returns converted HTML from Markdown for preview.
|
||||||
*/
|
*/
|
||||||
@@ -221,12 +237,82 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
repo.html.commit(id, new JGitUtil.CommitInfo(revCommit),
|
repo.html.commit(id, new JGitUtil.CommitInfo(revCommit),
|
||||||
JGitUtil.getBranchesOfCommit(git, revCommit.getName),
|
JGitUtil.getBranchesOfCommit(git, revCommit.getName),
|
||||||
JGitUtil.getTagsOfCommit(git, revCommit.getName),
|
JGitUtil.getTagsOfCommit(git, revCommit.getName),
|
||||||
repository, diffs, oldCommitId)
|
getCommitComments(repository.owner, repository.name, id, false),
|
||||||
|
repository, diffs, oldCommitId, hasWritePermission(repository.owner, repository.name, context.loginAccount))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
post("/:owner/:repository/commit/:id/comment/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
|
val id = params("id")
|
||||||
|
createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName, form.content,
|
||||||
|
form.fileName, form.oldLineNumber, form.newLineNumber, form.pullRequest)
|
||||||
|
recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
|
||||||
|
redirect(s"/${repository.owner}/${repository.name}/commit/${id}")
|
||||||
|
})
|
||||||
|
|
||||||
|
ajaxGet("/:owner/:repository/commit/:id/comment/_form")(readableUsersOnly { repository =>
|
||||||
|
val id = params("id")
|
||||||
|
val fileName = params.get("fileName")
|
||||||
|
val oldLineNumber = params.get("oldLineNumber") flatMap {b => Some(b.toInt)}
|
||||||
|
val newLineNumber = params.get("newLineNumber") flatMap {b => Some(b.toInt)}
|
||||||
|
val pullRequest = params.get("pullRequest")
|
||||||
|
repo.html.commentform(
|
||||||
|
commitId = id,
|
||||||
|
fileName, oldLineNumber, newLineNumber, pullRequest.map(_.toBoolean).getOrElse(false),
|
||||||
|
hasWritePermission = hasWritePermission(repository.owner, repository.name, context.loginAccount),
|
||||||
|
repository = repository
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
ajaxPost("/:owner/:repository/commit/:id/comment/_data/new", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
|
val id = params("id")
|
||||||
|
val commentId = createCommitComment(repository.owner, repository.name, id, context.loginAccount.get.userName,
|
||||||
|
form.content, form.fileName, form.oldLineNumber, form.newLineNumber, form.pullRequest)
|
||||||
|
recordCommentCommitActivity(repository.owner, repository.name, context.loginAccount.get.userName, id, form.content)
|
||||||
|
helper.html.commitcomment(getCommitComment(repository.owner, repository.name, commentId.toString).get,
|
||||||
|
hasWritePermission(repository.owner, repository.name, context.loginAccount), repository)
|
||||||
|
})
|
||||||
|
|
||||||
|
ajaxGet("/:owner/:repository/commit_comments/_data/:id")(readableUsersOnly { repository =>
|
||||||
|
getCommitComment(repository.owner, repository.name, params("id")) map { x =>
|
||||||
|
if(isEditable(x.userName, x.repositoryName, x.commentedUserName)){
|
||||||
|
params.get("dataType") collect {
|
||||||
|
case t if t == "html" => repo.html.editcomment(
|
||||||
|
x.content, x.commentId, x.userName, x.repositoryName)
|
||||||
|
} getOrElse {
|
||||||
|
contentType = formats("json")
|
||||||
|
org.json4s.jackson.Serialization.write(
|
||||||
|
Map("content" -> view.Markdown.toHtml(x.content,
|
||||||
|
repository, false, true, true, isEditable(x.userName, x.repositoryName, x.commentedUserName))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else Unauthorized
|
||||||
|
} getOrElse NotFound
|
||||||
|
})
|
||||||
|
|
||||||
|
ajaxPost("/:owner/:repository/commit_comments/edit/:id", commentForm)(readableUsersOnly { (form, repository) =>
|
||||||
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
|
getCommitComment(owner, name, params("id")).map { comment =>
|
||||||
|
if(isEditable(owner, name, comment.commentedUserName)){
|
||||||
|
updateCommitComment(comment.commentId, form.content)
|
||||||
|
redirect(s"/${owner}/${name}/commit_comments/_data/${comment.commentId}")
|
||||||
|
} else Unauthorized
|
||||||
|
} getOrElse NotFound
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ajaxPost("/:owner/:repository/commit_comments/delete/:id")(readableUsersOnly { repository =>
|
||||||
|
defining(repository.owner, repository.name){ case (owner, name) =>
|
||||||
|
getCommitComment(owner, name, params("id")).map { comment =>
|
||||||
|
if(isEditable(owner, name, comment.commentedUserName)){
|
||||||
|
Ok(deleteCommitComment(comment.commentId))
|
||||||
|
} else Unauthorized
|
||||||
|
} getOrElse NotFound
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays branches.
|
* Displays branches.
|
||||||
*/
|
*/
|
||||||
@@ -461,4 +547,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
|
|||||||
file
|
file
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def isEditable(owner: String, repository: String, author: String)(implicit context: app.Context): Boolean =
|
||||||
|
hasWritePermission(owner, repository, context.loginAccount) || author == context.loginAccount.get.userName
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,4 +44,11 @@ protected[model] trait TemplateComponent { self: Profile =>
|
|||||||
byRepository(userName, repositoryName) && (this.milestoneId === milestoneId)
|
byRepository(userName, repositoryName) && (this.milestoneId === milestoneId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait CommitTemplate extends BasicTemplate { self: Table[_] =>
|
||||||
|
val commitId = column[String]("COMMIT_ID")
|
||||||
|
|
||||||
|
def byCommit(owner: String, repository: String, commitId: String) =
|
||||||
|
byRepository(owner, repository) && (this.commitId === commitId)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
78
src/main/scala/model/Comment.scala
Normal file
78
src/main/scala/model/Comment.scala
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
trait Comment {
|
||||||
|
val commentedUserName: String
|
||||||
|
val registeredDate: java.util.Date
|
||||||
|
}
|
||||||
|
|
||||||
|
trait IssueCommentComponent extends TemplateComponent { self: Profile =>
|
||||||
|
import profile.simple._
|
||||||
|
import self._
|
||||||
|
|
||||||
|
lazy val IssueComments = new TableQuery(tag => new IssueComments(tag)){
|
||||||
|
def autoInc = this returning this.map(_.commentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
class IssueComments(tag: Tag) extends Table[IssueComment](tag, "ISSUE_COMMENT") with IssueTemplate {
|
||||||
|
val commentId = column[Int]("COMMENT_ID", O AutoInc)
|
||||||
|
val action = column[String]("ACTION")
|
||||||
|
val commentedUserName = column[String]("COMMENTED_USER_NAME")
|
||||||
|
val content = column[String]("CONTENT")
|
||||||
|
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||||
|
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||||
|
def * = (userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
|
||||||
|
|
||||||
|
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class IssueComment (
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
issueId: Int,
|
||||||
|
commentId: Int = 0,
|
||||||
|
action: String,
|
||||||
|
commentedUserName: String,
|
||||||
|
content: String,
|
||||||
|
registeredDate: java.util.Date,
|
||||||
|
updatedDate: java.util.Date
|
||||||
|
) extends Comment
|
||||||
|
|
||||||
|
trait CommitCommentComponent extends TemplateComponent { self: Profile =>
|
||||||
|
import profile.simple._
|
||||||
|
import self._
|
||||||
|
|
||||||
|
lazy val CommitComments = new TableQuery(tag => new CommitComments(tag)){
|
||||||
|
def autoInc = this returning this.map(_.commentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommitComments(tag: Tag) extends Table[CommitComment](tag, "COMMIT_COMMENT") with CommitTemplate {
|
||||||
|
val commentId = column[Int]("COMMENT_ID", O AutoInc)
|
||||||
|
val commentedUserName = column[String]("COMMENTED_USER_NAME")
|
||||||
|
val content = column[String]("CONTENT")
|
||||||
|
val fileName = column[Option[String]]("FILE_NAME")
|
||||||
|
val oldLine = column[Option[Int]]("OLD_LINE_NUMBER")
|
||||||
|
val newLine = column[Option[Int]]("NEW_LINE_NUMBER")
|
||||||
|
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
||||||
|
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
||||||
|
val pullRequest = column[Boolean]("PULL_REQUEST")
|
||||||
|
def * = (userName, repositoryName, commitId, commentId, commentedUserName, content, fileName, oldLine, newLine, registeredDate, updatedDate, pullRequest) <> (CommitComment.tupled, CommitComment.unapply)
|
||||||
|
|
||||||
|
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class CommitComment(
|
||||||
|
userName: String,
|
||||||
|
repositoryName: String,
|
||||||
|
commitId: String,
|
||||||
|
commentId: Int = 0,
|
||||||
|
commentedUserName: String,
|
||||||
|
content: String,
|
||||||
|
fileName: Option[String],
|
||||||
|
oldLine: Option[Int],
|
||||||
|
newLine: Option[Int],
|
||||||
|
registeredDate: java.util.Date,
|
||||||
|
updatedDate: java.util.Date,
|
||||||
|
pullRequest: Boolean
|
||||||
|
) extends Comment
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
package model
|
|
||||||
|
|
||||||
trait IssueCommentComponent extends TemplateComponent { self: Profile =>
|
|
||||||
import profile.simple._
|
|
||||||
import self._
|
|
||||||
|
|
||||||
lazy val IssueComments = new TableQuery(tag => new IssueComments(tag)){
|
|
||||||
def autoInc = this returning this.map(_.commentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
class IssueComments(tag: Tag) extends Table[IssueComment](tag, "ISSUE_COMMENT") with IssueTemplate {
|
|
||||||
val commentId = column[Int]("COMMENT_ID", O AutoInc)
|
|
||||||
val action = column[String]("ACTION")
|
|
||||||
val commentedUserName = column[String]("COMMENTED_USER_NAME")
|
|
||||||
val content = column[String]("CONTENT")
|
|
||||||
val registeredDate = column[java.util.Date]("REGISTERED_DATE")
|
|
||||||
val updatedDate = column[java.util.Date]("UPDATED_DATE")
|
|
||||||
def * = (userName, repositoryName, issueId, commentId, action, commentedUserName, content, registeredDate, updatedDate) <> (IssueComment.tupled, IssueComment.unapply)
|
|
||||||
|
|
||||||
def byPrimaryKey(commentId: Int) = this.commentId === commentId.bind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case class IssueComment(
|
|
||||||
userName: String,
|
|
||||||
repositoryName: String,
|
|
||||||
issueId: Int,
|
|
||||||
commentId: Int = 0,
|
|
||||||
action: String,
|
|
||||||
commentedUserName: String,
|
|
||||||
content: String,
|
|
||||||
registeredDate: java.util.Date,
|
|
||||||
updatedDate: java.util.Date
|
|
||||||
)
|
|
||||||
@@ -22,6 +22,7 @@ object Profile extends {
|
|||||||
} with AccountComponent
|
} with AccountComponent
|
||||||
with ActivityComponent
|
with ActivityComponent
|
||||||
with CollaboratorComponent
|
with CollaboratorComponent
|
||||||
|
with CommitCommentComponent
|
||||||
with GroupMemberComponent
|
with GroupMemberComponent
|
||||||
with IssueComponent
|
with IssueComponent
|
||||||
with IssueCommentComponent
|
with IssueCommentComponent
|
||||||
|
|||||||
@@ -98,8 +98,8 @@ object PluginSystem extends PluginService {
|
|||||||
// Compile and eval Scala source code
|
// Compile and eval Scala source code
|
||||||
ScalaPlugin.eval(pluginDir.listFiles.filter(_.getName.endsWith(".scala.html")).map { file =>
|
ScalaPlugin.eval(pluginDir.listFiles.filter(_.getName.endsWith(".scala.html")).map { file =>
|
||||||
ScalaPlugin.compileTemplate(
|
ScalaPlugin.compileTemplate(
|
||||||
id.replaceAll("-", ""),
|
id.replace("-", ""),
|
||||||
file.getName.replaceAll("\\.scala\\.html$", ""),
|
file.getName.stripSuffix(".scala.html"),
|
||||||
IOUtils.toString(new FileInputStream(file)))
|
IOUtils.toString(new FileInputStream(file)))
|
||||||
}.mkString("\n") + source)
|
}.mkString("\n") + source)
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,15 @@ trait ActivityService {
|
|||||||
Some(cut(comment, 200)),
|
Some(cut(comment, 200)),
|
||||||
currentDate)
|
currentDate)
|
||||||
|
|
||||||
|
def recordCommentCommitActivity(userName: String, repositoryName: String, activityUserName: String, commitId: String, comment: String)
|
||||||
|
(implicit s: Session): Unit =
|
||||||
|
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||||
|
"comment_commit",
|
||||||
|
s"[user:${activityUserName}] commented on commit [commit:${userName}/${repositoryName}@${commitId}]",
|
||||||
|
Some(cut(comment, 200)),
|
||||||
|
currentDate
|
||||||
|
)
|
||||||
|
|
||||||
def recordCreateWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String)
|
def recordCreateWikiPageActivity(userName: String, repositoryName: String, activityUserName: String, pageName: String)
|
||||||
(implicit s: Session): Unit =
|
(implicit s: Session): Unit =
|
||||||
Activities insert Activity(userName, repositoryName, activityUserName,
|
Activities insert Activity(userName, repositoryName, activityUserName,
|
||||||
|
|||||||
52
src/main/scala/service/CommitsService.scala
Normal file
52
src/main/scala/service/CommitsService.scala
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import scala.slick.jdbc.{StaticQuery => Q}
|
||||||
|
import Q.interpolation
|
||||||
|
|
||||||
|
import model.Profile._
|
||||||
|
import profile.simple._
|
||||||
|
import model.CommitComment
|
||||||
|
import util.Implicits._
|
||||||
|
import util.StringUtil._
|
||||||
|
|
||||||
|
|
||||||
|
trait CommitsService {
|
||||||
|
|
||||||
|
def getCommitComments(owner: String, repository: String, commitId: String, pullRequest: Boolean)(implicit s: Session) =
|
||||||
|
CommitComments filter {
|
||||||
|
t => t.byCommit(owner, repository, commitId) && (t.pullRequest === pullRequest || pullRequest)
|
||||||
|
} list
|
||||||
|
|
||||||
|
def getCommitComment(owner: String, repository: String, commentId: String)(implicit s: Session) =
|
||||||
|
if (commentId forall (_.isDigit))
|
||||||
|
CommitComments filter { t =>
|
||||||
|
t.byPrimaryKey(commentId.toInt) && t.byRepository(owner, repository)
|
||||||
|
} firstOption
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
def createCommitComment(owner: String, repository: String, commitId: String, loginUser: String,
|
||||||
|
content: String, fileName: Option[String], oldLine: Option[Int], newLine: Option[Int], pullRequest: Boolean)(implicit s: Session): Int =
|
||||||
|
CommitComments.autoInc insert CommitComment(
|
||||||
|
userName = owner,
|
||||||
|
repositoryName = repository,
|
||||||
|
commitId = commitId,
|
||||||
|
commentedUserName = loginUser,
|
||||||
|
content = content,
|
||||||
|
fileName = fileName,
|
||||||
|
oldLine = oldLine,
|
||||||
|
newLine = newLine,
|
||||||
|
registeredDate = currentDate,
|
||||||
|
updatedDate = currentDate,
|
||||||
|
pullRequest = pullRequest)
|
||||||
|
|
||||||
|
def updateCommitComment(commentId: Int, content: String)(implicit s: Session) =
|
||||||
|
CommitComments
|
||||||
|
.filter (_.byPrimaryKey(commentId))
|
||||||
|
.map { t =>
|
||||||
|
t.content -> t.updatedDate
|
||||||
|
}.update (content, currentDate)
|
||||||
|
|
||||||
|
def deleteCommitComment(commentId: Int)(implicit s: Session) =
|
||||||
|
CommitComments filter (_.byPrimaryKey(commentId)) delete
|
||||||
|
}
|
||||||
@@ -263,6 +263,7 @@ trait IssuesService {
|
|||||||
|
|
||||||
// Search Issue
|
// Search Issue
|
||||||
val issues = Issues
|
val issues = Issues
|
||||||
|
.filter(_.byRepository(owner, repository))
|
||||||
.innerJoin(IssueOutline).on { case (t1, t2) =>
|
.innerJoin(IssueOutline).on { case (t1, t2) =>
|
||||||
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
||||||
}
|
}
|
||||||
@@ -278,6 +279,7 @@ trait IssuesService {
|
|||||||
|
|
||||||
// Search IssueComment
|
// Search IssueComment
|
||||||
val comments = IssueComments
|
val comments = IssueComments
|
||||||
|
.filter(_.byRepository(owner, repository))
|
||||||
.innerJoin(Issues).on { case (t1, t2) =>
|
.innerJoin(Issues).on { case (t1, t2) =>
|
||||||
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
t1.byIssue(t2.userName, t2.repositoryName, t2.issueId)
|
||||||
}
|
}
|
||||||
@@ -419,7 +421,7 @@ object IssuesService {
|
|||||||
conditions.get("author").flatMap(_.headOption),
|
conditions.get("author").flatMap(_.headOption),
|
||||||
conditions.get("assignee").flatMap(_.headOption),
|
conditions.get("assignee").flatMap(_.headOption),
|
||||||
conditions.get("mentions").flatMap(_.headOption),
|
conditions.get("mentions").flatMap(_.headOption),
|
||||||
conditions.get("is").getOrElse(Seq.empty).filter(x => x == "open" || x == "closed").headOption.getOrElse("open"),
|
conditions.get("is").getOrElse(Seq.empty).find(x => x == "open" || x == "closed").getOrElse("open"),
|
||||||
sort,
|
sort,
|
||||||
direction,
|
direction,
|
||||||
conditions.get("visibility").flatMap(_.headOption),
|
conditions.get("visibility").flatMap(_.headOption),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ trait RepositorySearchService { self: IssuesService =>
|
|||||||
searchIssuesByKeyword(owner, repository, query).map { case (issue, commentCount, content) =>
|
searchIssuesByKeyword(owner, repository, query).map { case (issue, commentCount, content) =>
|
||||||
IssueSearchResult(
|
IssueSearchResult(
|
||||||
issue.issueId,
|
issue.issueId,
|
||||||
|
issue.isPullRequest,
|
||||||
issue.title,
|
issue.title,
|
||||||
issue.openedUserName,
|
issue.openedUserName,
|
||||||
issue.registeredDate,
|
issue.registeredDate,
|
||||||
@@ -111,6 +112,7 @@ object RepositorySearchService {
|
|||||||
|
|
||||||
case class IssueSearchResult(
|
case class IssueSearchResult(
|
||||||
issueId: Int,
|
issueId: Int,
|
||||||
|
isPullRequest: Boolean,
|
||||||
title: String,
|
title: String,
|
||||||
openedUserName: String,
|
openedUserName: String,
|
||||||
registeredDate: java.util.Date,
|
registeredDate: java.util.Date,
|
||||||
|
|||||||
@@ -46,15 +46,16 @@ trait RepositoryService { self: AccountService =>
|
|||||||
(Repositories filter { t => t.byRepository(oldUserName, oldRepositoryName) } firstOption).map { repository =>
|
(Repositories filter { t => t.byRepository(oldUserName, oldRepositoryName) } firstOption).map { repository =>
|
||||||
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
|
Repositories insert repository.copy(userName = newUserName, repositoryName = newRepositoryName)
|
||||||
|
|
||||||
val webHooks = WebHooks .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val webHooks = WebHooks .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val milestones = Milestones .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val milestones = Milestones .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val issueId = IssueId .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issueId = IssueId .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val issues = Issues .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issues = Issues .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val pullRequests = PullRequests .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val pullRequests = PullRequests .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val labels = Labels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val labels = Labels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val issueComments = IssueComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issueComments = IssueComments .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val issueLabels = IssueLabels .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
val collaborators = Collaborators.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
val commitComments = CommitComments.filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
|
val collaborators = Collaborators .filter(_.byRepository(oldUserName, oldRepositoryName)).list
|
||||||
|
|
||||||
Repositories.filter { t =>
|
Repositories.filter { t =>
|
||||||
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
(t.originUserName === oldUserName.bind) && (t.originRepositoryName === oldRepositoryName.bind)
|
||||||
@@ -94,6 +95,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
IssueComments .insertAll(issueComments .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
Labels .insertAll(labels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
IssueLabels .insertAll(issueLabels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
IssueLabels .insertAll(issueLabels .map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
|
CommitComments.insertAll(commitComments.map(_.copy(userName = newUserName, repositoryName = newRepositoryName)) :_*)
|
||||||
|
|
||||||
if(account.isGroupAccount){
|
if(account.isGroupAccount){
|
||||||
Collaborators.insertAll(getGroupMembers(newUserName).map(m => Collaborator(newUserName, newRepositoryName, m.userName)) :_*)
|
Collaborators.insertAll(getGroupMembers(newUserName).map(m => Collaborator(newUserName, newRepositoryName, m.userName)) :_*)
|
||||||
@@ -103,7 +105,9 @@ trait RepositoryService { self: AccountService =>
|
|||||||
|
|
||||||
// Update activity messages
|
// Update activity messages
|
||||||
Activities.filter { t =>
|
Activities.filter { t =>
|
||||||
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") || (t.message like s"%:${oldUserName}/${oldRepositoryName}#%")
|
(t.message like s"%:${oldUserName}/${oldRepositoryName}]%") ||
|
||||||
|
(t.message like s"%:${oldUserName}/${oldRepositoryName}#%") ||
|
||||||
|
(t.message like s"%:${oldUserName}/${oldRepositoryName}@%")
|
||||||
}.map { t => t.activityId -> t.message }.list.foreach { case (activityId, message) =>
|
}.map { t => t.activityId -> t.message }.list.foreach { case (activityId, message) =>
|
||||||
Activities.filter(_.activityId === activityId.bind).map(_.message).update(
|
Activities.filter(_.activityId === activityId.bind).map(_.message).update(
|
||||||
message
|
message
|
||||||
@@ -112,6 +116,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
.replace(s"[tag:${oldUserName}/${oldRepositoryName}#" ,s"[tag:${newUserName}/${newRepositoryName}#")
|
.replace(s"[tag:${oldUserName}/${oldRepositoryName}#" ,s"[tag:${newUserName}/${newRepositoryName}#")
|
||||||
.replace(s"[pullreq:${oldUserName}/${oldRepositoryName}#",s"[pullreq:${newUserName}/${newRepositoryName}#")
|
.replace(s"[pullreq:${oldUserName}/${oldRepositoryName}#",s"[pullreq:${newUserName}/${newRepositoryName}#")
|
||||||
.replace(s"[issue:${oldUserName}/${oldRepositoryName}#" ,s"[issue:${newUserName}/${newRepositoryName}#")
|
.replace(s"[issue:${oldUserName}/${oldRepositoryName}#" ,s"[issue:${newUserName}/${newRepositoryName}#")
|
||||||
|
.replace(s"[commit:${oldUserName}/${oldRepositoryName}@" ,s"[commit:${newUserName}/${newRepositoryName}@")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,6 +126,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
def deleteRepository(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
def deleteRepository(userName: String, repositoryName: String)(implicit s: Session): Unit = {
|
||||||
Activities .filter(_.byRepository(userName, repositoryName)).delete
|
Activities .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
Collaborators .filter(_.byRepository(userName, repositoryName)).delete
|
Collaborators .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
|
CommitComments.filter(_.byRepository(userName, repositoryName)).delete
|
||||||
IssueLabels .filter(_.byRepository(userName, repositoryName)).delete
|
IssueLabels .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
Labels .filter(_.byRepository(userName, repositoryName)).delete
|
Labels .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
IssueComments .filter(_.byRepository(userName, repositoryName)).delete
|
IssueComments .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
@@ -130,6 +136,30 @@ trait RepositoryService { self: AccountService =>
|
|||||||
Milestones .filter(_.byRepository(userName, repositoryName)).delete
|
Milestones .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
WebHooks .filter(_.byRepository(userName, repositoryName)).delete
|
WebHooks .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
Repositories .filter(_.byRepository(userName, repositoryName)).delete
|
Repositories .filter(_.byRepository(userName, repositoryName)).delete
|
||||||
|
|
||||||
|
// Update ORIGIN_USER_NAME and ORIGIN_REPOSITORY_NAME
|
||||||
|
Repositories
|
||||||
|
.filter { x => (x.originUserName === userName.bind) && (x.originRepositoryName === repositoryName.bind) }
|
||||||
|
.map { x => (x.userName, x.repositoryName) }
|
||||||
|
.list
|
||||||
|
.foreach { case (userName, repositoryName) =>
|
||||||
|
Repositories
|
||||||
|
.filter(_.byRepository(userName, repositoryName))
|
||||||
|
.map(x => (x.originUserName?, x.originRepositoryName?))
|
||||||
|
.update(None, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update PARENT_USER_NAME and PARENT_REPOSITORY_NAME
|
||||||
|
Repositories
|
||||||
|
.filter { x => (x.parentUserName === userName.bind) && (x.parentRepositoryName === repositoryName.bind) }
|
||||||
|
.map { x => (x.userName, x.repositoryName) }
|
||||||
|
.list
|
||||||
|
.foreach { case (userName, repositoryName) =>
|
||||||
|
Repositories
|
||||||
|
.filter(_.byRepository(userName, repositoryName))
|
||||||
|
.map(x => (x.parentUserName?, x.parentRepositoryName?))
|
||||||
|
.update(None, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,7 +190,7 @@ trait RepositoryService { self: AccountService =>
|
|||||||
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
|
JGitUtil.getRepositoryInfo(repository.userName, repository.repositoryName, baseUrl),
|
||||||
repository,
|
repository,
|
||||||
issues.size,
|
issues.size,
|
||||||
issues.filter(_ == true).size,
|
issues.count(_ == true),
|
||||||
getForkedCount(
|
getForkedCount(
|
||||||
repository.originUserName.getOrElse(repository.userName),
|
repository.originUserName.getOrElse(repository.userName),
|
||||||
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
repository.originRepositoryName.getOrElse(repository.repositoryName)
|
||||||
@@ -358,4 +388,4 @@ object RepositoryService {
|
|||||||
|
|
||||||
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
|
case class RepositoryTreeNode(owner: String, name: String, children: List[RepositoryTreeNode])
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ trait SystemSettingsService {
|
|||||||
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
|
props.setProperty(LdapUserNameAttribute, ldap.userNameAttribute)
|
||||||
ldap.additionalFilterCondition.foreach(x => props.setProperty(LdapAdditionalFilterCondition, x))
|
ldap.additionalFilterCondition.foreach(x => props.setProperty(LdapAdditionalFilterCondition, x))
|
||||||
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
|
ldap.fullNameAttribute.foreach(x => props.setProperty(LdapFullNameAttribute, x))
|
||||||
ldap.mailAttribute.foreach(x => props.setProperty(LdapMailAddressAttribute, x.toString))
|
ldap.mailAttribute.foreach(x => props.setProperty(LdapMailAddressAttribute, x))
|
||||||
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
|
ldap.tls.foreach(x => props.setProperty(LdapTls, x.toString))
|
||||||
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
|
ldap.keystore.foreach(x => props.setProperty(LdapKeystore, x))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.apache.commons.io.IOUtils
|
|||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import util.Directory._
|
import util.Directory._
|
||||||
import util.ControlUtil._
|
import util.ControlUtil._
|
||||||
|
import util.JDBCUtil._
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import util.Directory
|
import util.Directory
|
||||||
import plugin.PluginUpdateJob
|
import plugin.PluginUpdateJob
|
||||||
@@ -53,24 +54,53 @@ 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(
|
||||||
|
new Version(2, 7) {
|
||||||
|
override def update(conn: Connection): Unit = {
|
||||||
|
super.update(conn)
|
||||||
|
conn.select("SELECT * FROM REPOSITORY"){ rs =>
|
||||||
|
// Rename attached files directory from /issues to /comments
|
||||||
|
val userName = rs.getString("USER_NAME")
|
||||||
|
val repoName = rs.getString("REPOSITORY_NAME")
|
||||||
|
defining(Directory.getAttachedDir(userName, repoName)){ newDir =>
|
||||||
|
val oldDir = new File(newDir.getParentFile, "issues")
|
||||||
|
if(oldDir.exists && oldDir.isDirectory){
|
||||||
|
oldDir.renameTo(newDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update ORIGIN_USER_NAME and ORIGIN_REPOSITORY_NAME if it does not exist
|
||||||
|
val originalUserName = rs.getString("ORIGIN_USER_NAME")
|
||||||
|
val originalRepoName = rs.getString("ORIGIN_REPOSITORY_NAME")
|
||||||
|
if(originalUserName != null && originalRepoName != null){
|
||||||
|
if(conn.selectInt("SELECT COUNT(*) FROM REPOSITORY WHERE USER_NAME = ? AND REPOSITORY_NAME = ?",
|
||||||
|
originalUserName, originalRepoName) == 0){
|
||||||
|
conn.update("UPDATE REPOSITORY SET ORIGIN_USER_NAME = NULL, ORIGIN_REPOSITORY_NAME = NULL " +
|
||||||
|
"WHERE USER_NAME = ? AND REPOSITORY_NAME = ?", userName, repoName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update PARENT_USER_NAME and PARENT_REPOSITORY_NAME if it does not exist
|
||||||
|
val parentUserName = rs.getString("PARENT_USER_NAME")
|
||||||
|
val parentRepoName = rs.getString("PARENT_REPOSITORY_NAME")
|
||||||
|
if(parentUserName != null && parentRepoName != null){
|
||||||
|
if(conn.selectInt("SELECT COUNT(*) FROM REPOSITORY WHERE USER_NAME = ? AND REPOSITORY_NAME = ?",
|
||||||
|
parentUserName, parentRepoName) == 0){
|
||||||
|
conn.update("UPDATE REPOSITORY SET PARENT_USER_NAME = NULL, PARENT_REPOSITORY_NAME = NULL " +
|
||||||
|
"WHERE USER_NAME = ? AND REPOSITORY_NAME = ?", userName, repoName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
new Version(2, 6),
|
new Version(2, 6),
|
||||||
new Version(2, 5),
|
new Version(2, 5),
|
||||||
new Version(2, 4),
|
new Version(2, 4),
|
||||||
new Version(2, 3) {
|
new Version(2, 3) {
|
||||||
override def update(conn: Connection): Unit = {
|
override def update(conn: Connection): Unit = {
|
||||||
super.update(conn)
|
super.update(conn)
|
||||||
using(conn.createStatement.executeQuery("SELECT ACTIVITY_ID, ADDITIONAL_INFO FROM ACTIVITY WHERE ACTIVITY_TYPE='push'")){ rs =>
|
conn.select("SELECT ACTIVITY_ID, ADDITIONAL_INFO FROM ACTIVITY WHERE ACTIVITY_TYPE='push'"){ rs =>
|
||||||
while(rs.next) {
|
val curInfo = rs.getString("ADDITIONAL_INFO")
|
||||||
val info = rs.getString("ADDITIONAL_INFO")
|
val newInfo = curInfo.split("\n").filter(_ matches "^[0-9a-z]{40}:.*").mkString("\n")
|
||||||
val newInfo = info.split("\n").filter(_ matches "^[0-9a-z]{40}:.*").mkString("\n")
|
if (curInfo != newInfo) {
|
||||||
if (info != newInfo) {
|
conn.update("UPDATE ACTIVITY SET ADDITIONAL_INFO = ? WHERE ACTIVITY_ID = ?", newInfo, rs.getInt("ACTIVITY_ID"))
|
||||||
val id = rs.getString("ACTIVITY_ID")
|
|
||||||
using(conn.prepareStatement("UPDATE ACTIVITY SET ADDITIONAL_INFO=? WHERE ACTIVITY_ID=?")) { sql =>
|
|
||||||
sql.setString(1, newInfo)
|
|
||||||
sql.setLong(2, id.toLong)
|
|
||||||
sql.executeUpdate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FileUtils.deleteDirectory(Directory.getPluginCacheDir())
|
FileUtils.deleteDirectory(Directory.getPluginCacheDir())
|
||||||
@@ -87,16 +117,14 @@ object AutoUpdate {
|
|||||||
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector")
|
mimeUtil.registerMimeDetector("eu.medsea.mimeutil.detector.MagicMimeMimeDetector")
|
||||||
|
|
||||||
super.update(conn)
|
super.update(conn)
|
||||||
using(conn.createStatement.executeQuery("SELECT USER_NAME, REPOSITORY_NAME FROM REPOSITORY")){ rs =>
|
conn.select("SELECT USER_NAME, REPOSITORY_NAME FROM REPOSITORY"){ rs =>
|
||||||
while(rs.next){
|
defining(Directory.getAttachedDir(rs.getString("USER_NAME"), rs.getString("REPOSITORY_NAME"))){ dir =>
|
||||||
defining(Directory.getAttachedDir(rs.getString("USER_NAME"), rs.getString("REPOSITORY_NAME"))){ dir =>
|
if(dir.exists && dir.isDirectory){
|
||||||
if(dir.exists && dir.isDirectory){
|
dir.listFiles.foreach { file =>
|
||||||
dir.listFiles.foreach { file =>
|
if(file.getName.indexOf('.') < 0){
|
||||||
if(file.getName.indexOf('.') < 0){
|
val mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file, new MimeType("application/octet-stream"))).toString
|
||||||
val mimeType = MimeUtil2.getMostSpecificMimeType(mimeUtil.getMimeTypes(file, new MimeType("application/octet-stream"))).toString
|
if(mimeType.startsWith("image/")){
|
||||||
if(mimeType.startsWith("image/")){
|
file.renameTo(new File(file.getParent, file.getName + "." + mimeType.split("/")(1)))
|
||||||
file.renameTo(new File(file.getParent, file.getName + "." + mimeType.split("/")(1)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,14 +147,12 @@ object AutoUpdate {
|
|||||||
override def update(conn: Connection): Unit = {
|
override def update(conn: Connection): Unit = {
|
||||||
super.update(conn)
|
super.update(conn)
|
||||||
// Fix wiki repository configuration
|
// Fix wiki repository configuration
|
||||||
using(conn.createStatement.executeQuery("SELECT USER_NAME, REPOSITORY_NAME FROM REPOSITORY")){ rs =>
|
conn.select("SELECT USER_NAME, REPOSITORY_NAME FROM REPOSITORY"){ rs =>
|
||||||
while(rs.next){
|
using(Git.open(getWikiRepositoryDir(rs.getString("USER_NAME"), rs.getString("REPOSITORY_NAME")))){ git =>
|
||||||
using(Git.open(getWikiRepositoryDir(rs.getString("USER_NAME"), rs.getString("REPOSITORY_NAME")))){ git =>
|
defining(git.getRepository.getConfig){ config =>
|
||||||
defining(git.getRepository.getConfig){ config =>
|
if(!config.getBoolean("http", "receivepack", false)){
|
||||||
if(!config.getBoolean("http", "receivepack", false)){
|
config.setBoolean("http", null, "receivepack", true)
|
||||||
config.setBoolean("http", null, "receivepack", true)
|
config.save
|
||||||
config.save
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ object Directory {
|
|||||||
* Directory for files which are attached to issue.
|
* Directory for files which are attached to issue.
|
||||||
*/
|
*/
|
||||||
def getAttachedDir(owner: String, repository: String): File =
|
def getAttachedDir(owner: String, repository: String): File =
|
||||||
new File(s"${RepositoryHome}/${owner}/${repository}/issues")
|
new File(s"${RepositoryHome}/${owner}/${repository}/comments")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directory for uploaded files by the specified user.
|
* Directory for uploaded files by the specified user.
|
||||||
|
|||||||
55
src/main/scala/util/JDBCUtil.scala
Normal file
55
src/main/scala/util/JDBCUtil.scala
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import java.sql._
|
||||||
|
import util.ControlUtil._
|
||||||
|
import scala.collection.mutable.ListBuffer
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides implicit class which extends java.sql.Connection.
|
||||||
|
* This is used in automatic migration in [[servlet.AutoUpdateListener]].
|
||||||
|
*/
|
||||||
|
object JDBCUtil {
|
||||||
|
|
||||||
|
implicit class RichConnection(conn: Connection){
|
||||||
|
|
||||||
|
def update(sql: String, params: Any*): Int = {
|
||||||
|
execute(sql, params: _*){ stmt =>
|
||||||
|
stmt.executeUpdate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def select[T](sql: String, params: Any*)(f: ResultSet => T): Seq[T] = {
|
||||||
|
execute(sql, params: _*){ stmt =>
|
||||||
|
using(stmt.executeQuery()){ rs =>
|
||||||
|
val list = new ListBuffer[T]
|
||||||
|
while(rs.next){
|
||||||
|
list += f(rs)
|
||||||
|
}
|
||||||
|
list.toSeq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def selectInt(sql: String, params: Any*): Int = {
|
||||||
|
execute(sql, params: _*){ stmt =>
|
||||||
|
using(stmt.executeQuery()){ rs =>
|
||||||
|
if(rs.next) rs.getInt(1) else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def execute[T](sql: String, params: Any*)(f: (PreparedStatement) => T): T = {
|
||||||
|
using(conn.prepareStatement(sql)){ stmt =>
|
||||||
|
params.zipWithIndex.foreach { case (p, i) =>
|
||||||
|
p match {
|
||||||
|
case x: Int => stmt.setInt(i + 1, x)
|
||||||
|
case x: String => stmt.setString(i + 1, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f(stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -217,7 +217,7 @@ object JGitUtil {
|
|||||||
list.append((treeWalk.getObjectId(0), treeWalk.getFileMode(0), treeWalk.getPathString, treeWalk.getNameString, linkUrl))
|
list.append((treeWalk.getObjectId(0), treeWalk.getFileMode(0), treeWalk.getPathString, treeWalk.getNameString, linkUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
list = list.map(tuple =>
|
list.transform(tuple =>
|
||||||
if (tuple._2 != FileMode.TREE)
|
if (tuple._2 != FileMode.TREE)
|
||||||
tuple
|
tuple
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ trait LinkConverter { self: RequestCache =>
|
|||||||
issueIdPrefix: String = "#")(implicit context: app.Context): String = {
|
issueIdPrefix: String = "#")(implicit context: app.Context): String = {
|
||||||
value
|
value
|
||||||
// escape HTML tags
|
// escape HTML tags
|
||||||
.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """)
|
.replace("&", "&").replace("<", "<").replace(">", ">").replace("\"", """)
|
||||||
// convert issue id to link
|
// convert issue id to link
|
||||||
.replaceBy(("(?<=(^|\\W))" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r){ m =>
|
.replaceBy(("(?<=(^|\\W))" + issueIdPrefix + "([0-9]+)(?=(\\W|$))").r){ m =>
|
||||||
getIssue(repository.owner, repository.name, m.group(2)) match {
|
getIssue(repository.owner, repository.name, m.group(2)) match {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ object Markdown {
|
|||||||
} else s
|
} else s
|
||||||
|
|
||||||
val rootNode = new PegDownProcessor(
|
val rootNode = new PegDownProcessor(
|
||||||
Extensions.AUTOLINKS | Extensions.WIKILINKS | Extensions.FENCED_CODE_BLOCKS | Extensions.TABLES | Extensions.HARDWRAPS
|
Extensions.AUTOLINKS | Extensions.WIKILINKS | Extensions.FENCED_CODE_BLOCKS | Extensions.TABLES | Extensions.HARDWRAPS | Extensions.SUPPRESS_ALL_HTML
|
||||||
).parseMarkdown(source.toCharArray)
|
).parseMarkdown(source.toCharArray)
|
||||||
|
|
||||||
new GitBucketHtmlSerializer(markdown, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission).toHtml(rootNode)
|
new GitBucketHtmlSerializer(markdown, repository, enableWikiLink, enableRefsLink, enableTaskList, hasWritePermission).toHtml(rootNode)
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ object helpers extends AvatarImageProvider with LinkConverter with RequestCache
|
|||||||
.replaceAll("\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]", (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m.group(3)}</a>""")
|
.replaceAll("\\[branch:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]", (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m.group(3)}</a>""")
|
||||||
.replaceAll("\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]" , (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m.group(3)}</a>""")
|
.replaceAll("\\[tag:([^\\s]+?)/([^\\s]+?)#([^\\s]+?)\\]" , (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/tree/${encodeRefName(m.group(3))}">${m.group(3)}</a>""")
|
||||||
.replaceAll("\\[user:([^\\s]+?)\\]" , (m: Match) => user(m.group(1)).body)
|
.replaceAll("\\[user:([^\\s]+?)\\]" , (m: Match) => user(m.group(1)).body)
|
||||||
|
.replaceAll("\\[commit:([^\\s]+?)/([^\\s]+?)\\@([^\\s]+?)\\]", (m: Match) => s"""<a href="${context.path}/${m.group(1)}/${m.group(2)}/commit/${m.group(3)}">${m.group(1)}/${m.group(2)}@${m.group(3).substring(0, 7)}</a>""")
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
@(activity.activityType match {
|
@(activity.activityType match {
|
||||||
case "open_issue" => detailActivity(activity, "activity-issue.png")
|
case "open_issue" => detailActivity(activity, "activity-issue.png")
|
||||||
case "comment_issue" => detailActivity(activity, "activity-comment.png")
|
case "comment_issue" => detailActivity(activity, "activity-comment.png")
|
||||||
|
case "comment_commit" => detailActivity(activity, "activity-comment.png")
|
||||||
case "close_issue" => detailActivity(activity, "activity-issue-close.png")
|
case "close_issue" => detailActivity(activity, "activity-issue-close.png")
|
||||||
case "reopen_issue" => detailActivity(activity, "activity-issue-reopen.png")
|
case "reopen_issue" => detailActivity(activity, "activity-issue-reopen.png")
|
||||||
case "open_pullreq" => detailActivity(activity, "activity-merge.png")
|
case "open_pullreq" => detailActivity(activity, "activity-merge.png")
|
||||||
|
|||||||
@@ -7,18 +7,24 @@
|
|||||||
@defining("(id=\")([\\w\\-]*)(\")".r.findFirstMatchIn(textarea.body).map(_.group(2))){ textareaId =>
|
@defining("(id=\")([\\w\\-]*)(\")".r.findFirstMatchIn(textarea.body).map(_.group(2))){ textareaId =>
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$([$('#@textareaId').closest('div')[0], $('#@textareaId').next('div')[0]]).dropzone({
|
try {
|
||||||
url: '@path/upload/image/@owner/@repository',
|
$([$('#@textareaId').closest('div')[0], $('#@textareaId').next('div')[0]]).dropzone({
|
||||||
maxFilesize: 10,
|
url: '@path/upload/image/@owner/@repository',
|
||||||
acceptedFiles: 'image/*',
|
maxFilesize: 10,
|
||||||
dictInvalidFileType: 'Unfortunately, we don\'t support that file type. Try again with a PNG, GIF, or JPG.',
|
acceptedFiles: 'image/*',
|
||||||
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your images...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
dictInvalidFileType: 'Unfortunately, we don\'t support that file type. Try again with a PNG, GIF, or JPG.',
|
||||||
success: function(file, id) {
|
previewTemplate: "<div class=\"dz-preview\">\n <div class=\"dz-progress\"><span class=\"dz-upload\" data-dz-uploadprogress>Uploading your images...</span></div>\n <div class=\"dz-error-message\"><span data-dz-errormessage></span></div>\n</div>",
|
||||||
var images = '\n![' + file.name.split('.')[0] + '](@baseUrl/@owner/@repository/_attached/' + id + ')';
|
success: function(file, id) {
|
||||||
$('#@textareaId').val($('#@textareaId').val() + images);
|
var images = '\n![' + file.name.split('.')[0] + '](@baseUrl/@owner/@repository/_attached/' + id + ')';
|
||||||
$(file.previewElement).prevAll('div.dz-preview').addBack().remove();
|
$('#@textareaId').val($('#@textareaId').val() + images);
|
||||||
|
$(file.previewElement).prevAll('div.dz-preview').addBack().remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
if (e.message !== "Dropzone already attached.") {
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Adjust clickable area width
|
// Adjust clickable area width
|
||||||
$('#@textareaId').next('div.clickable').css('width', ($('#@textareaId').width() + 8) + 'px');
|
$('#@textareaId').next('div.clickable').css('width', ($('#@textareaId').width() + 8) + 'px');
|
||||||
|
|||||||
35
src/main/twirl/helper/commitcomment.scala.html
Normal file
35
src/main/twirl/helper/commitcomment.scala.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
@(comment: model.CommitComment,
|
||||||
|
hasWritePermission: Boolean,
|
||||||
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
|
latestCommitId: Option[String] = None)(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
<div class="@if(comment.fileName.isDefined && (!latestCommitId.isDefined || latestCommitId.get == comment.commitId)){inline-comment}" @if(comment.fileName.isDefined){filename=@comment.fileName.get} @if(comment.newLine.isDefined){newline=@comment.newLine.get} @if(comment.oldLine.isDefined){oldline=@comment.oldLine.get}>
|
||||||
|
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
|
||||||
|
<div class="box commit-comment-box commit-comment-@comment.commentId">
|
||||||
|
<div class="box-header-small">
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong")
|
||||||
|
<span class="muted">
|
||||||
|
commented
|
||||||
|
@if(comment.pullRequest){
|
||||||
|
on this Pull Request
|
||||||
|
}else{
|
||||||
|
@if(comment.fileName.isDefined){
|
||||||
|
on @comment.fileName.get
|
||||||
|
}
|
||||||
|
in <a href="@path/@repository.owner/@repository.name/commit/@comment.commitId">@comment.commitId.substring(0, 7)</a>
|
||||||
|
}
|
||||||
|
@helper.html.datetimeago(comment.registeredDate)
|
||||||
|
</span>
|
||||||
|
<span class="pull-right">
|
||||||
|
@if(hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false)){
|
||||||
|
<a href="#" data-comment-id="@comment.commentId"><i class="icon-pencil"></i></a>
|
||||||
|
<a href="#" data-comment-id="@comment.commentId"><i class="icon-remove-circle"></i></a>
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="box-content commit-commentContent-@comment.commentId">
|
||||||
|
@markdown(comment.content, repository, false, true, true, hasWritePermission)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
@(diffs: Seq[util.JGitUtil.DiffInfo],
|
@(diffs: Seq[util.JGitUtil.DiffInfo],
|
||||||
repository: service.RepositoryService.RepositoryInfo,
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
newCommitId: Option[String],
|
newCommitId: Option[String],
|
||||||
oldCommitId: Option[String],
|
oldCommitId: Option[String],
|
||||||
showIndex: Boolean)(implicit context: app.Context)
|
showIndex: Boolean,
|
||||||
|
pullRequest: Boolean,
|
||||||
|
hasWritePermission: Boolean,
|
||||||
|
showLineNotes: Boolean)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
@import org.eclipse.jgit.diff.DiffEntry.ChangeType
|
||||||
@@ -39,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
@diffs.zipWithIndex.map { case (diff, i) =>
|
@diffs.zipWithIndex.map { case (diff, i) =>
|
||||||
<a name="diff-@i"></a>
|
<a name="diff-@i"></a>
|
||||||
<table class="table table-bordered">
|
<table class="table table-bordered" commitId="@newCommitId" fileName="@diff.newPath">
|
||||||
<tr>
|
<tr>
|
||||||
<th style="font-weight: normal; line-height: 27px;" class="box-header">
|
<th style="font-weight: normal; line-height: 27px;" class="box-header">
|
||||||
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
@if(diff.changeType == ChangeType.COPY || diff.changeType == ChangeType.RENAME){
|
||||||
@@ -118,6 +121,67 @@ $(function(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@if(showLineNotes){
|
||||||
|
$('.inline-comment').each(function(i, v) {
|
||||||
|
var $v = $(v), filename = $v.attr('filename'),
|
||||||
|
oldline = $v.attr('oldline'), newline = $v.attr('newline'),
|
||||||
|
tmp = $('<tr class="not-diff"><td colspan="3" style="white-space: initial; line-height: initial; padding: 10px;"></td></tr>');
|
||||||
|
tmp.children('td').html($(this).clone().show());
|
||||||
|
if (typeof $('#show-notes')[0] !== 'undefined' && !$('#show-notes')[0].checked) {
|
||||||
|
$(this).hide();
|
||||||
|
}
|
||||||
|
if (typeof oldline !== 'undefined') {
|
||||||
|
$('table[filename="' + filename + '"]').find('table.inlinediff').find('.oldline').filter(function() {
|
||||||
|
return new RegExp('^' + oldline + '$').test($(this).text());
|
||||||
|
}).parent().nextAll(':not(.not-diff):first').before(tmp);
|
||||||
|
} else {
|
||||||
|
$('table[filename="' + filename + '"]').find('table.inlinediff').find('.newline').filter(function() {
|
||||||
|
return new RegExp('^' + newline + '\\+$').test($(this).text());
|
||||||
|
}).parent().nextAll(':not(.not-diff):first').before(tmp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@if(hasWritePermission) {
|
||||||
|
$('table.diff tr').hover(
|
||||||
|
function() {
|
||||||
|
$(this).find('b').css('display', 'inline-block');
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
$(this).find('b').css('display', 'none');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$('.add-comment').click(function() {
|
||||||
|
var $this = $(this),
|
||||||
|
$tr = $(this).closest('tr');
|
||||||
|
if (!$tr.nextAll(':not(.not-diff):first').prev().hasClass('inline-comment-form')) {
|
||||||
|
var commitId = $(this).closest('.table-bordered').attr('commitId'),
|
||||||
|
fileName = $(this).closest('.table-bordered').attr('fileName'),
|
||||||
|
oldLineNumber = $(this).closest('.newline').prev('.oldline').text(),
|
||||||
|
newLineNumber = $(this).closest('.newline').clone().children().remove().end().text(),
|
||||||
|
url = '@url(repository)/commit/' + commitId + '/comment/_form?fileName=' + fileName + '&pullRequest=@pullRequest';
|
||||||
|
if (!isNaN(oldLineNumber) && oldLineNumber != null && oldLineNumber !== '') {
|
||||||
|
url += ('&oldLineNumber=' + oldLineNumber)
|
||||||
|
}
|
||||||
|
if (!isNaN(newLineNumber) && newLineNumber != null && newLineNumber !== '') {
|
||||||
|
url += ('&newLineNumber=' + newLineNumber)
|
||||||
|
}
|
||||||
|
$.get(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
dataType : 'html'
|
||||||
|
},
|
||||||
|
function(responseContent) {
|
||||||
|
$this.hide();
|
||||||
|
var tmp = $('<tr class="inline-comment-form not-diff"><td colspan="3" style="white-space: initial; padding: 10px;"></td></tr>');
|
||||||
|
tmp.children('td').html(responseContent);
|
||||||
|
$tr.nextAll(':not(.not-diff):first').before(tmp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('table.diff').on('click', '.btn-default', function() {
|
||||||
|
$(this).closest('.inline-comment-form').remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ $(function(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('#preview').click(function(){
|
$('#preview').click(function(){
|
||||||
$('#preview-area').html('<img src="@assets/common/images/indicator.gif"> Previewing...');
|
$(this).closest('#preview-area').html('<img src="@assets/common/images/indicator.gif"> Previewing...');
|
||||||
$.post('@url(repository)/_preview', {
|
$.post('@url(repository)/_preview', {
|
||||||
content : $('#content').val(),
|
content : $('#content').val(),
|
||||||
enableWikiLink : @enableWikiLink,
|
enableWikiLink : @enableWikiLink,
|
||||||
|
|||||||
@@ -1,107 +1,115 @@
|
|||||||
@(issue: model.Issue,
|
@(issue: Option[model.Issue],
|
||||||
comments: List[model.IssueComment],
|
comments: List[model.Comment],
|
||||||
hasWritePermission: Boolean,
|
hasWritePermission: Boolean,
|
||||||
repository: service.RepositoryService.RepositoryInfo,
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
pullreq: Option[model.PullRequest] = None)(implicit context: app.Context)
|
pullreq: Option[model.PullRequest] = None)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
<div class="issue-avatar-image">@avatar(issue.openedUserName, 48)</div>
|
@if(issue.isDefined){
|
||||||
<div class="box issue-comment-box">
|
<div class="issue-avatar-image">@avatar(issue.get.openedUserName, 48)</div>
|
||||||
<div class="box-header-small">
|
<div class="box issue-comment-box">
|
||||||
@user(issue.openedUserName, styleClass="username strong") <span class="muted">commented @helper.html.datetimeago(issue.registeredDate)</span>
|
<div class="box-header-small">
|
||||||
<span class="pull-right">
|
@user(issue.get.openedUserName, styleClass="username strong") <span class="muted">commented @helper.html.datetimeago(issue.get.registeredDate)</span>
|
||||||
@if(hasWritePermission || loginAccount.map(_.userName == issue.openedUserName).getOrElse(false)){
|
<span class="pull-right">
|
||||||
<a href="#" data-issue-id="@issue.issueId"><i class="icon-pencil"></i></a>
|
@if(hasWritePermission || loginAccount.map(_.userName == issue.get.openedUserName).getOrElse(false)){
|
||||||
}
|
<a href="#" data-issue-id="@issue.get.issueId"><i class="icon-pencil"></i></a>
|
||||||
</span>
|
}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="box-content issue-content" id="issueContent">
|
||||||
|
@markdown(issue.get.content getOrElse "No description provided.", repository, false, true, true, hasWritePermission)
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-content issue-content" id="issueContent">
|
}
|
||||||
@markdown(issue.content getOrElse "No description provided.", repository, false, true, true, hasWritePermission)
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@comments.map { comment =>
|
@comments.map {
|
||||||
@if(comment.action != "close" && comment.action != "reopen" && comment.action != "delete_branch"){
|
case comment: model.IssueComment => {
|
||||||
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
|
@if(comment.action != "close" && comment.action != "reopen" && comment.action != "delete_branch"){
|
||||||
<div class="box issue-comment-box" id="comment-@comment.commentId">
|
<div class="issue-avatar-image">@avatar(comment.commentedUserName, 48)</div>
|
||||||
<div class="box-header-small">
|
<div class="box issue-comment-box" id="comment-@comment.commentId">
|
||||||
@user(comment.commentedUserName, styleClass="username strong")
|
<div class="box-header-small">
|
||||||
<span class="muted">
|
@user(comment.commentedUserName, styleClass="username strong")
|
||||||
@if(comment.action == "comment"){
|
<span class="muted">
|
||||||
commented
|
@if(comment.action == "comment"){
|
||||||
} else {
|
commented
|
||||||
@if(pullreq.isEmpty){ referenced the issue } else { referenced the pull request }
|
} else {
|
||||||
}
|
@if(pullreq.isEmpty){ referenced the issue } else { referenced the pull request }
|
||||||
@helper.html.datetimeago(comment.registeredDate)
|
}
|
||||||
</span>
|
@helper.html.datetimeago(comment.registeredDate)
|
||||||
<span class="pull-right">
|
</span>
|
||||||
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer" &&
|
<span class="pull-right">
|
||||||
(hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
|
@if(comment.action != "commit" && comment.action != "merge" && comment.action != "refer"
|
||||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-pencil"></i></a>
|
&& (hasWritePermission || loginAccount.map(_.userName == comment.commentedUserName).getOrElse(false))){
|
||||||
<a href="#" data-comment-id="@comment.commentId"><i class="icon-remove-circle"></i></a>
|
<a href="#" data-comment-id="@comment.commentId"><i class="icon-pencil"></i></a>
|
||||||
}
|
<a href="#" data-comment-id="@comment.commentId"><i class="icon-remove-circle"></i></a>
|
||||||
</span>
|
}
|
||||||
</div>
|
</span>
|
||||||
<div class="box-content"class="issue-content" id="commentContent-@comment.commentId">
|
</div>
|
||||||
@if(comment.action == "commit" && comment.content.split(" ").last.matches("[a-f0-9]{40}")){
|
<div class="box-content"class="issue-content" id="commentContent-@comment.commentId">
|
||||||
@defining(comment.content.substring(comment.content.length - 40)){ id =>
|
@if(comment.action == "commit" && comment.content.split(" ").last.matches("[a-f0-9]{40}")){
|
||||||
<div class="pull-right"><a href="@path/@repository.owner/@repository.name/commit/@id" class="monospace">@id.substring(0, 7)</a></div>
|
@defining(comment.content.substring(comment.content.length - 40)){ id =>
|
||||||
@markdown(comment.content.substring(0, comment.content.length - 41), repository, false, true, true, hasWritePermission)
|
<div class="pull-right"><a href="@path/@repository.owner/@repository.name/commit/@id" class="monospace">@id.substring(0, 7)</a></div>
|
||||||
}
|
@markdown(comment.content.substring(0, comment.content.length - 41), repository, false, true, true, hasWritePermission)
|
||||||
} else {
|
|
||||||
@if(comment.action == "refer"){
|
|
||||||
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) =>
|
|
||||||
<strong><a href="@path/@repository.owner/@repository.name/issues/@issueId">Issue #@issueId</a>: @rest.mkString(":")</strong>
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@markdown(comment.content, repository, false, true, true, hasWritePermission)
|
@if(comment.action == "refer"){
|
||||||
|
@defining(comment.content.split(":")){ case Array(issueId, rest @ _*) =>
|
||||||
|
<strong><a href="@path/@repository.owner/@repository.name/issues/@issueId">Issue #@issueId</a>: @rest.mkString(":")</strong>
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@markdown(comment.content, repository, false, true, true, hasWritePermission)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if(comment.action == "merge"){
|
||||||
|
<div class="small" style="margin-top: 10px; margin-bottom: 10px;">
|
||||||
|
<span class="label label-info">Merged</span>
|
||||||
|
@avatar(comment.commentedUserName, 20)
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong") merged commit <code>@pullreq.map(_.commitIdTo.substring(0, 7))</code> into
|
||||||
|
@if(pullreq.get.requestUserName == repository.owner){
|
||||||
|
<span class="label label-info monospace">@pullreq.map(_.branch)</span> from <span class="label label-info monospace">@pullreq.map(_.requestBranch)</span>
|
||||||
|
} else {
|
||||||
|
<span class="label label-info monospace">@pullreq.map(_.userName):@pullreq.map(_.branch)</span> from <span class="label label-info monospace">@pullreq.map(_.requestUserName):@pullreq.map(_.requestBranch)</span>
|
||||||
|
}
|
||||||
|
@helper.html.datetimeago(comment.registeredDate)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if(comment.action == "close" || comment.action == "close_comment"){
|
||||||
|
<div class="small issue-comment-action">
|
||||||
|
<span class="label label-important">Closed</span>
|
||||||
|
@avatar(comment.commentedUserName, 20)
|
||||||
|
@if(issue.isDefined && issue.get.isPullRequest){
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong") closed the pull request @helper.html.datetimeago(comment.registeredDate)
|
||||||
|
} else {
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong") closed the issue @helper.html.datetimeago(comment.registeredDate)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
@if(comment.action == "reopen" || comment.action == "reopen_comment"){
|
||||||
|
<div class="small issue-comment-action">
|
||||||
|
<span class="label label-success">Reopened</span>
|
||||||
|
@avatar(comment.commentedUserName, 20)
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong") reopened the issue @helper.html.datetimeago(comment.registeredDate)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
@if(comment.action == "delete_branch"){
|
||||||
|
<div class="small issue-comment-action">
|
||||||
|
<span class="label">Deleted</span>
|
||||||
|
@avatar(comment.commentedUserName, 20)
|
||||||
|
@user(comment.commentedUserName, styleClass="username strong") deleted the <span class="label label-info monospace">@pullreq.map(_.requestBranch)</span> branch @helper.html.datetimeago(comment.registeredDate)
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@if(comment.action == "merge"){
|
case comment: model.CommitComment => {
|
||||||
<div class="small" style="margin-top: 10px; margin-bottom: 10px;">
|
@helper.html.commitcomment(comment, hasWritePermission, repository, pullreq.map(_.commitIdTo))
|
||||||
<span class="label label-info">Merged</span>
|
|
||||||
@avatar(comment.commentedUserName, 20)
|
|
||||||
@user(comment.commentedUserName, styleClass="username strong") merged commit <code>@pullreq.map(_.commitIdTo.substring(0, 7))</code> into
|
|
||||||
@if(pullreq.get.requestUserName == repository.owner){
|
|
||||||
<span class="label label-info monospace">@pullreq.map(_.branch)</span> from <span class="label label-info monospace">@pullreq.map(_.requestBranch)</span>
|
|
||||||
} else {
|
|
||||||
<span class="label label-info monospace">@pullreq.map(_.userName):@pullreq.map(_.branch)</span> from <span class="label label-info monospace">@pullreq.map(_.requestUserName):@pullreq.map(_.requestBranch)</span>
|
|
||||||
}
|
|
||||||
@helper.html.datetimeago(comment.registeredDate)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if(comment.action == "close" || comment.action == "close_comment"){
|
|
||||||
<div class="small issue-comment-action">
|
|
||||||
<span class="label label-important">Closed</span>
|
|
||||||
@avatar(comment.commentedUserName, 20)
|
|
||||||
@if(issue.isPullRequest){
|
|
||||||
@user(comment.commentedUserName, styleClass="username strong") closed the pull request @helper.html.datetimeago(comment.registeredDate)
|
|
||||||
} else {
|
|
||||||
@user(comment.commentedUserName, styleClass="username strong") closed the issue @helper.html.datetimeago(comment.registeredDate)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if(comment.action == "reopen" || comment.action == "reopen_comment"){
|
|
||||||
<div class="small issue-comment-action">
|
|
||||||
<span class="label label-success">Reopened</span>
|
|
||||||
@avatar(comment.commentedUserName, 20)
|
|
||||||
@user(comment.commentedUserName, styleClass="username strong") reopened the issue @helper.html.datetimeago(comment.registeredDate)
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
@if(comment.action == "delete_branch"){
|
|
||||||
<div class="small issue-comment-action">
|
|
||||||
<span class="label">Deleted</span>
|
|
||||||
@avatar(comment.commentedUserName, 20)
|
|
||||||
@user(comment.commentedUserName, styleClass="username strong") deleted the <span class="label label-info monospace">@pullreq.map(_.requestBranch)</span> branch @helper.html.datetimeago(comment.registeredDate)
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
$(function(){
|
$(function(){
|
||||||
$('i.icon-pencil').click(function(){
|
@if(issue.isDefined){
|
||||||
|
$('.issue-comment-box i.icon-pencil').click(function(){
|
||||||
var id = $(this).closest('a').data('comment-id');
|
var id = $(this).closest('a').data('comment-id');
|
||||||
var url = '@url(repository)/issue_comments/_data/' + id;
|
var url = '@url(repository)/issue_comments/_data/' + id;
|
||||||
var $content = $('#commentContent-' + id);
|
var $content = $('#commentContent-' + id);
|
||||||
@@ -134,6 +142,34 @@ $(function(){
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
$(document).on('click', '.commit-comment-box i.icon-pencil', function(){
|
||||||
|
var id = $(this).closest('a').data('comment-id');
|
||||||
|
var url = '@url(repository)/commit_comments/_data/' + id;
|
||||||
|
var $content = $('.commit-commentContent-' + id, $(this).closest('.box'));
|
||||||
|
|
||||||
|
$.get(url,
|
||||||
|
{
|
||||||
|
dataType : 'html'
|
||||||
|
},
|
||||||
|
function(data){
|
||||||
|
$content.empty().html(data);
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$(document).on('click', '.commit-comment-box i.icon-remove-circle', function(){
|
||||||
|
if(confirm('Are you sure you want to delete this?')) {
|
||||||
|
var id = $(this).closest('a').data('comment-id');
|
||||||
|
$.post('@url(repository)/commit_comments/delete/' + id,
|
||||||
|
function(data){
|
||||||
|
if(data > 0) {
|
||||||
|
$('.commit-comment-' + id).closest('.not-diff').remove();
|
||||||
|
$('.commit-comment-' + id).closest('.inline-comment').remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
var extractMarkdown = function(data){
|
var extractMarkdown = function(data){
|
||||||
$('body').append('<div id="tmp"></div>');
|
$('body').append('<div id="tmp"></div>');
|
||||||
@@ -156,15 +192,40 @@ $(function(){
|
|||||||
return ss.join('');
|
return ss.join('');
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#issueContent').on('click', ':checkbox', function(ev){
|
$('div[class*=commit-commentContent-]').on('click', ':checkbox', function(ev){
|
||||||
var checkboxes = $('#issueContent :checkbox');
|
var $commentContent = $(ev.target).parents('div[class*=commit-commentContent-]'),
|
||||||
$.get('@url(repository)/issues/_data/@issue.issueId',
|
commentId = $commentContent.attr('class').match(/commit-commentContent-.+/)[0].replace(/commit-commentContent-/, ''),
|
||||||
|
checkboxes = $commentContent.find(':checkbox');
|
||||||
|
$.get('@url(repository)/commit_comments/_data/' + commentId,
|
||||||
{
|
{
|
||||||
dataType : 'html'
|
dataType : 'html'
|
||||||
},
|
},
|
||||||
function(responseContent){
|
function(responseContent){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '@url(repository)/issues/edit/@issue.issueId',
|
url: '@url(repository)/commit_comments/edit/' + commentId,
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
issueId : 0,
|
||||||
|
content : replaceTaskList(responseContent, checkboxes)
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
$('.commit-commentContent-' + commentId).html(data.content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
@if(issue.isDefined){
|
||||||
|
$('#issueContent').on('click', ':checkbox', function(ev){
|
||||||
|
var checkboxes = $('#issueContent :checkbox');
|
||||||
|
$.get('@url(repository)/issues/_data/@issue.get.issueId',
|
||||||
|
{
|
||||||
|
dataType : 'html'
|
||||||
|
},
|
||||||
|
function(responseContent){
|
||||||
|
$.ajax({
|
||||||
|
url: '@url(repository)/issues/edit/@issue.get.issueId',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: {
|
data: {
|
||||||
title : $('#issueTitle').text(),
|
title : $('#issueTitle').text(),
|
||||||
@@ -196,5 +257,7 @@ $(function(){
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
<span class="muted">
|
<span class="muted">
|
||||||
@user(issue.openedUserName, styleClass="username strong") opened this issue @helper.html.datetimeago(issue.registeredDate) - @defining(
|
@user(issue.openedUserName, styleClass="username strong") opened this issue @helper.html.datetimeago(issue.registeredDate) - @defining(
|
||||||
comments.filter( _.action.contains("comment") ).size
|
comments.count( _.action.contains("comment") )
|
||||||
){ count =>
|
){ count =>
|
||||||
@count @plural(count, "comment")
|
@count @plural(count, "comment")
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<hr>
|
<hr>
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span10">
|
<div class="span10">
|
||||||
@commentlist(issue, comments, hasWritePermission, repository)
|
@commentlist(Some(issue), comments, hasWritePermission, repository)
|
||||||
@commentform(issue, true, hasWritePermission, repository)
|
@commentform(issue, true, hasWritePermission, repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="span2">
|
<div class="span2">
|
||||||
@@ -89,4 +89,4 @@ $(function(){
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@(issue: model.Issue,
|
@(issue: model.Issue,
|
||||||
comments: List[model.IssueComment],
|
comments: List[model.Comment],
|
||||||
issueLabels: List[model.Label],
|
issueLabels: List[model.Label],
|
||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(model.Milestone, Int, Int)],
|
milestones: List[(model.Milestone, Int, Int)],
|
||||||
|
|||||||
@@ -14,11 +14,11 @@
|
|||||||
<span class="small">
|
<span class="small">
|
||||||
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
|
<a class="button-link@if(state == "open"){ selected}" href="?state=open">
|
||||||
<img src="@assets/common/images/milestone@(if(state == "open"){"-active"}).png"/>
|
<img src="@assets/common/images/milestone@(if(state == "open"){"-active"}).png"/>
|
||||||
@milestones.filter(_._1.closedDate.isEmpty).size Open
|
@milestones.count(_._1.closedDate.isEmpty) Open
|
||||||
</a>
|
</a>
|
||||||
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
|
<a class="button-link@if(state == "closed"){ selected}" href="?state=closed">
|
||||||
<img src="@assets/common/images/milestone@(if(state == "closed"){"-active"}).png"/>
|
<img src="@assets/common/images/milestone@(if(state == "closed"){"-active"}).png"/>
|
||||||
@milestones.filter(_._1.closedDate.isDefined).size Closed
|
@milestones.count(_._1.closedDate.isDefined) Closed
|
||||||
</a>
|
</a>
|
||||||
</span>
|
</span>
|
||||||
</th>
|
</th>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@(commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
@(commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
||||||
|
comments: Option[List[model.Comment]] = None,
|
||||||
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@@ -15,6 +16,14 @@
|
|||||||
@user(commit.authorName, commit.authorEmailAddress, "username")
|
@user(commit.authorName, commit.authorEmailAddress, "username")
|
||||||
</td>
|
</td>
|
||||||
<td>@commit.shortMessage</td>
|
<td>@commit.shortMessage</td>
|
||||||
|
<td style="width: 10%; text-align: right">
|
||||||
|
<span class="badge" style="display: inline">@if(comments.isDefined){
|
||||||
|
@comments.get.flatMap @{
|
||||||
|
case comment: model.CommitComment => Some(comment)
|
||||||
|
case other => None
|
||||||
|
}.count(t => t.commitId == commit.id && !t.pullRequest)
|
||||||
|
}</span>
|
||||||
|
</td>
|
||||||
<td style="width: 10%; text-align: right;">
|
<td style="width: 10%; text-align: right;">
|
||||||
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
<a href="@url(repository)/commit/@commit.id" class="monospace">@commit.id.substring(0, 7)</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
@(commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
@(commits: Seq[Seq[util.JGitUtil.CommitInfo]],
|
||||||
diffs: Seq[util.JGitUtil.DiffInfo],
|
diffs: Seq[util.JGitUtil.DiffInfo],
|
||||||
members: List[(String, String)],
|
members: List[(String, String)],
|
||||||
|
comments: List[model.Comment],
|
||||||
originId: String,
|
originId: String,
|
||||||
forkedId: String,
|
forkedId: String,
|
||||||
sourceId: String,
|
sourceId: String,
|
||||||
@@ -81,8 +82,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
} else {
|
} else {
|
||||||
@pulls.html.commits(commits, repository)
|
@pulls.html.commits(commits, Some(comments), repository)
|
||||||
@helper.html.diff(diffs, repository, Some(commitId), Some(sourceId), true)
|
@helper.html.diff(diffs, repository, Some(commitId), Some(sourceId), true, false, hasWritePermission, false)
|
||||||
|
<p>Showing you all comments on commits in this comparison.</p>
|
||||||
|
@issues.html.commentlist(None, comments, hasWritePermission, repository, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@(issue: model.Issue,
|
@(issue: model.Issue,
|
||||||
pullreq: model.PullRequest,
|
pullreq: model.PullRequest,
|
||||||
comments: List[model.IssueComment],
|
comments: List[model.Comment],
|
||||||
issueLabels: List[model.Label],
|
issueLabels: List[model.Label],
|
||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(model.Milestone, Int, Int)],
|
milestones: List[(model.Milestone, Int, Int)],
|
||||||
@@ -8,11 +8,17 @@
|
|||||||
hasWritePermission: Boolean,
|
hasWritePermission: Boolean,
|
||||||
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
|
@import model.IssueComment
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span10">
|
<div class="span10">
|
||||||
@issues.html.commentlist(issue, comments, hasWritePermission, repository, Some(pullreq))
|
<div id="comment-list">
|
||||||
@defining(comments.exists(_.action == "merge")){ merged =>
|
@issues.html.commentlist(Some(issue), comments, hasWritePermission, repository, Some(pullreq))
|
||||||
|
</div>
|
||||||
|
@defining(comments.flatMap {
|
||||||
|
case comment: IssueComment => Some(comment)
|
||||||
|
case other => None
|
||||||
|
}.exists(_.action == "merge")){ merged =>
|
||||||
@if(hasWritePermission && !issue.closed){
|
@if(hasWritePermission && !issue.closed){
|
||||||
<div class="box issue-comment-box" style="background-color: #d8f5cd;">
|
<div class="box issue-comment-box" style="background-color: #d8f5cd;">
|
||||||
<div class="box-content"class="issue-content" style="border: 1px solid #95c97e; padding: 10px;">
|
<div class="box-content"class="issue-content" style="border: 1px solid #95c97e; padding: 10px;">
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
@(issue: model.Issue,
|
@(issue: model.Issue,
|
||||||
pullreq: model.PullRequest,
|
pullreq: model.PullRequest,
|
||||||
comments: List[model.IssueComment],
|
comments: List[model.Comment],
|
||||||
issueLabels: List[model.Label],
|
issueLabels: List[model.Label],
|
||||||
collaborators: List[String],
|
collaborators: List[String],
|
||||||
milestones: List[(model.Milestone, Int, Int)],
|
milestones: List[(model.Milestone, Int, Int)],
|
||||||
@@ -36,7 +36,10 @@
|
|||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
@if(issue.closed) {
|
@if(issue.closed) {
|
||||||
@comments.find(_.action == "merge").map{ comment =>
|
@comments.flatMap @{
|
||||||
|
case comment: model.IssueComment => Some(comment)
|
||||||
|
case _ => None
|
||||||
|
}.find(_.action == "merge").map{ comment =>
|
||||||
<span class="label label-info issue-status">Merged</span>
|
<span class="label label-info issue-status">Merged</span>
|
||||||
<span class="muted">
|
<span class="muted">
|
||||||
@user(comment.commentedUserName, styleClass="username strong") merged @commits.size @plural(commits.size, "commit")
|
@user(comment.commentedUserName, styleClass="username strong") merged @commits.size @plural(commits.size, "commit")
|
||||||
@@ -59,7 +62,10 @@
|
|||||||
}
|
}
|
||||||
<br/><br/>
|
<br/><br/>
|
||||||
<ul class="nav nav-tabs fill-width pull-left" id="pullreq-tab">
|
<ul class="nav nav-tabs fill-width pull-left" id="pullreq-tab">
|
||||||
<li class="active"><a href="#conversation">Conversation <span class="badge">@comments.size</span></a></li>
|
<li class="active"><a href="#conversation">Conversation <span class="badge">@comments.flatMap @{
|
||||||
|
case comment: model.IssueComment => Some(comment)
|
||||||
|
case _: model.CommitComment => None
|
||||||
|
}.size</span></a></li>
|
||||||
<li><a href="#commits">Commits <span class="badge">@commits.size</span></a></li>
|
<li><a href="#commits">Commits <span class="badge">@commits.size</span></a></li>
|
||||||
<li><a href="#files">Files Changed <span class="badge">@diffs.size</span></a></li>
|
<li><a href="#files">Files Changed <span class="badge">@diffs.size</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -68,10 +74,10 @@
|
|||||||
@pulls.html.conversation(issue, pullreq, comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
@pulls.html.conversation(issue, pullreq, comments, issueLabels, collaborators, milestones, labels, hasWritePermission, repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="commits">
|
<div class="tab-pane" id="commits">
|
||||||
@pulls.html.commits(dayByDayCommits, repository)
|
@pulls.html.commits(dayByDayCommits, Some(comments), repository)
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" id="files">
|
<div class="tab-pane" id="files">
|
||||||
@helper.html.diff(diffs, repository, Some(commits.head.id), Some(commits.last.id), true)
|
@helper.html.diff(diffs, repository, Some(commits.head.id), Some(commits.last.id), true, true, hasWritePermission, true)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|||||||
61
src/main/twirl/repo/commentform.scala.html
Normal file
61
src/main/twirl/repo/commentform.scala.html
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
@(commitId: String,
|
||||||
|
fileName: Option[String] = None,
|
||||||
|
oldLineNumber: Option[Int] = None,
|
||||||
|
newLineNumber: Option[Int] = None,
|
||||||
|
pullRequest: Boolean,
|
||||||
|
hasWritePermission: Boolean,
|
||||||
|
repository: service.RepositoryService.RepositoryInfo)(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
@import view.helpers._
|
||||||
|
@if(loginAccount.isDefined){
|
||||||
|
@if(!fileName.isDefined){<hr/><br/>}
|
||||||
|
<form method="POST" validate="true" style="max-width: 874px;">
|
||||||
|
@if(!fileName.isDefined){
|
||||||
|
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
|
||||||
|
}
|
||||||
|
<div class="box issue-comment-box">
|
||||||
|
<div class="box-content">
|
||||||
|
@helper.html.preview(repository, "", false, true, true, hasWritePermission, "width: 635px; height: 100px; max-height: 150px;", elastic = true)
|
||||||
|
</div>
|
||||||
|
@if(fileName.isDefined){
|
||||||
|
<div class="pull-right" style="margin-top: 10px;">
|
||||||
|
<input type="button" class="btn btn-default" value="Cancel"/>
|
||||||
|
<input type="submit" class="btn btn-success btn-inline-comment" formaction="@url(repository)/commit/@commitId/comment/new" value="Comment"/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if(!fileName.isDefined){
|
||||||
|
<div class="pull-right">
|
||||||
|
<input type="submit" class="btn btn-success" formaction="@url(repository)/commit/@commitId/comment/new" value="Comment on this commit"/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<input type="hidden" name="pullRequest" value="@pullRequest">
|
||||||
|
@if(fileName.isDefined){<input type="hidden" name="fileName" value="@fileName.get">}
|
||||||
|
@if(oldLineNumber.isDefined){<input type="hidden" name="oldLineNumber" value="@oldLineNumber.get">}
|
||||||
|
@if(newLineNumber.isDefined){<input type="hidden" name="newLineNumber" value="@newLineNumber.get">}
|
||||||
|
</form>
|
||||||
|
<script>
|
||||||
|
$('.btn-inline-comment').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$form = $(e.target).attr('disabled', 'disabled').closest('form');
|
||||||
|
var param = {};
|
||||||
|
$($form.serializeArray()).each(function(i, v) {
|
||||||
|
param[v.name] = v.value;
|
||||||
|
});
|
||||||
|
$.ajax({
|
||||||
|
url: '@url(repository)/commit/@commitId/comment/_data/new',
|
||||||
|
type: 'POST',
|
||||||
|
data: param
|
||||||
|
}).done(function(data) {
|
||||||
|
$form.closest('tr').removeClass('inline-comment-form').find('td').html('<td colspan="3"></td>').html(data);
|
||||||
|
$('#comment-list').append(data);
|
||||||
|
if (typeof $('#show-notes')[0] !== 'undefined' && !$('#show-notes')[0].checked) {
|
||||||
|
$('#comment-list').children('.inline-comment').hide();
|
||||||
|
}
|
||||||
|
}).fail(function(req) {
|
||||||
|
$('.btn-inline-comment').removeAttr('disabled');
|
||||||
|
$('#error-content', $form).html($.parseJSON(req.responseText).content);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@
|
|||||||
commit: util.JGitUtil.CommitInfo,
|
commit: util.JGitUtil.CommitInfo,
|
||||||
branches: List[String],
|
branches: List[String],
|
||||||
tags: List[String],
|
tags: List[String],
|
||||||
|
comments: List[model.Comment],
|
||||||
repository: service.RepositoryService.RepositoryInfo,
|
repository: service.RepositoryService.RepositoryInfo,
|
||||||
diffs: Seq[util.JGitUtil.DiffInfo],
|
diffs: Seq[util.JGitUtil.DiffInfo],
|
||||||
oldCommitId: Option[String])(implicit context: app.Context)
|
oldCommitId: Option[String],
|
||||||
|
hasWritePermission: Boolean)(implicit context: app.Context)
|
||||||
@import context._
|
@import context._
|
||||||
@import view.helpers._
|
@import view.helpers._
|
||||||
@import util.Implicits._
|
@import util.Implicits._
|
||||||
@@ -81,7 +83,14 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@helper.html.diff(diffs, repository, Some(commit.id), oldCommitId, true)
|
@helper.html.diff(diffs, repository, Some(commit.id), oldCommitId, true, false, hasWritePermission, true)
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" id="show-notes"> Show line notes below
|
||||||
|
</label>
|
||||||
|
<div id="comment-list">
|
||||||
|
@issues.html.commentlist(None, comments, hasWritePermission, repository, None)
|
||||||
|
</div>
|
||||||
|
@commentform(commitId = commitId, pullRequest = false, hasWritePermission = hasWritePermission, repository = repository)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<script>
|
<script>
|
||||||
@@ -122,6 +131,15 @@ $(function(){
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#show-notes').change(function() {
|
||||||
|
if (this.checked) {
|
||||||
|
$('.inline-comment').show();
|
||||||
|
} else {
|
||||||
|
$('.inline-comment').hide();
|
||||||
|
$('.diff .inline-comment').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
|||||||
45
src/main/twirl/repo/editcomment.scala.html
Normal file
45
src/main/twirl/repo/editcomment.scala.html
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
@(content: String, commentId: Int, owner: String, repository: String)(implicit context: app.Context)
|
||||||
|
@import context._
|
||||||
|
<span class="error-edit-content-@commentId error"></span>
|
||||||
|
@helper.html.attached(owner, repository){
|
||||||
|
<textarea style="width: 635px; height: 100px;" id="edit-content-@commentId">@content</textarea>
|
||||||
|
}
|
||||||
|
<div>
|
||||||
|
<input type="button" class="cancel-comment-@commentId btn btn-small btn-danger" value="Cancel"/>
|
||||||
|
<input type="button" class="update-comment-@commentId btn btn-small pull-right" value="Update comment"/>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
var curriedCallback = function($box) {
|
||||||
|
return function(data){
|
||||||
|
$('.update-comment-@commentId, .cancel-comment-@commentId', $box).removeAttr('disabled');
|
||||||
|
$('.commit-commentContent-@commentId').empty().html(data.content);
|
||||||
|
prettyPrint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).on('click', '.update-comment-@commentId', function(){
|
||||||
|
$box = $(this).closest('.box');
|
||||||
|
$('.update-comment-@commentId, .cancel-comment-@commentId', $box).attr('disabled', 'disabled');
|
||||||
|
$.ajax({
|
||||||
|
url: '@path/@owner/@repository/commit_comments/edit/@commentId',
|
||||||
|
type: 'POST',
|
||||||
|
data: {
|
||||||
|
content : $('#edit-content-@commentId', $box).val()
|
||||||
|
}
|
||||||
|
}).done(
|
||||||
|
curriedCallback($box)
|
||||||
|
).fail(function(req) {
|
||||||
|
$('.update-comment-@commentId, .cancel-comment-@commentId', $box).removeAttr('disabled');
|
||||||
|
$('.error-edit-content-@commentId', $box).text($.parseJSON(req.responseText).content);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.cancel-comment-@commentId', function(){
|
||||||
|
$box = $(this).closest('.box');
|
||||||
|
$('.update-comment-@commentId, .cancel-comment-@commentId', $box).attr('disabled', 'disabled');
|
||||||
|
$.get('@path/@owner/@repository/commit_comments/_data/@commentId', curriedCallback($box));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -127,7 +127,8 @@ $(function(){
|
|||||||
$.post('@url(repository)/_preview', {
|
$.post('@url(repository)/_preview', {
|
||||||
content : editor.getValue(),
|
content : editor.getValue(),
|
||||||
enableWikiLink : false,
|
enableWikiLink : false,
|
||||||
enableRefsLink : false
|
enableRefsLink : false,
|
||||||
|
enableTaskList : false
|
||||||
}, function(data){
|
}, function(data){
|
||||||
$('#preview').empty().append(
|
$('#preview').empty().append(
|
||||||
$('<div class="markdown-body" style="padding-left: 16px; padding-right: 16px;">').html(data));
|
$('<div class="markdown-body" style="padding-left: 16px; padding-right: 16px;">').html(data));
|
||||||
|
|||||||
@@ -7,21 +7,37 @@
|
|||||||
@if(!hasWritePermission){
|
@if(!hasWritePermission){
|
||||||
<h3>This is an empty repository</h3>
|
<h3>This is an empty repository</h3>
|
||||||
} else {
|
} else {
|
||||||
|
<h3><strong>Quick setup</strong> — if you've done this kind of thing before</h3>
|
||||||
|
<div class="empty-repo-options">
|
||||||
|
via <a href="@repository.httpUrl" class="git-protocol-selector">HTTP</a>
|
||||||
|
@if(settings.ssh && loginAccount.isDefined){
|
||||||
|
or
|
||||||
|
<a href="@repository.sshUrl(settings.sshPort.getOrElse(service.SystemSettingsService.DefaultSshPort), loginAccount.get.userName)" class="git-protocol-selector">SSH</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
<h3 style="margin-top: 30px;">Create a new repository on the command line</h3>
|
<h3 style="margin-top: 30px;">Create a new repository on the command line</h3>
|
||||||
<pre>
|
<pre>
|
||||||
touch README.md
|
touch README.md
|
||||||
git init
|
git init
|
||||||
git add README.md
|
git add README.md
|
||||||
git commit -m "first commit"
|
git commit -m "first commit"
|
||||||
git remote add origin @repository.httpUrl
|
git remote add origin <span class="live-clone-url">@repository.httpUrl</span>
|
||||||
git push -u origin master
|
git push -u origin master
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<h3 style="margin-top: 30px;">Push an existing repository from the command line</h3>
|
<h3 style="margin-top: 30px;">Push an existing repository from the command line</h3>
|
||||||
<pre>
|
<pre>
|
||||||
git remote add origin @repository.httpUrl
|
git remote add origin <span class="live-clone-url">@repository.httpUrl</span>
|
||||||
git push -u origin master
|
git push -u origin master
|
||||||
</pre>
|
</pre>
|
||||||
|
<script>
|
||||||
|
$(function(){
|
||||||
|
$('.git-protocol-selector').click(function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('.live-clone-url').text($(e.target).attr('href'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
@issues.drop((page - 1) * IssueLimit).take(IssueLimit).map { issue =>
|
@issues.drop((page - 1) * IssueLimit).take(IssueLimit).map { issue =>
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div class="pull-right muted">#@issue.issueId</div>
|
<div class="pull-right muted">#@issue.issueId</div>
|
||||||
<h4 style="margin-top: 0px;"><a href="@url(repository)/issues/@issue.issueId">@issue.title</a></h4>
|
<h4 style="margin-top: 0px;"><a href="@url(repository)/@if(issue.isPullRequest){pull} else {issues}/@issue.issueId">@issue.title</a></h4>
|
||||||
@if(issue.highlightText.nonEmpty){
|
@if(issue.highlightText.nonEmpty){
|
||||||
<pre>@Html(issue.highlightText)</pre>
|
<pre>@Html(issue.highlightText)</pre>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@helper.html.diff(diffs, repository, None, None, false)
|
@helper.html.diff(diffs, repository, None, None, false, false, false, false)
|
||||||
@if(hasWritePermission){
|
@if(hasWritePermission){
|
||||||
<div>
|
<div>
|
||||||
@if(pageName.isDefined){
|
@if(pageName.isDefined){
|
||||||
|
|||||||
@@ -226,6 +226,10 @@ div.box-header-small {
|
|||||||
text-shadow: 0 1px 0 #fff
|
text-shadow: 0 1px 0 #fff
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inline-comment div.box-header-small {
|
||||||
|
background: #f2f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
div.box-content {
|
div.box-content {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid #d8d8d8;
|
border: 1px solid #d8d8d8;
|
||||||
@@ -860,9 +864,10 @@ div.issue-participants {
|
|||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.issue-comment-box {
|
div.issue-comment-box, div.commit-comment-box {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
|
max-width: 820px;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.issue-comment-action {
|
div.issue-comment-action {
|
||||||
@@ -959,13 +964,45 @@ table.diff thead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.inlinediff td.insert, table.inlinediff td.equal, table.inlinediff td.delete {
|
table.inlinediff td.insert, table.inlinediff td.equal, table.inlinediff td.delete {
|
||||||
width: 100%;
|
width: 900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.insert, td.equal, td.delete, td.empty {
|
td.insert, td.equal, td.delete, td.empty {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.diff .add-comment {
|
||||||
|
position: absolute;
|
||||||
|
background: blue;
|
||||||
|
top: 0;
|
||||||
|
color: white;
|
||||||
|
padding: 2px;
|
||||||
|
border: solid 1px blue;
|
||||||
|
border-radius: 3px;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff .add-comment:hover {
|
||||||
|
padding: 4px;
|
||||||
|
top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff tbody tr.not-diff {
|
||||||
|
font-family: '"Helvetica Neue", Helvetica, Arial, sans-serif';
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.not-diff .box {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff tbody tr.not-diff:hover {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.diff tbody tr.not-diff:hover td{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Repository Settings */
|
/* Repository Settings */
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ $(function(){
|
|||||||
prettyPrint();
|
prettyPrint();
|
||||||
});
|
});
|
||||||
|
|
||||||
function displayErrors(data){
|
function displayErrors(data, elem){
|
||||||
var i = 0;
|
var i = 0;
|
||||||
$.each(data, function(key, value){
|
$.each(data, function(key, value){
|
||||||
$('#error-' + key.split(".").join("_")).text(value);
|
$('#error-' + key.split(".").join("_"), elem).text(value);
|
||||||
if(i === 0){
|
if(i === 0){
|
||||||
$('#' + key).focus();
|
$('#' + key, elem).focus();
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
$(function(){
|
$(function(){
|
||||||
$.each($('form[validate=true]'), function(i, form){
|
$(document).on('submit', 'form[validate=true]', validate);
|
||||||
$(form).submit(validate);
|
$(document).on('click', 'input[formaction]', function(e){
|
||||||
});
|
var form = $(e.target).parents('form');
|
||||||
$.each($('input[formaction]'), function(i, input){
|
$(form).attr('action', $(e.target).attr('formaction'));
|
||||||
$(input).click(function(){
|
|
||||||
var form = $(input).parents('form');
|
|
||||||
$(form).attr('action', $(input).attr('formaction'));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,7 +25,7 @@ function validate(e){
|
|||||||
form.data('validated', false);
|
form.data('validated', false);
|
||||||
} else {
|
} else {
|
||||||
form.data('validated', false);
|
form.data('validated', false);
|
||||||
displayErrors(data);
|
displayErrors(data, form);
|
||||||
}
|
}
|
||||||
}, 'json');
|
}, 'json');
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -78,6 +78,16 @@ diffview = {
|
|||||||
e.appendChild(document.createTextNode(text));
|
e.appendChild(document.createTextNode(text));
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addButton (e) {
|
||||||
|
var b = document.createElement("b");
|
||||||
|
b.appendChild(document.createTextNode("+"));
|
||||||
|
b.style.display = "none";
|
||||||
|
b.className = "add-comment";
|
||||||
|
e.appendChild(b);
|
||||||
|
e.style.position = "relative";
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
var tdata = document.createElement("thead");
|
var tdata = document.createElement("thead");
|
||||||
var node = document.createElement("tr");
|
var node = document.createElement("tr");
|
||||||
@@ -119,8 +129,8 @@ diffview = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function addCellsInline (row, tidx, tidx2, textLines, change) {
|
function addCellsInline (row, tidx, tidx2, textLines, change) {
|
||||||
row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString()));
|
row.appendChild(ctelt("th", "oldline", tidx == null ? "" : (tidx + 1).toString()));
|
||||||
row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString()));
|
row.appendChild(addButton(ctelt("th", "newline", tidx2 == null ? "" : (tidx2 + 1).toString())));
|
||||||
row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
|
row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user