| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog; |
| |
| import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; |
| import de.agilecoders.wicket.extensions.markup.html.bootstrap.behavior.Draggable; |
| import de.agilecoders.wicket.extensions.markup.html.bootstrap.behavior.DraggableConfig; |
| import java.io.Serializable; |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.apache.syncope.client.console.panels.AbstractModalPanel; |
| import org.apache.syncope.client.console.wicket.markup.html.bootstrap.buttons.DefaultModalCloseButton; |
| import org.apache.wicket.Component; |
| import org.apache.wicket.WicketRuntimeException; |
| import org.apache.wicket.ajax.AjaxRequestTarget; |
| import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink; |
| import org.apache.wicket.core.request.handler.IPartialPageRequestHandler; |
| import org.apache.wicket.markup.html.WebMarkupContainer; |
| import org.apache.wicket.markup.html.form.Form; |
| import org.apache.wicket.markup.html.list.ListItem; |
| import org.apache.wicket.markup.html.list.ListView; |
| import org.apache.wicket.markup.html.panel.Panel; |
| import org.apache.wicket.model.CompoundPropertyModel; |
| import org.apache.wicket.model.IModel; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.syncope.client.ui.commons.panels.SubmitableModalPanel; |
| import org.apache.syncope.client.console.wicket.ajax.form.IndicatorModalCloseBehavior; |
| import org.apache.syncope.client.ui.commons.panels.ModalPanel; |
| import org.apache.syncope.client.ui.commons.panels.NotificationPanel; |
| import org.apache.wicket.ajax.AjaxEventBehavior; |
| import org.apache.wicket.event.IEvent; |
| import org.apache.wicket.markup.head.IHeaderResponse; |
| import org.apache.wicket.markup.head.OnDomReadyHeaderItem; |
| import org.apache.wicket.util.io.IClusterable; |
| |
| public class BaseModal<T extends Serializable> extends Modal<T> { |
| |
| private static final long serialVersionUID = -6142277554912316095L; |
| |
| protected static final Logger LOG = LoggerFactory.getLogger(BaseModal.class); |
| |
| /** the default id of the content component */ |
| public static final String CONTENT_ID = "content"; |
| |
| private static final String SUBMIT = "submit"; |
| |
| private static final String FORM = "form"; |
| |
| protected NotificationPanel notificationPanel; |
| |
| private final List<Component> components; |
| |
| private WindowClosedCallback windowClosedCallback; |
| |
| private Panel content; |
| |
| private AjaxSubmitLink submitButton; |
| |
| private final Form<T> form; |
| |
| private final DefaultModalCloseButton defaultModalCloseButton; |
| |
| private AjaxEventBehavior closeBehavior; |
| |
| private WebMarkupContainer footer; |
| |
| public BaseModal(final String id) { |
| super(id); |
| |
| form = new Form<>(FORM); |
| form.setOutputMarkupId(true); |
| add(form); |
| |
| content = new AbstractModalPanel<T>(this, null) { |
| |
| private static final long serialVersionUID = -6142277554912316095L; |
| |
| }; |
| |
| content.setOutputMarkupId(true); |
| |
| form.add(content); |
| |
| useCloseHandler(true); |
| this.windowClosedCallback = null; |
| components = new ArrayList<>(); |
| |
| // Note: not adding this would imply adding of WebjarsJavaScriptResourceReference about JQuery draggable |
| add(new Draggable(new DraggableConfig().withHandle(".modal-header").withCursor("move"))); |
| |
| defaultModalCloseButton = new DefaultModalCloseButton(); |
| addButton(defaultModalCloseButton); |
| setUseKeyboard(true); |
| setFadeIn(true); |
| } |
| |
| public Form<T> getForm() { |
| return form; |
| } |
| |
| public BaseModal<T> setFormModel(final T modelObject) { |
| form.setModel(new CompoundPropertyModel<>(modelObject)); |
| return this; |
| } |
| |
| public BaseModal<T> setFormModel(final IModel<T> model) { |
| form.setModel(model); |
| return this; |
| } |
| |
| public BaseModal<T> setFormAsMultipart(final boolean multipart) { |
| form.setMultiPart(multipart); |
| return this; |
| } |
| |
| public T getFormModel() { |
| return form.getModelObject(); |
| } |
| |
| public ModalPanel getContent() { |
| if (content instanceof ModalPanel) { |
| return (ModalPanel) content; |
| } |
| throw new IllegalStateException(); |
| } |
| |
| public BaseModal<T> setContent(final ModalPanel component) { |
| if (component instanceof Panel) { |
| return setInternalContent(Panel.class.cast(component)); |
| } |
| throw new IllegalArgumentException("Panel instance is required"); |
| } |
| |
| public BaseModal<T> setContent(final ModalPanel component, final AjaxRequestTarget target) { |
| setContent(component); |
| target.add(content); |
| return this; |
| } |
| |
| public BaseModal<T> changeCloseButtonLabel(final String label) { |
| defaultModalCloseButton.getModel().setObject(label); |
| return this; |
| } |
| |
| public BaseModal<T> changeCloseButtonLabel(final String label, final AjaxRequestTarget target) { |
| changeCloseButtonLabel(label); |
| target.add(defaultModalCloseButton); |
| return this; |
| } |
| |
| private BaseModal<T> setInternalContent(final Panel component) { |
| if (!component.getId().equals(CONTENT_ID)) { |
| throw new WicketRuntimeException("Modal content id is wrong. " |
| + "Component ID: " + component.getId() + "; content ID: " + CONTENT_ID); |
| } |
| |
| content.replaceWith(component); |
| content = component; |
| |
| return this; |
| } |
| |
| public BaseModal<T> setWindowClosedCallback(final WindowClosedCallback callback) { |
| windowClosedCallback = callback; |
| return this; |
| } |
| |
| @Override |
| protected void onClose(final IPartialPageRequestHandler target) { |
| if (windowClosedCallback != null) { |
| windowClosedCallback.onClose((AjaxRequestTarget) target); |
| } |
| } |
| |
| public AjaxSubmitLink addSubmitButton() { |
| if (!(BaseModal.this.getContent() instanceof SubmitableModalPanel)) { |
| throw new IllegalStateException(); |
| } |
| |
| AjaxSubmitLink submit = new AjaxSubmitLink(SUBMIT, form) { |
| |
| private static final long serialVersionUID = -5783994974426198290L; |
| |
| @Override |
| protected void onSubmit(final AjaxRequestTarget target) { |
| SubmitableModalPanel.class.cast(BaseModal.this.getContent()).onSubmit(target); |
| } |
| |
| @Override |
| protected void onError(final AjaxRequestTarget target) { |
| SubmitableModalPanel.class.cast(BaseModal.this.getContent()).onError(target); |
| } |
| }; |
| |
| submit.setOutputMarkupId(true); |
| |
| if (submitButton == null) { |
| submitButton = submit; |
| components.add(submitButton); |
| } else { |
| submitButton.replaceWith(submit); |
| submitButton = submit; |
| } |
| |
| return submit; |
| } |
| |
| public void removeSubmitButton() { |
| if (!(BaseModal.this.getContent() instanceof SubmitableModalPanel)) { |
| throw new IllegalStateException(); |
| } |
| |
| components.stream(). |
| filter(component -> SUBMIT.equals(component.getId())). |
| findFirst(). |
| ifPresent(components::remove); |
| |
| submitButton = null; |
| } |
| |
| @Override |
| protected void onInitialize() { |
| super.onInitialize(); |
| |
| final WebMarkupContainer dialog = (WebMarkupContainer) this.get("dialog"); |
| dialog.setMarkupId(this.getId()); |
| |
| footer = (WebMarkupContainer) this.get("dialog:footer"); |
| footer.addOrReplace(new ListView<Component>("inputs", components) { |
| |
| private static final long serialVersionUID = 4949588177564901031L; |
| |
| @Override |
| protected void populateItem(final ListItem<Component> item) { |
| item.add(item.getModelObject()); |
| } |
| }.setOutputMarkupId(true)).setOutputMarkupId(true); |
| } |
| |
| /** |
| * Generic modal event. |
| */ |
| public static class ModalEvent implements Serializable { |
| |
| private static final long serialVersionUID = 2668922412196063559L; |
| |
| /** |
| * Request target. |
| */ |
| private final AjaxRequestTarget target; |
| |
| /** |
| * Constructor. |
| * |
| * @param target request target. |
| */ |
| public ModalEvent(final AjaxRequestTarget target) { |
| this.target = target; |
| } |
| |
| /** |
| * Target getter. |
| * |
| * @return request target. |
| */ |
| public AjaxRequestTarget getTarget() { |
| return target; |
| } |
| } |
| |
| public static class ChangeFooterVisibilityEvent extends ModalEvent { |
| |
| private static final long serialVersionUID = -6157576856659866343L; |
| |
| public ChangeFooterVisibilityEvent(final AjaxRequestTarget target) { |
| super(target); |
| } |
| } |
| |
| @Override |
| public void onEvent(final IEvent<?> event) { |
| if (event.getPayload() instanceof ChangeFooterVisibilityEvent) { |
| if (BaseModal.this.footer != null) { |
| final AjaxRequestTarget target = ChangeFooterVisibilityEvent.class.cast(event.getPayload()).getTarget(); |
| target.add(BaseModal.this.footer.setEnabled(!BaseModal.this.footer.isEnabled())); |
| } |
| } |
| } |
| |
| //-------------------------------------------------------- |
| // Reqired for SYNCOPE-846 |
| //-------------------------------------------------------- |
| /** |
| * Sets whether the close handler is used or not. Default is false. |
| * |
| * @param useCloseHandler True if close handler should be used |
| * @return This |
| */ |
| public final Modal<T> useCloseHandler(final boolean useCloseHandler) { |
| if (useCloseHandler) { |
| if (closeBehavior == null) { |
| closeBehavior = new IndicatorModalCloseBehavior() { |
| |
| private static final long serialVersionUID = -4955472558917915340L; |
| |
| @Override |
| protected void onEvent(final AjaxRequestTarget target) { |
| if (isVisible()) { |
| onClose(target); |
| appendCloseDialogJavaScript(target); |
| } |
| } |
| }; |
| add(closeBehavior); |
| } |
| } else if (closeBehavior != null) { |
| remove(closeBehavior); |
| closeBehavior = null; |
| } |
| return this; |
| } |
| |
| @Override |
| public void renderHead(final IHeaderResponse response) { |
| super.renderHead(response); |
| response.render(OnDomReadyHeaderItem.forScript(createInitializerScript(getMarkupId(true)))); |
| } |
| |
| /** |
| * creates the initializer script of the modal dialog. |
| * |
| * @param markupId The component's markup id |
| * @return initializer script |
| */ |
| private String createInitializerScript(final String markupId) { |
| return addCloseHandlerScript(markupId, createBasicInitializerScript(markupId)); |
| } |
| |
| /** |
| * adds close handler to initializer script, if use of close handler has been defined. |
| * |
| * @param markupId markup id |
| * @param script base script to prepend |
| * @return close handler script |
| */ |
| private String addCloseHandlerScript(final String markupId, final String script) { |
| if (closeBehavior != null) { |
| return script + ";$('#" + markupId + "').on('hidden', function () { " |
| + " Wicket.Ajax.ajax({'u':'" + closeBehavior.getCallbackUrl() + "','c':'" + markupId + "'});" |
| + "})"; |
| } |
| |
| return script; |
| } |
| //-------------------------------------------------------- |
| |
| /** |
| * Callback called after the window has been closed. If no callback instance is specified using |
| * {@link BaseModal#setWindowClosedCallback(BaseModal.WindowClosedCallback)}, no ajax |
| * request will be fired. |
| */ |
| @FunctionalInterface |
| public interface WindowClosedCallback extends IClusterable { |
| |
| /** |
| * Called after the window has been closed. |
| * |
| * @param target {@link org.apache.wicket.ajax.AjaxRequestTarget} instance bound with the ajax request. |
| */ |
| void onClose(AjaxRequestTarget target); |
| } |
| } |