From 17820f3a1153250a325fed23dfc2da59ce6ba777 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 31 Oct 2011 22:47:21 -0400 Subject: [PATCH] More feeds work in Manager --- src/com/gitblit/RpcServlet.java | 15 +- src/com/gitblit/build/Build.java | 6 - .../gitblit/client/BooleanCellRenderer.java | 50 ++++++ src/com/gitblit/client/FeedsTableModel.java | 126 ++++++++++++++ src/com/gitblit/client/GitblitClient.java | 147 +++++++--------- src/com/gitblit/client/GitblitManager.java | 21 ++- src/com/gitblit/client/GitblitPanel.java | 74 ++++---- .../gitblit/client/GitblitRegistration.java | 22 ++- src/com/gitblit/client/NameRenderer.java | 48 ++---- .../client/SubscribedRepositoryRenderer.java | 62 +++++++ .../gitblit/client/SubscriptionsDialog.java | 159 ++++++++++++++++++ .../client/SyndicatedEntryTableModel.java | 8 +- src/com/gitblit/client/Translation.java | 42 ++--- src/com/gitblit/models/FeedModel.java | 96 +++++++++++ src/com/gitblit/models/RepositoryModel.java | 1 - src/com/gitblit/utils/RpcUtils.java | 37 +++- .../gitblit/wicket/GitBlitWebApp.properties | 5 +- tests/com/gitblit/tests/RpcTests.java | 2 +- 18 files changed, 700 insertions(+), 221 deletions(-) create mode 100644 src/com/gitblit/client/BooleanCellRenderer.java create mode 100644 src/com/gitblit/client/FeedsTableModel.java create mode 100644 src/com/gitblit/client/SubscribedRepositoryRenderer.java create mode 100644 src/com/gitblit/client/SubscriptionsDialog.java create mode 100644 src/com/gitblit/models/FeedModel.java diff --git a/src/com/gitblit/RpcServlet.java b/src/com/gitblit/RpcServlet.java index b068a39b..585770e8 100644 --- a/src/com/gitblit/RpcServlet.java +++ b/src/com/gitblit/RpcServlet.java @@ -91,28 +91,31 @@ public class RpcServlet extends JsonServlet { } result = repositories; } else if (RpcRequest.LIST_BRANCHES.equals(reqType)) { - // list all branches in all repositories accessible to user - Map> allBranches = new HashMap>(); + // list all local branches in all repositories accessible to user + Map> localBranches = new HashMap>(); List models = GitBlit.self().getRepositoryModels(user); for (RepositoryModel model : models) { if (!model.hasCommits) { // skip empty repository continue; } - // get branches + // get local branches Repository repository = GitBlit.self().getRepository(model.name); List refs = JGitUtils.getLocalBranches(repository, false, -1); - refs.addAll(JGitUtils.getRemoteBranches(repository, false, -1)); + if (model.showRemoteBranches) { + // add remote branches if repository displays them + refs.addAll(JGitUtils.getRemoteBranches(repository, false, -1)); + } if (refs.size() > 0) { List branches = new ArrayList(); for (RefModel ref : refs) { branches.add(ref.getName()); } - allBranches.put(model.name, branches); + localBranches.put(model.name, branches); } repository.close(); } - result = allBranches; + result = localBranches; } else if (RpcRequest.LIST_USERS.equals(reqType)) { // list users List names = GitBlit.self().getAllUsernames(); diff --git a/src/com/gitblit/build/Build.java b/src/com/gitblit/build/Build.java index 4389eace..ee53ff00 100644 --- a/src/com/gitblit/build/Build.java +++ b/src/com/gitblit/build/Build.java @@ -398,12 +398,6 @@ public class Build { "46a386136c901748e6a3af67ebde6c22bc6b4524", "e223571d77769cdafde59040da235842f3326453"); - public static final MavenObject SLF4JNOP = new MavenObject("SLF4J NOP", "org/slf4j", - "slf4j-nop", "1.6.1", 4800, 4100, 32300, - "70249094d4e5653b6bdfea46f3a1a4165c1e1993", - "4a8e77f7bf6897a3c3b7fc3acb4c862dfb905baa", - "24b2b46f9025f2db53b5b32143f7832538fa3178"); - public static final MavenObject SLF4LOG4J = new MavenObject("SLF4J LOG4J", "org/slf4j", "slf4j-log4j12", "1.6.1", 9800, 9500, 52400, "bd245d6746cdd4e6203e976e21d597a46f115802", diff --git a/src/com/gitblit/client/BooleanCellRenderer.java b/src/com/gitblit/client/BooleanCellRenderer.java new file mode 100644 index 00000000..c8341df6 --- /dev/null +++ b/src/com/gitblit/client/BooleanCellRenderer.java @@ -0,0 +1,50 @@ +/* + * 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.awt.Component; +import java.io.Serializable; + +import javax.swing.JCheckBox; +import javax.swing.JTable; +import javax.swing.SwingConstants; +import javax.swing.table.TableCellRenderer; + +/** + * Boolean checkbox cell renderer. + * + * @author James Moger + * + */ +public class BooleanCellRenderer extends JCheckBox implements TableCellRenderer, Serializable { + + private static final long serialVersionUID = 1L; + + public BooleanCellRenderer() { + super(); + setOpaque(false); + setHorizontalAlignment(SwingConstants.CENTER); + } + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int column) { + if (value instanceof Boolean) { + boolean checked = (Boolean) value; + this.setSelected(checked); + } + return this; + } +} \ No newline at end of file diff --git a/src/com/gitblit/client/FeedsTableModel.java b/src/com/gitblit/client/FeedsTableModel.java new file mode 100644 index 00000000..a628fc82 --- /dev/null +++ b/src/com/gitblit/client/FeedsTableModel.java @@ -0,0 +1,126 @@ +/* + * 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.List; + +import javax.swing.table.AbstractTableModel; + +import com.gitblit.models.FeedModel; + +/** + * Table model of a list of available feeds. + * + * @author James Moger + * + */ +public class FeedsTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + + List list; + + enum Columns { + Subscribed, Repository, Branch, Max_Length; + + @Override + public String toString() { + return name().replace('_', ' '); + } + } + + public FeedsTableModel() { + this(new ArrayList()); + } + + public FeedsTableModel(List feeds) { + this.list = feeds; + Collections.sort(this.list); + } + + @Override + public int getRowCount() { + return list.size(); + } + + @Override + public int getColumnCount() { + return Columns.values().length; + } + + @Override + public String getColumnName(int column) { + Columns col = Columns.values()[column]; + switch (col) { + case Repository: + return Translation.get("gb.repository"); + case Branch: + return Translation.get("gb.branch"); + } + return ""; + } + + /** + * Returns Object.class regardless of columnIndex. + * + * @param columnIndex + * the column being queried + * @return the Object.class + */ + public Class getColumnClass(int columnIndex) { + Columns col = Columns.values()[columnIndex]; + switch (col) { + case Subscribed: + return Boolean.class; + case Max_Length: + return Integer.class; + } + return String.class; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + Columns col = Columns.values()[columnIndex]; + switch (col) { + case Subscribed: + return true; + } + return false; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + FeedModel model = list.get(rowIndex); + Columns col = Columns.values()[columnIndex]; + switch (col) { + case Repository: + return model.repository; + case Branch: + return model.branch; + case Max_Length: + return model.maxRetrieval; + case Subscribed: + return model.subscribed; + } + return null; + } + + public FeedModel get(int modelRow) { + return list.get(modelRow); + } +} diff --git a/src/com/gitblit/client/GitblitClient.java b/src/com/gitblit/client/GitblitClient.java index ccde45a4..74e0e08d 100644 --- a/src/com/gitblit/client/GitblitClient.java +++ b/src/com/gitblit/client/GitblitClient.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -30,13 +31,13 @@ import com.gitblit.GitBlitException.UnauthorizedException; import com.gitblit.GitBlitException.UnknownRequestException; import com.gitblit.Keys; import com.gitblit.models.FederationModel; +import com.gitblit.models.FeedModel; 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; /** @@ -70,8 +71,12 @@ public class GitblitClient implements Serializable { private final List federationRegistrations; + private final List availableFeeds; + private final List syndicatedEntries; + private final Set subscribedRepositories; + private ServerStatus status; public GitblitClient(GitblitRegistration reg) { @@ -83,10 +88,13 @@ public class GitblitClient implements Serializable { this.allUsers = new ArrayList(); this.allRepositories = new ArrayList(); this.federationRegistrations = new ArrayList(); + this.availableFeeds = new ArrayList(); this.syndicatedEntries = new ArrayList(); + this.subscribedRepositories = new HashSet(); } public void login() throws IOException { + refreshAvailableFeeds(); refreshRepositories(); try { @@ -95,7 +103,7 @@ public class GitblitClient implements Serializable { } catch (IOException e) { e.printStackTrace(); } - + try { // credentials may not have administrator access // or server may have disabled rpc management @@ -155,7 +163,7 @@ public class GitblitClient implements Serializable { allRepositories.clear(); allRepositories.addAll(repositories.values()); Collections.sort(allRepositories); - updateSubscribedStates(); + markSubscribedFeeds(); return allRepositories; } @@ -176,108 +184,69 @@ 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; + public List getAvailableFeeds() { + return availableFeeds; } - 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 getAvailableFeeds(RepositoryModel repository) { + List repositoryFeeds = new ArrayList(); + if (repository == null) { + return repositoryFeeds; + } + for (FeedModel feed : availableFeeds) { + if (feed.repository.equalsIgnoreCase(repository.name)) { + repositoryFeeds.add(feed); } } + return repositoryFeeds; } - public List getSyndicatedEntries() { - return syndicatedEntries; + public List refreshAvailableFeeds() throws IOException { + List feeds = RpcUtils.getBranchFeeds(url, account, password); + availableFeeds.clear(); + availableFeeds.addAll(feeds); + markSubscribedFeeds(); + return availableFeeds; } - 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]; - } + public List refreshSubscribedFeeds() throws IOException { + Set allEntries = new HashSet(); + if (reg.feeds.size() > 0) { + for (FeedModel feed : reg.feeds) { + feed.lastRefresh = new Date(); + List entries = SyndicationUtils.readFeed(url, + feed.repository, feed.branch, feed.maxRetrieval, account, password); + allEntries.addAll(entries); } } - return false; + syndicatedEntries.clear(); + syndicatedEntries.addAll(allEntries); + Collections.sort(syndicatedEntries); + return syndicatedEntries; } - 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 void updateSubscribedFeeds(List list) { + reg.updateSubscribedFeeds(list); + markSubscribedFeeds(); } - 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); - } - } - } + private void markSubscribedFeeds() { + subscribedRepositories.clear(); + for (FeedModel feed : availableFeeds) { + // mark feed in the available list as subscribed + feed.subscribed = reg.feeds.contains(feed); + if (feed.subscribed) { + subscribedRepositories.add(feed.repository.toLowerCase()); } } - updateSubscribedStates(); - return true; + } + + public boolean isSubscribed(RepositoryModel repository) { + return subscribedRepositories.contains(repository.name.toLowerCase()); + } + + public List getSyndicatedEntries() { + return syndicatedEntries; } public List refreshFederationRegistrations() throws IOException { diff --git a/src/com/gitblit/client/GitblitManager.java b/src/com/gitblit/client/GitblitManager.java index 8f396f34..d9c2dccf 100644 --- a/src/com/gitblit/client/GitblitManager.java +++ b/src/com/gitblit/client/GitblitManager.java @@ -31,7 +31,6 @@ 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; @@ -62,6 +61,7 @@ import org.eclipse.jgit.util.FS; import com.gitblit.Constants; import com.gitblit.GitBlitException.ForbiddenException; +import com.gitblit.models.FeedModel; import com.gitblit.utils.StringUtils; /** @@ -205,8 +205,8 @@ public class GitblitManager extends JFrame implements RegistrationsDialog.Regist return; } // preserve feeds - newReg.feeds = reg.feeds; - + newReg.feeds.addAll(reg.feeds); + // use new reg reg = newReg; } @@ -311,7 +311,11 @@ public class GitblitManager extends JFrame implements RegistrationsDialog.Regist 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)); + // deserialize the field definitions + for (String definition : feeds) { + FeedModel feed = new FeedModel(definition); + reg.feeds.add(feed); + } } reg.lastLogin = lastLogin; registrations.put(reg.name, reg); @@ -343,8 +347,13 @@ 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); + // serialize the feed definitions + List definitions = new ArrayList(); + for (FeedModel feed : reg.feeds) { + definitions.add(feed.toString()); + } + if (definitions.size() > 0) { + config.setStringList("servers", reg.name, "feeds", definitions); } config.save(); return true; diff --git a/src/com/gitblit/client/GitblitPanel.java b/src/com/gitblit/client/GitblitPanel.java index 10e9c2d4..64e85eaf 100644 --- a/src/com/gitblit/client/GitblitPanel.java +++ b/src/com/gitblit/client/GitblitPanel.java @@ -56,6 +56,7 @@ import javax.swing.table.TableRowSorter; import com.gitblit.Constants.RpcRequest; import com.gitblit.client.ClosableTabComponent.CloseTabListener; +import com.gitblit.models.FeedModel; import com.gitblit.models.RepositoryModel; import com.gitblit.models.SettingModel; import com.gitblit.models.SyndicatedEntryModel; @@ -127,7 +128,7 @@ public class GitblitPanel extends JPanel implements CloseTabListener { tabs = new JTabbedPane(JTabbedPane.BOTTOM); tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel()); - tabs.addTab(Translation.get("gb.recentCommits"), createFeedsPanel()); + tabs.addTab(Translation.get("gb.recentActivity"), createFeedsPanel()); tabs.addTab(Translation.get("gb.users"), createUsersPanel()); tabs.addTab(Translation.get("gb.settings"), createSettingsPanel()); tabs.addTab(Translation.get("gb.status"), createStatusPanel()); @@ -186,11 +187,12 @@ public class GitblitPanel extends JPanel implements CloseTabListener { subscribeRepository.setEnabled(false); subscribeRepository.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { - subscribeRepository(getSelectedRepositories().get(0)); + List feeds = gitblit.getAvailableFeeds(getSelectedRepositories().get(0)); + subscribeFeeds(feeds); } }); - NameRenderer nameRenderer = new NameRenderer(true); + SubscribedRepositoryRenderer nameRenderer = new SubscribedRepositoryRenderer(gitblit); IndicatorsRenderer typeRenderer = new IndicatorsRenderer(); DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer(); @@ -333,20 +335,35 @@ public class GitblitPanel extends JPanel implements CloseTabListener { } }); + JButton subscribeFeeds = new JButton(Translation.get("gb.subscribe") + "..."); + subscribeFeeds.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + subscribeFeeds(gitblit.getAvailableFeeds()); + } + }); + JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); controls.add(refreshFeeds); + controls.add(subscribeFeeds); 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"); + feedsHeader = new HeaderPanel(Translation.get("gb.recentActivity"), "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); + name = syndicationEntriesTable.getColumnName(SyndicatedEntryTableModel.Columns.Repository + .ordinal()); + syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer); + + name = syndicationEntriesTable.getColumnName(SyndicatedEntryTableModel.Columns.Branch + .ordinal()); + syndicationEntriesTable.getColumn(name).setCellRenderer(nameRenderer); syndicationEntriesTable.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { @@ -656,7 +673,7 @@ public class GitblitPanel extends JPanel implements CloseTabListener { syndicationModel.entries.clear(); syndicationModel.entries.addAll(gitblit.getSyndicatedEntries()); syndicationModel.fireTableDataChanged(); - feedsHeader.setText(Translation.get("gb.recentCommits") + " (" + feedsHeader.setText(Translation.get("gb.recentActivity") + " (" + gitblit.getSyndicatedEntries().size() + ")"); } @@ -921,40 +938,21 @@ 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 subscribeFeeds(final List feeds) { + SubscriptionsDialog dialog = new SubscriptionsDialog(feeds) { + + private static final long serialVersionUID = 1L; + + @Override + public void save() { + gitblit.updateSubscribedFeeds(feeds); + listener.saveRegistration(gitblit.reg.name, gitblit.reg); + setVisible(false); + updateRepositoriesTable(); } - } + }; + dialog.setLocationRelativeTo(GitblitPanel.this); + dialog.setVisible(true); } protected void refreshFeeds() { diff --git a/src/com/gitblit/client/GitblitRegistration.java b/src/com/gitblit/client/GitblitRegistration.java index cbd4324d..05b23d01 100644 --- a/src/com/gitblit/client/GitblitRegistration.java +++ b/src/com/gitblit/client/GitblitRegistration.java @@ -16,9 +16,11 @@ package com.gitblit.client; import java.io.Serializable; +import java.util.ArrayList; import java.util.Date; import java.util.List; +import com.gitblit.models.FeedModel; import com.gitblit.utils.StringUtils; /** @@ -37,7 +39,7 @@ public class GitblitRegistration implements Serializable, Comparable feeds; + final List feeds; public GitblitRegistration(String name, String url, String account, char[] password) { this.url = url; @@ -49,6 +51,24 @@ public class GitblitRegistration implements Serializable, Comparable(); + } + + public void updateSubscribedFeeds(List list) { + for (FeedModel feed : list) { + if (feeds.contains(feed)) { + // possibly unsubscribe/remove feed + int index = feeds.indexOf(feed); + FeedModel existingFeed = feeds.get(index); + existingFeed.subscribed = feed.subscribed; + if (!existingFeed.subscribed) { + feeds.remove(index); + } + } else if (feed.subscribed) { + // new subscription + feeds.add(feed); + } + } } @Override diff --git a/src/com/gitblit/client/NameRenderer.java b/src/com/gitblit/client/NameRenderer.java index ce042555..f334d42a 100644 --- a/src/com/gitblit/client/NameRenderer.java +++ b/src/com/gitblit/client/NameRenderer.java @@ -18,12 +18,9 @@ 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. @@ -35,26 +32,13 @@ public class NameRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; - final String groupSpan; - - private final boolean displayIcon; - - private final ImageIcon blankIcon; - - private final ImageIcon subscribedIcon; + private final String groupSpan; public NameRenderer() { - this(false); - } - - public NameRenderer(boolean showIcon) { - this(Color.gray, new Color(0x00, 0x69, 0xD6), showIcon); + this(Color.gray, new Color(0x00, 0x69, 0xD6)); } - 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; + private NameRenderer(Color group, Color repo) { groupSpan = ""; setForeground(repo); } @@ -71,26 +55,14 @@ 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); - 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); - } - } + 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(value.toString()); + this.setText(name); } return this; } diff --git a/src/com/gitblit/client/SubscribedRepositoryRenderer.java b/src/com/gitblit/client/SubscribedRepositoryRenderer.java new file mode 100644 index 00000000..9943333f --- /dev/null +++ b/src/com/gitblit/client/SubscribedRepositoryRenderer.java @@ -0,0 +1,62 @@ +/* + * 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.awt.Component; + +import javax.swing.ImageIcon; +import javax.swing.JTable; + +import com.gitblit.models.RepositoryModel; + +/** + * Displays a subscribed icon on the left of the repository name, if there is at + * least one subscribed branch. + * + * @author James Moger + * + */ +public class SubscribedRepositoryRenderer extends NameRenderer { + + private static final long serialVersionUID = 1L; + + private final GitblitClient gitblit; + + private final ImageIcon blankIcon; + + private final ImageIcon subscribedIcon; + + public SubscribedRepositoryRenderer(GitblitClient gitblit) { + super(); + this.gitblit = gitblit; + blankIcon = new ImageIcon(getClass().getResource("/blank.png")); + subscribedIcon = new ImageIcon(getClass().getResource("/bullet_feed.png")); + } + + 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; + if (gitblit.isSubscribed(model)) { + setIcon(subscribedIcon); + } else { + setIcon(blankIcon); + } + } + return this; + } +} diff --git a/src/com/gitblit/client/SubscriptionsDialog.java b/src/com/gitblit/client/SubscriptionsDialog.java new file mode 100644 index 00000000..64a88d9c --- /dev/null +++ b/src/com/gitblit/client/SubscriptionsDialog.java @@ -0,0 +1,159 @@ +/* + * 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.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.KeyStroke; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.gitblit.models.FeedModel; + +/** + * Displays a list of repository branches and allows the user to check or + * uncheck branches. + * + * @author James Moger + * + */ +public abstract class SubscriptionsDialog extends JDialog { + + private static final long serialVersionUID = 1L; + + private final List feeds; + + private JTable feedsTable; + + private FeedsTableModel model; + + public SubscriptionsDialog(List registrations) { + super(); + this.feeds = registrations; + setTitle(Translation.get("gb.manage")); + setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); + initialize(); + setSize(600, 400); + } + + @Override + protected JRootPane createRootPane() { + KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); + JRootPane rootPane = new JRootPane(); + rootPane.registerKeyboardAction(new ActionListener() { + public void actionPerformed(ActionEvent actionEvent) { + setVisible(false); + } + }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW); + return rootPane; + } + + private void initialize() { + NameRenderer nameRenderer = new NameRenderer(); + model = new FeedsTableModel(feeds); + feedsTable = Utils.newTable(model, Utils.DATE_FORMAT); + feedsTable.setRowHeight(nameRenderer.getFont().getSize() + 8); + feedsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + int viewRow = feedsTable.getSelectedRow(); + if (viewRow == -1) { + return; + } + int modelRow = feedsTable.convertRowIndexToModel(viewRow); + FeedModel feed = model.get(modelRow); + feed.subscribed = !feed.subscribed; + model.fireTableDataChanged(); + } + }); + + String repository = feedsTable.getColumnName(FeedsTableModel.Columns.Repository.ordinal()); + feedsTable.getColumn(repository).setCellRenderer(nameRenderer); + + String branch = feedsTable.getColumnName(FeedsTableModel.Columns.Branch.ordinal()); + feedsTable.getColumn(branch).setCellRenderer(nameRenderer); + + String subscribed = feedsTable.getColumnName(FeedsTableModel.Columns.Subscribed.ordinal()); + feedsTable.getColumn(subscribed).setCellRenderer(new BooleanCellRenderer()); + feedsTable.getColumn(subscribed).setMinWidth(30); + feedsTable.getColumn(subscribed).setMaxWidth(30); + + Utils.packColumns(feedsTable, 5); + + final JButton cancel = new JButton(Translation.get("gb.cancel")); + cancel.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + setVisible(false); + } + }); + + final JButton save = new JButton(Translation.get("gb.save")); + save.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent event) { + save(); + } + }); + + feedsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getValueIsAdjusting()) { + return; + } + } + }); + + JPanel controls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0)); + controls.add(cancel); + controls.add(save); + + final Insets insets = new Insets(5, 5, 5, 5); + JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) { + + private static final long serialVersionUID = 1L; + + public Insets getInsets() { + return insets; + } + }; + centerPanel.add(new HeaderPanel(Translation.get("gb.subscribe") + "...", "feed_16x16.png"), + BorderLayout.NORTH); + centerPanel.add(new JScrollPane(feedsTable), BorderLayout.CENTER); + centerPanel.add(controls, BorderLayout.SOUTH); + + getContentPane().setLayout(new BorderLayout(5, 5)); + getContentPane().add(centerPanel, BorderLayout.CENTER); + } + + public abstract void save(); +} diff --git a/src/com/gitblit/client/SyndicatedEntryTableModel.java b/src/com/gitblit/client/SyndicatedEntryTableModel.java index 4f25c9ba..db62b1ae 100644 --- a/src/com/gitblit/client/SyndicatedEntryTableModel.java +++ b/src/com/gitblit/client/SyndicatedEntryTableModel.java @@ -25,7 +25,7 @@ import javax.swing.table.AbstractTableModel; import com.gitblit.models.SyndicatedEntryModel; /** - * Table model of List + * Table model for a list of retrieved feed entries. * * @author James Moger * @@ -37,7 +37,7 @@ public class SyndicatedEntryTableModel extends AbstractTableModel { List entries; enum Columns { - Date, Repository, Author, Message; + Date, Repository, Branch, Author, Message; @Override public String toString() { @@ -76,6 +76,8 @@ public class SyndicatedEntryTableModel extends AbstractTableModel { return Translation.get("gb.date"); case Repository: return Translation.get("gb.repository"); + case Branch: + return Translation.get("gb.branch"); case Author: return Translation.get("gb.author"); case Message: @@ -107,6 +109,8 @@ public class SyndicatedEntryTableModel extends AbstractTableModel { return entry.published; case Repository: return entry.repository; + case Branch: + return entry.branch; case Author: return entry.author; case Message: diff --git a/src/com/gitblit/client/Translation.java b/src/com/gitblit/client/Translation.java index cd5515b1..2e7b5bb4 100644 --- a/src/com/gitblit/client/Translation.java +++ b/src/com/gitblit/client/Translation.java @@ -15,42 +15,34 @@ */ package com.gitblit.client; -import java.io.InputStream; -import java.util.Properties; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +/** + * Loads the Gitblit language resource file. + * + * @author James Moger + * + */ public class Translation { - private final static Properties translation; + private final static ResourceBundle translation; static { - translation = new Properties(); - InputStream is = null; + ResourceBundle bundle; try { - is = Translation.class.getResource("/com/gitblit/wicket/GitBlitWebApp.properties") - .openStream(); - } catch (Throwable t) { - try { - is = Translation.class.getResource("/GitBlitWebApp.properties").openStream(); - } catch (Throwable x) { - } - } - if (is != null) { - try { - translation.load(is); - } catch (Throwable t) { - - } finally { - try { - is.close(); - } catch (Throwable t) { - } - } + // development location + bundle = ResourceBundle.getBundle("com/gitblit/wicket/GitBlitWebApp"); + } catch (MissingResourceException e) { + // runtime location + bundle = ResourceBundle.getBundle("GitBlitWebApp"); } + translation = bundle; } public static String get(String key) { if (translation.containsKey(key)) { - return translation.getProperty(key).trim(); + return translation.getString(key).trim(); } return key; } diff --git a/src/com/gitblit/models/FeedModel.java b/src/com/gitblit/models/FeedModel.java new file mode 100644 index 00000000..67cff955 --- /dev/null +++ b/src/com/gitblit/models/FeedModel.java @@ -0,0 +1,96 @@ +/* + * 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.text.MessageFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import com.gitblit.utils.StringUtils; + +/** + * FeedModel represents a syndication (RSS) feed. + * + * @author James Moger + */ +public class FeedModel implements Serializable, Comparable { + + public String repository; + public String branch; + public int maxRetrieval; + public Date lastRefresh; + + public boolean subscribed; + + private static final long serialVersionUID = 1L; + + public FeedModel() { + this(""); + } + + public FeedModel(String definition) { + maxRetrieval = -1; + lastRefresh = new Date(0); + + String[] fields = definition.split(":"); + repository = fields[0]; + if (fields.length > 1) { + branch = fields[1]; + maxRetrieval = Integer.parseInt(fields[2]); + try { + lastRefresh = new SimpleDateFormat("yyyyMMddHHmmss").parse(fields[3]); + } catch (ParseException e) { + } + subscribed = true; + } + } + + @Override + public String toString() { + return MessageFormat.format("{0}:{1}:{2,number,0}:{3,date,yyyyMMddHHmmss}", repository, + branch, maxRetrieval, lastRefresh); + } + + @Override + public int compareTo(FeedModel o) { + int repositoryCompare = repository.compareTo(o.repository); + if (repositoryCompare == 0) { + // same repository + if (StringUtils.isEmpty(branch)) { + return 1; + } else if (StringUtils.isEmpty(o.branch)) { + return -1; + } + return branch.compareTo(o.branch); + } + return repositoryCompare; + } + + @Override + public int hashCode() { + return (repository + (StringUtils.isEmpty(branch) ? "" : branch)).toLowerCase().hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o instanceof FeedModel) { + return hashCode() == o.hashCode(); + } + return false; + } +} diff --git a/src/com/gitblit/models/RepositoryModel.java b/src/com/gitblit/models/RepositoryModel.java index af420612..9a774fbd 100644 --- a/src/com/gitblit/models/RepositoryModel.java +++ b/src/com/gitblit/models/RepositoryModel.java @@ -55,7 +55,6 @@ public class RepositoryModel implements Serializable, Comparable> getAllBranches(String serverUrl, - String account, char[] password) throws IOException { + public static Map> getBranches(String serverUrl, String account, + char[] password) throws IOException { String url = asLink(serverUrl, RpcRequest.LIST_BRANCHES); - Map> allReferences = JsonUtils.retrieveJson(url, - BRANCHES_TYPE, account, password); - return allReferences; + Map> branches = JsonUtils.retrieveJson(url, BRANCHES_TYPE, + account, password); + return branches; + } + + /** + * Retrieves a list of available branch feeds in the Gitblit server. + * + * @param serverUrl + * @param account + * @param password + * @return + * @throws IOException + */ + public static List getBranchFeeds(String serverUrl, String account, char[] password) + throws IOException { + List feeds = new ArrayList(); + Map> allBranches = getBranches(serverUrl, account, password); + for (Map.Entry> entry : allBranches.entrySet()) { + for (String branch : entry.getValue()) { + FeedModel feed = new FeedModel(); + feed.repository = entry.getKey(); + feed.branch = branch; + feeds.add(feed); + } + } + return feeds; } /** diff --git a/src/com/gitblit/wicket/GitBlitWebApp.properties b/src/com/gitblit/wicket/GitBlitWebApp.properties index e16b44db..6d20a729 100644 --- a/src/com/gitblit/wicket/GitBlitWebApp.properties +++ b/src/com/gitblit/wicket/GitBlitWebApp.properties @@ -177,5 +177,6 @@ gb.free = free gb.version = version gb.releaseDate = release date gb.date = date -gb.recentCommits = recent commits -gb.subscribe = subscribe \ No newline at end of file +gb.recentActivity = recent activity +gb.subscribe = subscribe +gb.branch = branch \ No newline at end of file diff --git a/tests/com/gitblit/tests/RpcTests.java b/tests/com/gitblit/tests/RpcTests.java index 637d488e..37a49559 100644 --- a/tests/com/gitblit/tests/RpcTests.java +++ b/tests/com/gitblit/tests/RpcTests.java @@ -244,7 +244,7 @@ public class RpcTests extends TestCase { } public void testBranches() throws Exception { - Map> branches = RpcUtils.getAllBranches(url, account, + Map> branches = RpcUtils.getBranches(url, account, password.toCharArray()); assertTrue(branches != null); assertTrue(branches.size() > 0); -- 2.39.5