From 4cac0d3a0952078ce8ebd3fdedbefeb7803ac080 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 28 Oct 2011 21:24:02 -0400 Subject: [PATCH] Added basic rss subscriptions to Manager --- build.xml | 2 + docs/00_index.mkd | 1 + docs/04_releases.mkd | 1 + resources/bullet_feed.png | Bin 0 -> 448 bytes src/com/gitblit/build/Build.java | 2 + src/com/gitblit/client/DateCellRenderer.java | 21 +- src/com/gitblit/client/GitblitClient.java | 135 +++++++++- src/com/gitblit/client/GitblitManager.java | 12 +- src/com/gitblit/client/GitblitPanel.java | 233 ++++++++++++++++-- .../gitblit/client/GitblitRegistration.java | 2 + src/com/gitblit/client/NameRenderer.java | 46 +++- .../gitblit/client/RegistrationsDialog.java | 2 +- src/com/gitblit/client/StatusPanel.java | 2 +- .../client/SyndicatedEntryTableModel.java | 121 +++++++++ src/com/gitblit/client/Utils.java | 8 +- src/com/gitblit/models/RepositoryModel.java | 1 + .../gitblit/models/SyndicatedEntryModel.java | 59 +++++ src/com/gitblit/utils/SyndicationUtils.java | 40 ++- .../gitblit/wicket/GitBlitWebApp.properties | 5 +- .../gitblit/tests/SyndicationUtilsTest.java | 11 +- 20 files changed, 640 insertions(+), 64 deletions(-) create mode 100644 resources/bullet_feed.png create mode 100644 src/com/gitblit/client/SyndicatedEntryTableModel.java create mode 100644 src/com/gitblit/models/SyndicatedEntryModel.java diff --git a/build.xml b/build.xml index cdb0ac39..5c3494bd 100644 --- a/build.xml +++ b/build.xml @@ -448,6 +448,8 @@ + + diff --git a/docs/00_index.mkd b/docs/00_index.mkd index 09850752..1080f095 100644 --- a/docs/00_index.mkd +++ b/docs/00_index.mkd @@ -52,6 +52,7 @@ Gitblit requires a Java 6 Runtime Environment (JRE) or a Java 6 Development Kit - fixed: Gitblit can now browse the Linux kernel repository (issue 25) - fixed: Gitblit now runs on Servlet 3.0 webservers (e.g. Tomcat 7, Jetty 8) (issue 23) - fixed: Set the RSS content type of syndication feeds for Firefox 4 (issue 22) +- fixed: RSS feeds are now properly encoded to UTF-8 - fixed: Null pointer exception if did not set federation strategy (issue 20) - fixed: Gitblit GO allows SSL renegotiation if running on Java 1.6.0_22 or later - updated: MarkdownPapers 1.2.4 diff --git a/docs/04_releases.mkd b/docs/04_releases.mkd index 50612e61..778fd0e8 100644 --- a/docs/04_releases.mkd +++ b/docs/04_releases.mkd @@ -26,6 +26,7 @@ - fixed: Gitblit can now browse the Linux kernel repository (issue 25) - fixed: Gitblit now runs on Servlet 3.0 webservers (e.g. Tomcat 7, Jetty 8) (issue 23) - fixed: Set the RSS content type of syndication feeds for Firefox 4 (issue 22) +- fixed: RSS feeds are now properly encoded to UTF-8 - fixed: Null pointer exception if did not set federation strategy (issue 20) - fixed: Gitblit GO allows SSL renegotiation if running on Java 1.6.0_22 or later - updated: MarkdownPapers 1.2.4 diff --git a/resources/bullet_feed.png b/resources/bullet_feed.png new file mode 100644 index 0000000000000000000000000000000000000000..c6e5f241b98a44184d690008a5d7598f3ef176d2 GIT binary patch literal 448 zcmV;x0YCnUP)QCCb?>Vn8c#^)7 zba0n@PwqM2`Q7`wSJ}2rBg3G<(J&z34Ib78PAsj%*czOZDNhe)trUh~p7zAjb2R~$ zeh>Fe%s{#11X62)*(7to1sDdIMW4Xf9XyM@C=h#ZKrkiZ}R;DUaG-4WQI zhRIFn?1j4{7~c@UlY6-0^kXV40wyKrB0NYT-Ogz 10 * 24 * 60 * 60 * 1000L) { + String tmp = dateString; + dateString = title; + title = tmp; + } + this.setText(title); + this.setToolTipText(dateString); } return this; } diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java index 9f4dd3e6..ccde45a4 100644 --- a/src/com/gitblit/client/GitblitClient.java +++ b/src/com/gitblit/client/GitblitClient.java @@ -19,8 +19,10 @@ import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import com.gitblit.GitBlitException.ForbiddenException; import com.gitblit.GitBlitException.NotAllowedException; @@ -31,8 +33,11 @@ import com.gitblit.models.FederationModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.ServerSettings; import com.gitblit.models.ServerStatus; +import com.gitblit.models.SyndicatedEntryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.RpcUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.utils.SyndicationUtils; /** * GitblitClient is a object that retrieves data from a Gitblit server, caches @@ -45,6 +50,8 @@ public class GitblitClient implements Serializable { private static final long serialVersionUID = 1L; + protected final GitblitRegistration reg; + public final String url; public final String account; @@ -63,22 +70,35 @@ public class GitblitClient implements Serializable { private final List federationRegistrations; + private final List syndicatedEntries; + private ServerStatus status; - public GitblitClient(String url, String account, char[] password) { - this.url = url; - this.account = account; - this.password = password; + public GitblitClient(GitblitRegistration reg) { + this.reg = reg; + this.url = reg.url; + this.account = reg.account; + this.password = reg.password; this.allUsers = new ArrayList(); this.allRepositories = new ArrayList(); this.federationRegistrations = new ArrayList(); + this.syndicatedEntries = new ArrayList(); } public void login() throws IOException { refreshRepositories(); try { + // RSS feeds may be disabled by server + refreshSubscribedFeeds(); + } catch (IOException e) { + e.printStackTrace(); + } + + try { + // credentials may not have administrator access + // or server may have disabled rpc management refreshUsers(); refreshSettings(); allowManagement = true; @@ -91,6 +111,8 @@ public class GitblitClient implements Serializable { } try { + // credentials may not have administrator access + // or server may have disabled rpc administration refreshStatus(); allowAdministration = true; } catch (UnauthorizedException e) { @@ -133,6 +155,7 @@ public class GitblitClient implements Serializable { allRepositories.clear(); allRepositories.addAll(repositories.values()); Collections.sort(allRepositories); + updateSubscribedStates(); return allRepositories; } @@ -153,6 +176,110 @@ public class GitblitClient implements Serializable { return status; } + public List refreshSubscribedFeeds() throws IOException { + Set allFeeds = new HashSet(); + if (reg.feeds != null && reg.feeds.size() > 0) { + for (String feed : reg.feeds) { + String[] values = feed.split(":"); + String repository = values[0]; + String branch = null; + if (values.length > 1) { + branch = values[1]; + } + List list = SyndicationUtils.readFeed(url, repository, + branch, -1, account, password); + allFeeds.addAll(list); + } + } + syndicatedEntries.clear(); + syndicatedEntries.addAll(allFeeds); + Collections.sort(syndicatedEntries); + return syndicatedEntries; + } + + private void updateSubscribedStates() { + if (reg.feeds != null) { + Set subscribedRepositories = new HashSet(); + for (String feed : reg.feeds) { + if (feed.indexOf(':') > -1) { + // strip branch + subscribedRepositories.add(feed.substring(0, feed.indexOf(':')).toLowerCase()); + } else { + // default branch + subscribedRepositories.add(feed.toLowerCase()); + } + } + // set subscribed flag + for (RepositoryModel repository : allRepositories) { + repository.subscribed = subscribedRepositories.contains(repository.name + .toLowerCase()); + } + } + } + + public List getSyndicatedEntries() { + return syndicatedEntries; + } + + public boolean isSubscribed(RepositoryModel repository, String branch) { + if (reg.feeds != null && reg.feeds.size() > 0) { + for (String feed : reg.feeds) { + String[] values = feed.split(":"); + String repositoryName = values[0]; + if (repository.name.equalsIgnoreCase(repositoryName)) { + return true; + } + // TODO check branch subscriptions + String branchName = null; + if (values.length > 1) { + branchName = values[1]; + } + } + } + return false; + } + + public boolean subscribe(RepositoryModel repository, String branch) { + String feed = repository.name; + if (!StringUtils.isEmpty(branch)) { + feed += ":" + branch; + } + if (reg.feeds == null) { + reg.feeds = new ArrayList(); + } + reg.feeds.add(feed); + updateSubscribedStates(); + return true; + } + + public boolean unsubscribe(RepositoryModel repository, String branch) { + String feed = repository.name; + if (!StringUtils.isEmpty(branch)) { + feed += ":" + branch; + } + reg.feeds.remove(feed); + if (syndicatedEntries.size() > 0) { + List toRemove = new ArrayList(); + for (SyndicatedEntryModel model : syndicatedEntries) { + if (model.repository.equalsIgnoreCase(repository.name)) { + boolean emptyUnsubscribeBranch = StringUtils.isEmpty(branch); + boolean emptyFromBranch = StringUtils.isEmpty(model.branch); + if (emptyUnsubscribeBranch && emptyFromBranch) { + // default branch, remove + toRemove.add(model); + } else if (!emptyUnsubscribeBranch && !emptyFromBranch) { + if (model.branch.equals(branch)) { + // specific branch, remove + toRemove.add(model); + } + } + } + } + } + updateSubscribedStates(); + return true; + } + public List refreshFederationRegistrations() throws IOException { List list = RpcUtils.getFederationRegistrations(url, account, password); federationRegistrations.clear(); diff --git a/src/com/gitblit/client/GitblitManager.java b/src/com/gitblit/client/GitblitManager.java index f16616a3..a337040f 100644 --- a/src/com/gitblit/client/GitblitManager.java +++ b/src/com/gitblit/client/GitblitManager.java @@ -31,6 +31,7 @@ import java.net.ConnectException; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -204,11 +205,11 @@ public class GitblitManager extends JFrame implements RegistrationsDialog.Regist return; } } - + // login setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); final GitblitRegistration registration = reg; - final GitblitPanel panel = new GitblitPanel(registration); + final GitblitPanel panel = new GitblitPanel(registration, this); SwingWorker worker = new SwingWorker() { @Override @@ -303,6 +304,10 @@ public class GitblitManager extends JFrame implements RegistrationsDialog.Regist password = new String(Base64.decode(pw)).toCharArray(); } GitblitRegistration reg = new GitblitRegistration(server, url, account, password); + String[] feeds = config.getStringList("servers", server, "feeds"); + if (feeds != null) { + reg.feeds = new ArrayList(Arrays.asList(feeds)); + } reg.lastLogin = lastLogin; registrations.put(reg.name, reg); } @@ -333,6 +338,9 @@ public class GitblitManager extends JFrame implements RegistrationsDialog.Regist if (reg.lastLogin != null) { config.setString("servers", reg.name, "lastLogin", dateFormat.format(reg.lastLogin)); } + if (reg.feeds != null) { + config.setStringList("servers", reg.name, "feeds", reg.feeds); + } config.save(); return true; } catch (Throwable t) { diff --git a/src/com/gitblit/client/GitblitPanel.java b/src/com/gitblit/client/GitblitPanel.java index 03ddfb3a..198b24b3 100644 --- a/src/com/gitblit/client/GitblitPanel.java +++ b/src/com/gitblit/client/GitblitPanel.java @@ -58,6 +58,7 @@ import com.gitblit.Constants.RpcRequest; import com.gitblit.client.ClosableTabComponent.CloseTabListener; 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; @@ -76,6 +77,8 @@ public class GitblitPanel extends JPanel implements CloseTabListener { private final Insets insets = new Insets(margin, margin, margin, margin); + private final RegistrationsDialog.RegistrationListener listener; + private GitblitClient gitblit; private JTabbedPane tabs; @@ -96,14 +99,6 @@ public class GitblitPanel extends JPanel implements CloseTabListener { private JButton delRepository; - private NameRenderer nameRenderer; - - private IndicatorsRenderer typeRenderer; - - private DefaultTableCellRenderer ownerRenderer; - - private DefaultTableCellRenderer sizeRenderer; - private TableRowSorter defaultRepositoriesSorter; private TableRowSorter defaultUsersSorter; @@ -120,15 +115,19 @@ public class GitblitPanel extends JPanel implements CloseTabListener { private StatusPanel statusPanel; - public GitblitPanel(GitblitRegistration reg) { - this(reg.url, reg.account, reg.password); - } + private SyndicatedEntryTableModel syndicationModel; - public GitblitPanel(String url, String account, char[] password) { - this.gitblit = new GitblitClient(url, account, password); + private HeaderPanel feedsHeader; + + private JTable syndicationEntriesTable; + + public GitblitPanel(GitblitRegistration reg, RegistrationsDialog.RegistrationListener listener) { + this.gitblit = new GitblitClient(reg); + this.listener = listener; tabs = new JTabbedPane(JTabbedPane.BOTTOM); tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel()); + tabs.addTab(Translation.get("gb.recentCommits"), createFeedsPanel()); tabs.addTab(Translation.get("gb.users"), createUsersPanel()); tabs.addTab(Translation.get("gb.settings"), createSettingsPanel()); tabs.addTab(Translation.get("gb.status"), createStatusPanel()); @@ -183,20 +182,28 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } }); - nameRenderer = new NameRenderer(); - typeRenderer = new IndicatorsRenderer(); + final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "..."); + subscribeRepository.setEnabled(false); + subscribeRepository.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + subscribeRepository(getSelectedRepositories().get(0)); + } + }); + + NameRenderer nameRenderer = new NameRenderer(true); + IndicatorsRenderer typeRenderer = new IndicatorsRenderer(); - sizeRenderer = new DefaultTableCellRenderer(); + DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer(); sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT); sizeRenderer.setForeground(new Color(0, 0x80, 0)); - ownerRenderer = new DefaultTableCellRenderer(); + DefaultTableCellRenderer ownerRenderer = new DefaultTableCellRenderer(); ownerRenderer.setForeground(Color.gray); ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER); repositoriesModel = new RepositoriesTableModel(); defaultRepositoriesSorter = new TableRowSorter(repositoriesModel); - repositoriesTable = Utils.newTable(repositoriesModel); + repositoriesTable = Utils.newTable(repositoriesModel, Utils.DATE_FORMAT); repositoriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8); repositoriesTable.setRowSorter(defaultRepositoriesSorter); repositoriesTable.getRowSorter().toggleSortOrder( @@ -217,6 +224,7 @@ public class GitblitPanel extends JPanel implements CloseTabListener { 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); @@ -264,6 +272,7 @@ public class GitblitPanel extends JPanel implements CloseTabListener { repositoryControls.add(createRepository); repositoryControls.add(editRepository); repositoryControls.add(delRepository); + repositoryControls.add(subscribeRepository); JPanel repositoriesPanel = new JPanel(new BorderLayout(margin, margin)) { @@ -292,6 +301,94 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } } + 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(); + } + }); + + JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + controls.add(refreshFeeds); + controls.add(viewCommit); + controls.add(viewCommitDiff); + controls.add(viewTree); + + NameRenderer nameRenderer = new NameRenderer(); + syndicationModel = new SyndicatedEntryTableModel(); + feedsHeader = new HeaderPanel(Translation.get("gb.recentCommits"), "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); + + 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)) { + + private static final long serialVersionUID = 1L; + + @Override + public Insets getInsets() { + return insets; + } + }; + panel.add(feedsHeader, BorderLayout.NORTH); + panel.add(new JScrollPane(syndicationEntriesTable), BorderLayout.CENTER); + panel.add(controls, BorderLayout.SOUTH); + return panel; + } + private JPanel createUsersPanel() { JButton refreshUsers = new JButton(Translation.get("gb.refresh")); refreshUsers.addActionListener(new ActionListener() { @@ -323,9 +420,10 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } }); + NameRenderer nameRenderer = new NameRenderer(); usersModel = new UsersTableModel(); defaultUsersSorter = new TableRowSorter(usersModel); - usersTable = Utils.newTable(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); @@ -414,10 +512,11 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } }); + NameRenderer nameRenderer = new NameRenderer(); final SettingPanel settingPanel = new SettingPanel(); settingsModel = new SettingsTableModel(); defaultSettingsSorter = new TableRowSorter(settingsModel); - settingsTable = Utils.newTable(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); @@ -509,6 +608,9 @@ public class GitblitPanel extends JPanel implements CloseTabListener { updateRepositoriesTable(); Utils.packColumns(repositoriesTable, 5); + updateFeedsTable(); + Utils.packColumns(syndicationEntriesTable, 5); + if (gitblit.allowManagement()) { updateUsersTable(); } else { @@ -550,6 +652,14 @@ public class GitblitPanel extends JPanel implements CloseTabListener { + gitblit.getRepositories().size() + ")"); } + private void updateFeedsTable() { + syndicationModel.entries.clear(); + syndicationModel.entries.addAll(gitblit.getSyndicatedEntries()); + syndicationModel.fireTableDataChanged(); + feedsHeader.setText(Translation.get("gb.recentCommits") + " (" + + gitblit.getSyndicatedEntries().size() + ")"); + } + private void updateUsersTable() { usersModel.list.clear(); usersModel.list.addAll(gitblit.getUsers()); @@ -811,6 +921,89 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } } + protected void subscribeRepository(final RepositoryModel repository) { + if (repository == null) { + return; + } + // TODO this is lame. need better ui. + if (gitblit.isSubscribed(repository, null)) { + // unsubscribe + String msg = MessageFormat.format("Do you want to unsubscribe from {0}?", + repository.name); + String[] options = { "no", "yes" }; + int result = JOptionPane.showOptionDialog(GitblitPanel.this, msg, "Unsubscribe?", + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, + options[0]); + if (result == 1) { + if (gitblit.unsubscribe(repository, null)) { + updateFeedsTable(); + updateRepositoriesTable(); + listener.saveRegistration(repository.name, gitblit.reg); + } + } + } else { + // subscribe + String msg = MessageFormat.format("Do you want to subscribe to {0}?", repository.name); + String[] options = { "no", "yes" }; + int result = JOptionPane.showOptionDialog(GitblitPanel.this, msg, "Subscribe?", + JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, + options[0]); + if (result == 1) { + if (gitblit.subscribe(repository, null)) { + updateRepositoriesTable(); + listener.saveRegistration(repository.name, gitblit.reg); + } + } + } + } + + 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 diff --git a/src/com/gitblit/client/GitblitRegistration.java b/src/com/gitblit/client/GitblitRegistration.java index 0463c12b..cbd4324d 100644 --- a/src/com/gitblit/client/GitblitRegistration.java +++ b/src/com/gitblit/client/GitblitRegistration.java @@ -17,6 +17,7 @@ package com.gitblit.client; import java.io.Serializable; import java.util.Date; +import java.util.List; import com.gitblit.utils.StringUtils; @@ -36,6 +37,7 @@ public class GitblitRegistration implements Serializable, Comparable feeds; public GitblitRegistration(String name, String url, String account, char[] password) { this.url = url; diff --git a/src/com/gitblit/client/NameRenderer.java b/src/com/gitblit/client/NameRenderer.java index 5b1a1736..ce042555 100644 --- a/src/com/gitblit/client/NameRenderer.java +++ b/src/com/gitblit/client/NameRenderer.java @@ -18,9 +18,12 @@ package com.gitblit.client; import java.awt.Color; import java.awt.Component; +import javax.swing.ImageIcon; import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; +import com.gitblit.models.RepositoryModel; + /** * Repository name cell renderer. This renderer shows the group name in a gray * color and accentuates the repository name in a cornflower blue color. @@ -34,11 +37,24 @@ public class NameRenderer extends DefaultTableCellRenderer { final String groupSpan; + private final boolean displayIcon; + + private final ImageIcon blankIcon; + + private final ImageIcon subscribedIcon; + public NameRenderer() { - this(Color.gray, new Color(0x00, 0x69, 0xD6)); + this(false); + } + + public NameRenderer(boolean showIcon) { + this(Color.gray, new Color(0x00, 0x69, 0xD6), showIcon); } - public NameRenderer(Color group, Color repo) { + private NameRenderer(Color group, Color repo, boolean showIcon) { + blankIcon = new ImageIcon(getClass().getResource("/blank.png")); + subscribedIcon = new ImageIcon(getClass().getResource("/bullet_feed.png")); + displayIcon = showIcon; groupSpan = ""; setForeground(repo); } @@ -55,14 +71,26 @@ public class NameRenderer extends DefaultTableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - String name = value.toString(); - int lastSlash = name.lastIndexOf('/'); - if (!isSelected && lastSlash > -1) { - String group = name.substring(0, lastSlash + 1); - String repo = name.substring(lastSlash + 1); - setText("" + groupSpan + group + "" + repo); + if (value instanceof RepositoryModel) { + RepositoryModel model = (RepositoryModel) value; + String name = value.toString(); + int lastSlash = name.lastIndexOf('/'); + if (!isSelected && lastSlash > -1) { + String group = name.substring(0, lastSlash + 1); + String repo = name.substring(lastSlash + 1); + setText("" + groupSpan + group + "" + repo); + } else { + this.setText(name); + } + if (displayIcon) { + if (model.subscribed) { + setIcon(subscribedIcon); + } else { + setIcon(blankIcon); + } + } } else { - this.setText(name); + this.setText(value.toString()); } return this; } diff --git a/src/com/gitblit/client/RegistrationsDialog.java b/src/com/gitblit/client/RegistrationsDialog.java index 0b87bb47..d40e3335 100644 --- a/src/com/gitblit/client/RegistrationsDialog.java +++ b/src/com/gitblit/client/RegistrationsDialog.java @@ -92,7 +92,7 @@ public class RegistrationsDialog extends JDialog { private void initialize() { NameRenderer nameRenderer = new NameRenderer(); model = new RegistrationsTableModel(registrations); - registrationsTable = Utils.newTable(model); + registrationsTable = Utils.newTable(model, Utils.DATE_FORMAT); registrationsTable.setRowHeight(nameRenderer.getFont().getSize() + 8); String id = registrationsTable diff --git a/src/com/gitblit/client/StatusPanel.java b/src/com/gitblit/client/StatusPanel.java index b85d87af..797ae9b9 100644 --- a/src/com/gitblit/client/StatusPanel.java +++ b/src/com/gitblit/client/StatusPanel.java @@ -92,7 +92,7 @@ public class StatusPanel extends JPanel { fieldsPanel.add(createFieldPanel("gb.heapMaximum", heapMaximum)); model = new PropertiesTableModel(); - JTable propertiesTable = Utils.newTable(model); + JTable propertiesTable = Utils.newTable(model, Utils.DATE_FORMAT); String name = propertiesTable.getColumnName(PropertiesTableModel.Columns.Name.ordinal()); NameRenderer nameRenderer = new NameRenderer(); propertiesTable.setRowHeight(nameRenderer.getFont().getSize() + 8); diff --git a/src/com/gitblit/client/SyndicatedEntryTableModel.java b/src/com/gitblit/client/SyndicatedEntryTableModel.java new file mode 100644 index 00000000..4f25c9ba --- /dev/null +++ b/src/com/gitblit/client/SyndicatedEntryTableModel.java @@ -0,0 +1,121 @@ +/* + * 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.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import com.gitblit.models.SyndicatedEntryModel; + +/** + * Table model of List + * + * @author James Moger + * + */ +public class SyndicatedEntryTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + + List entries; + + enum Columns { + Date, Repository, Author, Message; + + @Override + public String toString() { + return name().replace('_', ' '); + } + } + + public SyndicatedEntryTableModel() { + this(new ArrayList()); + } + + public SyndicatedEntryTableModel(List entries) { + setEntries(entries); + } + + public void setEntries(List entries) { + this.entries = entries; + Collections.sort(entries); + } + + @Override + public int getRowCount() { + return entries.size(); + } + + @Override + public int getColumnCount() { + return Columns.values().length; + } + + @Override + public String getColumnName(int column) { + Columns col = Columns.values()[column]; + switch (col) { + case Date: + return Translation.get("gb.date"); + case Repository: + return Translation.get("gb.repository"); + case Author: + return Translation.get("gb.author"); + case Message: + return Translation.get("gb.message"); + } + return ""; + } + + /** + * Returns Object.class regardless of columnIndex. + * + * @param columnIndex + * the column being queried + * @return the Object.class + */ + public Class getColumnClass(int columnIndex) { + if (Columns.Date.ordinal() == columnIndex) { + return Date.class; + } + return String.class; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + SyndicatedEntryModel entry = entries.get(rowIndex); + Columns col = Columns.values()[columnIndex]; + switch (col) { + case Date: + return entry.published; + case Repository: + return entry.repository; + case Author: + return entry.author; + case Message: + return entry.title; + } + return null; + } + + public SyndicatedEntryModel get(int modelRow) { + return entries.get(modelRow); + } +} diff --git a/src/com/gitblit/client/Utils.java b/src/com/gitblit/client/Utils.java index 786eb9f2..1f44c32d 100644 --- a/src/com/gitblit/client/Utils.java +++ b/src/com/gitblit/client/Utils.java @@ -36,15 +36,19 @@ import javax.swing.table.TableModel; import com.gitblit.Constants.RpcRequest; public class Utils { + + 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) { + public static JTable newTable(TableModel model, String datePattern) { JTable table = new JTable(model); table.setCellSelectionEnabled(false); table.setRowSelectionAllowed(true); table.getTableHeader().setReorderingAllowed(false); table.setGridColor(new Color(0xd9d9d9)); table.setBackground(Color.white); - table.setDefaultRenderer(Date.class, new DateCellRenderer(null, Color.orange.darker())); + table.setDefaultRenderer(Date.class, new DateCellRenderer(datePattern, Color.orange.darker())); return table; } diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index 9a774fbd..af420612 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -55,6 +55,7 @@ public class RepositoryModel implements Serializable, Comparable { + + public String repository; + public String branch; + public String title; + public String author; + public Date published; + public String link; + public String content; + public String contentType; + + private static final long serialVersionUID = 1L; + + public SyndicatedEntryModel() { + } + + @Override + public int compareTo(SyndicatedEntryModel o) { + return o.published.compareTo(published); + } + + @Override + public int hashCode() { + return link.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof SyndicatedEntryModel) { + return hashCode() == o.hashCode(); + } + return false; + } +} diff --git a/src/com/gitblit/utils/SyndicationUtils.java b/src/com/gitblit/utils/SyndicationUtils.java index 3664a181..2237774e 100644 --- a/src/com/gitblit/utils/SyndicationUtils.java +++ b/src/com/gitblit/utils/SyndicationUtils.java @@ -27,6 +27,8 @@ import java.util.List; import org.eclipse.jgit.revwalk.RevCommit; import com.gitblit.Constants; +import com.gitblit.GitBlitException; +import com.gitblit.models.SyndicatedEntryModel; import com.sun.syndication.feed.synd.SyndContent; import com.sun.syndication.feed.synd.SyndContentImpl; import com.sun.syndication.feed.synd.SyndEntry; @@ -64,6 +66,7 @@ public class SyndicationUtils { SyndFeed feed = new SyndFeedImpl(); feed.setFeedType("rss_2.0"); + feed.setEncoding("UTF-8"); feed.setTitle(title); feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl, StringUtils.encodeURL(repository))); @@ -84,15 +87,14 @@ public class SyndicationUtils { entry.setPublishedDate(commit.getCommitterIdent().getWhen()); SyndContent content = new SyndContentImpl(); - content.setType("text/html"); - String html = StringUtils.escapeForHtml(commit.getFullMessage(), false); - content.setValue(StringUtils.breakLinesForHtml(html)); + content.setType("text/plain"); + content.setValue(commit.getFullMessage()); entry.setDescription(content); entries.add(entry); } feed.setEntries(entries); - OutputStreamWriter writer = new OutputStreamWriter(os); + OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8"); SyndFeedOutput output = new SyndFeedOutput(); output.output(feed, writer); writer.close(); @@ -112,12 +114,11 @@ public class SyndicationUtils { * is used. * @param username * @param password - * @return the JSON message as a string + * @return a list of SyndicationModel entries * @throws {@link IOException} */ - public static SyndFeed readFeed(String url, String repository, String branch, - int numberOfEntries, String username, char[] password) throws IOException, - FeedException { + public static List readFeed(String url, String repository, String branch, + int numberOfEntries, String username, char[] password) throws IOException { String feedUrl; if (StringUtils.isEmpty(branch)) { // no branch specified @@ -142,8 +143,27 @@ public class SyndicationUtils { URLConnection conn = ConnectionUtils.openReadConnection(feedUrl, username, password); InputStream is = conn.getInputStream(); SyndFeedInput input = new SyndFeedInput(); - SyndFeed feed = input.build(new XmlReader(is)); + SyndFeed feed = null; + try { + feed = input.build(new XmlReader(is)); + } catch (FeedException f) { + throw new GitBlitException(f); + } is.close(); - return feed; + List entries = new ArrayList(); + for (Object o : feed.getEntries()) { + SyndEntryImpl entry = (SyndEntryImpl) o; + SyndicatedEntryModel model = new SyndicatedEntryModel(); + model.repository = repository; + model.branch = branch; + model.title = entry.getTitle(); + model.author = entry.getAuthor(); + model.published = entry.getPublishedDate(); + model.link = entry.getLink(); + model.content = entry.getDescription().getValue(); + model.contentType = entry.getDescription().getType(); + entries.add(model); + } + return entries; } } diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index 267fb564..e16b44db 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -175,4 +175,7 @@ gb.heapAllocated = allocated heap gb.heapUsed = used heap gb.free = free gb.version = version -gb.releaseDate = release date \ No newline at end of file +gb.releaseDate = release date +gb.date = date +gb.recentCommits = recent commits +gb.subscribe = subscribe \ No newline at end of file diff --git a/tests/com/gitblit/tests/SyndicationUtilsTest.java b/tests/com/gitblit/tests/SyndicationUtilsTest.java index 33d7dbd2..6084b990 100644 --- a/tests/com/gitblit/tests/SyndicationUtilsTest.java +++ b/tests/com/gitblit/tests/SyndicationUtilsTest.java @@ -23,10 +23,9 @@ import junit.framework.TestCase; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; -import com.gitblit.client.GitblitFeed; +import com.gitblit.models.SyndicatedEntryModel; import com.gitblit.utils.JGitUtils; import com.gitblit.utils.SyndicationUtils; -import com.sun.syndication.feed.synd.SyndFeed; public class SyndicationUtilsTest extends TestCase { @@ -44,10 +43,10 @@ public class SyndicationUtilsTest extends TestCase { } public void testFeedRead() throws Exception { - GitblitFeed reader = new GitblitFeed("https://localhost:8443", "ticgit.git", "master"); - SyndFeed feed = reader.update(5, "admin", "admin".toCharArray()); + List feed = SyndicationUtils.readFeed("https://localhost:8443", + "ticgit.git", "master", 5, "admin", "admin".toCharArray()); assertTrue(feed != null); - assertTrue(feed.getEntries().size() > 0); - assertEquals(5, feed.getEntries().size()); + assertTrue(feed.size() > 0); + assertEquals(5, feed.size()); } } \ No newline at end of file -- 2.39.5