James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
commit | author | age
b0e164 1 /*
JM 2  * Copyright 2012 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.wicket.panels;
17
644bdd 18 import java.text.MessageFormat;
822dfe 19 import java.util.ArrayList;
b0e164 20 import java.util.Arrays;
ba6150 21 import java.util.Collections;
b0e164 22 import java.util.Iterator;
JM 23 import java.util.List;
24 import java.util.Map;
25
ba6150 26 import org.apache.wicket.Component;
b0e164 27 import org.apache.wicket.ajax.AjaxRequestTarget;
JM 28 import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
29 import org.apache.wicket.ajax.markup.html.form.AjaxButton;
30 import org.apache.wicket.markup.html.basic.Label;
31 import org.apache.wicket.markup.html.form.DropDownChoice;
32 import org.apache.wicket.markup.html.form.Form;
33 import org.apache.wicket.markup.html.form.IChoiceRenderer;
ef2290 34 import org.apache.wicket.markup.html.panel.Fragment;
b0e164 35 import org.apache.wicket.markup.repeater.Item;
JM 36 import org.apache.wicket.markup.repeater.OddEvenItem;
37 import org.apache.wicket.markup.repeater.RefreshingView;
38 import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter;
39 import org.apache.wicket.model.CompoundPropertyModel;
40 import org.apache.wicket.model.IModel;
ef2290 41 import org.eclipse.jgit.lib.PersonIdent;
b0e164 42
JM 43 import com.gitblit.Constants.AccessPermission;
092f0a 44 import com.gitblit.Constants.PermissionType;
8bc725 45 import com.gitblit.Constants.RegistrantType;
822dfe 46 import com.gitblit.models.RegistrantAccessPermission;
ef2290 47 import com.gitblit.models.UserModel;
b0e164 48 import com.gitblit.utils.DeepCopier;
8bc725 49 import com.gitblit.utils.StringUtils;
87f6c3 50 import com.gitblit.wicket.WicketUtils;
b0e164 51
JM 52 /**
822dfe 53  * Allows user to manipulate registrant access permissions.
699e71 54  *
b0e164 55  * @author James Moger
JM 56  *
57  */
822dfe 58 public class RegistrantPermissionsPanel extends BasePanel {
b0e164 59
JM 60     private static final long serialVersionUID = 1L;
699e71 61
ba6150 62     public enum Show {
JM 63         specified, mutable, effective;
699e71 64
ba6150 65         public boolean show(RegistrantAccessPermission ap) {
JM 66             switch (this) {
67             case specified:
68                 return ap.mutable || ap.isOwner();
69             case mutable:
70                 return ap.mutable;
71             case effective:
72                 return true;
73             default:
74                 return true;
75             }
76         }
77     }
699e71 78
ba6150 79     private Show activeState = Show.mutable;
699e71 80
092f0a 81     public RegistrantPermissionsPanel(String wicketId, RegistrantType registrantType, List<String> allRegistrants, final List<RegistrantAccessPermission> permissions, final Map<AccessPermission, String> translations) {
b0e164 82         super(wicketId);
092f0a 83         setOutputMarkupId(true);
ba6150 84
JM 85         /*
86          * Permission view toggle buttons
87          */
88         Form<Void> permissionToggleForm = new Form<Void>("permissionToggleForm");
89         permissionToggleForm.add(new ShowStateButton("showSpecified", Show.specified));
90         permissionToggleForm.add(new ShowStateButton("showMutable", Show.mutable));
91         permissionToggleForm.add(new ShowStateButton("showEffective", Show.effective));
92         add(permissionToggleForm);
93
94         /*
95          * Permission repeating display
96          */
822dfe 97         RefreshingView<RegistrantAccessPermission> dataView = new RefreshingView<RegistrantAccessPermission>("permissionRow") {
b0e164 98             private static final long serialVersionUID = 1L;
699e71 99
b0e164 100             @Override
822dfe 101             protected Iterator<IModel<RegistrantAccessPermission>> getItemModels() {
b0e164 102                 // the iterator returns RepositoryPermission objects, but we need it to
JM 103                 // return models
822dfe 104                 return new ModelIteratorAdapter<RegistrantAccessPermission>(permissions.iterator()) {
b0e164 105                     @Override
822dfe 106                     protected IModel<RegistrantAccessPermission> model(RegistrantAccessPermission permission) {
JM 107                         return new CompoundPropertyModel<RegistrantAccessPermission>(permission);
b0e164 108                     }
JM 109                 };
110             }
111
112             @Override
822dfe 113             protected Item<RegistrantAccessPermission> newItem(String id, int index, IModel<RegistrantAccessPermission> model) {
b0e164 114                 // this item sets markup class attribute to either 'odd' or
JM 115                 // 'even' for decoration
822dfe 116                 return new OddEvenItem<RegistrantAccessPermission>(id, index, model);
b0e164 117             }
699e71 118
JM 119             @Override
822dfe 120             public void populateItem(final Item<RegistrantAccessPermission> item) {
JM 121                 final RegistrantAccessPermission entry = item.getModelObject();
092f0a 122                 if (RegistrantType.REPOSITORY.equals(entry.registrantType)) {
8bc725 123                     String repoName = StringUtils.stripDotGit(entry.registrant);
7ba85b 124                     if (!entry.isMissing() && StringUtils.findInvalidCharacter(repoName) == null) {
092f0a 125                         // repository, strip .git and show swatch
ba6150 126                         Fragment repositoryFragment = new Fragment("registrant", "repositoryRegistrant", RegistrantPermissionsPanel.this);
JM 127                         Component swatch = new Label("repositorySwatch", "&nbsp;").setEscapeModelStrings(false);
128                         WicketUtils.setCssBackground(swatch, entry.toString());
129                         repositoryFragment.add(swatch);
130                         Label registrant = new Label("repositoryName", repoName);
131                         repositoryFragment.add(registrant);
132                         item.add(repositoryFragment);
092f0a 133                     } else {
7ba85b 134                         // regex or missing
092f0a 135                         Label label = new Label("registrant", entry.registrant);
JM 136                         WicketUtils.setCssStyle(label, "font-weight: bold;");
137                         item.add(label);
699e71 138                     }
ef2290 139                 } else if (RegistrantType.USER.equals(entry.registrantType)) {
JM 140                     // user
b461a4 141                     PersonIdent ident = new PersonIdent(entry.registrant, "");
99d0d4 142                     UserModel user = app().users().getUserModel(entry.registrant);
ef2290 143                     if (user != null) {
b461a4 144                         ident = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress);
ef2290 145                     }
JM 146
147                     Fragment userFragment = new Fragment("registrant", "userRegistrant", RegistrantPermissionsPanel.this);
b57b9e 148                     userFragment.add(new AvatarImage("userAvatar", ident, 20));
699e71 149                     userFragment.add(new Label("userName", entry.registrant));
JM 150                     item.add(userFragment);
8bc725 151                 } else {
ef2290 152                     // team
JM 153                     Fragment teamFragment = new Fragment("registrant", "teamRegistrant", RegistrantPermissionsPanel.this);
154                     teamFragment.add(new Label("teamName", entry.registrant));
155                     item.add(teamFragment);
8bc725 156                 }
092f0a 157                 switch (entry.permissionType) {
644bdd 158                 case ADMINISTRATOR:
JM 159                     Label administrator = new Label("pType", entry.source == null ? getString("gb.administrator") : entry.source);
160                     WicketUtils.setHtmlTooltip(administrator, getString("gb.administratorPermission"));
161                     WicketUtils.setCssClass(administrator, "label label-inverse");
162                     item.add(administrator);
163                     break;
092f0a 164                 case OWNER:
644bdd 165                     Label owner = new Label("pType", getString("gb.owner"));
092f0a 166                     WicketUtils.setHtmlTooltip(owner, getString("gb.ownerPermission"));
644bdd 167                     WicketUtils.setCssClass(owner, "label label-info");
092f0a 168                     item.add(owner);
644bdd 169                     break;
JM 170                 case TEAM:
171                     Label team = new Label("pType", entry.source == null ? getString("gb.team") : entry.source);
172                     WicketUtils.setHtmlTooltip(team, MessageFormat.format(getString("gb.teamPermission"), entry.source));
173                     WicketUtils.setCssClass(team, "label label-success");
174                     item.add(team);
092f0a 175                     break;
JM 176                 case REGEX:
177                     Label regex = new Label("pType", "regex");
644bdd 178                     if (!StringUtils.isEmpty(entry.source)) {
JM 179                         WicketUtils.setHtmlTooltip(regex, MessageFormat.format(getString("gb.regexPermission"), entry.source));
180                     }
181                     WicketUtils.setCssClass(regex, "label");
87f6c3 182                     item.add(regex);
092f0a 183                     break;
JM 184                 default:
7ba85b 185                     if (entry.isMissing()) {
JM 186                         // repository is missing, this permission will be removed on save
187                         Label missing = new Label("pType", getString("gb.missing"));
188                         WicketUtils.setCssClass(missing, "label label-important");
189                         WicketUtils.setHtmlTooltip(missing, getString("gb.missingPermission"));
190                         item.add(missing);
191                     } else {
192                         // standard permission
193                         item.add(new Label("pType", "").setVisible(false));
194                     }
092f0a 195                     break;
87f6c3 196                 }
b0e164 197
ba6150 198                 item.setVisible(activeState.show(entry));
JM 199
b0e164 200                 // use ajax to get immediate update of permission level change
JM 201                 // otherwise we can lose it if they change levels and then add
202                 // a new repository permission
203                 final DropDownChoice<AccessPermission> permissionChoice = new DropDownChoice<AccessPermission>(
204                         "permission", Arrays.asList(AccessPermission.values()), new AccessPermissionRenderer(translations));
87f6c3 205                 // only allow changing an explicitly defined permission
JM 206                 // this is designed to prevent changing a regex permission in
207                 // a repository
40b07b 208                 permissionChoice.setEnabled(entry.mutable);
092f0a 209                 permissionChoice.setOutputMarkupId(true);
40b07b 210                 if (entry.mutable) {
87f6c3 211                     permissionChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
699e71 212
87f6c3 213                         private static final long serialVersionUID = 1L;
b0e164 214
699e71 215                         @Override
87f6c3 216                         protected void onUpdate(AjaxRequestTarget target) {
JM 217                             target.addComponent(permissionChoice);
218                         }
219                     });
220                 }
b0e164 221
JM 222                 item.add(permissionChoice);
223             }
224         };
225         add(dataView);
226         setOutputMarkupId(true);
227
822dfe 228         // filter out registrants we already have permissions for
JM 229         final List<String> registrants = new ArrayList<String>(allRegistrants);
230         for (RegistrantAccessPermission rp : permissions) {
40b07b 231             if (rp.mutable) {
644bdd 232                 // remove editable duplicates
092f0a 233                 // this allows for specifying an explicit permission
JM 234                 registrants.remove(rp.registrant);
644bdd 235             } else if (rp.isAdmin()) {
JM 236                 // administrators can not have their permission changed
237                 registrants.remove(rp.registrant);
238             } else if (rp.isOwner()) {
239                 // owners can not have their permission changed
240                 registrants.remove(rp.registrant);
092f0a 241             }
b0e164 242         }
JM 243
ba6150 244         /*
JM 245          * Add permission form
246          */
092f0a 247         IModel<RegistrantAccessPermission> addPermissionModel = new CompoundPropertyModel<RegistrantAccessPermission>(new RegistrantAccessPermission(registrantType));
822dfe 248         Form<RegistrantAccessPermission> addPermissionForm = new Form<RegistrantAccessPermission>("addPermissionForm", addPermissionModel);
JM 249         addPermissionForm.add(new DropDownChoice<String>("registrant", registrants));
b0e164 250         addPermissionForm.add(new DropDownChoice<AccessPermission>("permission", Arrays
JM 251                 .asList(AccessPermission.NEWPERMISSIONS), new AccessPermissionRenderer(translations)));
252         AjaxButton button = new AjaxButton("addPermissionButton", addPermissionForm) {
253
254             private static final long serialVersionUID = 1L;
255
256             @Override
257             protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
258                 // add permission to our list
822dfe 259                 RegistrantAccessPermission rp = (RegistrantAccessPermission) form.getModel().getObject();
717267 260                 if (rp.permission == null) {
JM 261                     return;
262                 }
67389b 263                 if (rp.registrant == null) {
JM 264                     return;
265                 }
092f0a 266                 RegistrantAccessPermission copy = DeepCopier.copy(rp);
JM 267                 if (StringUtils.findInvalidCharacter(copy.registrant) != null) {
268                     copy.permissionType = PermissionType.REGEX;
ba6150 269                     copy.source = copy.registrant;
092f0a 270                 }
JM 271                 permissions.add(copy);
699e71 272
ba6150 273                 // resort permissions after insert to convey idea of eval order
JM 274                 Collections.sort(permissions);
699e71 275
822dfe 276                 // remove registrant from available choices
JM 277                 registrants.remove(rp.registrant);
699e71 278
b0e164 279                 // force the panel to refresh
822dfe 280                 target.addComponent(RegistrantPermissionsPanel.this);
b0e164 281             }
JM 282         };
283         addPermissionForm.add(button);
699e71 284
822dfe 285         // only show add permission form if we have a registrant choice
JM 286         add(addPermissionForm.setVisible(registrants.size() > 0));
b0e164 287     }
699e71 288
JM 289     @Override
092f0a 290     protected boolean getStatelessHint()
JM 291     {
292         return false;
293     }
294
699e71 295
b0e164 296     private class AccessPermissionRenderer implements IChoiceRenderer<AccessPermission> {
JM 297
298         private static final long serialVersionUID = 1L;
299
300         private final Map<AccessPermission, String> map;
301
302         public AccessPermissionRenderer(Map<AccessPermission, String> map) {
303             this.map = map;
304         }
305
306         @Override
307         public String getDisplayValue(AccessPermission type) {
308             return map.get(type);
309         }
310
311         @Override
312         public String getIdValue(AccessPermission type, int index) {
313             return Integer.toString(index);
314         }
315     }
699e71 316
ba6150 317     private class ShowStateButton extends AjaxButton {
JM 318         private static final long serialVersionUID = 1L;
319
320         Show buttonState;
699e71 321
ba6150 322         public ShowStateButton(String wicketId, Show state) {
JM 323             super(wicketId);
324             this.buttonState = state;
325             setOutputMarkupId(true);
326         }
699e71 327
ba6150 328         @Override
JM 329         protected void onBeforeRender()
330         {
331             String cssClass = "btn";
332             if (buttonState.equals(RegistrantPermissionsPanel.this.activeState)) {
333                 cssClass = "btn btn-info active";
334             }
335             WicketUtils.setCssClass(this, cssClass);
336             super.onBeforeRender();
337         }
699e71 338
ba6150 339         @Override
JM 340         protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
341             RegistrantPermissionsPanel.this.activeState = buttonState;
342             target.addComponent(RegistrantPermissionsPanel.this);
343         }
344     };
b0e164 345 }