<genjar jarfile="manager.jar">\r
<resource file="${basedir}/src/com/gitblit/client/splash.png" />\r
<resource file="${basedir}/resources/gitblt-favicon.png" />\r
+ <resource file="${basedir}/resources/gitweb-favicon.png" />\r
+ <resource file="${basedir}/resources/user_16x16.png" />\r
+ <resource file="${basedir}/resources/settings_16x16.png" />\r
<resource file="${basedir}/resources/lock_go_16x16.png" />\r
<resource file="${basedir}/resources/lock_pull_16x16.png" />\r
<resource file="${basedir}/resources/shield_16x16.png" />\r
import java.awt.EventQueue;\r
import java.awt.Font;\r
import java.awt.GridLayout;\r
+import java.awt.Point;\r
import java.awt.event.ActionEvent;\r
import java.awt.event.ActionListener;\r
import java.awt.event.KeyEvent;\r
+import java.awt.event.WindowAdapter;\r
+import java.awt.event.WindowEvent;\r
+import java.io.File;\r
import java.io.IOException;\r
-import java.util.ArrayList;\r
-import java.util.List;\r
+import java.text.MessageFormat;\r
+import java.util.LinkedHashMap;\r
+import java.util.Map;\r
+import java.util.Set;\r
\r
import javax.swing.ImageIcon;\r
import javax.swing.JFrame;\r
import javax.swing.KeyStroke;\r
import javax.swing.UIManager;\r
\r
+import org.eclipse.jgit.errors.ConfigInvalidException;\r
+import org.eclipse.jgit.lib.StoredConfig;\r
+import org.eclipse.jgit.storage.file.FileBasedConfig;\r
+import org.eclipse.jgit.util.Base64;\r
+import org.eclipse.jgit.util.FS;\r
+\r
import com.gitblit.Constants;\r
import com.gitblit.utils.StringUtils;\r
\r
/**\r
- * Sample RPC application.\r
+ * Gitblit Manager issues JSON RPC requests to a Gitblit server.\r
* \r
* @author James Moger\r
* \r
\r
private static final long serialVersionUID = 1L;\r
private JTabbedPane serverTabs;\r
+ private File configFile = new File(System.getProperty("user.home"), ".gitblit/config");\r
private GitblitRegistration localhost = new GitblitRegistration("default",\r
"https://localhost:8443", "admin", "admin".toCharArray());\r
\r
- private List<GitblitRegistration> registrations = new ArrayList<GitblitRegistration>();\r
+ private Map<String, GitblitRegistration> registrations = new LinkedHashMap<String, GitblitRegistration>();\r
private JMenu recentMenu;\r
\r
private GitblitManager() {\r
private void initialize() {\r
setContentPane(getCenterPanel());\r
setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());\r
-\r
setTitle("Gitblit Manager v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");\r
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
- setSize(800, 500);\r
+ addWindowListener(new WindowAdapter() {\r
+ @Override\r
+ public void windowClosing(WindowEvent event) {\r
+ saveSizeAndPosition();\r
+ }\r
+ });\r
+\r
+ setSizeAndPosition();\r
+ loadRegistrations();\r
+ rebuildRecentMenu();\r
+ }\r
+\r
+ private void setSizeAndPosition() {\r
+ String sz = null;\r
+ String pos = null;\r
+ try {\r
+ StoredConfig config = getConfig();\r
+ sz = config.getString("ui", null, "size");\r
+ pos = config.getString("ui", null, "position");\r
+ } catch (Throwable t) {\r
+ t.printStackTrace();\r
+ }\r
+\r
+ // try to restore saved window size\r
+ if (StringUtils.isEmpty(sz)) {\r
+ setSize(850, 500);\r
+ } else {\r
+ String[] chunks = sz.split("x");\r
+ int width = Integer.parseInt(chunks[0]);\r
+ int height = Integer.parseInt(chunks[1]);\r
+ setSize(width, height);\r
+ }\r
+\r
+ // try to restore saved window position\r
+ if (StringUtils.isEmpty(pos)) {\r
+ setLocationRelativeTo(null);\r
+ } else {\r
+ String[] chunks = pos.split(",");\r
+ int x = Integer.parseInt(chunks[0]);\r
+ int y = Integer.parseInt(chunks[1]);\r
+ setLocation(x, y);\r
+ }\r
+ }\r
+\r
+ private void saveSizeAndPosition() {\r
+ try {\r
+ // save window size and position\r
+ StoredConfig config = getConfig();\r
+ Dimension sz = GitblitManager.this.getSize();\r
+ config.setString("ui", null, "size",\r
+ MessageFormat.format("{0,number,0}x{1,number,0}", sz.width, sz.height));\r
+ Point pos = GitblitManager.this.getLocationOnScreen();\r
+ config.setString("ui", null, "position",\r
+ MessageFormat.format("{0,number,0},{1,number,0}", pos.x, pos.y));\r
+ config.save();\r
+ } catch (Throwable t) {\r
+ Utils.showException(GitblitManager.this, t);\r
+ }\r
}\r
\r
public void setVisible(boolean value) {\r
loginPrompt(localhost);\r
} else if (registrations.size() == 1) {\r
// single registration prompt\r
- loginPrompt(registrations.get(0));\r
+ GitblitRegistration reg = registrations.values().iterator().next();\r
+ loginPrompt(reg);\r
}\r
super.setVisible(value);\r
- setLocationRelativeTo(null);\r
}\r
}\r
\r
reg = new GitblitRegistration(nameField.getText(), url, accountField.getText(),\r
passwordField.getPassword());\r
boolean success = login(reg);\r
- registrations.add(0, reg);\r
+ registrations.put(reg.name, reg);\r
rebuildRecentMenu();\r
return success;\r
}\r
serverTabs.setSelectedIndex(idx);\r
serverTabs.setTabComponentAt(idx, new ClosableTabComponent(reg.name, null, serverTabs,\r
panel));\r
+ saveRegistration(reg);\r
return true;\r
} catch (IOException e) {\r
JOptionPane.showMessageDialog(GitblitManager.this, e.getMessage(),\r
\r
private void rebuildRecentMenu() {\r
recentMenu.removeAll();\r
- for (final GitblitRegistration reg : registrations) {\r
- JMenuItem item = new JMenuItem(reg.name);\r
+ ImageIcon icon = new ImageIcon(getClass().getResource("/gitblt-favicon.png"));\r
+ for (final GitblitRegistration reg : registrations.values()) {\r
+ JMenuItem item = new JMenuItem(reg.name, icon);\r
item.addActionListener(new ActionListener() {\r
public void actionPerformed(ActionEvent e) {\r
- login(reg);\r
+ loginPrompt(reg);\r
}\r
});\r
recentMenu.add(item);\r
}\r
}\r
\r
+ private void loadRegistrations() {\r
+ try {\r
+ StoredConfig config = getConfig();\r
+ Set<String> servers = config.getSubsections("servers");\r
+ for (String server : servers) {\r
+ String url = config.getString("servers", server, "url");\r
+ String account = config.getString("servers", server, "account");\r
+ char[] password = new String(Base64.decode(config.getString("servers", server,\r
+ "password"))).toCharArray();\r
+ GitblitRegistration reg = new GitblitRegistration(server, url, account, password);\r
+ registrations.put(reg.name, reg);\r
+ }\r
+ } catch (Throwable t) {\r
+ Utils.showException(GitblitManager.this, t);\r
+ }\r
+ }\r
+\r
+ private void saveRegistration(GitblitRegistration reg) {\r
+ try {\r
+ StoredConfig config = getConfig();\r
+ config.setString("servers", reg.name, "url", reg.url);\r
+ config.setString("servers", reg.name, "account", reg.account);\r
+ config.setString("servers", reg.name, "password",\r
+ Base64.encodeBytes(new String(reg.password).getBytes("UTF-8")));\r
+ config.save();\r
+ } catch (Throwable t) {\r
+ Utils.showException(GitblitManager.this, t);\r
+ }\r
+ }\r
+\r
+ private StoredConfig getConfig() throws IOException, ConfigInvalidException {\r
+ FileBasedConfig config = new FileBasedConfig(configFile, FS.detect());\r
+ config.load();\r
+ return config;\r
+ }\r
+\r
public static void main(String[] args) {\r
EventQueue.invokeLater(new Runnable() {\r
public void run() {\r
\r
private UsersModel usersModel;\r
\r
+ private JTable settingsTable;\r
+\r
+ private SettingsModel settingsModel;\r
+\r
private JButton createRepository;\r
\r
private JButton delRepository;\r
\r
private TableRowSorter<UsersModel> defaultUsersSorter;\r
\r
+ private TableRowSorter<SettingsModel> defaultSettingsSorter;\r
+\r
private JButton editRepository;\r
\r
public GitblitPanel(GitblitRegistration reg) {\r
public GitblitPanel(String url, String account, char[] password) {\r
this.gitblit = new GitblitModel(url, account, password);\r
\r
+ tabs = new JTabbedPane(JTabbedPane.BOTTOM);\r
+ tabs.addTab(Translation.get("gb.repositories"), createRepositoriesPanel());\r
+ tabs.addTab(Translation.get("gb.users"), createUsersPanel());\r
+ tabs.addTab(Translation.get("gb.federation"), new JPanel());\r
+ tabs.addTab(Translation.get("gb.settings"), createSettingsPanel());\r
+\r
+ setLayout(new BorderLayout());\r
+ add(tabs, BorderLayout.CENTER);\r
+ }\r
+\r
+ private JPanel createRepositoriesPanel() {\r
final JButton browseRepository = new JButton(Translation.get("gb.browse"));\r
browseRepository.setEnabled(false);\r
browseRepository.addActionListener(new ActionListener() {\r
repositoriesTable.setRowSorter(defaultRepositoriesSorter);\r
repositoriesTable.getRowSorter().toggleSortOrder(RepositoriesModel.Columns.Name.ordinal());\r
\r
- setRepositoryRenderer(RepositoriesModel.Columns.Name, nameRenderer);\r
- setRepositoryRenderer(RepositoriesModel.Columns.Indicators, typeRenderer);\r
- setRepositoryRenderer(RepositoriesModel.Columns.Owner, ownerRenderer);\r
- setRepositoryRenderer(RepositoriesModel.Columns.Size, sizeRenderer);\r
+ setRepositoryRenderer(RepositoriesModel.Columns.Name, nameRenderer, -1);\r
+ setRepositoryRenderer(RepositoriesModel.Columns.Indicators, typeRenderer, 100);\r
+ setRepositoryRenderer(RepositoriesModel.Columns.Owner, ownerRenderer, -1);\r
+ setRepositoryRenderer(RepositoriesModel.Columns.Size, sizeRenderer, 60);\r
\r
repositoriesTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {\r
@Override\r
return insets;\r
}\r
};\r
- repositoriesPanel.add(new HeaderPanel(Translation.get("gb.repositories")),\r
- BorderLayout.NORTH);\r
+ repositoriesPanel.add(new HeaderPanel(Translation.get("gb.repositories"),\r
+ "gitweb-favicon.png"), BorderLayout.NORTH);\r
repositoriesPanel.add(repositoryTablePanel, BorderLayout.CENTER);\r
repositoriesPanel.add(repositoryControls, BorderLayout.SOUTH);\r
\r
+ return repositoriesPanel;\r
+ }\r
+\r
+ private void setRepositoryRenderer(RepositoriesModel.Columns col, TableCellRenderer renderer,\r
+ int maxWidth) {\r
+ String name = repositoriesTable.getColumnName(col.ordinal());\r
+ repositoriesTable.getColumn(name).setCellRenderer(renderer);\r
+ if (maxWidth > 0) {\r
+ repositoriesTable.getColumn(name).setMinWidth(maxWidth);\r
+ repositoriesTable.getColumn(name).setMaxWidth(maxWidth);\r
+ }\r
+ }\r
+\r
+ private JPanel createUsersPanel() {\r
JButton refreshUsers = new JButton(Translation.get("gb.refresh"));\r
refreshUsers.addActionListener(new ActionListener() {\r
public void actionPerformed(ActionEvent e) {\r
return insets;\r
}\r
};\r
- usersPanel.add(new HeaderPanel(Translation.get("gb.users")), BorderLayout.NORTH);\r
+ usersPanel.add(new HeaderPanel(Translation.get("gb.users"), "user_16x16.png"),\r
+ BorderLayout.NORTH);\r
usersPanel.add(userTablePanel, BorderLayout.CENTER);\r
usersPanel.add(userControls, BorderLayout.SOUTH);\r
\r
- tabs = new JTabbedPane(JTabbedPane.BOTTOM);\r
- tabs.addTab(Translation.get("gb.repositories"), repositoriesPanel);\r
- tabs.addTab(Translation.get("gb.users"), usersPanel);\r
- tabs.addTab(Translation.get("gb.federation"), new JPanel());\r
-\r
- setLayout(new BorderLayout());\r
- add(tabs, BorderLayout.CENTER);\r
+ return usersPanel;\r
}\r
\r
- private void setRepositoryRenderer(RepositoriesModel.Columns col, TableCellRenderer renderer) {\r
- String name = repositoriesTable.getColumnName(col.ordinal());\r
- repositoriesTable.getColumn(name).setCellRenderer(renderer);\r
+ private JPanel createSettingsPanel() {\r
+ settingsModel = new SettingsModel();\r
+ defaultSettingsSorter = new TableRowSorter<SettingsModel>(settingsModel);\r
+ settingsTable = Utils.newTable(settingsModel);\r
+ String name = settingsTable.getColumnName(UsersModel.Columns.Name.ordinal());\r
+ settingsTable.setRowHeight(nameRenderer.getFont().getSize() + 8);\r
+ settingsTable.getColumn(name).setCellRenderer(nameRenderer);\r
+ settingsTable.setRowSorter(defaultSettingsSorter);\r
+ settingsTable.getRowSorter().toggleSortOrder(SettingsModel.Columns.Name.ordinal());\r
+ settingsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {\r
+\r
+ @Override\r
+ public void valueChanged(ListSelectionEvent e) {\r
+ if (e.getValueIsAdjusting()) {\r
+ return;\r
+ }\r
+ boolean selected = settingsTable.getSelectedRow() > -1;\r
+ boolean singleSelection = settingsTable.getSelectedRows().length == 1;\r
+ // TODO enable/disable setting buttons\r
+ }\r
+ });\r
+\r
+ final JTextField settingFilter = new JTextField();\r
+ settingFilter.addActionListener(new ActionListener() {\r
+ public void actionPerformed(ActionEvent e) {\r
+ filterSettings(settingFilter.getText());\r
+ }\r
+ });\r
+ settingFilter.addKeyListener(new KeyAdapter() {\r
+ public void keyReleased(KeyEvent e) {\r
+ filterSettings(settingFilter.getText());\r
+ }\r
+ });\r
+\r
+ JPanel settingFilterPanel = new JPanel(new BorderLayout(margin, margin));\r
+ settingFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);\r
+ settingFilterPanel.add(settingFilter, BorderLayout.CENTER);\r
+\r
+ JPanel settingsTablePanel = new JPanel(new BorderLayout(margin, margin));\r
+ settingsTablePanel.add(settingFilterPanel, BorderLayout.NORTH);\r
+ settingsTablePanel.add(new JScrollPane(settingsTable), BorderLayout.CENTER);\r
+\r
+ JPanel settingsControls = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));\r
+ // TODO update setting?\r
+\r
+ JPanel settingsPanel = new JPanel(new BorderLayout(margin, margin)) {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public Insets getInsets() {\r
+ return insets;\r
+ }\r
+ };\r
+ settingsPanel.add(new HeaderPanel(Translation.get("gb.settings"), "settings_16x16.png"),\r
+ BorderLayout.NORTH);\r
+ settingsPanel.add(settingsTablePanel, BorderLayout.CENTER);\r
+ settingsPanel.add(settingsControls, BorderLayout.SOUTH);\r
+\r
+ return settingsPanel;\r
}\r
\r
public void login() throws IOException {\r
\r
if (gitblit.allowAdmin()) {\r
updateUsersTable();\r
+ updateSettingsTable();\r
} else {\r
// user does not have administrator privileges\r
// hide admin repository buttons\r
usersModel.fireTableDataChanged();\r
}\r
\r
+ private void updateSettingsTable() {\r
+ settingsModel.setSettings(gitblit.getSettings());\r
+ settingsModel.fireTableDataChanged();\r
+ }\r
+\r
private void filterRepositories(final String fragment) {\r
if (StringUtils.isEmpty(fragment)) {\r
repositoriesTable.setRowSorter(defaultRepositoriesSorter);\r
usersTable.setRowSorter(sorter);\r
}\r
\r
+ private void filterSettings(final String fragment) {\r
+ if (StringUtils.isEmpty(fragment)) {\r
+ settingsTable.setRowSorter(defaultSettingsSorter);\r
+ return;\r
+ }\r
+ RowFilter<SettingsModel, Object> containsFilter = new RowFilter<SettingsModel, Object>() {\r
+ public boolean include(Entry<? extends SettingsModel, ? extends Object> entry) {\r
+ for (int i = entry.getValueCount() - 1; i >= 0; i--) {\r
+ if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ };\r
+ TableRowSorter<SettingsModel> sorter = new TableRowSorter<SettingsModel>(settingsModel);\r
+ sorter.setRowFilter(containsFilter);\r
+ settingsTable.setRowSorter(sorter);\r
+ }\r
+\r
private List<RepositoryModel> getSelectedRepositories() {\r
List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();\r
for (int viewRow : repositoriesTable.getSelectedRows()) {\r
success &= gitblit.deleteRepository(repository);\r
}\r
if (success) {\r
+ gitblit.refreshRepositories();\r
gitblit.refreshUsers();\r
}\r
return success;\r
*/\r
package com.gitblit.client;\r
\r
+import java.awt.BasicStroke;\r
import java.awt.Color;\r
import java.awt.FlowLayout;\r
import java.awt.GradientPaint;\r
import java.awt.geom.Point2D;\r
import java.awt.geom.Rectangle2D;\r
\r
+import javax.swing.ImageIcon;\r
import javax.swing.JLabel;\r
import javax.swing.JPanel;\r
\r
+import com.gitblit.utils.StringUtils;\r
+\r
public class HeaderPanel extends JPanel {\r
\r
private static final long serialVersionUID = 1L;\r
\r
private Color lightColor = new Color(0, 0, 0x60);\r
\r
- public HeaderPanel(String text) {\r
+ public HeaderPanel(String text, String icon) {\r
super(new FlowLayout(FlowLayout.LEFT), true);\r
setOpaque(true);\r
setBackground(new Color(0, 0, 0x20));\r
\r
JLabel label = new JLabel(text);\r
+ if (!StringUtils.isEmpty(icon)) {\r
+ label.setIcon(new ImageIcon(getClass().getResource("/" + icon)));\r
+ }\r
label.setForeground(Color.white);\r
label.setFont(label.getFont().deriveFont(14f));\r
add(label);\r
false);\r
g.setPaint(gradientPaint);\r
g.fill(new Rectangle2D.Double(0, 0, getWidth(), getHeight()));\r
+ g.setColor(new Color(0xff, 0x99, 0x00));\r
+ int stroke = 2;\r
+ g.setStroke(new BasicStroke(stroke));\r
+ g.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1);\r
}\r
}\r
--- /dev/null
+/*\r
+ * Copyright 2011 gitblit.com.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.client;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collections;\r
+import java.util.List;\r
+\r
+import javax.swing.table.AbstractTableModel;\r
+\r
+import com.gitblit.IStoredSettings;\r
+\r
+/**\r
+ * Table model of IStoredSettings.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+public class SettingsModel extends AbstractTableModel {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ IStoredSettings settings;\r
+\r
+ List<String> keys;\r
+\r
+ enum Columns {\r
+ Name, Value;\r
+\r
+ @Override\r
+ public String toString() {\r
+ return name().replace('_', ' ');\r
+ }\r
+ }\r
+\r
+ public SettingsModel() {\r
+ this(null);\r
+ }\r
+\r
+ public SettingsModel(IStoredSettings settings) {\r
+ setSettings(settings);\r
+ }\r
+\r
+ public void setSettings(IStoredSettings settings) {\r
+ this.settings = settings;\r
+ if (settings == null) {\r
+ keys = new ArrayList<String>();\r
+ } else {\r
+ keys = new ArrayList<String>(settings.getAllKeys(null));\r
+ Collections.sort(keys);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getRowCount() {\r
+ return keys.size();\r
+ }\r
+\r
+ @Override\r
+ public int getColumnCount() {\r
+ return Columns.values().length;\r
+ }\r
+\r
+ @Override\r
+ public String getColumnName(int column) {\r
+ Columns col = Columns.values()[column];\r
+ switch (col) {\r
+ case Name:\r
+ return Translation.get("gb.name");\r
+ }\r
+ return "";\r
+ }\r
+\r
+ /**\r
+ * Returns <code>Object.class</code> regardless of <code>columnIndex</code>.\r
+ * \r
+ * @param columnIndex\r
+ * the column being queried\r
+ * @return the Object.class\r
+ */\r
+ public Class<?> getColumnClass(int columnIndex) {\r
+ return String.class;\r
+ }\r
+\r
+ @Override\r
+ public Object getValueAt(int rowIndex, int columnIndex) {\r
+ String key = keys.get(rowIndex);\r
+ Columns col = Columns.values()[columnIndex];\r
+ switch (col) {\r
+ case Name:\r
+ return key;\r
+ case Value:\r
+ return settings.getString(key, "");\r
+ }\r
+ return null;\r
+ }\r
+}\r
\r
public static void showException(Component c, Throwable t) {\r
// TODO show the unexpected exception\r
+ t.printStackTrace();\r
}\r
\r
public static void packColumns(JTable table, int margin) {\r
gb.downloading = downloading\r
gb.loading = loading\r
gb.starting = starting\r
-gb.general = general
\ No newline at end of file
+gb.general = general\r
+gb.settings = settings
\ No newline at end of file