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.markup.html.panel;
018
019import java.io.Serializable;
020import java.util.Collections;
021import java.util.Comparator;
022import java.util.List;
023
024import org.apache.wicket.AttributeModifier;
025import org.apache.wicket.Component;
026import org.apache.wicket.feedback.FeedbackMessage;
027import org.apache.wicket.feedback.FeedbackMessagesModel;
028import org.apache.wicket.feedback.IFeedback;
029import org.apache.wicket.feedback.IFeedbackMessageFilter;
030import org.apache.wicket.markup.html.WebMarkupContainer;
031import org.apache.wicket.markup.html.basic.Label;
032import org.apache.wicket.markup.html.list.ListItem;
033import org.apache.wicket.markup.html.list.ListView;
034import org.apache.wicket.model.IModel;
035
036
037/**
038 * A panel that displays {@link org.apache.wicket.feedback.FeedbackMessage}s in a list view. The
039 * maximum number of messages to show can be set with setMaxMessages().
040 * 
041 * @see org.apache.wicket.feedback.FeedbackMessage
042 * @see org.apache.wicket.feedback.FeedbackMessages
043 * @author Jonathan Locke
044 * @author Eelco Hillenius
045 */
046public class FeedbackPanel extends Panel implements IFeedback
047{
048        /**
049         * List for messages.
050         */
051        private final class MessageListView extends ListView<FeedbackMessage>
052        {
053                private static final long serialVersionUID = 1L;
054
055                /**
056                 * @see org.apache.wicket.Component#Component(String)
057                 */
058                public MessageListView(final String id)
059                {
060                        super(id);
061                        setDefaultModel(newFeedbackMessagesModel());
062                }
063
064                @Override
065                protected IModel<FeedbackMessage> getListItemModel(
066                        final IModel<? extends List<FeedbackMessage>> listViewModel, final int index)
067                {
068                        return new IModel<FeedbackMessage>()
069                        {
070                                private static final long serialVersionUID = 1L;
071
072                                /**
073                                 * WICKET-4258 Feedback messages might be cleared already.
074                                 * 
075                                 * @see org.apache.wicket.settings.ApplicationSettings#setFeedbackMessageCleanupFilter(org.apache.wicket.feedback.IFeedbackMessageFilter)
076                                 */
077                                @Override
078                                public FeedbackMessage getObject()
079                                {
080                                        if (index >= listViewModel.getObject().size())
081                                        {
082                                                return null;
083                                        }
084                                        else
085                                        {
086                                                return listViewModel.getObject().get(index);
087                                        }
088                                }
089                        };
090                }
091
092                @Override
093                protected void populateItem(final ListItem<FeedbackMessage> listItem)
094                {
095                        final FeedbackMessage message = listItem.getModelObject();
096                        message.markRendered();
097                        final Component label = newMessageDisplayComponent("message", message);
098                        final AttributeModifier levelModifier = AttributeModifier.append("class",
099                                getCSSClass(message));
100                        listItem.add(levelModifier);
101                        listItem.add(label);
102                }
103
104                /**
105                 * WICKET-4831 - Overridable to allow customization
106                 * 
107                 * @param index
108                 *            The index of the item
109                 * @param itemModel
110                 *            object in the list that the item represents
111                 * @return
112                 */
113                @Override
114                protected ListItem<FeedbackMessage> newItem(int index, IModel<FeedbackMessage> itemModel)
115                {
116                        return FeedbackPanel.this.newMessageItem(index, itemModel);
117                }
118    }
119
120        private static final long serialVersionUID = 1L;
121
122        /** Message view */
123        private final MessageListView messageListView;
124
125        /**
126         * @see org.apache.wicket.Component#Component(String)
127         */
128        public FeedbackPanel(final String id)
129        {
130                this(id, null);
131        }
132
133        /**
134         * @see org.apache.wicket.Component#Component(String)
135         * 
136         * @param id
137         * @param filter
138         */
139        public FeedbackPanel(final String id, IFeedbackMessageFilter filter)
140        {
141                super(id);
142                WebMarkupContainer messagesContainer = new WebMarkupContainer("feedbackul")
143                {
144                        private static final long serialVersionUID = 1L;
145
146                        @Override
147                        protected void onConfigure()
148                        {
149                                super.onConfigure();
150                                setVisible(anyMessage());
151                        }
152                };
153                add(messagesContainer);
154                messageListView = new MessageListView("messages");
155                messageListView.setVersioned(false);
156                messagesContainer.add(messageListView);
157
158                if (filter != null)
159                {
160                        setFilter(filter);
161                }
162        }
163
164        /**
165         * Search messages that this panel will render, and see if there is any message of level ERROR
166         * or up. This is a convenience method; same as calling 'anyMessage(FeedbackMessage.ERROR)'.
167         * 
168         * @return whether there is any message for this panel of level ERROR or up
169         */
170        public final boolean anyErrorMessage()
171        {
172                return anyMessage(FeedbackMessage.ERROR);
173        }
174
175        /**
176         * Search messages that this panel will render, and see if there is any message.
177         * 
178         * @return whether there is any message for this panel
179         */
180        public final boolean anyMessage()
181        {
182                return anyMessage(FeedbackMessage.UNDEFINED);
183        }
184
185        /**
186         * Search messages that this panel will render, and see if there is any message of the given
187         * level.
188         * 
189         * @param level
190         *            the level, see FeedbackMessage
191         * @return whether there is any message for this panel of the given level
192         */
193        public final boolean anyMessage(int level)
194        {
195                List<FeedbackMessage> msgs = getCurrentMessages();
196
197                for (FeedbackMessage msg : msgs)
198                {
199                        if (msg.isLevel(level))
200                        {
201                                return true;
202                        }
203                }
204
205                return false;
206        }
207
208        /**
209         * @return Model for feedback messages on which you can install filters and other properties
210         */
211        public final FeedbackMessagesModel getFeedbackMessagesModel()
212        {
213                return (FeedbackMessagesModel)messageListView.getDefaultModel();
214        }
215
216        /**
217         * @return The current message filter
218         */
219        public final IFeedbackMessageFilter getFilter()
220        {
221                return getFeedbackMessagesModel().getFilter();
222        }
223
224        /**
225         * @return The current sorting comparator
226         */
227        public final Comparator<FeedbackMessage> getSortingComparator()
228        {
229                return getFeedbackMessagesModel().getSortingComparator();
230        }
231
232        /**
233         * @see org.apache.wicket.Component#isVersioned()
234         */
235        @Override
236        public boolean isVersioned()
237        {
238                return false;
239        }
240
241        /**
242         * Sets a filter to use on the feedback messages model
243         * 
244         * @param filter
245         *            The message filter to install on the feedback messages model
246         * 
247         * @return FeedbackPanel this.
248         */
249        public final FeedbackPanel setFilter(IFeedbackMessageFilter filter)
250        {
251                getFeedbackMessagesModel().setFilter(filter);
252                return this;
253        }
254
255        /**
256         * @param maxMessages
257         *            The maximum number of feedback messages that this feedback panel should show at
258         *            one time
259         * 
260         * @return FeedbackPanel this.
261         */
262        public final FeedbackPanel setMaxMessages(int maxMessages)
263        {
264                messageListView.setViewSize(maxMessages);
265                return this;
266        }
267
268        /**
269         * Sets the comparator used for sorting the messages.
270         * 
271         * @param sortingComparator
272         *            comparator used for sorting the messages.
273         * 
274         * @return FeedbackPanel this.
275         */
276        public final FeedbackPanel setSortingComparator(Comparator<FeedbackMessage> sortingComparator)
277        {
278                getFeedbackMessagesModel().setSortingComparator(sortingComparator);
279                return this;
280        }
281
282        /**
283         * Gets the css class for the given message.
284         * 
285         * @param message
286         *            the message
287         * @return the css class; by default, this returns feedbackPanel + the message level, eg
288         *         'feedbackPanelERROR', but you can override this method to provide your own
289         */
290        protected String getCSSClass(final FeedbackMessage message)
291        {
292                String cssClass;
293                switch (message.getLevel())
294                {
295                        case FeedbackMessage.UNDEFINED:
296                                cssClass = getString(FeedbackMessage.UNDEFINED_CSS_CLASS_KEY);
297                                break;
298                        case FeedbackMessage.DEBUG:
299                                cssClass = getString(FeedbackMessage.DEBUG_CSS_CLASS_KEY);
300                                break;
301                        case FeedbackMessage.INFO:
302                                cssClass = getString(FeedbackMessage.INFO_CSS_CLASS_KEY);
303                                break;
304                        case FeedbackMessage.SUCCESS:
305                                cssClass = getString(FeedbackMessage.SUCCESS_CSS_CLASS_KEY);
306                                break;
307                        case FeedbackMessage.WARNING:
308                                cssClass = getString(FeedbackMessage.WARNING_CSS_CLASS_KEY);
309                                break;
310                        case FeedbackMessage.ERROR:
311                                cssClass = getString(FeedbackMessage.ERROR_CSS_CLASS_KEY);
312                                break;
313                        case FeedbackMessage.FATAL:
314                                cssClass = getString(FeedbackMessage.FATAL_CSS_CLASS_KEY);
315                                break;
316                        default:
317                                cssClass = "feedbackPanel" + message.getLevelAsString();
318                }
319                return cssClass;
320        }
321
322        /**
323         * Gets the currently collected messages for this panel.
324         * 
325         * @return the currently collected messages for this panel, possibly empty
326         */
327        protected final List<FeedbackMessage> getCurrentMessages()
328        {
329                final List<? extends FeedbackMessage> messages = messageListView.getModelObject();
330                return Collections.unmodifiableList(messages);
331        }
332
333        /**
334         * Gets a new instance of FeedbackMessagesModel to use.
335         * 
336         * @return Instance of FeedbackMessagesModel to use
337         */
338        protected FeedbackMessagesModel newFeedbackMessagesModel()
339        {
340                return new FeedbackMessagesModel(this);
341        }
342
343        /**
344         * Generates a component that is used to display the message inside the feedback panel. This
345         * component must handle being attached to <code>span</code> tags.
346         * 
347         * By default a {@link Label} is used.
348         * 
349         * Note that the created component is expected to respect feedback panel's
350         * {@link #getEscapeModelStrings()} value
351         * 
352         * @param id
353         *            parent id
354         * @param message
355         *            feedback message
356         * @return component used to display the message
357         */
358        protected Component newMessageDisplayComponent(String id, FeedbackMessage message)
359        {
360                Serializable rawMessage = message.getMessage();
361                Label label = new Label(id, rawMessage);
362                label.setEscapeModelStrings(FeedbackPanel.this.getEscapeModelStrings());
363                return label;
364        }
365
366        /**
367         * Allows to define the listItem to use in the feedback's message list.
368         * 
369         * @param index
370         *            The index of the item
371         * @param itemModel
372         *            The model object of the item
373         * @return Container that holds components of the feedback MessageListView.
374         */
375        protected ListItem<FeedbackMessage> newMessageItem(int index, IModel<FeedbackMessage> itemModel) {
376        return new ListItem<>(index, itemModel);
377    }
378}