James Moger
2014-06-03 85b5d72949ead641ba697543324ff5d236e23fd1
Overhaul EditRepositoryPage for layout and usability
6 files modified
617 ■■■■ changed files
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties 24 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html 248 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java 288 ●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html 35 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java 20 ●●●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html 2 ●●● patch | view | raw | blame | history
src/main/java/com/gitblit/wicket/GitBlitWebApp.properties
@@ -133,7 +133,7 @@
gb.status = status
gb.origin = origin
gb.headRef = default branch (HEAD)
gb.headRefDescription = change the ref that HEAD links to. e.g. refs/heads/master
gb.headRefDescription = The default branch that will be cloned and displayed on the Summary page.
gb.federationStrategy = federation strategy
gb.federationRegistration = federation registration
gb.federationResults = federation pull results
@@ -223,8 +223,8 @@
gb.noHits = no hits
gb.authored = authored
gb.committed = committed
gb.indexedBranches = indexed branches
gb.indexedBranchesDescription = select the branches to include in your Lucene index
gb.indexedBranches = Indexed Branches
gb.indexedBranchesDescription = Select the branches to be indexed by Lucene
gb.noIndexedRepositoriesWarning = none of your repositories are configured for Lucene indexing
gb.undefinedQueryWarning = query is undefined!
gb.noSelectedRepositoriesWarning = please select one or more repositories!
@@ -704,3 +704,21 @@
gb.initWithReadmeDescription = This will generate a simple README document for your repository.
gb.initWithGitignore = Include a .gitignore file
gb.initWithGitignoreDescription = This will insert a config file that instructs your Git clients to ignore files or directories that match defined patterns.
gb.receive = receive
gb.permissions = permissions
gb.ownersDescription = Owners can manage all repository settings but they are not allowed to rename a repository unless it is their personal repository.
gb.userPermissionsDescription = You can specify individual user permissions. These settings will override team or regex permissions.
gb.teamPermissionsDescription = You can specify individual team permissions. These settings will override regex permissions.
gb.ticketSettings = Ticket Settings
gb.receiveSettings = Receive Settings
gb.receiveSettingsDescription = The receive settings control pushes to the repository.
gb.preReceiveDescription = Pre-receive hooks are executed after commits are received but <em>BEFORE</em> the refs are updated.<p>This is the appropriate hook for rejecting a push.</p>
gb.postReceiveDescription = Post-receive hooks are executed after commits are received but <em>AFTER</em> the refs are updated.<p>This is the appropriate hook for notifications, build triggers, etc.</p>
gb.federationStrategyDescription = Control if and how to federate this repository with another Gitblit.
gb.federationSetsDescription = This repository will be included in the selected federation sets.
gb.miscellaneous = miscellaneous
gb.originDescription = The url from which this repository was cloned.
gb.gc = GC
gb.garbageCollection = Garbage Collection
gb.garbageCollectionDescription = The garbage collector will pack loose objects pushed from clients and will remove unreferenced objects from the repository.
gb.commitMessageRendererDescription = Commit messages can be displayed as plaintext or as rendered markup.
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -9,14 +9,17 @@
    <form style="padding-top:5px;" wicket:id="editForm">
<div class="row">
<div class="span12">
<div class="tabbable">
<div class="tabbable tabs-left">
    <!-- tab titles -->
    <ul class="nav nav-tabs">
        <li class="active"><a href="#general" data-toggle="tab"><wicket:message key="gb.general"></wicket:message></a></li>
        <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.accessPermissions"></wicket:message></a></li>
        <li><a href="#permissions" data-toggle="tab"><wicket:message key="gb.permissions"></wicket:message></a></li>
        <li><a href="#receive" data-toggle="tab"><wicket:message key="gb.receive"></wicket:message></a></li>
        <li><a href="#tickets" data-toggle="tab"><wicket:message key="gb.tickets"></wicket:message></a></li>
        <li><a href="#federation" data-toggle="tab"><wicket:message key="gb.federation"></wicket:message></a></li>
        <li><a href="#search" data-toggle="tab"><wicket:message key="gb.search"></wicket:message></a></li>
        <li><a href="#hooks" data-toggle="tab"><wicket:message key="gb.hookScripts"></wicket:message></a></li>
        <li><a href="#gc" data-toggle="tab"><wicket:message key="gb.gc"></wicket:message></a></li>
        <li><a href="#misc" data-toggle="tab"><wicket:message key="gb.miscellaneous"></wicket:message></a></li>
    </ul>
    <!-- tab content -->
@@ -24,97 +27,200 @@
        <!-- general tab -->
        <div class="tab-pane active" id="general">
        <div wicket:id="namePanel"></div>
            <div wicket:id="namePanel"></div>
        
        <table class="plain">
            <tbody class="settings">
                <tr><th colspan="2"><hr/></th></tr>
                <tr><th><wicket:message key="gb.origin"></wicket:message></th><td class="edit"><input class="span5" type="text" wicket:id="origin" size="80" tabindex="3" /></td></tr>
                <tr><th><wicket:message key="gb.headRef"></wicket:message></th><td class="edit"><select class="span3" wicket:id="HEAD" tabindex="4" /> &nbsp;<span class="help-inline"><wicket:message key="gb.headRefDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.gcPeriod"></wicket:message></th><td class="edit"><select class="span2" wicket:id="gcPeriod" tabindex="5" /> &nbsp;<span class="help-inline"><wicket:message key="gb.gcPeriodDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.gcThreshold"></wicket:message></th><td class="edit"><input class="span1" type="text" wicket:id="gcThreshold" tabindex="6" /> &nbsp;<span class="help-inline"><wicket:message key="gb.gcThresholdDescription"></wicket:message></span></td></tr>
                <tr><th colspan="2"><hr/></th></tr>
                <tr><th><wicket:message key="gb.acceptNewTickets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewTickets" tabindex="7" /> &nbsp;<span class="help-inline"><wicket:message key="gb.acceptNewTicketsDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.acceptNewPatchsets"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="acceptNewPatchsets" tabindex="8" /> &nbsp;<span class="help-inline"><wicket:message key="gb.acceptNewPatchsetsDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.requireApproval"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="requireApproval" tabindex="9" /> &nbsp;<span class="help-inline"><wicket:message key="gb.requireApprovalDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.mergeTo"></wicket:message></th><td class="edit"><select class="span2" wicket:id="mergeTo" tabindex="10" /> &nbsp;<span class="help-inline"><wicket:message key="gb.mergeToDescription"></wicket:message></span></td></tr>
                <tr><th colspan="2"><hr/></th></tr>
                <tr><th><wicket:message key="gb.enableIncrementalPushTags"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="useIncrementalPushTags" tabindex="11" /> &nbsp;<span class="help-inline"><wicket:message key="gb.useIncrementalPushTagsDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.showRemoteBranches"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="showRemoteBranches" tabindex="12" /> &nbsp;<span class="help-inline"><wicket:message key="gb.showRemoteBranchesDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.skipSizeCalculation"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSizeCalculation" tabindex="13" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSizeCalculationDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.skipSummaryMetrics"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="skipSummaryMetrics" tabindex="14" /> &nbsp;<span class="help-inline"><wicket:message key="gb.skipSummaryMetricsDescription"></wicket:message></span></label></td></tr>
                <tr><th><wicket:message key="gb.maxActivityCommits"></wicket:message></th><td class="edit"><select class="span2" wicket:id="maxActivityCommits" tabindex="15" /> &nbsp;<span class="help-inline"><wicket:message key="gb.maxActivityCommitsDescription"></wicket:message></span></td></tr>
                <tr><th><wicket:message key="gb.metricAuthorExclusions"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="metricAuthorExclusions" size="40" tabindex="16" /></td></tr>
                <tr><th><wicket:message key="gb.commitMessageRenderer"></wicket:message></th><td class="edit"><select class="span2" wicket:id="commitMessageRenderer" tabindex="17" /></td></tr>
                <tr><th colspan="2"><hr/></th></tr>
                <tr><th><wicket:message key="gb.mailingLists"></wicket:message></th><td class="edit"><input class="span8" type="text" wicket:id="mailingLists" size="40" tabindex="18" /></td></tr>
            </tbody>
        </table>
            <hr/>
            <div wicket:id="accessPolicyPanel"></div>
        </div>
        <!-- access permissions -->
        <div class="tab-pane" id="permissions">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.owners"></wicket:message></th><td class="edit"><span wicket:id="owners" tabindex="19" /> </td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th></th><td style="padding:2px;"><span wicket:id="accessPolicyPanel"></span></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.isFrozen"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="isFrozen" tabindex="21" /> &nbsp;<span class="help-inline"><wicket:message key="gb.isFrozenDescription"></wicket:message></span></label></td></tr>
                    <tr><th><wicket:message key="gb.allowForks"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="allowForks" tabindex="22" /> &nbsp;<span class="help-inline"><wicket:message key="gb.allowForksDescription"></wicket:message></span></label></td></tr>
                    <tr><th><wicket:message key="gb.verifyCommitter"></wicket:message></th><td class="edit"><label class="checkbox"><input type="checkbox" wicket:id="verifyCommitter" tabindex="23" /> &nbsp;<span class="help-inline"><wicket:message key="gb.verifyCommitterDescription"></wicket:message></span><br/><span class="help-inline" style="padding-left:10px;"><wicket:message key="gb.verifyCommitterNote"></wicket:message></span></label></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.userPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="users"></span></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.teamPermissions"></wicket:message></th><td style="padding:2px;"><span wicket:id="teams"></span></td></tr>
                </tbody>
            </table>
            <h4><wicket:message key="gb.owners"></wicket:message></h4>
            <p><wicket:message key="gb.ownersDescription"></wicket:message></p>
            <div wicket:id="owners"></div>
            <hr />
            <h4><wicket:message key="gb.userPermissions"></wicket:message></h4>
            <p><wicket:message key="gb.userPermissionsDescription"></wicket:message></p>
            <div wicket:id="users"></div>
            <hr />
            <h4><wicket:message key="gb.teamPermissions"></wicket:message></h4>
            <p><wicket:message key="gb.teamPermissionsDescription"></wicket:message></p>
            <div wicket:id="teams"></div>
        </div>
        <!-- federation -->
        <div class="tab-pane" id="federation">
        <!-- receive -->
        <div class="tab-pane" id="receive">
            <h4><wicket:message key="gb.receiveSettings"></wicket:message></h4>
            <p><wicket:message key="gb.receiveSettingsDescription"></wicket:message></p>
            <hr/>
            <div wicket:id="isFrozen"></div>
            <div wicket:id="verifyCommitter"></div>
            <div wicket:id="incrementalPushTags"></div>
            <hr />
            <h4><wicket:message key="gb.preReceiveScripts"></wicket:message></h4>
            <p><wicket:message key="gb.preReceiveDescription"></wicket:message></p>
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.federationStrategy"></wicket:message></th><td class="edit"><select class="span4" wicket:id="federationStrategy" tabindex="24" /></td></tr>
                    <tr><th><wicket:message key="gb.federationSets"></wicket:message></th><td style="padding:2px;"><span wicket:id="federationSets"></span></td></tr>
                    <tr>
                        <td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td>
                        <td style="vertical-align: top;"><span wicket:id="inheritedPreReceive"></span></td>
                    </tr>
                </tbody>
            </table>
            <hr />
            <h4><wicket:message key="gb.postReceiveScripts"></wicket:message></h4>
            <p><wicket:message key="gb.postReceiveDescription"></wicket:message></p>
            <table class="plain">
                <tbody class="settings">
                    <tr>
                        <td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td>
                        <td style="vertical-align: top;"><span wicket:id="inheritedPostReceive"></span></td>
                    </tr>
                </tbody>
            </table>
            <div wicket:id="customFieldsSection">
                <hr />
                <h4><wicket:message key="gb.customFields"></wicket:message></h4>
                <p><wicket:message key="gb.customFieldsDescription"></wicket:message></p>
                <table class="plain">
                    <tbody class="settings">
                        <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr>
                    </tbody>
                </table>
            </div>
        </div>
        <!-- tickets tab -->
        <div class="tab-pane" id="tickets">
            <h4><wicket:message key="gb.ticketSettings"></wicket:message></h4>
            <p><wicket:message key="gb.ticketsWelcome"></wicket:message></p>
            <hr/>
            <div wicket:id="acceptNewPatchsets"></div>
            <div wicket:id="acceptNewTickets"></div>
            <div wicket:id="requireApproval"></div>
            <div wicket:id="mergeTo"></div>
        </div>
        <!-- federation -->
        <div class="tab-pane" id="federation">
            <h4><wicket:message key="gb.federation"></wicket:message></h4>
            <p><wicket:message key="gb.federationRepositoryDescription"></wicket:message></p>
            <hr/>
            <div wicket:id="federationStrategy"></div>
            <hr />
            <h4><wicket:message key="gb.federationSets"></wicket:message></h4>
            <p><wicket:message key="gb.federationSetsDescription"></wicket:message></p>
            <div wicket:id="federationSets"></div>
        </div>
        <!-- search -->
        <div class="tab-pane" id="search">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.indexedBranches"></wicket:message></th><td style="padding:2px;"><span wicket:id="indexedBranches"></span></td></tr>
                </tbody>
            </table>
            <h4><wicket:message key="gb.indexedBranches"></wicket:message></h4>
            <p><wicket:message key="gb.indexedBranchesDescription"></wicket:message></p>
            <div wicket:id="indexedBranches"></div>
        </div>
        <!-- hooks -->
        <div class="tab-pane" id="hooks">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.preReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPreReceive"></span></th><td style="padding:2px;"><span wicket:id="preReceiveScripts"></span></td></tr>
                    <tr><th><wicket:message key="gb.postReceiveScripts"></wicket:message><p></p><span wicket:id="inheritedPostReceive"></span></th><td style="padding:2px;"><span wicket:id="postReceiveScripts"></span></td></tr>
                    <div wicket:id="customFieldsSection">
                        <tr><td colspan="2"><h3><wicket:message key="gb.customFields"></wicket:message> &nbsp;<small><wicket:message key="gb.customFieldsDescription"></wicket:message></small></h3></td></tr>
                        <tr wicket:id="customFieldsListView"><th><span wicket:id="customFieldLabel"></span></th><td class="edit"><input class="span8" type="text" wicket:id="customFieldValue" /></td></tr>
                    </div>
                </tbody>
            </table>
        <!-- garbage collection -->
        <div class="tab-pane" id="gc">
            <h4><wicket:message key="gb.garbageCollection"></wicket:message></h4>
            <p><wicket:message key="gb.garbageCollectionDescription"></wicket:message></p>
            <div wicket:id="gcPeriod"></div>
            <div wicket:id="gcThreshold"></div>
        </div>
        <!-- misc -->
        <div class="tab-pane" id="misc">
            <div wicket:id="origin"></div>
            <div wicket:id="head"></div>
            <hr/>
            <div wicket:id="showRemoteBranches"></div>
            <div wicket:id="skipSizeCalculation"></div>
            <div wicket:id="skipSummaryMetrics"></div>
            <div wicket:id="maxActivityCommits"></div>
            <div wicket:id="commitMessageRenderer"></div>
            <div wicket:id="metricAuthorExclusions"></div>
            <div wicket:id="mailingLists"></div>
        </div>
        <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> &nbsp; <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div>
        </div>
    </div>
</div>
</div>
</div>
<div class="row">
<div class="span12">
    <div class="form-actions"><input class="btn btn-appmenu" type="submit" value="Save" wicket:message="value:gb.save" wicket:id="save" /> &nbsp; <input class="btn" type="submit" value="Cancel" wicket:message="value:gb.cancel" wicket:id="cancel" /> &nbsp; <input class="btn btn-danger" type="submit" value="Delete" wicket:message="value:gb.delete" wicket:id="delete" /></div>
</div>
</div>
</form>    
</body>
<wicket:fragment wicket:id="checkboxOption">
    <div style="padding-top:4px;">
        <div>
            <label style="font-weight:bold;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label>
        </div>
        <label class="checkbox" style="color:#777;" wicket:id="description"></label>
    </div>
</wicket:fragment>
<wicket:fragment wicket:id="choiceOption">
    <div style="padding-top:4px;">
        <div>
            <b><span wicket:id="name"></span></b>
        </div>
        <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
        <p style="padding-top:5px;"><select class="span2" wicket:id="choice" /></p>
        </label>
    </div>
</wicket:fragment>
<wicket:fragment wicket:id="textfieldOption">
    <div style="padding-top:4px;">
        <div>
            <b><span wicket:id="name"></span></b>
        </div>
        <label class="checkbox" style="color:#777;"> <span wicket:id="description"></span>
        <p style="padding-top:5px;"><input class="span2" type="text" wicket:id="text" /></p>
        </label>
    </div>
</wicket:fragment>
</wicket:extend>
</html>
src/main/java/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -43,9 +43,11 @@
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.model.util.ListModel;
import org.eclipse.jgit.lib.Repository;
@@ -200,7 +202,7 @@
            }
        }
        final Palette<UserChoice> ownersPalette = new Palette<UserChoice>("owners", new ListModel<UserChoice>(owners), new CollectionModel<UserChoice>(
              persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, true);
              persons), new ChoiceRenderer<UserChoice>(null, "userId"), 12, false);
        // indexed local branches palette
        List<String> allLocalBranches = new ArrayList<String>();
@@ -387,21 +389,7 @@
            }
        };
        // do not let the browser pre-populate these fields
        form.add(new SimpleAttributeModifier("autocomplete", "off"));
        // field names reflective match RepositoryModel fields
        namePanel = new RepositoryNamePanel("namePanel", repositoryModel);
        namePanel.setEditable(allowEditName);
        form.add(namePanel);
        form.add(ownersPalette);
        form.add(new CheckBox("allowForks").setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));
        form.add(new CheckBox("isFrozen"));
        // TODO enable origin definition
        form.add(new TextField<String>("origin").setEnabled(false/* isCreate */));
        // allow relinking HEAD to a branch or tag other than master on edit repository
        // Determine available refs & branches
        List<String> availableRefs = new ArrayList<String>();
        List<String> availableBranches = new ArrayList<String>();
        if (!ArrayUtils.isEmpty(repositoryModel.availableRefs)) {
@@ -414,53 +402,79 @@
                }
            }
        }
        form.add(new DropDownChoice<String>("HEAD", availableRefs).setEnabled(availableRefs.size() > 0));
        boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false);
        int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7);
        if (repositoryModel.gcPeriod == 0) {
            repositoryModel.gcPeriod = defaultGcPeriod;
        }
        List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 );
        form.add(new DropDownChoice<Integer>("gcPeriod", gcPeriods, new GCPeriodRenderer()).setEnabled(gcEnabled));
        form.add(new TextField<String>("gcThreshold").setEnabled(gcEnabled));
        // do not let the browser pre-populate these fields
        form.add(new SimpleAttributeModifier("autocomplete", "off"));
        // federation strategies - remove ORIGIN choice if this repository has
        // no origin.
        List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
                Arrays.asList(FederationStrategy.values()));
        if (StringUtils.isEmpty(repositoryModel.origin)) {
            federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
        }
        form.add(new DropDownChoice<FederationStrategy>("federationStrategy", federationStrategies,
                new FederationTypeRenderer()));
        form.add(new CheckBox("acceptNewPatchsets"));
        form.add(new CheckBox("acceptNewTickets"));
        form.add(new CheckBox("requireApproval"));
        form.add(new DropDownChoice<String>("mergeTo", availableBranches).setEnabled(availableBranches.size() > 0));
        form.add(new CheckBox("useIncrementalPushTags"));
        form.add(new CheckBox("showRemoteBranches"));
        form.add(new CheckBox("skipSizeCalculation"));
        form.add(new CheckBox("skipSummaryMetrics"));
        List<Integer> maxActivityCommits  = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500);
        form.add(new DropDownChoice<Integer>("maxActivityCommits", maxActivityCommits, new MaxActivityCommitsRenderer()));
        metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? ""
                : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " "));
        form.add(new TextField<String>("metricAuthorExclusions", metricAuthorExclusions));
        //
        //
        // GENERAL
        //
        namePanel = new RepositoryNamePanel("namePanel", repositoryModel);
        namePanel.setEditable(allowEditName);
        form.add(namePanel);
        mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
                : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
        form.add(new TextField<String>("mailingLists", mailingLists));
        form.add(indexedBranchesPalette);
        // XXX AccessPolicyPanel is defined later.
        final CheckBox verifyCommitter = new CheckBox("verifyCommitter");
        verifyCommitter.setOutputMarkupId(true);
        form.add(verifyCommitter);
        form.add(newChoice("head",
                getString("gb.headRef"),
                getString("gb.headRefDescription"),
                new PropertyModel<String>(repositoryModel, "HEAD"),
                availableRefs));
        //
        // PERMISSIONS
        //
        form.add(ownersPalette);
        form.add(usersPalette);
        form.add(teamsPalette);
        form.add(federationSetsPalette);
        //
        // TICKETS
        //
        form.add(newCheckbox("acceptNewPatchsets",
                getString("gb.acceptNewPatchsets"),
                getString("gb.acceptNewPatchsetsDescription"),
                new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets")));
        form.add(newCheckbox("acceptNewTickets",
                getString("gb.acceptNewTickets"),
                getString("gb.acceptNewTicketsDescription"),
                new PropertyModel<Boolean>(repositoryModel, "acceptNewPatchsets")));
        form.add(newCheckbox("requireApproval",
                getString("gb.requireApproval"),
                getString("gb.requireApprovalDescription"),
                new PropertyModel<Boolean>(repositoryModel, "requireApproval")));
        form.add(newChoice("mergeTo",
                getString("gb.mergeTo"),
                getString("gb.mergeToDescription"),
                new PropertyModel<String>(repositoryModel, "mergeTo"),
                availableBranches));
        //
        // RECEIVE
        //
        form.add(newCheckbox("isFrozen",
                getString("gb.isFrozen"),
                getString("gb.isFrozenDescription"),
                new PropertyModel<Boolean>(repositoryModel, "isFrozen")));
        form.add(newCheckbox("incrementalPushTags",
                getString("gb.enableIncrementalPushTags"),
                getString("gb.useIncrementalPushTagsDescription"),
                new PropertyModel<Boolean>(repositoryModel, "useIncrementalPushTags")));
        final CheckBox verifyCommitter = new CheckBox("checkbox", new PropertyModel<Boolean>(repositoryModel, "verifyCommitter"));
        verifyCommitter.setOutputMarkupId(true);
        form.add(newCheckbox("verifyCommitter",
                getString("gb.verifyCommitter"),
                getString("gb.verifyCommitterDescription"),
                verifyCommitter));
        form.add(preReceivePalette);
        form.add(new BulletListPanel("inheritedPreReceive", getString("gb.inherited"), app().repositories()
                .getPreReceiveScriptsInherited(repositoryModel)));
@@ -471,6 +485,116 @@
        WebMarkupContainer customFieldsSection = new WebMarkupContainer("customFieldsSection");
        customFieldsSection.add(customFieldsListView);
        form.add(customFieldsSection.setVisible(!app().settings().getString(Keys.groovy.customFields, "").isEmpty()));
        //
        // FEDERATION
        //
        List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
                Arrays.asList(FederationStrategy.values()));
        // federation strategies - remove ORIGIN choice if this repository has no origin.
        if (StringUtils.isEmpty(repositoryModel.origin)) {
            federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
        }
        form.add(newChoice("federationStrategy",
                getString("gb.federationStrategy"),
                getString("gb.federationStrategyDescription"),
                new DropDownChoice<FederationStrategy>(
                        "choice",
                        new PropertyModel<FederationStrategy>(repositoryModel, "federationStrategy"),
                        federationStrategies,
                        new FederationTypeRenderer())));
        form.add(federationSetsPalette);
        //
        // SEARCH
        //
        form.add(indexedBranchesPalette);
        //
        // GARBAGE COLLECTION
        //
        boolean gcEnabled = app().settings().getBoolean(Keys.git.enableGarbageCollection, false);
        int defaultGcPeriod = app().settings().getInteger(Keys.git.defaultGarbageCollectionPeriod, 7);
        if (repositoryModel.gcPeriod == 0) {
            repositoryModel.gcPeriod = defaultGcPeriod;
        }
        List<Integer> gcPeriods = Arrays.asList(1, 2, 3, 4, 5, 7, 10, 14 );
        form.add(newChoice("gcPeriod",
                getString("gb.gcPeriod"),
                getString("gb.gcPeriodDescription"),
                new DropDownChoice<Integer>("choice",
                        new PropertyModel<Integer>(repositoryModel, "gcPeriod"),
                        gcPeriods,
                        new GCPeriodRenderer())).setEnabled(gcEnabled));
        form.add(newTextfield("gcThreshold",
                getString("gb.gcThreshold"),
                getString("gb.gcThresholdDescription"),
                "span1",
                new PropertyModel<String>(repositoryModel, "gcThreshold")).setEnabled(gcEnabled));
        //
        // MISCELLANEOUS
        //
        form.add(newTextfield("origin",
                getString("gb.origin"),
                getString("gb.originDescription"),
                "span6",
                new PropertyModel<String>(repositoryModel, "origin")).setEnabled(false));
        form.add(newCheckbox("showRemoteBranches",
                getString("gb.showRemoteBranches"),
                getString("gb.showRemoteBranchesDescription"),
                new PropertyModel<Boolean>(repositoryModel, "showRemoteBranches")));
        form.add(newCheckbox("skipSizeCalculation",
                getString("gb.skipSizeCalculation"),
                getString("gb.skipSizeCalculationDescription"),
                new PropertyModel<Boolean>(repositoryModel, "skipSizeCalculation")));
        form.add(newCheckbox("skipSummaryMetrics",
                getString("gb.skipSummaryMetrics"),
                getString("gb.skipSummaryMetricsDescription"),
                new PropertyModel<Boolean>(repositoryModel, "skipSummaryMetrics")));
        List<Integer> maxActivityCommits  = Arrays.asList(-1, 0, 25, 50, 75, 100, 150, 200, 250, 500);
        form.add(newChoice("maxActivityCommits",
                getString("gb.maxActivityCommits"),
                getString("gb.maxActivityCommitsDescription"),
                new DropDownChoice<Integer>("choice",
                        new PropertyModel<Integer>(repositoryModel, "maxActivityCommits"),
                        maxActivityCommits,
                        new MaxActivityCommitsRenderer())));
        List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());
        form.add(newChoice("commitMessageRenderer",
                getString("gb.commitMessageRenderer"),
                getString("gb.commitMessageRendererDescription"),
                new DropDownChoice<CommitMessageRenderer>("choice",
                        new PropertyModel<CommitMessageRenderer>(repositoryModel, "commitMessageRenderer"),
                        renderers)));
        metricAuthorExclusions = new Model<String>(ArrayUtils.isEmpty(repositoryModel.metricAuthorExclusions) ? ""
                : StringUtils.flattenStrings(repositoryModel.metricAuthorExclusions, " "));
        form.add(newTextfield("metricAuthorExclusions",
                getString("gb.metricAuthorExclusions"),
                getString("gb.metricAuthorExclusions"),
                "span6",
                metricAuthorExclusions));
        mailingLists = new Model<String>(ArrayUtils.isEmpty(repositoryModel.mailingLists) ? ""
                : StringUtils.flattenStrings(repositoryModel.mailingLists, " "));
        form.add(newTextfield("mailingLists",
                getString("gb.mailingLists"),
                getString("gb.mailingLists"),
                "span6",
                mailingLists));
        // initial enable/disable of permission controls
        if (repositoryModel.accessRestriction.equals(AccessRestrictionType.NONE)) {
@@ -488,6 +612,9 @@
            teamsPalette.setEnabled(allowFineGrainedControls);
        }
        //
        // ACCESS POLICY PANEL (GENERAL)
        //
        AjaxFormChoiceComponentUpdatingBehavior callback = new AjaxFormChoiceComponentUpdatingBehavior() {
            private static final long serialVersionUID = 1L;
@@ -516,10 +643,10 @@
        accessPolicyPanel = new AccessPolicyPanel("accessPolicyPanel", repositoryModel, callback);
        form.add(accessPolicyPanel);
        List<CommitMessageRenderer> renderers = Arrays.asList(CommitMessageRenderer.values());
        DropDownChoice<CommitMessageRenderer> messageRendererChoice = new DropDownChoice<CommitMessageRenderer>("commitMessageRenderer", renderers);
        form.add(messageRendererChoice);
        //
        // FORM CONTROLS
        //
        form.add(new Button("save"));
        Button cancel = new Button("cancel") {
            private static final long serialVersionUID = 1L;
@@ -625,6 +752,51 @@
        }
    }
    private Fragment newCheckbox(String wicketId, String title, String description, IModel<Boolean> model) {
        Fragment fragment = new Fragment(wicketId, "checkboxOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        fragment.add(new CheckBox("checkbox", model));
        return fragment;
    }
    private Fragment newCheckbox(String wicketId, String title, String description, CheckBox checkbox) {
        Fragment fragment = new Fragment(wicketId, "checkboxOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        fragment.add(checkbox);
        return fragment;
    }
    private <T> Fragment newChoice(String wicketId, String title, String description, IModel<T> model, List<T> choices) {
        Fragment fragment = new Fragment(wicketId, "choiceOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        fragment.add(new DropDownChoice<>("choice", model, choices).setEnabled(choices.size() > 0));
        return fragment;
    }
    private <T> Fragment newChoice(String wicketId, String title, String description, DropDownChoice<?> choice) {
        Fragment fragment = new Fragment(wicketId, "choiceOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        fragment.add(choice.setEnabled(choice.getChoices().size() > 0));
        return fragment;
    }
    private Fragment newTextfield(String wicketId, String title, String description, String css, IModel<String> model) {
        Fragment fragment = new Fragment(wicketId, "textfieldOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        TextField<String> tf = new TextField<String>("text", model);
        if (!StringUtils.isEmpty(css)) {
            WicketUtils.setCssClass(tf, css);
        }
        fragment.add(tf);
        return fragment;
    }
    private class FederationTypeRenderer implements IChoiceRenderer<FederationStrategy> {
        private static final long serialVersionUID = 1L;
src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.html
@@ -10,19 +10,32 @@
    <h4><wicket:message key="gb.accessPolicy"></wicket:message></h4>
    <p><wicket:message key="gb.accessPolicyDescription"></wicket:message></p>
        
    <span wicket:id="policiesGroup">
            <div wicket:id="policies">
                <div style="display: inline-block;vertical-align: top;">
                    <input type="radio" wicket:id="radio" />
                    <img wicket:id="image"></img>
                </div>
                <div style="display: inline-block;vertical-align: top;">
                    <b><span wicket:id="name"></span></b><br/>
                    <span wicket:id="description"></span>
                </div>
    <div wicket:id="policiesGroup">
        <div wicket:id="policies" style="padding-top:4px;">
            <div style="display: inline-block;vertical-align: top;">
                <input type="radio" wicket:id="radio" />
                <img wicket:id="image"></img>
            </div>
        </span>
            <div style="display: inline-block;vertical-align: top;">
                <b><span wicket:id="name"></span></b><br/>
                <span class="help-inline" wicket:id="description"></span>
            </div>
        </div>
    </div>
    
    <hr />
    <div wicket:id="allowForks"></div>
<wicket:fragment wicket:id="checkboxOption">
    <div style="padding-top:4px;">
        <div>
            <label style="font-weight:bold;" class="checkbox"><input type="checkbox" wicket:id="checkbox" /> <span wicket:id="name"></span></label>
        </div>
        <label class="checkbox" style="color:#777;" wicket:id="description"></label>
    </div>
</wicket:fragment>
</wicket:panel>
</body>
</html>
src/main/java/com/gitblit/wicket/panels/AccessPolicyPanel.java
@@ -21,10 +21,13 @@
import org.apache.wicket.ajax.form.AjaxFormChoiceComponentUpdatingBehavior;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import com.gitblit.Constants.AccessRestrictionType;
@@ -48,6 +51,8 @@
    private final AjaxFormChoiceComponentUpdatingBehavior callback;
    private RadioGroup<AccessPolicy> policiesGroup;
    private IModel<Boolean> allowForks;
    public AccessPolicyPanel(String wicketId, RepositoryModel repository) {
        this(wicketId, repository, null);
@@ -142,13 +147,28 @@
        }
        add(policiesGroup);
        allowForks = Model.of(true);
        add(newCheckbox("allowForks",
                getString("gb.allowForks"),
                getString("gb.allowForksDescription"),
                allowForks).setEnabled(app().settings().getBoolean(Keys.web.allowForking, true)));
        setOutputMarkupId(true);
    }
    private Fragment newCheckbox(String wicketId, String title, String description, IModel<Boolean> model) {
        Fragment fragment = new Fragment(wicketId, "checkboxOption", this);
        fragment.add(new Label("name", title));
        fragment.add(new Label("description", description));
        fragment.add(new CheckBox("checkbox", model));
        return fragment;
    }
    public void updateModel(RepositoryModel repository) {
        AccessPolicy policy = policiesGroup.getModelObject();
        repository.authorizationControl = policy.control;
        repository.accessRestriction = policy.type;
        repository.allowForks = allowForks.getObject();
    }
    @Override
src/main/java/com/gitblit/wicket/panels/RepositoryNamePanel.html
@@ -15,7 +15,7 @@
            </tr>
            <tr>
                <td><select class="span2" wicket:id="projectPath" /></td>
                <td class="edit"><input class="span3" type="text" wicket:id="name" id="name" /> &nbsp;<span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td>
                <td class="edit"><input class="span4" type="text" wicket:id="name" id="name" /> &nbsp;<span class="help-inline"><wicket:message key="gb.nameDescription"></wicket:message></span></td>
            </tr>
        </tbody>
    </table>