/* * Copyright 2012 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.wicket.panels; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.form.DropDownChoice; import org.apache.wicket.markup.html.form.Form; import org.apache.wicket.markup.html.form.IChoiceRenderer; import org.apache.wicket.markup.html.panel.Fragment; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.markup.repeater.OddEvenItem; import org.apache.wicket.markup.repeater.RefreshingView; import org.apache.wicket.markup.repeater.util.ModelIteratorAdapter; import org.apache.wicket.model.CompoundPropertyModel; import org.apache.wicket.model.IModel; import org.eclipse.jgit.lib.PersonIdent; import com.gitblit.Constants.AccessPermission; import com.gitblit.Constants.PermissionType; import com.gitblit.Constants.RegistrantType; import com.gitblit.GitBlit; import com.gitblit.models.RegistrantAccessPermission; import com.gitblit.models.UserModel; import com.gitblit.utils.DeepCopier; import com.gitblit.utils.StringUtils; import com.gitblit.wicket.WicketUtils; /** * Allows user to manipulate registrant access permissions. * * @author James Moger * */ public class RegistrantPermissionsPanel extends BasePanel { private static final long serialVersionUID = 1L; public enum Show { specified, mutable, effective; public boolean show(RegistrantAccessPermission ap) { switch (this) { case specified: return ap.mutable || ap.isOwner(); case mutable: return ap.mutable; case effective: return true; default: return true; } } } private Show activeState = Show.mutable; public RegistrantPermissionsPanel(String wicketId, RegistrantType registrantType, List allRegistrants, final List permissions, final Map translations) { super(wicketId); setOutputMarkupId(true); /* * Permission view toggle buttons */ Form permissionToggleForm = new Form("permissionToggleForm"); permissionToggleForm.add(new ShowStateButton("showSpecified", Show.specified)); permissionToggleForm.add(new ShowStateButton("showMutable", Show.mutable)); permissionToggleForm.add(new ShowStateButton("showEffective", Show.effective)); add(permissionToggleForm); /* * Permission repeating display */ RefreshingView dataView = new RefreshingView("permissionRow") { private static final long serialVersionUID = 1L; @Override protected Iterator> getItemModels() { // the iterator returns RepositoryPermission objects, but we need it to // return models return new ModelIteratorAdapter(permissions.iterator()) { @Override protected IModel model(RegistrantAccessPermission permission) { return new CompoundPropertyModel(permission); } }; } @Override protected Item newItem(String id, int index, IModel model) { // this item sets markup class attribute to either 'odd' or // 'even' for decoration return new OddEvenItem(id, index, model); } public void populateItem(final Item item) { final RegistrantAccessPermission entry = item.getModelObject(); if (RegistrantType.REPOSITORY.equals(entry.registrantType)) { String repoName = StringUtils.stripDotGit(entry.registrant); if (!entry.isMissing() && StringUtils.findInvalidCharacter(repoName) == null) { // repository, strip .git and show swatch Fragment repositoryFragment = new Fragment("registrant", "repositoryRegistrant", RegistrantPermissionsPanel.this); Component swatch = new Label("repositorySwatch", " ").setEscapeModelStrings(false); WicketUtils.setCssBackground(swatch, entry.toString()); repositoryFragment.add(swatch); Label registrant = new Label("repositoryName", repoName); repositoryFragment.add(registrant); item.add(repositoryFragment); } else { // regex or missing Label label = new Label("registrant", entry.registrant); WicketUtils.setCssStyle(label, "font-weight: bold;"); item.add(label); } } else if (RegistrantType.USER.equals(entry.registrantType)) { // user PersonIdent ident = new PersonIdent(entry.registrant, ""); UserModel user = GitBlit.self().getUserModel(entry.registrant); if (user != null) { ident = new PersonIdent(user.getDisplayName(), user.emailAddress == null ? user.getDisplayName() : user.emailAddress); } Fragment userFragment = new Fragment("registrant", "userRegistrant", RegistrantPermissionsPanel.this); userFragment.add(new GravatarImage("userAvatar", ident, 20, false)); userFragment.add(new Label("userName", entry.registrant)); item.add(userFragment); } else { // team Fragment teamFragment = new Fragment("registrant", "teamRegistrant", RegistrantPermissionsPanel.this); teamFragment.add(new Label("teamName", entry.registrant)); item.add(teamFragment); } switch (entry.permissionType) { case ADMINISTRATOR: Label administrator = new Label("pType", entry.source == null ? getString("gb.administrator") : entry.source); WicketUtils.setHtmlTooltip(administrator, getString("gb.administratorPermission")); WicketUtils.setCssClass(administrator, "label label-inverse"); item.add(administrator); break; case OWNER: Label owner = new Label("pType", getString("gb.owner")); WicketUtils.setHtmlTooltip(owner, getString("gb.ownerPermission")); WicketUtils.setCssClass(owner, "label label-info"); item.add(owner); break; case TEAM: Label team = new Label("pType", entry.source == null ? getString("gb.team") : entry.source); WicketUtils.setHtmlTooltip(team, MessageFormat.format(getString("gb.teamPermission"), entry.source)); WicketUtils.setCssClass(team, "label label-success"); item.add(team); break; case REGEX: Label regex = new Label("pType", "regex"); if (!StringUtils.isEmpty(entry.source)) { WicketUtils.setHtmlTooltip(regex, MessageFormat.format(getString("gb.regexPermission"), entry.source)); } WicketUtils.setCssClass(regex, "label"); item.add(regex); break; default: if (entry.isMissing()) { // repository is missing, this permission will be removed on save Label missing = new Label("pType", getString("gb.missing")); WicketUtils.setCssClass(missing, "label label-important"); WicketUtils.setHtmlTooltip(missing, getString("gb.missingPermission")); item.add(missing); } else { // standard permission item.add(new Label("pType", "").setVisible(false)); } break; } item.setVisible(activeState.show(entry)); // use ajax to get immediate update of permission level change // otherwise we can lose it if they change levels and then add // a new repository permission final DropDownChoice permissionChoice = new DropDownChoice( "permission", Arrays.asList(AccessPermission.values()), new AccessPermissionRenderer(translations)); // only allow changing an explicitly defined permission // this is designed to prevent changing a regex permission in // a repository permissionChoice.setEnabled(entry.mutable); permissionChoice.setOutputMarkupId(true); if (entry.mutable) { permissionChoice.add(new AjaxFormComponentUpdatingBehavior("onchange") { private static final long serialVersionUID = 1L; protected void onUpdate(AjaxRequestTarget target) { target.addComponent(permissionChoice); } }); } item.add(permissionChoice); } }; add(dataView); setOutputMarkupId(true); // filter out registrants we already have permissions for final List registrants = new ArrayList(allRegistrants); for (RegistrantAccessPermission rp : permissions) { if (rp.mutable) { // remove editable duplicates // this allows for specifying an explicit permission registrants.remove(rp.registrant); } else if (rp.isAdmin()) { // administrators can not have their permission changed registrants.remove(rp.registrant); } else if (rp.isOwner()) { // owners can not have their permission changed registrants.remove(rp.registrant); } } /* * Add permission form */ IModel addPermissionModel = new CompoundPropertyModel(new RegistrantAccessPermission(registrantType)); Form addPermissionForm = new Form("addPermissionForm", addPermissionModel); addPermissionForm.add(new DropDownChoice("registrant", registrants)); addPermissionForm.add(new DropDownChoice("permission", Arrays .asList(AccessPermission.NEWPERMISSIONS), new AccessPermissionRenderer(translations))); AjaxButton button = new AjaxButton("addPermissionButton", addPermissionForm) { private static final long serialVersionUID = 1L; @Override protected void onSubmit(AjaxRequestTarget target, Form form) { // add permission to our list RegistrantAccessPermission rp = (RegistrantAccessPermission) form.getModel().getObject(); if (rp.permission == null) { return; } RegistrantAccessPermission copy = DeepCopier.copy(rp); if (StringUtils.findInvalidCharacter(copy.registrant) != null) { copy.permissionType = PermissionType.REGEX; copy.source = copy.registrant; } permissions.add(copy); // resort permissions after insert to convey idea of eval order Collections.sort(permissions); // remove registrant from available choices registrants.remove(rp.registrant); // force the panel to refresh target.addComponent(RegistrantPermissionsPanel.this); } }; addPermissionForm.add(button); // only show add permission form if we have a registrant choice add(addPermissionForm.setVisible(registrants.size() > 0)); } protected boolean getStatelessHint() { return false; } private class AccessPermissionRenderer implements IChoiceRenderer { private static final long serialVersionUID = 1L; private final Map map; public AccessPermissionRenderer(Map map) { this.map = map; } @Override public String getDisplayValue(AccessPermission type) { return map.get(type); } @Override public String getIdValue(AccessPermission type, int index) { return Integer.toString(index); } } private class ShowStateButton extends AjaxButton { private static final long serialVersionUID = 1L; Show buttonState; public ShowStateButton(String wicketId, Show state) { super(wicketId); this.buttonState = state; setOutputMarkupId(true); } @Override protected void onBeforeRender() { String cssClass = "btn"; if (buttonState.equals(RegistrantPermissionsPanel.this.activeState)) { cssClass = "btn btn-info active"; } WicketUtils.setCssClass(this, cssClass); super.onBeforeRender(); } @Override protected void onSubmit(AjaxRequestTarget target, Form form) { RegistrantPermissionsPanel.this.activeState = buttonState; target.addComponent(RegistrantPermissionsPanel.this); } }; }