James Moger
2011-11-01 38688b1f17bb2d43a144e92b086768e3e2523d2a
Refactored monolithic GitblitPanel into pieces. Revised feed generation.
4 files added
11 files modified
2585 ■■■■■ changed files
src/com/gitblit/SyndicationServlet.java 25 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/EditRepositoryDialog.java 1 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/FeedsPanel.java 210 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/FeedsTableModel.java 6 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitManager.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitPanel.java 1108 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/RepositoriesPanel.java 454 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/SettingsPanel.java 275 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/StatusPanel.java 73 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/UsersPanel.java 356 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/Utils.java 23 ●●●● patch | view | raw | blame | history
src/com/gitblit/models/FeedModel.java 2 ●●● patch | view | raw | blame | history
src/com/gitblit/utils/SyndicationUtils.java 24 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/SyndicationUtilsTest.java 24 ●●●●● patch | view | raw | blame | history
src/com/gitblit/SyndicationServlet.java
@@ -16,6 +16,7 @@
package com.gitblit;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServlet;
@@ -26,10 +27,13 @@
import org.slf4j.LoggerFactory;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.SyndicatedEntryModel;
import com.gitblit.utils.HttpUtils;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.utils.SyndicationUtils;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
/**
 * SyndicationServlet generates RSS 2.0 feeds and feed links.
@@ -139,9 +143,26 @@
        Repository repository = GitBlit.self().getRepository(repositoryName);
        RepositoryModel model = GitBlit.self().getRepositoryModel(repositoryName);
        List<RevCommit> commits = JGitUtils.getRevLog(repository, objectId, 0, length);
        List<SyndicatedEntryModel> entries = new ArrayList<SyndicatedEntryModel>();
        String gitblitUrl = HttpUtils.getGitblitURL(request);
        // convert RevCommit to SyndicatedEntryModel
        for (RevCommit commit : commits) {
            SyndicatedEntryModel entry = new SyndicatedEntryModel();
            entry.title = commit.getShortMessage();
            entry.author = commit.getAuthorIdent().getName();
            entry.link = MessageFormat.format("{0}/commit/{1}/{2}", gitblitUrl,
                    StringUtils.encodeURL(model.name), commit.getName());
            entry.published = commit.getCommitterIdent().getWhen();
            entry.contentType = "text/plain";
            entry.content = commit.getFullMessage();
            entry.repository = model.name;
            entry.branch = objectId;
            entries.add(entry);
        }
        try {
            SyndicationUtils.toRSS(HttpUtils.getGitblitURL(request), getTitle(model.name, objectId), model.description,
                    model.name, commits, response.getOutputStream());
            SyndicationUtils.toRSS(gitblitUrl, getTitle(model.name, objectId), model.description,
                    model.name, entries, response.getOutputStream());
        } catch (Exception e) {
            logger.error("An error occurred during feed generation", e);
        }
src/com/gitblit/client/EditRepositoryDialog.java
@@ -246,6 +246,7 @@
        getContentPane().setLayout(new BorderLayout(5, 5));
        getContentPane().add(centerPanel, BorderLayout.CENTER);
        pack();
        nameField.requestFocus();
    }
    private JPanel newFieldPanel(String label, JComponent comp) {
src/com/gitblit/client/FeedsPanel.java
New file
@@ -0,0 +1,210 @@
/*
 * 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.client;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.FeedModel;
import com.gitblit.models.SyndicatedEntryModel;
/**
 * RSS Feeds Panel displays recent entries and launches the browser to view the
 * commit. commitdiff, or tree of a commit.
 *
 * @author James Moger
 *
 */
public abstract class FeedsPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final GitblitClient gitblit;
    private SyndicatedEntryTableModel tableModel;
    private HeaderPanel header;
    private JTable table;
    public FeedsPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    private void initialize() {
        JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));
        refreshFeeds.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshFeeds();
            }
        });
        final JButton viewCommit = new JButton(Translation.get("gb.view"));
        viewCommit.setEnabled(false);
        viewCommit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewCommit();
            }
        });
        final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
        viewCommitDiff.setEnabled(false);
        viewCommitDiff.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewCommitDiff();
            }
        });
        final JButton viewTree = new JButton(Translation.get("gb.tree"));
        viewTree.setEnabled(false);
        viewTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewTree();
            }
        });
        JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "...");
        subscribeFeeds.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                subscribeFeeds(gitblit.getAvailableFeeds());
            }
        });
        JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0));
        controls.add(refreshFeeds);
        controls.add(subscribeFeeds);
        controls.add(viewCommit);
        controls.add(viewCommitDiff);
        controls.add(viewTree);
        NameRenderer nameRenderer = new NameRenderer();
        tableModel = new SyndicatedEntryTableModel();
        header = new HeaderPanel(Translation.get("gb.recentActivity"), "feed_16x16.png");
        table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        String name = table.getColumnName(SyndicatedEntryTableModel.Columns.Author.ordinal());
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.getColumn(name).setCellRenderer(nameRenderer);
        name = table.getColumnName(SyndicatedEntryTableModel.Columns.Repository.ordinal());
        table.getColumn(name).setCellRenderer(nameRenderer);
        name = table.getColumnName(SyndicatedEntryTableModel.Columns.Branch.ordinal());
        table.getColumn(name).setCellRenderer(nameRenderer);
        table.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    if (e.isControlDown()) {
                        viewCommitDiff();
                    } else {
                        viewCommit();
                    }
                }
            }
        });
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean singleSelection = table.getSelectedRowCount() == 1;
                viewCommit.setEnabled(singleSelection);
                viewCommitDiff.setEnabled(singleSelection);
                viewTree.setEnabled(singleSelection);
            }
        });
        setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        add(header, BorderLayout.NORTH);
        add(new JScrollPane(table), BorderLayout.CENTER);
        add(controls, BorderLayout.SOUTH);
    }
    @Override
    public Insets getInsets() {
        return Utils.INSETS;
    }
    protected void refreshFeeds() {
        // TODO change request type here
        GitblitWorker worker = new GitblitWorker(FeedsPanel.this, RpcRequest.LIST_USERS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshSubscribedFeeds();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    protected abstract void subscribeFeeds(List<FeedModel> feeds);
    protected void updateTable(boolean pack) {
        tableModel.entries.clear();
        tableModel.entries.addAll(gitblit.getSyndicatedEntries());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.recentActivity") + " ("
                + gitblit.getSyndicatedEntries().size() + ")");
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
    }
    protected SyndicatedEntryModel getSelectedSyndicatedEntry() {
        int viewRow = table.getSelectedRow();
        int modelRow = table.convertRowIndexToModel(viewRow);
        SyndicatedEntryModel entry = tableModel.get(modelRow);
        return entry;
    }
    protected void viewCommit() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        Utils.browse(entry.link);
    }
    protected void viewCommitDiff() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        Utils.browse(entry.link.replace("/commit/", "/commitdiff/"));
    }
    protected void viewTree() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        Utils.browse(entry.link.replace("/commit/", "/tree/"));
    }
}
src/com/gitblit/client/FeedsTableModel.java
@@ -36,7 +36,7 @@
    List<FeedModel> list;
    enum Columns {
        Subscribed, Repository, Branch, Max_Length;
        Subscribed, Repository, Branch;
        @Override
        public String toString() {
@@ -87,8 +87,6 @@
        switch (col) {
        case Subscribed:
            return Boolean.class;
        case Max_Length:
            return Integer.class;
        }
        return String.class;
    }
@@ -112,8 +110,6 @@
            return model.repository;
        case Branch:
            return model.branch;
        case Max_Length:
            return model.maxRetrieval;
        case Subscribed:
            return model.subscribed;
        }
src/com/gitblit/client/GitblitClient.java
@@ -290,7 +290,7 @@
    public boolean createRepository(RepositoryModel repository, List<String> permittedUsers)
            throws IOException {
        boolean success = true;
        success &= RpcUtils.createRepository(repository, url, account, password);
        success &= RpcUtils.createRepository(repository, url, account, password);
        if (permittedUsers.size() > 0) {
            // if new repository has named members, set them
            success &= RpcUtils.setRepositoryMembers(repository, permittedUsers, url, account,
src/com/gitblit/client/GitblitManager.java
@@ -56,12 +56,12 @@
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.Base64;
import org.eclipse.jgit.util.FS;
import com.gitblit.Constants;
import com.gitblit.GitBlitException.ForbiddenException;
import com.gitblit.models.FeedModel;
import com.gitblit.utils.Base64;
import com.gitblit.utils.StringUtils;
/**
src/com/gitblit/client/GitblitPanel.java
@@ -16,52 +16,18 @@
package com.gitblit.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URI;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableRowSorter;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.client.ClosableTabComponent.CloseTabListener;
import com.gitblit.models.FeedModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.models.SettingModel;
import com.gitblit.models.SyndicatedEntryModel;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
/**
 * GitblitPanel performs the login, all business logic, and contains all widgets
@@ -74,53 +40,21 @@
    private static final long serialVersionUID = 1L;
    private final int margin = 5;
    private final Insets insets = new Insets(margin, margin, margin, margin);
    private final RegistrationsDialog.RegistrationListener listener;
    private GitblitClient gitblit;
    private JTabbedPane tabs;
    private JTable repositoriesTable;
    private RepositoriesPanel repositoriesPanel;
    private RepositoriesTableModel repositoriesModel;
    private FeedsPanel feedsPanel;
    private JTable usersTable;
    private UsersPanel usersPanel;
    private UsersTableModel usersModel;
    private JTable settingsTable;
    private SettingsTableModel settingsModel;
    private JButton createRepository;
    private JButton delRepository;
    private TableRowSorter<RepositoriesTableModel> defaultRepositoriesSorter;
    private TableRowSorter<UsersTableModel> defaultUsersSorter;
    private TableRowSorter<SettingsTableModel> defaultSettingsSorter;
    private JButton editRepository;
    private HeaderPanel repositoriesHeader;
    private HeaderPanel usersHeader;
    private HeaderPanel settingsHeader;
    private SettingsPanel settingsPanel;
    private StatusPanel statusPanel;
    private SyndicatedEntryTableModel syndicationModel;
    private HeaderPanel feedsHeader;
    private JTable syndicationEntriesTable;
    public GitblitPanel(GitblitRegistration reg, RegistrationsDialog.RegistrationListener listener) {
        this.gitblit = new GitblitClient(reg);
@@ -132,510 +66,74 @@
        tabs.addTab(Translation.get("gb.users"), createUsersPanel());
        tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());
        tabs.addTab(Translation.get("gb.status"), createStatusPanel());
        tabs.addChangeListener(new ChangeListener() {
            public void stateChanged(ChangeEvent e) {
                tabs.getSelectedComponent().requestFocus();
            }
        });
        setLayout(new BorderLayout());
        add(tabs, BorderLayout.CENTER);
    }
    private JPanel createRepositoriesPanel() {
        final JButton browseRepository = new JButton(Translation.get("gb.browse"));
        browseRepository.setEnabled(false);
        browseRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                RepositoryModel model = getSelectedRepositories().get(0);
                String u = MessageFormat.format("{0}/summary/{1}", gitblit.url,
                        StringUtils.encodeURL(model.name));
                try {
                    Desktop.getDesktop().browse(new URI(u));
                } catch (Exception x) {
                    x.printStackTrace();
                }
            }
        });
        JButton refreshRepositories = new JButton(Translation.get("gb.refresh"));
        refreshRepositories.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshRepositories();
            }
        });
        createRepository = new JButton(Translation.get("gb.create"));
        createRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createRepository();
            }
        });
        editRepository = new JButton(Translation.get("gb.edit"));
        editRepository.setEnabled(false);
        editRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editRepository(getSelectedRepositories().get(0));
            }
        });
        delRepository = new JButton(Translation.get("gb.delete"));
        delRepository.setEnabled(false);
        delRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteRepositories(getSelectedRepositories());
            }
        });
        final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "...");
        subscribeRepository.setEnabled(false);
        subscribeRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                List<FeedModel> feeds = gitblit.getAvailableFeeds(getSelectedRepositories().get(0));
                subscribeFeeds(feeds);
            }
        });
        SubscribedRepositoryRenderer nameRenderer = new SubscribedRepositoryRenderer(gitblit);
        IndicatorsRenderer typeRenderer = new IndicatorsRenderer();
        DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer();
        sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
        sizeRenderer.setForeground(new Color(0, 0x80, 0));
        DefaultTableCellRenderer ownerRenderer = new DefaultTableCellRenderer();
        ownerRenderer.setForeground(Color.gray);
        ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
        repositoriesModel = new RepositoriesTableModel();
        defaultRepositoriesSorter = new TableRowSorter<RepositoriesTableModel>(repositoriesModel);
        repositoriesTable = Utils.newTable(repositoriesModel, Utils.DATE_FORMAT);
        repositoriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
        repositoriesTable.setRowSorter(defaultRepositoriesSorter);
        repositoriesTable.getRowSorter().toggleSortOrder(
                RepositoriesTableModel.Columns.Name.ordinal());
        setRepositoryRenderer(RepositoriesTableModel.Columns.Name, nameRenderer, -1);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Indicators, typeRenderer, 100);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Owner, ownerRenderer, -1);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Size, sizeRenderer, 60);
        repositoriesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean singleSelection = repositoriesTable.getSelectedRowCount() == 1;
                boolean selected = repositoriesTable.getSelectedRow() > -1;
                browseRepository.setEnabled(singleSelection);
                delRepository.setEnabled(selected);
                subscribeRepository.setEnabled(singleSelection);
                if (selected) {
                    int viewRow = repositoriesTable.getSelectedRow();
                    int modelRow = repositoriesTable.convertRowIndexToModel(viewRow);
                    RepositoryModel model = ((RepositoriesTableModel) repositoriesTable.getModel()).list
                            .get(modelRow);
                    editRepository.setEnabled(singleSelection
                            && (gitblit.allowManagement() || gitblit.isOwner(model)));
                } else {
                    editRepository.setEnabled(false);
                }
            }
        });
        repositoriesTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && gitblit.allowManagement()) {
                    editRepository(getSelectedRepositories().get(0));
                }
            }
        });
        final JTextField repositoryFilter = new JTextField();
        repositoryFilter.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterRepositories(repositoryFilter.getText());
            }
        });
        repositoryFilter.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterRepositories(repositoryFilter.getText());
            }
        });
        JPanel repositoryFilterPanel = new JPanel(new BorderLayout(margin, margin));
        repositoryFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        repositoryFilterPanel.add(repositoryFilter, BorderLayout.CENTER);
        JPanel repositoryTablePanel = new JPanel(new BorderLayout(margin, margin));
        repositoryTablePanel.add(repositoryFilterPanel, BorderLayout.NORTH);
        repositoryTablePanel.add(new JScrollPane(repositoriesTable), BorderLayout.CENTER);
        JPanel repositoryControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        repositoryControls.add(refreshRepositories);
        repositoryControls.add(browseRepository);
        repositoryControls.add(createRepository);
        repositoryControls.add(editRepository);
        repositoryControls.add(delRepository);
        repositoryControls.add(subscribeRepository);
        JPanel repositoriesPanel = new JPanel(new BorderLayout(margin, margin)) {
        repositoriesPanel = new RepositoriesPanel(gitblit) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return insets;
            @Override
            protected void subscribeFeeds(List<FeedModel> feeds) {
                GitblitPanel.this.subscribeFeeds(feeds);
            }
        };
        repositoriesHeader = new HeaderPanel(Translation.get("gb.repositories"),
                "gitweb-favicon.png");
        repositoriesPanel.add(repositoriesHeader, BorderLayout.NORTH);
        repositoriesPanel.add(repositoryTablePanel, BorderLayout.CENTER);
        repositoriesPanel.add(repositoryControls, BorderLayout.SOUTH);
            @Override
            protected void updateUsersTable() {
                usersPanel.updateTable(false);
            }
        };
        return repositoriesPanel;
    }
    private void setRepositoryRenderer(RepositoriesTableModel.Columns col,
            TableCellRenderer renderer, int maxWidth) {
        String name = repositoriesTable.getColumnName(col.ordinal());
        repositoriesTable.getColumn(name).setCellRenderer(renderer);
        if (maxWidth > 0) {
            repositoriesTable.getColumn(name).setMinWidth(maxWidth);
            repositoriesTable.getColumn(name).setMaxWidth(maxWidth);
        }
    }
    private JPanel createFeedsPanel() {
        JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));
        refreshFeeds.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshFeeds();
            }
        });
        final JButton viewCommit = new JButton(Translation.get("gb.view"));
        viewCommit.setEnabled(false);
        viewCommit.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewCommit();
            }
        });
        final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));
        viewCommitDiff.setEnabled(false);
        viewCommitDiff.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewCommitDiff();
            }
        });
        final JButton viewTree = new JButton(Translation.get("gb.tree"));
        viewTree.setEnabled(false);
        viewTree.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                viewTree();
            }
        });
        JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "...");
        subscribeFeeds.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                subscribeFeeds(gitblit.getAvailableFeeds());
            }
        });
        JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        controls.add(refreshFeeds);
        controls.add(subscribeFeeds);
        controls.add(viewCommit);
        controls.add(viewCommitDiff);
        controls.add(viewTree);
        NameRenderer nameRenderer = new NameRenderer();
        syndicationModel = new SyndicatedEntryTableModel();
        feedsHeader = new HeaderPanel(Translation.get("gb.recentActivity"), "feed_16x16.png");
        syndicationEntriesTable = Utils.newTable(syndicationModel, Utils.DATE_FORMAT);
        String name = syndicationEntriesTable
                .getColumnName(SyndicatedEntryTableModel.Columns.Author.ordinal());
        syndicationEntriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
        syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer);
        name = syndicationEntriesTable.getColumnName(SyndicatedEntryTableModel.Columns.Repository
                .ordinal());
        syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer);
        name = syndicationEntriesTable.getColumnName(SyndicatedEntryTableModel.Columns.Branch
                .ordinal());
        syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer);
        syndicationEntriesTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    if (e.isControlDown()) {
                        viewCommitDiff();
                    } else {
                        viewCommit();
                    }
                }
            }
        });
        syndicationEntriesTable.getSelectionModel().addListSelectionListener(
                new ListSelectionListener() {
                    @Override
                    public void valueChanged(ListSelectionEvent e) {
                        if (e.getValueIsAdjusting()) {
                            return;
                        }
                        boolean singleSelection = syndicationEntriesTable.getSelectedRowCount() == 1;
                        viewCommit.setEnabled(singleSelection);
                        viewCommitDiff.setEnabled(singleSelection);
                        viewTree.setEnabled(singleSelection);
                    }
                });
        JPanel panel = new JPanel(new BorderLayout(5, 5)) {
        feedsPanel = new FeedsPanel(gitblit) {
            private static final long serialVersionUID = 1L;
            @Override
            public Insets getInsets() {
                return insets;
            protected void subscribeFeeds(List<FeedModel> feeds) {
                GitblitPanel.this.subscribeFeeds(feeds);
            }
        };
        panel.add(feedsHeader, BorderLayout.NORTH);
        panel.add(new JScrollPane(syndicationEntriesTable), BorderLayout.CENTER);
        panel.add(controls, BorderLayout.SOUTH);
        return panel;
        return feedsPanel;
    }
    private JPanel createUsersPanel() {
        JButton refreshUsers = new JButton(Translation.get("gb.refresh"));
        refreshUsers.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshUsers();
            }
        });
        JButton createUser = new JButton(Translation.get("gb.create"));
        createUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createUser();
            }
        });
        final JButton editUser = new JButton(Translation.get("gb.edit"));
        editUser.setEnabled(false);
        editUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editUser(getSelectedUsers().get(0));
            }
        });
        final JButton delUser = new JButton(Translation.get("gb.delete"));
        delUser.setEnabled(false);
        delUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteUsers(getSelectedUsers());
            }
        });
        NameRenderer nameRenderer = new NameRenderer();
        usersModel = new UsersTableModel();
        defaultUsersSorter = new TableRowSorter<UsersTableModel>(usersModel);
        usersTable = Utils.newTable(usersModel, Utils.DATE_FORMAT);
        String name = usersTable.getColumnName(UsersTableModel.Columns.Name.ordinal());
        usersTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
        usersTable.getColumn(name).setCellRenderer(nameRenderer);
        usersTable.setRowSorter(defaultUsersSorter);
        usersTable.getRowSorter().toggleSortOrder(UsersTableModel.Columns.Name.ordinal());
        usersTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean selected = usersTable.getSelectedRow() > -1;
                boolean singleSelection = usersTable.getSelectedRows().length == 1;
                editUser.setEnabled(singleSelection && selected);
                delUser.setEnabled(selected);
            }
        });
        usersTable.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    editUser(getSelectedUsers().get(0));
                }
            }
        });
        final JTextField userFilter = new JTextField();
        userFilter.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterUsers(userFilter.getText());
            }
        });
        userFilter.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterUsers(userFilter.getText());
            }
        });
        JPanel userFilterPanel = new JPanel(new BorderLayout(margin, margin));
        userFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        userFilterPanel.add(userFilter, BorderLayout.CENTER);
        JPanel userTablePanel = new JPanel(new BorderLayout(margin, margin));
        userTablePanel.add(userFilterPanel, BorderLayout.NORTH);
        userTablePanel.add(new JScrollPane(usersTable), BorderLayout.CENTER);
        JPanel userControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        userControls.add(refreshUsers);
        userControls.add(createUser);
        userControls.add(editUser);
        userControls.add(delUser);
        JPanel usersPanel = new JPanel(new BorderLayout(margin, margin)) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return insets;
            }
        };
        usersHeader = new HeaderPanel(Translation.get("gb.users"), "user_16x16.png");
        usersPanel.add(usersHeader, BorderLayout.NORTH);
        usersPanel.add(userTablePanel, BorderLayout.CENTER);
        usersPanel.add(userControls, BorderLayout.SOUTH);
        usersPanel = new UsersPanel(gitblit);
        return usersPanel;
    }
    private JPanel createSettingsPanel() {
        JButton refreshSettings = new JButton(Translation.get("gb.refresh"));
        refreshSettings.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshSettings();
            }
        });
        final JButton editSetting = new JButton(Translation.get("gb.edit"));
        editSetting.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int viewRow = settingsTable.getSelectedRow();
                int modelRow = settingsTable.convertRowIndexToModel(viewRow);
                String key = settingsModel.keys.get(modelRow);
                SettingModel setting = settingsModel.settings.get(key);
                editSetting(setting);
            }
        });
        NameRenderer nameRenderer = new NameRenderer();
        final SettingPanel settingPanel = new SettingPanel();
        settingsModel = new SettingsTableModel();
        defaultSettingsSorter = new TableRowSorter<SettingsTableModel>(settingsModel);
        settingsTable = Utils.newTable(settingsModel, Utils.DATE_FORMAT);
        settingsTable.setDefaultRenderer(SettingModel.class, new SettingCellRenderer());
        String name = settingsTable.getColumnName(UsersTableModel.Columns.Name.ordinal());
        settingsTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
        settingsTable.getColumn(name).setCellRenderer(nameRenderer);
        settingsTable.setRowSorter(defaultSettingsSorter);
        settingsTable.getRowSorter().toggleSortOrder(SettingsTableModel.Columns.Name.ordinal());
        settingsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean singleSelection = settingsTable.getSelectedRows().length == 1;
                editSetting.setEnabled(singleSelection);
                if (singleSelection) {
                    int viewRow = settingsTable.getSelectedRow();
                    int modelRow = settingsTable.convertRowIndexToModel(viewRow);
                    SettingModel setting = settingsModel.get(modelRow);
                    settingPanel.setSetting(setting);
                } else {
                    settingPanel.clear();
                }
            }
        });
        final JTextField settingFilter = new JTextField();
        settingFilter.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterSettings(settingFilter.getText());
            }
        });
        settingFilter.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterSettings(settingFilter.getText());
            }
        });
        JPanel settingFilterPanel = new JPanel(new BorderLayout(margin, margin));
        settingFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        settingFilterPanel.add(settingFilter, BorderLayout.CENTER);
        JPanel settingsTablePanel = new JPanel(new BorderLayout(margin, margin));
        settingsTablePanel.add(settingFilterPanel, BorderLayout.NORTH);
        settingsTablePanel.add(new JScrollPane(settingsTable), BorderLayout.CENTER);
        settingsTablePanel.add(settingPanel, BorderLayout.SOUTH);
        JPanel settingsControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        settingsControls.add(refreshSettings);
        settingsControls.add(editSetting);
        JPanel settingsPanel = new JPanel(new BorderLayout(margin, margin)) {
            private static final long serialVersionUID = 1L;
            public Insets getInsets() {
                return insets;
            }
        };
        settingsHeader = new HeaderPanel(Translation.get("gb.settings"), "settings_16x16.png");
        settingsPanel.add(settingsHeader, BorderLayout.NORTH);
        settingsPanel.add(settingsTablePanel, BorderLayout.CENTER);
        settingsPanel.add(settingsControls, BorderLayout.SOUTH);
        settingsPanel = new SettingsPanel(gitblit);
        return settingsPanel;
    }
    private JPanel createStatusPanel() {
        JButton refreshStatus = new JButton(Translation.get("gb.refresh"));
        refreshStatus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshStatus();
            }
        });
        JPanel controls = new JPanel();
        controls.add(refreshStatus);
        JPanel panel = new JPanel(new BorderLayout());
        statusPanel = new StatusPanel();
        panel.add(statusPanel, BorderLayout.CENTER);
        panel.add(controls, BorderLayout.SOUTH);
        return panel;
        statusPanel = new StatusPanel(gitblit);
        return statusPanel;
    }
    public void login() throws IOException {
        gitblit.login();
        updateRepositoriesTable();
        Utils.packColumns(repositoriesTable, 5);
        updateFeedsTable();
        Utils.packColumns(syndicationEntriesTable, 5);
        repositoriesPanel.updateTable(true);
        feedsPanel.updateTable(true);
        if (gitblit.allowManagement()) {
            updateUsersTable();
            usersPanel.updateTable(false);
        } else {
            // user does not have administrator privileges
            // hide admin repository buttons
            createRepository.setVisible(false);
            editRepository.setVisible(false);
            delRepository.setVisible(false);
            repositoriesPanel.disableManagement();
            while (tabs.getTabCount() > 2) {
                // remove all management/administration tabs
@@ -644,9 +142,8 @@
        }
        if (gitblit.allowAdministration()) {
            updateSettingsTable();
            updateStatusPanel();
            Utils.packColumns(settingsTable, 5);
            settingsPanel.updateTable(true);
            statusPanel.updateTable(false);
        } else {
            // remove the settings tab
            String[] titles = { Translation.get("gb.settings"), Translation.get("gb.status") };
@@ -661,281 +158,14 @@
        }
    }
    private void updateRepositoriesTable() {
        repositoriesModel.list.clear();
        repositoriesModel.list.addAll(gitblit.getRepositories());
        repositoriesModel.fireTableDataChanged();
        repositoriesHeader.setText(Translation.get("gb.repositories") + " ("
                + gitblit.getRepositories().size() + ")");
    }
    private void updateFeedsTable() {
        syndicationModel.entries.clear();
        syndicationModel.entries.addAll(gitblit.getSyndicatedEntries());
        syndicationModel.fireTableDataChanged();
        feedsHeader.setText(Translation.get("gb.recentActivity") + " ("
                + gitblit.getSyndicatedEntries().size() + ")");
    }
    private void updateUsersTable() {
        usersModel.list.clear();
        usersModel.list.addAll(gitblit.getUsers());
        usersModel.fireTableDataChanged();
        usersHeader.setText(Translation.get("gb.users") + " (" + gitblit.getUsers().size() + ")");
    }
    private void updateSettingsTable() {
        settingsModel.setSettings(gitblit.getSettings());
        settingsModel.fireTableDataChanged();
        settingsHeader.setText(Translation.get("gb.settings"));
    }
    private void updateStatusPanel() {
        statusPanel.setStatus(gitblit.url, gitblit.getStatus());
    }
    private void filterRepositories(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            repositoriesTable.setRowSorter(defaultRepositoriesSorter);
            return;
        }
        RowFilter<RepositoriesTableModel, Object> containsFilter = new RowFilter<RepositoriesTableModel, Object>() {
            public boolean include(Entry<? extends RepositoriesTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<RepositoriesTableModel> sorter = new TableRowSorter<RepositoriesTableModel>(
                repositoriesModel);
        sorter.setRowFilter(containsFilter);
        repositoriesTable.setRowSorter(sorter);
    }
    private void filterUsers(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            usersTable.setRowSorter(defaultUsersSorter);
            return;
        }
        RowFilter<UsersTableModel, Object> containsFilter = new RowFilter<UsersTableModel, Object>() {
            public boolean include(Entry<? extends UsersTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<UsersTableModel> sorter = new TableRowSorter<UsersTableModel>(usersModel);
        sorter.setRowFilter(containsFilter);
        usersTable.setRowSorter(sorter);
    }
    private void filterSettings(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            settingsTable.setRowSorter(defaultSettingsSorter);
            return;
        }
        RowFilter<SettingsTableModel, Object> containsFilter = new RowFilter<SettingsTableModel, Object>() {
            public boolean include(Entry<? extends SettingsTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<SettingsTableModel> sorter = new TableRowSorter<SettingsTableModel>(
                settingsModel);
        sorter.setRowFilter(containsFilter);
        settingsTable.setRowSorter(sorter);
    }
    private List<RepositoryModel> getSelectedRepositories() {
        List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
        for (int viewRow : repositoriesTable.getSelectedRows()) {
            int modelRow = repositoriesTable.convertRowIndexToModel(viewRow);
            RepositoryModel model = repositoriesModel.list.get(modelRow);
            repositories.add(model);
        }
        return repositories;
    }
    private List<UserModel> getSelectedUsers() {
        List<UserModel> users = new ArrayList<UserModel>();
        for (int viewRow : usersTable.getSelectedRows()) {
            int modelRow = usersTable.convertRowIndexToModel(viewRow);
            UserModel model = usersModel.list.get(modelRow);
            users.add(model);
        }
        return users;
    }
    @Override
    public Insets getInsets() {
        return insets;
        return Utils.INSETS;
    }
    @Override
    public void closeTab(Component c) {
        gitblit = null;
    }
    protected void refreshRepositories() {
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_REPOSITORIES) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshRepositories();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateRepositoriesTable();
            }
        };
        worker.execute();
    }
    /**
     * Displays the create repository dialog and fires a SwingWorker to update
     * the server, if appropriate.
     *
     */
    protected void createRepository() {
        EditRepositoryDialog dialog = new EditRepositoryDialog();
        dialog.setLocationRelativeTo(GitblitPanel.this);
        dialog.setUsers(null, gitblit.getUsernames(), null);
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), null);
        dialog.setVisible(true);
        final RepositoryModel newRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        if (newRepository == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_REPOSITORY) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createRepository(newRepository, permittedUsers);
                if (success) {
                    gitblit.refreshRepositories();
                    if (permittedUsers.size() > 0) {
                        gitblit.refreshUsers();
                    }
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateRepositoriesTable();
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
                        getRequestType(), newRepository.name);
            }
        };
        worker.execute();
    }
    /**
     * Displays the edit repository dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     * @param repository
     */
    protected void editRepository(final RepositoryModel repository) {
        EditRepositoryDialog dialog = new EditRepositoryDialog(repository);
        dialog.setLocationRelativeTo(GitblitPanel.this);
        List<String> usernames = gitblit.getUsernames();
        List<String> members = gitblit.getPermittedUsernames(repository);
        dialog.setUsers(repository.owner, usernames, members);
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), repository.federationSets);
        dialog.setVisible(true);
        final RepositoryModel revisedRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        if (revisedRepository == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_REPOSITORY) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateRepository(repository.name, revisedRepository,
                        permittedUsers);
                if (success) {
                    gitblit.refreshRepositories();
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateRepositoriesTable();
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
                        getRequestType(), repository.name);
            }
        };
        worker.execute();
    }
    protected void deleteRepositories(final List<RepositoryModel> repositories) {
        if (repositories == null || repositories.size() == 0) {
            return;
        }
        StringBuilder message = new StringBuilder("Delete the following repositories?\n\n");
        for (RepositoryModel repository : repositories) {
            message.append(repository.name).append("\n");
        }
        int result = JOptionPane.showConfirmDialog(GitblitPanel.this, message.toString(),
                "Delete Repositories?", JOptionPane.YES_NO_OPTION);
        if (result == JOptionPane.YES_OPTION) {
            GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_REPOSITORY) {
                @Override
                protected Boolean doRequest() throws IOException {
                    boolean success = true;
                    for (RepositoryModel repository : repositories) {
                        success &= gitblit.deleteRepository(repository);
                    }
                    if (success) {
                        gitblit.refreshRepositories();
                        gitblit.refreshUsers();
                    }
                    return success;
                }
                @Override
                protected void onSuccess() {
                    updateRepositoriesTable();
                    updateUsersTable();
                }
                @Override
                protected void onFailure() {
                    showFailure("Failed to delete specified repositories!");
                }
            };
            worker.execute();
        }
    }
    protected void subscribeFeeds(final List<FeedModel> feeds) {
@@ -948,274 +178,10 @@
                gitblit.updateSubscribedFeeds(feeds);
                listener.saveRegistration(gitblit.reg.name, gitblit.reg);
                setVisible(false);
                updateRepositoriesTable();
                repositoriesPanel.updateTable(false);
            }
        };
        dialog.setLocationRelativeTo(GitblitPanel.this);
        dialog.setVisible(true);
    }
    protected void refreshFeeds() {
        // TODO change request type here
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_USERS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshSubscribedFeeds();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateFeedsTable();
            }
        };
        worker.execute();
    }
    protected SyndicatedEntryModel getSelectedSyndicatedEntry() {
        int viewRow = syndicationEntriesTable.getSelectedRow();
        int modelRow = syndicationEntriesTable.convertRowIndexToModel(viewRow);
        SyndicatedEntryModel entry = syndicationModel.get(modelRow);
        return entry;
    }
    protected void viewCommit() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        browse(entry.link);
    }
    protected void viewCommitDiff() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        browse(entry.link.replace("/commit/", "/commitdiff/"));
    }
    protected void viewTree() {
        SyndicatedEntryModel entry = getSelectedSyndicatedEntry();
        browse(entry.link.replace("/commit/", "/tree/"));
    }
    protected void browse(String url) {
        try {
            Desktop.getDesktop().browse(new URI(url));
        } catch (Exception x) {
            x.printStackTrace();
        }
    }
    protected void refreshUsers() {
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_USERS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshUsers();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateUsersTable();
            }
        };
        worker.execute();
    }
    /**
     * Displays the create user dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     */
    protected void createUser() {
        EditUserDialog dialog = new EditUserDialog(gitblit.getSettings());
        dialog.setLocationRelativeTo(GitblitPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), null);
        dialog.setVisible(true);
        final UserModel newUser = dialog.getUser();
        if (newUser == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_USER) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createUser(newUser);
                if (success) {
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for user \"{1}\".",
                        getRequestType(), newUser.username);
            }
        };
        worker.execute();
    }
    /**
     * Displays the edit user dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     * @param user
     */
    protected void editUser(final UserModel user) {
        EditUserDialog dialog = new EditUserDialog(user, gitblit.getSettings());
        dialog.setLocationRelativeTo(GitblitPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), new ArrayList<String>(user.repositories));
        dialog.setVisible(true);
        final UserModel revisedUser = dialog.getUser();
        if (revisedUser == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_USER) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateUser(user.username, revisedUser);
                if (success) {
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for user \"{1}\".",
                        getRequestType(), user.username);
            }
        };
        worker.execute();
    }
    protected void deleteUsers(final List<UserModel> users) {
        if (users == null || users.size() == 0) {
            return;
        }
        StringBuilder message = new StringBuilder("Delete the following users?\n\n");
        for (UserModel user : users) {
            message.append(user.username).append("\n");
        }
        int result = JOptionPane.showConfirmDialog(GitblitPanel.this, message.toString(),
                "Delete Users?", JOptionPane.YES_NO_OPTION);
        if (result == JOptionPane.YES_OPTION) {
            GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_USER) {
                @Override
                protected Boolean doRequest() throws IOException {
                    boolean success = true;
                    for (UserModel user : users) {
                        success &= gitblit.deleteUser(user);
                    }
                    if (success) {
                        gitblit.refreshUsers();
                    }
                    return success;
                }
                @Override
                protected void onSuccess() {
                    updateUsersTable();
                }
                @Override
                protected void onFailure() {
                    showFailure("Failed to delete specified users!");
                }
            };
            worker.execute();
        }
    }
    protected void refreshSettings() {
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_SETTINGS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshSettings();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateSettingsTable();
            }
        };
        worker.execute();
    }
    protected void refreshStatus() {
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_STATUS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshStatus();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateStatusPanel();
            }
        };
        worker.execute();
    }
    protected void editSetting(final SettingModel settingModel) {
        final JTextField textField = new JTextField(settingModel.currentValue);
        JPanel editPanel = new JPanel(new GridLayout(0, 1));
        editPanel.add(new JLabel("New Value"));
        editPanel.add(textField);
        JPanel settingPanel = new JPanel(new BorderLayout());
        settingPanel.add(new SettingPanel(settingModel), BorderLayout.CENTER);
        settingPanel.add(editPanel, BorderLayout.SOUTH);
        settingPanel.setPreferredSize(new Dimension(800, 200));
        String[] options;
        if (settingModel.currentValue.equals(settingModel.defaultValue)) {
            options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.save") };
        } else {
            options = new String[] { Translation.get("gb.cancel"),
                    Translation.get("gb.setDefault"), Translation.get("gb.save") };
        }
        String defaultOption = options[0];
        int selection = JOptionPane.showOptionDialog(GitblitPanel.this, settingPanel,
                settingModel.name, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
                new ImageIcon(getClass().getResource("/settings_16x16.png")), options,
                defaultOption);
        if (selection <= 0) {
            return;
        }
        if (options[selection].equals(Translation.get("gb.setDefault"))) {
            textField.setText(settingModel.defaultValue);
        }
        final Map<String, String> newSettings = new HashMap<String, String>();
        newSettings.put(settingModel.name, textField.getText().trim());
        GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.EDIT_SETTINGS) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateSettings(newSettings);
                if (success) {
                    gitblit.refreshSettings();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateSettingsTable();
            }
        };
        worker.execute();
    }
}
src/com/gitblit/client/RepositoriesPanel.java
New file
@@ -0,0 +1,454 @@
/*
 * 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.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableRowSorter;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.FeedModel;
import com.gitblit.models.RepositoryModel;
import com.gitblit.utils.StringUtils;
/**
 * RSS Feeds Panel displays recent entries and launches the browser to view the
 * commit. commitdiff, or tree of a commit.
 *
 * @author James Moger
 *
 */
public abstract class RepositoriesPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final GitblitClient gitblit;
    private HeaderPanel header;
    private JTable table;
    private RepositoriesTableModel tableModel;
    private TableRowSorter<RepositoriesTableModel> defaultSorter;
    private JButton createRepository;
    private JButton editRepository;
    private JButton delRepository;
    private JTextField filterTextfield;
    public RepositoriesPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    private void initialize() {
        final JButton browseRepository = new JButton(Translation.get("gb.browse"));
        browseRepository.setEnabled(false);
        browseRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                RepositoryModel model = getSelectedRepositories().get(0);
                String u = MessageFormat.format("{0}/summary/{1}", gitblit.url,
                        StringUtils.encodeURL(model.name));
                Utils.browse(u);
            }
        });
        JButton refreshRepositories = new JButton(Translation.get("gb.refresh"));
        refreshRepositories.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshRepositories();
            }
        });
        createRepository = new JButton(Translation.get("gb.create"));
        createRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createRepository();
            }
        });
        editRepository = new JButton(Translation.get("gb.edit"));
        editRepository.setEnabled(false);
        editRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editRepository(getSelectedRepositories().get(0));
            }
        });
        delRepository = new JButton(Translation.get("gb.delete"));
        delRepository.setEnabled(false);
        delRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteRepositories(getSelectedRepositories());
            }
        });
        final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "...");
        subscribeRepository.setEnabled(false);
        subscribeRepository.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                List<FeedModel> feeds = gitblit.getAvailableFeeds(getSelectedRepositories().get(0));
                subscribeFeeds(feeds);
            }
        });
        SubscribedRepositoryRenderer nameRenderer = new SubscribedRepositoryRenderer(gitblit);
        IndicatorsRenderer typeRenderer = new IndicatorsRenderer();
        DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer();
        sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
        sizeRenderer.setForeground(new Color(0, 0x80, 0));
        DefaultTableCellRenderer ownerRenderer = new DefaultTableCellRenderer();
        ownerRenderer.setForeground(Color.gray);
        ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
        tableModel = new RepositoriesTableModel();
        defaultSorter = new TableRowSorter<RepositoriesTableModel>(tableModel);
        table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.setRowSorter(defaultSorter);
        table.getRowSorter().toggleSortOrder(RepositoriesTableModel.Columns.Name.ordinal());
        setRepositoryRenderer(RepositoriesTableModel.Columns.Name, nameRenderer, -1);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Indicators, typeRenderer, 100);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Owner, ownerRenderer, -1);
        setRepositoryRenderer(RepositoriesTableModel.Columns.Size, sizeRenderer, 60);
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean singleSelection = table.getSelectedRowCount() == 1;
                boolean selected = table.getSelectedRow() > -1;
                browseRepository.setEnabled(singleSelection);
                delRepository.setEnabled(selected);
                subscribeRepository.setEnabled(singleSelection);
                if (selected) {
                    int viewRow = table.getSelectedRow();
                    int modelRow = table.convertRowIndexToModel(viewRow);
                    RepositoryModel model = ((RepositoriesTableModel) table.getModel()).list
                            .get(modelRow);
                    editRepository.setEnabled(singleSelection
                            && (gitblit.allowManagement() || gitblit.isOwner(model)));
                } else {
                    editRepository.setEnabled(false);
                }
            }
        });
        table.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2 && gitblit.allowManagement()) {
                    editRepository(getSelectedRepositories().get(0));
                }
            }
        });
        filterTextfield = new JTextField();
        filterTextfield.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterRepositories(filterTextfield.getText());
            }
        });
        filterTextfield.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterRepositories(filterTextfield.getText());
            }
        });
        JPanel repositoryFilterPanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        repositoryFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        repositoryFilterPanel.add(filterTextfield, BorderLayout.CENTER);
        JPanel repositoryTablePanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        repositoryTablePanel.add(repositoryFilterPanel, BorderLayout.NORTH);
        repositoryTablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
        JPanel repositoryControls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0));
        repositoryControls.add(refreshRepositories);
        repositoryControls.add(browseRepository);
        repositoryControls.add(createRepository);
        repositoryControls.add(editRepository);
        repositoryControls.add(delRepository);
        repositoryControls.add(subscribeRepository);
        setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        header = new HeaderPanel(Translation.get("gb.repositories"), "gitweb-favicon.png");
        add(header, BorderLayout.NORTH);
        add(repositoryTablePanel, BorderLayout.CENTER);
        add(repositoryControls, BorderLayout.SOUTH);
    }
    @Override
    public void requestFocus() {
        filterTextfield.requestFocus();
    }
    @Override
    public Insets getInsets() {
        return Utils.INSETS;
    }
    private void setRepositoryRenderer(RepositoriesTableModel.Columns col,
            TableCellRenderer renderer, int maxWidth) {
        String name = table.getColumnName(col.ordinal());
        table.getColumn(name).setCellRenderer(renderer);
        if (maxWidth > 0) {
            table.getColumn(name).setMinWidth(maxWidth);
            table.getColumn(name).setMaxWidth(maxWidth);
        }
    }
    protected abstract void subscribeFeeds(List<FeedModel> feeds);
    protected abstract void updateUsersTable();
    protected void disableManagement() {
        createRepository.setVisible(false);
        editRepository.setVisible(false);
        delRepository.setVisible(false);
    }
    protected void updateTable(boolean pack) {
        tableModel.list.clear();
        tableModel.list.addAll(gitblit.getRepositories());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.repositories") + " (" + gitblit.getRepositories().size()
                + ")");
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
    }
    private void filterRepositories(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            table.setRowSorter(defaultSorter);
            return;
        }
        RowFilter<RepositoriesTableModel, Object> containsFilter = new RowFilter<RepositoriesTableModel, Object>() {
            public boolean include(Entry<? extends RepositoriesTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<RepositoriesTableModel> sorter = new TableRowSorter<RepositoriesTableModel>(
                tableModel);
        sorter.setRowFilter(containsFilter);
        table.setRowSorter(sorter);
    }
    private List<RepositoryModel> getSelectedRepositories() {
        List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
        for (int viewRow : table.getSelectedRows()) {
            int modelRow = table.convertRowIndexToModel(viewRow);
            RepositoryModel model = tableModel.list.get(modelRow);
            repositories.add(model);
        }
        return repositories;
    }
    protected void refreshRepositories() {
        GitblitWorker worker = new GitblitWorker(RepositoriesPanel.this,
                RpcRequest.LIST_REPOSITORIES) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshRepositories();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    /**
     * Displays the create repository dialog and fires a SwingWorker to update
     * the server, if appropriate.
     *
     */
    protected void createRepository() {
        EditRepositoryDialog dialog = new EditRepositoryDialog();
        dialog.setLocationRelativeTo(RepositoriesPanel.this);
        dialog.setUsers(null, gitblit.getUsernames(), null);
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), null);
        dialog.setVisible(true);
        final RepositoryModel newRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        if (newRepository == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_REPOSITORY) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createRepository(newRepository, permittedUsers);
                if (success) {
                    gitblit.refreshRepositories();
                    if (permittedUsers.size() > 0) {
                        gitblit.refreshUsers();
                    }
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
                        getRequestType(), newRepository.name);
            }
        };
        worker.execute();
    }
    /**
     * Displays the edit repository dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     * @param repository
     */
    protected void editRepository(final RepositoryModel repository) {
        EditRepositoryDialog dialog = new EditRepositoryDialog(repository);
        dialog.setLocationRelativeTo(RepositoriesPanel.this);
        List<String> usernames = gitblit.getUsernames();
        List<String> members = gitblit.getPermittedUsernames(repository);
        dialog.setUsers(repository.owner, usernames, members);
        dialog.setRepositories(gitblit.getRepositories());
        dialog.setFederationSets(gitblit.getFederationSets(), repository.federationSets);
        dialog.setVisible(true);
        final RepositoryModel revisedRepository = dialog.getRepository();
        final List<String> permittedUsers = dialog.getPermittedUsers();
        if (revisedRepository == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_REPOSITORY) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateRepository(repository.name, revisedRepository,
                        permittedUsers);
                if (success) {
                    gitblit.refreshRepositories();
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
                updateUsersTable();
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
                        getRequestType(), repository.name);
            }
        };
        worker.execute();
    }
    protected void deleteRepositories(final List<RepositoryModel> repositories) {
        if (repositories == null || repositories.size() == 0) {
            return;
        }
        StringBuilder message = new StringBuilder("Delete the following repositories?\n\n");
        for (RepositoryModel repository : repositories) {
            message.append(repository.name).append("\n");
        }
        int result = JOptionPane.showConfirmDialog(RepositoriesPanel.this, message.toString(),
                "Delete Repositories?", JOptionPane.YES_NO_OPTION);
        if (result == JOptionPane.YES_OPTION) {
            GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_REPOSITORY) {
                @Override
                protected Boolean doRequest() throws IOException {
                    boolean success = true;
                    for (RepositoryModel repository : repositories) {
                        success &= gitblit.deleteRepository(repository);
                    }
                    if (success) {
                        gitblit.refreshRepositories();
                        gitblit.refreshUsers();
                    }
                    return success;
                }
                @Override
                protected void onSuccess() {
                    updateTable(false);
                    updateUsersTable();
                }
                @Override
                protected void onFailure() {
                    showFailure("Failed to delete specified repositories!");
                }
            };
            worker.execute();
        }
    }
}
src/com/gitblit/client/SettingsPanel.java
New file
@@ -0,0 +1,275 @@
/*
 * 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.client;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.SettingModel;
import com.gitblit.utils.StringUtils;
/**
 * Settings panel displays a list of server settings and their associated
 * metadata. This panel also allows editing of a setting.
 *
 * @author James Moger
 *
 */
public class SettingsPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final GitblitClient gitblit;
    private HeaderPanel header;
    private JTable table;
    private SettingsTableModel tableModel;
    private TableRowSorter<SettingsTableModel> defaultSorter;
    private JTextField filterTextfield;
    public SettingsPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    private void initialize() {
        JButton refreshSettings = new JButton(Translation.get("gb.refresh"));
        refreshSettings.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshSettings();
            }
        });
        final JButton editSetting = new JButton(Translation.get("gb.edit"));
        editSetting.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int viewRow = table.getSelectedRow();
                int modelRow = table.convertRowIndexToModel(viewRow);
                String key = tableModel.keys.get(modelRow);
                SettingModel setting = tableModel.settings.get(key);
                editSetting(setting);
            }
        });
        NameRenderer nameRenderer = new NameRenderer();
        final SettingPanel settingPanel = new SettingPanel();
        tableModel = new SettingsTableModel();
        defaultSorter = new TableRowSorter<SettingsTableModel>(tableModel);
        table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        table.setDefaultRenderer(SettingModel.class, new SettingCellRenderer());
        String name = table.getColumnName(UsersTableModel.Columns.Name.ordinal());
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.getColumn(name).setCellRenderer(nameRenderer);
        table.setRowSorter(defaultSorter);
        table.getRowSorter().toggleSortOrder(SettingsTableModel.Columns.Name.ordinal());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean singleSelection = table.getSelectedRows().length == 1;
                editSetting.setEnabled(singleSelection);
                if (singleSelection) {
                    int viewRow = table.getSelectedRow();
                    int modelRow = table.convertRowIndexToModel(viewRow);
                    SettingModel setting = tableModel.get(modelRow);
                    settingPanel.setSetting(setting);
                } else {
                    settingPanel.clear();
                }
            }
        });
        table.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    int viewRow = table.getSelectedRow();
                    int modelRow = table.convertRowIndexToModel(viewRow);
                    SettingModel setting = tableModel.get(modelRow);
                    editSetting(setting);
                }
            }
        });
        filterTextfield = new JTextField();
        filterTextfield.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterSettings(filterTextfield.getText());
            }
        });
        filterTextfield.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterSettings(filterTextfield.getText());
            }
        });
        JPanel settingFilterPanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        settingFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        settingFilterPanel.add(filterTextfield, BorderLayout.CENTER);
        JPanel settingsTablePanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        settingsTablePanel.add(settingFilterPanel, BorderLayout.NORTH);
        settingsTablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
        settingsTablePanel.add(settingPanel, BorderLayout.SOUTH);
        JPanel settingsControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        settingsControls.add(refreshSettings);
        settingsControls.add(editSetting);
        setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        header = new HeaderPanel(Translation.get("gb.settings"), "settings_16x16.png");
        add(header, BorderLayout.NORTH);
        add(settingsTablePanel, BorderLayout.CENTER);
        add(settingsControls, BorderLayout.SOUTH);
    }
    @Override
    public void requestFocus() {
        filterTextfield.requestFocus();
    }
    @Override
    public Insets getInsets() {
        return Utils.INSETS;
    }
    protected void updateTable(boolean pack) {
        tableModel.setSettings(gitblit.getSettings());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.settings"));
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
    }
    private void filterSettings(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            table.setRowSorter(defaultSorter);
            return;
        }
        RowFilter<SettingsTableModel, Object> containsFilter = new RowFilter<SettingsTableModel, Object>() {
            public boolean include(Entry<? extends SettingsTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<SettingsTableModel> sorter = new TableRowSorter<SettingsTableModel>(
                tableModel);
        sorter.setRowFilter(containsFilter);
        table.setRowSorter(sorter);
    }
    protected void refreshSettings() {
        GitblitWorker worker = new GitblitWorker(SettingsPanel.this, RpcRequest.LIST_SETTINGS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshSettings();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    protected void editSetting(final SettingModel settingModel) {
        final JTextField textField = new JTextField(settingModel.currentValue);
        JPanel editPanel = new JPanel(new GridLayout(0, 1));
        editPanel.add(new JLabel("New Value"));
        editPanel.add(textField);
        JPanel settingPanel = new JPanel(new BorderLayout());
        settingPanel.add(new SettingPanel(settingModel), BorderLayout.CENTER);
        settingPanel.add(editPanel, BorderLayout.SOUTH);
        settingPanel.setPreferredSize(new Dimension(800, 200));
        String[] options;
        if (settingModel.currentValue.equals(settingModel.defaultValue)) {
            options = new String[] { Translation.get("gb.cancel"), Translation.get("gb.save") };
        } else {
            options = new String[] { Translation.get("gb.cancel"),
                    Translation.get("gb.setDefault"), Translation.get("gb.save") };
        }
        String defaultOption = options[0];
        int selection = JOptionPane.showOptionDialog(SettingsPanel.this, settingPanel,
                settingModel.name, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
                new ImageIcon(getClass().getResource("/settings_16x16.png")), options,
                defaultOption);
        if (selection <= 0) {
            return;
        }
        if (options[selection].equals(Translation.get("gb.setDefault"))) {
            textField.setText(settingModel.defaultValue);
        }
        final Map<String, String> newSettings = new HashMap<String, String>();
        newSettings.put(settingModel.name, textField.getText().trim());
        GitblitWorker worker = new GitblitWorker(SettingsPanel.this, RpcRequest.EDIT_SETTINGS) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateSettings(newSettings);
                if (success) {
                    gitblit.refreshSettings();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
}
src/com/gitblit/client/StatusPanel.java
@@ -21,13 +21,18 @@
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import com.gitblit.Constants;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.ServerStatus;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.TimeUtils;
@@ -40,29 +45,32 @@
public class StatusPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final Insets insets = new Insets(5, 5, 5, 5);
    private final GitblitClient gitblit;
    private JLabel bootDate;
    private JLabel url;
    private JLabel servletContainer;
    private JLabel heapMaximum;
    private JLabel heapAllocated;
    private JLabel heapUsed;
    private PropertiesTableModel model;
    private HeaderPanel headerPanel;
    private PropertiesTableModel tableModel;
    private HeaderPanel header;
    private JLabel version;
    private JLabel releaseDate;
    public StatusPanel() {
    public StatusPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    public StatusPanel(String url, ServerStatus status) {
        this();
        setStatus(url, status);
    }
    private void initialize() {
        JButton refreshStatus = new JButton(Translation.get("gb.refresh"));
        refreshStatus.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshStatus();
            }
        });
        version = new JLabel();
        releaseDate = new JLabel();
        bootDate = new JLabel();
@@ -73,13 +81,13 @@
        heapAllocated = new JLabel();
        heapUsed = new JLabel();
        JPanel fieldsPanel = new JPanel(new GridLayout(0, 1, 0, 5)) {
        JPanel fieldsPanel = new JPanel(new GridLayout(0, 1, 0, Utils.MARGIN)) {
            private static final long serialVersionUID = 1L;
            @Override
            public Insets getInsets() {
                return insets;
                return Utils.INSETS;
            }
        };
        fieldsPanel.add(createFieldPanel("gb.version", version));
@@ -91,8 +99,8 @@
        fieldsPanel.add(createFieldPanel("gb.heapAllocated", heapAllocated));
        fieldsPanel.add(createFieldPanel("gb.heapMaximum", heapMaximum));
        model = new PropertiesTableModel();
        JTable propertiesTable = Utils.newTable(model, Utils.DATE_FORMAT);
        tableModel = new PropertiesTableModel();
        JTable propertiesTable = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        String name = propertiesTable.getColumnName(PropertiesTableModel.Columns.Name.ordinal());
        NameRenderer nameRenderer = new NameRenderer();
        propertiesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);
@@ -102,14 +110,18 @@
        centerPanel.add(fieldsPanel, BorderLayout.NORTH);
        centerPanel.add(new JScrollPane(propertiesTable), BorderLayout.CENTER);
        headerPanel = new HeaderPanel(Translation.get("gb.status"), "health_16x16.png");
        JPanel controls = new JPanel();
        controls.add(refreshStatus);
        header = new HeaderPanel(Translation.get("gb.status"), "health_16x16.png");
        setLayout(new BorderLayout());
        add(headerPanel, BorderLayout.NORTH);
        add(header, BorderLayout.NORTH);
        add(centerPanel, BorderLayout.CENTER);
        add(controls, BorderLayout.SOUTH);
    }
    private JPanel createFieldPanel(String key, JLabel valueLabel) {
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
        JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, Utils.MARGIN, 0));
        JLabel textLabel = new JLabel(Translation.get(key));
        textLabel.setFont(textLabel.getFont().deriveFont(Font.BOLD));
        textLabel.setPreferredSize(new Dimension(120, 10));
@@ -120,23 +132,40 @@
    @Override
    public Insets getInsets() {
        return insets;
        return Utils.INSETS;
    }
    public void setStatus(String url, ServerStatus status) {
        headerPanel.setText(Translation.get("gb.status"));
    protected void refreshStatus() {
        GitblitWorker worker = new GitblitWorker(StatusPanel.this, RpcRequest.LIST_STATUS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshStatus();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    protected void updateTable(boolean pack) {
        ServerStatus status = gitblit.getStatus();
        header.setText(Translation.get("gb.status"));
        version.setText(Constants.NAME + (status.isGO ? " GO v" : " WAR v") + status.version);
        releaseDate.setText(status.releaseDate);
        bootDate.setText(status.bootDate.toString() + " (" + TimeUtils.timeAgo(status.bootDate)
                + ")");
        this.url.setText(url);
        url.setText(gitblit.url);
        servletContainer.setText(status.servletContainer);
        ByteFormat byteFormat = new ByteFormat();
        heapMaximum.setText(byteFormat.format(status.heapMaximum));
        heapAllocated.setText(byteFormat.format(status.heapAllocated));
        heapUsed.setText(byteFormat.format(status.heapAllocated - status.heapFree) + " ("
                + byteFormat.format(status.heapFree) + " " + Translation.get("gb.free") + ")");
        model.setProperties(status.systemProperties);
        model.fireTableDataChanged();
        tableModel.setProperties(status.systemProperties);
        tableModel.fireTableDataChanged();
    }
}
src/com/gitblit/client/UsersPanel.java
New file
@@ -0,0 +1,356 @@
/*
 * 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.client;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableRowSorter;
import com.gitblit.Constants.RpcRequest;
import com.gitblit.models.UserModel;
import com.gitblit.utils.StringUtils;
/**
 * Users panel displays a list of user accounts and allows management of those
 * accounts.
 *
 * @author James Moger
 *
 */
public class UsersPanel extends JPanel {
    private static final long serialVersionUID = 1L;
    private final GitblitClient gitblit;
    private HeaderPanel header;
    private JTable table;
    private UsersTableModel tableModel;
    private TableRowSorter<UsersTableModel> defaultSorter;
    private JTextField filterTextfield;
    public UsersPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
        initialize();
    }
    private void initialize() {
        JButton refreshUsers = new JButton(Translation.get("gb.refresh"));
        refreshUsers.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshUsers();
            }
        });
        JButton createUser = new JButton(Translation.get("gb.create"));
        createUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                createUser();
            }
        });
        final JButton editUser = new JButton(Translation.get("gb.edit"));
        editUser.setEnabled(false);
        editUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editUser(getSelectedUsers().get(0));
            }
        });
        final JButton delUser = new JButton(Translation.get("gb.delete"));
        delUser.setEnabled(false);
        delUser.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteUsers(getSelectedUsers());
            }
        });
        NameRenderer nameRenderer = new NameRenderer();
        tableModel = new UsersTableModel();
        defaultSorter = new TableRowSorter<UsersTableModel>(tableModel);
        table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
        String name = table.getColumnName(UsersTableModel.Columns.Name.ordinal());
        table.setRowHeight(nameRenderer.getFont().getSize() + 8);
        table.getColumn(name).setCellRenderer(nameRenderer);
        table.setRowSorter(defaultSorter);
        table.getRowSorter().toggleSortOrder(UsersTableModel.Columns.Name.ordinal());
        table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                boolean selected = table.getSelectedRow() > -1;
                boolean singleSelection = table.getSelectedRows().length == 1;
                editUser.setEnabled(singleSelection && selected);
                delUser.setEnabled(selected);
            }
        });
        table.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    editUser(getSelectedUsers().get(0));
                }
            }
        });
        filterTextfield = new JTextField();
        filterTextfield.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                filterUsers(filterTextfield.getText());
            }
        });
        filterTextfield.addKeyListener(new KeyAdapter() {
            public void keyReleased(KeyEvent e) {
                filterUsers(filterTextfield.getText());
            }
        });
        JPanel userFilterPanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        userFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
        userFilterPanel.add(filterTextfield, BorderLayout.CENTER);
        JPanel userTablePanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        userTablePanel.add(userFilterPanel, BorderLayout.NORTH);
        userTablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
        JPanel userControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
        userControls.add(refreshUsers);
        userControls.add(createUser);
        userControls.add(editUser);
        userControls.add(delUser);
        setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
        header = new HeaderPanel(Translation.get("gb.users"), "user_16x16.png");
        add(header, BorderLayout.NORTH);
        add(userTablePanel, BorderLayout.CENTER);
        add(userControls, BorderLayout.SOUTH);
    }
    @Override
    public void requestFocus() {
        filterTextfield.requestFocus();
    }
    @Override
    public Insets getInsets() {
        return Utils.INSETS;
    }
    protected void updateTable(boolean pack) {
        tableModel.list.clear();
        tableModel.list.addAll(gitblit.getUsers());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.users") + " (" + gitblit.getUsers().size() + ")");
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
    }
    private void filterUsers(final String fragment) {
        if (StringUtils.isEmpty(fragment)) {
            table.setRowSorter(defaultSorter);
            return;
        }
        RowFilter<UsersTableModel, Object> containsFilter = new RowFilter<UsersTableModel, Object>() {
            public boolean include(Entry<? extends UsersTableModel, ? extends Object> entry) {
                for (int i = entry.getValueCount() - 1; i >= 0; i--) {
                    if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
                        return true;
                    }
                }
                return false;
            }
        };
        TableRowSorter<UsersTableModel> sorter = new TableRowSorter<UsersTableModel>(tableModel);
        sorter.setRowFilter(containsFilter);
        table.setRowSorter(sorter);
    }
    private List<UserModel> getSelectedUsers() {
        List<UserModel> users = new ArrayList<UserModel>();
        for (int viewRow : table.getSelectedRows()) {
            int modelRow = table.convertRowIndexToModel(viewRow);
            UserModel model = tableModel.list.get(modelRow);
            users.add(model);
        }
        return users;
    }
    protected void refreshUsers() {
        GitblitWorker worker = new GitblitWorker(UsersPanel.this, RpcRequest.LIST_USERS) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshUsers();
                return true;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
        };
        worker.execute();
    }
    /**
     * Displays the create user dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     */
    protected void createUser() {
        EditUserDialog dialog = new EditUserDialog(gitblit.getSettings());
        dialog.setLocationRelativeTo(UsersPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), null);
        dialog.setVisible(true);
        final UserModel newUser = dialog.getUser();
        if (newUser == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_USER) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.createUser(newUser);
                if (success) {
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for user \"{1}\".",
                        getRequestType(), newUser.username);
            }
        };
        worker.execute();
    }
    /**
     * Displays the edit user dialog and fires a SwingWorker to update the
     * server, if appropriate.
     *
     * @param user
     */
    protected void editUser(final UserModel user) {
        EditUserDialog dialog = new EditUserDialog(user, gitblit.getSettings());
        dialog.setLocationRelativeTo(UsersPanel.this);
        dialog.setUsers(gitblit.getUsers());
        dialog.setRepositories(gitblit.getRepositories(), new ArrayList<String>(user.repositories));
        dialog.setVisible(true);
        final UserModel revisedUser = dialog.getUser();
        if (revisedUser == null) {
            return;
        }
        GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_USER) {
            @Override
            protected Boolean doRequest() throws IOException {
                boolean success = gitblit.updateUser(user.username, revisedUser);
                if (success) {
                    gitblit.refreshUsers();
                }
                return success;
            }
            @Override
            protected void onSuccess() {
                updateTable(false);
            }
            @Override
            protected void onFailure() {
                showFailure("Failed to execute request \"{0}\" for user \"{1}\".",
                        getRequestType(), user.username);
            }
        };
        worker.execute();
    }
    protected void deleteUsers(final List<UserModel> users) {
        if (users == null || users.size() == 0) {
            return;
        }
        StringBuilder message = new StringBuilder("Delete the following users?\n\n");
        for (UserModel user : users) {
            message.append(user.username).append("\n");
        }
        int result = JOptionPane.showConfirmDialog(UsersPanel.this, message.toString(),
                "Delete Users?", JOptionPane.YES_NO_OPTION);
        if (result == JOptionPane.YES_OPTION) {
            GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_USER) {
                @Override
                protected Boolean doRequest() throws IOException {
                    boolean success = true;
                    for (UserModel user : users) {
                        success &= gitblit.deleteUser(user);
                    }
                    if (success) {
                        gitblit.refreshUsers();
                    }
                    return success;
                }
                @Override
                protected void onSuccess() {
                    updateTable(false);
                }
                @Override
                protected void onFailure() {
                    showFailure("Failed to delete specified users!");
                }
            };
            worker.execute();
        }
    }
}
src/com/gitblit/client/Utils.java
@@ -17,10 +17,13 @@
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.text.MessageFormat;
import java.util.Date;
@@ -36,9 +39,13 @@
import com.gitblit.Constants.RpcRequest;
public class Utils {
    public final static int MARGIN = 5;
    public final static Insets INSETS = new Insets(MARGIN, MARGIN, MARGIN, MARGIN);
    public final static String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm";
    public final static String DATE_FORMAT = "yyyy-MM-dd";
    public static JTable newTable(TableModel model, String datePattern) {
@@ -48,7 +55,8 @@
        table.getTableHeader().setReorderingAllowed(false);
        table.setGridColor(new Color(0xd9d9d9));
        table.setBackground(Color.white);
        table.setDefaultRenderer(Date.class, new DateCellRenderer(datePattern, Color.orange.darker()));
        table.setDefaultRenderer(Date.class,
                new DateCellRenderer(datePattern, Color.orange.darker()));
        return table;
    }
@@ -131,4 +139,13 @@
        // Set the width
        col.setPreferredWidth(width);
    }
    public static void browse(String url) {
        try {
            Desktop.getDesktop().browse(new URI(url));
        } catch (Exception x) {
            showException(null, x);
        }
    }
}
src/com/gitblit/models/FeedModel.java
@@ -68,7 +68,7 @@
    @Override
    public int compareTo(FeedModel o) {
        int repositoryCompare = repository.compareTo(o.repository);
        int repositoryCompare = StringUtils.compareRepositoryNames(repository, o.repository);
        if (repositoryCompare == 0) {
            // same repository
            if (StringUtils.isEmpty(branch)) {
src/com/gitblit/utils/SyndicationUtils.java
@@ -24,8 +24,6 @@
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.Constants;
import com.gitblit.GitBlitException;
import com.gitblit.models.SyndicatedEntryModel;
@@ -50,19 +48,20 @@
public class SyndicationUtils {
    /**
     * Outputs an RSS feed of the list of commits to the outputstream.
     * Outputs an RSS feed of the list of entries to the outputstream.
     * 
     * @param hostUrl
     * @param title
     * @param description
     * @param repository
     * @param commits
     * @param entryModels
     * @param os
     * @throws IOException
     * @throws FeedException
     */
    public static void toRSS(String hostUrl, String title, String description, String repository,
            List<RevCommit> commits, OutputStream os) throws IOException, FeedException {
            List<SyndicatedEntryModel> entryModels, OutputStream os) throws IOException,
            FeedException {
        SyndFeed feed = new SyndFeedImpl();
        feed.setFeedType("rss_2.0");
@@ -78,17 +77,16 @@
        feed.setImage(image);
        List<SyndEntry> entries = new ArrayList<SyndEntry>();
        for (RevCommit commit : commits) {
        for (SyndicatedEntryModel entryModel : entryModels) {
            SyndEntry entry = new SyndEntryImpl();
            entry.setTitle(commit.getShortMessage());
            entry.setAuthor(commit.getAuthorIdent().getName());
            entry.setLink(MessageFormat.format("{0}/commit/{1}/{2}", hostUrl,
                    StringUtils.encodeURL(repository), commit.getName()));
            entry.setPublishedDate(commit.getCommitterIdent().getWhen());
            entry.setTitle(entryModel.title);
            entry.setAuthor(entryModel.author);
            entry.setLink(entryModel.link);
            entry.setPublishedDate(entryModel.published);
            SyndContent content = new SyndContentImpl();
            content.setType("text/plain");
            content.setValue(commit.getFullMessage());
            content.setType(entryModel.contentType);
            content.setValue(entryModel.content);
            entry.setDescription(content);
            entries.add(entry);
        }
tests/com/gitblit/tests/SyndicationUtilsTest.java
@@ -16,28 +16,36 @@
package com.gitblit.tests;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import com.gitblit.models.SyndicatedEntryModel;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.SyndicationUtils;
public class SyndicationUtilsTest extends TestCase {
    public void testSyndication() throws Exception {
        Repository repository = GitBlitSuite.getHelloworldRepository();
        List<RevCommit> commits = JGitUtils.getRevLog(repository, 1);
        List<SyndicatedEntryModel> entries = new ArrayList<SyndicatedEntryModel>();
        for (int i = 0; i < 10; i++) {
            SyndicatedEntryModel entry = new SyndicatedEntryModel();
            entry.title = "Title " + i;
            entry.author = "Author " + i;
            entry.link = "Link " + i;
            entry.published = new Date();
            entry.contentType = "text/plain";
            entry.content = "Content " + i;
            entry.repository = "Repository " + i;
            entry.branch = "Branch " + i;
            entries.add(entry);
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        SyndicationUtils.toRSS("http://localhost", "Title", "Description", "Repository", commits,
        SyndicationUtils.toRSS("http://localhost", "Title", "Description", "Repository", entries,
                os);
        String feed = os.toString();
        os.close();
        assertTrue(feed.length() > 100);
        assertTrue(feed.indexOf("<title>Title</title>") > -1);
        assertTrue(feed.indexOf("<description>Description</description>") > -1);
    }