James Moger
2013-05-02 b7502e5c0666154c7378469fd769b3aeb242e949
Added SparkleShare invite url panel

This will probably be merged into a refined, single multi-protocol
url panel.
3 files added
6 files modified
339 ■■■■■ changed files
src/main/java/WEB-INF/web.xml 18 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/Constants.java 2 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/SparkleShareInviteServlet.java 210 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/BasePage.java 27 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.html 1 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/SummaryPage.java 10 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html 18 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java 52 ●●●●● patch | view | raw | blame | history
src/main/java/WEB-INF/web.xml
@@ -55,6 +55,20 @@
    </servlet-mapping>
    
    
    <!-- SparkleShare Invite Servlet
         <url-pattern> MUST match:
            * com.gitblit.Constants.SPARKLESHARE_INVITE_PATH
            * Wicket Filter ignorePaths parameter -->
    <servlet>
        <servlet-name>SparkleShareInviteServlet</servlet-name>
        <servlet-class>com.gitblit.SparkleShareInviteServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>SparkleShareInviteServlet</servlet-name>
        <url-pattern>/sparkleshare/*</url-pattern>
    </servlet-mapping>
    <!-- Syndication Servlet
         <url-pattern> MUST match: 
            * SyndicationFilter
@@ -244,6 +258,8 @@
                 * GitFilter <url-pattern>
                 * GitServlet <url-pattern>
                 * com.gitblit.Constants.GIT_PATH
                 * SparkleshareInviteServlet <url-pattern>
                 * com.gitblit.Constants.SPARKLESHARE_INVITE_PATH
                 * Zipfilter <url-pattern>
                 * ZipServlet <url-pattern>
                 * com.gitblit.Constants.ZIP_PATH
@@ -253,7 +269,7 @@
                 * PagesFilter <url-pattern>
                 * PagesServlet <url-pattern>
                 * com.gitblit.Constants.PAGES_PATH -->
            <param-value>git/,feed/,zip/,federation/,rpc/,pages/,robots.txt</param-value>
            <param-value>git/,feed/,zip/,federation/,rpc/,pages/,robots.txt,sparkleshare/</param-value>
        </init-param>
    </filter>
    <filter-mapping>
src/main/java/com/gitblit/Constants.java
@@ -57,6 +57,8 @@
    public static final String RPC_PATH = "/rpc/";
    
    public static final String PAGES= "/pages/";
    public static final String SPARKLESHARE_INVITE_PATH = "/sparkleshare/";
    public static final String BORDER = "***********************************************************";
src/main/java/com/gitblit/SparkleShareInviteServlet.java
New file
@@ -0,0 +1,210 @@
/*
 * Copyright 2013 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;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
/**
 * Handles requests for Sparkleshare Invites
 *
 * @author James Moger
 *
 */
public class SparkleShareInviteServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    public SparkleShareInviteServlet() {
        super();
    }
    /**
     * Returns an Sparkleshare invite url to this servlet for the repository.
     * https://github.com/hbons/SparkleShare/wiki/Invites
     *
     * @param baseURL
     * @param repository
     * @param username
     * @return an url
     */
    public static String asLink(String baseURL, String repository, String username) {
        if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
            baseURL = baseURL.substring(0, baseURL.length() - 1);
        }
        String url = baseURL + Constants.SPARKLESHARE_INVITE_PATH
                + ((StringUtils.isEmpty(username) ? "" : (username + "@")))
                + repository + ".xml";
        url = url.replace("https://", "sparkleshare://");
        url = url.replace("http://", "sparkleshare-unsafe://");
        return url;
    }
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
        processRequest(request, response);
    }
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }
    protected void processRequest(javax.servlet.http.HttpServletRequest request,
            javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException,
            java.io.IOException {
        // extract repo name from request
        String path = request.getPathInfo();
        if (path != null && path.length() > 1) {
            if (path.charAt(0) == '/') {
                path = path.substring(1);
            }
        }
        // trim trailing .xml
        if (path.endsWith(".xml")) {
            path = path.substring(0, path.length() - 4);
        }
        String username = null;
        int fetch = path.indexOf('@');
        if (fetch > -1) {
            username = path.substring(0, fetch);
            path = path.substring(fetch + 1);
        }
        UserModel user;
        if (StringUtils.isEmpty(username)) {
            user = GitBlit.self().authenticate(request);
        } else {
            user = GitBlit.self().getUserModel(username);
        }
        if (user == null) {
            user = UserModel.ANONYMOUS;
            username = "";
        }
        // ensure that the requested repository exists and is sparkleshared
        RepositoryModel model = GitBlit.self().getRepositoryModel(path);
        if (model == null) {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            response.getWriter().append(MessageFormat.format("Repository \"{0}\" not found!", path));
            return;
        } else if (!model.isSparkleshared()) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.getWriter().append(MessageFormat.format("Repository \"{0}\" is not sparkleshared!", path));
            return;
        }
        if (GitBlit.getBoolean(Keys.git.enableGitServlet, true)
                || GitBlit.getInteger(Keys.git.daemonPort, 0) > 0) {
            // Gitblit as server
            // determine username for repository url
            if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
                if (!user.canRewindRef(model)) {
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    response.getWriter().append(MessageFormat.format("\"{0}\" does not have RW+ permissions for {1}!", user.username, path));
                    return;
                }
            }
            if (model.accessRestriction.exceeds(AccessRestrictionType.NONE)) {
                username = user.username + "@";
            } else {
                username = "";
            }
            String serverPort = "";
            if (request.getScheme().equals("https")) {
                if (request.getServerPort() != 443) {
                    serverPort = ":" + request.getServerPort();
                }
            } else if (request.getScheme().equals("http")) {
                if (request.getServerPort() != 80) {
                    serverPort = ":" + request.getServerPort();
                }
            }
            // assume http/https serving
            String scheme = request.getScheme();
            String servletPath = Constants.GIT_PATH;
            // try to switch to git://, if git servlet disabled and repo has no restrictions
            if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)
                    && (GitBlit.getInteger(Keys.git.daemonPort, 0) > 0)
                    && AccessRestrictionType.NONE == model.accessRestriction) {
                scheme = "git";
                servletPath = "/";
                serverPort = GitBlit.getString(Keys.git.daemonPort, "");
            }
            // construct Sparkleshare invite
            StringBuilder sb = new StringBuilder();
            sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            sb.append("<sparkleshare><invite>\n");
            sb.append(MessageFormat.format("<address>{0}://{1}{2}{3}{4}</address>\n", scheme, username, request.getServerName(), serverPort, request.getContextPath()));
            sb.append(MessageFormat.format("<remote_path>{0}{1}</remote_path>\n", servletPath, model.name));
            if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) {
                // Gitblit is running it's own fanout service for pubsub notifications
                sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, "")));
            }
            sb.append("</invite></sparkleshare>\n");
            // write invite to client
            response.setContentType("application/xml");
            response.setContentLength(sb.length());
            response.getWriter().append(sb.toString());
        } else {
            // Gitblit as viewer, repository access handled externally so
            // assume RW+ permission
            List<String> others = GitBlit.getStrings(Keys.web.otherUrls);
            if (others.size() == 0) {
                return;
            }
            String address = MessageFormat.format(others.get(0), "", username);
            StringBuilder sb = new StringBuilder();
            sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
            sb.append("<sparkleshare><invite>\n");
            sb.append(MessageFormat.format("<address>{0}</address>\n", address));
            sb.append(MessageFormat.format("<remote_path>{0}</remote_path>\n", model.name));
            if (GitBlit.getInteger(Keys.fanout.port, 0) > 0) {
                // Gitblit is running it's own fanout service for pubsub notifications
                sb.append(MessageFormat.format("<announcements_url>tcp://{0}:{1}</announcements_url>\n", request.getServerName(), GitBlit.getString(Keys.fanout.port, "")));
            }
            sb.append("</invite></sparkleshare>\n");
            // write invite to client
            response.setContentType("application/xml");
            response.setContentLength(sb.length());
            response.getWriter().append(sb.toString());
        }
    }
}
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -452,3 +452,4 @@
gb.externalPermissions = {0} access permissions for {1} are externally maintained
gb.viewAccess = You do not have Gitblit read or write access
gb.yourProtocolPermissionIs = Your {0} access permission for {1} is {2}
gb.sparkleshareInvite = SparkleShare invite
src/main/java/com/gitblit/wicket/pages/BasePage.java
@@ -57,6 +57,7 @@
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.SparkleShareInviteServlet;
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
@@ -312,6 +313,32 @@
        }
    }
    
    protected String getSparkleShareInviteUrl(RepositoryModel repository) {
        if (repository.isBare && repository.isSparkleshared()) {
            UserModel user = GitBlitWebSession.get().getUser();
            if (user == null) {
                user = UserModel.ANONYMOUS;
            }
            String username = null;
            if (UserModel.ANONYMOUS != user) {
                username = user.username;
            }
            if (GitBlit.getBoolean(Keys.git.enableGitServlet, true) || (GitBlit.getInteger(Keys.git.daemonPort, 0) > 0)) {
                // Gitblit as server
                // ensure user can rewind
                if (user.canRewindRef(repository)) {
                    String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest());
                    return SparkleShareInviteServlet.asLink(baseURL, repository.name, username);
                }
            } else {
                // Gitblit as viewer, assume RW+ permission
                String baseURL = WicketUtils.getGitblitURL(RequestCycle.get().getRequest());
                return SparkleShareInviteServlet.asLink(baseURL, repository.name, username);
            }
        }
        return null;
    }
    protected List<ProjectModel> getProjectModels() {
        final UserModel user = GitBlitWebSession.get().getUser();
        List<ProjectModel> projects = GitBlit.self().getProjectModels(user, true);
src/main/java/com/gitblit/wicket/pages/SummaryPage.html
@@ -26,6 +26,7 @@
                        <div wicket:id="otherUrls" >
                            <div wicket:id="otherUrl" style="padding-top:10px"></div>
                        </div>
                        <div wicket:id="repositorySparkleShareInviteUrl">[repository sparkleshare invite url]</div>
                    </td>
                </tr>
            </table>
src/main/java/com/gitblit/wicket/pages/SummaryPage.java
@@ -60,6 +60,7 @@
import com.gitblit.wicket.panels.DetailedRepositoryUrlPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.LogPanel;
import com.gitblit.wicket.panels.SparkleShareInvitePanel;
import com.gitblit.wicket.panels.TagsPanel;
public class SummaryPage extends RepositoryPage {
@@ -167,6 +168,15 @@
        }
        add(gitDaemonUrlPanel);
        
        String sparkleshareUrl = getSparkleShareInviteUrl(model);
        if (StringUtils.isEmpty(sparkleshareUrl)) {
            add(new Label("repositorySparkleShareInviteUrl").setVisible(false));
        } else {
            Component sparklesharePanel = new SparkleShareInvitePanel("repositorySparkleShareInviteUrl", getLocalizer(), this, sparkleshareUrl, accessPermission);
            WicketUtils.setCssStyle(sparklesharePanel, "padding-top: 10px;");
            add(sparklesharePanel);
        }
        ListDataProvider<String> urls = new ListDataProvider<String>(repositoryUrls);
        DataView<String> otherUrlsView = new DataView<String>("otherUrls", urls) {
            private static final long serialVersionUID = 1L;
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.html
New file
@@ -0,0 +1,18 @@
<!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">
<wicket:panel>
       <span class="repositoryUrlContainer">
        <span class="repositoryUrlEndCap">
            <img wicket:id="sparkleshareIcon"></img>
        </span>
        <span class="repositoryUrl">
            <a wicket:id="inviteUrl"><wicket:message key="gb.sparkleshareInvite"></wicket:message></a>
        </span>
        <span class="hidden-phone hidden-tablet repositoryUrlEndCap" wicket:id="accessPermission">[access permission]</span>
    </span>
</wicket:panel>
</html>
src/main/java/com/gitblit/wicket/panels/SparkleShareInvitePanel.java
New file
@@ -0,0 +1,52 @@
/*
 * Copyright 2013 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.panels;
import org.apache.wicket.Component;
import org.apache.wicket.Localizer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.ContextImage;
import org.apache.wicket.markup.html.link.ExternalLink;
import com.gitblit.Constants.AccessPermission;
import com.gitblit.wicket.WicketUtils;
public class SparkleShareInvitePanel extends BasePanel {
    private static final long serialVersionUID = 1L;
    public SparkleShareInvitePanel(String wicketId, Localizer localizer, Component parent, String url, AccessPermission ap) {
        super(wicketId);
        ContextImage star = WicketUtils.newImage("sparkleshareIcon", "star_16x16.png");
        add(star);
        add(new ExternalLink("inviteUrl", url));
        String note = localizer.getString("gb.externalAccess", parent);
        String permission = "";
        if (ap != null) {
            permission = ap.toString();
            if (ap.atLeast(AccessPermission.PUSH)) {
                note = localizer.getString("gb.readWriteAccess", parent);
            } else if (ap.atLeast(AccessPermission.CLONE)) {
                note = localizer.getString("gb.readOnlyAccess", parent);
            } else {
                note = localizer.getString("gb.viewAccess", parent);
            }
        }
        Label label = new Label("accessPermission", permission);
        WicketUtils.setHtmlTooltip(label, note);
        add(label);
    }
}