001/**
002 * Copyright 2005-2018 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.element;
017
018import java.util.ArrayList;
019import java.util.Collections;
020import java.util.List;
021
022import org.apache.commons.lang.StringUtils;
023import org.kuali.rice.krad.datadictionary.parse.BeanTag;
024import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
025import org.kuali.rice.krad.datadictionary.parse.BeanTags;
026import org.kuali.rice.krad.datadictionary.validator.ValidationTrace;
027import org.kuali.rice.krad.datadictionary.validator.Validator;
028import org.kuali.rice.krad.uif.UifConstants;
029import org.kuali.rice.krad.uif.component.Component;
030import org.kuali.rice.krad.uif.container.Group;
031import org.kuali.rice.krad.uif.lifecycle.ViewLifecycleRestriction;
032import org.kuali.rice.krad.uif.util.ComponentFactory;
033import org.kuali.rice.krad.uif.util.ComponentUtils;
034import org.kuali.rice.krad.uif.util.LifecycleElement;
035import org.kuali.rice.krad.uif.view.View;
036import org.kuali.rice.krad.uif.widget.Help;
037import org.kuali.rice.krad.uif.util.LifecycleElement;
038import org.kuali.rice.krad.util.KRADConstants;
039
040/**
041 * Content element that renders a header element and optionally a <code>Group</code> to
042 * present along with the header text
043 *
044 * <p>
045 * Generally the group is used to display content to the right of the header,
046 * such as links for the group or other information
047 * </p>
048 *
049 * @author Kuali Rice Team (rice.collab@kuali.org)
050 */
051@BeanTags({@BeanTag(name = "header", parent = "Uif-HeaderBase"),
052        @BeanTag(name = "headerOne", parent = "Uif-HeaderOne"),
053        @BeanTag(name = "headerTwo", parent = "Uif-HeaderTwo"),
054        @BeanTag(name = "headerThree", parent = "Uif-HeaderThree"),
055        @BeanTag(name = "headerFour", parent = "Uif-HeaderFour"),
056        @BeanTag(name = "headerFive", parent = "Uif-HeaderFive"),
057        @BeanTag(name = "headerSix", parent = "Uif-HeaderSix"),
058        @BeanTag(name = "pageHeader", parent = "Uif-PageHeader"),
059        @BeanTag(name = "sectionHeader", parent = "Uif-SectionHeader"),
060        @BeanTag(name = "subSectionHeader", parent = "Uif-SubSectionHeader"),
061        @BeanTag(name = "subCollectionHeader", parent = "Uif-SubCollectionHeader")})
062public class Header extends ContentElementBase {
063    private static final long serialVersionUID = -6950408292923393244L;
064
065    private String headerText;
066    private String headerLevel;
067
068    private String headerTagStyle;
069    private List<String> headerTagCssClasses;
070    private boolean headerTagOnly;
071
072    private Message richHeaderMessage;
073    private List<Component> inlineComponents;
074
075    private Group upperGroup;
076    private Group rightGroup;
077    private Group lowerGroup;
078
079    private boolean renderWrapper;
080
081    public Header() {
082        super();
083
084        headerTagCssClasses = new ArrayList<String>();
085        renderWrapper = true;
086    }
087
088    /**
089     * Sets up rich message content for the label, if any exists
090     *
091     * {@inheritDoc}
092     */
093    @Override
094    public void performApplyModel(Object model, LifecycleElement parent) {
095        super.performApplyModel(model, parent);
096
097        if (richHeaderMessage == null && headerText != null && headerText.contains(
098                KRADConstants.MessageParsing.LEFT_TOKEN) && headerText.contains(
099                KRADConstants.MessageParsing.RIGHT_TOKEN)) {
100            Message message = ComponentFactory.getMessage();
101            message.setMessageText(headerText);
102            message.setInlineComponents(inlineComponents);
103            message.setRenderWrapperTag(false);
104            
105            this.setRichHeaderMessage(message);
106        }
107    }
108
109    /**
110     * The following finalization is performed:
111     *
112     * <ul>
113     * <li>Set render on header group to false if no items are configured</li>
114     * </ul>
115     *
116     * {@inheritDoc}
117     */
118    @Override
119    public void performFinalize(Object model, LifecycleElement parent) {
120        super.performFinalize(model, parent);
121
122        // don't render header groups if no items were configured
123        if ((getUpperGroup() != null) && (getUpperGroup().getItems().isEmpty())) {
124            getUpperGroup().setRender(false);
125        }
126
127        if ((getRightGroup() != null) && (getRightGroup().getItems().isEmpty())) {
128            getRightGroup().setRender(false);
129        }
130
131        if ((getLowerGroup() != null) && (getLowerGroup().getItems().isEmpty())) {
132            getLowerGroup().setRender(false);
133        }
134
135        //add preset styles to header groups
136        if (getUpperGroup() != null) {
137            getUpperGroup().addStyleClass("uif-header-upperGroup");
138        }
139
140        if (getRightGroup() != null) {
141            getRightGroup().addStyleClass("uif-header-rightGroup");
142        }
143
144        if (getLowerGroup() != null) {
145            getLowerGroup().addStyleClass("uif-header-lowerGroup");
146        }
147    }
148
149    /**
150     * {@inheritDoc}
151     */
152    @Override
153    public List<String> getAdditionalTemplates() {
154        List<String> additionalTemplates = super.getAdditionalTemplates();
155
156        Object parent = getContext().get(UifConstants.ContextVariableNames.PARENT);
157        
158        Help help = null;
159        if (parent instanceof Group) {
160            help = ((Group) parent).getHelp();
161        } else if (parent instanceof View) {
162            help = ((View) parent).getHelp();
163        }
164        
165        if (help != null) {
166            String helpTemplate = help.getTemplate();
167            if (additionalTemplates.isEmpty()) {
168                additionalTemplates = new ArrayList<String>();
169            }
170            additionalTemplates.add(helpTemplate);
171        }
172        
173        return additionalTemplates;
174    }
175
176    /**
177     * Text that should be displayed on the header
178     *
179     * @return header text
180     */
181    @BeanTagAttribute
182    public String getHeaderText() {
183        return this.headerText;
184    }
185
186    /**
187     * Setter for the header text
188     *
189     * @param headerText
190     */
191    public void setHeaderText(String headerText) {
192        this.headerText = headerText;
193    }
194
195    /**
196     * HTML header level (h1 ... h6) that should be applied to the header text
197     *
198     * @return header level
199     */
200    @BeanTagAttribute
201    public String getHeaderLevel() {
202        return this.headerLevel;
203    }
204
205    /**
206     * Setter for the header level
207     *
208     * @param headerLevel
209     */
210    public void setHeaderLevel(String headerLevel) {
211        this.headerLevel = headerLevel;
212    }
213
214    /**
215     * Style classes that should be applied to the header text (h tag)
216     *
217     * <p>
218     * Note the style class given here applies to only the header text. The
219     * style class property inherited from the <code>Component</code> interface
220     * can be used to set the class for the whole field div (which could
221     * include a nested <code>Group</code>)
222     * </p>
223     *
224     * @return list of style classes
225     * @see org.kuali.rice.krad.uif.component.Component#getCssClasses()
226     */
227    @BeanTagAttribute
228    public List<String> getHeaderTagCssClasses() {
229        return this.headerTagCssClasses;
230    }
231
232    /**
233     * Setter for the list of classes to apply to the header h tag
234     *
235     * @param headerTagCssClasses
236     */
237    public void setHeaderTagCssClasses(List<String> headerTagCssClasses) {
238        this.headerTagCssClasses = headerTagCssClasses;
239    }
240
241    /**
242     * Builds the HTML class attribute string by combining the headerStyleClasses list
243     * with a space delimiter
244     *
245     * @return class attribute string
246     */
247    public String getHeaderStyleClassesAsString() {
248        if (headerTagCssClasses != null) {
249            return StringUtils.join(headerTagCssClasses, " ");
250        }
251
252        return "";
253    }
254
255    /**
256     * Style that should be applied to the header h tag
257     *
258     * <p>
259     * Note the style given here applies to only the header text. The style
260     * property inherited from the <code>Component</code> interface can be used
261     * to set the style for the whole header div (which could include a nested
262     * <code>Group</code>)
263     * </p>
264     *
265     * @return header style
266     * @see org.kuali.rice.krad.uif.component.Component#getStyle()
267     */
268    @BeanTagAttribute
269    public String getHeaderTagStyle() {
270        return this.headerTagStyle;
271    }
272
273    /**
274     * Setter for the header h tag style
275     *
276     * @param headerTagStyle
277     */
278    public void setHeaderTagStyle(String headerTagStyle) {
279        this.headerTagStyle = headerTagStyle;
280    }
281
282    @BeanTagAttribute
283    public boolean isHeaderTagOnly() {
284        return headerTagOnly;
285    }
286
287    public void setHeaderTagOnly(boolean headerTagOnly) {
288        this.headerTagOnly = headerTagOnly;
289    }
290
291    /**
292     * Nested group instance that can be used to render contents above the header text
293     *
294     * <p>
295     * The header group is useful for adding content such as links or actions that is presented with the header
296     * </p>
297     *
298     * @return Group instance
299     */
300    @BeanTagAttribute
301    public Group getUpperGroup() {
302        return upperGroup;
303    }
304
305    /**
306     * Setter for the header group instance that is rendered above the header text
307     *
308     * @param upperGroup
309     */
310    public void setUpperGroup(Group upperGroup) {
311        this.upperGroup = upperGroup;
312    }
313
314    /**
315     * Nested group instance that can be used to render contents to the right of the header text
316     *
317     * <p>
318     * The header group is useful for adding content such as links or actions that is presented with the header
319     * </p>
320     *
321     * @return Group instance
322     */
323    @BeanTagAttribute
324    public Group getRightGroup() {
325        return rightGroup;
326    }
327
328    /**
329     * Setter for the header group instance that is rendered to the right of the header text
330     *
331     * @param rightGroup
332     */
333    public void setRightGroup(Group rightGroup) {
334        this.rightGroup = rightGroup;
335    }
336
337    /**
338     * Nested group instance that can be used to render contents below the header text
339     *
340     * <p>
341     * The header group is useful for adding content such as links or actions that is presented with the header
342     * </p>
343     *
344     * @return Group instance
345     */
346    @BeanTagAttribute
347    public Group getLowerGroup() {
348        return lowerGroup;
349    }
350
351    /**
352     * Setter for the header group instance that is rendered below the header text
353     *
354     * @param lowerGroup
355     */
356    public void setLowerGroup(Group lowerGroup) {
357        this.lowerGroup = lowerGroup;
358    }
359
360    /**
361     * List of <code>Component</code> instances contained in the lower header group
362     *
363     * <p>
364     * Convenience method for configuration to get the items List from the
365     * lower header group
366     * </p>
367     *
368     * @return List<? extends Component> items
369     */
370    @ViewLifecycleRestriction
371    @BeanTagAttribute
372    public List<? extends Component> getItems() {
373        if (lowerGroup != null) {
374            return lowerGroup.getItems();
375        }
376
377        return null;
378    }
379
380    /**
381     * Setter for the lower group's items
382     *
383     * <p>
384     * Convenience method for configuration to set the items List for the
385     * lower header group
386     * </p>
387     *
388     * @param items
389     */
390    public void setItems(List<? extends Component> items) {
391        if (lowerGroup != null) {
392            lowerGroup.setItems(items);
393        }
394    }
395
396    /**
397     * Gets the Message that represents the rich message content of the header if headerText is using rich message
398     * tags.
399     * <b>DO NOT set this
400     * property directly unless you need full control over the message structure.</b>
401     *
402     * @return rich message structure, null if no rich message structure
403     */
404    @BeanTagAttribute
405    public Message getRichHeaderMessage() {
406        return richHeaderMessage;
407    }
408
409    /**
410     * Sets the Message that represents the rich message content of the header if headerText is using rich message
411     * tags.
412     * <b>DO
413     * NOT set this
414     * property directly unless you need full control over the message structure.</b>
415     *
416     * @param richHeaderMessage
417     */
418    public void setRichHeaderMessage(Message richHeaderMessage) {
419        this.richHeaderMessage = richHeaderMessage;
420    }
421
422    /**
423     * Gets the inlineComponents used by index in a Header that has rich message component index tags in its headerText
424     *
425     * @return the Label's inlineComponents
426     */
427    @BeanTagAttribute
428    public List<Component> getInlineComponents() {
429        return inlineComponents;
430    }
431
432    /**
433     * Sets the inlineComponents used by index in a Header that has rich message component index tags in its headerText
434     *
435     * @param inlineComponents
436     */
437    public void setInlineComponents(List<Component> inlineComponents) {
438        this.inlineComponents = inlineComponents;
439    }
440
441    /**
442     * {@inheritDoc}
443     */
444    @Override
445    public void completeValidation(ValidationTrace tracer) {
446        tracer.addBean(this);
447
448        // Checks that a correct header level is set
449        String headerLevel = getHeaderLevel().toUpperCase();
450        boolean correctHeaderLevel = false;
451        if (headerLevel.compareTo("H1") == 0) {
452            correctHeaderLevel = true;
453        } else if (headerLevel.compareTo("H2") == 0) {
454            correctHeaderLevel = true;
455        } else if (headerLevel.compareTo("H3") == 0) {
456            correctHeaderLevel = true;
457        } else if (headerLevel.compareTo("H4") == 0) {
458            correctHeaderLevel = true;
459        } else if (headerLevel.compareTo("H5") == 0) {
460            correctHeaderLevel = true;
461        } else if (headerLevel.compareTo("H6") == 0) {
462            correctHeaderLevel = true;
463        } else if (headerLevel.compareTo("LABEL") == 0) {
464            correctHeaderLevel = true;
465        }
466        if (!correctHeaderLevel) {
467            String currentValues[] = {"headerLevel =" + getHeaderLevel()};
468            tracer.createError("HeaderLevel must be of values h1, h2, h3, h4, h5, h6, or label", currentValues);
469        }
470
471        // Checks that header text is set
472        if (getHeaderText() == null) {
473            if (!Validator.checkExpressions(this, "headerText")) {
474                String currentValues[] = {"headertText =" + getHeaderText()};
475                tracer.createWarning("HeaderText should be set", currentValues);
476            }
477        }
478
479        super.completeValidation(tracer.getCopy());
480    }
481}