From 2754961708ff0eb6030faf6db2edf8fbdae227d5 Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Mon, 11 Jan 2016 12:25:06 +0000 Subject: 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 --- src/main/java/com/gitblit/utils/JGitUtils.java | 131 +++++++++++++++++++++++++ 1 file changed, 131 insertions(+) (limited to 'src/main/java/com/gitblit/utils/JGitUtils.java') 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 getTreeEntries(Repository db, String branch, Collection ignorePaths) throws IOException { + List list = new ArrayList(); + 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; + } + } -- cgit v1.2.3 From 795ce2765773156da868720303a6b2638d14820b Mon Sep 17 00:00:00 2001 From: Paul Martin Date: Sun, 27 Mar 2016 17:05:38 +0100 Subject: Friendly URL for EditFile Page #974 + Prevent Edit of old doc version --- src/main/java/com/gitblit/utils/JGitUtils.java | 17 +++++++++++++++++ src/main/java/com/gitblit/wicket/GitBlitWebApp.java | 2 ++ .../java/com/gitblit/wicket/GitBlitWebApp.properties | 7 ++++++- .../java/com/gitblit/wicket/pages/EditFilePage.html | 4 ++-- .../java/com/gitblit/wicket/pages/EditFilePage.java | 20 ++++++++++---------- 5 files changed, 37 insertions(+), 13 deletions(-) (limited to 'src/main/java/com/gitblit/utils/JGitUtils.java') diff --git a/src/main/java/com/gitblit/utils/JGitUtils.java b/src/main/java/com/gitblit/utils/JGitUtils.java index 3aaad6d7..90d40020 100644 --- a/src/main/java/com/gitblit/utils/JGitUtils.java +++ b/src/main/java/com/gitblit/utils/JGitUtils.java @@ -2727,5 +2727,22 @@ public class JGitUtils { } return success; } + + /** + * Returns true if the commit identified by commitId is at the tip of it's branch. + * + * @param repository + * @param commitId + * @return true if the given commit is the tip + */ + public static boolean isTip(Repository repository, String commitId) { + try { + RefModel tip = getBranch(repository, commitId); + return (tip != null); + } catch (Exception e) { + LOGGER.error("Failed to determine isTip", e); + } + return false; + } } diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java index 296c2544..c10d8873 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.java +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.java @@ -59,6 +59,7 @@ import com.gitblit.wicket.pages.CommitPage; import com.gitblit.wicket.pages.ComparePage; import com.gitblit.wicket.pages.DocPage; import com.gitblit.wicket.pages.DocsPage; +import com.gitblit.wicket.pages.EditFilePage; import com.gitblit.wicket.pages.EditMilestonePage; import com.gitblit.wicket.pages.EditRepositoryPage; import com.gitblit.wicket.pages.EditTicketPage; @@ -230,6 +231,7 @@ public class GitBlitWebApp extends WebApplication implements GitblitWicketApp { // setup the markup document urls mount("/docs", DocsPage.class, "r", "h"); mount("/doc", DocPage.class, "r", "h", "f"); + mount("/editfile", EditFilePage.class, "r", "h", "f"); // federation urls mount("/proposal", ReviewProposalPage.class, "t"); diff --git a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties index 36c416e7..f425615c 100644 --- a/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/main/java/com/gitblit/wicket/GitBlitWebApp.properties @@ -770,4 +770,9 @@ gb.filestore = filestore gb.filestoreStats = Filestore contains {0} files with a total size of {1}. ({2} remaining) gb.statusChangedOn = status changed on gb.statusChangedBy = status changed by -gb.filestoreHelp = How to use the Filestore? \ No newline at end of file +gb.filestoreHelp = How to use the Filestore? +gb.editFile = edit file +gb.continueEditing = Continue Editing +gb.commitChanges = Commit Changes +gb.fileNotMergeable = Unable to commit {0}. This file can not be automatically merged. +gb.fileCommitted = Successfully committed {0}. \ No newline at end of file diff --git a/src/main/java/com/gitblit/wicket/pages/EditFilePage.html b/src/main/java/com/gitblit/wicket/pages/EditFilePage.html index 602149b7..d6b6eee3 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditFilePage.html +++ b/src/main/java/com/gitblit/wicket/pages/EditFilePage.html @@ -34,8 +34,8 @@ diff --git a/src/main/java/com/gitblit/wicket/pages/EditFilePage.java b/src/main/java/com/gitblit/wicket/pages/EditFilePage.java index 378f2a35..00375373 100644 --- a/src/main/java/com/gitblit/wicket/pages/EditFilePage.java +++ b/src/main/java/com/gitblit/wicket/pages/EditFilePage.java @@ -17,6 +17,7 @@ package com.gitblit.wicket.pages; import java.io.IOException; +import java.text.MessageFormat; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -51,7 +52,7 @@ import com.gitblit.wicket.MarkupProcessor; import com.gitblit.wicket.MarkupProcessor.MarkupDocument; import com.gitblit.wicket.WicketUtils; -@CacheControl(LastModified.BOOT) +@CacheControl(LastModified.REPOSITORY) public class EditFilePage extends RepositoryPage { public EditFilePage(final PageParameters params) { @@ -96,9 +97,9 @@ public class EditFilePage extends RepositoryPage { Fragment fragment; String displayedCommitId = commit.getId().getName(); MarkupDocument markupDoc = processor.parse(repositoryName, displayedCommitId, documentPath, markupText); - logger.info("Loading Edit File page: " + displayedCommitId); + logger.trace("Loading Edit File page: " + displayedCommitId); - if (currentUser.canEdit(getRepositoryModel())) { + if (currentUser.canEdit(getRepositoryModel()) && JGitUtils.isTip(getRepository(), objectId.toString())) { final Model documentContent = new Model(markupDoc.markup); final Model commitMessage = new Model("Document update"); @@ -112,7 +113,6 @@ public class EditFilePage extends RepositoryPage { @Override protected void onSubmit() { - final Repository repository = getRepository(); final String document = documentContent.getObject(); final String message = commitMessage.getObject(); @@ -125,7 +125,7 @@ public class EditFilePage extends RepositoryPage { try { ObjectId docAtLoad = getRepository().resolve(commitIdAtLoad.getObject()); - logger.info("Commiting Edit File page: " + commitIdAtLoad.getObject()); + logger.trace("Commiting Edit File page: " + commitIdAtLoad.getObject()); DirCache index = DirCache.newInCore(); DirCacheBuilder builder = index.builder(); @@ -156,12 +156,12 @@ public class EditFilePage extends RepositoryPage { } if (success == false) { - EditFilePage.this.error("Unable to commit document " + path, false); + getSession().error(MessageFormat.format(getString("gb.fileNotMergeable"),path)); return; } + getSession().info(MessageFormat.format(getString("gb.fileCommitted"),path)); setResponsePage(EditFilePage.class, params); - return; } }; @@ -197,12 +197,12 @@ public class EditFilePage extends RepositoryPage { fragment.add(new ExternalLink("rawLink", rawUrl)); add(fragment); - + } @Override protected String getPageName() { - return getString("gb.docs"); + return getString("gb.editFile"); } @Override @@ -212,7 +212,7 @@ public class EditFilePage extends RepositoryPage { @Override protected Class getRepoNavPageClass() { - return DocsPage.class; + return EditFilePage.class; } -- cgit v1.2.3