summaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
authorPaul Martin <paul@paulsputer.com>2016-01-11 12:25:06 +0000
committerPaul Martin <paul@paulsputer.com>2016-03-27 17:09:16 +0100
commit2754961708ff0eb6030faf6db2edf8fbdae227d5 (patch)
tree827f0904a9e8a5db2282986230b374c42cfd69c1 /src/main/java
parent86401c3ff40c137a937a3dd83f12022fce5529b4 (diff)
downloadgitblit-2754961708ff0eb6030faf6db2edf8fbdae227d5.tar.gz
gitblit-2754961708ff0eb6030faf6db2edf8fbdae227d5.zip
Document edit capability via ProseMirror submodule #974
+ New docEdit page with links from docPage and docList + Bespoke menu system with full screen edit mode + npm required for building client side scripts + Ant script added for BuildUI which performs npm commands + Update font-awesome to 4.5.0 + Factor out to JGitUtils common code in BranchTicketService for EditFilePage + getTreeEntries + commitIndex + Merge capability for document editing
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/gitblit/tickets/BranchTicketService.java109
-rw-r--r--src/main/java/com/gitblit/utils/JGitUtils.java131
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocPage.html4
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocPage.java2
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocsPage.html4
-rw-r--r--src/main/java/com/gitblit/wicket/pages/DocsPage.java4
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditFilePage.html60
-rw-r--r--src/main/java/com/gitblit/wicket/pages/EditFilePage.java220
8 files changed, 428 insertions, 106 deletions
diff --git a/src/main/java/com/gitblit/tickets/BranchTicketService.java b/src/main/java/com/gitblit/tickets/BranchTicketService.java
index 83966939..7bef435a 100644
--- a/src/main/java/com/gitblit/tickets/BranchTicketService.java
+++ b/src/main/java/com/gitblit/tickets/BranchTicketService.java
@@ -19,7 +19,6 @@ import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -31,21 +30,17 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
-import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheBuilder;
import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.events.RefsChangedEvent;
import org.eclipse.jgit.events.RefsChangedListener;
-import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefRename;
-import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.RefUpdate.Result;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -338,7 +333,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi
Set<String> ignorePaths = new HashSet<String>();
ignorePaths.add(file);
- for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) {
+ for (DirCacheEntry entry : JGitUtils.getTreeEntries(db, BRANCH, ignorePaths)) {
builder.add(entry);
}
@@ -804,7 +799,7 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi
}
}
- for (DirCacheEntry entry : getTreeEntries(db, ignorePaths)) {
+ for (DirCacheEntry entry : JGitUtils.getTreeEntries(db, BRANCH, ignorePaths)) {
builder.add(entry);
}
@@ -816,108 +811,18 @@ public class BranchTicketService extends ITicketService implements RefsChangedLi
return newIndex;
}
- /**
- * Returns all tree entries that do not match the ignore paths.
- *
- * @param db
- * @param ignorePaths
- * @param dcBuilder
- * @throws IOException
- */
- private List<DirCacheEntry> getTreeEntries(Repository db, Collection<String> ignorePaths) throws IOException {
- List<DirCacheEntry> list = new ArrayList<DirCacheEntry>();
- TreeWalk tw = null;
- try {
- ObjectId treeId = db.resolve(BRANCH + "^{tree}");
- if (treeId == null) {
- // branch does not exist yet, could be migrating tickets
- return list;
- }
- tw = new TreeWalk(db);
- int hIdx = tw.addTree(treeId);
- tw.setRecursive(true);
-
- while (tw.next()) {
- String path = tw.getPathString();
- CanonicalTreeParser hTree = null;
- if (hIdx != -1) {
- hTree = tw.getTree(hIdx, CanonicalTreeParser.class);
- }
- if (!ignorePaths.contains(path)) {
- // add all other tree entries
- if (hTree != null) {
- final DirCacheEntry entry = new DirCacheEntry(path);
- entry.setObjectId(hTree.getEntryObjectId());
- entry.setFileMode(hTree.getEntryFileMode());
- list.add(entry);
- }
- }
- }
- } finally {
- if (tw != null) {
- tw.close();
- }
- }
- return list;
- }
-
private boolean commitIndex(Repository db, DirCache index, String author, String message) throws IOException, ConcurrentRefUpdateException {
+ final boolean forceCommit = true;
boolean success = false;
-
+
ObjectId headId = db.resolve(BRANCH + "^{commit}");
if (headId == null) {
// create the branch
createTicketsBranch(db);
- headId = db.resolve(BRANCH + "^{commit}");
- }
- ObjectInserter odi = db.newObjectInserter();
- try {
- // Create the in-memory index of the new/updated ticket
- ObjectId indexTreeId = index.writeTree(odi);
-
- // Create a commit object
- PersonIdent ident = new PersonIdent(author, "gitblit@localhost");
- CommitBuilder commit = new CommitBuilder();
- commit.setAuthor(ident);
- commit.setCommitter(ident);
- commit.setEncoding(Constants.ENCODING);
- commit.setMessage(message);
- commit.setParentId(headId);
- commit.setTreeId(indexTreeId);
-
- // Insert the commit into the repository
- ObjectId commitId = odi.insert(commit);
- odi.flush();
-
- RevWalk revWalk = new RevWalk(db);
- try {
- RevCommit revCommit = revWalk.parseCommit(commitId);
- RefUpdate ru = db.updateRef(BRANCH);
- ru.setNewObjectId(commitId);
- ru.setExpectedOldObjectId(headId);
- ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
- Result rc = ru.forceUpdate();
- switch (rc) {
- case NEW:
- case FORCED:
- case FAST_FORWARD:
- success = true;
- break;
- case REJECTED:
- case LOCK_FAILURE:
- throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
- ru.getRef(), rc);
- default:
- throw new JGitInternalException(MessageFormat.format(
- JGitText.get().updatingRefFailed, BRANCH, commitId.toString(),
- rc));
- }
- } finally {
- revWalk.close();
- }
- } finally {
- odi.close();
}
+
+ success = JGitUtils.commitIndex(db, BRANCH, index, headId, forceCommit, author, "gitblit@localhost", message);
+
return success;
}
diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java
index 7a008139..3aaad6d7 100644
--- a/src/main/java/com/gitblit/utils/JGitUtils.java
+++ b/src/main/java/com/gitblit/utils/JGitUtils.java
@@ -21,6 +21,7 @@ import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -36,16 +37,21 @@ import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.TagCommand;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.api.errors.JGitInternalException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffEntry.ChangeType;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawTextComparator;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.LargeObjectException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.errors.StopWalkException;
+import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.BlobBasedConfig;
import org.eclipse.jgit.lib.CommitBuilder;
import org.eclipse.jgit.lib.Constants;
@@ -63,6 +69,7 @@ import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.lib.TreeFormatter;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.merge.RecursiveMerger;
+import org.eclipse.jgit.merge.ThreeWayMerger;
import org.eclipse.jgit.revwalk.RevBlob;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
@@ -75,6 +82,7 @@ import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.FetchResult;
import org.eclipse.jgit.transport.RefSpec;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.OrTreeFilter;
@@ -2597,4 +2605,127 @@ public class JGitUtils {
+ "objects/" + oid;
}
+
+ /**
+ * Returns all tree entries that do not match the ignore paths.
+ *
+ * @param db
+ * @param ignorePaths
+ * @param dcBuilder
+ * @throws IOException
+ */
+ public static List<DirCacheEntry> getTreeEntries(Repository db, String branch, Collection<String> ignorePaths) throws IOException {
+ List<DirCacheEntry> list = new ArrayList<DirCacheEntry>();
+ TreeWalk tw = null;
+ try {
+ ObjectId treeId = db.resolve(branch + "^{tree}");
+ if (treeId == null) {
+ // branch does not exist yet
+ return list;
+ }
+ tw = new TreeWalk(db);
+ int hIdx = tw.addTree(treeId);
+ tw.setRecursive(true);
+
+ while (tw.next()) {
+ String path = tw.getPathString();
+ CanonicalTreeParser hTree = null;
+ if (hIdx != -1) {
+ hTree = tw.getTree(hIdx, CanonicalTreeParser.class);
+ }
+ if (!ignorePaths.contains(path)) {
+ // add all other tree entries
+ if (hTree != null) {
+ final DirCacheEntry entry = new DirCacheEntry(path);
+ entry.setObjectId(hTree.getEntryObjectId());
+ entry.setFileMode(hTree.getEntryFileMode());
+ list.add(entry);
+ }
+ }
+ }
+ } finally {
+ if (tw != null) {
+ tw.close();
+ }
+ }
+ return list;
+ }
+
+ public static boolean commitIndex(Repository db, String branch, DirCache index,
+ ObjectId parentId, boolean forceCommit,
+ String author, String authorEmail, String message) throws IOException, ConcurrentRefUpdateException {
+ boolean success = false;
+
+ ObjectId headId = db.resolve(branch + "^{commit}");
+ ObjectId baseId = parentId;
+ if (baseId == null || headId == null) { return false; }
+
+ ObjectInserter odi = db.newObjectInserter();
+ try {
+ // Create the in-memory index of the new/updated ticket
+ ObjectId indexTreeId = index.writeTree(odi);
+
+ // Create a commit object
+ PersonIdent ident = new PersonIdent(author, authorEmail);
+
+ if (forceCommit == false) {
+ ThreeWayMerger merger = MergeStrategy.RECURSIVE.newMerger(db, true);
+ merger.setObjectInserter(odi);
+ merger.setBase(baseId);
+ boolean mergeSuccess = merger.merge(indexTreeId, headId);
+
+ if (mergeSuccess) {
+ indexTreeId = merger.getResultTreeId();
+ } else {
+ //Manual merge required
+ return false;
+ }
+ }
+
+ CommitBuilder commit = new CommitBuilder();
+ commit.setAuthor(ident);
+ commit.setCommitter(ident);
+ commit.setEncoding(com.gitblit.Constants.ENCODING);
+ commit.setMessage(message);
+ commit.setParentId(headId);
+ commit.setTreeId(indexTreeId);
+
+ // Insert the commit into the repository
+ ObjectId commitId = odi.insert(commit);
+ odi.flush();
+
+ RevWalk revWalk = new RevWalk(db);
+ try {
+ RevCommit revCommit = revWalk.parseCommit(commitId);
+ RefUpdate ru = db.updateRef(branch);
+ ru.setForceUpdate(forceCommit);
+ ru.setNewObjectId(commitId);
+ ru.setExpectedOldObjectId(headId);
+ ru.setRefLogMessage("commit: " + revCommit.getShortMessage(), false);
+ Result rc = ru.update();
+
+ switch (rc) {
+ case NEW:
+ case FORCED:
+ case FAST_FORWARD:
+ success = true;
+ break;
+ case REJECTED:
+ case LOCK_FAILURE:
+ throw new ConcurrentRefUpdateException(JGitText.get().couldNotLockHEAD,
+ ru.getRef(), rc);
+ default:
+ throw new JGitInternalException(MessageFormat.format(
+ JGitText.get().updatingRefFailed, branch, commitId.toString(),
+ rc));
+ }
+ } finally {
+ revWalk.close();
+ }
+ } finally {
+ odi.close();
+ }
+ return success;
+ }
+
}
diff --git a/src/main/java/com/gitblit/wicket/pages/DocPage.html b/src/main/java/com/gitblit/wicket/pages/DocPage.html
index 2bb7e1bc..6df6f3a9 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/DocPage.html
@@ -12,7 +12,7 @@
<div class="docs" style="margin-top: -10px;">
<!-- doc nav links -->
<div style="float: right;" class="docnav">
- <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
+ <a wicket:id="editLink"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
</div>
<!-- document content -->
@@ -24,7 +24,7 @@
<div class="docs">
<!-- doc nav links -->
<div style="float: right;" class="docnav">
- <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
+ <a wicket:id="editLink"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
</div>
<!-- document content -->
diff --git a/src/main/java/com/gitblit/wicket/pages/DocPage.java b/src/main/java/com/gitblit/wicket/pages/DocPage.java
index 5ed9c400..fe05ada2 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DocPage.java
@@ -85,6 +85,8 @@ public class DocPage extends RepositoryPage {
}
// document page links
+ fragment.add(new BookmarkablePageLink<Void>("editLink", EditFilePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
fragment.add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
fragment.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.html b/src/main/java/com/gitblit/wicket/pages/DocsPage.html
index 2253205b..54306b1b 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocsPage.html
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.html
@@ -20,7 +20,7 @@
<div wicket:id="tabContent" class="tab-pane">
<!-- doc nav links -->
<div style="float: right;" class="docnav">
- <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
+ <a wicket:id="editLink"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
</div>
<div class="content" wicket:id="content"></div>
</div>
@@ -43,7 +43,7 @@
<td class="size"><span wicket:id="docSize">[doc size]</span></td>
<td class="treeLinks">
<span class="hidden-phone link">
- <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
+ <a wicket:id="view"><wicket:message key="gb.view"></wicket:message></a> | <a wicket:id="edit"><wicket:message key="gb.edit"></wicket:message></a> | <a wicket:id="raw"><wicket:message key="gb.raw"></wicket:message></a> | <a wicket:id="blame"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="history"><wicket:message key="gb.history"></wicket:message></a>
</span>
</td>
</tr>
diff --git a/src/main/java/com/gitblit/wicket/pages/DocsPage.java b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
index 8be9a573..93016920 100644
--- a/src/main/java/com/gitblit/wicket/pages/DocsPage.java
+++ b/src/main/java/com/gitblit/wicket/pages/DocsPage.java
@@ -103,6 +103,8 @@ public class DocsPage extends RepositoryPage {
public void populateItem(final Item<MarkupDocument> item) {
MarkupDocument doc = item.getModelObject();
// document page links
+ item.add(new BookmarkablePageLink<Void>("editLink", EditFilePage.class,
+ WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
item.add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
WicketUtils.newPathParameter(repositoryName, commitId, doc.documentPath)));
item.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
@@ -148,6 +150,8 @@ public class DocsPage extends RepositoryPage {
// links
item.add(new BookmarkablePageLink<Void>("view", DocPage.class, WicketUtils
.newPathParameter(repositoryName, commitId, entry.path)));
+ item.add(new BookmarkablePageLink<Void>("edit", EditFilePage.class, WicketUtils
+ .newPathParameter(repositoryName, commitId, entry.path)));
String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, commitId, entry.path);
item.add(new ExternalLink("raw", rawUrl));
item.add(new BookmarkablePageLink<Void>("blame", BlamePage.class, WicketUtils
diff --git a/src/main/java/com/gitblit/wicket/pages/EditFilePage.html b/src/main/java/com/gitblit/wicket/pages/EditFilePage.html
new file mode 100644
index 00000000..602149b7
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditFilePage.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:wicket="http://wicket.apache.org/dtds.data/wicket-xhtml1.3-strict.dtd"
+ xml:lang="en"
+ lang="en">
+
+<!-- contribute editor resources to the page header -->
+<wicket:head>
+ <link rel="stylesheet" href="gitblit-editor.min.css" />
+ <script type="text/javascript" src="gitblit-editor.min.js"></script>
+</wicket:head>
+
+<body>
+<wicket:extend>
+<div wicket:id="doc"></div>
+<wicket:fragment wicket:id="markupContent">
+ <div class="docs" style="margin-top: -10px;">
+
+ <!-- doc nav links -->
+ <div style="float: right;position: relative;z-index: 100;margin-top: 1px;border-radius: 0px 3px 0px 3px;" class="docnav">
+ <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
+ </div>
+ <div id="visualEditor"></div>
+ <form id="documentEditor" style="padding-top:5px;" wicket:id="documentEditor">
+ <textarea id="editor" wicket:id="content">[content]</textarea>
+
+ <div id="commitDialog" class="modal hide fade">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+ <h3>Commit Document Changes</h3>
+ </div>
+ <div class="modal-body">
+ <div wicket:id="commitAuthor"></div>
+ <textarea style="width:100%; resize:none" wicket:id="commitMessage"></textarea>
+ </div>
+ <div class="modal-footer">
+ <a href="#" data-dismiss="modal" class="btn">Continue editing</a>
+ <a href="#" onclick="commitChanges()" class="btn btn-primary">Commit changes</a>
+ </div>
+ </div>
+ </form>
+
+ </div>
+</wicket:fragment>
+
+<wicket:fragment wicket:id="plainContent">
+ <div class="docs">
+ <!-- doc nav links -->
+ <div style="float: right;" class="docnav">
+ <a wicket:id="blameLink"><wicket:message key="gb.blame"></wicket:message></a> | <a wicket:id="historyLink"><wicket:message key="gb.history"></wicket:message></a> | <a wicket:id="rawLink"><wicket:message key="gb.raw"></wicket:message></a>
+ </div>
+
+ <!-- document content -->
+ <div wicket:id="content">[content]</div>
+ </div>
+</wicket:fragment>
+
+</wicket:extend>
+</body>
+</html> \ No newline at end of file
diff --git a/src/main/java/com/gitblit/wicket/pages/EditFilePage.java b/src/main/java/com/gitblit/wicket/pages/EditFilePage.java
new file mode 100644
index 00000000..378f2a35
--- /dev/null
+++ b/src/main/java/com/gitblit/wicket/pages/EditFilePage.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2016 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.wicket.pages;
+
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.wicket.PageParameters;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.form.Form;
+import org.apache.wicket.markup.html.form.TextArea;
+import org.apache.wicket.markup.html.link.BookmarkablePageLink;
+import org.apache.wicket.markup.html.link.ExternalLink;
+import org.apache.wicket.markup.html.panel.Fragment;
+import org.apache.wicket.model.Model;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheBuilder;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+
+import com.gitblit.Constants;
+import com.gitblit.models.UserModel;
+import com.gitblit.servlet.RawServlet;
+import com.gitblit.utils.BugtraqProcessor;
+import com.gitblit.utils.JGitUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.wicket.CacheControl;
+import com.gitblit.wicket.GitBlitWebSession;
+import com.gitblit.wicket.CacheControl.LastModified;
+import com.gitblit.wicket.MarkupProcessor;
+import com.gitblit.wicket.MarkupProcessor.MarkupDocument;
+import com.gitblit.wicket.WicketUtils;
+
+@CacheControl(LastModified.BOOT)
+public class EditFilePage extends RepositoryPage {
+
+ public EditFilePage(final PageParameters params) {
+ super(params);
+
+ final UserModel currentUser = (GitBlitWebSession.get().getUser() != null) ? GitBlitWebSession.get().getUser() : UserModel.ANONYMOUS;
+
+ final String path = WicketUtils.getPath(params).replace("%2f", "/").replace("%2F", "/");
+ MarkupProcessor processor = new MarkupProcessor(app().settings(), app().xssFilter());
+
+ Repository r = getRepository();
+ RevCommit commit = JGitUtils.getCommit(r, objectId);
+ String [] encodings = getEncodings();
+
+ // Read raw markup content and transform it to html
+ String documentPath = path;
+ String markupText = JGitUtils.getStringContent(r, commit.getTree(), path, encodings);
+
+ // Hunt for document
+ if (StringUtils.isEmpty(markupText)) {
+ String name = StringUtils.stripFileExtension(path);
+
+ List<String> docExtensions = processor.getAllExtensions();
+ for (String ext : docExtensions) {
+ String checkName = name + "." + ext;
+ markupText = JGitUtils.getStringContent(r, commit.getTree(), checkName, encodings);
+ if (!StringUtils.isEmpty(markupText)) {
+ // found it
+ documentPath = path;
+ break;
+ }
+ }
+ }
+
+ if (markupText == null) {
+ markupText = "";
+ }
+
+ BugtraqProcessor bugtraq = new BugtraqProcessor(app().settings());
+ markupText = bugtraq.processText(getRepository(), repositoryName, markupText);
+
+ Fragment fragment;
+ String displayedCommitId = commit.getId().getName();
+ MarkupDocument markupDoc = processor.parse(repositoryName, displayedCommitId, documentPath, markupText);
+ logger.info("Loading Edit File page: " + displayedCommitId);
+
+ if (currentUser.canEdit(getRepositoryModel())) {
+
+ final Model<String> documentContent = new Model<String>(markupDoc.markup);
+ final Model<String> commitMessage = new Model<String>("Document update");
+ final Model<String> commitIdAtLoad = new Model<String>(displayedCommitId);
+
+ fragment = new Fragment("doc", "markupContent", this);
+
+ Form<Void> form = new Form<Void>("documentEditor") {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void onSubmit() {
+
+ final Repository repository = getRepository();
+ final String document = documentContent.getObject();
+ final String message = commitMessage.getObject();
+
+ final String branchName = JGitUtils.getBranch(getRepository(), objectId).getName();
+ final String authorEmail = StringUtils.isEmpty(currentUser.emailAddress) ? (currentUser.username + "@gitblit") : currentUser.emailAddress;
+
+ boolean success = false;
+
+ try {
+ ObjectId docAtLoad = getRepository().resolve(commitIdAtLoad.getObject());
+
+ logger.info("Commiting Edit File page: " + commitIdAtLoad.getObject());
+
+ DirCache index = DirCache.newInCore();
+ DirCacheBuilder builder = index.builder();
+ byte[] bytes = document.getBytes( Constants.ENCODING );
+
+ final DirCacheEntry fileUpdate = new DirCacheEntry(path);
+ fileUpdate.setLength(bytes.length);
+ fileUpdate.setLastModified(System.currentTimeMillis());
+ fileUpdate.setFileMode(FileMode.REGULAR_FILE);
+ fileUpdate.setObjectId(repository.newObjectInserter().insert( org.eclipse.jgit.lib.Constants.OBJ_BLOB, bytes ));
+ builder.add(fileUpdate);
+
+ Set<String> ignorePaths = new HashSet<String>();
+ ignorePaths.add(path);
+
+ for (DirCacheEntry entry : JGitUtils.getTreeEntries(repository, branchName, ignorePaths)) {
+ builder.add(entry);
+ }
+
+ builder.finish();
+
+ final boolean forceCommit = false;
+
+ success = JGitUtils.commitIndex(repository, branchName, index, docAtLoad, forceCommit, currentUser.getDisplayName(), authorEmail, message);
+
+ } catch (IOException | ConcurrentRefUpdateException e) {
+ e.printStackTrace();
+ }
+
+ if (success == false) {
+ EditFilePage.this.error("Unable to commit document " + path, false);
+ return;
+ }
+
+ setResponsePage(EditFilePage.class, params);
+ return;
+ }
+ };
+
+ final TextArea<String> docIO = new TextArea<String>("content", documentContent);
+ docIO.setOutputMarkupId(false);
+
+ form.add(new Label("commitAuthor", String.format("%s <%s>", currentUser.getDisplayName(), currentUser.emailAddress)));
+ form.add(new TextArea<String>("commitMessage", commitMessage));
+
+
+ form.setOutputMarkupId(false);
+ form.add(docIO);
+
+ addBottomScriptInline("attachDocumentEditor(document.querySelector('textarea#editor'), $('#commitDialog'));");
+
+ fragment.add(form);
+
+ } else {
+
+ final Model<String> documentContent = new Model<String>(markupDoc.html);
+
+ fragment = new Fragment("doc", "plainContent", this);
+
+ fragment.add(new Label("content", documentContent).setEscapeModelStrings(false));
+ }
+
+ // document page links
+ fragment.add(new BookmarkablePageLink<Void>("blameLink", BlamePage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
+ fragment.add(new BookmarkablePageLink<Void>("historyLink", HistoryPage.class,
+ WicketUtils.newPathParameter(repositoryName, objectId, documentPath)));
+ String rawUrl = RawServlet.asLink(getContextUrl(), repositoryName, objectId, documentPath);
+ fragment.add(new ExternalLink("rawLink", rawUrl));
+
+ add(fragment);
+
+ }
+
+ @Override
+ protected String getPageName() {
+ return getString("gb.docs");
+ }
+
+ @Override
+ protected boolean isCommitPage() {
+ return true;
+ }
+
+ @Override
+ protected Class<? extends BasePage> getRepoNavPageClass() {
+ return DocsPage.class;
+ }
+
+
+
+}