James Moger
2011-11-04 e33b91aa4d43246ad62832e66e2acfad3dfb3608
Support pagination in RSS feeds. Standardize pg as page parameter.
8 files modified
204 ■■■■ changed files
docs/02_rpc.mkd 3 ●●●● patch | view | raw | blame | history
src/com/gitblit/SyndicationServlet.java 14 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/FeedsPanel.java 70 ●●●● patch | view | raw | blame | history
src/com/gitblit/client/GitblitClient.java 16 ●●●●● patch | view | raw | blame | history
src/com/gitblit/client/SearchDialog.java 49 ●●●● patch | view | raw | blame | history
src/com/gitblit/utils/SyndicationUtils.java 16 ●●●● patch | view | raw | blame | history
src/com/gitblit/wicket/WicketUtils.java 14 ●●●● patch | view | raw | blame | history
tests/com/gitblit/tests/SyndicationUtilsTest.java 22 ●●●● patch | view | raw | blame | history
docs/02_rpc.mkd
@@ -42,6 +42,7 @@
<tr><td><em>repository</em></td><td><em>required</em></td><td>repository name is part of the url (see examples below)</td></tr>
<tr><td>h=</td><td><em>optional</em><br/>default: HEAD</td><td>starting branch, ref, or commit id</td></tr>
<tr><td>l=</td><td><em>optional</em><br/>default: web.syndicationEntries</td><td>maximum return count</td></tr>
<tr><td>pg=</td><td><em>optional</em><br/>default: 0</td><td>page number for paging<br/>(offset into history = pagenumber*maximum return count)</td></tr>
<tr><td colspan='3'><b>search query</b></td></tr>
<tr><td>s=</td><td><em>required</em></td><td>search string</td></tr>
<tr><td>st=</td><td><em>optional</em><br/>default: COMMIT</td><td>search type</td></tr>
@@ -51,7 +52,7 @@
    https://localhost:8443/feed/gitblit.git?l=50&h=refs/heads/master
    https://localhost:8443/feed/gitblit.git?l=50&h=refs/heads/master&s=documentation
    https://localhost:8443/feed/gitblit.git?l=50&h=refs/heads/master&s=james&st=author
    https://localhost:8443/feed/gitblit.git?l=50&h=refs/heads/master&s=james&st=author&pg=2
## JSON Remote Procedure Call (RPC) Interface
src/com/gitblit/SyndicationServlet.java
@@ -129,6 +129,7 @@
        String repositoryName = url;
        String objectId = request.getParameter("h");
        String l = request.getParameter("l");
        String page = request.getParameter("pg");
        String searchString = request.getParameter("s");
        Constants.SearchType searchType = Constants.SearchType.COMMIT;
        if (!StringUtils.isEmpty(request.getParameter("st"))) {
@@ -147,6 +148,13 @@
            } catch (NumberFormatException x) {
            }
        }
        int offset = 0;
        if (!StringUtils.isEmpty(page)) {
            try {
                offset = length * Integer.parseInt(page);
            } catch (NumberFormatException x) {
            }
        }
        response.setContentType("application/rss+xml; charset=UTF-8");
        Repository repository = GitBlit.self().getRepository(repositoryName);
@@ -154,11 +162,11 @@
        List<RevCommit> commits;
        if (StringUtils.isEmpty(searchString)) {
            // standard log/history lookup
            commits = JGitUtils.getRevLog(repository, objectId, 0, length);
            commits = JGitUtils.getRevLog(repository, objectId, offset, length);
        } else {
            // repository search
            commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType, 0,
                    length);
            commits = JGitUtils.searchRevlogs(repository, objectId, searchString, searchType,
                    offset, length);
        }
        Map<ObjectId, List<RefModel>> allRefs = JGitUtils.getAllRefs(repository);
        List<SyndicatedEntryModel> entries = new ArrayList<SyndicatedEntryModel>();
src/com/gitblit/client/FeedsPanel.java
@@ -18,6 +18,7 @@
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
@@ -76,6 +77,12 @@
    private JComboBox authorSelector;
    private int page;
    private JButton prev;
    private JButton next;
    public FeedsPanel(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
@@ -83,10 +90,29 @@
    }
    private void initialize() {
        prev = new JButton("<");
        prev.setToolTipText(Translation.get("gb.pagePrevious"));
        prev.setEnabled(false);
        prev.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshFeeds(--page);
            }
        });
        next = new JButton(">");
        next.setToolTipText(Translation.get("gb.pageNext"));
        next.setEnabled(false);
        next.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshFeeds(++page);
            }
        });
        JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));
        refreshFeeds.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshFeeds();
                refreshFeeds(0);
            }
        });
@@ -205,6 +231,8 @@
        northControls.add(repositorySelector);
        northControls.add(new JLabel(Translation.get("gb.author")));
        northControls.add(authorSelector);
//        northControls.add(prev);
//        northControls.add(next);
        JPanel northPanel = new JPanel(new BorderLayout(0, Utils.MARGIN));
        northPanel.add(header, BorderLayout.NORTH);
@@ -221,11 +249,12 @@
        return Utils.INSETS;
    }
    protected void refreshFeeds() {
    protected void refreshFeeds(final int page) {
        this.page = page;
        GitblitWorker worker = new GitblitWorker(FeedsPanel.this, null) {
            @Override
            protected Boolean doRequest() throws IOException {
                gitblit.refreshSubscribedFeeds();
                gitblit.refreshSubscribedFeeds(page);
                return true;
            }
@@ -244,24 +273,33 @@
        tableModel.entries.addAll(gitblit.getSyndicatedEntries());
        tableModel.fireTableDataChanged();
        header.setText(Translation.get("gb.activity") + " ("
                + gitblit.getSyndicatedEntries().size() + ")");
                + gitblit.getSyndicatedEntries().size() + (page > 0 ? (", pg " + (page + 1)) : "")
                + ")");
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
        // determine unique repositories
        Set<String> uniqueRepositories = new HashSet<String>();
        for (SyndicatedEntryModel entry : tableModel.entries) {
            uniqueRepositories.add(entry.repository);
        table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true)));
        if (page == 0) {
            // determine unique repositories
            Set<String> uniqueRepositories = new HashSet<String>();
            for (SyndicatedEntryModel entry : tableModel.entries) {
                uniqueRepositories.add(entry.repository);
            }
            // repositories
            List<String> sortedRespositories = new ArrayList<String>(uniqueRepositories);
            StringUtils.sortRepositorynames(sortedRespositories);
            repositoryChoices.removeAllElements();
            repositoryChoices.addElement(ALL);
            for (String repo : sortedRespositories) {
                repositoryChoices.addElement(repo);
            }
        }
        // repositories
        List<String> sortedRespositories = new ArrayList<String>(uniqueRepositories);
        StringUtils.sortRepositorynames(sortedRespositories);
        repositoryChoices.removeAllElements();
        repositoryChoices.addElement(ALL);
        for (String repo : sortedRespositories) {
            repositoryChoices.addElement(repo);
        }
        // update pagination buttons
        next.setEnabled(tableModel.entries.size() > 0);
        prev.setEnabled(page > 0);
    }
    private void updateAuthors() {
src/com/gitblit/client/GitblitClient.java
@@ -101,13 +101,7 @@
        refreshSettings();
        refreshAvailableFeeds();
        refreshRepositories();
        try {
            // RSS feeds may be disabled by server
            refreshSubscribedFeeds();
        } catch (IOException e) {
            e.printStackTrace();
        }
        refreshSubscribedFeeds(0);
        try {
            // credentials may not have administrator access
@@ -253,14 +247,14 @@
        return availableFeeds;
    }
    public List<SyndicatedEntryModel> refreshSubscribedFeeds() throws IOException {
    public List<SyndicatedEntryModel> refreshSubscribedFeeds(int page) throws IOException {
        Set<SyndicatedEntryModel> allEntries = new HashSet<SyndicatedEntryModel>();
        if (reg.feeds.size() > 0) {
            for (FeedModel feed : reg.feeds) {
                feed.lastRefreshDate = feed.currentRefreshDate;
                feed.currentRefreshDate = new Date();
                List<SyndicatedEntryModel> entries = SyndicationUtils.readFeed(url,
                        feed.repository, feed.branch, -1, account, password);
                        feed.repository, feed.branch, -1, page, account, password);
                allEntries.addAll(entries);
            }
        }
@@ -308,9 +302,9 @@
    }
    public List<SyndicatedEntryModel> search(String repository, String branch, String fragment,
            Constants.SearchType type, int numberOfEntries) throws IOException {
            Constants.SearchType type, int numberOfEntries, int page) throws IOException {
        return SyndicationUtils.readSearchFeed(url, repository, branch, fragment, type,
                numberOfEntries, account, password);
                numberOfEntries, page, account, password);
    }
    public List<FederationModel> refreshFederationRegistrations() throws IOException {
src/com/gitblit/client/SearchDialog.java
@@ -18,6 +18,7 @@
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
@@ -77,6 +78,12 @@
    private JComboBox maxHitsSelector;
    private int page;
    private JButton prev;
    private JButton next;
    public SearchDialog(GitblitClient gitblit) {
        super();
        this.gitblit = gitblit;
@@ -88,10 +95,28 @@
    private void initialize() {
        prev = new JButton("<");
        prev.setToolTipText(Translation.get("gb.pagePrevious"));
        prev.setEnabled(false);
        prev.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                search(--page);
            }
        });
        next = new JButton(">");
        next.setToolTipText(Translation.get("gb.pageNext"));
        next.setEnabled(false);
        next.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                search(++page);
            }
        });
        final JButton search = new JButton(Translation.get("gb.search"));
        search.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                search();
                search(0);
            }
        });
@@ -194,12 +219,12 @@
        searchTypeSelector.setSelectedItem(Constants.SearchType.COMMIT);
        maxHitsSelector = new JComboBox(new Integer[] { 25, 50, 75, 100 });
        maxHitsSelector.setSelectedIndex(-1);
        maxHitsSelector.setSelectedIndex(0);
        searchFragment = new JTextField(25);
        searchFragment.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                search();
                search(0);
            }
        });
@@ -214,6 +239,8 @@
        northControls.add(maxHitsSelector);
        northControls.add(searchFragment);
        northControls.add(search);
        northControls.add(prev);
        northControls.add(next);
        JPanel northPanel = new JPanel(new BorderLayout(0, Utils.MARGIN));
        northPanel.add(header, BorderLayout.NORTH);
@@ -263,7 +290,8 @@
        }
    }
    protected void search() {
    protected void search(final int page) {
        this.page = page;
        final String repository = repositorySelector.getSelectedItem().toString();
        final String branch = branchSelector.getSelectedIndex() > -1 ? branchSelector
                .getSelectedItem().toString() : null;
@@ -272,13 +300,15 @@
        final String fragment = searchFragment.getText();
        final int maxEntryCount = maxHitsSelector.getSelectedIndex() > -1 ? ((Integer) maxHitsSelector
                .getSelectedItem()) : -1;
        if (StringUtils.isEmpty(fragment)) {
            return;
        }
        SwingWorker<List<SyndicatedEntryModel>, Void> worker = new SwingWorker<List<SyndicatedEntryModel>, Void>() {
            @Override
            protected List<SyndicatedEntryModel> doInBackground() throws IOException {
                return gitblit.search(repository, branch, fragment, searchType, maxEntryCount);
                return gitblit
                        .search(repository, branch, fragment, searchType, maxEntryCount, page);
            }
            @Override
@@ -298,11 +328,18 @@
        tableModel.entries.clear();
        tableModel.entries.addAll(entries);
        tableModel.fireTableDataChanged();
        setTitle(Translation.get("gb.search") + ": " + fragment + " (" + entries.size() + ")");
        setTitle(Translation.get("gb.search") + ": " + fragment + " (" + entries.size()
                + (page > 0 ? (", pg " + (page + 1)) : "") + ")");
        header.setText(getTitle());
        if (pack) {
            Utils.packColumns(table, Utils.MARGIN);
        }
        table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true)));
        // update pagination buttons
        int maxHits = (Integer) maxHitsSelector.getSelectedItem();
        next.setEnabled(entries.size() == maxHits);
        prev.setEnabled(page > 0);
    }
    protected SyndicatedEntryModel getSelectedSyndicatedEntry() {
src/com/gitblit/utils/SyndicationUtils.java
@@ -100,7 +100,7 @@
            content.setType(entryModel.contentType);
            content.setValue(entryModel.content);
            entry.setDescription(content);
            entries.add(entry);
        }
        feed.setEntries(entries);
@@ -123,17 +123,22 @@
     * @param numberOfEntries
     *            the number of entries to retrieve. if <= 0 the server default
     *            is used.
     * @param page
     *            0-indexed. used to paginate the results.
     * @param username
     * @param password
     * @return a list of SyndicationModel entries
     * @throws {@link IOException}
     */
    public static List<SyndicatedEntryModel> readFeed(String url, String repository, String branch,
            int numberOfEntries, String username, char[] password) throws IOException {
            int numberOfEntries, int page, String username, char[] password) throws IOException {
        // build feed url
        List<String> parameters = new ArrayList<String>();
        if (numberOfEntries > 0) {
            parameters.add("l=" + numberOfEntries);
        }
        if (page > 0) {
            parameters.add("pg=" + page);
        }
        if (!StringUtils.isEmpty(branch)) {
            parameters.add("h=" + branch);
@@ -155,6 +160,8 @@
     * @param numberOfEntries
     *            the number of entries to retrieve. if <= 0 the server default
     *            is used.
     * @param page
     *            0-indexed. used to paginate the results.
     * @param username
     * @param password
     * @return a list of SyndicationModel entries
@@ -162,13 +169,16 @@
     */
    public static List<SyndicatedEntryModel> readSearchFeed(String url, String repository,
            String branch, String fragment, Constants.SearchType searchType, int numberOfEntries,
            String username, char[] password) throws IOException {
            int page, String username, char[] password) throws IOException {
        // determine parameters
        List<String> parameters = new ArrayList<String>();
        parameters.add("s=" + StringUtils.encodeURL(fragment));
        if (numberOfEntries > 0) {
            parameters.add("l=" + numberOfEntries);
        }
        if (page > 0) {
            parameters.add("pg=" + page);
        }
        if (!StringUtils.isEmpty(branch)) {
            parameters.add("h=" + branch);
        }
src/com/gitblit/wicket/WicketUtils.java
@@ -284,9 +284,9 @@
            return newObjectParameter(repositoryName, objectId);
        }
        if (StringUtils.isEmpty(objectId)) {
            return new PageParameters("r=" + repositoryName + ",page=" + pageNumber);
            return new PageParameters("r=" + repositoryName + ",pg=" + pageNumber);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",page=" + pageNumber);
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",pg=" + pageNumber);
    }
    public static PageParameters newHistoryPageParameter(String repositoryName, String objectId,
@@ -295,10 +295,10 @@
            return newObjectParameter(repositoryName, objectId);
        }
        if (StringUtils.isEmpty(objectId)) {
            return new PageParameters("r=" + repositoryName + ",f=" + path + ",page=" + pageNumber);
            return new PageParameters("r=" + repositoryName + ",f=" + path + ",pg=" + pageNumber);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + objectId + ",f=" + path
                + ",page=" + pageNumber);
                + ",pg=" + pageNumber);
    }
    public static PageParameters newBlobDiffParameter(String repositoryName, String baseCommitId,
@@ -323,10 +323,10 @@
            String search, Constants.SearchType type, int pageNumber) {
        if (StringUtils.isEmpty(commitId)) {
            return new PageParameters("r=" + repositoryName + ",s=" + search + ",st=" + type.name()
                    + ",page=" + pageNumber);
                    + ",pg=" + pageNumber);
        }
        return new PageParameters("r=" + repositoryName + ",h=" + commitId + ",s=" + search
                + ",st=" + type.name() + ",page=" + pageNumber);
                + ",st=" + type.name() + ",pg=" + pageNumber);
    }
    public static String getRepositoryName(PageParameters params) {
@@ -355,7 +355,7 @@
    public static int getPage(PageParameters params) {
        // index from 1
        return params.getInt("page", 1);
        return params.getInt("pg", 1);
    }
    public static String getUsername(PageParameters params) {
tests/com/gitblit/tests/SyndicationUtilsTest.java
@@ -18,7 +18,9 @@
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import junit.framework.TestCase;
@@ -51,16 +53,24 @@
    }
    public void testFeedRead() throws Exception {
        List<SyndicatedEntryModel> feed = SyndicationUtils.readFeed("https://localhost:8443",
                "ticgit.git", "master", 5, "admin", "admin".toCharArray());
        assertTrue(feed != null);
        assertTrue(feed.size() > 0);
        assertEquals(5, feed.size());
        Set<String> links = new HashSet<String>();
        for (int i = 0; i < 2; i++) {
            List<SyndicatedEntryModel> feed = SyndicationUtils.readFeed("https://localhost:8443",
                    "ticgit.git", "master", 5, i, "admin", "admin".toCharArray());
            assertTrue(feed != null);
            assertTrue(feed.size() > 0);
            assertEquals(5, feed.size());
            for (SyndicatedEntryModel entry : feed) {
                links.add(entry.link);
            }
        }
        // confirm we have 10 unique commits
        assertEquals("Feed pagination failed", 10, links.size());
    }
    public void testSearchFeedRead() throws Exception {
        List<SyndicatedEntryModel> feed = SyndicationUtils.readSearchFeed("https://localhost:8443",
                "ticgit.git", null, "documentation", null, 5, "admin", "admin".toCharArray());
                "ticgit.git", null, "documentation", null, 5, 0, "admin", "admin".toCharArray());
        assertTrue(feed != null);
        assertTrue(feed.size() > 0);
        assertEquals(2, feed.size());