From e33b91aa4d43246ad62832e66e2acfad3dfb3608 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 4 Nov 2011 22:28:32 -0400 Subject: [PATCH] Support pagination in RSS feeds. Standardize pg as page parameter. --- docs/02_rpc.mkd | 3 +- src/com/gitblit/SyndicationServlet.java | 14 +++- src/com/gitblit/client/FeedsPanel.java | 70 ++++++++++++++----- src/com/gitblit/client/GitblitClient.java | 16 ++--- src/com/gitblit/client/SearchDialog.java | 49 +++++++++++-- src/com/gitblit/utils/SyndicationUtils.java | 16 ++++- src/com/gitblit/wicket/WicketUtils.java | 14 ++-- .../gitblit/tests/SyndicationUtilsTest.java | 22 ++++-- 8 files changed, 151 insertions(+), 53 deletions(-) diff --git a/docs/02_rpc.mkd b/docs/02_rpc.mkd index bb58d68f..d1bb1f33 100644 --- a/docs/02_rpc.mkd +++ b/docs/02_rpc.mkd @@ -42,6 +42,7 @@ The Gitblit API includes methods for retrieving and interpreting RSS feeds. The repositoryrequiredrepository name is part of the url (see examples below) h=optional
default: HEADstarting branch, ref, or commit id l=optional
default: web.syndicationEntriesmaximum return count +pg=optional
default: 0page number for paging
(offset into history = pagenumber*maximum return count) search query s=requiredsearch string st=optional
default: COMMITsearch type @@ -51,7 +52,7 @@ The Gitblit API includes methods for retrieving and interpreting RSS feeds. The 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 diff --git a/src/com/gitblit/SyndicationServlet.java b/src/com/gitblit/SyndicationServlet.java index 128df43f..39e37ca9 100644 --- a/src/com/gitblit/SyndicationServlet.java +++ b/src/com/gitblit/SyndicationServlet.java @@ -129,6 +129,7 @@ public class SyndicationServlet extends HttpServlet { 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 @@ public class SyndicationServlet extends HttpServlet { } 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 @@ public class SyndicationServlet extends HttpServlet { List 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> allRefs = JGitUtils.getAllRefs(repository); List entries = new ArrayList(); diff --git a/src/com/gitblit/client/FeedsPanel.java b/src/com/gitblit/client/FeedsPanel.java index a8094f80..97764db7 100644 --- a/src/com/gitblit/client/FeedsPanel.java +++ b/src/com/gitblit/client/FeedsPanel.java @@ -18,6 +18,7 @@ package com.gitblit.client; 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 @@ public abstract class FeedsPanel extends JPanel { private JComboBox authorSelector; + private int page; + + private JButton prev; + + private JButton next; + public FeedsPanel(GitblitClient gitblit) { super(); this.gitblit = gitblit; @@ -83,10 +90,29 @@ public abstract class FeedsPanel extends JPanel { } 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 @@ public abstract class FeedsPanel extends JPanel { 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 @@ public abstract class FeedsPanel extends JPanel { 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 @@ public abstract class FeedsPanel extends JPanel { 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 uniqueRepositories = new HashSet(); - for (SyndicatedEntryModel entry : tableModel.entries) { - uniqueRepositories.add(entry.repository); - } + table.scrollRectToVisible(new Rectangle(table.getCellRect(0, 0, true))); - // repositories - List sortedRespositories = new ArrayList(uniqueRepositories); - StringUtils.sortRepositorynames(sortedRespositories); - repositoryChoices.removeAllElements(); - repositoryChoices.addElement(ALL); - for (String repo : sortedRespositories) { - repositoryChoices.addElement(repo); + if (page == 0) { + // determine unique repositories + Set uniqueRepositories = new HashSet(); + for (SyndicatedEntryModel entry : tableModel.entries) { + uniqueRepositories.add(entry.repository); + } + + // repositories + List sortedRespositories = new ArrayList(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() { diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java index e8460f5f..588b6d80 100644 --- a/src/com/gitblit/client/GitblitClient.java +++ b/src/com/gitblit/client/GitblitClient.java @@ -101,13 +101,7 @@ public class GitblitClient implements Serializable { 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 @@ public class GitblitClient implements Serializable { return availableFeeds; } - public List refreshSubscribedFeeds() throws IOException { + public List refreshSubscribedFeeds(int page) throws IOException { Set allEntries = new HashSet(); if (reg.feeds.size() > 0) { for (FeedModel feed : reg.feeds) { feed.lastRefreshDate = feed.currentRefreshDate; feed.currentRefreshDate = new Date(); List 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 class GitblitClient implements Serializable { } public List 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 refreshFederationRegistrations() throws IOException { diff --git a/src/com/gitblit/client/SearchDialog.java b/src/com/gitblit/client/SearchDialog.java index 2f45611e..5dbea789 100644 --- a/src/com/gitblit/client/SearchDialog.java +++ b/src/com/gitblit/client/SearchDialog.java @@ -18,6 +18,7 @@ package com.gitblit.client; 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 @@ public class SearchDialog extends JFrame { private JComboBox maxHitsSelector; + private int page; + + private JButton prev; + + private JButton next; + public SearchDialog(GitblitClient gitblit) { super(); this.gitblit = gitblit; @@ -88,10 +95,28 @@ public class SearchDialog extends JFrame { 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 @@ public class SearchDialog extends JFrame { 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 @@ public class SearchDialog extends JFrame { 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 @@ public class SearchDialog extends JFrame { } } - 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 @@ public class SearchDialog extends JFrame { final String fragment = searchFragment.getText(); final int maxEntryCount = maxHitsSelector.getSelectedIndex() > -1 ? ((Integer) maxHitsSelector .getSelectedItem()) : -1; + if (StringUtils.isEmpty(fragment)) { return; } SwingWorker, Void> worker = new SwingWorker, Void>() { @Override protected List 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 @@ public class SearchDialog extends JFrame { 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() { diff --git a/src/com/gitblit/utils/SyndicationUtils.java b/src/com/gitblit/utils/SyndicationUtils.java index 4ba56223..6919cd24 100644 --- a/src/com/gitblit/utils/SyndicationUtils.java +++ b/src/com/gitblit/utils/SyndicationUtils.java @@ -100,7 +100,7 @@ public class SyndicationUtils { content.setType(entryModel.contentType); content.setValue(entryModel.content); entry.setDescription(content); - + entries.add(entry); } feed.setEntries(entries); @@ -123,18 +123,23 @@ public class SyndicationUtils { * @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 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 parameters = new ArrayList(); 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 @@ public class SyndicationUtils { * @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 class SyndicationUtils { */ public static List 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 parameters = new ArrayList(); 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); } diff --git a/src/com/gitblit/wicket/WicketUtils.java b/src/com/gitblit/wicket/WicketUtils.java index f47663ca..37b44472 100644 --- a/src/com/gitblit/wicket/WicketUtils.java +++ b/src/com/gitblit/wicket/WicketUtils.java @@ -284,9 +284,9 @@ public class WicketUtils { 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 @@ public class WicketUtils { 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 @@ public class WicketUtils { 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 class WicketUtils { 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) { diff --git a/tests/com/gitblit/tests/SyndicationUtilsTest.java b/tests/com/gitblit/tests/SyndicationUtilsTest.java index 98bdd4bf..e0a32bf3 100644 --- a/tests/com/gitblit/tests/SyndicationUtilsTest.java +++ b/tests/com/gitblit/tests/SyndicationUtilsTest.java @@ -18,7 +18,9 @@ package com.gitblit.tests; 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 class SyndicationUtilsTest extends TestCase { } public void testFeedRead() throws Exception { - List 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 links = new HashSet(); + for (int i = 0; i < 2; i++) { + List 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 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()); -- 2.39.5