<resource file="${basedir}/resources/book_16x16.png" />\r
<resource file="${basedir}/resources/bug_16x16.png" />\r
<resource file="${basedir}/resources/health_16x16.png" />\r
+ <resource file="${basedir}/resources/feed_16x16.png" />\r
+ <resource file="${basedir}/resources/bullet_feed.png" />\r
<resource file="${basedir}/resources/blank.png" />\r
<resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp.properties" />\r
\r
- fixed: Gitblit can now browse the Linux kernel repository (issue 25)\r
- fixed: Gitblit now runs on Servlet 3.0 webservers (e.g. Tomcat 7, Jetty 8) (issue 23)\r
- fixed: Set the RSS content type of syndication feeds for Firefox 4 (issue 22)\r
+- fixed: RSS feeds are now properly encoded to UTF-8\r
- fixed: Null pointer exception if did not set federation strategy (issue 20)\r
- fixed: Gitblit GO allows SSL renegotiation if running on Java 1.6.0_22 or later\r
- updated: MarkdownPapers 1.2.4\r
- fixed: Gitblit can now browse the Linux kernel repository (issue 25)\r
- fixed: Gitblit now runs on Servlet 3.0 webservers (e.g. Tomcat 7, Jetty 8) (issue 23)\r
- fixed: Set the RSS content type of syndication feeds for Firefox 4 (issue 22)\r
+- fixed: RSS feeds are now properly encoded to UTF-8\r
- fixed: Null pointer exception if did not set federation strategy (issue 20)\r
- fixed: Gitblit GO allows SSL renegotiation if running on Java 1.6.0_22 or later\r
- updated: MarkdownPapers 1.2.4\r
public static void manager(DownloadListener listener) {\r
downloadListener = listener;\r
downloadFromApache(MavenObject.GSON, BuildType.RUNTIME);\r
+ downloadFromApache(MavenObject.ROME, BuildType.RUNTIME);\r
+ downloadFromApache(MavenObject.JDOM, BuildType.RUNTIME);\r
downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME);\r
\r
downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME);\r
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
if (value instanceof Date) {\r
Date date = (Date) value;\r
- String timeAgo;\r
- String strDate;\r
+ String title;\r
+ String dateString;\r
if (date.getTime() == 0) {\r
- timeAgo = "--";\r
- strDate = "never";\r
+ title = "--";\r
+ dateString = "never";\r
} else {\r
- timeAgo = TimeUtils.timeAgo(date);\r
- strDate = new SimpleDateFormat(pattern).format((Date) value);\r
+ title = TimeUtils.timeAgo(date);\r
+ dateString = new SimpleDateFormat(pattern).format((Date) value);\r
}\r
\r
- this.setText(timeAgo);\r
- this.setToolTipText(strDate);\r
+ if ((System.currentTimeMillis() - date.getTime()) > 10 * 24 * 60 * 60 * 1000L) {\r
+ String tmp = dateString;\r
+ dateString = title;\r
+ title = tmp;\r
+ }\r
+ this.setText(title);\r
+ this.setToolTipText(dateString);\r
}\r
return this;\r
}\r
import java.io.Serializable;\r
import java.util.ArrayList;\r
import java.util.Collections;\r
+import java.util.HashSet;\r
import java.util.List;\r
import java.util.Map;\r
+import java.util.Set;\r
\r
import com.gitblit.GitBlitException.ForbiddenException;\r
import com.gitblit.GitBlitException.NotAllowedException;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.ServerSettings;\r
import com.gitblit.models.ServerStatus;\r
+import com.gitblit.models.SyndicatedEntryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.RpcUtils;\r
+import com.gitblit.utils.StringUtils;\r
+import com.gitblit.utils.SyndicationUtils;\r
\r
/**\r
* GitblitClient is a object that retrieves data from a Gitblit server, caches\r
\r
private static final long serialVersionUID = 1L;\r
\r
+ protected final GitblitRegistration reg;\r
+\r
public final String url;\r
\r
public final String account;\r
\r
private final List<FederationModel> federationRegistrations;\r
\r
+ private final List<SyndicatedEntryModel> syndicatedEntries;\r
+\r
private ServerStatus status;\r
\r
- public GitblitClient(String url, String account, char[] password) {\r
- this.url = url;\r
- this.account = account;\r
- this.password = password;\r
+ public GitblitClient(GitblitRegistration reg) {\r
+ this.reg = reg;\r
+ this.url = reg.url;\r
+ this.account = reg.account;\r
+ this.password = reg.password;\r
\r
this.allUsers = new ArrayList<UserModel>();\r
this.allRepositories = new ArrayList<RepositoryModel>();\r
this.federationRegistrations = new ArrayList<FederationModel>();\r
+ this.syndicatedEntries = new ArrayList<SyndicatedEntryModel>();\r
}\r
\r
public void login() throws IOException {\r
refreshRepositories();\r
\r
try {\r
+ // RSS feeds may be disabled by server\r
+ refreshSubscribedFeeds();\r
+ } catch (IOException e) {\r
+ e.printStackTrace();\r
+ }\r
+ \r
+ try {\r
+ // credentials may not have administrator access\r
+ // or server may have disabled rpc management\r
refreshUsers();\r
refreshSettings();\r
allowManagement = true;\r
}\r
\r
try {\r
+ // credentials may not have administrator access\r
+ // or server may have disabled rpc administration\r
refreshStatus();\r
allowAdministration = true;\r
} catch (UnauthorizedException e) {\r
allRepositories.clear();\r
allRepositories.addAll(repositories.values());\r
Collections.sort(allRepositories);\r
+ updateSubscribedStates();\r
return allRepositories;\r
}\r
\r
return status;\r
}\r
\r
+ public List<SyndicatedEntryModel> refreshSubscribedFeeds() throws IOException {\r
+ Set<SyndicatedEntryModel> allFeeds = new HashSet<SyndicatedEntryModel>();\r
+ if (reg.feeds != null && reg.feeds.size() > 0) {\r
+ for (String feed : reg.feeds) {\r
+ String[] values = feed.split(":");\r
+ String repository = values[0];\r
+ String branch = null;\r
+ if (values.length > 1) {\r
+ branch = values[1];\r
+ }\r
+ List<SyndicatedEntryModel> list = SyndicationUtils.readFeed(url, repository,\r
+ branch, -1, account, password);\r
+ allFeeds.addAll(list);\r
+ }\r
+ }\r
+ syndicatedEntries.clear();\r
+ syndicatedEntries.addAll(allFeeds);\r
+ Collections.sort(syndicatedEntries);\r
+ return syndicatedEntries;\r
+ }\r
+\r
+ private void updateSubscribedStates() {\r
+ if (reg.feeds != null) {\r
+ Set<String> subscribedRepositories = new HashSet<String>();\r
+ for (String feed : reg.feeds) {\r
+ if (feed.indexOf(':') > -1) {\r
+ // strip branch\r
+ subscribedRepositories.add(feed.substring(0, feed.indexOf(':')).toLowerCase());\r
+ } else {\r
+ // default branch\r
+ subscribedRepositories.add(feed.toLowerCase());\r
+ }\r
+ }\r
+ // set subscribed flag\r
+ for (RepositoryModel repository : allRepositories) {\r
+ repository.subscribed = subscribedRepositories.contains(repository.name\r
+ .toLowerCase());\r
+ }\r
+ }\r
+ }\r
+\r
+ public List<SyndicatedEntryModel> getSyndicatedEntries() {\r
+ return syndicatedEntries;\r
+ }\r
+\r
+ public boolean isSubscribed(RepositoryModel repository, String branch) {\r
+ if (reg.feeds != null && reg.feeds.size() > 0) {\r
+ for (String feed : reg.feeds) {\r
+ String[] values = feed.split(":");\r
+ String repositoryName = values[0];\r
+ if (repository.name.equalsIgnoreCase(repositoryName)) {\r
+ return true;\r
+ }\r
+ // TODO check branch subscriptions\r
+ String branchName = null;\r
+ if (values.length > 1) {\r
+ branchName = values[1];\r
+ }\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ public boolean subscribe(RepositoryModel repository, String branch) {\r
+ String feed = repository.name;\r
+ if (!StringUtils.isEmpty(branch)) {\r
+ feed += ":" + branch;\r
+ }\r
+ if (reg.feeds == null) {\r
+ reg.feeds = new ArrayList<String>();\r
+ }\r
+ reg.feeds.add(feed);\r
+ updateSubscribedStates();\r
+ return true;\r
+ }\r
+\r
+ public boolean unsubscribe(RepositoryModel repository, String branch) {\r
+ String feed = repository.name;\r
+ if (!StringUtils.isEmpty(branch)) {\r
+ feed += ":" + branch;\r
+ }\r
+ reg.feeds.remove(feed);\r
+ if (syndicatedEntries.size() > 0) {\r
+ List<SyndicatedEntryModel> toRemove = new ArrayList<SyndicatedEntryModel>();\r
+ for (SyndicatedEntryModel model : syndicatedEntries) {\r
+ if (model.repository.equalsIgnoreCase(repository.name)) {\r
+ boolean emptyUnsubscribeBranch = StringUtils.isEmpty(branch);\r
+ boolean emptyFromBranch = StringUtils.isEmpty(model.branch);\r
+ if (emptyUnsubscribeBranch && emptyFromBranch) {\r
+ // default branch, remove\r
+ toRemove.add(model);\r
+ } else if (!emptyUnsubscribeBranch && !emptyFromBranch) {\r
+ if (model.branch.equals(branch)) {\r
+ // specific branch, remove\r
+ toRemove.add(model);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ updateSubscribedStates();\r
+ return true;\r
+ }\r
+\r
public List<FederationModel> refreshFederationRegistrations() throws IOException {\r
List<FederationModel> list = RpcUtils.getFederationRegistrations(url, account, password);\r
federationRegistrations.clear();\r
import java.text.MessageFormat;\r
import java.text.SimpleDateFormat;\r
import java.util.ArrayList;\r
+import java.util.Arrays;\r
import java.util.Collections;\r
import java.util.Comparator;\r
import java.util.Date;\r
return;\r
}\r
}\r
- \r
+\r
// login\r
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));\r
final GitblitRegistration registration = reg;\r
- final GitblitPanel panel = new GitblitPanel(registration);\r
+ final GitblitPanel panel = new GitblitPanel(registration, this);\r
SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {\r
\r
@Override\r
password = new String(Base64.decode(pw)).toCharArray();\r
}\r
GitblitRegistration reg = new GitblitRegistration(server, url, account, password);\r
+ String[] feeds = config.getStringList("servers", server, "feeds");\r
+ if (feeds != null) {\r
+ reg.feeds = new ArrayList<String>(Arrays.asList(feeds));\r
+ }\r
reg.lastLogin = lastLogin;\r
registrations.put(reg.name, reg);\r
}\r
if (reg.lastLogin != null) {\r
config.setString("servers", reg.name, "lastLogin", dateFormat.format(reg.lastLogin));\r
}\r
+ if (reg.feeds != null) {\r
+ config.setStringList("servers", reg.name, "feeds", reg.feeds);\r
+ }\r
config.save();\r
return true;\r
} catch (Throwable t) {\r
import com.gitblit.client.ClosableTabComponent.CloseTabListener;\r
import com.gitblit.models.RepositoryModel;\r
import com.gitblit.models.SettingModel;\r
+import com.gitblit.models.SyndicatedEntryModel;\r
import com.gitblit.models.UserModel;\r
import com.gitblit.utils.StringUtils;\r
\r
\r
private final Insets insets = new Insets(margin, margin, margin, margin);\r
\r
+ private final RegistrationsDialog.RegistrationListener listener;\r
+\r
private GitblitClient gitblit;\r
\r
private JTabbedPane tabs;\r
\r
private JButton delRepository;\r
\r
- private NameRenderer nameRenderer;\r
-\r
- private IndicatorsRenderer typeRenderer;\r
-\r
- private DefaultTableCellRenderer ownerRenderer;\r
-\r
- private DefaultTableCellRenderer sizeRenderer;\r
-\r
private TableRowSorter<RepositoriesTableModel> defaultRepositoriesSorter;\r
\r
private TableRowSorter<UsersTableModel> defaultUsersSorter;\r
\r
private StatusPanel statusPanel;\r
\r
- public GitblitPanel(GitblitRegistration reg) {\r
- this(reg.url, reg.account, reg.password);\r
- }\r
+ private SyndicatedEntryTableModel syndicationModel;\r
\r
- public GitblitPanel(String url, String account, char[] password) {\r
- this.gitblit = new GitblitClient(url, account, password);\r
+ private HeaderPanel feedsHeader;\r
+\r
+ private JTable syndicationEntriesTable;\r
+\r
+ public GitblitPanel(GitblitRegistration reg, RegistrationsDialog.RegistrationListener listener) {\r
+ this.gitblit = new GitblitClient(reg);\r
+ this.listener = listener;\r
\r
tabs = new JTabbedPane(JTabbedPane.BOTTOM);\r
tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel());\r
+ tabs.addTab(Translation.get("gb.recentCommits"), createFeedsPanel());\r
tabs.addTab(Translation.get("gb.users"), createUsersPanel());\r
tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());\r
tabs.addTab(Translation.get("gb.status"), createStatusPanel());\r
}\r
});\r
\r
- nameRenderer = new NameRenderer();\r
- typeRenderer = new IndicatorsRenderer();\r
+ final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "...");\r
+ subscribeRepository.setEnabled(false);\r
+ subscribeRepository.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ subscribeRepository(getSelectedRepositories().get(0));\r
+ }\r
+ });\r
+\r
+ NameRenderer nameRenderer = new NameRenderer(true);\r
+ IndicatorsRenderer typeRenderer = new IndicatorsRenderer();\r
\r
- sizeRenderer = new DefaultTableCellRenderer();\r
+ DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer();\r
sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT);\r
sizeRenderer.setForeground(new Color(0, 0x80, 0));\r
\r
- ownerRenderer = new DefaultTableCellRenderer();\r
+ DefaultTableCellRenderer ownerRenderer = new DefaultTableCellRenderer();\r
ownerRenderer.setForeground(Color.gray);\r
ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER);\r
\r
repositoriesModel = new RepositoriesTableModel();\r
defaultRepositoriesSorter = new TableRowSorter<RepositoriesTableModel>(repositoriesModel);\r
- repositoriesTable = Utils.newTable(repositoriesModel);\r
+ repositoriesTable = Utils.newTable(repositoriesModel, Utils.DATE_FORMAT);\r
repositoriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
repositoriesTable.setRowSorter(defaultRepositoriesSorter);\r
repositoriesTable.getRowSorter().toggleSortOrder(\r
boolean selected = repositoriesTable.getSelectedRow() > -1;\r
browseRepository.setEnabled(singleSelection);\r
delRepository.setEnabled(selected);\r
+ subscribeRepository.setEnabled(singleSelection);\r
if (selected) {\r
int viewRow = repositoriesTable.getSelectedRow();\r
int modelRow = repositoriesTable.convertRowIndexToModel(viewRow);\r
repositoryControls.add(createRepository);\r
repositoryControls.add(editRepository);\r
repositoryControls.add(delRepository);\r
+ repositoryControls.add(subscribeRepository);\r
\r
JPanel repositoriesPanel = new JPanel(new BorderLayout(margin, margin)) {\r
\r
}\r
}\r
\r
+ private JPanel createFeedsPanel() {\r
+ JButton refreshFeeds = new JButton(Translation.get("gb.refresh"));\r
+ refreshFeeds.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ refreshFeeds();\r
+ }\r
+ });\r
+\r
+ final JButton viewCommit = new JButton(Translation.get("gb.view"));\r
+ viewCommit.setEnabled(false);\r
+ viewCommit.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ viewCommit();\r
+ }\r
+ });\r
+\r
+ final JButton viewCommitDiff = new JButton(Translation.get("gb.commitdiff"));\r
+ viewCommitDiff.setEnabled(false);\r
+ viewCommitDiff.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ viewCommitDiff();\r
+ }\r
+ });\r
+\r
+ final JButton viewTree = new JButton(Translation.get("gb.tree"));\r
+ viewTree.setEnabled(false);\r
+ viewTree.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ viewTree();\r
+ }\r
+ });\r
+\r
+ JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));\r
+ controls.add(refreshFeeds);\r
+ controls.add(viewCommit);\r
+ controls.add(viewCommitDiff);\r
+ controls.add(viewTree);\r
+\r
+ NameRenderer nameRenderer = new NameRenderer();\r
+ syndicationModel = new SyndicatedEntryTableModel();\r
+ feedsHeader = new HeaderPanel(Translation.get("gb.recentCommits"), "feed_16x16.png");\r
+ syndicationEntriesTable = Utils.newTable(syndicationModel, Utils.DATE_FORMAT);\r
+ String name = syndicationEntriesTable\r
+ .getColumnName(SyndicatedEntryTableModel.Columns.Author.ordinal());\r
+ syndicationEntriesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
+ syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer);\r
+\r
+ syndicationEntriesTable.addMouseListener(new MouseAdapter() {\r
+ public void mouseClicked(MouseEvent e) {\r
+ if (e.getClickCount() == 2) {\r
+ if (e.isControlDown()) {\r
+ viewCommitDiff();\r
+ } else {\r
+ viewCommit();\r
+ }\r
+ }\r
+ }\r
+ });\r
+\r
+ syndicationEntriesTable.getSelectionModel().addListSelectionListener(\r
+ new ListSelectionListener() {\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if (e.getValueIsAdjusting()) {\r
+ return;\r
+ }\r
+ boolean singleSelection = syndicationEntriesTable.getSelectedRowCount() == 1;\r
+ viewCommit.setEnabled(singleSelection);\r
+ viewCommitDiff.setEnabled(singleSelection);\r
+ viewTree.setEnabled(singleSelection);\r
+ }\r
+ });\r
+\r
+ JPanel panel = new JPanel(new BorderLayout(5, 5)) {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ @Override\r
+ public Insets getInsets() {\r
+ return insets;\r
+ }\r
+ };\r
+ panel.add(feedsHeader, BorderLayout.NORTH);\r
+ panel.add(new JScrollPane(syndicationEntriesTable), BorderLayout.CENTER);\r
+ panel.add(controls, BorderLayout.SOUTH);\r
+ return panel;\r
+ }\r
+\r
private JPanel createUsersPanel() {\r
JButton refreshUsers = new JButton(Translation.get("gb.refresh"));\r
refreshUsers.addActionListener(new ActionListener() {\r
}\r
});\r
\r
+ NameRenderer nameRenderer = new NameRenderer();\r
usersModel = new UsersTableModel();\r
defaultUsersSorter = new TableRowSorter<UsersTableModel>(usersModel);\r
- usersTable = Utils.newTable(usersModel);\r
+ usersTable = Utils.newTable(usersModel, Utils.DATE_FORMAT);\r
String name = usersTable.getColumnName(UsersTableModel.Columns.Name.ordinal());\r
usersTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
usersTable.getColumn(name).setCellRenderer(nameRenderer);\r
}\r
});\r
\r
+ NameRenderer nameRenderer = new NameRenderer();\r
final SettingPanel settingPanel = new SettingPanel();\r
settingsModel = new SettingsTableModel();\r
defaultSettingsSorter = new TableRowSorter<SettingsTableModel>(settingsModel);\r
- settingsTable = Utils.newTable(settingsModel);\r
+ settingsTable = Utils.newTable(settingsModel, Utils.DATE_FORMAT);\r
settingsTable.setDefaultRenderer(SettingModel.class, new SettingCellRenderer());\r
String name = settingsTable.getColumnName(UsersTableModel.Columns.Name.ordinal());\r
settingsTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
updateRepositoriesTable();\r
Utils.packColumns(repositoriesTable, 5);\r
\r
+ updateFeedsTable();\r
+ Utils.packColumns(syndicationEntriesTable, 5);\r
+\r
if (gitblit.allowManagement()) {\r
updateUsersTable();\r
} else {\r
+ gitblit.getRepositories().size() + ")");\r
}\r
\r
+ private void updateFeedsTable() {\r
+ syndicationModel.entries.clear();\r
+ syndicationModel.entries.addAll(gitblit.getSyndicatedEntries());\r
+ syndicationModel.fireTableDataChanged();\r
+ feedsHeader.setText(Translation.get("gb.recentCommits") + " ("\r
+ + gitblit.getSyndicatedEntries().size() + ")");\r
+ }\r
+\r
private void updateUsersTable() {\r
usersModel.list.clear();\r
usersModel.list.addAll(gitblit.getUsers());\r
}\r
}\r
\r
+ protected void subscribeRepository(final RepositoryModel repository) {\r
+ if (repository == null) {\r
+ return;\r
+ }\r
+ // TODO this is lame. need better ui.\r
+ if (gitblit.isSubscribed(repository, null)) {\r
+ // unsubscribe\r
+ String msg = MessageFormat.format("Do you want to unsubscribe from {0}?",\r
+ repository.name);\r
+ String[] options = { "no", "yes" };\r
+ int result = JOptionPane.showOptionDialog(GitblitPanel.this, msg, "Unsubscribe?",\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options,\r
+ options[0]);\r
+ if (result == 1) {\r
+ if (gitblit.unsubscribe(repository, null)) {\r
+ updateFeedsTable();\r
+ updateRepositoriesTable();\r
+ listener.saveRegistration(repository.name, gitblit.reg);\r
+ }\r
+ }\r
+ } else {\r
+ // subscribe\r
+ String msg = MessageFormat.format("Do you want to subscribe to {0}?", repository.name);\r
+ String[] options = { "no", "yes" };\r
+ int result = JOptionPane.showOptionDialog(GitblitPanel.this, msg, "Subscribe?",\r
+ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options,\r
+ options[0]);\r
+ if (result == 1) {\r
+ if (gitblit.subscribe(repository, null)) {\r
+ updateRepositoriesTable();\r
+ listener.saveRegistration(repository.name, gitblit.reg);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ protected void refreshFeeds() {\r
+ // TODO change request type here\r
+ GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_USERS) {\r
+ @Override\r
+ protected Boolean doRequest() throws IOException {\r
+ gitblit.refreshSubscribedFeeds();\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ protected void onSuccess() {\r
+ updateFeedsTable();\r
+ }\r
+ };\r
+ worker.execute();\r
+ }\r
+\r
+ protected SyndicatedEntryModel getSelectedSyndicatedEntry() {\r
+ int viewRow = syndicationEntriesTable.getSelectedRow();\r
+ int modelRow = syndicationEntriesTable.convertRowIndexToModel(viewRow);\r
+ SyndicatedEntryModel entry = syndicationModel.get(modelRow);\r
+ return entry;\r
+ }\r
+\r
+ protected void viewCommit() {\r
+ SyndicatedEntryModel entry = getSelectedSyndicatedEntry();\r
+ browse(entry.link);\r
+ }\r
+\r
+ protected void viewCommitDiff() {\r
+ SyndicatedEntryModel entry = getSelectedSyndicatedEntry();\r
+ browse(entry.link.replace("/commit/", "/commitdiff/"));\r
+ }\r
+\r
+ protected void viewTree() {\r
+ SyndicatedEntryModel entry = getSelectedSyndicatedEntry();\r
+ browse(entry.link.replace("/commit/", "/tree/"));\r
+ }\r
+\r
+ protected void browse(String url) {\r
+ try {\r
+ Desktop.getDesktop().browse(new URI(url));\r
+ } catch (Exception x) {\r
+ x.printStackTrace();\r
+ }\r
+ }\r
+\r
protected void refreshUsers() {\r
GitblitWorker worker = new GitblitWorker(GitblitPanel.this, RpcRequest.LIST_USERS) {\r
@Override\r
\r
import java.io.Serializable;\r
import java.util.Date;\r
+import java.util.List;\r
\r
import com.gitblit.utils.StringUtils;\r
\r
char[] password;\r
boolean savePassword;\r
Date lastLogin;\r
+ List<String> feeds;\r
\r
public GitblitRegistration(String name, String url, String account, char[] password) {\r
this.url = url;\r
import java.awt.Color;\r
import java.awt.Component;\r
\r
+import javax.swing.ImageIcon;\r
import javax.swing.JTable;\r
import javax.swing.table.DefaultTableCellRenderer;\r
\r
+import com.gitblit.models.RepositoryModel;\r
+\r
/**\r
* Repository name cell renderer. This renderer shows the group name in a gray\r
* color and accentuates the repository name in a cornflower blue color.\r
\r
final String groupSpan;\r
\r
+ private final boolean displayIcon;\r
+\r
+ private final ImageIcon blankIcon;\r
+\r
+ private final ImageIcon subscribedIcon;\r
+\r
public NameRenderer() {\r
- this(Color.gray, new Color(0x00, 0x69, 0xD6));\r
+ this(false);\r
+ }\r
+\r
+ public NameRenderer(boolean showIcon) {\r
+ this(Color.gray, new Color(0x00, 0x69, 0xD6), showIcon);\r
}\r
\r
- public NameRenderer(Color group, Color repo) {\r
+ private NameRenderer(Color group, Color repo, boolean showIcon) {\r
+ blankIcon = new ImageIcon(getClass().getResource("/blank.png"));\r
+ subscribedIcon = new ImageIcon(getClass().getResource("/bullet_feed.png"));\r
+ displayIcon = showIcon;\r
groupSpan = "<span style='color:" + getHexColor(group) + "'>";\r
setForeground(repo);\r
}\r
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,\r
boolean hasFocus, int row, int column) {\r
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);\r
- String name = value.toString();\r
- int lastSlash = name.lastIndexOf('/');\r
- if (!isSelected && lastSlash > -1) {\r
- String group = name.substring(0, lastSlash + 1);\r
- String repo = name.substring(lastSlash + 1);\r
- setText("<html><body>" + groupSpan + group + "</span>" + repo);\r
+ if (value instanceof RepositoryModel) {\r
+ RepositoryModel model = (RepositoryModel) value;\r
+ String name = value.toString();\r
+ int lastSlash = name.lastIndexOf('/');\r
+ if (!isSelected && lastSlash > -1) {\r
+ String group = name.substring(0, lastSlash + 1);\r
+ String repo = name.substring(lastSlash + 1);\r
+ setText("<html><body>" + groupSpan + group + "</span>" + repo);\r
+ } else {\r
+ this.setText(name);\r
+ }\r
+ if (displayIcon) {\r
+ if (model.subscribed) {\r
+ setIcon(subscribedIcon);\r
+ } else {\r
+ setIcon(blankIcon);\r
+ }\r
+ }\r
} else {\r
- this.setText(name);\r
+ this.setText(value.toString());\r
}\r
return this;\r
}\r
private void initialize() {\r
NameRenderer nameRenderer = new NameRenderer();\r
model = new RegistrationsTableModel(registrations);\r
- registrationsTable = Utils.newTable(model);\r
+ registrationsTable = Utils.newTable(model, Utils.DATE_FORMAT);\r
registrationsTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
\r
String id = registrationsTable\r
fieldsPanel.add(createFieldPanel("gb.heapMaximum", heapMaximum));\r
\r
model = new PropertiesTableModel();\r
- JTable propertiesTable = Utils.newTable(model);\r
+ JTable propertiesTable = Utils.newTable(model, Utils.DATE_FORMAT);\r
String name = propertiesTable.getColumnName(PropertiesTableModel.Columns.Name.ordinal());\r
NameRenderer nameRenderer = new NameRenderer();\r
propertiesTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.client;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.List;\r
+\r
+import javax.swing.table.AbstractTableModel;\r
+\r
+import com.gitblit.models.SyndicatedEntryModel;\r
+\r
+/**\r
+ * Table model of List<SyndicatedEntryModel>\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class SyndicatedEntryTableModel extends AbstractTableModel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ List<SyndicatedEntryModel> entries;\r
+\r
+ enum Columns {\r
+ Date, Repository, Author, Message;\r
+\r
+ @Override\r
+ public String toString() {\r
+ return name().replace('_', ' ');\r
+ }\r
+ }\r
+\r
+ public SyndicatedEntryTableModel() {\r
+ this(new ArrayList<SyndicatedEntryModel>());\r
+ }\r
+\r
+ public SyndicatedEntryTableModel(List<SyndicatedEntryModel> entries) {\r
+ setEntries(entries);\r
+ }\r
+\r
+ public void setEntries(List<SyndicatedEntryModel> entries) {\r
+ this.entries = entries;\r
+ Collections.sort(entries);\r
+ }\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return entries.size();\r
+ }\r
+\r
+ @Override\r
+ public int getColumnCount() {\r
+ return Columns.values().length;\r
+ }\r
+\r
+ @Override\r
+ public String getColumnName(int column) {\r
+ Columns col = Columns.values()[column];\r
+ switch (col) {\r
+ case Date:\r
+ return Translation.get("gb.date");\r
+ case Repository:\r
+ return Translation.get("gb.repository");\r
+ case Author:\r
+ return Translation.get("gb.author");\r
+ case Message:\r
+ return Translation.get("gb.message");\r
+ }\r
+ return "";\r
+ }\r
+\r
+ /**\r
+ * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.\r
+ * \r
+ * @param columnIndex\r
+ * the column being queried\r
+ * @return the Object.class\r
+ */\r
+ public Class<?> getColumnClass(int columnIndex) {\r
+ if (Columns.Date.ordinal() == columnIndex) {\r
+ return Date.class;\r
+ }\r
+ return String.class;\r
+ }\r
+\r
+ @Override\r
+ public Object getValueAt(int rowIndex, int columnIndex) {\r
+ SyndicatedEntryModel entry = entries.get(rowIndex);\r
+ Columns col = Columns.values()[columnIndex];\r
+ switch (col) {\r
+ case Date:\r
+ return entry.published;\r
+ case Repository:\r
+ return entry.repository;\r
+ case Author:\r
+ return entry.author;\r
+ case Message:\r
+ return entry.title;\r
+ }\r
+ return null;\r
+ }\r
+\r
+ public SyndicatedEntryModel get(int modelRow) {\r
+ return entries.get(modelRow);\r
+ }\r
+}\r
import com.gitblit.Constants.RpcRequest;\r
\r
public class Utils {\r
+ \r
+ public final static String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm";\r
+ \r
+ public final static String DATE_FORMAT = "yyyy-MM-dd";\r
\r
- public static JTable newTable(TableModel model) {\r
+ public static JTable newTable(TableModel model, String datePattern) {\r
JTable table = new JTable(model);\r
table.setCellSelectionEnabled(false);\r
table.setRowSelectionAllowed(true);\r
table.getTableHeader().setReorderingAllowed(false);\r
table.setGridColor(new Color(0xd9d9d9));\r
table.setBackground(Color.white);\r
- table.setDefaultRenderer(Date.class, new DateCellRenderer(null, Color.orange.darker()));\r
+ table.setDefaultRenderer(Date.class, new DateCellRenderer(datePattern, Color.orange.darker()));\r
return table;\r
}\r
\r
public String frequency;\r
public String origin;\r
public String size;\r
+ public boolean subscribed;\r
\r
public RepositoryModel() {\r
this("", "", "", new Date(0));\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.models;\r
+\r
+import java.io.Serializable;\r
+import java.util.Date;\r
+\r
+/**\r
+ * SyndicationEntryModel represents an entry in a syndication (RSS) feed.\r
+ * \r
+ * @author James Moger\r
+ */\r
+public class SyndicatedEntryModel implements Serializable, Comparable<SyndicatedEntryModel> {\r
+\r
+ public String repository;\r
+ public String branch;\r
+ public String title;\r
+ public String author;\r
+ public Date published;\r
+ public String link;\r
+ public String content;\r
+ public String contentType;\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public SyndicatedEntryModel() {\r
+ }\r
+\r
+ @Override\r
+ public int compareTo(SyndicatedEntryModel o) {\r
+ return o.published.compareTo(published);\r
+ }\r
+\r
+ @Override\r
+ public int hashCode() {\r
+ return link.hashCode();\r
+ }\r
+\r
+ @Override\r
+ public boolean equals(Object o) {\r
+ if (o instanceof SyndicatedEntryModel) {\r
+ return hashCode() == o.hashCode();\r
+ }\r
+ return false;\r
+ }\r
+}\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
import com.gitblit.Constants;\r
+import com.gitblit.GitBlitException;\r
+import com.gitblit.models.SyndicatedEntryModel;\r
import com.sun.syndication.feed.synd.SyndContent;\r
import com.sun.syndication.feed.synd.SyndContentImpl;\r
import com.sun.syndication.feed.synd.SyndEntry;\r
\r
SyndFeed feed = new SyndFeedImpl();\r
feed.setFeedType("rss_2.0");\r
+ feed.setEncoding("UTF-8");\r
feed.setTitle(title);\r
feed.setLink(MessageFormat.format("{0}/summary/{1}", hostUrl,\r
StringUtils.encodeURL(repository)));\r
entry.setPublishedDate(commit.getCommitterIdent().getWhen());\r
\r
SyndContent content = new SyndContentImpl();\r
- content.setType("text/html");\r
- String html = StringUtils.escapeForHtml(commit.getFullMessage(), false);\r
- content.setValue(StringUtils.breakLinesForHtml(html));\r
+ content.setType("text/plain");\r
+ content.setValue(commit.getFullMessage());\r
entry.setDescription(content);\r
entries.add(entry);\r
}\r
feed.setEntries(entries);\r
\r
- OutputStreamWriter writer = new OutputStreamWriter(os);\r
+ OutputStreamWriter writer = new OutputStreamWriter(os, "UTF-8");\r
SyndFeedOutput output = new SyndFeedOutput();\r
output.output(feed, writer);\r
writer.close();\r
* is used.\r
* @param username\r
* @param password\r
- * @return the JSON message as a string\r
+ * @return a list of SyndicationModel entries\r
* @throws {@link IOException}\r
*/\r
- public static SyndFeed readFeed(String url, String repository, String branch,\r
- int numberOfEntries, String username, char[] password) throws IOException,\r
- FeedException {\r
+ public static List<SyndicatedEntryModel> readFeed(String url, String repository, String branch,\r
+ int numberOfEntries, String username, char[] password) throws IOException {\r
String feedUrl;\r
if (StringUtils.isEmpty(branch)) {\r
// no branch specified\r
URLConnection conn = ConnectionUtils.openReadConnection(feedUrl, username, password);\r
InputStream is = conn.getInputStream();\r
SyndFeedInput input = new SyndFeedInput();\r
- SyndFeed feed = input.build(new XmlReader(is));\r
+ SyndFeed feed = null;\r
+ try {\r
+ feed = input.build(new XmlReader(is));\r
+ } catch (FeedException f) {\r
+ throw new GitBlitException(f);\r
+ }\r
is.close();\r
- return feed;\r
+ List<SyndicatedEntryModel> entries = new ArrayList<SyndicatedEntryModel>();\r
+ for (Object o : feed.getEntries()) {\r
+ SyndEntryImpl entry = (SyndEntryImpl) o;\r
+ SyndicatedEntryModel model = new SyndicatedEntryModel();\r
+ model.repository = repository;\r
+ model.branch = branch;\r
+ model.title = entry.getTitle();\r
+ model.author = entry.getAuthor();\r
+ model.published = entry.getPublishedDate();\r
+ model.link = entry.getLink();\r
+ model.content = entry.getDescription().getValue();\r
+ model.contentType = entry.getDescription().getType();\r
+ entries.add(model);\r
+ }\r
+ return entries;\r
}\r
}\r
gb.heapUsed = used heap\r
gb.free = free\r
gb.version = version\r
-gb.releaseDate = release date
\ No newline at end of file
+gb.releaseDate = release date\r
+gb.date = date\r
+gb.recentCommits = recent commits\r
+gb.subscribe = subscribe
\ No newline at end of file
import org.eclipse.jgit.lib.Repository;\r
import org.eclipse.jgit.revwalk.RevCommit;\r
\r
-import com.gitblit.client.GitblitFeed;\r
+import com.gitblit.models.SyndicatedEntryModel;\r
import com.gitblit.utils.JGitUtils;\r
import com.gitblit.utils.SyndicationUtils;\r
-import com.sun.syndication.feed.synd.SyndFeed;\r
\r
public class SyndicationUtilsTest extends TestCase {\r
\r
}\r
\r
public void testFeedRead() throws Exception {\r
- GitblitFeed reader = new GitblitFeed("https://localhost:8443", "ticgit.git", "master");\r
- SyndFeed feed = reader.update(5, "admin", "admin".toCharArray());\r
+ List<SyndicatedEntryModel> feed = SyndicationUtils.readFeed("https://localhost:8443",\r
+ "ticgit.git", "master", 5, "admin", "admin".toCharArray());\r
assertTrue(feed != null);\r
- assertTrue(feed.getEntries().size() > 0);\r
- assertEquals(5, feed.getEntries().size());\r
+ assertTrue(feed.size() > 0);\r
+ assertEquals(5, feed.size());\r
}\r
}
\ No newline at end of file