James Moger
2013-01-23 1613f4067028b73e05544b55d45b6e136ce0238d
commit | author | age
38688b 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.client;
17
18 import java.awt.BorderLayout;
19 import java.awt.Color;
20 import java.awt.FlowLayout;
21 import java.awt.Insets;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.KeyAdapter;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.MouseAdapter;
27 import java.awt.event.MouseEvent;
28 import java.io.IOException;
29 import java.util.ArrayList;
1aabf0 30 import java.util.Arrays;
38688b 31 import java.util.List;
a5b159 32 import java.util.Map;
38688b 33
JM 34 import javax.swing.JButton;
35 import javax.swing.JLabel;
36 import javax.swing.JOptionPane;
37 import javax.swing.JPanel;
38 import javax.swing.JScrollPane;
39 import javax.swing.JTable;
40 import javax.swing.JTextField;
41 import javax.swing.RowFilter;
42 import javax.swing.SwingConstants;
43 import javax.swing.event.ListSelectionEvent;
44 import javax.swing.event.ListSelectionListener;
45 import javax.swing.table.DefaultTableCellRenderer;
46 import javax.swing.table.TableCellRenderer;
47 import javax.swing.table.TableRowSorter;
48
1aabf0 49 import com.gitblit.Constants;
38688b 50 import com.gitblit.Constants.RpcRequest;
0e6eac 51 import com.gitblit.Keys;
38688b 52 import com.gitblit.models.FeedModel;
661db6 53 import com.gitblit.models.RegistrantAccessPermission;
38688b 54 import com.gitblit.models.RepositoryModel;
JM 55 import com.gitblit.utils.StringUtils;
56
57 /**
58  * RSS Feeds Panel displays recent entries and launches the browser to view the
59  * commit. commitdiff, or tree of a commit.
60  * 
61  * @author James Moger
62  * 
63  */
64 public abstract class RepositoriesPanel extends JPanel {
65
66     private static final long serialVersionUID = 1L;
67
68     private final GitblitClient gitblit;
69
70     private HeaderPanel header;
71
72     private JTable table;
73
74     private RepositoriesTableModel tableModel;
75
76     private TableRowSorter<RepositoriesTableModel> defaultSorter;
77
78     private JButton createRepository;
79
80     private JButton editRepository;
81
82     private JButton delRepository;
83
84     private JTextField filterTextfield;
85
fee060 86     private JButton clearCache;
JM 87
38688b 88     public RepositoriesPanel(GitblitClient gitblit) {
JM 89         super();
90         this.gitblit = gitblit;
91         initialize();
92     }
93
94     private void initialize() {
95         final JButton browseRepository = new JButton(Translation.get("gb.browse"));
96         browseRepository.setEnabled(false);
97         browseRepository.addActionListener(new ActionListener() {
98             public void actionPerformed(ActionEvent e) {
99                 RepositoryModel model = getSelectedRepositories().get(0);
ec5a88 100                 String url = gitblit.getURL("summary", model.name, null);
JM 101                 Utils.browse(url);
38688b 102             }
JM 103         });
104
105         JButton refreshRepositories = new JButton(Translation.get("gb.refresh"));
106         refreshRepositories.addActionListener(new ActionListener() {
107             public void actionPerformed(ActionEvent e) {
108                 refreshRepositories();
fee060 109             }
JM 110         });
111         
112         clearCache = new JButton(Translation.get("gb.clearCache"));
113         clearCache.addActionListener(new ActionListener() {
114             public void actionPerformed(ActionEvent e) {
115                 clearCache();
38688b 116             }
JM 117         });
118
119         createRepository = new JButton(Translation.get("gb.create"));
120         createRepository.addActionListener(new ActionListener() {
121             public void actionPerformed(ActionEvent e) {
122                 createRepository();
123             }
124         });
125
126         editRepository = new JButton(Translation.get("gb.edit"));
127         editRepository.setEnabled(false);
128         editRepository.addActionListener(new ActionListener() {
129             public void actionPerformed(ActionEvent e) {
130                 editRepository(getSelectedRepositories().get(0));
131             }
132         });
133
134         delRepository = new JButton(Translation.get("gb.delete"));
135         delRepository.setEnabled(false);
136         delRepository.addActionListener(new ActionListener() {
137             public void actionPerformed(ActionEvent e) {
138                 deleteRepositories(getSelectedRepositories());
139             }
140         });
141
142         final JButton subscribeRepository = new JButton(Translation.get("gb.subscribe") + "...");
143         subscribeRepository.setEnabled(false);
144         subscribeRepository.addActionListener(new ActionListener() {
145             public void actionPerformed(ActionEvent e) {
146                 List<FeedModel> feeds = gitblit.getAvailableFeeds(getSelectedRepositories().get(0));
147                 subscribeFeeds(feeds);
148             }
149         });
150
ee458f 151         final JButton logRepository = new JButton(Translation.get("gb.log") + "...");
JM 152         logRepository.setEnabled(false);
153         logRepository.addActionListener(new ActionListener() {
154             public void actionPerformed(ActionEvent e) {
155                 RepositoryModel model = getSelectedRepositories().get(0);
156                 showSearchDialog(false, model);
157             }
158         });
159
9119cf 160         final JButton searchRepository = new JButton(Translation.get("gb.search") + "...");
JM 161         searchRepository.setEnabled(false);
162         searchRepository.addActionListener(new ActionListener() {
163             public void actionPerformed(ActionEvent e) {
164                 RepositoryModel model = getSelectedRepositories().get(0);
ee458f 165                 showSearchDialog(true, model);
9119cf 166             }
JM 167         });
168
38688b 169         SubscribedRepositoryRenderer nameRenderer = new SubscribedRepositoryRenderer(gitblit);
JM 170         IndicatorsRenderer typeRenderer = new IndicatorsRenderer();
171
172         DefaultTableCellRenderer sizeRenderer = new DefaultTableCellRenderer();
173         sizeRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
174         sizeRenderer.setForeground(new Color(0, 0x80, 0));
175
176         DefaultTableCellRenderer ownerRenderer = new DefaultTableCellRenderer();
177         ownerRenderer.setForeground(Color.gray);
178         ownerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
179
180         tableModel = new RepositoriesTableModel();
181         defaultSorter = new TableRowSorter<RepositoriesTableModel>(tableModel);
182         table = Utils.newTable(tableModel, Utils.DATE_FORMAT);
183         table.setRowSorter(defaultSorter);
184         table.getRowSorter().toggleSortOrder(RepositoriesTableModel.Columns.Name.ordinal());
185
186         setRepositoryRenderer(RepositoriesTableModel.Columns.Name, nameRenderer, -1);
187         setRepositoryRenderer(RepositoriesTableModel.Columns.Indicators, typeRenderer, 100);
188         setRepositoryRenderer(RepositoriesTableModel.Columns.Owner, ownerRenderer, -1);
189         setRepositoryRenderer(RepositoriesTableModel.Columns.Size, sizeRenderer, 60);
190
191         table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
192             @Override
193             public void valueChanged(ListSelectionEvent e) {
194                 if (e.getValueIsAdjusting()) {
195                     return;
196                 }
197                 boolean singleSelection = table.getSelectedRowCount() == 1;
ee458f 198                 boolean selected = table.getSelectedRow() > -1;
9119cf 199                 if (singleSelection) {
JM 200                     RepositoryModel repository = getSelectedRepositories().get(0);
201                     browseRepository.setEnabled(repository.hasCommits);
ee458f 202                     logRepository.setEnabled(repository.hasCommits);
9119cf 203                     searchRepository.setEnabled(repository.hasCommits);
JM 204                     subscribeRepository.setEnabled(repository.hasCommits);
205                 } else {
206                     browseRepository.setEnabled(false);
ee458f 207                     logRepository.setEnabled(false);
9119cf 208                     searchRepository.setEnabled(false);
JM 209                     subscribeRepository.setEnabled(false);
210                 }
ee458f 211                 delRepository.setEnabled(selected);
38688b 212                 if (selected) {
JM 213                     int viewRow = table.getSelectedRow();
214                     int modelRow = table.convertRowIndexToModel(viewRow);
215                     RepositoryModel model = ((RepositoriesTableModel) table.getModel()).list
216                             .get(modelRow);
217                     editRepository.setEnabled(singleSelection
218                             && (gitblit.allowManagement() || gitblit.isOwner(model)));
219                 } else {
220                     editRepository.setEnabled(false);
221                 }
222             }
223         });
224
225         table.addMouseListener(new MouseAdapter() {
226             public void mouseClicked(MouseEvent e) {
227                 if (e.getClickCount() == 2 && gitblit.allowManagement()) {
228                     editRepository(getSelectedRepositories().get(0));
229                 }
230             }
231         });
232
233         filterTextfield = new JTextField();
234         filterTextfield.addActionListener(new ActionListener() {
235             public void actionPerformed(ActionEvent e) {
236                 filterRepositories(filterTextfield.getText());
237             }
238         });
239         filterTextfield.addKeyListener(new KeyAdapter() {
240             public void keyReleased(KeyEvent e) {
241                 filterRepositories(filterTextfield.getText());
242             }
243         });
244
245         JPanel repositoryFilterPanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
246         repositoryFilterPanel.add(new JLabel(Translation.get("gb.filter")), BorderLayout.WEST);
247         repositoryFilterPanel.add(filterTextfield, BorderLayout.CENTER);
248
249         JPanel repositoryTablePanel = new JPanel(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
250         repositoryTablePanel.add(repositoryFilterPanel, BorderLayout.NORTH);
251         repositoryTablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
252
253         JPanel repositoryControls = new JPanel(new FlowLayout(FlowLayout.CENTER, Utils.MARGIN, 0));
fee060 254         repositoryControls.add(clearCache);
38688b 255         repositoryControls.add(refreshRepositories);
JM 256         repositoryControls.add(browseRepository);
257         repositoryControls.add(createRepository);
258         repositoryControls.add(editRepository);
259         repositoryControls.add(delRepository);
260         repositoryControls.add(subscribeRepository);
ee458f 261         repositoryControls.add(logRepository);
9119cf 262         repositoryControls.add(searchRepository);
38688b 263
JM 264         setLayout(new BorderLayout(Utils.MARGIN, Utils.MARGIN));
0cf876 265         header = new HeaderPanel(Translation.get("gb.repositories"), "git-orange-16x16.png");
38688b 266         add(header, BorderLayout.NORTH);
JM 267         add(repositoryTablePanel, BorderLayout.CENTER);
268         add(repositoryControls, BorderLayout.SOUTH);
269     }
270
271     @Override
272     public void requestFocus() {
273         filterTextfield.requestFocus();
274     }
275
276     @Override
277     public Insets getInsets() {
278         return Utils.INSETS;
279     }
280
281     private void setRepositoryRenderer(RepositoriesTableModel.Columns col,
282             TableCellRenderer renderer, int maxWidth) {
283         String name = table.getColumnName(col.ordinal());
284         table.getColumn(name).setCellRenderer(renderer);
285         if (maxWidth > 0) {
286             table.getColumn(name).setMinWidth(maxWidth);
287             table.getColumn(name).setMaxWidth(maxWidth);
288         }
289     }
290
291     protected abstract void subscribeFeeds(List<FeedModel> feeds);
292
293     protected abstract void updateUsersTable();
4bd203 294
f08aab 295     protected abstract void updateTeamsTable();
38688b 296
JM 297     protected void disableManagement() {
fee060 298         clearCache.setVisible(false);
38688b 299         createRepository.setVisible(false);
JM 300         editRepository.setVisible(false);
301         delRepository.setVisible(false);
302     }
303
304     protected void updateTable(boolean pack) {
305         tableModel.list.clear();
306         tableModel.list.addAll(gitblit.getRepositories());
307         tableModel.fireTableDataChanged();
308         header.setText(Translation.get("gb.repositories") + " (" + gitblit.getRepositories().size()
309                 + ")");
310         if (pack) {
311             Utils.packColumns(table, Utils.MARGIN);
312         }
313     }
314
315     private void filterRepositories(final String fragment) {
316         if (StringUtils.isEmpty(fragment)) {
317             table.setRowSorter(defaultSorter);
318             return;
319         }
320         RowFilter<RepositoriesTableModel, Object> containsFilter = new RowFilter<RepositoriesTableModel, Object>() {
321             public boolean include(Entry<? extends RepositoriesTableModel, ? extends Object> entry) {
322                 for (int i = entry.getValueCount() - 1; i >= 0; i--) {
323                     if (entry.getStringValue(i).toLowerCase().contains(fragment.toLowerCase())) {
324                         return true;
325                     }
326                 }
327                 return false;
328             }
329         };
330         TableRowSorter<RepositoriesTableModel> sorter = new TableRowSorter<RepositoriesTableModel>(
331                 tableModel);
332         sorter.setRowFilter(containsFilter);
333         table.setRowSorter(sorter);
334     }
335
336     private List<RepositoryModel> getSelectedRepositories() {
337         List<RepositoryModel> repositories = new ArrayList<RepositoryModel>();
338         for (int viewRow : table.getSelectedRows()) {
339             int modelRow = table.convertRowIndexToModel(viewRow);
340             RepositoryModel model = tableModel.list.get(modelRow);
341             repositories.add(model);
342         }
343         return repositories;
344     }
345
346     protected void refreshRepositories() {
347         GitblitWorker worker = new GitblitWorker(RepositoriesPanel.this,
348                 RpcRequest.LIST_REPOSITORIES) {
349             @Override
350             protected Boolean doRequest() throws IOException {
351                 gitblit.refreshRepositories();
352                 return true;
353             }
354
355             @Override
356             protected void onSuccess() {
357                 updateTable(false);
358             }
359         };
360         worker.execute();
361     }
fee060 362     
JM 363     protected void clearCache() {
364         GitblitWorker worker = new GitblitWorker(RepositoriesPanel.this,
365                 RpcRequest.CLEAR_REPOSITORY_CACHE) {
366             @Override
367             protected Boolean doRequest() throws IOException {
368                 if (gitblit.clearRepositoryCache()) {
369                     gitblit.refreshRepositories();
370                     return true;
371                 }
372                 return false;
373             }
374
375             @Override
376             protected void onSuccess() {
377                 updateTable(false);
378             }
379         };
380         worker.execute();
381     }
38688b 382
JM 383     /**
384      * Displays the create repository dialog and fires a SwingWorker to update
385      * the server, if appropriate.
386      * 
387      */
388     protected void createRepository() {
f08aab 389         EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion());
38688b 390         dialog.setLocationRelativeTo(RepositoriesPanel.this);
94dcbd 391         dialog.setAccessRestriction(gitblit.getDefaultAccessRestriction());
6adf56 392         dialog.setAuthorizationControl(gitblit.getDefaultAuthorizationControl());
38688b 393         dialog.setUsers(null, gitblit.getUsernames(), null);
f08aab 394         dialog.setTeams(gitblit.getTeamnames(), null);
38688b 395         dialog.setRepositories(gitblit.getRepositories());
JM 396         dialog.setFederationSets(gitblit.getFederationSets(), null);
1aabf0 397         dialog.setIndexedBranches(new ArrayList<String>(Arrays.asList(Constants.DEFAULT_BRANCH)), null);
f2dff4 398         dialog.setPreReceiveScripts(gitblit.getPreReceiveScriptsUnused(null),
JM 399                 gitblit.getPreReceiveScriptsInherited(null), null);
400         dialog.setPostReceiveScripts(gitblit.getPostReceiveScriptsUnused(null),
401                 gitblit.getPostReceiveScriptsInherited(null), null);
38688b 402         dialog.setVisible(true);
JM 403         final RepositoryModel newRepository = dialog.getRepository();
822dfe 404         final List<RegistrantAccessPermission> permittedUsers = dialog.getUserAccessPermissions();
JM 405         final List<RegistrantAccessPermission> permittedTeams = dialog.getTeamAccessPermissions();
38688b 406         if (newRepository == null) {
JM 407             return;
408         }
409
410         GitblitWorker worker = new GitblitWorker(this, RpcRequest.CREATE_REPOSITORY) {
411
412             @Override
413             protected Boolean doRequest() throws IOException {
4bd203 414                 boolean success = gitblit.createRepository(newRepository, permittedUsers,
JM 415                         permittedTeams);
38688b 416                 if (success) {
JM 417                     gitblit.refreshRepositories();
418                     if (permittedUsers.size() > 0) {
419                         gitblit.refreshUsers();
f08aab 420                     }
JM 421                     if (permittedTeams.size() > 0) {
422                         gitblit.refreshTeams();
38688b 423                     }
JM 424                 }
425                 return success;
426             }
427
428             @Override
429             protected void onSuccess() {
430                 updateTable(false);
431                 updateUsersTable();
f08aab 432                 updateTeamsTable();
38688b 433             }
JM 434
435             @Override
436             protected void onFailure() {
437                 showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
438                         getRequestType(), newRepository.name);
439             }
440         };
441         worker.execute();
442     }
443
444     /**
445      * Displays the edit repository dialog and fires a SwingWorker to update the
446      * server, if appropriate.
447      * 
448      * @param repository
449      */
450     protected void editRepository(final RepositoryModel repository) {
4bd203 451         EditRepositoryDialog dialog = new EditRepositoryDialog(gitblit.getProtocolVersion(),
JM 452                 repository);
38688b 453         dialog.setLocationRelativeTo(RepositoriesPanel.this);
JM 454         List<String> usernames = gitblit.getUsernames();
5e8826 455         List<RegistrantAccessPermission> members = gitblit.getUserAccessPermissions(repository);
661db6 456         dialog.setUsers(new ArrayList<String>(repository.owners), usernames, members);
5e8826 457         dialog.setTeams(gitblit.getTeamnames(), gitblit.getTeamAccessPermissions(repository));
JM 458         dialog.setRepositories(gitblit.getRepositories());
38688b 459         dialog.setFederationSets(gitblit.getFederationSets(), repository.federationSets);
1aabf0 460         List<String> allLocalBranches = new ArrayList<String>();
JM 461         allLocalBranches.add(Constants.DEFAULT_BRANCH);
462         allLocalBranches.addAll(repository.getLocalBranches());
463         dialog.setIndexedBranches(allLocalBranches, repository.indexedBranches);
97d3af 464         dialog.setPreReceiveScripts(gitblit.getPreReceiveScriptsUnused(repository),
f2dff4 465                 gitblit.getPreReceiveScriptsInherited(repository), repository.preReceiveScripts);
97d3af 466         dialog.setPostReceiveScripts(gitblit.getPostReceiveScriptsUnused(repository),
f2dff4 467                 gitblit.getPostReceiveScriptsInherited(repository), repository.postReceiveScripts);
a5b159 468         if (gitblit.getSettings().hasKey(Keys.groovy.customFields)) {
JM 469             Map<String, String> map = gitblit.getSettings().get(Keys.groovy.customFields).getMap();
470             dialog.setCustomFields(repository, map);
471         }
38688b 472         dialog.setVisible(true);
JM 473         final RepositoryModel revisedRepository = dialog.getRepository();
822dfe 474         final List<RegistrantAccessPermission> permittedUsers = dialog.getUserAccessPermissions();
JM 475         final List<RegistrantAccessPermission> permittedTeams = dialog.getTeamAccessPermissions();
38688b 476         if (revisedRepository == null) {
JM 477             return;
478         }
479
480         GitblitWorker worker = new GitblitWorker(this, RpcRequest.EDIT_REPOSITORY) {
481
482             @Override
483             protected Boolean doRequest() throws IOException {
484                 boolean success = gitblit.updateRepository(repository.name, revisedRepository,
f08aab 485                         permittedUsers, permittedTeams);
38688b 486                 if (success) {
JM 487                     gitblit.refreshRepositories();
488                     gitblit.refreshUsers();
f08aab 489                     gitblit.refreshTeams();
38688b 490                 }
JM 491                 return success;
492             }
493
494             @Override
495             protected void onSuccess() {
496                 updateTable(false);
497                 updateUsersTable();
f08aab 498                 updateTeamsTable();
38688b 499             }
JM 500
501             @Override
502             protected void onFailure() {
503                 showFailure("Failed to execute request \"{0}\" for repository \"{1}\".",
504                         getRequestType(), repository.name);
505             }
506         };
507         worker.execute();
508     }
509
510     protected void deleteRepositories(final List<RepositoryModel> repositories) {
511         if (repositories == null || repositories.size() == 0) {
512             return;
513         }
514         StringBuilder message = new StringBuilder("Delete the following repositories?\n\n");
515         for (RepositoryModel repository : repositories) {
516             message.append(repository.name).append("\n");
517         }
518         int result = JOptionPane.showConfirmDialog(RepositoriesPanel.this, message.toString(),
519                 "Delete Repositories?", JOptionPane.YES_NO_OPTION);
520         if (result == JOptionPane.YES_OPTION) {
521             GitblitWorker worker = new GitblitWorker(this, RpcRequest.DELETE_REPOSITORY) {
522                 @Override
523                 protected Boolean doRequest() throws IOException {
524                     boolean success = true;
525                     for (RepositoryModel repository : repositories) {
526                         success &= gitblit.deleteRepository(repository);
527                     }
528                     if (success) {
529                         gitblit.refreshRepositories();
530                         gitblit.refreshUsers();
f08aab 531                         gitblit.refreshTeams();
38688b 532                     }
JM 533                     return success;
534                 }
535
536                 @Override
537                 protected void onSuccess() {
538                     updateTable(false);
539                     updateUsersTable();
f08aab 540                     updateTeamsTable();
38688b 541                 }
JM 542
543                 @Override
544                 protected void onFailure() {
545                     showFailure("Failed to delete specified repositories!");
546                 }
547             };
548             worker.execute();
549         }
550     }
551
ee458f 552     private void showSearchDialog(boolean isSearch, final RepositoryModel repository) {
JM 553         final SearchDialog dialog = new SearchDialog(gitblit, isSearch);
9119cf 554         if (repository != null) {
ee458f 555             dialog.selectRepository(repository);
9119cf 556         }
ee458f 557         dialog.setLocationRelativeTo(this);
JM 558         dialog.setVisible(true);
9119cf 559     }
38688b 560 }