James Moger
2011-09-14 8f73a7cc630bb61d088c7cdad30a6708870184ee
Implemented Federation Sets. Documentation.
17 files modified
192 ■■■■ changed files
distrib/gitblit.properties 26 ●●●● patch | view | raw | blame | history
docs/00_index.mkd 1 ●●●● patch | view | raw | blame | history
docs/01_setup.mkd 1 ●●●● patch | view | raw | blame | history
docs/02_federation.mkd 11 ●●●●● patch | view | raw | blame | history
docs/04_releases.mkd 1 ●●●● patch | view | raw | blame | history
resources/gitblit.css 6 ●●●●● patch | view | raw | blame | history
src/com/gitblit/Constants.java 10 ●●●●● patch | view | raw | blame | history
src/com/gitblit/FederationServlet.java 18 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 29 ●●●●● patch | view | raw | blame | history
src/com/gitblit/build/BuildSite.java 4 ●●● patch | view | raw | blame | history
src/com/gitblit/models/RepositoryModel.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.html 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.java 24 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/FederationProposalPage.java 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/FederationTokensPanel.html 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/FederationTokensPanel.java 29 ●●●●● patch | view | raw | blame | history
distrib/gitblit.properties
@@ -226,22 +226,26 @@
# Registered extensions for google-code-prettify
#
# SPACE-DELIMITED
# SINCE 0.5.0
web.prettyPrintExtensions = c cpp cs css htm html java js php pl prefs properties py rb sh sql xml vb
# Registered extensions for markdown transformation
#
# SPACE-DELIMITED
# CASE-SENSITIVE
# SINCE 0.5.0
web.markdownExtensions = md mkd markdown MD MKD
# Image extensions
#
# SPACE-DELIMITED
# SINCE 0.5.0
web.imageExtensions = bmp jpg gif png 
# Registered extensions for binary blobs
#
# SPACE-DELIMITED
# SINCE 0.5.0
web.binaryExtensions = jar pdf tar.gz zip
@@ -304,8 +308,9 @@
# SINCE 0.6.0
mail.fromAddress = 
# Space-separated list of email addresses for the Gitblit administrators
# List of email addresses for the Gitblit administrators
#
# SPACE-DELIMITED
# SINCE 0.6.0
mail.adminAddresses = 
@@ -338,7 +343,7 @@
#
# CASE-SENSITIVE
# SINCE 0.6.0
# RESTART REQUIRED
# RESTART REQUIRED *(only to enable or disable federation)*
federation.passphrase =
# Control whether or not this Gitblit instance can receive federation proposals
@@ -360,6 +365,17 @@
# SINCE 0.6.0
federation.defaultFrequency = 60 mins
# Federation Sets are named groups of repositories.  The Federation Sets are
# available for selection in the repository settings page.  You can assign a
# repository to one or more sets and then distribute the token for the set.
# This allows you to grant federation pull access to a subset of your available
# repositories.  Tokens for federation sets only grant repository pull access.
#
# SPACE-DELIMITED
# CASE-SENSITIVE
# SINCE 0.6.0
federation.sets =
# Federation pull registrations
# Registrations are read once, at startup.
#
@@ -369,10 +385,10 @@
#   The shortest frequency allowed is every 5 minutes
#   Decimal frequency values are cast to integers
#   Frequency values may be specified in mins, hours, or days
#   Values that can not be parsed default to *federation.defaultFrequency*
#   Values that can not be parsed or are unspecified default to *federation.defaultFrequency*
#
# folder:
#   if blank, the folder is *git.repositoriesFolder*
#   if unspecified, the folder is *git.repositoriesFolder*
#   if specified, the folder is relative to *git.repositoriesFolder*
#
# mergeAccounts:
@@ -384,7 +400,7 @@
#   notified by email of pull failures
#
# include and exclude:
#   space-separated list of repositories to include or exclude from pull
#   Space-delimited list of repositories to include or exclude from pull
#   may be * wildcard to include or exclude all
#   may use fuzzy match (e.g. org.eclipse.*)
docs/00_index.mkd
@@ -30,6 +30,7 @@
<br/>**New:** *federation.allowProposals = false*
<br/>**New:** *federation.proposalsFolder = proposals*
<br/>**New:** *federation.defaultFrequency = 60 mins*
<br/>**New:** *federation.sets =*
<br/>**New:** *mail.* settings for sending emails
<br/>**New:** user role *#notfederated* to prevent a user account from being pulled by a federated Gitblit instance
- added: google-gson dependency
docs/01_setup.mkd
@@ -132,6 +132,7 @@
        showReadme = false
        excludeFromFederation = false
        isFederated = false
        federationSets =
        
#### Repository Names
Repository names must be unique and are CASE-SENSITIVE ON CASE-SENSITIVE FILESYSTEMS.  The name must be composed of letters, digits, or `/ _ - .`<br/>
docs/02_federation.mkd
@@ -49,6 +49,9 @@
If *federation.passphrase* has a non-empty value, the federation tokens are displayed in the log file and are visible, to administrators, in the web ui.
#### Federation Sets
Federation Sets (*federation.sets*) are named groups of repositories.  The Federation Sets are available for selection in the repository settings page.  You can assign a repository to one or more sets and then distribute the token for the set.  This allows you to grant federation pull access to a subset of your available repositories.  Tokens for federation sets only grant pull access for the member repositories.
### Federation Proposals (Source Gitblit Instance)
@@ -177,7 +180,7 @@
</tr>
<tr><th>federation.N.frequency</th>
<td>N [mins/hours/days]</td>
<td>x [mins/hours/days]</td>
<td>the period to wait between pull executions</td>
</tr>
@@ -198,16 +201,16 @@
<tr><th>federation.N.freeze</th>
<td>boolean</td>
<td>freeze the repository at the end of each pull</td>
<td>freeze the repository after the first pull, subsequent pulls respect the local *isFrozen* setting</td>
</tr>
<tr><th>federation.N.include</th>
<td>string array<br/>(space separated)</td>
<td>string array<br/>(space-delimited)</td>
<td>list of included repositories *(wildcard and fuzzy matching supported)*</td>
</tr>
<tr><th>federation.N.exclude</th>
<td>string array<br/>(space separated)</td>
<td>string array<br/>(space-delimited)</td>
<td>list of excluded repositories *(wildcard and fuzzy matching supported)*</td>
</tr>
docs/04_releases.mkd
@@ -10,6 +10,7 @@
<br/>**New:** *federation.allowProposals = false*
<br/>**New:** *federation.proposalsFolder = proposals*
<br/>**New:** *federation.defaultFrequency = 60 mins*
<br/>**New:** *federation.sets =*
<br/>**New:** *mail.* settings for sending emails
<br/>**New:** user role *#notfederated* to prevent a user account from being pulled by a federated Gitblit instance
- added: google-gson dependency
resources/gitblit.css
@@ -22,6 +22,12 @@
    background: url(background.png) repeat-x scroll 0 0 #FFFFFF;
}
hr {
    color: #ffffff;
    background-color: #ffffff;
    height: 1px; !important
}
pre, code, pre.prettyprint, pre.plainprint {
    color: black;
    font-family: monospace;
src/com/gitblit/Constants.java
@@ -151,7 +151,7 @@
            return name();
        }
    }
    /**
     * Enumeration representing the federation types.
     */
@@ -167,6 +167,14 @@
            return FEDERATE_THIS;
        }
        public boolean exceeds(FederationStrategy type) {
            return this.ordinal() > type.ordinal();
        }
        public boolean atLeast(FederationStrategy type) {
            return this.ordinal() >= type.ordinal();
        }
        @Override
        public String toString() {
            return name();
src/com/gitblit/FederationServlet.java
@@ -225,6 +225,12 @@
        Object result = null;
        if (FederationRequest.PULL_REPOSITORIES.equals(reqType)) {
            // build a reverse-lookup for token->federation set name
            Map<String, String> federationSets = new HashMap<String, String>();
            for (String set : GitBlit.getStrings(Keys.federation.sets)) {
                federationSets.put(GitBlit.self().getFederationToken(set), set);
            }
            // Determine the Gitblit clone url
            StringBuilder sb = new StringBuilder();
            sb.append(HttpUtils.getHostURL(request));
@@ -253,7 +259,17 @@
                    }
                    break;
                }
                repositories.put(url, model);
                if (federationSets.containsKey(token)) {
                    // include repositories only for federation set
                    String set = federationSets.get(token);
                    if (model.federationSets.contains(set)) {
                        repositories.put(url, model);
                    }
                } else {
                    // standard federation token for ALL
                    repositories.put(url, model);
                }
            }
            result = repositories;
        } else {
src/com/gitblit/GitBlit.java
@@ -21,6 +21,7 @@
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -521,6 +522,8 @@
            model.showReadme = getConfig(config, "showReadme", false);
            model.federationStrategy = FederationStrategy.fromName(getConfig(config,
                    "federationStrategy", null));
            model.federationSets = new ArrayList<String>(Arrays.asList(config.getStringList(
                    "gitblit", null, "federationSets")));
            model.isFederated = getConfig(config, "isFederated", false);
            model.origin = config.getString("remote", "origin", "url");
        }
@@ -697,6 +700,7 @@
        config.setBoolean("gitblit", null, "showRemoteBranches", repository.showRemoteBranches);
        config.setBoolean("gitblit", null, "isFrozen", repository.isFrozen);
        config.setBoolean("gitblit", null, "showReadme", repository.showReadme);
        config.setStringList("gitblit", null, "federationSets", repository.federationSets);
        config.setString("gitblit", null, "federationStrategy",
                repository.federationStrategy.name());
        config.setBoolean("gitblit", null, "isFederated", repository.isFederated);
@@ -810,9 +814,16 @@
            validPassphrase = false;
        }
        if (validPassphrase) {
            // standard tokens
            for (FederationToken tokenType : FederationToken.values()) {
                logger.info(MessageFormat.format("Federation {0} token = {1}", tokenType.name(),
                        getFederationToken(tokenType)));
            }
            // federation set tokens
            for (String set : settings.getStrings(Keys.federation.sets)) {
                logger.info(MessageFormat.format("Federation Set {0} token = {1}", set,
                        getFederationToken(set)));
            }
        }
@@ -838,6 +849,7 @@
            keys.remove(Keys.federation.allowProposals);
            keys.remove(Keys.federation.proposalsFolder);
            keys.remove(Keys.federation.defaultFrequency);
            keys.remove(Keys.federation.sets);
            Collections.sort(keys);
            Map<String, FederationModel> federatedModels = new HashMap<String, FederationModel>();
            for (String key : keys) {
@@ -936,8 +948,13 @@
     */
    public List<String> getFederationTokens() {
        List<String> tokens = new ArrayList<String>();
        // generate standard tokens
        for (FederationToken type : FederationToken.values()) {
            tokens.add(getFederationToken(type));
        }
        // generate tokens for federation sets
        for (String set : settings.getStrings(Keys.federation.sets)) {
            tokens.add(getFederationToken(set));
        }
        return tokens;
    }
@@ -949,8 +966,18 @@
     * @return a federation token
     */
    public String getFederationToken(FederationToken type) {
        return getFederationToken(type.name());
    }
    /**
     * Returns the specified federation token for this Gitblit instance.
     *
     * @param value
     * @return a federation token
     */
    public String getFederationToken(String value) {
        String passphrase = settings.getString(Keys.federation.passphrase, "");
        return StringUtils.getSHA1(passphrase + "-" + type.name());
        return StringUtils.getSHA1(passphrase + "-" + value);
    }
    /**
src/com/gitblit/build/BuildSite.java
@@ -55,6 +55,8 @@
 */
public class BuildSite {
    private static final String SPACE_DELIMITED = "SPACE-DELIMITED";
    private static final String CASE_SENSITIVE = "CASE-SENSITIVE";
    private static final String RESTART_REQUIRED = "RESTART REQUIRED";
@@ -265,7 +267,7 @@
        for (Setting setting : settings) {
            for (String comment : setting.comments) {
                if (comment.contains(SINCE) || comment.contains(RESTART_REQUIRED)
                        || comment.contains(CASE_SENSITIVE)) {
                        || comment.contains(CASE_SENSITIVE) || comment.contains(SPACE_DELIMITED)) {
                    sb.append(MessageFormat.format(
                            "<span style=\"color:#004000;\"># <i>{0}</i></span>",
                            transformMarkdown(comment)));
src/com/gitblit/models/RepositoryModel.java
@@ -16,7 +16,9 @@
package com.gitblit.models;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.FederationStrategy;
@@ -45,6 +47,7 @@
    public boolean isFrozen;
    public boolean showReadme;
    public FederationStrategy federationStrategy;
    public List<String> federationSets;
    public boolean isFederated;
    public String frequency;
    public String origin;
@@ -60,6 +63,7 @@
        this.owner = owner;
        this.lastChange = lastchange;
        this.accessRestriction = AccessRestrictionType.NONE;
        federationSets = new ArrayList<String>();
    }
    @Override
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -108,9 +108,9 @@
gb.excludeFromFederation = exclude from federation
gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this object
gb.tokens = federation tokens
gb.tokenAllDescription = federate repositories, users, & settings
gb.tokenUnrDescription = federate repositories & users
gb.tokenJurDescription = federate repositories
gb.tokenAllDescription = all repositories, users, & settings
gb.tokenUnrDescription = all repositories & users
gb.tokenJurDescription = all repositories
gb.federatedRepositoryDefinitions = repository definitions
gb.federatedUserDefinitions = user definitions
gb.federatedSettingDefinitions = setting definitions
@@ -128,9 +128,10 @@
gb.exclusions = exclusions
gb.registration = registration
gb.registrations = federation registrations
gb.sendProposal send proposal
gb.sendProposal propose
gb.status = status
gb.origin = origin
gb.federationStrategy = federation strategy
gb.federationRegistration = federation registration
gb.federationResults = federation pull results
gb.federationSets = federation sets
src/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -23,10 +23,13 @@
                <tr><th><wicket:message key="gb.enableDocs"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="useDocs" tabindex="6" /> &nbsp;<i><wicket:message key="gb.useDocsDescription"></wicket:message></i></td></tr>
                <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="7" /> &nbsp;<i><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></i></td></tr>
                <tr><th><wicket:message key="gb.showReadme"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="showReadme" tabindex="8" /> &nbsp;<i><wicket:message key="gb.showReadmeDescription"></wicket:message></i></td></tr>
                <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select wicket:id="accessRestriction" tabindex="9" /></td></tr>
                <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="10" /> &nbsp;<i><wicket:message key="gb.isFrozenDescription"></wicket:message></i></td></tr>
                <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><input type="checkbox" wicket:id="isFrozen" tabindex="9" /> &nbsp;<i><wicket:message key="gb.isFrozenDescription"></wicket:message></i></td></tr>
                <tr><td style="padding-top:10px;" colspan="2"><hr></hr></td></tr>
                <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select wicket:id="accessRestriction" tabindex="10" /></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
                <tr><td style="padding-top:10px;" colspan="2"><hr></hr></td></tr>
                <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select wicket:id="federationStrategy" tabindex="11" /></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.permittedUsers"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
                <tr><th style="vertical-align: top;"><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
                <tr><th></th><td class="editButton"><input type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" tabindex="12" /> <input type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" tabindex="13" /></td></tr>
            </tbody>
        </table>
src/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -73,6 +73,7 @@
        // ensure this user can create or edit this repository
        checkPermissions(repositoryModel);
        List<String> federationSets = new ArrayList<String>();
        List<String> repositoryUsers = new ArrayList<String>();
        if (isCreate) {
            super.setupPage("", getString("gb.newRepository"));
@@ -82,12 +83,21 @@
                repositoryUsers.addAll(GitBlit.self().getRepositoryUsers(repositoryModel));
                Collections.sort(repositoryUsers);
            }
            federationSets.addAll(repositoryModel.federationSets);
        }
        final String oldName = repositoryModel.name;
        // users palette
        final Palette<String> usersPalette = new Palette<String>("users", new ListModel<String>(
                repositoryUsers), new CollectionModel<String>(GitBlit.self().getAllUsernames()),
                new ChoiceRenderer<String>("", ""), 10, false);
        // federation sets palette
        List<String> sets = GitBlit.getStrings(Keys.federation.sets);
        final Palette<String> federationSetsPalette = new Palette<String>("federationSets",
                new ListModel<String>(federationSets), new CollectionModel<String>(sets),
                new ChoiceRenderer<String>("", ""), 10, false);
        CompoundPropertyModel<RepositoryModel> model = new CompoundPropertyModel<RepositoryModel>(
                repositoryModel);
        Form<RepositoryModel> form = new Form<RepositoryModel>("editForm", model) {
@@ -136,6 +146,15 @@
                        return;
                    }
                    // save federation set preferences
                    if (repositoryModel.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
                        repositoryModel.federationSets.clear();
                        Iterator<String> sets = federationSetsPalette.getSelectedChoices();
                        while (sets.hasNext()) {
                            repositoryModel.federationSets.add(sets.next());
                        }
                    }
                    // save the repository
                    GitBlit.self().updateRepositoryModel(oldName, repositoryModel, isCreate);
@@ -171,8 +190,8 @@
                .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer()));
        form.add(new CheckBox("isFrozen"));
        // TODO enable origin definition
        form.add(new TextField<String>("origin").setEnabled(false/*isCreate*/));
        form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
        // federation strategies - remove ORIGIN choice if this repository has
        // no origin.
        List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
@@ -187,6 +206,7 @@
        form.add(new CheckBox("showRemoteBranches"));
        form.add(new CheckBox("showReadme"));
        form.add(usersPalette);
        form.add(federationSetsPalette);
        form.add(new Button("save"));
        Button cancel = new Button("cancel") {
src/com/gitblit/wicket/pages/FederationProposalPage.java
@@ -79,6 +79,7 @@
        sb.append(asParam(p, proposal.name, "frequency",
                GitBlit.getString(Keys.federation.defaultFrequency, "60 mins")));
        sb.append(asParam(p, proposal.name, "folder", proposal.name));
        sb.append(asParam(p, proposal.name, "freeze", "true"));
        sb.append(asParam(p, proposal.name, "sendStatus", "true"));
        sb.append(asParam(p, proposal.name, "notifyOnError", "true"));
        sb.append(asParam(p, proposal.name, "exclude", ""));
src/com/gitblit/wicket/panels/FederationTokensPanel.html
@@ -8,8 +8,7 @@
<wicket:panel>
        <div class="admin_nav">
            <a wicket:id="federatedRepositories"><wicket:message key="gb.federatedRepositoryDefinitions">[repositories]</wicket:message></a>
             | <a wicket:id="federatedUsers"><wicket:message key="gb.federatedUserDefinitions">[users]</wicket:message></a>
             <a wicket:id="federatedUsers"><wicket:message key="gb.federatedUserDefinitions">[users]</wicket:message></a>
             | <a wicket:id="federatedSettings"><wicket:message key="gb.federatedSettingDefinitions">[settings]</wicket:message></a>            
        </div>
@@ -20,15 +19,13 @@
                <wicket:message key="gb.tokens">[tokens]</wicket:message>
            </th>
            <th></th>
            <th></th>
            <th class="right"></th>
        </tr>
        <tbody>        
               <tr wicket:id="row">
                   <td class="left"><span class="list" wicket:id="field">[field]</span></td>
                   <td class="left"><span wicket:id="description"></span></td>
                   <td><span class="sha1"" wicket:id="value">[value]</span></td>
                   <td><span wicket:id="description"></span></td>
                   <td class="rightAlign"><span class="link"><a wicket:id="send"><wicket:message key="gb.sendProposal">[send proposal]</wicket:message></a></span></td>
                   <td class="rightAlign"><span class="link"><a wicket:id="repositoryDefinitions"><wicket:message key="gb.federatedRepositoryDefinitions">[repository definitions]</wicket:message></a> | <a wicket:id="send"><wicket:message key="gb.sendProposal">[send proposal]</wicket:message></a></span></td>
               </tr>
        </tbody>
    </table>
src/com/gitblit/wicket/panels/FederationTokensPanel.java
@@ -16,6 +16,7 @@
package com.gitblit.wicket.panels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.wicket.markup.html.basic.Label;
@@ -29,6 +30,7 @@
import com.gitblit.Constants.FederationToken;
import com.gitblit.FederationServlet;
import com.gitblit.GitBlit;
import com.gitblit.Keys;
import com.gitblit.wicket.WicketUtils;
public class FederationTokensPanel extends BasePanel {
@@ -38,11 +40,7 @@
    public FederationTokensPanel(String wicketId, final boolean showFederation) {
        super(wicketId);
        String baseUrl = getRequest().getRelativePathPrefixToContextRoot();
        add(new ExternalLink("federatedRepositories", FederationServlet.asPullLink(baseUrl, GitBlit
                .self().getFederationToken(FederationToken.REPOSITORIES),
                FederationRequest.PULL_REPOSITORIES)));
        final String baseUrl = getRequest().getRelativePathPrefixToContextRoot();
        add(new ExternalLink("federatedUsers", FederationServlet.asPullLink(baseUrl, GitBlit.self()
                .getFederationToken(FederationToken.USERS_AND_REPOSITORIES),
                FederationRequest.PULL_USERS)));
@@ -52,7 +50,13 @@
        final List<String[]> data = new ArrayList<String[]>();
        for (FederationToken token : FederationToken.values()) {
            data.add(new String[] { token.name(), GitBlit.self().getFederationToken(token) });
            data.add(new String[] { token.name(), GitBlit.self().getFederationToken(token), null });
        }
        List<String> sets = GitBlit.getStrings(Keys.federation.sets);
        Collections.sort(sets);
        for (String set : sets) {
            data.add(new String[] { FederationToken.REPOSITORIES.name(),
                    GitBlit.self().getFederationToken(set), set });
        }
        DataView<String[]> dataView = new DataView<String[]>("row", new ListDataProvider<String[]>(
@@ -69,9 +73,17 @@
            public void populateItem(final Item<String[]> item) {
                final String[] entry = item.getModelObject();
                final FederationToken token = FederationToken.fromName(entry[0]);
                item.add(new Label("field", entry[0]));
                if (entry[2] == null) {
                    // standard federation token
                    item.add(new Label("description", describeToken(token)));
                } else {
                    // federation set token
                    item.add(new Label("description", entry[2]));
                }
                item.add(new Label("value", entry[1]));
                item.add(new ExternalLink("repositoryDefinitions", FederationServlet.asPullLink(
                        baseUrl, entry[1], FederationRequest.PULL_REPOSITORIES)));
                // TODO make this work
                Link<Void> sendProposal = new Link<Void>("send") {
@@ -87,7 +99,6 @@
                        "Please enter URL for remote Gitblit instance:"));
                item.add(sendProposal);
                item.add(new Label("description", describeToken(token)));
                WicketUtils.setAlternatingBackground(item, counter);
                counter++;
            }