SHaselbauer
2013-01-21 fb9813874c811ae06604c30d875e9dce57df9874
Merge multiple owners feature (pull request #63, #66)
2 files added
32 files modified
616 ■■■■ changed files
src/com/gitblit/GitBlit.java 36 ●●●●● patch | view | raw | blame | history
src/com/gitblit/GitFilter.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/client/EditRepositoryDialog.java 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/EditUserDialog.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/RegistrantPermissionsPanel.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/RepositoriesPanel.java 5 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/RepositoriesTableModel.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/RepositoryModel.java 69 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/UserModel.java 9 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/MultiConfigUtil.java 89 ●●●●● patch | view | raw | blame | history
src/com/gitblit/utils/StringComparator.java 24 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp.properties 8 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_es.properties 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_ja.properties 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_ko.properties 8 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_nl.properties 8 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_pl.properties 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties 8 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.html 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/EditRepositoryPage.java 26 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RepositoryPage.java 10 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/RootSubPage.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.html 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/pages/SummaryPage.java 17 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html 2 ●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java 144 ●●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java 4 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RepositoriesPanel.html 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/panels/RepositoriesPanel.java 78 ●●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/FederationTests.java 2 ●●● patch | view | raw | blame | history
tests/com/gitblit/tests/GitServletTest.java 7 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/PermissionsTest.java 6 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/RpcTests.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/GitBlit.java
@@ -109,6 +109,7 @@
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.JsonUtils;
import com.gitblit.utils.MetricUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.ObjectCache;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.TimeUtils;
@@ -180,6 +181,8 @@
    private TimeZone timezone;
    
    private FileBasedConfig projectConfigs;
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public GitBlit() {
        if (gitblit == null) {
@@ -822,7 +825,7 @@
        // 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)) {
            if (rm.isUsersPersonalRepository(user.username) || rm.isRepoAdministrator(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
@@ -936,14 +939,14 @@
            for (RepositoryModel model : getRepositoryModels(user)) {
                if (model.isUsersPersonalRepository(username)) {
                    // personal repository
                    model.owner = user.username;
                    model.addRepoAdministrator(user.username);
                    String oldRepositoryName = model.name;
                    model.name = "~" + user.username + model.name.substring(model.projectPath.length());
                    model.projectPath = "~" + user.username;
                    updateRepositoryModel(oldRepositoryName, model, false);
                } else if (model.isOwner(username)) {
                } else if (model.isRepoAdministrator(username)) {
                    // common/shared repo
                    model.owner = user.username;
                    model.addRepoAdministrator(user.username);
                    updateRepositoryModel(model.name, model, false);
                }
            }
@@ -1662,7 +1665,7 @@
        
        if (config != null) {
            model.description = getConfig(config, "description", "");
            model.owner = getConfig(config, "owner", "");
            model.addRepoAdministrators(multiConfigUtil.convertStringToSet(getConfig(config, "owner", "")));
            model.useTickets = getConfig(config, "useTickets", false);
            model.useDocs = getConfig(config, "useDocs", false);
            model.allowForks = getConfig(config, "allowForks", true);
@@ -2169,7 +2172,7 @@
    public void updateConfiguration(Repository r, RepositoryModel repository) {
        StoredConfig config = r.getConfig();
        config.setString(Constants.CONFIG_GITBLIT, null, "description", repository.description);
        config.setString(Constants.CONFIG_GITBLIT, null, "owner", repository.owner);
        config.setString(Constants.CONFIG_GITBLIT, null, "owner", multiConfigUtil.convertCollectionToSingleLineString(repository.getRepoAdministrators()));
        config.setBoolean(Constants.CONFIG_GITBLIT, null, "useTickets", repository.useTickets);
        config.setBoolean(Constants.CONFIG_GITBLIT, null, "useDocs", repository.useDocs);
        config.setBoolean(Constants.CONFIG_GITBLIT, null, "allowForks", repository.allowForks);
@@ -3079,9 +3082,15 @@
        }
        
        // schedule lucene engine
        boolean branchIndexingActivated = settings.getBoolean(
                Keys.git.branchIndexingActivated, true);
        logger.info("Branch indexing is "
                + (branchIndexingActivated ? "" : "not") + " activated");
        if (branchIndexingActivated) {
        logger.info("Lucene executor is scheduled to process indexed branches every 2 minutes.");
        scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2, TimeUnit.MINUTES);
            scheduledExecutor.scheduleAtFixedRate(luceneExecutor, 1, 2,
                    TimeUnit.MINUTES);
        }
        // schedule gc engine
        if (gcExecutor.isReady()) {
            logger.info("GC executor is scheduled to scan repositories every 24 hours.");
@@ -3250,17 +3259,22 @@
        // create a Gitblit repository model for the clone
        RepositoryModel cloneModel = repository.cloneAs(cloneName);
        // owner has REWIND/RW+ permissions
        cloneModel.owner = user.username;
        cloneModel.addRepoAdministrator(user.username);
        updateRepositoryModel(cloneName, cloneModel, false);
        // add the owner of the source repository to the clone's access list
        if (!StringUtils.isEmpty(repository.owner)) {
            UserModel originOwner = getUserModel(repository.owner);
        Set<String> repoAdministrators = repository.getRepoAdministrators();
        if (repoAdministrators != null) {
            for (String repoAdministrator : repoAdministrators) {
                if (!StringUtils.isEmpty(repoAdministrator)) {
                    UserModel originOwner = getUserModel(repoAdministrator);
            if (originOwner != null) {
                originOwner.setRepositoryPermission(cloneName, AccessPermission.CLONE);
                updateUserModel(originOwner.username, originOwner, false);
            }
        }
            }
        }
        // grant origin's user list clone permission to fork
        List<String> users = getRepositoryUsers(repository);
src/com/gitblit/GitFilter.java
@@ -222,7 +222,7 @@
                // create repository
                RepositoryModel model = new RepositoryModel();
                model.name = repository;
                model.owner = user.username;
                model.addRepoAdministrator(user.username);
                model.projectPath = StringUtils.getFirstPathElement(repository);
                if (model.isUsersPersonalRepository(user.username)) {
                    // personal repository, default to private for user
src/com/gitblit/client/EditRepositoryDialog.java
@@ -335,7 +335,7 @@
        usersPalette = new RegistrantPermissionsPanel(RegistrantType.USER);
        JPanel northFieldsPanel = new JPanel(new GridLayout(0, 1, 0, 5));
        northFieldsPanel.add(newFieldPanel(Translation.get("gb.owner"), ownerField));
        northFieldsPanel.add(newFieldPanel(Translation.get("gb.repoAdministrators"), ownerField));
        northFieldsPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"),
                accessRestriction), BorderLayout.NORTH);
@@ -556,8 +556,8 @@
        repository.name = rname;
        repository.description = descriptionField.getText();
        repository.owner = ownerField.getSelectedItem() == null ? null
                : ownerField.getSelectedItem().toString();
        repository.addRepoAdministrator(ownerField.getSelectedItem() == null ? null
                : ownerField.getSelectedItem().toString());
        repository.HEAD = headRefField.getSelectedItem() == null ? null
                : headRefField.getSelectedItem().toString();
        repository.gcPeriod = (Integer) gcPeriod.getSelectedItem();
src/com/gitblit/client/EditUserDialog.java
@@ -389,7 +389,7 @@
        List<String> restricted = new ArrayList<String>();
        for (RepositoryModel repo : repositories) {
            // exclude Owner or personal repositories
            if (!repo.isOwner(username) && !repo.isUsersPersonalRepository(username)) {
            if (!repo.isRepoAdministrator(username) && !repo.isUsersPersonalRepository(username)) {
                if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)
                        && repo.authorizationControl.equals(AuthorizationControl.NAMED)) {
                    restricted.add(repo.name);
@@ -438,7 +438,7 @@
                    permission.mutable = false;
                    continue;
                }
                boolean isOwner = rm.isOwner(username);
                boolean isOwner = rm.isRepoAdministrator(username);
                if (isOwner) {
                    permission.permissionType = PermissionType.OWNER;
                    permission.mutable = false;
src/com/gitblit/client/GitblitClient.java
@@ -162,7 +162,7 @@
    }
    public boolean isOwner(RepositoryModel model) {
        return account != null && account.equalsIgnoreCase(model.owner);
        return model.isRepoAdministrator(account);
    }
    public String getURL(String action, String repository, String objectId) {
@@ -532,7 +532,7 @@
        // 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)) {
            if (rm.isUsersPersonalRepository(user.username) || rm.isRepoAdministrator(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
src/com/gitblit/client/RegistrantPermissionsPanel.java
@@ -209,8 +209,8 @@
                setToolTipText(Translation.get("gb.administratorPermission"));
                break;
            case OWNER:
                setText(Translation.get("gb.owner"));
                setToolTipText(Translation.get("gb.ownerPermission"));
                setText(Translation.get("gb.repoAdministrators"));
                setToolTipText(Translation.get("gb.repoAdministratorPermission"));
                break;
            case TEAM:
                setText(ap.source == null ? Translation.get("gb.team") : ap.source);
src/com/gitblit/client/RepositoriesPanel.java
@@ -52,6 +52,7 @@
import com.gitblit.models.RegistrantAccessPermission;
import com.gitblit.models.FeedModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringUtils;
/**
@@ -84,6 +85,8 @@
    private JTextField filterTextfield;
    private JButton clearCache;
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public RepositoriesPanel(GitblitClient gitblit) {
        super();
@@ -453,7 +456,7 @@
        dialog.setLocationRelativeTo(RepositoriesPanel.this);
        List<String> usernames = gitblit.getUsernames();
        List<RegistrantAccessPermission> members = gitblit.getUserAccessPermissions(repository);
        dialog.setUsers(repository.owner, usernames, members);
        dialog.setUsers(multiConfigUtil.convertCollectionToSingleLineString(repository.getRepoAdministrators()), usernames, members);
        dialog.setTeams(gitblit.getTeamnames(), gitblit.getTeamAccessPermissions(repository));
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), repository.federationSets);
src/com/gitblit/client/RepositoriesTableModel.java
@@ -73,7 +73,7 @@
        case Description:
            return Translation.get("gb.description");
        case Owner:
            return Translation.get("gb.owner");
            return Translation.get("gb.repoAdministrators");
        case Last_Change:
            return Translation.get("gb.lastChange");
        case Size:
@@ -111,7 +111,7 @@
        case Description:
            return model.description;
        case Owner:
            return model.owner;
            return model.getRepoAdministrators();
        case Indicators:
            return model;
        case Last_Change:
src/com/gitblit/models/RepositoryModel.java
@@ -23,10 +23,14 @@
import java.util.Set;
import java.util.TreeSet;
import org.apache.wicket.markup.html.basic.MultiLineLabel;
import com.gitblit.Constants.AccessRestrictionType;
import com.gitblit.Constants.AuthorizationControl;
import com.gitblit.Constants.FederationStrategy;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringComparator;
import com.gitblit.utils.StringUtils;
/**
@@ -36,14 +40,15 @@
 * @author James Moger
 * 
 */
public class RepositoryModel implements Serializable, Comparable<RepositoryModel> {
public class RepositoryModel implements Serializable,
        Comparable<RepositoryModel> {
    private static final long serialVersionUID = 1L;
    // field names are reflectively mapped in EditRepository page
    public String name;
    public String description;
    public String owner;
    private Set<String> repoAdministrators = new TreeSet<String>(new StringComparator());
    public Date lastChange;
    public boolean hasCommits;
    public boolean showRemoteBranches;
@@ -83,14 +88,17 @@
    public transient boolean isCollectingGarbage;
    public Date lastGC;
    
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public RepositoryModel() {
        this("", "", "", new Date(0));
    }
    public RepositoryModel(String name, String description, String owner, Date lastchange) {
    public RepositoryModel(String name, String description, String owner,
            Date lastchange) {
        this.name = name;
        this.description = description;
        this.owner = owner;
        this.addRepoAdministrator(owner);
        this.lastChange = lastchange;
        this.accessRestriction = AccessRestrictionType.NONE;
        this.authorizationControl = AuthorizationControl.NAMED;
@@ -160,16 +168,14 @@
        return !StringUtils.isEmpty(originRepository);
    }
    
    public boolean isOwner(String username) {
        return owner != null && username != null && owner.equalsIgnoreCase(username);
    }
    public boolean isPersonalRepository() {
        return !StringUtils.isEmpty(projectPath) && projectPath.charAt(0) == '~';
        return !StringUtils.isEmpty(projectPath)
                && projectPath.charAt(0) == '~';
    }
    
    public boolean isUsersPersonalRepository(String username) {
        return !StringUtils.isEmpty(projectPath) && projectPath.equalsIgnoreCase("~" + username);
        return !StringUtils.isEmpty(projectPath)
                && projectPath.equalsIgnoreCase("~" + username);
    }
    
    public boolean allowAnonymousView() {
@@ -195,4 +201,47 @@
        clone.skipSummaryMetrics = skipSummaryMetrics;
        return clone;
    }
    public void addRepoAdministrator(String repoAdministrator) {
        if (repoAdministrator != null && repoAdministrator.trim().length() > 0) {
            this.repoAdministrators.add(repoAdministrator.toLowerCase());
        }
    }
    public void removeRepoAdministrator(String repoAdministrator) {
        if (repoAdministrator != null && repoAdministrator.trim().length() > 0) {
            this.repoAdministrators.remove(repoAdministrator.toLowerCase());
        }
    }
    public void addRepoAdministrators(Set<String> repoAdministrators) {
        if (repoAdministrators != null) {
            for (String admin : repoAdministrators) {
                this.addRepoAdministrator(admin);
            }
        }
    }
    public void removeRepoAdministrators(Set<String> repoAdministrators) {
        if (repoAdministrators != null) {
            for (String admin : repoAdministrators) {
                this.removeRepoAdministrator(admin);
            }
        }
    }
    public void removeAllRepoAdministrators() {
        this.repoAdministrators.clear();
    }
    public Set<String> getRepoAdministrators() {
        return this.repoAdministrators;
    }
    public boolean isRepoAdministrator(String username) {
        if (username == null || username.trim().length() == 0) {
            return false;
        }
        return this.repoAdministrators.contains(username.toLowerCase());
    }
}
src/com/gitblit/models/UserModel.java
@@ -108,8 +108,7 @@
    @Deprecated
    @Unused
    public boolean canAccessRepository(RepositoryModel repository) {
        boolean isOwner = !StringUtils.isEmpty(repository.owner)
                && repository.owner.equals(username);
        boolean isOwner = repository.isRepoAdministrator(username);
        boolean allowAuthenticated = isAuthenticated && AuthorizationControl.AUTHENTICATED.equals(repository.authorizationControl);
        return canAdmin() || isOwner || repositories.contains(repository.name.toLowerCase())
                || hasTeamAccess(repository.name) || allowAuthenticated;
@@ -304,7 +303,7 @@
        }
        
        // repository owner - either specified owner or personal repository
        if (repository.isOwner(username) || repository.isUsersPersonalRepository(username)) {
        if (repository.isRepoAdministrator(username) || repository.isUsersPersonalRepository(username)) {
            ap.permissionType = PermissionType.OWNER;
            ap.permission = AccessPermission.REWIND;
            return ap;
@@ -412,7 +411,7 @@
            // can not fork your own repository
            return false;
        }
        if (canAdmin() || repository.isOwner(username)) {
        if (canAdmin() || repository.isRepoAdministrator(username)) {
            return true;
        }
        if (!repository.allowForks) {
@@ -429,7 +428,7 @@
    }
    
    public boolean canEdit(RepositoryModel model) {
        return canAdmin() || model.isUsersPersonalRepository(username) || model.isOwner(username);
        return canAdmin() || model.isUsersPersonalRepository(username) || model.isRepoAdministrator(username);
    }
    
    /**
src/com/gitblit/utils/MultiConfigUtil.java
New file
@@ -0,0 +1,89 @@
/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.utils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
 * Utility class to convert Strings into Collections and vice versa.
 *
 * @author saheba
 *
 */
public class MultiConfigUtil implements Serializable {
    private static final long serialVersionUID = 1324076956473037856L;
    public static final String OPTION_SEPARATOR = ";";
    /**
     * converts a collection of strings into a single line string by concatenating them and separating the different elements with the OPTION_SEPARATOR
     *
     * @param collection of strings
     *
     * @return
     */
    public String convertCollectionToSingleLineString(Collection<String> collection) {
        String result = "";
        for (String string : collection) {
            if (!result.equals("")) {
                result += OPTION_SEPARATOR;
            }
            result += string;
        }
        return result;
    }
    /**
     * converts a collection of strings into a list of strings
     *
     * @param collection
     *
     * @return
     */
    public List<String> convertCollectionToList(Collection<String> collection) {
        List<String> result = new ArrayList<String>();
        for (String string : collection) {
                result.add(string);
        }
        return result;
    }
    /**
     * converts a single line string into a set of strings by splitting the given string with the OPTION_SEPARATOR
     *
     * @param string which contains one or more options concatenated with the OPTION_SEPARATOR
     *
     * @return
     */
    public Set<String> convertStringToSet(String string) {
        Set<String> result = new HashSet<String>();
        if (string != null && string.trim().length() > 0) {
            String[] splitted = string.split(OPTION_SEPARATOR);
            for (int i = 0; i < splitted.length; i++) {
                String possible = splitted[i].trim();
                if (possible.length() > 0) {
                    result.add(possible);
                }
            }
        }
        return result;
    }
}
src/com/gitblit/utils/StringComparator.java
New file
@@ -0,0 +1,24 @@
package com.gitblit.utils;
import java.io.Serializable;
import java.util.Comparator;
/**
 * A comparator for {@link java.util.TreeSet} that sorts strings ascending inside the {@link java.util.TreeSet}
 *
 * @author saheba
 *
 */
public class StringComparator implements Comparator<String>, Serializable {
    private static final long serialVersionUID = 7563266118711225424L;
    /* (non-Javadoc)
     * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
     */
    @Override
    public int compare(String o1, String o2) {
        // TODO Auto-generated method stub
        return o1.compareTo(o2);
    }
}
src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -1,5 +1,5 @@
gb.repository = repository
gb.owner = owner
gb.repoAdministrators = repository administrators
gb.description = description
gb.lastChange = last change
gb.refs = refs
@@ -94,7 +94,7 @@
gb.showReadme = show readme
gb.showReadmeDescription = show a \"readme\" Markdown file on the summary page
gb.nameDescription = use '/' to group repositories.  e.g. libraries/mycoollib.git
gb.ownerDescription = the owner may edit repository settings
gb.repoAdministratorsDescription = the repository administrators may edit repository settings
gb.blob = blob
gb.commitActivityTrend = commit activity trend
gb.commitActivityDOW = commit activity by day of week
@@ -279,7 +279,7 @@
gb.emailAddress = email address
gb.errorAdminLoginRequired = Administration requires a login
gb.errorOnlyAdminMayCreateRepository = Only an administrator may create a repository
gb.errorOnlyAdminOrOwnerMayEditRepository = Only an administrator or the owner may edit a repository
gb.errorOnlyAdminOrRepoAdminMayEditRepository = Only an administrator or a repository administrator may edit a repository
gb.errorAdministrationDisabled = Administration is disabled
gb.lastNDays = last {0} days
gb.completeGravatarProfile = Complete profile on Gravatar.com
@@ -364,7 +364,7 @@
gb.gcPeriodDescription = duration between garbage collections
gb.gcThreshold = GC threshold
gb.gcThresholdDescription = minimum total size of loose objects to trigger early garbage collection
gb.ownerPermission = repository owner
gb.repoAdministratorPermission = repository administrator
gb.administrator = admin
gb.administratorPermission = Gitblit administrator
gb.team = team
src/com/gitblit/wicket/GitBlitWebApp_es.properties
@@ -1,5 +1,5 @@
gb.repository = Repositorio
gb.owner = Propietario
gb.repoAdministrators = Administradores del repositorio
gb.description = Descripci\u00F3n
gb.lastChange = Actualizado
gb.refs = Refs
@@ -94,7 +94,7 @@
gb.showReadme = Ver l\u00E9eme
gb.showReadmeDescription = Mostrar el archivo \"l\u00E9eme\" de Markdown en la p\u00E1gina resumen
gb.nameDescription = Usa '/' para agrupar repositorios. ej. librerias/mylibreria.git
gb.ownerDescription = El propietario puede editar la configuraci\u00F3n del repositorio
gb.repoAdministratorsDescription = Administradores del repositorio puede editar la configuraci\u00F3n del repositorio
gb.blob = Objeto
gb.commitActivityTrend = Tendencia de actividad del repositorio
gb.commitActivityDOW = Actividad de consignas por d\u00EDa de la semana
@@ -364,7 +364,7 @@
gb.gcPeriodDescription = Duraci\u00F3n entre periodos de limpieza
gb.gcThreshold = L\u00EDmites para GC
gb.gcThresholdDescription = Tama\u00F1o m\u00EDnimo total de objetos sueltos para activar la recolecci\u00F3n inmediata de basura
gb.ownerPermission = Propietario del repositorio
gb.repoAdministratorPermission = Administrador del repositorio
gb.administrator = Admin
gb.administratorPermission = Administrador de Gitblit
gb.team = Equipo
src/com/gitblit/wicket/GitBlitWebApp_ja.properties
@@ -1,5 +1,5 @@
gb.repository = \u30ea\u30dd\u30b8\u30c8\u30ea
gb.owner = \u6240\u6709\u8005
gb.repoAdministrators = \u30EA\u30DD\u30B8\u30C8\u30EA\u7BA1\u7406\u8005
gb.description = \u8aac\u660e
gb.lastChange = \u6700\u5f8c\u306e\u5909\u66f4
gb.refs = refs
@@ -94,7 +94,7 @@
gb.showReadme = readme\u8868\u793a
gb.showReadmeDescription = \"readme\" Markdown\u30d5\u30a1\u30a4\u30eb\u3092\u6982\u8981\u30da\u30fc\u30b8\u306b\u8868\u793a\u3059\u308b
gb.nameDescription = \u30ea\u30dd\u30b8\u30c8\u30ea\u3092\u30b0\u30eb\u30fc\u30d7\u5316\u3059\u308b\u306b\u306f '/' \u3092\u4f7f\u3046\u3002 e.g. libraries/mycoollib.git
gb.ownerDescription = \u6240\u6709\u8005\u306f\u30ea\u30dd\u30b8\u30c8\u30ea\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3067\u304d\u308b
gb.repoAdministratorsDescription = \u30EA\u30DD\u30B8\u30C8\u30EA\u7BA1\u7406\u8005\u306f\u30ea\u30dd\u30b8\u30c8\u30ea\u306e\u8a2d\u5b9a\u3092\u5909\u66f4\u3067\u304d\u308b
gb.blob = blob
gb.commitActivityTrend = commit activity trend
gb.commitActivityDOW = commit activity by day of week
@@ -279,7 +279,7 @@
gb.emailAddress = email address
gb.errorAdminLoginRequired = Administration requires a login
gb.errorOnlyAdminMayCreateRepository = Only an administrator may create a repository
gb.errorOnlyAdminOrOwnerMayEditRepository = Only an administrator or the owner may edit a repository
gb.errorOnlyAdminOrRepoAdminMayEditRepository = Only an administrator or a repository administrator may edit a repository
gb.errorAdministrationDisabled = Administration is disabled
gb.lastNDays = last {0} days
gb.completeGravatarProfile = Complete profile on Gravatar.com
src/com/gitblit/wicket/GitBlitWebApp_ko.properties
@@ -1,5 +1,5 @@
gb.repository = \uC800\uC7A5\uC18C
gb.owner = \uC18C\uC720\uC790
gb.repoAdministrators = \uC800\uC7A5\uC18C \uAD00\uB9AC\uC790
gb.description = \uC124\uBA85
gb.lastChange = \uCD5C\uADFC \uBCC0\uACBD
gb.refs = refs
@@ -94,7 +94,7 @@
gb.showReadme = \uB9AC\uB4DC\uBBF8(readme) \uBCF4\uAE30
gb.showReadmeDescription = \uC694\uC57D\uD398\uC774\uC9C0\uC5D0\uC11C \"readme\" \uB9C8\uD06C\uB2E4\uC6B4 \uD30C\uC77C \uBCF4\uAE30
gb.nameDescription = \uC800\uC7A5\uC18C\uB97C \uADF8\uB8F9\uC73C\uB85C \uBB36\uC73C\uB824\uBA74 '/' \uB97C \uC0AC\uC6A9. \uC608) libraries/reponame.git
gb.ownerDescription = \uC18C\uC720\uC790\uB294 \uC800\uC7A5\uC18C \uC124\uC815\uC744 \uBCC0\uACBD\uD560 \uC218 \uC788\uC74C
gb.repoAdministratorsDescription = \uC800\uC7A5\uC18C \uAD00\uB9AC\uC790 \uC800\uC7A5\uC18C \uC124\uC815\uC744 \uBCC0\uACBD\uD560 \uC218 \uC788\uC74C
gb.blob = blob
gb.commitActivityTrend = \uCEE4\uBC0B \uD65C\uB3D9 \uD2B8\uB79C\uB4DC
gb.commitActivityDOW = 1\uC8FC\uC77C\uC758 \uC77C\uB2E8\uC704 \uCEE4\uBC0B \uD65C\uB3D9
@@ -279,7 +279,7 @@
gb.emailAddress = \uC774\uBA54\uC77C \uC8FC\uC18C
gb.errorAdminLoginRequired = \uAD00\uB9AC\uB97C \uC704\uD574\uC11C\uB294 \uB85C\uADF8\uC778\uC774 \uD544\uC694
gb.errorOnlyAdminMayCreateRepository = \uAD00\uB9AC\uC790\uB9CC \uC800\uC7A5\uC18C\uB97C \uB9CC\uB4E4\uC218 \uC788\uC74C
gb.errorOnlyAdminOrOwnerMayEditRepository = \uAD00\uB9AC\uC790\uC640 \uC18C\uC720\uC790\uB9CC \uC800\uC7A5\uC18C\uB97C \uC218\uC815\uD560 \uC218 \uC788\uC74C
gb.errorOnlyAdminOrRepoAdminMayEditRepository = \uAD00\uB9AC\uC790\uC640 \uC800\uC7A5\uC18C \uAD00\uB9AC\uC790\uB9CC \uC800\uC7A5\uC18C\uB97C \uC218\uC815\uD560 \uC218 \uC788\uC74C
gb.errorAdministrationDisabled = \uAD00\uB9AC\uAE30\uB2A5 \uBE44\uD65C\uC131\uD654\uB428
gb.lastNDays = {0} \uC77C\uC804
gb.completeGravatarProfile = Gravatar.com \uC5D0 \uD504\uB85C\uD30C\uC77C \uC0DD\uC131\uB428
@@ -364,7 +364,7 @@
gb.gcPeriodDescription = \uAC00\uBE44\uC9C0 \uD074\uB809\uC158\uAC04\uC758 \uC2DC\uAC04 \uAC04\uACA9
gb.gcThreshold = GC \uAE30\uC900\uC810
gb.gcThresholdDescription = \uC870\uAE30 \uAC00\uBE44\uC9C0 \uCEEC\uB809\uC158\uC744 \uBC1C\uC0DD\uC2DC\uD0A4\uAE30 \uC704\uD55C \uC624\uBE0C\uC81D\uD2B8\uB4E4\uC758 \uCD5C\uC18C \uC804\uCCB4 \uD06C\uAE30
gb.ownerPermission = \uC800\uC7A5\uC18C \uC624\uB108
gb.repoAdministratorPermission = \uC800\uC7A5\uC18C \uAD00\uB9AC\uC790
gb.administrator = \uAD00\uB9AC\uC790
gb.administratorPermission = Gitblit \uAD00\uB9AC\uC790
gb.team = \uD300
src/com/gitblit/wicket/GitBlitWebApp_nl.properties
@@ -1,5 +1,5 @@
gb.repository = repositorie
gb.owner = eigenaar
gb.repoAdministrators = repository beheerders
gb.description = omschrijving
gb.lastChange = laatste wijziging
gb.refs = refs
@@ -94,7 +94,7 @@
gb.showReadme = toon readme
gb.showReadmeDescription = toon een \"readme\" Markdown bestand in de samenvattingspagina
gb.nameDescription = gebruik '/' voor het groeperen van repositories.  bijv. libraries/mycoollib.git
gb.ownerDescription = de eigenaar mag repository instellingen wijzigen
gb.repoAdministratorsDescription = repository beheerders mag repository instellingen wijzigen
gb.blob = blob
gb.commitActivityTrend = commit activiteit trend
gb.commitActivityDOW = commit activiteit per dag van de week
@@ -279,7 +279,7 @@
gb.emailAddress = emailadres
gb.errorAdminLoginRequired = Aanmelden vereist voor beheerwerk
gb.errorOnlyAdminMayCreateRepository = Alleen een beheerder kan een repositorie maken
gb.errorOnlyAdminOrOwnerMayEditRepository = Alleen een beheerder of de eigenaar kan een repositorie wijzigen
gb.errorOnlyAdminOrOwnerMayEditRepository = Alleen een beheerder of een repository beheerder kan een repositorie wijzigen
gb.errorAdministrationDisabled = Beheer is uitgeschakeld
gb.lastNDays = laatste {0} dagen
gb.completeGravatarProfile = Completeer profiel op Gravatar.com
@@ -364,7 +364,7 @@
gb.gcPeriodDescription = tijdsduur tussen opruimacties
gb.gcThreshold = opruim drempel
gb.gcThresholdDescription = minimum totaalomvang van losse objecten voor het starten van opruimactie
gb.ownerPermission = repositorie eigenaar
gb.repoAdministratorPermission = repository beheerder
gb.administrator = beheer
gb.administratorPermission = Gitblit beheerder
gb.team = team
src/com/gitblit/wicket/GitBlitWebApp_pl.properties
@@ -1,5 +1,5 @@
gb.repository = Repozytorium
gb.owner = W\u0142a\u015Bciciel
gb.repoAdministrators = Administratorzy repozytorium
gb.description = Opis
gb.lastChange = Ostatnia zmiana
gb.refs = Refs
@@ -94,7 +94,7 @@
gb.showReadme = Poka\u017C readme
gb.showReadmeDescription = Poka\u017C sparsowany \"readme\" na stronie podsumowania
gb.nameDescription = u\u017Cyj '/' do grupowania repozytori\u00F3w, np. libraries/server-lib.git
gb.ownerDescription = W\u0142a\u015Bciciel mo\u017Ce edytowa\u0107 ustawienia repozytorium
gb.repoAdministratorsDescription = Administrator repozytorium mo\u017Ce edytowa\u0107 ustawienia repozytorium
gb.blob = blob
gb.commitActivityTrend = Aktywno\u015B\u0107 zmian
gb.commitActivityDOW = Aktywno\u015B\u0107 zmian wed\u0142ug dnia tygodnia
@@ -279,7 +279,7 @@
gb.emailAddress = Adres email
gb.errorAdminLoginRequired = Administracja wymaga zalogowania
gb.errorOnlyAdminMayCreateRepository = Tylko administrator mo\u017Ce utworzy\u0107 repozytorium
gb.errorOnlyAdminOrOwnerMayEditRepository = Tylko administrator lub w\u0142a\u015Bciciel mo\u017Ce edytowa\u0107 repozytorium.
gb.errorOnlyAdminOrRepoAdminMayEditRepository = Tylko administrator lub administrator repozytorium mo\u017Ce edytowa\u0107 repozytorium.
gb.errorAdministrationDisabled = Administracja jest wy\u0142\u0105czona
gb.lastNDays = Ostatnich {0} dni
gb.completeGravatarProfile = Pe\u0142ny profil na Gravatar.com
src/com/gitblit/wicket/GitBlitWebApp_pt_BR.properties
@@ -1,5 +1,5 @@
gb.repository = repositório
gb.owner = proprietário
gb.repoAdministrators = administradores do repositório
gb.description = descrição
gb.lastChange = Ãºltima alteração
gb.refs = refs
@@ -94,7 +94,7 @@
gb.showReadme = mostrar readme
gb.showReadmeDescription = mostrar um arquivo \"leia-me\" na página de resumo
gb.nameDescription = usar '/' para agrupar repositórios.  e.g. libraries/mycoollib.git
gb.ownerDescription = o proprietário pode editar configurações do repositório
gb.repoAdministratorsDescription = o administradores do repositório pode editar configurações do repositório
gb.blob = blob
gb.commitActivityTrend = tendência dos commits
gb.commitActivityDOW = commits diários
@@ -279,7 +279,7 @@
gb.emailAddress = e-mail
gb.errorAdminLoginRequired = Administração requer um login
gb.errorOnlyAdminMayCreateRepository = Somente umadministrador pode criar um repositório
gb.errorOnlyAdminOrOwnerMayEditRepository = Somente umadministrador pode editar um repositório
gb.errorOnlyAdminOrRepoAdminMayEditRepository = Somente umadministrador ou um administrador de repositório pode editar um repositório
gb.errorAdministrationDisabled = Administração está desabilitada
gb.lastNDays = Ãºltimos {0} dias
gb.completeGravatarProfile = Profile completo em Gravatar.com
@@ -364,7 +364,7 @@
gb.gcPeriodDescription = duração entre as coletas de lixo
gb.gcThreshold = limite do GC 
gb.gcThresholdDescription = tamanho total mínimo de objetos \"soltos\" que ativam a coleta de lixo
gb.ownerPermission = proprietário do repositório
gb.repoAdministratorPermission = administrador do repositório
gb.administrator = administrador
gb.administratorPermission = administrador do Gitblit
gb.team = equipe
src/com/gitblit/wicket/pages/EditRepositoryPage.html
@@ -50,7 +50,7 @@
        <div class="tab-pane" id="permissions">
            <table class="plain">
                <tbody class="settings">
                    <tr><th><wicket:message key="gb.owner"></wicket:message></th><td class="edit"><select class="span2" wicket:id="owner" tabindex="15" /> &nbsp;<span class="help-inline"><wicket:message key="gb.ownerDescription"></wicket:message></span></td></tr>
                    <tr><th><wicket:message key="gb.repoAdministrators"></wicket:message></th><td class="edit"><span wicket:id="repoAdministrators" tabindex="15" /> &nbsp;<span class="help-inline"><wicket:message key="gb.repoAdministratorsDescription"></wicket:message></span></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
                    <tr><th><wicket:message key="gb.accessRestriction"></wicket:message></th><td class="edit"><select class="span4" wicket:id="accessRestriction" tabindex="16" /></td></tr>
                    <tr><th colspan="2"><hr/></th></tr>
src/com/gitblit/wicket/pages/EditRepositoryPage.java
@@ -61,6 +61,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.StringChoiceRenderer;
@@ -69,6 +70,8 @@
import com.gitblit.wicket.panels.RegistrantPermissionsPanel;
public class EditRepositoryPage extends RootSubPage {
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    private final boolean isCreate;
@@ -94,7 +97,7 @@
            // personal create permissions, inject personal repository path
            model.name = user.getPersonalPath() + "/";
            model.projectPath = user.getPersonalPath();
            model.owner = user.username;
            model.addRepoAdministrator(user.username);
            // personal repositories are private by default
            model.accessRestriction = AccessRestrictionType.VIEW;
            model.authorizationControl = AuthorizationControl.NAMED;
@@ -163,6 +166,12 @@
                RegistrantType.USER, GitBlit.self().getAllUsernames(), repositoryUsers, getAccessPermissions());
        final RegistrantPermissionsPanel teamsPalette = new RegistrantPermissionsPanel("teams", 
                RegistrantType.TEAM, GitBlit.self().getAllTeamnames(), repositoryTeams, getAccessPermissions());
        // repo administrators palette
        List admins = multiConfigUtil.convertCollectionToList(repositoryModel.getRepoAdministrators());
        List persons = GitBlit.self().getAllUsernames();
        final Palette repoAdministratorsPalette = new Palette("repoAdministrators", new ListModel<String>(admins), new CollectionModel<String>(
              persons), new StringChoiceRenderer(), 10, true);
        // indexed local branches palette
        List<String> allLocalBranches = new ArrayList<String>();
@@ -326,6 +335,12 @@
                    }
                    repositoryModel.indexedBranches = indexedBranches;
                    repositoryModel.removeAllRepoAdministrators();
                    Iterator<String> repoAdmins = repoAdministratorsPalette.getSelectedChoices();
                    while (repoAdmins.hasNext()) {
                        repositoryModel.addRepoAdministrator(repoAdmins.next());
                    }
                    // pre-receive scripts
                    List<String> preReceiveScripts = new ArrayList<String>();
                    Iterator<String> pres = preReceivePalette.getSelectedChoices();
@@ -377,8 +392,7 @@
        // field names reflective match RepositoryModel fields
        form.add(new TextField<String>("name").setEnabled(allowEditName));
        form.add(new TextField<String>("description"));
        form.add(new DropDownChoice<String>("owner", GitBlit.self().getAllUsernames())
                .setEnabled(GitBlitWebSession.get().canAdmin() && !repositoryModel.isPersonalRepository()));
        form.add(repoAdministratorsPalette);
        form.add(new CheckBox("allowForks").setEnabled(GitBlit.getBoolean(Keys.web.allowForking, true)));
        DropDownChoice<AccessRestrictionType> accessRestriction = new DropDownChoice<AccessRestrictionType>("accessRestriction", Arrays
                .asList(AccessRestrictionType.values()), new AccessRestrictionRenderer());
@@ -559,9 +573,9 @@
                        isAdmin = true;
                        return;
                    } else {
                        if (!model.owner.equalsIgnoreCase(user.username)) {
                            // User is not an Admin nor Owner
                            error(getString("gb.errorOnlyAdminOrOwnerMayEditRepository"), true);
                        if (!model.isRepoAdministrator(user.username)) {
                            // User is not an Admin nor RepoAdministrator
                            error(getString("gb.errorOnlyAdminOrRepoAdminMayEditRepository"), true);
                        }
                    }
                }
src/com/gitblit/wicket/pages/RepositoryPage.java
@@ -79,7 +79,7 @@
    
    private final Map<String, PageRegistration> registeredPages;
    private boolean showAdmin;
    private boolean isOwner;
    private boolean isRepoAdministrator;
    
    public RepositoryPage(PageParameters params) {
        super(params);
@@ -183,10 +183,10 @@
        } else {
            showAdmin = GitBlit.getBoolean(Keys.web.allowAdministration, false);
        }
        isOwner = GitBlitWebSession.get().isLoggedIn()
                && (model.owner != null && model.owner.equalsIgnoreCase(GitBlitWebSession.get()
        isRepoAdministrator = GitBlitWebSession.get().isLoggedIn()
                && (model.isRepoAdministrator(GitBlitWebSession.get()
                        .getUsername()));
        if (showAdmin || isOwner) {
        if (showAdmin || isRepoAdministrator) {
            pages.put("edit", new PageRegistration("gb.edit", EditRepositoryPage.class, params));
        }
        return pages;
@@ -540,7 +540,7 @@
    }
    
    public boolean isOwner() {
        return isOwner;
        return isRepoAdministrator;
    }
    
    private class SearchForm extends SessionlessForm<Void> implements Serializable {
src/com/gitblit/wicket/pages/RootSubPage.java
@@ -88,7 +88,7 @@
            if (repositoryModel.accessRestriction.exceeds(AccessRestrictionType.NONE)
                    && repositoryModel.authorizationControl.equals(AuthorizationControl.NAMED)) {
                if (user != null &&
                        (repositoryModel.isOwner(user.username) || repositoryModel.isUsersPersonalRepository(user.username))) {
                        (repositoryModel.isRepoAdministrator(user.username) || repositoryModel.isUsersPersonalRepository(user.username))) {
                    // exclude Owner or personal repositories
                    continue;
                }
src/com/gitblit/wicket/pages/SummaryPage.html
@@ -16,7 +16,7 @@
        <div class="hidden-phone" style="padding-bottom: 10px;"> 
            <table class="plain">
                <tr><th><wicket:message key="gb.description">[description]</wicket:message></th><td><span wicket:id="repositoryDescription">[repository description]</span></td></tr>
                <tr><th><wicket:message key="gb.owner">[owner]</wicket:message></th><td><span wicket:id="repositoryOwner">[repository owner]</span></td></tr>
                <tr><th><wicket:message key="gb.repoAdministrators">[owner]</wicket:message></th><td><span wicket:id="repositoryAdministrators">[repository owner]</span></td></tr>
                <tr><th><wicket:message key="gb.lastChange">[last change]</wicket:message></th><td><span wicket:id="repositoryLastChange">[repository last change]</span></td></tr>
                <tr><th><wicket:message key="gb.stats">[stats]</wicket:message></th><td><span wicket:id="branchStats">[branch stats]</span> <span class="link"><a wicket:id="metrics"><wicket:message key="gb.metrics">[metrics]</wicket:message></a></span></td></tr>
                <tr><th style="vertical-align:top;"><wicket:message key="gb.repositoryUrl">[URL]</wicket:message>&nbsp;<img style="vertical-align: top;padding-left:3px;" wicket:id="accessRestrictionIcon" /></th><td><span wicket:id="repositoryCloneUrl">[repository clone url]</span><div wicket:id="otherUrls"></div></td></tr>
src/com/gitblit/wicket/pages/SummaryPage.java
@@ -49,6 +49,7 @@
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.panels.BranchesPanel;
@@ -58,6 +59,8 @@
import com.gitblit.wicket.panels.TagsPanel;
public class SummaryPage extends RepositoryPage {
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public SummaryPage(PageParameters params) {
        super(params);
@@ -82,16 +85,12 @@
        // repository description
        add(new Label("repositoryDescription", getRepositoryModel().description));
        String owner = getRepositoryModel().owner;
        if (StringUtils.isEmpty(owner)) {
            add(new Label("repositoryOwner").setVisible(false));
        String repoAdministrators = multiConfigUtil.convertCollectionToSingleLineString(getRepositoryModel().getRepoAdministrators());
        if (StringUtils.isEmpty(repoAdministrators)) {
            add(new Label("repositoryAdministrators").setVisible(false));
        } else {
            UserModel ownerModel = GitBlit.self().getUserModel(owner);
            if (ownerModel != null) {
                add(new LinkPanel("repositoryOwner", null, ownerModel.getDisplayName(), UserPage.class, WicketUtils.newUsernameParameter(owner)));
            } else {
                add(new Label("repositoryOwner", owner));
            }
            //TODO reimplement link panel for each username
            add(new Label("repositoryAdministrators", repoAdministrators));
        }
        add(WicketUtils.createTimestampLabel("repositoryLastChange",
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.html
@@ -51,7 +51,7 @@
                        <img style="border:0px;vertical-align:middle;" src="feed_16x16.png"></img>
                    </a>
                </div>
                <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryOwner">[owner]</span>
                <span style="color: #999;font-style:italic;font-size:0.8em;" wicket:id="repositoryAdministrators">[owner]</span>
            </div>    
            
            <div class="pageTitle" style="border:0px;">
src/com/gitblit/wicket/panels/ProjectRepositoryPanel.java
@@ -36,6 +36,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.ArrayUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
@@ -51,86 +52,104 @@
    private static final long serialVersionUID = 1L;
    public ProjectRepositoryPanel(String wicketId, Localizer localizer, Component parent,
            final boolean isAdmin, final RepositoryModel entry,
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public ProjectRepositoryPanel(String wicketId, Localizer localizer,
            Component parent, final boolean isAdmin,
            final RepositoryModel entry,
            final Map<AccessRestrictionType, String> accessRestrictions) {
        super(wicketId);
        final boolean showSwatch = GitBlit.getBoolean(Keys.web.repositoryListSwatches, true);
        final boolean gitServlet = GitBlit.getBoolean(Keys.git.enableGitServlet, true);
        final boolean showSize = GitBlit.getBoolean(Keys.web.showRepositorySizes, true);
        final boolean showSwatch = GitBlit.getBoolean(
                Keys.web.repositoryListSwatches, true);
        final boolean gitServlet = GitBlit.getBoolean(
                Keys.git.enableGitServlet, true);
        final boolean showSize = GitBlit.getBoolean(
                Keys.web.showRepositorySizes, true);
        // repository swatch
        Component swatch;
        if (entry.isBare) {
            swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);
            swatch = new Label("repositorySwatch", "&nbsp;")
                    .setEscapeModelStrings(false);
        } else {
            swatch = new Label("repositorySwatch", "!");
            WicketUtils.setHtmlTooltip(swatch, localizer.getString("gb.workingCopyWarning", parent));
            WicketUtils.setHtmlTooltip(swatch,
                    localizer.getString("gb.workingCopyWarning", parent));
        }
        WicketUtils.setCssBackground(swatch, entry.toString());
        add(swatch);
        swatch.setVisible(showSwatch);
        PageParameters pp = WicketUtils.newRepositoryParameter(entry.name);
        add(new LinkPanel("repositoryName", "list", StringUtils.getRelativePath(entry.projectPath,
                StringUtils.stripDotGit(entry.name)), SummaryPage.class, pp));
        add(new Label("repositoryDescription", entry.description).setVisible(!StringUtils
                .isEmpty(entry.description)));
        add(new LinkPanel("repositoryName", "list",
                StringUtils.getRelativePath(entry.projectPath,
                        StringUtils.stripDotGit(entry.name)),
                SummaryPage.class, pp));
        add(new Label("repositoryDescription", entry.description)
                .setVisible(!StringUtils.isEmpty(entry.description)));
        if (StringUtils.isEmpty(entry.originRepository)) {
            add(new Label("originRepository").setVisible(false));
        } else {
            Fragment forkFrag = new Fragment("originRepository", "originFragment", this);
            forkFrag.add(new LinkPanel("originRepository", null, StringUtils.stripDotGit(entry.originRepository),
                    SummaryPage.class, WicketUtils.newRepositoryParameter(entry.originRepository)));
            Fragment forkFrag = new Fragment("originRepository",
                    "originFragment", this);
            forkFrag.add(new LinkPanel("originRepository", null, StringUtils
                    .stripDotGit(entry.originRepository), SummaryPage.class,
                    WicketUtils.newRepositoryParameter(entry.originRepository)));
            add(forkFrag);
        }
        add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp).setVisible(entry.useTickets));
        add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp).setVisible(entry.useDocs));
        add(new BookmarkablePageLink<Void>("tickets", TicketsPage.class, pp)
                .setVisible(entry.useTickets));
        add(new BookmarkablePageLink<Void>("docs", DocsPage.class, pp)
                .setVisible(entry.useDocs));
        if (entry.isFrozen) {
            add(WicketUtils.newImage("frozenIcon", "cold_16x16.png", localizer.getString("gb.isFrozen", parent)));
            add(WicketUtils.newImage("frozenIcon", "cold_16x16.png",
                    localizer.getString("gb.isFrozen", parent)));
        } else {
            add(WicketUtils.newClearPixel("frozenIcon").setVisible(false));
        }
        if (entry.isFederated) {
            add(WicketUtils.newImage("federatedIcon", "federated_16x16.png", localizer.getString("gb.isFederated", parent)));
            add(WicketUtils.newImage("federatedIcon", "federated_16x16.png",
                    localizer.getString("gb.isFederated", parent)));
        } else {
            add(WicketUtils.newClearPixel("federatedIcon").setVisible(false));
        }
        switch (entry.accessRestriction) {
        case NONE:
            add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(false));
            add(WicketUtils.newBlankImage("accessRestrictionIcon").setVisible(
                    false));
            break;
        case PUSH:
            add(WicketUtils.newImage("accessRestrictionIcon", "lock_go_16x16.png",
            add(WicketUtils.newImage("accessRestrictionIcon",
                    "lock_go_16x16.png",
                    accessRestrictions.get(entry.accessRestriction)));
            break;
        case CLONE:
            add(WicketUtils.newImage("accessRestrictionIcon", "lock_pull_16x16.png",
            add(WicketUtils.newImage("accessRestrictionIcon",
                    "lock_pull_16x16.png",
                    accessRestrictions.get(entry.accessRestriction)));
            break;
        case VIEW:
            add(WicketUtils.newImage("accessRestrictionIcon", "shield_16x16.png",
            add(WicketUtils.newImage("accessRestrictionIcon",
                    "shield_16x16.png",
                    accessRestrictions.get(entry.accessRestriction)));
            break;
        default:
            add(WicketUtils.newBlankImage("accessRestrictionIcon"));
        }
        if (StringUtils.isEmpty(entry.owner)) {
            add(new Label("repositoryOwner").setVisible(false));
        if (entry.getRepoAdministrators().size() < 1) {
            add(new Label("repositoryAdministrators").setVisible(false));
        } else {
            UserModel ownerModel = GitBlit.self().getUserModel(entry.owner);
            String owner = entry.owner;
            if (ownerModel != null) {
                owner = ownerModel.getDisplayName();
            }
            add(new Label("repositoryOwner", owner + " (" +
                    localizer.getString("gb.owner", parent) + ")"));
            add(new Label("repositoryAdministrators",
                    multiConfigUtil.convertCollectionToSingleLineString(entry
                            .getRepoAdministrators())
                            + " ("
                            + localizer.getString("gb.repoAdministrators", parent) + ")"));
        }
        UserModel user = GitBlitWebSession.get().getUser();
@@ -138,15 +157,19 @@
            user = UserModel.ANONYMOUS;
        }
        Fragment repositoryLinks;
        boolean showOwner = entry.isOwner(user.username);
        boolean isRepoAdministrator = entry.isRepoAdministrator(user.username);
        // owner of personal repository gets admin powers
        boolean showAdmin = isAdmin || entry.isUsersPersonalRepository(user.username);
        boolean showAdmin = isAdmin
                || entry.isUsersPersonalRepository(user.username);
        if (showAdmin || showOwner) {
            repositoryLinks = new Fragment("repositoryLinks", showAdmin ? "repositoryAdminLinks"
                    : "repositoryOwnerLinks", this);
            repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository", EditRepositoryPage.class,
                    WicketUtils.newRepositoryParameter(entry.name)));
        if (showAdmin || isRepoAdministrator) {
            repositoryLinks = new Fragment(
                    "repositoryLinks",
                    showAdmin ? "repositoryAdminLinks" : "repositoryOwnerLinks",
                    this);
            repositoryLinks.add(new BookmarkablePageLink<Void>(
                    "editRepository", EditRepositoryPage.class, WicketUtils
                            .newRepositoryParameter(entry.name)));
            if (showAdmin) {
                Link<Void> deleteLink = new Link<Void>("deleteRepository") {
@@ -157,28 +180,41 @@
                        if (GitBlit.self().deleteRepositoryModel(entry)) {
                            // redirect to the owning page
                            if (entry.isPersonalRepository()) {
                                setResponsePage(getPage().getClass(), WicketUtils.newUsernameParameter(entry.projectPath.substring(1)));
                                setResponsePage(
                                        getPage().getClass(),
                                        WicketUtils
                                                .newUsernameParameter(entry.projectPath
                                                        .substring(1)));
                            } else {
                                setResponsePage(getPage().getClass(), WicketUtils.newProjectParameter(entry.projectPath));
                                setResponsePage(
                                        getPage().getClass(),
                                        WicketUtils
                                                .newProjectParameter(entry.projectPath));
                            }
                        } else {
                            error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
                            error(MessageFormat.format(
                                    getString("gb.repositoryDeleteFailed"),
                                    entry));
                        }
                    }
                };
                deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
                        localizer.getString("gb.deleteRepository", parent), entry)));
                deleteLink.add(new JavascriptEventConfirmation("onclick",
                        MessageFormat.format(localizer.getString(
                                "gb.deleteRepository", parent), entry)));
                repositoryLinks.add(deleteLink);
            }
        } else {
            repositoryLinks = new Fragment("repositoryLinks", "repositoryUserLinks", this);
            repositoryLinks = new Fragment("repositoryLinks",
                    "repositoryUserLinks", this);
        }
        repositoryLinks.add(new BookmarkablePageLink<Void>("tree", TreePage.class, WicketUtils
                .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));
        repositoryLinks.add(new BookmarkablePageLink<Void>("tree",
                TreePage.class, WicketUtils.newRepositoryParameter(entry.name))
                .setEnabled(entry.hasCommits));
        repositoryLinks.add(new BookmarkablePageLink<Void>("log", LogPage.class, WicketUtils
                .newRepositoryParameter(entry.name)).setEnabled(entry.hasCommits));
        repositoryLinks.add(new BookmarkablePageLink<Void>("log",
                LogPage.class, WicketUtils.newRepositoryParameter(entry.name))
                .setEnabled(entry.hasCommits));
        add(repositoryLinks);
@@ -190,17 +226,20 @@
        }
        Label lastChangeLabel = new Label("repositoryLastChange", lastChange);
        add(lastChangeLabel);
        WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));
        WicketUtils.setCssClass(lastChangeLabel,
                getTimeUtils().timeAgoCss(entry.lastChange));
        if (entry.hasCommits) {
            // Existing repository
            add(new Label("repositorySize", entry.size).setVisible(showSize));
        } else {
            // New repository
            add(new Label("repositorySize", localizer.getString("gb.empty", parent)).setEscapeModelStrings(false));
            add(new Label("repositorySize", localizer.getString("gb.empty",
                    parent)).setEscapeModelStrings(false));
        }
        add(new ExternalLink("syndication", SyndicationServlet.asLink("", entry.name, null, 0)));
        add(new ExternalLink("syndication", SyndicationServlet.asLink("",
                entry.name, null, 0)));
        List<String> repositoryUrls = new ArrayList<String>();
        if (gitServlet) {
@@ -209,7 +248,8 @@
        }
        repositoryUrls.addAll(GitBlit.self().getOtherCloneUrls(entry.name));
        String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? "" : repositoryUrls.remove(0);
        String primaryUrl = ArrayUtils.isEmpty(repositoryUrls) ? ""
                : repositoryUrls.remove(0);
        add(new RepositoryUrlPanel("repositoryCloneUrl", primaryUrl));
    }
}
src/com/gitblit/wicket/panels/RegistrantPermissionsPanel.java
@@ -162,8 +162,8 @@
                    item.add(administrator);
                    break;
                case OWNER:
                    Label owner = new Label("pType", getString("gb.owner"));
                    WicketUtils.setHtmlTooltip(owner, getString("gb.ownerPermission"));
                    Label owner = new Label("pType", getString("gb.repoAdministrators"));
                    WicketUtils.setHtmlTooltip(owner, getString("gb.repoAdministratorPermission"));
                    WicketUtils.setCssClass(owner, "label label-info");
                    item.add(owner);
                    break;
src/com/gitblit/wicket/panels/RepositoriesPanel.html
@@ -57,7 +57,7 @@
                <wicket:message key="gb.repository">Repository</wicket:message>
            </th>
            <th class="hidden-phone" wicket:id="orderByDescription"><wicket:message key="gb.description">Description</wicket:message></th>
            <th class="hidden-tablet hidden-phone" wicket:id="orderByOwner"><wicket:message key="gb.owner">Owner</wicket:message></th>
            <th class="hidden-tablet hidden-phone" wicket:id="orderByOwner"><wicket:message key="gb.repoAdministrators">Owner</wicket:message></th>
            <th class="hidden-phone"></th>
            <th wicket:id="orderByDate"><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
            <th class="hidden-phone"></th>
@@ -72,7 +72,7 @@
                <wicket:message key="gb.repository">Repository</wicket:message>
            </th>
            <th class="hidden-phone" ><span><wicket:message key="gb.description">Description</wicket:message></span></th>
            <th class="hidden-tablet hidden-phone"><span><wicket:message key="gb.owner">Owner</wicket:message></span></th>
            <th class="hidden-tablet hidden-phone"><span><wicket:message key="gb.repoAdministrators">Owner</wicket:message></span></th>
            <th class="hidden-phone"></th>
            <th><wicket:message key="gb.lastChange">Last Change</wicket:message></th>
            <th class="hidden-phone"></th>
@@ -88,7 +88,7 @@
    <wicket:fragment wicket:id="repositoryRow">
        <td class="left" style="padding-left:3px;" ><b><span class="repositorySwatch" wicket:id="repositorySwatch"></span></b> <span style="padding-left:3px;" wicket:id="repositoryName">[repository name]</span></td>
        <td class="hidden-phone"><span class="list" wicket:id="repositoryDescription">[repository description]</span></td>
        <td class="hidden-tablet hidden-phone author"><span wicket:id="repositoryOwner">[repository owner]</span></td>
        <td class="hidden-tablet hidden-phone author"><span wicket:id="repositoryAdministrators">[repository owner]</span></td>
        <td class="hidden-phone" style="text-align: right;padding-right:10px;"><img class="inlineIcon" wicket:id="forkIcon" /><img class="inlineIcon" wicket:id="ticketsIcon" /><img class="inlineIcon" wicket:id="docsIcon" /><img class="inlineIcon" wicket:id="frozenIcon" /><img class="inlineIcon" wicket:id="federatedIcon" /><img class="inlineIcon" wicket:id="accessRestrictionIcon" /></td>
        <td><span wicket:id="repositoryLastChange">[last change]</span></td>
        <td class="hidden-phone" style="text-align: right;padding-right:15px;"><span style="font-size:0.8em;" wicket:id="repositorySize">[repository size]</span></td>
src/com/gitblit/wicket/panels/RepositoriesPanel.java
@@ -49,6 +49,7 @@
import com.gitblit.models.ProjectModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
@@ -63,6 +64,8 @@
public class RepositoriesPanel extends BasePanel {
    private static final long serialVersionUID = 1L;
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    public RepositoriesPanel(String wicketId, final boolean showAdmin, final boolean showManagement,
            List<RepositoryModel> models, boolean enableLinks,
@@ -287,14 +290,7 @@
                    row.add(WicketUtils.newBlankImage("accessRestrictionIcon"));
                }
                String owner = entry.owner;
                if (!StringUtils.isEmpty(owner)) {
                    UserModel ownerModel = GitBlit.self().getUserModel(owner);
                    if (ownerModel != null) {
                        owner = ownerModel.getDisplayName();
                    }
                }
                row.add(new Label("repositoryOwner", owner));
                row.add(new Label("repositoryAdministrators", multiConfigUtil.convertCollectionToSingleLineString(entry.getRepoAdministrators())));
                String lastChange;
                if (entry.lastChange.getTime() == 0) {
@@ -306,42 +302,29 @@
                row.add(lastChangeLabel);
                WicketUtils.setCssClass(lastChangeLabel, getTimeUtils().timeAgoCss(entry.lastChange));
                boolean showOwner = user != null && entry.isOwner(user.username);
                boolean myPersonalRepository = showOwner && entry.isUsersPersonalRepository(user.username);
                boolean isRepoAdministrator = user != null && entry.isRepoAdministrator(user.username);
                boolean myPersonalRepository = isRepoAdministrator && entry.isUsersPersonalRepository(user.username);
                if (showAdmin || myPersonalRepository) {
                    Fragment repositoryLinks = new Fragment("repositoryLinks",
                            "repositoryAdminLinks", this);
                    repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
                            EditRepositoryPage.class, WicketUtils
                                    .newRepositoryParameter(entry.name)));
                    Link<Void> deleteLink = new Link<Void>("deleteRepository") {
                        private static final long serialVersionUID = 1L;
                        @Override
                        public void onClick() {
                            if (GitBlit.self().deleteRepositoryModel(entry)) {
                                if (dp instanceof SortableRepositoriesProvider) {
                                    info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));
                                    ((SortableRepositoriesProvider) dp).remove(entry);
                                } else {
                                    setResponsePage(getPage().getClass(), getPage().getPageParameters());
                                }
                            } else {
                                error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
                            }
                        }
                    };
                    Link<Void> deleteLink = new DeleteLink(entry, dp);
                    deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
                            getString("gb.deleteRepository"), entry)));
                    repositoryLinks.add(deleteLink);
                    row.add(repositoryLinks);
                } else if (showOwner) {
                } else if (isRepoAdministrator) {
                    Fragment repositoryLinks = new Fragment("repositoryLinks",
                            "repositoryOwnerLinks", this);
                            "repositoryAdminLinks", this);
                    repositoryLinks.add(new BookmarkablePageLink<Void>("editRepository",
                            EditRepositoryPage.class, WicketUtils
                                    .newRepositoryParameter(entry.name)));
                    Link<Void> deleteLink = new DeleteLink(entry, dp);
                    deleteLink.add(new JavascriptEventConfirmation("onclick", MessageFormat.format(
                            getString("gb.deleteRepository"), entry)));
                    repositoryLinks.add(deleteLink);
                    row.add(repositoryLinks);
                } else {
                    row.add(new Label("repositoryLinks"));
@@ -368,6 +351,35 @@
            Fragment fragment = new Fragment("headerContent", "groupRepositoryHeader", this);
            add(fragment);
        }
    }
    private class DeleteLink extends Link<Void> {
            private RepositoryModel entry;
            private IDataProvider<RepositoryModel> dp;
            private static final long serialVersionUID = 1L;
            public DeleteLink(RepositoryModel entry, IDataProvider<RepositoryModel> dp) {
                super("deleteRepository");
                this.entry=entry;
                this.dp=dp;
            }
            @Override
            public void onClick() {
                if (GitBlit.self().deleteRepositoryModel(entry)) {
                    if (dp instanceof SortableRepositoriesProvider) {
                        info(MessageFormat.format(getString("gb.repositoryDeleted"), entry));
                        ((SortableRepositoriesProvider) dp).remove(entry);
                    } else {
                        setResponsePage(getPage().getClass(), getPage().getPageParameters());
                    }
                } else {
                    error(MessageFormat.format(getString("gb.repositoryDeleteFailed"), entry));
                }
            }
    }
    private static class GroupRepositoryModel extends RepositoryModel {
@@ -463,6 +475,8 @@
        private List<RepositoryModel> list;
        private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
        protected SortableRepositoriesProvider(List<RepositoryModel> list) {
            this.list = list;
            setSort(SortBy.date.name(), false);
@@ -516,9 +530,9 @@
                    @Override
                    public int compare(RepositoryModel o1, RepositoryModel o2) {
                        if (asc) {
                            return o1.owner.compareTo(o2.owner);
                            return multiConfigUtil.convertCollectionToSingleLineString(o1.getRepoAdministrators()).compareTo(multiConfigUtil.convertCollectionToSingleLineString(o2.getRepoAdministrators()));
                        }
                        return o2.owner.compareTo(o1.owner);
                        return multiConfigUtil.convertCollectionToSingleLineString(o2.getRepoAdministrators()).compareTo(multiConfigUtil.convertCollectionToSingleLineString(o1.getRepoAdministrators()));
                    }
                });
            } else if (prop.equals(SortBy.description.name())) {
tests/com/gitblit/tests/FederationTests.java
@@ -72,7 +72,7 @@
            model.accessRestriction = AccessRestrictionType.VIEW;
            model.description = "cloneable repository " + i;
            model.lastChange = new Date();
            model.owner = "adminuser";
            model.addRepoAdministrator("adminuser");
            model.name = "repo" + i + ".git";
            model.size = "5 MB";
            model.hasCommits = true;
tests/com/gitblit/tests/GitServletTest.java
@@ -41,6 +41,7 @@
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MultiConfigUtil;
import com.gitblit.utils.PushLogUtils;
public class GitServletTest {
@@ -58,6 +59,8 @@
    String password = GitBlitSuite.password;
    private static final AtomicBoolean started = new AtomicBoolean(false);
    private MultiConfigUtil multiConfigUtil = new MultiConfigUtil();
    @BeforeClass
    public static void startGitblit() throws Exception {
@@ -725,7 +728,7 @@
            
            // confirm default personal repository permissions
            RepositoryModel model = GitBlit.self().getRepositoryModel(MessageFormat.format("~{0}/ticgit.git", user.username));
            assertEquals("Unexpected owner", user.username, model.owner);
            assertEquals("Unexpected owner", user.username, multiConfigUtil.convertCollectionToSingleLineString(model.getRepoAdministrators()));
            assertEquals("Unexpected authorization control", AuthorizationControl.NAMED, model.authorizationControl);
            assertEquals("Unexpected access restriction", AccessRestrictionType.VIEW, model.accessRestriction);
            
@@ -749,7 +752,7 @@
            
            // confirm default project repository permissions
            RepositoryModel model = GitBlit.self().getRepositoryModel("project/ticgit.git");
            assertEquals("Unexpected owner", user.username, model.owner);
            assertEquals("Unexpected owner", user.username, multiConfigUtil.convertCollectionToSingleLineString(model.getRepoAdministrators()));
            assertEquals("Unexpected authorization control", AuthorizationControl.fromName(GitBlit.getString(Keys.git.defaultAuthorizationControl, "NAMED")), model.authorizationControl);
            assertEquals("Unexpected access restriction", AccessRestrictionType.fromName(GitBlit.getString(Keys.git.defaultAccessRestriction, "NONE")), model.accessRestriction);
tests/com/gitblit/tests/PermissionsTest.java
@@ -2327,7 +2327,7 @@
        repository.accessRestriction = AccessRestrictionType.VIEW;
        UserModel user = new UserModel("test");
        repository.owner = user.username;
        repository.addRepoAdministrator(user.username);
        assertFalse("user SHOULD NOT HAVE a repository permission!", user.hasRepositoryPermission(repository.name));
        assertTrue("owner CAN NOT view!", user.canView(repository));
@@ -2351,7 +2351,7 @@
        repository.accessRestriction = AccessRestrictionType.VIEW;
        UserModel user = new UserModel("test");
        repository.owner = user.username;
        repository.addRepoAdministrator(user.username);
        assertFalse("user SHOULD NOT HAVE a repository permission!", user.hasRepositoryPermission(repository.name));
        assertTrue("user CAN NOT view!", user.canView(repository));
@@ -2375,7 +2375,7 @@
        repository.accessRestriction = AccessRestrictionType.VIEW;
        UserModel user = new UserModel("visitor");
        repository.owner = "test";
        repository.addRepoAdministrator("test");
        assertFalse("user HAS a repository permission!", user.hasRepositoryPermission(repository.name));
        assertFalse("user CAN view!", user.canView(repository));
tests/com/gitblit/tests/RpcTests.java
@@ -167,7 +167,7 @@
        RepositoryModel model = new RepositoryModel();
        model.name = "garbagerepo.git";
        model.description = "created by RpcUtils";
        model.owner = "garbage";
        model.addRepoAdministrator("garbage");
        model.accessRestriction = AccessRestrictionType.VIEW;
        model.authorizationControl = AuthorizationControl.AUTHENTICATED;