(refs #13)File editing (add, edit and delete) in repository viewer is available.

This commit is contained in:
takezoe
2014-04-27 02:02:36 +09:00
parent 0e5591017a
commit a97edb7ef5
6 changed files with 311 additions and 153 deletions

View File

@@ -15,7 +15,7 @@ import org.eclipse.jgit.treewalk._
import java.util.zip.{ZipEntry, ZipOutputStream} import java.util.zip.{ZipEntry, ZipOutputStream}
import jp.sf.amateras.scalatra.forms._ import jp.sf.amateras.scalatra.forms._
import org.eclipse.jgit.dircache.DirCache import org.eclipse.jgit.dircache.DirCache
import org.eclipse.jgit.revwalk.RevWalk import org.eclipse.jgit.revwalk.{RevCommit, RevWalk}
class RepositoryViewerController extends RepositoryViewerControllerBase class RepositoryViewerController extends RepositoryViewerControllerBase
with RepositoryService with AccountService with ActivityService with ReferrerAuthenticator with CollaboratorsAuthenticator with RepositoryService with AccountService with ActivityService with ReferrerAuthenticator with CollaboratorsAuthenticator
@@ -26,14 +26,40 @@ class RepositoryViewerController extends RepositoryViewerControllerBase
trait RepositoryViewerControllerBase extends ControllerBase { trait RepositoryViewerControllerBase extends ControllerBase {
self: RepositoryService with AccountService with ActivityService with ReferrerAuthenticator with CollaboratorsAuthenticator => self: RepositoryService with AccountService with ActivityService with ReferrerAuthenticator with CollaboratorsAuthenticator =>
case class EditorForm(content: String, message: Option[String], charset: String) case class EditorForm(
branch: String,
path: String,
content: String,
message: Option[String],
charset: String,
newFileName: String,
oldFileName: Option[String]
)
case class DeleteForm(
branch: String,
path: String,
message: Option[String],
fileName: String
)
val editorForm = mapping( val editorForm = mapping(
"content" -> trim(label("Content", text())), "branch" -> trim(label("Branch", text(required))),
"message" -> trim(label("Messgae", optional(text()))), "path" -> trim(label("Path", text())),
"charset" -> trim(label("Charset", text())) "content" -> trim(label("Content", text(required))),
"message" -> trim(label("Message", optional(text()))),
"charset" -> trim(label("Charset", text(required))),
"newFileName" -> trim(label("Filename", text(required))),
"oldFileName" -> trim(label("Old filename", optional(text())))
)(EditorForm.apply) )(EditorForm.apply)
val deleteForm = mapping(
"branch" -> trim(label("Branch", text(required))),
"path" -> trim(label("Path", text())),
"message" -> trim(label("Message", optional(text()))),
"fileName" -> trim(label("Filename", text(required)))
)(DeleteForm.apply)
/** /**
* Returns converted HTML from Markdown for preview. * Returns converted HTML from Markdown for preview.
*/ */
@@ -82,96 +108,68 @@ trait RepositoryViewerControllerBase extends ControllerBase {
} }
}) })
/** get("/:owner/:repository/new/*")(collaboratorsOnly { repository =>
* Displays the file content of the specified branch or commit. val (branch, path) = splitPath(repository, multiParams("splat").head)
*/ repo.html.editor(branch, repository, if(path.length == 0) Nil else path.split("/").toList,
None, JGitUtil.ContentInfo("text", None, Some("UTF-8")))
})
get("/:owner/:repository/edit/*")(collaboratorsOnly { repository => get("/:owner/:repository/edit/*")(collaboratorsOnly { repository =>
val (id, path) = splitPath(repository, multiParams("splat").head) val (branch, path) = splitPath(repository, multiParams("splat").head)
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id)) val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
@scala.annotation.tailrec getPathObjectId(git, path, revCommit).map { objectId =>
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match { val paths = path.split("/")
case true if(walk.getPathString == path) => Some(walk.getObjectId(0)) repo.html.editor(branch, repository, paths.take(paths.size - 1).toList, Some(paths.last),
case true => getPathObjectId(path, walk) JGitUtil.getContentInfo(git, path, objectId))
case false => None
}
using(new TreeWalk(git.getRepository)){ treeWalk =>
treeWalk.addTree(revCommit.getTree)
treeWalk.setRecursive(true)
getPathObjectId(path, treeWalk)
} map { objectId =>
// Viewer
val large = FileUtil.isLarge(git.getRepository.getObjectDatabase.open(objectId).getSize)
val viewer = if(FileUtil.isImage(path)) "image" else if(large) "large" else "other"
val bytes = if(viewer == "other") JGitUtil.getContentFromId(git, objectId, false) else None
val content = if(viewer == "other"){
if(bytes.isDefined && FileUtil.isText(bytes.get)){
// text
JGitUtil.ContentInfo("text", Some(StringUtil.convertFromByteArray(bytes.get)), Some(StringUtil.detectEncoding(bytes.get)))
} else {
// binary
JGitUtil.ContentInfo("binary", None, None)
}
} else {
// image or large
JGitUtil.ContentInfo(viewer, None, None)
}
repo.html.editor(id, repository, path.split("/").toList, content, new JGitUtil.CommitInfo(revCommit))
} getOrElse NotFound } getOrElse NotFound
} }
}) })
post("/:owner/:repository/edit/*", editorForm)(collaboratorsOnly { (form, repository) => get("/:owner/:repository/remove/*")(collaboratorsOnly { repository =>
val (id, path) = splitPath(repository, multiParams("splat").head) val (branch, path) = splitPath(repository, multiParams("splat").head)
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(branch))
LockUtil.lock(s"${repository.owner}/${repository.name}"){ getPathObjectId(git, path, revCommit).map { objectId =>
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => val paths = path.split("/")
val loginAccount = context.loginAccount.get repo.html.delete(branch, repository, paths.take(paths.size - 1).toList, paths.last,
val builder = DirCache.newInCore.builder() JGitUtil.getContentInfo(git, path, objectId))
val inserter = git.getRepository.newObjectInserter() } getOrElse NotFound
val headName = s"refs/heads/${id}"
val headTip = git.getRepository.resolve(s"refs/heads/${id}")
JGitUtil.processTree(git, headTip){ (treePath, tree) =>
if(treePath != path){
builder.add(JGitUtil.createDirCacheEntry(treePath, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
builder.add(JGitUtil.createDirCacheEntry(path, FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, form.content.getBytes(form.charset))))
builder.finish()
val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter),
loginAccount.fullName, loginAccount.mailAddress, form.message.getOrElse(s"Update ${path.split("/").last}"))
inserter.flush()
inserter.release()
// update refs
val refUpdate = git.getRepository.updateRef(headName)
refUpdate.setNewObjectId(commitId)
refUpdate.setForceUpdate(false)
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
//refUpdate.setRefLogMessage("merged", true)
refUpdate.update()
// record activity
recordPushActivity(repository.owner, repository.name, loginAccount.userName, id,
List(new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))))
// TODO invoke hook
redirect(s"/${repository.owner}/${repository.name}/blob/${id}/${path}")
}
} }
}) })
post("/:owner/:repository/create", editorForm)(collaboratorsOnly { (form, repository) =>
commitFile(repository, form.branch, form.path, Some(form.newFileName), None, form.content, form.charset,
form.message.getOrElse(s"Create ${form.newFileName}"))
redirect(s"/${repository.owner}/${repository.name}/blob/${form.branch}/${
if(form.path.length == 0) form.newFileName else s"${form.path}/${form.newFileName}"
}")
})
post("/:owner/:repository/update", editorForm)(collaboratorsOnly { (form, repository) =>
commitFile(repository, form.branch, form.path, Some(form.newFileName), form.oldFileName, form.content, form.charset,
if(form.oldFileName.exists(_ == form.newFileName)){
form.message.getOrElse(s"Update ${form.newFileName}")
} else {
form.message.getOrElse(s"Rename ${form.oldFileName.get} to ${form.newFileName}")
})
redirect(s"/${repository.owner}/${repository.name}/blob/${form.branch}/${
if(form.path.length == 0) form.newFileName else s"${form.path}/${form.newFileName}"
}")
})
post("/:owner/:repository/remove", deleteForm)(collaboratorsOnly { (form, repository) =>
commitFile(repository, form.branch, form.path, None, Some(form.fileName), "", "",
form.message.getOrElse(s"Delete ${form.fileName}"))
redirect(s"/${repository.owner}/${repository.name}/tree/${form.branch}${if(form.path.length == 0) "" else form.path}")
})
/** /**
* Displays the file content of the specified branch or commit. * Displays the file content of the specified branch or commit.
*/ */
@@ -181,19 +179,7 @@ trait RepositoryViewerControllerBase extends ControllerBase {
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git => using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id)) val revCommit = JGitUtil.getRevCommitFromId(git, git.getRepository.resolve(id))
getPathObjectId(git, path, revCommit).map { objectId =>
@scala.annotation.tailrec
def getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
case true if(walk.getPathString == path) => Some(walk.getObjectId(0))
case true => getPathObjectId(path, walk)
case false => None
}
using(new TreeWalk(git.getRepository)){ treeWalk =>
treeWalk.addTree(revCommit.getTree)
treeWalk.setRecursive(true)
getPathObjectId(path, treeWalk)
} map { objectId =>
if(raw){ if(raw){
// Download // Download
defining(JGitUtil.getContentFromId(git, objectId, false).get){ bytes => defining(JGitUtil.getContentFromId(git, objectId, false).get){ bytes =>
@@ -201,26 +187,8 @@ trait RepositoryViewerControllerBase extends ControllerBase {
bytes bytes
} }
} else { } else {
// Viewer repo.html.blob(id, repository, path.split("/").toList, JGitUtil.getContentInfo(git, path, objectId),
val large = FileUtil.isLarge(git.getRepository.getObjectDatabase.open(objectId).getSize) new JGitUtil.CommitInfo(revCommit), hasWritePermission(repository.owner, repository.name, context.loginAccount))
val viewer = if(FileUtil.isImage(path)) "image" else if(large) "large" else "other"
val bytes = if(viewer == "other") JGitUtil.getContentFromId(git, objectId, false) else None
val content = if(viewer == "other"){
if(bytes.isDefined && FileUtil.isText(bytes.get)){
// text
JGitUtil.ContentInfo("text", Some(StringUtil.convertFromByteArray(bytes.get)), Some(StringUtil.detectEncoding(bytes.get)))
} else {
// binary
JGitUtil.ContentInfo("binary", None, None)
}
} else {
// image or large
JGitUtil.ContentInfo(viewer, None, None)
}
repo.html.blob(id, repository, path.split("/").toList, content, new JGitUtil.CommitInfo(revCommit),
hasWritePermission(repository.owner, repository.name, context.loginAccount))
} }
} getOrElse NotFound } getOrElse NotFound
} }
@@ -395,4 +363,70 @@ trait RepositoryViewerControllerBase extends ControllerBase {
} }
} }
private def commitFile(repository: service.RepositoryService.RepositoryInfo,
branch: String, path: String, newFileName: Option[String], oldFileName: Option[String],
content: String, charset: String, message: String) = {
val newPath = newFileName.map { newFileName => if(path.length == 0) newFileName else s"${path}/${newFileName}" }
val oldPath = oldFileName.map { oldFileName => if(path.length == 0) oldFileName else s"${path}/${oldFileName}" }
LockUtil.lock(s"${repository.owner}/${repository.name}"){
using(Git.open(getRepositoryDir(repository.owner, repository.name))){ git =>
val loginAccount = context.loginAccount.get
val builder = DirCache.newInCore.builder()
val inserter = git.getRepository.newObjectInserter()
val headName = s"refs/heads/${branch}"
val headTip = git.getRepository.resolve(s"refs/heads/${branch}")
JGitUtil.processTree(git, headTip){ (path, tree) =>
if(path != newPath && !oldPath.exists(_ == path)){
builder.add(JGitUtil.createDirCacheEntry(path, tree.getEntryFileMode, tree.getEntryObjectId))
}
}
newPath.foreach { newPath =>
builder.add(JGitUtil.createDirCacheEntry(newPath, FileMode.REGULAR_FILE,
inserter.insert(Constants.OBJ_BLOB, content.getBytes(charset))))
builder.finish()
}
val commitId = JGitUtil.createNewCommit(git, inserter, headTip, builder.getDirCache.writeTree(inserter),
loginAccount.fullName, loginAccount.mailAddress, message)
inserter.flush()
inserter.release()
// update refs
val refUpdate = git.getRepository.updateRef(headName)
refUpdate.setNewObjectId(commitId)
refUpdate.setForceUpdate(false)
refUpdate.setRefLogIdent(new PersonIdent(loginAccount.fullName, loginAccount.mailAddress))
//refUpdate.setRefLogMessage("merged", true)
refUpdate.update()
// record activity
recordPushActivity(repository.owner, repository.name, loginAccount.userName, branch,
List(new CommitInfo(JGitUtil.getRevCommitFromId(git, commitId))))
// TODO invoke hook
}
}
}
private def getPathObjectId(git: Git, path: String, revCommit: RevCommit): Option[ObjectId] = {
@scala.annotation.tailrec
def _getPathObjectId(path: String, walk: TreeWalk): Option[ObjectId] = walk.next match {
case true if(walk.getPathString == path) => Some(walk.getObjectId(0))
case true => _getPathObjectId(path, walk)
case false => None
}
using(new TreeWalk(git.getRepository)){ treeWalk =>
treeWalk.addTree(revCommit.getTree)
treeWalk.setRecursive(true)
_getPathObjectId(path, treeWalk)
}
}
} }

View File

@@ -550,6 +550,26 @@ object JGitUtil {
} }
} }
def getContentInfo(git: Git, path: String, objectId: ObjectId): ContentInfo = {
// Viewer
val large = FileUtil.isLarge(git.getRepository.getObjectDatabase.open(objectId).getSize)
val viewer = if(FileUtil.isImage(path)) "image" else if(large) "large" else "other"
val bytes = if(viewer == "other") JGitUtil.getContentFromId(git, objectId, false) else None
if(viewer == "other"){
if(bytes.isDefined && FileUtil.isText(bytes.get)){
// text
ContentInfo("text", Some(StringUtil.convertFromByteArray(bytes.get)), Some(StringUtil.detectEncoding(bytes.get)))
} else {
// binary
ContentInfo("binary", None, None)
}
} else {
// image or large
ContentInfo(viewer, None, None)
}
}
/** /**
* Get object content of the given object id as byte array from the Git repository. * Get object content of the given object id as byte array from the Git repository.
* *

View File

@@ -35,6 +35,9 @@
} }
<a class="btn btn-mini" href="?raw=true">Raw</a> <a class="btn btn-mini" href="?raw=true">Raw</a>
<a class="btn btn-mini" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a> <a class="btn btn-mini" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a>
@if(hasWritePermission){
<a class="btn btn-mini btn-danger" href="@url(repository)/remove/@encodeRefName(branch)/@pathList.mkString("/")">Delete</a>
}
</div> </div>
</th> </th>
</tr> </tr>

View File

@@ -0,0 +1,110 @@
@(branch: String,
repository: service.RepositoryService.RepositoryInfo,
pathList: List[String],
fileName: String,
content: util.JGitUtil.ContentInfo)(implicit context: app.Context)
@import context._
@import view.helpers._
@html.main(s"Deleting ${path} at ${fileName} - ${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository)
@tab(branch, repository, "files")
<form method="POST" action="@url(repository)/remove" validate="true">
<div class="head">
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
@pathList.zipWithIndex.map { case (section, i) =>
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
}
@fileName
<input type="hidden" name="fileName" id="fileName" value="@fileName"/>
<input type="hidden" name="branch" id="branch" value="@branch"/>
<input type="hidden" name="path" id="path" value="@pathList.mkString("/")"/>
</div>
<table class="table table-bordered">
<th style="font-weight: normal;" class="box-header">
@fileName
<div class="pull-right align-right">
<a href="@url(repository)/blob/@branch/@{(pathList ::: List(fileName)).mkString("/")}" class="btn btn-small">View</a>
</div>
</th>
<tr>
<td>
<div id="diffText"></div>
<textarea id="newText" style="display: none;"></textarea>
<textarea id="oldText" style="display: none;">@content.content</textarea>
</td>
</tr>
</table>
<div class="issue-avatar-image">@avatar(loginAccount.get.userName, 48)</div>
<div class="box issue-comment-box">
<div class="box-content">
<div>
<strong>Commit changes</strong>
</div>
<div>
<input type="text" name="message" style="width: 98%;"/>
</div>
<div style="text-align: right;">
<a href="@url(repository)/blob/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-danger">Cancel</a>
<input type="submit" id="commit" class="btn btn-success" value="Commit changes"/>
</div>
</div>
</div>
</form>
}
<script type="text/javascript" src="@assets/jsdifflib/difflib.js"></script>
<script type="text/javascript" src="@assets/jsdifflib/diffview.js"></script>
<link href="@assets/jsdifflib/diffview.css" type="text/css" rel="stylesheet" />
<style type="text/css">
table.inlinediff {
width: 100%;
}
table.inlinediff thead {
display: none;
}
td.insert, td.equal, td.delete {
width: 100%;
}
</style>
<script>
function diffUsingJS(oldTextId, newTextId, outputId) {
// get the baseText and newText values from the two textboxes, and split them into lines
var oldText = document.getElementById(oldTextId).value;
if(oldText == ''){
var oldLines = [];
} else {
var oldLines = difflib.stringAsLines(oldText);
}
var newText = document.getElementById(newTextId).value
if(newText == ''){
var newLines = [];
} else {
var newLines = difflib.stringAsLines(newText);
}
// create a SequenceMatcher instance that diffs the two sets of lines
var sm = new difflib.SequenceMatcher(oldLines, newLines);
// get the opcodes from the SequenceMatcher instance
// opcodes is a list of 3-tuples describing what changes should be made to the base text
// in order to yield the new text
var opcodes = sm.get_opcodes();
var diffoutputdiv = document.getElementById(outputId);
while (diffoutputdiv.firstChild) diffoutputdiv.removeChild(diffoutputdiv.firstChild);
// build the diff view and add it to the current DOM
diffoutputdiv.appendChild(diffview.buildView({
baseTextLines: oldLines,
newTextLines: newLines,
opcodes: opcodes,
contextSize: 4,
viewType: 1
}));
}
$(function(){
diffUsingJS('oldText', 'newText', 'diffText');
});
</script>

View File

@@ -1,47 +1,32 @@
@(branch: String, @(branch: String,
repository: service.RepositoryService.RepositoryInfo, repository: service.RepositoryService.RepositoryInfo,
pathList: List[String], pathList: List[String],
content: util.JGitUtil.ContentInfo, fileName: Option[String],
latestCommit: util.JGitUtil.CommitInfo)(implicit context: app.Context) content: util.JGitUtil.ContentInfo)(implicit context: app.Context)
@import context._ @import context._
@import view.helpers._ @import view.helpers._
@html.main(s"${repository.owner}/${repository.name}", Some(repository)) { @html.main(if(fileName.isEmpty) "New File" else s"Editing ${path} at ${fileName} - ${repository.owner}/${repository.name}", Some(repository)) {
@html.header("code", repository) @html.header("code", repository)
@tab(branch, repository, "files") @tab(branch, repository, "files")
<div class="head"> <form method="POST" action="@url(repository)/@if(fileName.isEmpty){create}else{update}" validate="true">
<a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> / <span class="error" id="error-newFileName"></span>
@pathList.zipWithIndex.map { case (section, i) => <div class="head">
@if(i == pathList.length - 1){ <a href="@url(repository)/tree/@encodeRefName(branch)">@repository.name</a> /
@section @pathList.zipWithIndex.map { case (section, i) =>
} else {
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> / <a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
} }
<input type="text" name="newFileName" id="newFileName" placeholder="Name your file..." value="@fileName"/>
<input type="hidden" name="oldFileName" id="oldFileName" value="@fileName"/>
<input type="hidden" name="branch" id="branch" value="@branch"/>
<input type="hidden" name="path" id="path" value="@pathList.mkString("/")"/>
</div>
<style type="text/css" media="screen">
#editor {
width: 100%;
height: 600px;
} }
</div> </style>
<style type="text/css" media="screen">
#editor {
width: 100%;
height: 600px;
}
</style>
<form method="POST" action="@url(repository)/edit/@encodeRefName(branch)/@pathList.mkString("/")">
<table class="table table-bordered"> <table class="table table-bordered">
@*
<tr>
<th style="font-weight: normal;">
<div class="pull-left">
@avatar(latestCommit, 20)
@user(latestCommit.committer, latestCommit.mailAddress, "username strong")
<span class="muted">@datetime(latestCommit.time)</span>
<a href="@url(repository)/commit/@latestCommit.id" class="commit-message">@link(latestCommit.summary, repository)</a>
</div>
<div class="btn-group pull-right">
<a class="btn btn-mini" href="?raw=true">Raw</a>
<a class="btn btn-mini" href="@url(repository)/commits/@encodeRefName(branch)/@pathList.mkString("/")">History</a>
</div>
</th>
</tr>
*@
<tr> <tr>
<td> <td>
<div id="editor"></div> <div id="editor"></div>
@@ -55,14 +40,14 @@
<strong>Commit changes</strong> <strong>Commit changes</strong>
</div> </div>
<div> <div>
<input type="text" name="message" style="width: 98%;" placeholder="Update @pathList.last"/> <input type="text" name="message" style="width: 98%;"/>
</div> </div>
<div style="text-align: right;"> <div style="text-align: right;">
<a href="@url(repository)/blob/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-danger">Cancel</a> <a href="@url(repository)/blob/@encodeRefName(branch)/@pathList.mkString("/")" class="btn btn-danger">Cancel</a>
<input type="submit" id="commit" class="btn btn-success" value="Commit changes" disabled="true"/> <input type="submit" id="commit" class="btn btn-success" value="Commit changes" disabled="true"/>
<input type="hidden" id="charset" name="charset" value="@content.charset"/> <input type="hidden" id="charset" name="charset" value="@content.charset"/>
<input type="hidden" id="content" name="content" value=""/> <input type="hidden" id="content" name="content" value=""/>
<input type="hidden" id="initial" value="@content.content.get"/> <input type="hidden" id="initial" value="@content.content"/>
</div> </div>
</div> </div>
</div> </div>
@@ -74,7 +59,9 @@ $(function(){
$('#editor').text($('#initial').val()); $('#editor').text($('#initial').val());
var editor = ace.edit("editor"); var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai"); editor.setTheme("ace/theme/monokai");
editor.getSession().setMode("ace/mode/@editorType(pathList.last)"); @if(fileName.isDefined){
editor.getSession().setMode("ace/mode/@editorType(fileName.get)");
}
editor.on('change', function(){ editor.on('change', function(){
$('#commit').attr('disabled', editor.getValue() == $('#initial').val()); $('#commit').attr('disabled', editor.getValue() == $('#initial').val());
}); });
@@ -82,5 +69,7 @@ $(function(){
$('#commit').click(function(){ $('#commit').click(function(){
$('#content').val(editor.getValue()); $('#content').val(editor.getValue());
}); });
}) })
</script> </script>

View File

@@ -19,6 +19,8 @@
@pathList.zipWithIndex.map { case (section, i) => @pathList.zipWithIndex.map { case (section, i) =>
<a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> / <a href="@url(repository)/tree/@encodeRefName(branch)/@pathList.take(i + 1).mkString("/")">@section</a> /
} }
<!-- TODO Add new file icon for committers -->
<a href="@url(repository)/new/@encodeRefName(branch)/@pathList.mkString("/")">+</a>
</div> </div>
<div class="box"> <div class="box">
<table class="table table-file-list" style="border: 1px solid silver;"> <table class="table table-file-list" style="border: 1px solid silver;">