docs/02_rpc.mkd
@@ -30,6 +30,7 @@ <tr><td>LIST_FEDERATION_RESULTS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List FederationModel</td></tr> <tr><td>LIST_FEDERATION_PROPOSALS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List FederationProposal </td></tr> <tr><td>LIST_FEDERATION_SETS</td><td>-</td><td><em>admin</em></td><td>-</td><td>List FederationSet </td></tr> <tr><td>LIST_SETTINGS</td><td>-</td><td><em>admin</em></td><td>-</td><td>Properties</td></tr> </table> ### RPC Client src/com/gitblit/Constants.java
@@ -204,7 +204,7 @@ LIST_REPOSITORIES, CREATE_REPOSITORY, EDIT_REPOSITORY, DELETE_REPOSITORY, LIST_USERS, CREATE_USER, EDIT_USER, DELETE_USER, LIST_REPOSITORY_MEMBERS, SET_REPOSITORY_MEMBERS, LIST_FEDERATION_REGISTRATIONS, LIST_FEDERATION_RESULTS, LIST_FEDERATION_PROPOSALS, LIST_FEDERATION_SETS; LIST_FEDERATION_PROPOSALS, LIST_FEDERATION_SETS, LIST_SETTINGS; public static RpcRequest fromName(String name) { for (RpcRequest type : values()) { src/com/gitblit/RpcServlet.java
@@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; @@ -163,6 +164,17 @@ } else { response.sendError(HttpServletResponse.SC_FORBIDDEN); } } else if (RpcRequest.LIST_SETTINGS.equals(reqType)) { // return the server's settings Properties settings = new Properties(); List<String> keys = GitBlit.getAllKeys(null); for (String key:keys) { String value = GitBlit.getString(key, null); if (value != null) { settings.put(key, value); } } result = settings; } // send the result of the request src/com/gitblit/client/EditRepositoryDialog.java
New file @@ -0,0 +1,224 @@ /* * 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.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.Constants.FederationStrategy; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; public class EditRepositoryDialog extends JDialog { private static final long serialVersionUID = 1L; private final RepositoryModel repository; private boolean canceled = true; private JTextField nameField; private JTextField descriptionField; private JCheckBox useTickets; private JCheckBox useDocs; private JCheckBox showRemoteBranches; private JCheckBox showReadme; private JCheckBox isFrozen; private JComboBox accessRestriction; private JComboBox federationStrategy; private JComboBox owner; private JPalette<String> usersPalette; private JPalette<String> setsPalette; public EditRepositoryDialog(List<UserModel> allusers) { this(new RepositoryModel(), allusers); setTitle("Create Repository"); } public EditRepositoryDialog(RepositoryModel aRepository, List<UserModel> allUsers) { super(); this.repository = new RepositoryModel(); initialize(aRepository, allUsers); setModal(true); setTitle("Edit Repository: " + aRepository.name); setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); } private void initialize(RepositoryModel anRepository, List<UserModel> allUsers) { nameField = new JTextField(anRepository.name == null ? "" : anRepository.name, 35); descriptionField = new JTextField(anRepository.description == null ? "" : anRepository.description, 35); owner = new JComboBox(allUsers.toArray()); if (!StringUtils.isEmpty(anRepository.owner)) { UserModel currentOwner = null; for (UserModel user : allUsers) { if (user.username.equalsIgnoreCase(anRepository.owner)) { currentOwner = user; break; } } owner.setSelectedItem(currentOwner); } useTickets = new JCheckBox("distributed Ticgit issues", anRepository.useTickets); useDocs = new JCheckBox("enumerates Markdown documentation in repository", anRepository.useDocs); showRemoteBranches = new JCheckBox("show remote branches", anRepository.showRemoteBranches); showReadme = new JCheckBox("show a \"readme\" Markdown file on the summary page", anRepository.showReadme); isFrozen = new JCheckBox("deny push operations", anRepository.isFrozen); accessRestriction = new JComboBox(AccessRestrictionType.values()); accessRestriction.setSelectedItem(anRepository.accessRestriction); federationStrategy = new JComboBox(FederationStrategy.values()); federationStrategy.setSelectedItem(anRepository.federationStrategy); JPanel fieldsPanel = new JPanel(new GridLayout(0, 1)); fieldsPanel.add(newFieldPanel("name", nameField)); fieldsPanel.add(newFieldPanel("description", descriptionField)); fieldsPanel.add(newFieldPanel("owner", owner)); fieldsPanel.add(newFieldPanel("enable tickets", useTickets)); fieldsPanel.add(newFieldPanel("enable docs", useDocs)); fieldsPanel.add(newFieldPanel("show remote branches", showRemoteBranches)); fieldsPanel.add(newFieldPanel("show readme", showReadme)); fieldsPanel.add(newFieldPanel("is frozen", isFrozen)); usersPalette = new JPalette<String>(); JPanel accessPanel = new JPanel(new BorderLayout(5, 5)); accessPanel.add(newFieldPanel("access restriction", accessRestriction), BorderLayout.NORTH); accessPanel.add(newFieldPanel("permitted users", usersPalette), BorderLayout.CENTER); setsPalette = new JPalette<String>(); JPanel federationPanel = new JPanel(new BorderLayout(5, 5)); federationPanel.add(newFieldPanel("federation strategy", federationStrategy), BorderLayout.NORTH); federationPanel.add(newFieldPanel("federation sets", setsPalette), BorderLayout.CENTER); JPanel panel = new JPanel(new BorderLayout(5, 5)); panel.add(fieldsPanel, BorderLayout.NORTH); panel.add(accessPanel, BorderLayout.CENTER); panel.add(federationPanel, BorderLayout.SOUTH); JButton createButton = new JButton("Save"); createButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (validateFields()) { canceled = false; setVisible(false); } } }); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { canceled = true; setVisible(false); } }); JPanel controls = new JPanel(); controls.add(cancelButton); controls.add(createButton); final Insets _insets = new Insets(5, 5, 5, 5); JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) { private static final long serialVersionUID = 1L; @Override public Insets getInsets() { return _insets; } }; centerPanel.add(panel, BorderLayout.CENTER); centerPanel.add(controls, BorderLayout.SOUTH); getContentPane().setLayout(new BorderLayout(5, 5)); getContentPane().add(centerPanel, BorderLayout.CENTER); pack(); setLocationRelativeTo(null); } private JPanel newFieldPanel(String label, JComponent comp) { JLabel fieldLabel = new JLabel(label); fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD)); fieldLabel.setPreferredSize(new Dimension(150, 20)); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); panel.add(fieldLabel); panel.add(comp); return panel; } private boolean validateFields() { // TODO validate input and populate model return true; } private void showValidationError(String message) { JOptionPane.showMessageDialog(EditRepositoryDialog.this, message, "Validation Error", JOptionPane.ERROR_MESSAGE); } public void setUsers(List<String> all, List<String> selected) { usersPalette.setObjects(all, selected); } public void setFederationSets(List<String> all, List<String> selected) { setsPalette.setObjects(all, selected); } public RepositoryModel getRepository() { if (canceled) { return null; } return repository; } } src/com/gitblit/client/EditUserDialog.java
New file @@ -0,0 +1,237 @@ /* * 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.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JTextField; import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; import com.gitblit.utils.StringUtils; public class EditUserDialog extends JDialog { private static final long serialVersionUID = 1L; private final UserModel user; private final IStoredSettings settings; private boolean canceled = true; private JTextField usernameField; private JPasswordField passwordField; private JPasswordField confirmPasswordField; private JCheckBox canAdminCheckbox; private JCheckBox notFederatedCheckbox; private JPalette<String> repositoryPalette; public EditUserDialog(IStoredSettings settings) { this(new UserModel(""), settings); setTitle("Create User"); } public EditUserDialog(UserModel anUser, IStoredSettings settings) { super(); this.user = new UserModel(""); this.settings = settings; initialize(anUser); setModal(true); setTitle("Edit User: " + anUser.username); setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage()); } private void initialize(UserModel anUser) { usernameField = new JTextField(anUser.username == null ? "" : anUser.username, 25); passwordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25); confirmPasswordField = new JPasswordField(anUser.password == null ? "" : anUser.password, 25); canAdminCheckbox = new JCheckBox("can administer Gitblit server", anUser.canAdmin); notFederatedCheckbox = new JCheckBox( "block federated Gitblit instances from pulling this account", anUser.excludeFromFederation); JPanel fieldsPanel = new JPanel(new GridLayout(0, 1)); fieldsPanel.add(newFieldPanel("username", usernameField)); fieldsPanel.add(newFieldPanel("password", passwordField)); fieldsPanel.add(newFieldPanel("confirm password", confirmPasswordField)); fieldsPanel.add(newFieldPanel("can admin", canAdminCheckbox)); fieldsPanel.add(newFieldPanel("exclude from federation", notFederatedCheckbox)); repositoryPalette = new JPalette<String>(); JPanel panel = new JPanel(new BorderLayout()); panel.add(fieldsPanel, BorderLayout.NORTH); panel.add(newFieldPanel("restricted repositories", repositoryPalette), BorderLayout.CENTER); JButton createButton = new JButton("Save"); createButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (validateFields()) { canceled = false; setVisible(false); } } }); JButton cancelButton = new JButton("Cancel"); cancelButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { canceled = true; setVisible(false); } }); JPanel controls = new JPanel(); controls.add(cancelButton); controls.add(createButton); final Insets _insets = new Insets(5, 5, 5, 5); JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) { private static final long serialVersionUID = 1L; @Override public Insets getInsets() { return _insets; } }; centerPanel.add(panel, BorderLayout.CENTER); centerPanel.add(controls, BorderLayout.SOUTH); getContentPane().setLayout(new BorderLayout(5, 5)); getContentPane().add(centerPanel, BorderLayout.CENTER); pack(); setLocationRelativeTo(null); } private JPanel newFieldPanel(String label, JComponent comp) { JLabel fieldLabel = new JLabel(label); fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD)); fieldLabel.setPreferredSize(new Dimension(150, 20)); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0)); panel.add(fieldLabel); panel.add(comp); return panel; } private boolean validateFields() { String uname = usernameField.getText(); if (StringUtils.isEmpty(uname)) { showValidationError("Please enter a username!"); return false; } // TODO verify username uniqueness on create // if (isCreate) { // UserModel model = GitBlit.self().getUserModel(username); // if (model != null) { // error(MessageFormat.format("Username ''{0}'' is unavailable.", // username)); // return; // } // } int minLength = settings.getInteger(Keys.realm.minPasswordLength, 5); if (minLength < 4) { minLength = 4; } char[] pw = passwordField.getPassword(); if (pw == null || pw.length < minLength) { showValidationError(MessageFormat.format( "Password is too short. Minimum length is {0} characters.", minLength)); return false; } char[] cpw = confirmPasswordField.getPassword(); if (cpw == null || cpw.length != pw.length) { showValidationError("Please confirm the password!"); return false; } if (!Arrays.equals(pw, cpw)) { showValidationError("Passwords do not match!"); return false; } user.username = uname; String type = settings.getString(Keys.realm.passwordStorage, "md5"); if (type.equalsIgnoreCase("md5")) { // store MD5 digest of password user.password = StringUtils.MD5_TYPE + StringUtils.getMD5(new String(pw)); } else { user.password = new String(pw); } user.canAdmin = canAdminCheckbox.isSelected(); user.excludeFromFederation = notFederatedCheckbox.isSelected(); user.repositories.clear(); user.repositories.addAll(repositoryPalette.getSelections()); return true; } private void showValidationError(String message) { JOptionPane.showMessageDialog(EditUserDialog.this, message, "Validation Error", JOptionPane.ERROR_MESSAGE); } public void setRepositories(List<RepositoryModel> repositories, List<String> selected) { List<String> restricted = new ArrayList<String>(); for (RepositoryModel repo : repositories) { if (repo.accessRestriction.exceeds(AccessRestrictionType.NONE)) { restricted.add(repo.name); } } StringUtils.sortRepositorynames(restricted); if (selected != null) { StringUtils.sortRepositorynames(selected); } repositoryPalette.setObjects(restricted, selected); } public UserModel getUser() { if (canceled) { return null; } return user; } } src/com/gitblit/client/GitblitClient.java
@@ -148,13 +148,13 @@ } reg = new GitblitRegistration(nameField.getText(), url, accountField.getText(), passwordField.getPassword()); login(reg); boolean success = login(reg); registrations.add(0, reg); rebuildRecentMenu(); return true; return success; } private void login(GitblitRegistration reg) { private boolean login(GitblitRegistration reg) { try { GitblitPanel panel = new GitblitPanel(reg); panel.login(); @@ -163,10 +163,12 @@ serverTabs.setSelectedIndex(idx); serverTabs.setTabComponentAt(idx, new ClosableTabComponent(reg.name, null, serverTabs, panel)); return true; } catch (IOException e) { JOptionPane.showMessageDialog(GitblitClient.this, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } return false; } private void rebuildRecentMenu() { src/com/gitblit/client/GitblitClientLauncher.java
@@ -25,6 +25,7 @@ import java.util.Collections; import java.util.List; import com.gitblit.Constants; import com.gitblit.Launcher; import com.gitblit.build.Build; import com.gitblit.build.Build.DownloadListener; @@ -82,6 +83,8 @@ if (g != null) { // Splash is 320x120 FontMetrics fm = g.getFontMetrics(); // paint startup status g.setColor(Color.darkGray); int h = fm.getHeight() + fm.getMaxDescent(); int x = 5; @@ -93,6 +96,11 @@ g.setColor(Color.WHITE); int xw = fm.stringWidth(string); g.drawString(string, x + ((w - xw) / 2), y - 5); // paint version String ver = "v" + Constants.VERSION; int vw = g.getFontMetrics().stringWidth(ver); g.drawString(ver, 320 - vw - 5, 34); g.dispose(); splash.update(); } src/com/gitblit/client/GitblitPanel.java
@@ -20,6 +20,7 @@ import java.awt.Component; import java.awt.Desktop; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -27,6 +28,7 @@ import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -34,6 +36,7 @@ import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; @@ -41,6 +44,7 @@ import javax.swing.JTextField; import javax.swing.RowFilter; import javax.swing.SwingConstants; import javax.swing.SwingWorker; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; @@ -49,7 +53,11 @@ import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import com.gitblit.Constants.RpcRequest; import com.gitblit.GitBlitException.ForbiddenException; import com.gitblit.GitBlitException.UnauthorizedException; import com.gitblit.IStoredSettings; import com.gitblit.Keys; import com.gitblit.client.ClosableTabComponent.CloseTabListener; import com.gitblit.models.FederationModel; import com.gitblit.models.RepositoryModel; @@ -72,13 +80,17 @@ private final Insets insets = new Insets(margin, margin, margin, margin); private String url; private final String url; private String account; private final String account; private char[] password; private final char[] password; private boolean isAdmin; private volatile boolean isAdmin; private volatile List<UserModel> allUsers; private volatile IStoredSettings settings; private JTabbedPane tabs; @@ -104,6 +116,8 @@ private TableRowSorter<RepositoriesModel> defaultSorter; private List<RepositoryModel> allRepositories; public GitblitPanel(GitblitRegistration reg) { this(reg.url, reg.account, reg.password); } @@ -128,10 +142,25 @@ } }); JButton refreshRepositories = new JButton("Refresh"); refreshRepositories.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { refreshRepositoriesTable(); } catch (ForbiddenException x) { explainForbidden(RpcRequest.LIST_REPOSITORIES); } catch (UnauthorizedException x) { explainUnauthorized(RpcRequest.LIST_REPOSITORIES); } catch (Throwable t) { showException(t); } } }); createRepository = new JButton("Create"); createRepository.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("TODO Create Repository"); createRepository(); } }); @@ -139,9 +168,7 @@ editRepository.setEnabled(false); editRepository.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for (RepositoryModel model : getSelectedRepositories()) { System.out.println("TODO Edit " + model); } editRepository(getSelectedRepositories().get(0)); } }); @@ -149,9 +176,7 @@ delRepository.setEnabled(false); delRepository.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for (RepositoryModel model : getSelectedRepositories()) { System.out.println("TODO Delete " + model); } deleteRepositories(getSelectedRepositories()); } }); @@ -165,7 +190,7 @@ } }); nameRenderer = new NameRenderer(Color.gray, new Color(0x00, 0x69, 0xD6)); nameRenderer = new NameRenderer(); typeRenderer = new TypeRenderer(); sizeRenderer = new DefaultTableCellRenderer(); @@ -246,10 +271,25 @@ repositoriesPanel.add(tablePanel, BorderLayout.CENTER); repositoriesPanel.add(repositoryControls, BorderLayout.SOUTH); JButton refreshUsers = new JButton("Refresh"); refreshUsers.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { refreshUsersTable(); } catch (ForbiddenException x) { explainForbidden(RpcRequest.LIST_USERS); } catch (UnauthorizedException x) { explainUnauthorized(RpcRequest.LIST_USERS); } catch (Throwable t) { showException(t); } } }); JButton createUser = new JButton("Create"); createUser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.out.println("TODO Create User"); createUser(); } }); @@ -257,9 +297,7 @@ editUser.setEnabled(false); editUser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for (UserModel user : getSelectedUsers()) { System.out.println("TODO Edit " + user); } editUser(getSelectedUsers().get(0)); } }); @@ -267,9 +305,7 @@ delUser.setEnabled(false); delUser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { for (UserModel user : getSelectedUsers()) { System.out.println("TODO Delete " + user); } deleteUsers(getSelectedUsers()); } }); @@ -288,7 +324,8 @@ } }); JPanel userControls = new JPanel(); JPanel userControls = new JPanel(new GridLayout(0, 2)); userControls.add(refreshUsers); userControls.add(createUser); userControls.add(editUser); userControls.add(delUser); @@ -327,6 +364,7 @@ try { refreshUsersTable(); refreshSettings(); isAdmin = true; refreshFederationPanel(); } catch (ForbiddenException e) { @@ -348,8 +386,10 @@ private void refreshRepositoriesTable() throws IOException { Map<String, RepositoryModel> repositories = RpcUtils .getRepositories(url, account, password); allRepositories = new ArrayList<RepositoryModel>(repositories.values()); Collections.sort(allRepositories); repositoriesModel.list.clear(); repositoriesModel.list.addAll(repositories.values()); repositoriesModel.list.addAll(allRepositories); repositoriesModel.fireTableDataChanged(); packColumns(repositoriesTable, 2); } @@ -360,8 +400,12 @@ } private void refreshUsersTable() throws IOException { List<UserModel> users = RpcUtils.getUsers(url, account, password); usersList.setListData(users.toArray()); allUsers = RpcUtils.getUsers(url, account, password); usersList.setListData(allUsers.toArray()); } private void refreshSettings() throws IOException { settings = RpcUtils.getSettings(url, account, password); } private void refreshFederationPanel() throws IOException { @@ -465,4 +509,313 @@ @Override public void closeTab(Component c) { } /** * Displays the create repository dialog and fires a SwingWorker to update * the server, if appropriate. * */ protected void createRepository() { EditRepositoryDialog dialog = new EditRepositoryDialog(allUsers); dialog.setVisible(true); final RepositoryModel newRepository = dialog.getRepository(); if (newRepository == null) { return; } final RpcRequest request = RpcRequest.CREATE_REPOSITORY; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws IOException { return RpcUtils.createRepository(newRepository, url, account, password); } @Override protected void done() { try { boolean success = get(); if (success) { refreshRepositoriesTable(); } else { String msg = MessageFormat.format( "Failed to execute request \"{0}\" for repository \"{1}\".", request.name(), newRepository.name); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } /** * Displays the edit repository dialog and fires a SwingWorker to update the * server, if appropriate. * * @param repository */ protected void editRepository(final RepositoryModel repository) { EditRepositoryDialog dialog = new EditRepositoryDialog(repository, allUsers); List<String> usernames = new ArrayList<String>(); for (UserModel user : this.allUsers) { usernames.add(user.username); } Collections.sort(usernames); dialog.setUsers(usernames, null); dialog.setFederationSets(settings.getStrings(Keys.federation.sets), repository.federationSets); dialog.setVisible(true); final RepositoryModel revisedRepository = dialog.getRepository(); if (revisedRepository == null) { return; } final RpcRequest request = RpcRequest.EDIT_REPOSITORY; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws IOException { return RpcUtils.updateRepository(repository.name, revisedRepository, url, account, password); } @Override protected void done() { try { boolean success = get(); if (success) { refreshRepositoriesTable(); } else { String msg = MessageFormat.format( "Failed to execute request \"{0}\" for repository \"{1}\".", request.name(), repository.name); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } protected void deleteRepositories(final List<RepositoryModel> repositories) { if (repositories == null || repositories.size() == 0) { return; } StringBuilder message = new StringBuilder("Delete the following repositories?\n\n"); for (RepositoryModel repository : repositories) { message.append(repository.name).append("\n"); } int result = JOptionPane.showConfirmDialog(GitblitPanel.this, message.toString(), "Delete Repositories?", JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { final RpcRequest request = RpcRequest.DELETE_REPOSITORY; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws Exception { boolean success = true; for (RepositoryModel repository : repositories) { success &= RpcUtils.deleteRepository(repository, url, account, password); } return success; } @Override protected void done() { try { boolean success = get(); if (success) { refreshRepositoriesTable(); } else { String msg = "Failed to delete specified repositories!"; JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } } /** * Displays the create user dialog and fires a SwingWorker to update the * server, if appropriate. * */ protected void createUser() { EditUserDialog dialog = new EditUserDialog(settings); dialog.setRepositories(allRepositories, null); dialog.setVisible(true); final UserModel newUser = dialog.getUser(); if (newUser == null) { return; } final RpcRequest request = RpcRequest.CREATE_USER; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws IOException { return RpcUtils.createUser(newUser, url, account, password); } @Override protected void done() { try { boolean success = get(); if (success) { refreshUsersTable(); } else { String msg = MessageFormat.format( "Failed to execute request \"{0}\" for user \"{1}\".", request.name(), newUser.username); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } /** * Displays the edit user dialog and fires a SwingWorker to update the * server, if appropriate. * * @param user */ protected void editUser(final UserModel user) { EditUserDialog dialog = new EditUserDialog(user, settings); dialog.setRepositories(allRepositories, new ArrayList<String>(user.repositories)); dialog.setVisible(true); final UserModel revisedUser = dialog.getUser(); if (revisedUser == null) { return; } final RpcRequest request = RpcRequest.EDIT_USER; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws IOException { return RpcUtils.updateUser(user.username, revisedUser, url, account, password); } @Override protected void done() { try { boolean success = get(); if (success) { refreshUsersTable(); } else { String msg = MessageFormat.format( "Failed to execute request \"{0}\" for user \"{1}\".", request.name(), user.username); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } protected void deleteUsers(final List<UserModel> users) { if (users == null || users.size() == 0) { return; } StringBuilder message = new StringBuilder("Delete the following users?\n\n"); for (UserModel user : users) { message.append(user.username).append("\n"); } int result = JOptionPane.showConfirmDialog(GitblitPanel.this, message.toString(), "Delete Users?", JOptionPane.YES_NO_OPTION); if (result == JOptionPane.YES_OPTION) { final RpcRequest request = RpcRequest.DELETE_USER; SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws Exception { boolean success = true; for (UserModel user : users) { success &= RpcUtils.deleteUser(user, url, account, password); } return success; } @Override protected void done() { try { boolean success = get(); if (success) { refreshUsersTable(); } else { String msg = "Failed to delete specified users!"; JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Error!", JOptionPane.ERROR_MESSAGE); } } catch (ForbiddenException e) { explainForbidden(request); } catch (UnauthorizedException e) { explainUnauthorized(request); } catch (Throwable t) { showException(t); } } }; worker.execute(); } } private void explainForbidden(RpcRequest request) { String msg = MessageFormat.format( "The request \"{0}\" has been forbidden by the Gitblit server @ {1}.", request.name(), url); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Forbidden", JOptionPane.ERROR_MESSAGE); } private void explainUnauthorized(RpcRequest request) { String msg = MessageFormat.format( "The account \"{0}\" is not authorized to execute the request \"{1}\".", account, request.name()); JOptionPane.showMessageDialog(GitblitPanel.this, msg, "Unauthorized", JOptionPane.ERROR_MESSAGE); } private void showException(Throwable t) { // TODO show the unexpected exception } } src/com/gitblit/client/JPalette.java
New file @@ -0,0 +1,177 @@ /* * 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.Color; import java.awt.Dimension; import java.awt.GridBagLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; public class JPalette<T> extends JPanel { private static final long serialVersionUID = 1L; private PaletteModel<T> availableModel; private PaletteModel<T> selectedModel; public JPalette() { super(new BorderLayout(5, 5)); availableModel = new PaletteModel<T>(); selectedModel = new PaletteModel<T>(); final JTable available = new JTable(availableModel); final JTable selected = new JTable(selectedModel); JButton add = new JButton("->"); add.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { List<T> move = new ArrayList<T>(); if (available.getSelectedRowCount() <= 0) { return; } for (int row : available.getSelectedRows()) { int modelIndex = available.convertRowIndexToModel(row); T item = (T) availableModel.list.get(modelIndex); move.add(item); } availableModel.list.removeAll(move); selectedModel.list.addAll(move); availableModel.fireTableDataChanged(); selectedModel.fireTableDataChanged(); } }); JButton subtract = new JButton("<-"); subtract.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { List<T> move = new ArrayList<T>(); if (selected.getSelectedRowCount() <= 0) { return; } for (int row : selected.getSelectedRows()) { int modelIndex = selected.convertRowIndexToModel(row); T item = (T) selectedModel.list.get(modelIndex); move.add(item); } selectedModel.list.removeAll(move); availableModel.list.addAll(move); selectedModel.fireTableDataChanged(); availableModel.fireTableDataChanged(); } }); JPanel controls = new JPanel(new GridLayout(0, 1, 0, 5)); controls.add(add); controls.add(subtract); JPanel center = new JPanel(new GridBagLayout()); center.add(controls); add(newListPanel("Available", available), BorderLayout.WEST); add(center, BorderLayout.CENTER); add(newListPanel("Selected", selected), BorderLayout.EAST); } private JPanel newListPanel(String label, JTable table) { NameRenderer nameRenderer = new NameRenderer(); table.setCellSelectionEnabled(false); table.setRowSelectionAllowed(true); table.setRowHeight(nameRenderer.getFont().getSize() + 8); table.getTableHeader().setReorderingAllowed(false); table.setGridColor(new Color(0xd9d9d9)); table.setBackground(Color.white); table.getColumn(table.getColumnName(0)).setCellRenderer(nameRenderer); JScrollPane jsp = new JScrollPane(table); jsp.setPreferredSize(new Dimension(225, 175)); JPanel panel = new JPanel(new BorderLayout()); panel.add(new JLabel(label), BorderLayout.NORTH); panel.add(jsp, BorderLayout.CENTER); return panel; } public void setObjects(List<T> all, List<T> selected) { List<T> available = new ArrayList<T>(all); if (selected != null) { available.removeAll(selected); } availableModel.list.clear(); availableModel.list.addAll(available); availableModel.fireTableDataChanged(); if (selected != null) { selectedModel.list.clear(); selectedModel.list.addAll(selected); selectedModel.fireTableDataChanged(); } } public List<T> getSelections() { return new ArrayList<T>(selectedModel.list); } public class PaletteModel<K> extends AbstractTableModel { private static final long serialVersionUID = 1L; List<K> list; public PaletteModel() { this(new ArrayList<K>()); } public PaletteModel(List<K> list) { this.list = new ArrayList<K>(list); } @Override public int getRowCount() { return list.size(); } @Override public int getColumnCount() { return 1; } @Override public String getColumnName(int column) { return "Name"; } public Class<?> getColumnClass(int columnIndex) { return String.class; } @Override public Object getValueAt(int rowIndex, int columnIndex) { K o = list.get(rowIndex); return o.toString(); } } } src/com/gitblit/client/NameRenderer.java
@@ -34,6 +34,10 @@ final String groupSpan; public NameRenderer() { this(Color.gray, new Color(0x00, 0x69, 0xD6)); } public NameRenderer(Color group, Color repo) { groupSpan = "<span style='color:" + getHexColor(group) + "'>"; setForeground(repo); src/com/gitblit/utils/JsonUtils.java
@@ -157,6 +157,25 @@ } return gson().fromJson(json, type); } /** * Reads a gson object from the specified url. * * @param url * @param clazz * @param username * @param password * @return the deserialized object * @throws {@link IOException} */ public static <X> X retrieveJson(String url, Class<X> clazz, String username, char[] password) throws IOException { String json = retrieveJsonString(url, username, password); if (StringUtils.isEmpty(json)) { return null; } return gson().fromJson(json, clazz); } /** * Retrieves a JSON message. src/com/gitblit/utils/RpcUtils.java
@@ -21,9 +21,11 @@ import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Properties; import com.gitblit.Constants; import com.gitblit.Constants.RpcRequest; import com.gitblit.IStoredSettings; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -294,7 +296,7 @@ List<FederationModel> list = new ArrayList<FederationModel>(registrations); return list; } /** * Retrieves the list of federation proposals. * @@ -304,15 +306,15 @@ * @return a collection of FederationProposal objects * @throws IOException */ public static List<FederationProposal> getFederationProposals(String serverUrl, String account, char[] password) throws IOException { public static List<FederationProposal> getFederationProposals(String serverUrl, String account, char[] password) throws IOException { String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_PROPOSALS); Collection<FederationProposal> proposals = JsonUtils.retrieveJson(url, PROPOSALS_TYPE, account, password); List<FederationProposal> list = new ArrayList<FederationProposal>(proposals); return list; } /** * Retrieves the list of federation repository sets. * @@ -322,13 +324,29 @@ * @return a collection of FederationSet objects * @throws IOException */ public static List<FederationSet> getFederationSets(String serverUrl, String account, char[] password) throws IOException { public static List<FederationSet> getFederationSets(String serverUrl, String account, char[] password) throws IOException { String url = asLink(serverUrl, RpcRequest.LIST_FEDERATION_SETS); Collection<FederationSet> sets = JsonUtils.retrieveJson(url, SETS_TYPE, account, password); Collection<FederationSet> sets = JsonUtils.retrieveJson(url, SETS_TYPE, account, password); List<FederationSet> list = new ArrayList<FederationSet>(sets); return list; } /** * Retrieves the settings of the Gitblit server. * * @param serverUrl * @param account * @param password * @return an IStoredSettings object * @throws IOException */ public static IStoredSettings getSettings(String serverUrl, String account, char[] password) throws IOException { String url = asLink(serverUrl, RpcRequest.LIST_SETTINGS); Properties props = JsonUtils.retrieveJson(url, Properties.class, account, password); RpcSettings settings = new RpcSettings(props); return settings; } /** @@ -351,4 +369,31 @@ int resultCode = JsonUtils.sendJsonString(url, json, account, password); return resultCode == 200; } /** * Settings implementation that wraps a retrieved properties instance. This * class is used for RPC communication. * * @author James Moger * */ private static class RpcSettings extends IStoredSettings { private final Properties properties = new Properties(); public RpcSettings(Properties props) { super(RpcSettings.class); properties.putAll(props); } @Override protected Properties read() { return properties; } @Override public String toString() { return "RpcSettings"; } } } src/com/gitblit/wicket/GitBlitWebApp.properties
@@ -92,7 +92,7 @@ gb.isFrozenDescription = deny push operations gb.zip = zip gb.showReadme = show readme gb.showReadmeDescription = show a \"readme\" markdown file on the summary page gb.showReadmeDescription = show a \"readme\" Markdown file on the summary page gb.nameDescription = use '/' to group repositories. e.g. libraries/mycoollib.git gb.ownerDescription = the owner may edit repository settings gb.blob = blob @@ -106,7 +106,7 @@ gb.federateThis = federate this repository gb.federateOrigin = federate the origin gb.excludeFromFederation = exclude from federation gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this object gb.excludeFromFederationDescription = block federated Gitblit instances from pulling this account gb.tokens = federation tokens gb.tokenAllDescription = all repositories, users, & settings gb.tokenUnrDescription = all repositories & users tests/com/gitblit/tests/RpcTests.java
@@ -23,6 +23,7 @@ import com.gitblit.Constants.AccessRestrictionType; import com.gitblit.GitBlitException.UnauthorizedException; import com.gitblit.IStoredSettings; import com.gitblit.models.FederationModel; import com.gitblit.models.FederationProposal; import com.gitblit.models.FederationSet; @@ -203,4 +204,9 @@ List<FederationSet> sets = RpcUtils.getFederationSets(url, account, password.toCharArray()); assertTrue("No federation sets were retrieved!", sets.size() > 0); } public void testSettings() throws Exception { IStoredSettings settings = RpcUtils.getSettings(url, account, password.toCharArray()); assertTrue("No settings were retrieved!", settings.getAllKeys(null).size() > 0); } }