James Moger
2012-11-02 ba6150d1712d5f5986e72333831940a46316aab3
Permission filtering in web ui

Present the mutable permissions by default. Allow the administrator or
owner to toggle the displayed permissions to see how the user
and team permissions are applied to a repository.
12 files modified
383 ■■■■ changed files
.gitignore 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java 61 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 56 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/RegistrantPermissionsPanel.java 25 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/UsersPanel.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/client/Utils.java 25 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/RegistrantAccessPermission.java 51 ●●●●● patch | view | raw | blame | history
src/com/gitblit/models/UserModel.java 25 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 5 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditUserPage.java 22 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html 20 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java 90 ●●●●● patch | view | raw | blame | history
.gitignore
@@ -28,3 +28,4 @@
/.gradle
/projects.conf
/pom.xml
/deploy
src/com/gitblit/GitBlit.java
@@ -79,6 +79,8 @@
import com.gitblit.Constants.FederationRequest;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.Constants.FederationToken;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.models.FederationModel;
import com.gitblit.models.FederationProposal;
import com.gitblit.models.FederationSet;
@@ -658,18 +660,48 @@
     * @return a user object or null
     */
    public UserModel getUserModel(String username) {
        UserModel user = userService.getUserModel(username);
        if (user != null) {
            // TODO reconsider ownership as a user property
            // manually specify personal repository ownerships
            String folder = "~" + username;
            for (String repository : getRepositoryList()) {
                if (repository.toLowerCase().startsWith(folder)) {
                    user.setRepositoryPermission(repository, AccessPermission.REWIND);
        UserModel user = userService.getUserModel(username);
        return user;
    }
    /**
     * Returns the effective list of permissions for this user, taking into account
     * team memberships, ownerships.
     *
     * @param user
     * @return the effective list of permissions for the user
     */
    public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
        Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>();
        set.addAll(user.getRepositoryPermissions());
        // Flag missing repositories
        for (RegistrantAccessPermission permission : set) {
            if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
                RepositoryModel rm = GitBlit.self().getRepositoryModel(permission.registrant);
                if (rm == null) {
                    permission.permissionType = PermissionType.MISSING;
                    permission.mutable = false;
                    continue;
                }
            }
        }
        return user;
        // TODO reconsider ownership as a user property
        // manually specify personal repository ownerships
        for (RepositoryModel rm : repositoryListCache.values()) {
            if (rm.isUsersPersonalRepository(user.username) || rm.isOwner(user.username)) {
                RegistrantAccessPermission rp = new RegistrantAccessPermission(rm.name, AccessPermission.REWIND,
                        PermissionType.OWNER, RegistrantType.REPOSITORY, null, false);
                // user may be owner of a repository to which they've inherited
                // a team permission, replace any existing perm with owner perm
                set.remove(rp);
                set.add(rp);
            }
        }
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(set);
        Collections.sort(list);
        return list;
    }
    /**
@@ -681,7 +713,16 @@
     * @return a list of RegistrantAccessPermissions
     */
    public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
        if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
            // no permissions needed, REWIND for everyone!
            return list;
        }
        if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl)) {
            // no permissions needed, REWIND for authenticated!
            return list;
        }
        // NAMED users and teams
        for (UserModel user : userService.getAllUsers()) {
            RegistrantAccessPermission ap = user.getRepositoryPermission(repository);
            if (ap.permission.exceeds(AccessPermission.NONE)) {
src/com/gitblit/client/GitblitClient.java
@@ -31,6 +31,8 @@
import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.GitBlitException.ForbiddenException;
import com.gitblit.GitBlitException.NotAllowedException;
import com.gitblit.GitBlitException.UnauthorizedException;
@@ -505,15 +507,63 @@
        return usernames;
    }
    
    /**
     * Returns the effective list of permissions for this user, taking into account
     * team memberships, ownerships.
     *
     * @param user
     * @return the effective list of permissions for the user
     */
    public List<RegistrantAccessPermission> getUserAccessPermissions(UserModel user) {
        Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>();
        set.addAll(user.getRepositoryPermissions());
        // Flag missing repositories
        for (RegistrantAccessPermission permission : set) {
            if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
                RepositoryModel rm = getRepository(permission.registrant);
                if (rm == null) {
                    permission.permissionType = PermissionType.MISSING;
                    permission.mutable = false;
                    continue;
                }
            }
        }
        // TODO reconsider ownership as a user property
        // manually specify personal repository ownerships
        for (RepositoryModel rm : allRepositories) {
            if (rm.isUsersPersonalRepository(user.username) || rm.isOwner(user.username)) {
                RegistrantAccessPermission rp = new RegistrantAccessPermission(rm.name, AccessPermission.REWIND,
                        PermissionType.OWNER, RegistrantType.REPOSITORY, null, false);
                // user may be owner of a repository to which they've inherited
                // a team permission, replace any existing perm with owner perm
                set.remove(rp);
                set.add(rp);
            }
        }
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>(set);
        Collections.sort(list);
        return list;
    }
    public List<RegistrantAccessPermission> getUserAccessPermissions(RepositoryModel repository) {
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
        for (UserModel user : getUsers()) {
        List<RegistrantAccessPermission> list = new ArrayList<RegistrantAccessPermission>();
        if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
            // no permissions needed, REWIND for everyone!
            return list;
        }
        if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl)) {
            // no permissions needed, REWIND for authenticated!
            return list;
        }
        // NAMED users and teams
        for (UserModel user : allUsers) {
            RegistrantAccessPermission ap = user.getRepositoryPermission(repository);
            if (ap.permission.exceeds(AccessPermission.NONE)) {
                list.add(ap);
            }
        }
        Collections.sort(list);
        return list;
    }
src/com/gitblit/client/RegistrantPermissionsPanel.java
@@ -16,11 +16,14 @@
package com.gitblit.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.DefaultCellEditor;
@@ -36,6 +39,7 @@
import com.gitblit.Constants.AccessPermission;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.client.Utils.RowRenderer;
import com.gitblit.models.RegistrantAccessPermission;
import com.gitblit.utils.StringUtils;
@@ -60,7 +64,23 @@
    public RegistrantPermissionsPanel(final RegistrantType registrantType) {
        super(new BorderLayout(5, 5));
        tableModel = new RegistrantPermissionsTableModel();
        permissionsTable = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        permissionsTable = Utils.newTable(tableModel, Utils.DATE_FORMAT, new RowRenderer() {
            Color clear = new Color(0, 0, 0, 0);
            Color iceGray = new Color(0xf0, 0xf0, 0xf0);
            @Override
            public void prepareRow(Component c, boolean isSelected, int row, int column) {
                if (isSelected) {
                    c.setBackground(permissionsTable.getSelectionBackground());
                } else {
                    if (tableModel.permissions.get(row).mutable) {
                        c.setBackground(clear);
                    } else {
                        c.setBackground(iceGray);
                    }
                }
            }
        });
        permissionsTable.setModel(tableModel);
        permissionsTable.setPreferredScrollableViewportSize(new Dimension(400, 150));
        JScrollPane jsp = new JScrollPane(permissionsTable);
@@ -91,11 +111,14 @@
                rp.permission = (AccessPermission) permissionSelector.getSelectedItem();
                if (StringUtils.findInvalidCharacter(rp.registrant) != null) {
                    rp.permissionType = PermissionType.REGEX;
                    rp.source = rp.registrant;
                } else {
                    rp.permissionType = PermissionType.EXPLICIT;
                }
                tableModel.permissions.add(rp);
                // resort permissions after insert to convey idea of eval order
                Collections.sort(tableModel.permissions);
                
                registrantModel.removeElement(rp.registrant);
                registrantSelector.setSelectedIndex(-1);
src/com/gitblit/client/UsersPanel.java
@@ -308,7 +308,7 @@
                gitblit.getSettings());
        dialog.setLocationRelativeTo(UsersPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), user.getRepositoryPermissions());
        dialog.setRepositories(gitblit.getRepositories(), gitblit.getUserAccessPermissions(user));
        dialog.setTeams(gitblit.getTeams(), user.teams == null ? null : new ArrayList<TeamModel>(
                user.teams));
        dialog.setVisible(true);
src/com/gitblit/client/Utils.java
@@ -49,7 +49,25 @@
    public final static String DATE_FORMAT = "yyyy-MM-dd";
    public static JTable newTable(TableModel model, String datePattern) {
        JTable table = new JTable(model);
        return newTable(model, datePattern, null);
    }
    public static JTable newTable(TableModel model, String datePattern, final RowRenderer rowRenderer) {
        JTable table;
        if (rowRenderer == null) {
            table = new JTable(model);
        } else {
            table = new JTable(model) {
                @Override
                public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                    Component c = super.prepareRenderer(renderer, row, column);
                    boolean isSelected = isCellSelected(row, column);
                    rowRenderer.prepareRow(c, isSelected, row, column);
                    return c;
                }
            };
        }
        table.setRowHeight(table.getFont().getSize() + 8);
        table.setCellSelectionEnabled(false);
        table.setRowSelectionAllowed(true);
@@ -148,5 +166,8 @@
            showException(null, x);
        }
    }
    public static abstract class RowRenderer {
        public abstract void prepareRow(Component c, boolean isSelected, int row, int column);
    }
}
src/com/gitblit/models/RegistrantAccessPermission.java
@@ -63,18 +63,67 @@
    public boolean isOwner() {
        return PermissionType.OWNER.equals(permissionType);
    }
    public boolean isExplicit() {
        return PermissionType.EXPLICIT.equals(permissionType);
    }
    public boolean isRegex() {
        return PermissionType.REGEX.equals(permissionType);
    }
    public boolean isTeam() {
        return PermissionType.TEAM.equals(permissionType);
    }
    public boolean isMissing() {
        return PermissionType.MISSING.equals(permissionType);
    }
    public int getScore() {
        switch (registrantType) {
        case REPOSITORY:
            if (isAdmin()) {
                return 0;
            }
            if (isOwner()) {
                return 1;
            }
            if (isExplicit()) {
                return 2;
            }
            if (isRegex()) {
                return 3;
            }
            if (isTeam()) {
                return 4;
            }
        default:
            return 0;
        }
    }
    
    @Override
    public int compareTo(RegistrantAccessPermission p) {
        switch (registrantType) {
        case REPOSITORY:
            // repository permissions are sorted in score order
            // to convey the order in which permissions are tested
            int score1 = getScore();
            int score2 = p.getScore();
            if (score1 <= 2 && score2 <= 2) {
                // group admin, owner, and explicit together
                return StringUtils.compareRepositoryNames(registrant, p.registrant);
            }
            if (score1 < score2) {
                return -1;
            } else if (score2 < score1) {
                return 1;
            }
            return StringUtils.compareRepositoryNames(registrant, p.registrant);
        default:
            return registrant.toLowerCase().compareTo(p.registrant.toLowerCase());
            // user and team permissions are string sorted
            return registrant.toLowerCase().compareTo(p.registrant.toLowerCase());
        }
    }
    
src/com/gitblit/models/UserModel.java
@@ -21,6 +21,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -160,7 +161,20 @@
            list.add(new RegistrantAccessPermission(registrant, ap, pType, RegistrantType.REPOSITORY, source, mutable));
        }
        Collections.sort(list);
        return list;
        // include immutable team permissions, being careful to preserve order
        Set<RegistrantAccessPermission> set = new LinkedHashSet<RegistrantAccessPermission>(list);
        for (TeamModel team : teams) {
            for (RegistrantAccessPermission teamPermission : team.getRepositoryPermissions()) {
                // we can not change an inherited team permission, though we can override
                teamPermission.registrantType = RegistrantType.REPOSITORY;
                teamPermission.permissionType = PermissionType.TEAM;
                teamPermission.source = team.name;
                teamPermission.mutable = false;
                set.add(teamPermission);
            }
        }
        return new ArrayList<RegistrantAccessPermission>(set);
    }
    
    /**
@@ -253,6 +267,13 @@
        ap.permission = AccessPermission.NONE;
        ap.mutable = false;
        if (AccessRestrictionType.NONE.equals(repository.accessRestriction)) {
            // anonymous rewind
            ap.permissionType = PermissionType.ADMINISTRATOR;
            ap.permission = AccessPermission.REWIND;
            return ap;
        }
        // administrator
        if (canAdmin()) {
            ap.permissionType = PermissionType.ADMINISTRATOR;
@@ -277,7 +298,7 @@
        }
        
        if (AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl) && isAuthenticated) {
            // AUTHENTICATED is a shortcut for authorizing all logged-in users RW access
            // AUTHENTICATED is a shortcut for authorizing all logged-in users RW+ access
            ap.permission = AccessPermission.REWIND;
            return ap;
        }
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -370,4 +370,7 @@
gb.team = team
gb.teamPermission = permission set by \"{0}\" team membership
gb.missing = missing!
gb.missingPermission = the repository for this permission is missing!
gb.missingPermission = the repository for this permission is missing!
gb.mutable = mutable
gb.specified = specified
gb.effective = effective
src/com/gitblit/wicket/pages/EditUserPage.java
@@ -34,13 +34,11 @@
import org.apache.wicket.model.util.CollectionModel;
import org.apache.wicket.model.util.ListModel;
import com.gitblit.Constants.PermissionType;
import com.gitblit.Constants.RegistrantType;
import com.gitblit.GitBlit;
import com.gitblit.GitBlitException;
import com.gitblit.Keys;
import com.gitblit.models.RegistrantAccessPermission;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.TeamModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
@@ -104,25 +102,7 @@
        Collections.sort(userTeams);
        
        final String oldName = userModel.username;
        final List<RegistrantAccessPermission> permissions = userModel.getRepositoryPermissions();
        for (RegistrantAccessPermission permission : permissions) {
            if (permission.mutable && PermissionType.EXPLICIT.equals(permission.permissionType)) {
                // Ensure this is NOT an owner permission - which is non-editable
                // We don't know this from within the usermodel, ownership is a
                // property of a repository.
                RepositoryModel rm = GitBlit.self().getRepositoryModel(permission.registrant);
                if (rm == null) {
                    permission.permissionType = PermissionType.MISSING;
                    permission.mutable = false;
                    continue;
                }
                boolean isOwner = rm.isOwner(oldName);
                if (isOwner) {
                    permission.permissionType = PermissionType.OWNER;
                    permission.mutable = false;
                }
            }
        }
        final List<RegistrantAccessPermission> permissions = GitBlit.self().getUserAccessPermissions(userModel);
        final Palette<String> teams = new Palette<String>("teams", new ListModel<String>(
                new ArrayList<String>(userTeams)), new CollectionModel<String>(GitBlit.self()
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.html
@@ -7,20 +7,32 @@
<body>
<wicket:panel>
    <div wicket:id="permissionRow">
    <form class="form-inline" wicket:id="permissionToggleForm">
        <div style="padding-bottom:10px" class="btn-group pull-right" data-toggle="buttons-radio">
            <a class="btn btn-info" wicket:id="showMutable"><wicket:message key="gb.mutable"></wicket:message></a>
            <a class="btn btn-info" wicket:id="showSpecified"><wicket:message key="gb.specified"></wicket:message></a>
            <a class="btn btn-info" wicket:id="showEffective"><wicket:message key="gb.effective"></wicket:message></a>
        </div>
    </form>
    <div style="clear:both;" wicket:id="permissionRow">
        <div style="padding-top:10px;border-left:1px solid #ccc;border-right:1px solid #ccc;" class="row-fluid">
            <div style="padding-top:5px;padding-left:5px" class="span6"><span wicket:id="registrant"></span></div><div style="padding-top:5px;padding-right:5px;text-align:right;" class="span3"><span class="label" wicket:id="pType">[permission type]</span></div> <select class="input-medium" wicket:id="permission"></select>
        </div>
    </div>
    <div style="padding-top:15px;" class="row-fluid">
    <div style="clear:both; padding-top:15px;" class="row-fluid">
        <form style="padding: 20px 40px;" class="well form-inline" wicket:id="addPermissionForm">
            <select class="input-xlarge" wicket:id="registrant"></select> <select class="input-large" wicket:id="permission"></select> <input class="btn btn-success" type="submit" value="Add" wicket:message="value:gb.add" wicket:id="addPermissionButton"/>
        </form>
    </div>
    <wicket:fragment wicket:id="repositoryRegistrant">
        <b><span class="repositorySwatch" wicket:id="repositorySwatch"></span></b> <span wicket:id="repositoryName"></span>
    </wicket:fragment>
    <wicket:fragment wicket:id="userRegistrant">
        <span wicket:id="userAvatar"></span> <span style="font-weight: bold;" wicket:id="userName"></span>
        <span wicket:id="userAvatar"></span> <span wicket:id="userName"></span>
    </wicket:fragment>
    <wicket:fragment wicket:id="teamRegistrant">
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java
@@ -18,10 +18,12 @@
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
@@ -57,12 +59,42 @@
public class RegistrantPermissionsPanel extends BasePanel {
    private static final long serialVersionUID = 1L;
    public enum Show {
        specified, mutable, effective;
        public boolean show(RegistrantAccessPermission ap) {
            switch (this) {
            case specified:
                return ap.mutable || ap.isOwner();
            case mutable:
                return ap.mutable;
            case effective:
                return true;
            default:
                return true;
            }
        }
    }
    private Show activeState = Show.mutable;
    public RegistrantPermissionsPanel(String wicketId, RegistrantType registrantType, List<String> allRegistrants, final List<RegistrantAccessPermission> permissions, final Map<AccessPermission, String> translations) {
        super(wicketId);
        setOutputMarkupId(true);
        // update existing permissions repeater
        /*
         * Permission view toggle buttons
         */
        Form<Void> permissionToggleForm = new Form<Void>("permissionToggleForm");
        permissionToggleForm.add(new ShowStateButton("showSpecified", Show.specified));
        permissionToggleForm.add(new ShowStateButton("showMutable", Show.mutable));
        permissionToggleForm.add(new ShowStateButton("showEffective", Show.effective));
        add(permissionToggleForm);
        /*
         * Permission repeating display
         */
        RefreshingView<RegistrantAccessPermission> dataView = new RefreshingView<RegistrantAccessPermission>("permissionRow") {
            private static final long serialVersionUID = 1L;
        
@@ -91,16 +123,19 @@
                    String repoName = StringUtils.stripDotGit(entry.registrant);
                    if (!entry.isMissing() && StringUtils.findInvalidCharacter(repoName) == null) {
                        // repository, strip .git and show swatch
                        Label registrant = new Label("registrant", repoName);
                        WicketUtils.setCssClass(registrant, "repositorySwatch");
                        WicketUtils.setCssBackground(registrant, repoName);
                        item.add(registrant);
                        Fragment repositoryFragment = new Fragment("registrant", "repositoryRegistrant", RegistrantPermissionsPanel.this);
                        Component swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);
                        WicketUtils.setCssBackground(swatch, entry.toString());
                        repositoryFragment.add(swatch);
                        Label registrant = new Label("repositoryName", repoName);
                        repositoryFragment.add(registrant);
                        item.add(repositoryFragment);
                    } else {
                        // regex or missing
                        Label label = new Label("registrant", entry.registrant);
                        WicketUtils.setCssStyle(label, "font-weight: bold;");
                        item.add(label);
                    }
                    }
                } else if (RegistrantType.USER.equals(entry.registrantType)) {
                    // user
                    PersonIdent ident = new PersonIdent(entry.registrant, null);
@@ -160,6 +195,8 @@
                    break;
                }
                item.setVisible(activeState.show(entry));
                // use ajax to get immediate update of permission level change
                // otherwise we can lose it if they change levels and then add
                // a new repository permission
@@ -203,7 +240,9 @@
            }
        }
        // add new permission form
        /*
         * Add permission form
         */
        IModel<RegistrantAccessPermission> addPermissionModel = new CompoundPropertyModel<RegistrantAccessPermission>(new RegistrantAccessPermission(registrantType));
        Form<RegistrantAccessPermission> addPermissionForm = new Form<RegistrantAccessPermission>("addPermissionForm", addPermissionModel);
        addPermissionForm.add(new DropDownChoice<String>("registrant", registrants));
@@ -223,8 +262,12 @@
                RegistrantAccessPermission copy = DeepCopier.copy(rp);
                if (StringUtils.findInvalidCharacter(copy.registrant) != null) {
                    copy.permissionType = PermissionType.REGEX;
                    copy.source = copy.registrant;
                }
                permissions.add(copy);
                // resort permissions after insert to convey idea of eval order
                Collections.sort(permissions);
                
                // remove registrant from available choices
                registrants.remove(rp.registrant);
@@ -265,4 +308,33 @@
            return Integer.toString(index);
        }
    }
    private class ShowStateButton extends AjaxButton {
        private static final long serialVersionUID = 1L;
        Show buttonState;
        public ShowStateButton(String wicketId, Show state) {
            super(wicketId);
            this.buttonState = state;
            setOutputMarkupId(true);
        }
        @Override
        protected void onBeforeRender()
        {
            String cssClass = "btn";
            if (buttonState.equals(RegistrantPermissionsPanel.this.activeState)) {
                cssClass = "btn btn-info active";
            }
            WicketUtils.setCssClass(this, cssClass);
            super.onBeforeRender();
        }
        @Override
        protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
            RegistrantPermissionsPanel.this.activeState = buttonState;
            target.addComponent(RegistrantPermissionsPanel.this);
        }
    };
}