James Moger
2013-10-24 a7317acec01cde855a9f9f3d2da3dcc49d89aa86
src/main/java/com/gitblit/wicket/pages/RepositoryPage.java
@@ -16,9 +16,12 @@
package com.gitblit.wicket.pages;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -28,6 +31,7 @@
import org.apache.wicket.Component;
import org.apache.wicket.PageParameters;
import org.apache.wicket.behavior.SimpleAttributeModifier;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.TextField;
@@ -41,9 +45,14 @@
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.pegdown.LinkRenderer;
import org.pegdown.ast.WikiLinkNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.gitblit.Constants;
import com.gitblit.GitBlit;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.PagesServlet;
import com.gitblit.SyndicationServlet;
@@ -52,11 +61,13 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.SubmoduleModel;
import com.gitblit.models.UserModel;
import com.gitblit.models.UserRepositoryPreferences;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.DeepCopier;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.PushLogUtils;
import com.gitblit.utils.RefLogUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TicgitUtils;
import com.gitblit.wicket.CacheControl;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.PageRegistration;
import com.gitblit.wicket.PageRegistration.OtherPageLink;
@@ -68,20 +79,24 @@
public abstract class RepositoryPage extends RootPage {
   protected final Logger logger = LoggerFactory.getLogger(getClass());
   private final String PARAM_STAR = "star";
   protected final String projectName;
   protected final String repositoryName;
   protected final String objectId;
   private transient Repository r;
   private RepositoryModel m;
   private Map<String, SubmoduleModel> submodules;
   private final Map<String, PageRegistration> registeredPages;
   private boolean showAdmin;
   private boolean isOwner;
   public RepositoryPage(PageParameters params) {
      super(params);
      repositoryName = WicketUtils.getRepositoryName(params);
@@ -92,7 +107,7 @@
         projectName = root;
      }
      objectId = WicketUtils.getObject(params);
      if (StringUtils.isEmpty(repositoryName)) {
         error(MessageFormat.format(getString("gb.repositoryNotSpecifiedFor"), getPageName()), true);
      }
@@ -100,7 +115,7 @@
      if (!getRepositoryModel().hasCommits) {
         setResponsePage(EmptyRepositoryPage.class, params);
      }
      if (getRepositoryModel().isCollectingGarbage) {
         error(MessageFormat.format(getString("gb.busyCollectingGarbage"), getRepositoryModel().name), true);
      }
@@ -117,6 +132,22 @@
                        branch.reference.getName());
            if (!canAccess) {
               error(getString("gb.accessDenied"), true);
            }
         }
      }
      if (params.containsKey(PARAM_STAR)) {
         // set starred state
         boolean star = params.getBoolean(PARAM_STAR);
         UserModel user = GitBlitWebSession.get().getUser();
         if (user != null && user.isAuthenticated) {
            UserRepositoryPreferences prefs = user.getPreferences().getRepositoryPreferences(getRepositoryModel().name);
            prefs.starred = star;
            try {
               GitBlit.self().updateUserModel(user.username, user, false);
            } catch (GitBlitException e) {
               logger.error("Failed to update user " + user.username, e);
               error(getString("gb.failedToUpdateUser"), false);
            }
         }
      }
@@ -140,7 +171,7 @@
      // set stateless page preference
      setStatelessHint(true);
   }
   @Override
   protected Class<? extends BasePage> getRootNavPageClass() {
      return RepositoriesPage.class;
@@ -149,7 +180,7 @@
   protected Class<? extends BasePage> getRepoNavPageClass() {
      return getClass();
   }
   private Map<String, PageRegistration> registerPages() {
      PageParameters params = null;
      if (!StringUtils.isEmpty(repositoryName)) {
@@ -161,30 +192,28 @@
      RepositoryModel model = getRepositoryModel();
      // standard links
      if (PushLogUtils.getPushLogBranch(r) == null) {
      if (RefLogUtils.getRefLogBranch(r) == null) {
         pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
      } else {
         pages.put("summary", new PageRegistration("gb.summary", SummaryPage.class, params));
//         pages.put("overview", new PageRegistration("gb.overview", OverviewPage.class, params));
         pages.put("reflog", new PageRegistration("gb.reflog", ReflogPage.class, params));
      }
      pages.put("commits", new PageRegistration("gb.commits", LogPage.class, params));
      pages.put("tree", new PageRegistration("gb.tree", TreePage.class, params));
      pages.put("compare", new PageRegistration("gb.compare", ComparePage.class, params));
      pages.put("compare", new PageRegistration("gb.compare", ComparePage.class, params, true));
      if (GitBlit.getBoolean(Keys.web.allowForking, true)) {
         pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params));
         pages.put("forks", new PageRegistration("gb.forks", ForksPage.class, params, true));
      }
      // conditional links
      // per-repository extra page links
      if (model.useTickets && TicgitUtils.getTicketsBranch(r) != null) {
         pages.put("tickets", new PageRegistration("gb.tickets", TicketsPage.class, params));
      }
      if (model.showReadme || model.useDocs) {
         pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params));
      if (model.useDocs) {
         pages.put("docs", new PageRegistration("gb.docs", DocsPage.class, params, true));
      }
      if (JGitUtils.getPagesBranch(r) != null) {
         OtherPageLink pagesLink = new OtherPageLink("gb.pages", PagesServlet.asLink(
               getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null));
               getRequest().getRelativePathPrefixToContextRoot(), repositoryName, null), true);
         pages.put("pages", pagesLink);
      }
@@ -199,12 +228,9 @@
      isOwner = GitBlitWebSession.get().isLoggedIn()
            && (model.isOwner(GitBlitWebSession.get()
                  .getUsername()));
      if (showAdmin || isOwner) {
         pages.put("edit", new PageRegistration("gb.edit", EditRepositoryPage.class, params));
      }
      return pages;
   }
   protected boolean allowForkControls() {
      return GitBlit.getBoolean(Keys.web.allowForking, true);
   }
@@ -222,7 +248,7 @@
         add(new LinkPanel("projectTitle", null, project.name,
               ProjectPage.class, WicketUtils.newProjectParameter(project.name)));
      }
      String name = StringUtils.stripDotGit(repositoryName);
      if (!StringUtils.isEmpty(projectName) && name.startsWith(projectName)) {
         name = name.substring(projectName.length() + 1);
@@ -230,7 +256,7 @@
      add(new LinkPanel("repositoryName", null, name, SummaryPage.class,
            WicketUtils.newRepositoryParameter(repositoryName)));
      add(new Label("pageName", pageName).setRenderBodyOnly(true));
      UserModel user = GitBlitWebSession.get().getUser();
      if (user == null) {
         user = UserModel.ANONYMOUS;
@@ -253,12 +279,32 @@
         } else {
            // link to origin repository
            Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
            forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
            forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(model.originRepository),
                  SummaryPage.class, WicketUtils.newRepositoryParameter(model.originRepository)));
            add(forkFrag);
         }
      }
      // (un)star link allows a user to star a repository
      if (user.isAuthenticated) {
         PageParameters starParams = DeepCopier.copy(getPageParameters());
         starParams.put(PARAM_STAR, !user.getPreferences().isStarredRepository(model.name));
         String toggleStarUrl = getRequestCycle().urlFor(getClass(), starParams).toString();
         if (user.getPreferences().isStarredRepository(model.name)) {
            // show unstar button
            add(new Label("starLink").setVisible(false));
            addToolbarButton("unstarLink", "icon-star-empty", getString("gb.unstar"), toggleStarUrl);
         } else {
            // show star button
            addToolbarButton("starLink", "icon-star", getString("gb.star"), toggleStarUrl);
            add(new Label("unstarLink").setVisible(false));
         }
      } else {
         // anonymous user
         add(new Label("starLink").setVisible(false));
         add(new Label("unstarLink").setVisible(false));
      }
      // fork controls
      if (!allowForkControls() || user == null || !user.isAuthenticated) {
         // must be logged-in to fork, hide all fork controls
@@ -272,7 +318,7 @@
         if (hasFork || !canFork) {
            // user not allowed to fork or fork already exists or repo forbids forking
            add(new ExternalLink("forkLink", "").setVisible(false));
            if (hasFork && !fork.equals(model.name)) {
               // user has fork, view my fork link
               String url = getRequestCycle().urlFor(SummaryPage.class, WicketUtils.newRepositoryParameter(fork)).toString();
@@ -288,8 +334,25 @@
            add(new ExternalLink("forkLink", url));
         }
      }
      if (showAdmin || isOwner) {
         String url = getRequestCycle().urlFor(EditRepositoryPage.class, WicketUtils.newRepositoryParameter(model.name)).toString();
         add(new ExternalLink("editLink", url));
      } else {
         add(new Label("editLink").setVisible(false));
      }
      super.setupPage(repositoryName, pageName);
   }
   protected void addToolbarButton(String wicketId, String iconClass, String label, String url) {
      Fragment button = new Fragment(wicketId, "toolbarLinkFragment", this);
      Label icon = new Label("icon");
      WicketUtils.setCssClass(icon, iconClass);
      button.add(icon);
      button.add(new Label("label", label));
      button.add(new SimpleAttributeModifier("href", url));
      add(button);
   }
   protected void addSyndicationDiscoveryLink() {
@@ -333,13 +396,38 @@
      RevCommit commit = JGitUtils.getCommit(r, objectId);
      if (commit == null) {
         error(MessageFormat.format(getString("gb.failedToFindCommit"),
               objectId, repositoryName, getPageName()), true);
               objectId, repositoryName, getPageName()), null, LogPage.class,
               WicketUtils.newRepositoryParameter(repositoryName));
      }
      getSubmodules(commit);
      return commit;
   }
   private Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
   protected String getBestCommitId(RevCommit commit) {
      String head = null;
      try {
         head = r.resolve(getRepositoryModel().HEAD).getName();
      } catch (Exception e) {
      }
      String id = commit.getName();
      if (!StringUtils.isEmpty(head) && head.equals(id)) {
         // match default branch
         return Repository.shortenRefName(getRepositoryModel().HEAD);
      }
      // find first branch match
      for (RefModel ref : JGitUtils.getLocalBranches(r, false, -1)) {
         if (ref.getObjectId().getName().equals(id)) {
            return ref.getName();
         }
      }
      // return sha
      return id;
   }
   protected Map<String, SubmoduleModel> getSubmodules(RevCommit commit) {
      if (submodules == null) {
         submodules = new HashMap<String, SubmoduleModel>();
         for (SubmoduleModel model : JGitUtils.getSubmodules(r, commit.getTree())) {
@@ -348,9 +436,12 @@
      }
      return submodules;
   }
   protected SubmoduleModel getSubmodule(String path) {
      SubmoduleModel model = submodules.get(path);
      SubmoduleModel model = null;
      if (submodules != null) {
         model = submodules.get(path);
      }
      if (model == null) {
         // undefined submodule?!
         model = new SubmoduleModel(path.substring(path.lastIndexOf('/') + 1), path, path);
@@ -361,7 +452,7 @@
         // extract the repository name from the clone url
         List<String> patterns = GitBlit.getStrings(Keys.git.submoduleUrlPatterns);
         String submoduleName = StringUtils.extractRepositoryPath(model.url, patterns.toArray(new String[0]));
         // determine the current path for constructing paths relative
         // to the current repository
         String currentPath = "";
@@ -381,7 +472,7 @@
         if (submoduleName.lastIndexOf('/') > -1) {
            String name = submoduleName.substring(submoduleName.lastIndexOf('/') + 1);
            candidates.add(currentPath + StringUtils.stripDotGit(name));
            candidates.add(currentPath + candidates.get(candidates.size() - 1) + ".git");
            candidates.add(candidates.get(candidates.size() - 1) + ".git");
         }
         // absolute
@@ -404,11 +495,11 @@
               return model;
            }
         }
         // we do not have a copy of the submodule, but we need a path
         model.gitblitPath = candidates.get(0);
         return model;
      }
      }
   }
   protected String getShortObjectId(String objectId) {
@@ -419,12 +510,17 @@
      add(new RefsPanel("refsPanel", repositoryName, c, JGitUtils.getAllRefs(r, getRepositoryModel().showRemoteBranches)));
   }
   protected void addFullText(String wicketId, String text, boolean substituteRegex) {
      String html = StringUtils.escapeForHtml(text, false);
      if (substituteRegex) {
         html = GitBlit.self().processCommitMessage(repositoryName, html);
      } else {
         html = StringUtils.breakLinesForHtml(html);
   protected void addFullText(String wicketId, String text) {
      RepositoryModel model = getRepositoryModel();
      String content = GitBlit.self().processCommitMessage(model, text);
      String html;
      switch (model.commitMessageRenderer) {
      case MARKDOWN:
         html = MessageFormat.format("<div class='commit_message'>{0}</div>", content);
         break;
      default:
         html = MessageFormat.format("<pre class='commit_message'>{0}</pre>", content);
         break;
      }
      add(new Label(wicketId, html).setEscapeModelStrings(false));
   }
@@ -508,6 +604,30 @@
      super.onBeforeRender();
   }
   @Override
   protected void setLastModified() {
      if (getClass().isAnnotationPresent(CacheControl.class)) {
         CacheControl cacheControl = getClass().getAnnotation(CacheControl.class);
         switch (cacheControl.value()) {
         case REPOSITORY:
            RepositoryModel repository = getRepositoryModel();
            if (repository != null) {
               setLastModified(repository.lastChange);
            }
            break;
         case COMMIT:
            RevCommit commit = getCommit();
            if (commit != null) {
               Date commitDate = JGitUtils.getCommitDate(commit);
               setLastModified(commitDate);
            }
            break;
         default:
            super.setLastModified();
         }
      }
   }
   protected PageParameters newRepositoryParameter() {
      return WicketUtils.newRepositoryParameter(repositoryName);
   }
@@ -523,11 +643,38 @@
   public boolean isShowAdmin() {
      return showAdmin;
   }
   public boolean isOwner() {
      return isOwner;
   }
   /**
    * Returns a Pegdown/Markdown link renderer which renders WikiLinks.
    *
    * @return a link renderer
    */
   protected LinkRenderer getLinkRenderer() {
      RevCommit head = JGitUtils.getCommit(r, "HEAD");
      final String id = getBestCommitId(head);
      LinkRenderer renderer = new LinkRenderer() {
         @Override
         public Rendering render(WikiLinkNode node) {
            try {
               String path = URLEncoder.encode(node.getText().replace(' ', '-'), "UTF-8");
               String name = node.getText();
               if (name.indexOf('/') > -1) {
                  name = name.substring(name.lastIndexOf('/') + 1);
               }
               String url = urlFor(MarkdownPage.class, WicketUtils.newPathParameter(repositoryName, id, path)).toString();
               return new Rendering(url, name);
            } catch (UnsupportedEncodingException e) {
               throw new IllegalStateException();
            }
         }
      };
      return renderer;
   }
   private class SearchForm extends SessionlessForm<Void> implements Serializable {
      private static final long serialVersionUID = 1L;
@@ -561,7 +708,7 @@
         Constants.SearchType searchType = searchTypeModel.getObject();
         String searchString = searchBoxModel.getObject();
         if (StringUtils.isEmpty(searchString)) {
            // redirect to self to avoid wicket page update bug
            // redirect to self to avoid wicket page update bug
            PageParameters params = RepositoryPage.this.getPageParameters();
            String relativeUrl = urlFor(RepositoryPage.this.getClass(), params).toString();
            String absoluteUrl = RequestUtils.toAbsolutePath(relativeUrl);