build.xml
@@ -448,6 +448,8 @@ <resource file="${basedir}/resources/book_16x16.png" /> <resource file="${basedir}/resources/bug_16x16.png" /> <resource file="${basedir}/resources/health_16x16.png" /> <resource file="${basedir}/resources/feed_16x16.png" /> <resource file="${basedir}/resources/bullet_feed.png" /> <resource file="${basedir}/resources/blank.png" /> <resource file="${basedir}/src/com/gitblit/wicket/GitBlitWebApp.properties" /> docs/00_index.mkd
@@ -52,6 +52,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 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 resources/bullet_feed.png
src/com/gitblit/build/Build.java
@@ -138,6 +138,8 @@ public static void manager(DownloadListener listener) { downloadListener = listener; downloadFromApache(MavenObject.GSON, BuildType.RUNTIME); downloadFromApache(MavenObject.ROME, BuildType.RUNTIME); downloadFromApache(MavenObject.JDOM, BuildType.RUNTIME); downloadFromApache(MavenObject.JSCH, BuildType.RUNTIME); downloadFromEclipse(MavenObject.JGIT, BuildType.RUNTIME); src/com/gitblit/client/DateCellRenderer.java
@@ -49,18 +49,23 @@ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (value instanceof Date) { Date date = (Date) value; String timeAgo; String strDate; String title; String dateString; if (date.getTime() == 0) { timeAgo = "--"; strDate = "never"; title = "--"; dateString = "never"; } else { timeAgo = TimeUtils.timeAgo(date); strDate = new SimpleDateFormat(pattern).format((Date) value); title = TimeUtils.timeAgo(date); dateString = new SimpleDateFormat(pattern).format((Date) value); } this.setText(timeAgo); this.setToolTipText(strDate); if ((System.currentTimeMillis() - date.getTime()) > 10 * 24 * 60 * 60 * 1000L) { String tmp = dateString; dateString = title; title = tmp; } this.setText(title); this.setToolTipText(dateString); } return this; } src/com/gitblit/client/GitblitClient.java
@@ -19,8 +19,10 @@ 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.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 @@ -44,6 +49,8 @@ public class GitblitClient implements Serializable { private static final long serialVersionUID = 1L; protected final GitblitRegistration reg; public final String url; @@ -63,22 +70,35 @@ private final List<FederationModel> federationRegistrations; private final List<SyndicatedEntryModel> 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<UserModel>(); this.allRepositories = new ArrayList<RepositoryModel>(); this.federationRegistrations = new ArrayList<FederationModel>(); this.syndicatedEntries = new ArrayList<SyndicatedEntryModel>(); } 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 @@ } try { // credentials may not have administrator access // or server may have disabled rpc administration refreshStatus(); allowAdministration = true; } catch (UnauthorizedException e) { @@ -133,6 +155,7 @@ allRepositories.clear(); allRepositories.addAll(repositories.values()); Collections.sort(allRepositories); updateSubscribedStates(); return allRepositories; } @@ -153,6 +176,110 @@ return status; } public List<SyndicatedEntryModel> refreshSubscribedFeeds() throws IOException { Set<SyndicatedEntryModel> allFeeds = new HashSet<SyndicatedEntryModel>(); 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<SyndicatedEntryModel> 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<String> subscribedRepositories = new HashSet<String>(); 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<SyndicatedEntryModel> 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<String>(); } 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<SyndicatedEntryModel> toRemove = new ArrayList<SyndicatedEntryModel>(); 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<FederationModel> refreshFederationRegistrations() throws IOException { List<FederationModel> list = RpcUtils.getFederationRegistrations(url, account, password); federationRegistrations.clear(); src/com/gitblit/client/GitblitManager.java
@@ -31,6 +31,7 @@ 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; @@ -208,7 +209,7 @@ // 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<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override @@ -303,6 +304,10 @@ 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<String>(Arrays.asList(feeds)); } reg.lastLogin = lastLogin; registrations.put(reg.name, reg); } @@ -333,6 +338,9 @@ 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) { src/com/gitblit/client/GitblitPanel.java
@@ -58,6 +58,7 @@ 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; @@ -75,6 +76,8 @@ private final int margin = 5; private final Insets insets = new Insets(margin, margin, margin, margin); private final RegistrationsDialog.RegistrationListener listener; private GitblitClient gitblit; @@ -96,14 +99,6 @@ private JButton delRepository; private NameRenderer nameRenderer; private IndicatorsRenderer typeRenderer; private DefaultTableCellRenderer ownerRenderer; private DefaultTableCellRenderer sizeRenderer; private TableRowSorter<RepositoriesTableModel> defaultRepositoriesSorter; private TableRowSorter<UsersTableModel> defaultUsersSorter; @@ -120,15 +115,19 @@ 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 @@ } }); 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)); } }); sizeRenderer = new DefaultTableCellRenderer(); NameRenderer nameRenderer = new NameRenderer(true); IndicatorsRenderer typeRenderer = new IndicatorsRenderer(); 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<RepositoriesTableModel>(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 @@ 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 @@ repositoryControls.add(createRepository); repositoryControls.add(editRepository); repositoryControls.add(delRepository); repositoryControls.add(subscribeRepository); JPanel repositoriesPanel = new JPanel(new BorderLayout(margin, margin)) { @@ -290,6 +299,94 @@ repositoriesTable.getColumn(name).setMinWidth(maxWidth); repositoriesTable.getColumn(name).setMaxWidth(maxWidth); } } 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() { @@ -323,9 +420,10 @@ } }); NameRenderer nameRenderer = new NameRenderer(); usersModel = new UsersTableModel(); defaultUsersSorter = new TableRowSorter<UsersTableModel>(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 @@ } }); NameRenderer nameRenderer = new NameRenderer(); final SettingPanel settingPanel = new SettingPanel(); settingsModel = new SettingsTableModel(); defaultSettingsSorter = new TableRowSorter<SettingsTableModel>(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 @@ updateRepositoriesTable(); Utils.packColumns(repositoriesTable, 5); updateFeedsTable(); Utils.packColumns(syndicationEntriesTable, 5); if (gitblit.allowManagement()) { updateUsersTable(); } else { @@ -548,6 +650,14 @@ repositoriesModel.fireTableDataChanged(); repositoriesHeader.setText(Translation.get("gb.repositories") + " (" + 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() { @@ -811,6 +921,89 @@ } } 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 src/com/gitblit/client/GitblitRegistration.java
@@ -17,6 +17,7 @@ import java.io.Serializable; import java.util.Date; import java.util.List; import com.gitblit.utils.StringUtils; @@ -36,6 +37,7 @@ char[] password; boolean savePassword; Date lastLogin; List<String> feeds; public GitblitRegistration(String name, String url, String account, char[] password) { this.url = url; src/com/gitblit/client/NameRenderer.java
@@ -18,8 +18,11 @@ 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 @@ -34,11 +37,24 @@ 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(Color group, Color repo) { public NameRenderer(boolean showIcon) { this(Color.gray, new Color(0x00, 0x69, 0xD6), showIcon); } 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 = "<span style='color:" + getHexColor(group) + "'>"; setForeground(repo); } @@ -55,6 +71,8 @@ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (value instanceof RepositoryModel) { RepositoryModel model = (RepositoryModel) value; String name = value.toString(); int lastSlash = name.lastIndexOf('/'); if (!isSelected && lastSlash > -1) { @@ -64,6 +82,16 @@ } else { this.setText(name); } if (displayIcon) { if (model.subscribed) { setIcon(subscribedIcon); } else { setIcon(blankIcon); } } } else { this.setText(value.toString()); } return this; } } src/com/gitblit/client/RegistrationsDialog.java
@@ -92,7 +92,7 @@ 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 src/com/gitblit/client/StatusPanel.java
@@ -92,7 +92,7 @@ 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); src/com/gitblit/client/SyndicatedEntryTableModel.java
New file @@ -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<SyndicatedEntryModel> * * @author James Moger * */ public class SyndicatedEntryTableModel extends AbstractTableModel { private static final long serialVersionUID = 1L; List<SyndicatedEntryModel> entries; enum Columns { Date, Repository, Author, Message; @Override public String toString() { return name().replace('_', ' '); } } public SyndicatedEntryTableModel() { this(new ArrayList<SyndicatedEntryModel>()); } public SyndicatedEntryTableModel(List<SyndicatedEntryModel> entries) { setEntries(entries); } public void setEntries(List<SyndicatedEntryModel> 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 <code>Object.class</code> regardless of <code>columnIndex</code>. * * @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); } } src/com/gitblit/client/Utils.java
@@ -37,14 +37,18 @@ public class Utils { public static JTable newTable(TableModel model) { 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, 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; } src/com/gitblit/models/RepositoryModel.java
@@ -55,6 +55,7 @@ public String frequency; public String origin; public String size; public boolean subscribed; public RepositoryModel() { this("", "", "", new Date(0)); src/com/gitblit/models/SyndicatedEntryModel.java
New file @@ -0,0 +1,59 @@ /* * 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.models; import java.io.Serializable; import java.util.Date; /** * SyndicationEntryModel represents an entry in a syndication (RSS) feed. * * @author James Moger */ public class SyndicatedEntryModel implements Serializable, Comparable<SyndicatedEntryModel> { 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; } } src/com/gitblit/utils/SyndicationUtils.java
@@ -27,6 +27,8 @@ 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 @@ 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 @@ 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 @@ * 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<SyndicatedEntryModel> 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 @@ 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<SyndicatedEntryModel> entries = new ArrayList<SyndicatedEntryModel>(); 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; } } src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -176,3 +176,6 @@ gb.free = free gb.version = version gb.releaseDate = release date gb.date = date gb.recentCommits = recent commits gb.subscribe = subscribe tests/com/gitblit/tests/SyndicationUtilsTest.java
@@ -23,10 +23,9 @@ 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 void testFeedRead() throws Exception { GitblitFeed reader = new GitblitFeed("https://localhost:8443", "ticgit.git", "master"); SyndFeed feed = reader.update(5, "admin", "admin".toCharArray()); List<SyndicatedEntryModel> 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()); } }