Support pagination in RSS feeds. Standardize pg as page parameter.
| | |
| | | <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>
|
| | |
| | |
|
| | | 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
|
| | |
|
| | |
| | | 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"))) {
|
| | |
| | | } 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);
|
| | |
| | | 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>();
|
| | |
| | | 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;
|
| | |
| | |
|
| | | private JComboBox authorSelector;
|
| | |
|
| | | private int page;
|
| | |
|
| | | private JButton prev;
|
| | |
|
| | | private JButton next;
|
| | |
|
| | | public FeedsPanel(GitblitClient gitblit) {
|
| | | super();
|
| | | this.gitblit = gitblit;
|
| | |
| | | }
|
| | |
|
| | | 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);
|
| | | }
|
| | | });
|
| | |
|
| | |
| | | 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);
|
| | |
| | | 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;
|
| | | }
|
| | |
|
| | |
| | | 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);
|
| | | }
|
| | | 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) {
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | // update pagination buttons
|
| | | next.setEnabled(tableModel.entries.size() > 0);
|
| | | prev.setEnabled(page > 0);
|
| | | }
|
| | |
|
| | | private void updateAuthors() {
|
| | | String repository = ALL;
|
| | | if (repositorySelector.getSelectedIndex() > -1) {
|
| | |
| | | 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
|
| | |
| | | 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);
|
| | | }
|
| | | }
|
| | |
| | | }
|
| | |
|
| | | 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 {
|
| | |
| | | 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;
|
| | |
| | |
|
| | | private JComboBox maxHitsSelector;
|
| | |
|
| | | private int page;
|
| | |
|
| | | private JButton prev;
|
| | |
|
| | | private JButton next;
|
| | |
|
| | | public SearchDialog(GitblitClient gitblit) {
|
| | | super();
|
| | | this.gitblit = gitblit;
|
| | |
| | |
|
| | | 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);
|
| | | }
|
| | | });
|
| | |
|
| | |
| | | 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);
|
| | | }
|
| | | });
|
| | |
|
| | |
| | | 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);
|
| | |
| | | }
|
| | | }
|
| | |
|
| | | 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;
|
| | |
| | | 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
|
| | |
| | | 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() {
|
| | |
| | | * @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);
|
| | |
| | | * @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
|
| | |
| | | */
|
| | | 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);
|
| | | }
|
| | |
| | | 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,
|
| | |
| | | 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,
|
| | |
| | | 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) {
|
| | |
| | |
|
| | | 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) {
|
| | |
| | | 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;
|
| | |
|
| | |
| | | }
|
| | |
|
| | | public void testFeedRead() throws Exception {
|
| | | 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, "admin", "admin".toCharArray());
|
| | | "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());
|