001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.ajax.markup.html.form;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.ajax.AjaxRequestTarget;
021import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
022import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
023import org.apache.wicket.markup.ComponentTag;
024import org.apache.wicket.markup.html.form.AbstractSubmitLink;
025import org.apache.wicket.markup.html.form.Form;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028
029/**
030 * A link that submits a form via ajax. Since this link takes the form as a constructor argument it
031 * does not need to be inside form's component hierarchy.
032 * 
033 * @since 1.2
034 * 
035 * @author Igor Vaynberg (ivaynberg)
036 * 
037 */
038public abstract class AjaxSubmitLink extends AbstractSubmitLink
039{
040        private static final long serialVersionUID = 1L;
041
042        private static final Logger logger = LoggerFactory.getLogger(AjaxSubmitLink.class);
043
044        private final Form<?> form;
045
046        /**
047         * Construct.
048         * 
049         * @param id
050         */
051        public AjaxSubmitLink(String id)
052        {
053                this(id, null);
054        }
055
056        /**
057         * Construct.
058         * 
059         * @param id
060         * @param form
061         */
062        public AjaxSubmitLink(String id, final Form<?> form)
063        {
064                super(id, form);
065
066                this.form = form;
067        }
068
069        @Override
070        protected void onInitialize()
071        {
072                super.onInitialize();
073
074                add(newAjaxFormSubmitBehavior("click"));
075        }
076
077        protected AjaxFormSubmitBehavior newAjaxFormSubmitBehavior(String event)
078        {
079                return new AjaxFormSubmitBehavior(form, event)
080                {
081                        private static final long serialVersionUID = 1L;
082
083                        @Override
084                        protected void onError(AjaxRequestTarget target)
085                        {
086                                AjaxSubmitLink.this.onError(target);
087                        }
088
089                        @Override
090                        protected Form<?> findForm()
091                        {
092                                return AjaxSubmitLink.this.getForm();
093                        }
094
095                        @Override
096                        public boolean getDefaultProcessing()
097                        {
098                                return AjaxSubmitLink.this.getDefaultFormProcessing();
099                        }
100
101                        @Override
102                        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
103                        {
104                                attributes.setPreventDefault(true);
105                                super.updateAjaxAttributes(attributes);
106                                AjaxSubmitLink.this.updateAjaxAttributes(attributes);
107                        }
108
109                        @Override
110                        protected void onSubmit(AjaxRequestTarget target)
111                        {
112                                AjaxSubmitLink.this.onSubmit(target);
113                        }
114
115                        @Override
116                        protected void onAfterSubmit(AjaxRequestTarget target)
117                        {
118                                AjaxSubmitLink.this.onAfterSubmit(target);
119                        }
120                        
121                        @Override
122                        public boolean getStatelessHint(Component component)
123                        {
124                                return AjaxSubmitLink.this.getStatelessHint();
125                        }
126                };
127        }
128
129        /**
130         * Override this method to provide special submit handling in a multi-button form. This method
131         * will be called <em>before</em> the form's onSubmit method.
132         * 
133         * @param target the {@link AjaxRequestTarget}
134         */
135        protected void onSubmit(AjaxRequestTarget target)
136        {
137        }
138
139        /**
140         * Override this method to provide special submit handling in a multi-button form. This method
141         * will be called <em>after</em> the form's onSubmit method.
142         * 
143         * @param target the {@link AjaxRequestTarget}
144         */
145        protected void onAfterSubmit(AjaxRequestTarget target)
146        {
147        }
148
149        protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
150        {
151        }
152
153        @Override
154        protected void onComponentTag(ComponentTag tag)
155        {
156                super.onComponentTag(tag);
157
158                if (isEnabledInHierarchy())
159                {
160                        String tagName = tag.getName();
161                        
162                        if (tagName.equalsIgnoreCase("a") || tagName.equalsIgnoreCase("link")
163                                || tagName.equalsIgnoreCase("area"))
164                        {
165                                // disable any href attr in markup
166                                tag.put("href", "#");
167                        }
168                        else if (tagName.equalsIgnoreCase("button"))
169                        {
170                                // WICKET-5597 prevent default submit
171                                tag.put("type", "button");
172                        }
173                        else if (tagName.equalsIgnoreCase("input") &&
174                                "submit".equalsIgnoreCase(tag.getAttribute("type")))
175                        {
176                                // WICKET-5879 prevent default submit
177                                tag.getAttributes().put("type", "button");
178                        }
179                }
180                else
181                {
182                        disableLink(tag);
183                }
184        }
185
186        /**
187         * Final implementation of the Button's onError. AjaxSubmitLinks have their own onError which is
188         * called.
189         * 
190         * @see org.apache.wicket.markup.html.form.Button#onError()
191         */
192        @Override
193        public final void onError()
194        {
195                logger.warn("unexpected invocation of #onError() on {}", this);
196        }
197
198
199        /**
200         * Listener method invoked on form submit with errors. This method is called <em>before</em>
201         * {@link Form#onError()}.
202         * 
203         * @param target
204         */
205        protected void onError(AjaxRequestTarget target)
206        {
207        }
208
209        /**
210         * Use {@link #onSubmit(AjaxRequestTarget)} instead.
211         */
212        @Override
213        public final void onSubmit()
214        {
215                logger.warn("unexpected invocation of #onSubmit() on {}", this);
216        }
217
218        /**
219         * Use {@link #onAfterSubmit(AjaxRequestTarget)} instead.
220         */
221        @Override
222        public final void onAfterSubmit()
223        {
224                logger.warn("unexpected invocation of #onAfterSubmit() on {}", this);
225        }
226
227        @Override
228        protected boolean getStatelessHint()
229        {
230                return false;
231        }
232}