1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.kuali.rice.krad.uif.freemarker;
17
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 import org.apache.commons.lang.StringEscapeUtils;
26 import org.kuali.rice.krad.uif.UifConstants;
27 import org.kuali.rice.krad.uif.component.Component;
28 import org.kuali.rice.krad.uif.container.CollectionGroup;
29 import org.kuali.rice.krad.uif.container.Group;
30 import org.kuali.rice.krad.uif.layout.LayoutManager;
31 import org.kuali.rice.krad.uif.layout.StackedLayoutManager;
32 import org.kuali.rice.krad.uif.util.ScriptUtils;
33 import org.kuali.rice.krad.uif.widget.Disclosure;
34 import org.kuali.rice.krad.uif.element.Pager;
35 import org.kuali.rice.krad.uif.widget.Tooltip;
36 import org.springframework.util.StringUtils;
37
38 import freemarker.core.Environment;
39 import freemarker.core.InlineTemplateUtils;
40 import freemarker.core.Macro;
41 import freemarker.ext.beans.BeansWrapper;
42 import freemarker.template.ObjectWrapper;
43 import freemarker.template.TemplateException;
44 import freemarker.template.TemplateModel;
45 import freemarker.template.TemplateModelException;
46
47
48
49
50
51
52 public class FreeMarkerInlineRenderUtils {
53
54
55
56
57
58
59
60
61
62
63 @SuppressWarnings("unchecked")
64 public static <T> T resolve(Environment env, String name) {
65 TemplateModel tm = resolveModel(env, name);
66 try {
67 return (T) getBeansWrapper(env).unwrap(tm);
68 } catch (TemplateModelException e) {
69 throw new IllegalArgumentException("Failed to unwrap " + name + ", template model " + tm, e);
70 }
71 }
72
73
74
75
76
77
78
79
80
81
82
83
84
85 public static <T> T resolve(Environment env, String name, Class<T> type) {
86 Object rv = resolve(env, name);
87
88 if ((rv instanceof Collection) && !Collection.class.isAssignableFrom(type)) {
89 Collection<?> rc = (Collection<?>) rv;
90 if (rc.isEmpty()) {
91 return null;
92 } else {
93 rv = rc.iterator().next();
94 }
95 }
96
97 if ("".equals(rv) && !String.class.equals(type)) {
98 return null;
99 } else {
100 return type.cast(rv);
101 }
102 }
103
104
105
106
107
108
109
110
111 public static BeansWrapper getBeansWrapper(Environment env) {
112 ObjectWrapper wrapper = env.getObjectWrapper();
113
114 if (!(wrapper instanceof BeansWrapper)) {
115 throw new UnsupportedOperationException("FreeMarker environment uses unsupported ObjectWrapper " + wrapper);
116 }
117
118 return (BeansWrapper) wrapper;
119 }
120
121
122
123
124
125
126
127
128
129
130 public static TemplateModel resolveModel(Environment env, String name) {
131 try {
132 return env.getVariable(name);
133 } catch (TemplateModelException e) {
134 throw new IllegalArgumentException("Failed to resolve " + name + " in current freemarker environment", e);
135 }
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 public static void renderTemplate(Environment env, Component component, String body, boolean componentUpdate,
156 boolean includeSrc, Map<String, TemplateModel> tmplParms) throws TemplateException, IOException {
157 String dataJsScripts = "";
158 String templateJsScripts = "";
159
160 if (component == null) {
161 return;
162 }
163
164 String s;
165 Writer out = env.getOut();
166
167 if ((component.isRender() && (!component.isRetrieveViaAjax() || componentUpdate)) || (component
168 .getProgressiveRender() != null && !component.getProgressiveRender().equals("") && !component
169 .isProgressiveRenderViaAJAX() && !component.isProgressiveRenderAndRefresh())) {
170
171 if (StringUtils.hasText(s = component.getPreRenderContent())) {
172 out.write(s);
173 }
174
175 if (component.isSelfRendered()) {
176 out.write(component.getRenderedHtmlOutput());
177 } else {
178 if (includeSrc) {
179 env.include(component.getTemplate(), env.getTemplate().getEncoding(), true);
180 }
181
182 Macro fmMacro = component.getTemplateName() == null ? null : (Macro) env.getMainNamespace().get(
183 component.getTemplateName());
184
185 if (fmMacro == null) {
186
187 env.include(component.getTemplate(), env.getTemplate().getEncoding(), true);
188 fmMacro = component.getTemplateName() == null ? null : (Macro) env.getCurrentNamespace().get(
189 component.getTemplateName());
190
191
192 if (fmMacro == null) {
193 throw new TemplateException("No macro found using " + component.getTemplateName(), env);
194 }
195 }
196
197 Map<String, Object> args = new java.util.HashMap<String, Object>();
198 args.put(component.getComponentTypeName(), component);
199
200 if (tmplParms != null) {
201 args.putAll(tmplParms);
202 }
203
204 if (StringUtils.hasText(body)) {
205 args.put("body", body);
206 }
207
208 InlineTemplateUtils.invokeMacro(env, fmMacro, args, null);
209 }
210
211 if (StringUtils.hasText(s = component.getEventHandlerScript())) {
212 templateJsScripts += s;
213 }
214
215 if (StringUtils.hasText(s = component.getScriptDataAttributesJs())) {
216 dataJsScripts += s;
217 }
218
219 if (StringUtils.hasText(s = component.getPostRenderContent())) {
220 out.append(s);
221 }
222
223 }
224
225 if (componentUpdate || UifConstants.ViewStatus.RENDERED.equals(component.getViewStatus())) {
226 renderScript(dataJsScripts, component, UifConstants.RoleTypes.DATA_SCRIPT, out);
227 renderScript(templateJsScripts, component, null, out);
228 return;
229 }
230
231 String methodToCallOnRefresh = component.getMethodToCallOnRefresh();
232 if (!StringUtils.hasText(methodToCallOnRefresh)) {
233 methodToCallOnRefresh = "";
234 }
235
236 if ((!component.isRender() && (component.isProgressiveRenderViaAJAX() || component
237 .isProgressiveRenderAndRefresh() || component.isDisclosedByAction() || component.isRefreshedByAction()))
238 || component.isRetrieveViaAjax()) {
239 out.write("<span id=\"");
240 out.write(component.getId());
241 out.write("\" data-role=\"placeholder\" class=\"uif-placeholder "
242 + component.getStyleClassesAsString()
243 + "\"></span>");
244 }
245
246 if (StringUtils.hasText(component.getProgressiveRender())) {
247 for (String cName : component.getProgressiveDisclosureControlNames()) {
248 templateJsScripts += "var condition = function(){return ("
249 + component.getProgressiveDisclosureConditionJs()
250 + ");};setupProgressiveCheck('"
251 + StringEscapeUtils.escapeJavaScript(cName)
252 + "', '"
253 + component.getId()
254 + "', condition,"
255 + component.isProgressiveRenderAndRefresh()
256 + ", '"
257 + methodToCallOnRefresh
258 + "', "
259 + ScriptUtils.translateValue(component.getFieldsToSendOnRefresh())
260 + ");";
261 }
262
263 templateJsScripts += "hiddenInputValidationToggle('" + component.getId() + "');";
264 }
265
266 if (StringUtils.hasText(component.getConditionalRefresh())) {
267 for (String cName : component.getConditionalRefreshControlNames()) {
268 templateJsScripts += "var condition = function(){return ("
269 + component.getConditionalRefreshConditionJs()
270 + ");};setupRefreshCheck('"
271 + StringEscapeUtils.escapeJavaScript(cName)
272 + "', '"
273 + component.getId()
274 + "', condition,'"
275 + methodToCallOnRefresh
276 + "', "
277 + ScriptUtils.translateValue(component.getFieldsToSendOnRefresh())
278 + ");";
279 }
280 }
281
282 List<String> refreshWhenChanged = component.getRefreshWhenChangedPropertyNames();
283 if (refreshWhenChanged != null) {
284 for (String cName : refreshWhenChanged) {
285 templateJsScripts += "setupOnChangeRefresh('"
286 + StringEscapeUtils.escapeJavaScript(cName)
287 + "', '"
288 + component.getId()
289 + "','"
290 + methodToCallOnRefresh
291 + "', "
292 + ScriptUtils.translateValue(component.getFieldsToSendOnRefresh())
293 + ");";
294 }
295 }
296
297 renderScript(dataJsScripts, component, UifConstants.RoleTypes.DATA_SCRIPT, out);
298 renderScript(templateJsScripts, component, null, out);
299
300 renderTooltip(component, out);
301 }
302
303
304
305
306
307
308
309
310
311
312
313
314 public static void renderTooltip(Component component, Writer out) throws IOException {
315 Tooltip tt = component.getToolTip();
316 String script = "";
317 if (tt != null && StringUtils.hasText(tt.getTooltipContent())) {
318 String templateOptionsJSString = tt.getTemplateOptionsJSString();
319 script += "createTooltip('"
320 + component.getId()
321 + "', '"
322 + tt.getTooltipContent()
323 + "', "
324 + (templateOptionsJSString == null ? "''" : templateOptionsJSString)
325 + ", "
326 + tt.isOnMouseHover()
327 + ", "
328 + tt.isOnFocus()
329 + ");";
330
331 renderScript(script, component, null, out);
332 }
333 }
334
335
336
337
338
339
340
341
342
343
344
345
346
347 public static void renderScript(String script, Component component, String role, Writer out) throws IOException {
348 if (script == null || "".equals(script.trim())) {
349 return;
350 }
351 out.write("<input name=\"script\" type=\"hidden\" data-role=\"");
352 out.write(role == null ? "script" : role);
353 out.write("\" ");
354
355 if (component != null && component.getId() != null) {
356 out.write("data-for=\"");
357 out.write(component.getId());
358 out.write("\" ");
359 }
360
361 out.write("value=\"");
362 out.write(StringEscapeUtils.escapeHtml(script));
363 out.write("\" />");
364 }
365
366
367
368
369
370
371
372
373
374
375
376
377
378 public static void renderAttrBuild(Component component, Writer out) throws IOException {
379 String s = component.getStyleClassesAsString();
380 if (StringUtils.hasText(s)) {
381 out.write(" class=\"");
382 out.write(s);
383 out.write("\"");
384 }
385
386 s = component.getStyle();
387 if (StringUtils.hasText(s)) {
388 out.write(" style=\"");
389 out.write(s);
390 out.write("\"");
391 }
392
393 s = component.getTitle();
394 if (StringUtils.hasText(s)) {
395 out.write(" title=\"");
396 out.write(s);
397 out.write("\"");
398 }
399
400 s = component.getRole();
401 if (StringUtils.hasText(s)) {
402 out.write(" role=\"");
403 out.write(s);
404 out.write("\"");
405 }
406
407 s = component.getAriaAttributesAsString();
408 if (StringUtils.hasText(s)) {
409 out.write(s);
410 }
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432 public static void renderOpenDiv(Component component, Writer out) throws IOException {
433 out.write("<div id=\"");
434 out.write(component.getId());
435 out.write("\"");
436 renderAttrBuild(component, out);
437 out.write(component.getSimpleDataAttributes());
438 out.write(">");
439 }
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459 public static void renderCloseDiv(Writer out) throws IOException {
460 out.write("</div>");
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483 public static void renderOpenGroupWrap(Environment env, Group group) throws IOException, TemplateException {
484 Writer out = env.getOut();
485 renderTemplate(env, group.getHeader(), null, false, false, null);
486
487 if (group.isRenderLoading()) {
488 out.write("<div id=\"");
489 out.write(group.getId());
490 out.write("_disclosureContent\" data-role=\"placeholder\"> Loading... </div>");
491 } else {
492 Disclosure disclosure = group.getDisclosure();
493 if (disclosure != null && disclosure.isRender()) {
494 out.write("<div id=\"");
495 out.write(group.getId() + UifConstants.IdSuffixes.DISCLOSURE_CONTENT);
496 out.write("\" data-role=\"disclosureContent\" data-open=\"");
497 out.write(Boolean.toString(disclosure.isDefaultOpen()));
498 out.write("\" class=\"uif-disclosureContent\">");
499 }
500 renderTemplate(env, group.getInstructionalMessage(), null, false, false, null);
501 }
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524 public static void renderCloseGroupWrap(Environment env, Group group) throws IOException, TemplateException {
525 Writer out = env.getOut();
526
527 boolean renderLoading = group.isRenderLoading();
528 if (!renderLoading) {
529 renderTemplate(env, group.getFooter(), null, false, false, null);
530 }
531
532 Disclosure disclosure = group.getDisclosure();
533 if (disclosure != null && disclosure.isRender()) {
534 if (!renderLoading) {
535 out.write("</div>");
536 }
537 Map<String, TemplateModel> tmplParms = new HashMap<String, TemplateModel>();
538 tmplParms.put("parent", env.getObjectWrapper().wrap(group));
539 renderTemplate(env, disclosure, null, false, false, tmplParms);
540 }
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555 public static void renderCollectionGroup(Environment env,
556 CollectionGroup group) throws IOException, TemplateException {
557 renderOpenGroupWrap(env, group);
558
559 Map<String, TemplateModel> tmplParms = new HashMap<String, TemplateModel>();
560 tmplParms.put("componentId", env.getObjectWrapper().wrap(group.getId()));
561 renderTemplate(env, group.getCollectionLookup(), null, false, false, tmplParms);
562
563 if ("TOP".equals(group.getAddLinePlacement())) {
564 if (group.isRenderAddBlankLineButton()) {
565 renderTemplate(env, group.getAddBlankLineAction(), null, false, false, null);
566 }
567
568 if (group.isAddWithDialog()) {
569 renderTemplate(env, group.getAddWithDialogAction(), null, false, false, null);
570 }
571 }
572
573 LayoutManager layoutManager = group.getLayoutManager();
574 String managerTemplateName = layoutManager.getTemplateName();
575 List<? extends Component> items = group.getItems();
576
577 if ("uif_stacked".equals(managerTemplateName)) {
578 renderStacked(env, items, (StackedLayoutManager) layoutManager, group);
579 } else {
580 Macro fmMacro = (Macro) env.getMainNamespace().get(layoutManager.getTemplateName());
581
582 if (fmMacro == null) {
583 throw new TemplateException("No macro found using " + layoutManager.getTemplateName(), env);
584 }
585
586 Map<String, Object> args = new java.util.HashMap<String, Object>();
587 args.put("items", items);
588 args.put("manager", group.getLayoutManager());
589 args.put("container", group);
590 InlineTemplateUtils.invokeMacro(env, fmMacro, args, null);
591 }
592
593 if ("BOTTOM".equals(group.getAddLinePlacement())) {
594 if (group.isRenderAddBlankLineButton()) {
595 renderTemplate(env, group.getAddBlankLineAction(), null, false, false, null);
596 }
597
598 if (group.isAddWithDialog()) {
599 renderTemplate(env, group.getAddWithDialogAction(), null, false, false, null);
600 }
601 }
602
603 if (group.isAddWithDialog()) {
604 renderTemplate(env, group.getAddLineDialog(), null, false, false, null);
605 }
606
607 renderCloseGroupWrap(env, group);
608 }
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625 public static void renderStacked(Environment env, List<? extends Component> items, StackedLayoutManager manager,
626 CollectionGroup container) throws IOException, TemplateException {
627 String s;
628 Writer out = env.getOut();
629
630 Pager pager = manager.getPagerWidget();
631 Map<String, TemplateModel> pagerTmplParms = null;
632 if (pager != null && container.isUseServerPaging()) {
633 pagerTmplParms = new HashMap<String, TemplateModel>();
634 renderTemplate(env, pager, null, false, false, pagerTmplParms);
635 }
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659 Group wrapperGroup = manager.getWrapperGroup();
660 if (wrapperGroup != null) {
661 renderTemplate(env, wrapperGroup, null, false, false, null);
662 } else {
663 for (Group item : manager.getStackedGroups()) {
664 renderTemplate(env, item, null, false, false, null);
665 }
666 }
667
668
669
670 if (pager != null && container.isUseServerPaging()) {
671 pagerTmplParms = new HashMap<String, TemplateModel>();
672 renderTemplate(env, pager, null, false, false, pagerTmplParms);
673 }
674 }
675
676 }