/*
documentr - Edit, maintain, and present software documentation on the web.
Copyright (C) 2012-2013 Maik Schreiber
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.blizzy.documentr.web.page;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import de.blizzy.documentr.DocumentrConstants;
import de.blizzy.documentr.access.AuthenticationUtil;
import de.blizzy.documentr.access.DocumentrPermissionEvaluator;
import de.blizzy.documentr.access.Permission;
import de.blizzy.documentr.access.User;
import de.blizzy.documentr.access.UserStore;
import de.blizzy.documentr.markdown.IPageRenderer;
import de.blizzy.documentr.markdown.MarkdownProcessor;
import de.blizzy.documentr.page.CommitCherryPickConflictResolve;
import de.blizzy.documentr.page.CommitCherryPickResult;
import de.blizzy.documentr.page.ICherryPicker;
import de.blizzy.documentr.page.IPageStore;
import de.blizzy.documentr.page.MergeConflict;
import de.blizzy.documentr.page.Page;
import de.blizzy.documentr.page.PageMetadata;
import de.blizzy.documentr.page.PageNotFoundException;
import de.blizzy.documentr.page.PageTextData;
import de.blizzy.documentr.page.PageUtil;
import de.blizzy.documentr.repository.IGlobalRepositoryManager;
import de.blizzy.documentr.util.Util;
import de.blizzy.documentr.web.util.ErrorController;
@Controller
@RequestMapping("/page")
@Slf4j
public class PageController {
@Autowired
private IPageStore pageStore;
@Autowired
private ICherryPicker cherryPicker;
@Autowired
private IGlobalRepositoryManager globalRepositoryManager;
@Autowired
private MarkdownProcessor markdownProcessor;
@Autowired
private UserStore userStore;
@Autowired
private IPageRenderer pageRenderer;
@Autowired
private DocumentrPermissionEvaluator permissionEvaluator;
@RequestMapping(value="/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method={ RequestMethod.GET, RequestMethod.HEAD })
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, VIEW)")
public String getPage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, Model model, HttpServletRequest request, HttpServletResponse response)
throws IOException {
try {
path = Util.toRealPagePath(path);
PageMetadata metadata = pageStore.getPageMetadata(projectName, branchName, path);
long lastEdited = metadata.getLastEdited().getTime();
long authenticationCreated = AuthenticationUtil.getAuthenticationCreationTime(request.getSession());
long projectEditTime = PageUtil.getProjectEditTime(projectName);
long lastModified = Math.max(lastEdited, authenticationCreated);
if (projectEditTime >= 0) {
lastModified = Math.max(lastModified, projectEditTime);
}
long modifiedSince = request.getDateHeader("If-Modified-Since"); //$NON-NLS-1$
if ((modifiedSince >= 0) && (lastModified <= modifiedSince)) {
return ErrorController.notModified();
}
response.setDateHeader("Last-Modified", lastModified); //$NON-NLS-1$
response.setDateHeader("Expires", 0); //$NON-NLS-1$
response.setHeader("Cache-Control", "must-revalidate, private"); //$NON-NLS-1$ //$NON-NLS-2$
Page page = pageStore.getPage(projectName, branchName, path, false);
model.addAttribute("path", path); //$NON-NLS-1$
model.addAttribute("pageName", //$NON-NLS-1$
path.contains("/") ? StringUtils.substringAfterLast(path, "/") : path); //$NON-NLS-1$ //$NON-NLS-2$
model.addAttribute("parentPagePath", page.getParentPagePath()); //$NON-NLS-1$
model.addAttribute("title", page.getTitle()); //$NON-NLS-1$
String viewRestrictionRole = page.getViewRestrictionRole();
model.addAttribute("viewRestrictionRole", //$NON-NLS-1$
(viewRestrictionRole != null) ? viewRestrictionRole : StringUtils.EMPTY);
model.addAttribute("commit", metadata.getCommit()); //$NON-NLS-1$
return "/project/branch/page/view"; //$NON-NLS-1$
} catch (PageNotFoundException e) {
return ErrorController.notFound("page.notFound"); //$NON-NLS-1$
}
}
@RequestMapping(value="/create/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{parentPagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}", method=RequestMethod.GET)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
public String createPage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String parentPagePath, Model model) {
PageForm form = new PageForm(projectName, branchName, null,
Util.toRealPagePath(parentPagePath), null, null, StringUtils.EMPTY, null,
ArrayUtils.EMPTY_STRING_ARRAY, DocumentrConstants.PAGE_ORDER_INDEX_UNORDERED);
model.addAttribute("pageForm", form); //$NON-NLS-1$
return "/project/branch/page/edit"; //$NON-NLS-1$
}
@RequestMapping(value="/edit/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.GET)
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, EDIT_PAGE)")
public String editPage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, Model model, HttpSession session) throws IOException {
try {
path = Util.toRealPagePath(path);
Page page = pageStore.getPage(projectName, branchName, path, true);
String text = ((PageTextData) page.getData()).getText();
String viewRestrictionRole = page.getViewRestrictionRole();
PageMetadata metadata = pageStore.getPageMetadata(projectName, branchName, path);
String commit = metadata.getCommit();
MergeConflict conflict = (MergeConflict) session.getAttribute("conflict"); //$NON-NLS-1$
session.removeAttribute("conflict"); //$NON-NLS-1$
if (conflict != null) {
projectName = (String) session.getAttribute("conflict.projectName"); //$NON-NLS-1$
session.removeAttribute("conflict.projectName"); //$NON-NLS-1$
branchName = (String) session.getAttribute("conflict.branchName"); //$NON-NLS-1$
session.removeAttribute("conflict.branchName"); //$NON-NLS-1$
path = (String) session.getAttribute("conflict.pagePath"); //$NON-NLS-1$
session.removeAttribute("conflict.pagePath"); //$NON-NLS-1$
text = conflict.getText();
commit = conflict.getNewBaseCommit();
}
String[] tags = page.getTags().toArray(ArrayUtils.EMPTY_STRING_ARRAY);
Arrays.sort(tags);
PageForm form = new PageForm(projectName, branchName, path, page.getParentPagePath(),
page.getTitle(), text, (viewRestrictionRole != null) ? viewRestrictionRole : StringUtils.EMPTY,
commit, tags, page.getOrderIndex());
model.addAttribute("pageForm", form); //$NON-NLS-1$
if (conflict != null) {
model.addAttribute("mergeConflict", Boolean.TRUE); //$NON-NLS-1$
}
return "/project/branch/page/edit"; //$NON-NLS-1$
} catch (PageNotFoundException e) {
return ErrorController.notFound("page.notFound"); //$NON-NLS-1$
}
}
@RequestMapping(value="/save/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}", method=RequestMethod.POST)
@PreAuthorize("hasBranchPermission(#form.projectName, #form.branchName, EDIT_PAGE)")
public String savePage(@ModelAttribute @Valid PageForm form, BindingResult bindingResult,
Model model, Authentication authentication) throws IOException {
String projectName = form.getProjectName();
String branchName = form.getBranchName();
User user = userStore.getUser(authentication.getName());
if (!globalRepositoryManager.listProjectBranches(projectName).contains(branchName)) {
bindingResult.rejectValue("branchName", "page.branch.nonexistent"); //$NON-NLS-1$ //$NON-NLS-2$
}
if (bindingResult.hasErrors()) {
return "/project/branch/page/edit"; //$NON-NLS-1$
}
String parentPagePath = form.getParentPagePath();
if (StringUtils.isBlank(parentPagePath)) {
parentPagePath = null;
}
parentPagePath = Util.toRealPagePath(parentPagePath);
Page page = Page.fromText(form.getTitle(), form.getText());
String path = form.getPath();
if (StringUtils.isBlank(path)) {
path = parentPagePath + "/" + Util.simplifyForUrl(form.getTitle()); //$NON-NLS-1$
}
page.setTags(Sets.newHashSet(form.getTags()));
page.setViewRestrictionRole(StringUtils.isNotBlank(form.getViewRestrictionRole()) ?
form.getViewRestrictionRole() : null);
page.setOrderIndex(form.getOrderIndex());
Page oldPage = null;
try {
oldPage = pageStore.getPage(projectName, branchName, path, true);
} catch (PageNotFoundException e) {
// okay
}
if ((oldPage == null) || !page.equals(oldPage)) {
MergeConflict conflict = pageStore.savePage(projectName, branchName, path,
page, Strings.emptyToNull(form.getCommit()), user);
if (conflict != null) {
form.setText(conflict.getText());
form.setCommit(conflict.getNewBaseCommit());
model.addAttribute("mergeConflict", Boolean.TRUE); //$NON-NLS-1$
return "/project/branch/page/edit"; //$NON-NLS-1$
}
}
Integer start = form.getParentPageSplitRangeStart();
Integer end = form.getParentPageSplitRangeEnd();
if (StringUtils.isNotBlank(parentPagePath) && (start != null) && (end != null) &&
permissionEvaluator.hasBranchPermission(authentication, projectName, branchName, Permission.EDIT_PAGE)) {
log.info("splitting off {}-{} of {}/{}/{}", //$NON-NLS-1$
start, end, projectName, branchName, parentPagePath);
Page parentPage = pageStore.getPage(projectName, branchName, parentPagePath, true);
String text = ((PageTextData) parentPage.getData()).getText();
end = Math.min(end, text.length());
text = text.substring(0, start) + text.substring(end);
parentPage.setData(new PageTextData(text));
pageStore.savePage(projectName, branchName, parentPagePath, parentPage, null, user);
}
return "redirect:/page/" + projectName + "/" + branchName + "/" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Util.toUrlPagePath(path);
}
@ModelAttribute
public PageForm createPageForm(@PathVariable String projectName, @PathVariable String branchName,
@RequestParam(required=false) String path, @RequestParam(required=false) String parentPagePath,
@RequestParam(required=false) String title, @RequestParam(required=false) String text,
@RequestParam(required=false) String viewRestrictionRole,
@RequestParam(required=false) String commit,
@RequestParam(required=false) String[] tags,
@RequestParam(required=false) Integer orderIndex) {
if (tags == null) {
tags = ArrayUtils.EMPTY_STRING_ARRAY;
}
return ((path != null) && (title != null) && (text != null)) ?
new PageForm(projectName, branchName, path, parentPagePath, title, text,
StringUtils.isNotBlank(viewRestrictionRole) ? viewRestrictionRole : StringUtils.EMPTY,
Strings.emptyToNull(commit), tags, orderIndex) :
null;
}
@RequestMapping(value="/generateName/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{parentPagePath:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json",
method=RequestMethod.POST, produces="application/json")
@ResponseBody
@PreAuthorize("hasBranchPermission(#projectName, #branchName, VIEW)")
public Map<String, Object> generateName(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String parentPagePath, @RequestParam String title) throws IOException {
String name = Util.simplifyForUrl(title);
String path = Util.toRealPagePath(parentPagePath) + "/" + name; //$NON-NLS-1$
boolean pageExists = false;
try {
Page page = pageStore.getPage(projectName, branchName, path, false);
pageExists = page != null;
} catch (PageNotFoundException e) {
// okay
}
Map<String, Object> result = new HashMap<String, Object>();
result.put("path", path); //$NON-NLS-1$
result.put("exists", Boolean.valueOf(pageExists)); //$NON-NLS-1$
return result;
}
@RequestMapping(value="/markdownToHtml/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/json",
method=RequestMethod.POST, produces="application/json")
@ResponseBody
@PreAuthorize("isAuthenticated()")
public Map<String, String> markdownToHtml(@PathVariable String projectName, @PathVariable String branchName,
@RequestParam String markdown, @RequestParam(required=false) String pagePath, Authentication authentication,
Locale locale, HttpServletRequest request) {
String contextPath = request.getContextPath();
Map<String, String> result = new HashMap<String, String>();
String html = markdownProcessor.markdownToHtml(markdown, projectName, branchName, pagePath, authentication, locale,
contextPath);
html = markdownProcessor.processNonCacheableMacros(html, projectName, branchName, pagePath, authentication, locale,
contextPath);
result.put("html", html); //$NON-NLS-1$
return result;
}
@RequestMapping(value="/copyToBranch/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.POST)
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, VIEW) and " +
"hasBranchPermission(#projectName, #targetBranchName, EDIT_PAGE)")
public String copyToBranch(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam String targetBranchName, Authentication authentication)
throws IOException {
path = Util.toRealPagePath(path);
Page page = pageStore.getPage(projectName, branchName, path, true);
User user = userStore.getUser(authentication.getName());
pageStore.savePage(projectName, targetBranchName, path, page, null, user);
return "redirect:/page/edit/" + projectName + "/" + targetBranchName + "/" + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Util.toUrlPagePath(path);
}
@RequestMapping(value="/delete/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.GET)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
public String deletePage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, Authentication authentication) throws IOException {
path = Util.toRealPagePath(path);
User user = userStore.getUser(authentication.getName());
pageStore.deletePage(projectName, branchName, path, user);
return "redirect:/page/" + projectName + "/" + branchName + //$NON-NLS-1$ //$NON-NLS-2$
"/" + DocumentrConstants.HOME_PAGE_NAME; //$NON-NLS-1$
}
@RequestMapping(value="/relocate/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.POST)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
public String relocatePage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam String newParentPagePath, Authentication authentication)
throws IOException {
path = Util.toRealPagePath(path);
newParentPagePath = Util.toRealPagePath(newParentPagePath);
User user = userStore.getUser(authentication.getName());
pageStore.relocatePage(projectName, branchName, path, newParentPagePath, user);
String pageName = path.contains("/") ? StringUtils.substringAfterLast(path, "/") : path; //$NON-NLS-1$ //$NON-NLS-2$
return "redirect:/page/" + projectName + "/" + branchName + //$NON-NLS-1$ //$NON-NLS-2$
"/" + Util.toUrlPagePath(newParentPagePath + "/" + pageName); //$NON-NLS-1$ //$NON-NLS-2$
}
@RequestMapping(value="/markdown/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json",
method=RequestMethod.GET)
@ResponseBody
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, VIEW)")
public Map<String, String> getPageMarkdown(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam Set<String> versions) throws IOException {
try {
return pageStore.getMarkdown(projectName, branchName, Util.toRealPagePath(path), versions);
} catch (PageNotFoundException e) {
return Collections.emptyMap();
}
}
@RequestMapping(value="/markdownInRange/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/" +
"{rangeStart:[0-9]+},{rangeEnd:[0-9]+}/{commit:[0-9a-fA-F]+}/json",
method=RequestMethod.GET)
@ResponseBody
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, VIEW)")
public Map<String, String> getPageMarkdownInRange(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @PathVariable int rangeStart, @PathVariable int rangeEnd,
@PathVariable String commit) throws IOException {
Page page = pageStore.getPage(projectName, branchName, Util.toRealPagePath(path), commit, true);
String markdown = ((PageTextData) page.getData()).getText();
Map<String, String> result = Maps.newHashMap();
markdown = markdown.substring(rangeStart, Math.min(rangeEnd, markdown.length()));
markdown = markdown.replaceAll("[\\r\\n]+$", StringUtils.EMPTY); //$NON-NLS-1$
result.put("markdown", markdown); //$NON-NLS-1$
return result;
}
@RequestMapping(value="/changes/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.GET)
@PreAuthorize("isAuthenticated() and hasPagePermission(#projectName, #branchName, #path, VIEW)")
public String getPageChanges(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, Model model) {
model.addAttribute("projectName", projectName); //$NON-NLS-1$
model.addAttribute("branchName", branchName); //$NON-NLS-1$
path = Util.toRealPagePath(path);
model.addAttribute("path", path); //$NON-NLS-1$
return "/project/branch/page/changes"; //$NON-NLS-1$
}
@RequestMapping(value="/saveRange/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json",
method=RequestMethod.POST)
@ResponseBody
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, EDIT_PAGE)")
public Map<String, Object> savePageRange(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam String markdown, @RequestParam String range,
@RequestParam String commit, Authentication authentication, Locale locale, HttpServletRequest request) throws IOException {
path = Util.toRealPagePath(path);
markdown = markdown.replaceAll("[\\r\\n]+$", StringUtils.EMPTY); //$NON-NLS-1$
int rangeStart = Integer.parseInt(StringUtils.substringBefore(range, ",")); //$NON-NLS-1$
int rangeEnd = Integer.parseInt(StringUtils.substringAfter(range, ",")); //$NON-NLS-1$
Page page = pageStore.getPage(projectName, branchName, path, commit, true);
String text = ((PageTextData) page.getData()).getText();
rangeEnd = Math.min(rangeEnd, text.length());
String oldMarkdown = text.substring(rangeStart, rangeEnd);
String cleanedOldMarkdown = oldMarkdown.replaceAll("[\\r\\n]+$", StringUtils.EMPTY); //$NON-NLS-1$
rangeEnd -= oldMarkdown.length() - cleanedOldMarkdown.length();
String newText = text.substring(0, rangeStart) + markdown + text.substring(Math.min(rangeEnd, text.length()));
page.setData(new PageTextData(newText));
User user = userStore.getUser(authentication.getName());
MergeConflict conflict = pageStore.savePage(projectName, branchName, path, page, commit, user);
Map<String, Object> result = Maps.newHashMap();
if (conflict != null) {
result.put("conflict", Boolean.TRUE); //$NON-NLS-1$
HttpSession session = request.getSession();
session.setAttribute("conflict", conflict); //$NON-NLS-1$
session.setAttribute("conflict.projectName", projectName); //$NON-NLS-1$
session.setAttribute("conflict.branchName", branchName); //$NON-NLS-1$
session.setAttribute("conflict.path", path); //$NON-NLS-1$
} else {
String newCommit = pageStore.getPageMetadata(projectName, branchName, path).getCommit();
String contextPath = request.getContextPath();
String html = pageRenderer.getHtml(projectName, branchName, path, authentication, locale, contextPath);
html = markdownProcessor.processNonCacheableMacros(html, projectName, branchName, path, authentication, locale,
contextPath);
result.put("html", html); //$NON-NLS-1$
result.put("commit", newCommit); //$NON-NLS-1$
}
return result;
}
@RequestMapping(value="/restoreVersion/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json",
method=RequestMethod.POST)
@ResponseBody
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, EDIT_PAGE)")
public Map<String, Object> restoreVersion(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam String version, Authentication authentication) throws IOException {
User user = userStore.getUser(authentication.getName());
pageStore.restorePageVersion(projectName, branchName, Util.toRealPagePath(path), version, user);
return Collections.emptyMap();
}
@RequestMapping(value="/cherryPick/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}",
method=RequestMethod.POST)
@PreAuthorize("hasPagePermission(#projectName, #branchName, #path, VIEW)")
public String cherryPick(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam String version1, @RequestParam String version2,
@RequestParam("branch") Set<String> targetBranches, @RequestParam boolean dryRun,
WebRequest request, Model model, Authentication authentication, Locale locale) throws IOException {
path = Util.toRealPagePath(path);
for (String targetBranch : targetBranches) {
if (!permissionEvaluator.hasPagePermission(
authentication, projectName, targetBranch, path, Permission.EDIT_PAGE)) {
return ErrorController.forbidden();
}
}
List<String> commits = cherryPicker.getCommitsList(projectName, branchName, path, version1, version2);
if (commits.isEmpty()) {
throw new IllegalArgumentException("no commits to cherry-pick"); //$NON-NLS-1$
}
User user = userStore.getUser(authentication.getName());
Map<String, String[]> params = request.getParameterMap();
Set<CommitCherryPickConflictResolve> resolves = Sets.newHashSet();
for (Map.Entry<String, String[]> entry : params.entrySet()) {
String name = entry.getKey();
if (name.startsWith("resolveText_")) { //$NON-NLS-1$
String branchCommit = StringUtils.substringAfter(name, "_"); //$NON-NLS-1$
String branch = StringUtils.substringBefore(branchCommit, "/"); //$NON-NLS-1$
String commit = StringUtils.substringAfter(branchCommit, "/"); //$NON-NLS-1$
String text = entry.getValue()[0];
CommitCherryPickConflictResolve resolve = new CommitCherryPickConflictResolve(branch, commit, text);
resolves.add(resolve);
}
}
Map<String, List<CommitCherryPickResult>> results = cherryPicker.cherryPick(
projectName, branchName, path, commits, targetBranches, resolves, dryRun, user, locale);
if (results.keySet().size() != targetBranches.size()) {
throw new IllegalStateException();
}
if (!dryRun) {
boolean allOk = true;
loop: for (List<CommitCherryPickResult> branchResults : results.values()) {
for (CommitCherryPickResult result : branchResults) {
if (result.getStatus() != CommitCherryPickResult.Status.OK) {
allOk = false;
break loop;
}
}
}
if (allOk) {
return "redirect:/page/" + projectName + "/" + branchName + //$NON-NLS-1$ //$NON-NLS-2$
"/" + Util.toUrlPagePath(path); //$NON-NLS-1$
}
}
model.addAttribute("cherryPickResults", results); //$NON-NLS-1$
model.addAttribute("version1", version1); //$NON-NLS-1$
model.addAttribute("version2", version2); //$NON-NLS-1$
model.addAttribute("resolves", resolves); //$NON-NLS-1$
return "/project/branch/page/cherryPick"; //$NON-NLS-1$
}
@RequestMapping(value="/split/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/" +
"{rangeStart:[0-9]+},{rangeEnd:[0-9]+}",
method=RequestMethod.GET)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
public String splitPage(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @PathVariable int rangeStart, @PathVariable int rangeEnd,
Model model) throws IOException {
log.info("splitting off {}-{} of {}/{}/{}", //$NON-NLS-1$
rangeStart, rangeEnd, projectName, branchName, path);
path = Util.toRealPagePath(path);
Page page = pageStore.getPage(projectName, branchName, path, true);
String text = ((PageTextData) page.getData()).getText();
rangeEnd = Math.min(rangeEnd, text.length());
text = text.substring(rangeStart, rangeEnd).trim();
PageForm form = new PageForm(projectName, branchName, null, path, null, text, StringUtils.EMPTY, null,
ArrayUtils.EMPTY_STRING_ARRAY, DocumentrConstants.PAGE_ORDER_INDEX_UNORDERED);
form.setParentPageSplitRangeStart(rangeStart);
form.setParentPageSplitRangeEnd(rangeEnd);
model.addAttribute("pageForm", form); //$NON-NLS-1$
return "/project/branch/page/edit"; //$NON-NLS-1$
}
@RequestMapping(value="/saveChildrenOrder/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json", method=RequestMethod.POST)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
@ResponseBody
public Map<String, Object> saveChildrenOrder(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, @RequestParam(value="childrenOrder[]", required=false) List<String> childrenOrder,
Authentication authentication) throws IOException {
log.info("saving children order for page {}/{}/{}", projectName, branchName, path); //$NON-NLS-1$
if (childrenOrder == null) {
childrenOrder = Collections.emptyList();
}
log.debug("new children order: {}", childrenOrder); //$NON-NLS-1$
User user = userStore.getUser(authentication.getName());
pageStore.saveChildrenOrder(projectName, branchName, path, childrenOrder, user);
return Collections.emptyMap();
}
@RequestMapping(value="/resetChildrenOrder/{projectName:" + DocumentrConstants.PROJECT_NAME_PATTERN + "}/" +
"{branchName:" + DocumentrConstants.BRANCH_NAME_PATTERN + "}/" +
"{path:" + DocumentrConstants.PAGE_PATH_URL_PATTERN + "}/json", method=RequestMethod.GET)
@PreAuthorize("hasBranchPermission(#projectName, #branchName, EDIT_PAGE)")
@ResponseBody
public Map<String, Object> resetChildrenOrder(@PathVariable String projectName, @PathVariable String branchName,
@PathVariable String path, Authentication authentication) throws IOException {
log.info("resetting children order for page {}/{}/{}", projectName, branchName, path); //$NON-NLS-1$
User user = userStore.getUser(authentication.getName());
pageStore.resetChildrenOrder(projectName, branchName, path, user);
return Collections.emptyMap();
}
}