1 /**
2 * Copyright 2005-2013 The Kuali Foundation
3 *
4 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 org.kuali.rice.krad.uif.element;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20 import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21 import org.kuali.rice.krad.datadictionary.parse.BeanTags;
22 import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
23 import org.kuali.rice.krad.datadictionary.validator.Validator;
24 import org.kuali.rice.krad.uif.component.Component;
25 import org.kuali.rice.krad.uif.util.MessageStructureUtils;
26 import org.kuali.rice.krad.uif.view.View;
27 import org.kuali.rice.krad.util.KRADConstants;
28
29 import java.util.List;
30
31 /**
32 * Encapsulates a text message to be displayed
33 *
34 * <p>
35 * The <code>Message</code> is used to display static text in the user
36 * interface
37 * </p>
38 *
39 * @author Kuali Rice Team (rice.collab@kuali.org)
40 */
41 @BeanTags({@BeanTag(name = "message-bean", parent = "Uif-Message"),
42 @BeanTag(name = "instructionalMessage-bean", parent = "Uif-InstructionalMessage"),
43 @BeanTag(name = "constraintMessage-bean", parent = "Uif-ConstraintMessage"),
44 @BeanTag(name = "requiredMessage-bean", parent = "Uif-RequiredMessage"),
45 @BeanTag(name = "requiredInstructionsMessage-bean", parent = "Uif-RequiredInstructionsMessage"),
46 @BeanTag(name = "stateBased-requiredInstructionsMessage-bean",
47 parent = "Uif-StateBased-RequiredInstructionsMessage"),
48 @BeanTag(name = "dialogPrompt-bean", parent = "Uif-DialogPrompt"),
49 @BeanTag(name = "imageCutineMessage-bean", parent = "Uif-ImageCutineMessage")})
50 public class Message extends ContentElementBase {
51 private static final long serialVersionUID = 4090058533452450395L;
52
53 private String messageText;
54 private boolean generateSpan;
55
56 private List<Component> inlineComponents;
57 private List<Component> messageComponentStructure;
58
59 private boolean parseComponents;
60
61 public Message() {
62 super();
63
64 generateSpan = true;
65 parseComponents = true;
66 }
67
68 /**
69 * Message perfom apply model parses message text for rich text functionality if the messageText contains
70 * [ or ] special characters
71 *
72 * @see Component#performApplyModel(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
73 */
74 @Override
75 public void performApplyModel(View view, Object model, Component parent) {
76 super.performApplyModel(view, model, parent);
77
78 //if messageText contains the special characters [] then parse and fill in the messageComponentStructure
79 //but if messageComponentStructure has already been set it overrides messageText by default
80 if (messageText != null && messageText.contains(KRADConstants.MessageParsing.LEFT_TOKEN) &&
81 messageText.contains(KRADConstants.MessageParsing.RIGHT_TOKEN) &&
82 (messageComponentStructure == null || messageComponentStructure.isEmpty())) {
83
84 messageComponentStructure = MessageStructureUtils.parseMessage(this.getId(), this.getMessageText(),
85 this.getInlineComponents(), view, parseComponents);
86
87 if (messageComponentStructure != null) {
88 for (Component component : messageComponentStructure) {
89 view.getViewHelperService().performComponentInitialization(view, model, component);
90 }
91 }
92 }
93
94 }
95
96 /**
97 * @see Component#performFinalize(org.kuali.rice.krad.uif.view.View, Object, org.kuali.rice.krad.uif.component.Component)
98 */
99 @Override
100 public void performFinalize(View view, Object model, Component parent) {
101 super.performFinalize(view, model, parent);
102 //Message needs to be aware of its own parent because it now contains content that can have validation
103 this.addDataAttribute("parent", parent.getId());
104 }
105
106 /**
107 * @see org.kuali.rice.krad.uif.component.ComponentBase#getComponentsForLifecycle()
108 */
109 @Override
110 public List<Component> getComponentsForLifecycle() {
111 List<Component> components = super.getComponentsForLifecycle();
112
113 if (messageComponentStructure != null) {
114 for (Component component : messageComponentStructure) {
115 components.add(component);
116 }
117 }
118
119 return components;
120 }
121
122 /**
123 * Override to render only if the message text has been given or there is a conditional expression on the
124 * message text
125 *
126 * @see org.kuali.rice.krad.uif.component.ComponentBase#isRender()
127 */
128 @Override
129 public boolean isRender() {
130 boolean render = super.isRender();
131
132 if (render) {
133 render = getPropertyExpressions().containsKey("messageText") || (StringUtils.isNotBlank(messageText)
134 && !StringUtils.equals(messageText, " "));
135 }
136
137 return render;
138 }
139
140 /**
141 * Text that makes up the message that will be displayed.
142 *
143 * <p>If special characters [] are detected the message inserts special content at that location.
144 * The types of features supported are (note that <> are not part of the content below,
145 * they specify placeholders):
146 * <ul>
147 * <li>[id=<component id>] - insert component with id specified at that location in the message</li>
148 * <li>[n] - insert component at index n from the inlineComponent list</li>
149 * <li>[<html tag>][/<html tag>] - insert html content directly into the message content at that
150 * location,
151 * without the need to escape the <> characters in xml</li>
152 * <li>[color=<html color code/name>][/color] - wrap content in color tags to make text that color
153 * in the message</li>
154 * <li>[css=<css classes>][/css] - apply css classes specified to the wrapped content - same as wrapping
155 * the content in span with class property set</li>
156 * </ul>
157 * If the [] characters are needed in message text, they need to be declared with an escape character: \\[ \\]
158 * </p>
159 *
160 * @return String message text
161 */
162 @BeanTagAttribute(name = "messageText")
163 public String getMessageText() {
164 return this.messageText;
165 }
166
167 /**
168 * Setter for the message text
169 *
170 * @param messageText
171 */
172 public void setMessageText(String messageText) {
173 this.messageText = messageText;
174 }
175
176 /**
177 * If true, generate the span around this message (default). When false, skip span generation for this
178 * message - this has the additional effect the css classes/style classes will be lost for this message.
179 *
180 * @return true if generating a wrapping span, false otherwise
181 */
182 @BeanTagAttribute(name = "generateSpan")
183 public boolean isGenerateSpan() {
184 return generateSpan;
185 }
186
187 /**
188 * Sets the generate span flag
189 *
190 * @param generateSpan
191 */
192 public void setGenerateSpan(boolean generateSpan) {
193 this.generateSpan = generateSpan;
194 }
195
196 /**
197 * The message component structure is a list of components which represent the components that make up a message
198 * when using rich message functionality.
199 *
200 * <p>The structure represents the parsed messageText when not set. Normally this structure is setup by the Message
201 * class and <b>SHOULD NOT BE SET</b> in xml, unless full control over the structure is needed. </p>
202 *
203 * @return list of components which represent the message structure
204 */
205 public List<Component> getMessageComponentStructure() {
206 return messageComponentStructure;
207 }
208
209 /**
210 * Set the message component structure. This will override/ignore messageText when set. Normally
211 * this <b>SHOULD NOT BE SET</b> by the xml configuration.
212 *
213 * @param messageComponentStructure list of components which represent the message structure
214 */
215 public void setMessageComponentStructure(List<Component> messageComponentStructure) {
216 this.messageComponentStructure = messageComponentStructure;
217 }
218
219 /**
220 * The inlineComponents are a list of components in order by index.
221 *
222 * <p>inlineComponents is only used when the message is using rich message functionality. A message
223 * with [0] will reference component at index 0 of this list and insert it at that place in the message,
224 * and likewise [1] will reference item 1, etc. If the index referenced is out of bounds (or list doesnt exist),
225 * an error will be thrown during message parse.</p>
226 *
227 * @return the inlineComponents to be filled in at indexes referenced by [n] in the message
228 */
229 @BeanTagAttribute(name = "inlineComponents", type = BeanTagAttribute.AttributeType.LISTBEAN)
230 public List<Component> getInlineComponents() {
231 return inlineComponents;
232 }
233
234 /**
235 * Set the inlineComponents to be filled in at indexes referenced by [n] in the message
236 *
237 * @param inlineComponents the inlineComponents to be filled in at indexes referenced by [n] in the message
238 */
239 public void setInlineComponents(List<Component> inlineComponents) {
240 this.inlineComponents = inlineComponents;
241 }
242
243 /**
244 * @see org.kuali.rice.krad.uif.component.Component#completeValidation
245 */
246 @Override
247 public void completeValidation(ValidationTrace tracer) {
248 tracer.addBean(this);
249
250 // Checks that text is set
251 if (getMessageText() == null) {
252 if (Validator.checkExpressions(this, "messageText")) {
253 String currentValues[] = {"messageText =" + getMessageText()};
254 tracer.createWarning("MessageText should be set", currentValues);
255 }
256 }
257
258 super.completeValidation(tracer.getCopy());
259 }
260
261 /**
262 * Indicates if the inline components must be parsed for rich messages
263 *
264 * @return boolean
265 */
266 @BeanTagAttribute(name = "parseComponents")
267 public boolean isParseComponents() {
268 return parseComponents;
269 }
270
271 /**
272 * Sets the parse components flag to indicate if inline components must be parsed for rich messages
273 *
274 * @param parseComponents
275 */
276 public void setParseComponents(boolean parseComponents) {
277 this.parseComponents = parseComponents;
278 }
279 }