James Moger
2011-10-22 16038ce2adf5f06f75230150f75d110a804414c1
commit | author | age
da0269 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;
b7f591 19 import java.awt.Component;
da0269 20 import java.awt.Dimension;
JM 21 import java.awt.FlowLayout;
22 import java.awt.Font;
23 import java.awt.GridLayout;
24 import java.awt.Insets;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
f14f76 27 import java.awt.event.KeyEvent;
bcc616 28 import java.text.MessageFormat;
JM 29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.HashSet;
da0269 32 import java.util.List;
bcc616 33 import java.util.Set;
da0269 34
bcc616 35 import javax.swing.DefaultComboBoxModel;
da0269 36 import javax.swing.ImageIcon;
JM 37 import javax.swing.JButton;
38 import javax.swing.JCheckBox;
39 import javax.swing.JComboBox;
40 import javax.swing.JComponent;
41 import javax.swing.JDialog;
42 import javax.swing.JLabel;
b7f591 43 import javax.swing.JList;
da0269 44 import javax.swing.JOptionPane;
JM 45 import javax.swing.JPanel;
f14f76 46 import javax.swing.JRootPane;
55a46b 47 import javax.swing.JTabbedPane;
da0269 48 import javax.swing.JTextField;
f14f76 49 import javax.swing.KeyStroke;
b7f591 50 import javax.swing.ListCellRenderer;
da0269 51
JM 52 import com.gitblit.Constants.AccessRestrictionType;
53 import com.gitblit.Constants.FederationStrategy;
54 import com.gitblit.models.RepositoryModel;
55 import com.gitblit.utils.StringUtils;
56
b7f591 57 /**
JM 58  * Dialog to create/edit a repository.
59  * 
60  * @author James Moger
61  */
da0269 62 public class EditRepositoryDialog extends JDialog {
JM 63
64     private static final long serialVersionUID = 1L;
65
16038c 66     private final String repositoryName;
JM 67
da0269 68     private final RepositoryModel repository;
bcc616 69
JM 70     private boolean isCreate;
da0269 71
JM 72     private boolean canceled = true;
73
74     private JTextField nameField;
75
76     private JTextField descriptionField;
77
78     private JCheckBox useTickets;
79
80     private JCheckBox useDocs;
81
82     private JCheckBox showRemoteBranches;
83
84     private JCheckBox showReadme;
85
3d293a 86     private JCheckBox skipSizeCalculation;
JM 87
da0269 88     private JCheckBox isFrozen;
JM 89
90     private JComboBox accessRestriction;
91
92     private JComboBox federationStrategy;
93
bcc616 94     private JComboBox ownerField;
da0269 95
JM 96     private JPalette<String> usersPalette;
97
98     private JPalette<String> setsPalette;
99
bcc616 100     private Set<String> repositoryNames;
JM 101
102     public EditRepositoryDialog() {
103         this(new RepositoryModel());
104         this.isCreate = true;
b7f591 105         setTitle(Translation.get("gb.newRepository"));
da0269 106     }
JM 107
bcc616 108     public EditRepositoryDialog(RepositoryModel aRepository) {
da0269 109         super();
16038c 110         this.repositoryName = aRepository.name;
da0269 111         this.repository = new RepositoryModel();
bcc616 112         this.repositoryNames = new HashSet<String>();
JM 113         this.isCreate = false;
114         initialize(aRepository);
da0269 115         setModal(true);
55a46b 116         setResizable(false);
b7f591 117         setTitle(Translation.get("gb.edit") + ": " + aRepository.name);
da0269 118         setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
f14f76 119     }
16038c 120
f14f76 121     @Override
JM 122     protected JRootPane createRootPane() {
123         KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
124         JRootPane rootPane = new JRootPane();
125         rootPane.registerKeyboardAction(new ActionListener() {
126             public void actionPerformed(ActionEvent actionEvent) {
127                 setVisible(false);
128             }
129         }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
130         return rootPane;
da0269 131     }
JM 132
bcc616 133     private void initialize(RepositoryModel anRepository) {
da0269 134         nameField = new JTextField(anRepository.name == null ? "" : anRepository.name, 35);
JM 135         descriptionField = new JTextField(anRepository.description == null ? ""
136                 : anRepository.description, 35);
55a46b 137
JM 138         JTextField originField = new JTextField(anRepository.origin == null ? ""
139                 : anRepository.origin, 40);
140         originField.setEditable(false);
da0269 141
bcc616 142         ownerField = new JComboBox();
da0269 143
b7f591 144         useTickets = new JCheckBox(Translation.get("gb.useTicketsDescription"),
JM 145                 anRepository.useTickets);
146         useDocs = new JCheckBox(Translation.get("gb.useDocsDescription"), anRepository.useDocs);
147         showRemoteBranches = new JCheckBox(Translation.get("gb.showRemoteBranchesDescription"),
148                 anRepository.showRemoteBranches);
149         showReadme = new JCheckBox(Translation.get("gb.showReadmeDescription"),
da0269 150                 anRepository.showReadme);
3d293a 151         skipSizeCalculation = new JCheckBox(Translation.get("gb.skipSizeCalculationDescription"),
JM 152                 anRepository.skipSizeCalculation);
b7f591 153         isFrozen = new JCheckBox(Translation.get("gb.isFrozenDescription"), anRepository.isFrozen);
da0269 154
JM 155         accessRestriction = new JComboBox(AccessRestrictionType.values());
b7f591 156         accessRestriction.setRenderer(new AccessRestrictionRenderer());
da0269 157         accessRestriction.setSelectedItem(anRepository.accessRestriction);
JM 158
bcc616 159         // federation strategies - remove ORIGIN choice if this repository has
JM 160         // no origin.
161         List<FederationStrategy> federationStrategies = new ArrayList<FederationStrategy>(
162                 Arrays.asList(FederationStrategy.values()));
163         if (StringUtils.isEmpty(anRepository.origin)) {
164             federationStrategies.remove(FederationStrategy.FEDERATE_ORIGIN);
165         }
166         federationStrategy = new JComboBox(federationStrategies.toArray());
b7f591 167         federationStrategy.setRenderer(new FederationStrategyRenderer());
da0269 168         federationStrategy.setSelectedItem(anRepository.federationStrategy);
JM 169
170         JPanel fieldsPanel = new JPanel(new GridLayout(0, 1));
b7f591 171         fieldsPanel.add(newFieldPanel(Translation.get("gb.name"), nameField));
JM 172         fieldsPanel.add(newFieldPanel(Translation.get("gb.description"), descriptionField));
55a46b 173         fieldsPanel.add(newFieldPanel(Translation.get("gb.origin"), originField));
bcc616 174         fieldsPanel.add(newFieldPanel(Translation.get("gb.owner"), ownerField));
da0269 175
b7f591 176         fieldsPanel.add(newFieldPanel(Translation.get("gb.enableTickets"), useTickets));
JM 177         fieldsPanel.add(newFieldPanel(Translation.get("gb.enableDocs"), useDocs));
178         fieldsPanel
179                 .add(newFieldPanel(Translation.get("gb.showRemoteBranches"), showRemoteBranches));
180         fieldsPanel.add(newFieldPanel(Translation.get("gb.showReadme"), showReadme));
16038c 181         fieldsPanel.add(newFieldPanel(Translation.get("gb.skipSizeCalculation"),
JM 182                 skipSizeCalculation));
b7f591 183         fieldsPanel.add(newFieldPanel(Translation.get("gb.isFrozen"), isFrozen));
da0269 184
JM 185         usersPalette = new JPalette<String>();
186         JPanel accessPanel = new JPanel(new BorderLayout(5, 5));
b7f591 187         accessPanel.add(newFieldPanel(Translation.get("gb.accessRestriction"), accessRestriction),
JM 188                 BorderLayout.NORTH);
189         accessPanel.add(newFieldPanel(Translation.get("gb.permittedUsers"), usersPalette),
190                 BorderLayout.CENTER);
da0269 191
JM 192         setsPalette = new JPalette<String>();
193         JPanel federationPanel = new JPanel(new BorderLayout(5, 5));
b7f591 194         federationPanel.add(
JM 195                 newFieldPanel(Translation.get("gb.federationStrategy"), federationStrategy),
da0269 196                 BorderLayout.NORTH);
b7f591 197         federationPanel.add(newFieldPanel(Translation.get("gb.federationSets"), setsPalette),
JM 198                 BorderLayout.CENTER);
da0269 199
55a46b 200         JTabbedPane panel = new JTabbedPane(JTabbedPane.TOP);
JM 201         panel.addTab(Translation.get("gb.general"), fieldsPanel);
202         panel.addTab(Translation.get("gb.accessRestriction"), accessPanel);
203         panel.addTab(Translation.get("gb.federation"), federationPanel);
da0269 204
b7f591 205         JButton createButton = new JButton(Translation.get("gb.save"));
da0269 206         createButton.addActionListener(new ActionListener() {
JM 207             public void actionPerformed(ActionEvent event) {
208                 if (validateFields()) {
209                     canceled = false;
210                     setVisible(false);
211                 }
212             }
213         });
214
b7f591 215         JButton cancelButton = new JButton(Translation.get("gb.cancel"));
da0269 216         cancelButton.addActionListener(new ActionListener() {
JM 217             public void actionPerformed(ActionEvent event) {
218                 canceled = true;
219                 setVisible(false);
220             }
221         });
222
223         JPanel controls = new JPanel();
224         controls.add(cancelButton);
225         controls.add(createButton);
226
227         final Insets _insets = new Insets(5, 5, 5, 5);
228         JPanel centerPanel = new JPanel(new BorderLayout(5, 5)) {
229
230             private static final long serialVersionUID = 1L;
231
232             @Override
233             public Insets getInsets() {
234                 return _insets;
235             }
236         };
237         centerPanel.add(panel, BorderLayout.CENTER);
238         centerPanel.add(controls, BorderLayout.SOUTH);
239
240         getContentPane().setLayout(new BorderLayout(5, 5));
241         getContentPane().add(centerPanel, BorderLayout.CENTER);
242         pack();
243     }
244
245     private JPanel newFieldPanel(String label, JComponent comp) {
246         JLabel fieldLabel = new JLabel(label);
247         fieldLabel.setFont(fieldLabel.getFont().deriveFont(Font.BOLD));
248         fieldLabel.setPreferredSize(new Dimension(150, 20));
249         JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
250         panel.add(fieldLabel);
251         panel.add(comp);
252         return panel;
253     }
254
255     private boolean validateFields() {
bcc616 256         String rname = nameField.getText();
JM 257         if (StringUtils.isEmpty(rname)) {
258             error("Please enter a repository name!");
259             return false;
260         }
261
262         // automatically convert backslashes to forward slashes
263         rname = rname.replace('\\', '/');
264         // Automatically replace // with /
265         rname = rname.replace("//", "/");
266
267         // prohibit folder paths
268         if (rname.startsWith("/")) {
269             error("Leading root folder references (/) are prohibited.");
270             return false;
271         }
272         if (rname.startsWith("../")) {
273             error("Relative folder references (../) are prohibited.");
274             return false;
275         }
276         if (rname.contains("/../")) {
277             error("Relative folder references (../) are prohibited.");
278             return false;
279         }
280
281         // confirm valid characters in repository name
282         Character c = StringUtils.findInvalidCharacter(rname);
283         if (c != null) {
284             error(MessageFormat.format("Illegal character ''{0}'' in repository name!", c));
285             return false;
286         }
287
288         // verify repository name uniqueness on create
289         if (isCreate) {
290             // force repo names to lowercase
291             // this means that repository name checking for rpc creation
292             // is case-insensitive, regardless of the Gitblit server's
293             // filesystem
294             if (repositoryNames.contains(rname.toLowerCase())) {
295                 error(MessageFormat.format(
296                         "Can not create repository ''{0}'' because it already exists.", rname));
297                 return false;
298             }
16038c 299         } else {
JM 300             // check rename collision
301             if (!repositoryName.equalsIgnoreCase(rname)) {
302                 if (repositoryNames.contains(rname.toLowerCase())) {
303                     error(MessageFormat.format(
304                             "Failed to rename ''{0}'' because ''{1}'' already exists.",
305                             repositoryName, rname));
306                     return false;
307                 }
308             }
bcc616 309         }
JM 310
311         if (accessRestriction.getSelectedItem() == null) {
312             error("Please select access restriction!");
313             return false;
314         }
315
316         if (federationStrategy.getSelectedItem() == null) {
317             error("Please select federation strategy!");
318             return false;
319         }
320
321         repository.name = rname;
322         repository.description = descriptionField.getText();
323         repository.owner = ownerField.getSelectedItem() == null ? null : ownerField
324                 .getSelectedItem().toString();
325         repository.useTickets = useTickets.isSelected();
326         repository.useDocs = useDocs.isSelected();
327         repository.showRemoteBranches = showRemoteBranches.isSelected();
328         repository.showReadme = showReadme.isSelected();
3d293a 329         repository.skipSizeCalculation = skipSizeCalculation.isSelected();
bcc616 330         repository.isFrozen = isFrozen.isSelected();
JM 331
332         repository.accessRestriction = (AccessRestrictionType) accessRestriction.getSelectedItem();
333         repository.federationStrategy = (FederationStrategy) federationStrategy.getSelectedItem();
334
335         if (repository.federationStrategy.exceeds(FederationStrategy.EXCLUDE)) {
336             repository.federationSets = setsPalette.getSelections();
337         }
da0269 338         return true;
JM 339     }
340
bcc616 341     private void error(String message) {
b7f591 342         JOptionPane.showMessageDialog(EditRepositoryDialog.this, message,
JM 343                 Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
da0269 344     }
JM 345
bcc616 346     public void setUsers(String owner, List<String> all, List<String> selected) {
JM 347         ownerField.setModel(new DefaultComboBoxModel(all.toArray()));
348         if (!StringUtils.isEmpty(owner)) {
349             ownerField.setSelectedItem(owner);
350         }
da0269 351         usersPalette.setObjects(all, selected);
bcc616 352     }
JM 353
354     public void setRepositories(List<RepositoryModel> repositories) {
355         repositoryNames.clear();
356         for (RepositoryModel repository : repositories) {
357             // force repo names to lowercase
358             // this means that repository name checking for rpc creation
359             // is case-insensitive, regardless of the Gitblit server's
360             // filesystem
361             repositoryNames.add(repository.name.toLowerCase());
362         }
da0269 363     }
JM 364
365     public void setFederationSets(List<String> all, List<String> selected) {
366         setsPalette.setObjects(all, selected);
367     }
368
369     public RepositoryModel getRepository() {
370         if (canceled) {
371             return null;
372         }
373         return repository;
374     }
b7f591 375
bcc616 376     public List<String> getPermittedUsers() {
JM 377         return usersPalette.getSelections();
378     }
379
b7f591 380     /**
JM 381      * ListCellRenderer to display descriptive text about the access
382      * restriction.
383      * 
384      */
385     private class AccessRestrictionRenderer extends JLabel implements ListCellRenderer {
386
387         private static final long serialVersionUID = 1L;
388
389         @Override
390         public Component getListCellRendererComponent(JList list, Object value, int index,
391                 boolean isSelected, boolean cellHasFocus) {
392             if (value instanceof AccessRestrictionType) {
393                 AccessRestrictionType restriction = (AccessRestrictionType) value;
394                 switch (restriction) {
395                 case NONE:
396                     setText(Translation.get("gb.notRestricted"));
397                     break;
398                 case PUSH:
399                     setText(Translation.get("gb.pushRestricted"));
400                     break;
401                 case CLONE:
402                     setText(Translation.get("gb.cloneRestricted"));
403                     break;
404                 case VIEW:
405                     setText(Translation.get("gb.viewRestricted"));
406                     break;
407                 }
408             } else {
409                 setText(value.toString());
410             }
411             return this;
412         }
413     }
414
415     /**
416      * ListCellRenderer to display descriptive text about the federation
417      * strategy.
418      */
419     private class FederationStrategyRenderer extends JLabel implements ListCellRenderer {
420
421         private static final long serialVersionUID = 1L;
422
423         @Override
424         public Component getListCellRendererComponent(JList list, Object value, int index,
425                 boolean isSelected, boolean cellHasFocus) {
426             if (value instanceof FederationStrategy) {
427                 FederationStrategy strategy = (FederationStrategy) value;
428                 switch (strategy) {
429                 case EXCLUDE:
430                     setText(Translation.get("gb.excludeFromFederation"));
431                     break;
432                 case FEDERATE_THIS:
433                     setText(Translation.get("gb.federateThis"));
434                     break;
435                 case FEDERATE_ORIGIN:
436                     setText(Translation.get("gb.federateOrigin"));
437                     break;
438                 }
439             } else {
440                 setText(value.toString());
441             }
442             return this;
443         }
444     }
da0269 445 }