001
014
015 package com.liferay.portal.templateparser;
016
017 import com.liferay.portal.kernel.configuration.Filter;
018 import com.liferay.portal.kernel.io.unsync.UnsyncStringWriter;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021 import com.liferay.portal.kernel.mobile.device.Device;
022 import com.liferay.portal.kernel.mobile.device.UnknownDevice;
023 import com.liferay.portal.kernel.portlet.PortletRequestModel;
024 import com.liferay.portal.kernel.template.StringTemplateResource;
025 import com.liferay.portal.kernel.template.Template;
026 import com.liferay.portal.kernel.template.TemplateConstants;
027 import com.liferay.portal.kernel.template.TemplateManagerUtil;
028 import com.liferay.portal.kernel.template.TemplateResource;
029 import com.liferay.portal.kernel.template.URLTemplateResource;
030 import com.liferay.portal.kernel.templateparser.TemplateNode;
031 import com.liferay.portal.kernel.templateparser.TransformException;
032 import com.liferay.portal.kernel.templateparser.TransformerListener;
033 import com.liferay.portal.kernel.util.Constants;
034 import com.liferay.portal.kernel.util.GetterUtil;
035 import com.liferay.portal.kernel.util.InstanceFactory;
036 import com.liferay.portal.kernel.util.LocaleUtil;
037 import com.liferay.portal.kernel.util.LocalizationUtil;
038 import com.liferay.portal.kernel.util.PortalClassLoaderUtil;
039 import com.liferay.portal.kernel.util.PropertiesUtil;
040 import com.liferay.portal.kernel.util.SetUtil;
041 import com.liferay.portal.kernel.util.StringBundler;
042 import com.liferay.portal.kernel.util.StringPool;
043 import com.liferay.portal.kernel.util.StringUtil;
044 import com.liferay.portal.kernel.util.Validator;
045 import com.liferay.portal.kernel.xml.Attribute;
046 import com.liferay.portal.kernel.xml.Document;
047 import com.liferay.portal.kernel.xml.DocumentException;
048 import com.liferay.portal.kernel.xml.Element;
049 import com.liferay.portal.kernel.xml.SAXReaderUtil;
050 import com.liferay.portal.model.Company;
051 import com.liferay.portal.security.permission.PermissionThreadLocal;
052 import com.liferay.portal.service.CompanyLocalServiceUtil;
053 import com.liferay.portal.theme.ThemeDisplay;
054 import com.liferay.portal.util.PropsUtil;
055 import com.liferay.portal.xsl.XSLTemplateResource;
056 import com.liferay.portal.xsl.XSLURIResolver;
057 import com.liferay.portlet.journal.util.JournalXSLURIResolver;
058 import com.liferay.portlet.portletdisplaytemplate.util.PortletDisplayTemplateConstants;
059 import com.liferay.taglib.util.VelocityTaglib;
060
061 import java.io.IOException;
062
063 import java.net.URL;
064
065 import java.util.ArrayList;
066 import java.util.HashMap;
067 import java.util.HashSet;
068 import java.util.List;
069 import java.util.Locale;
070 import java.util.Map;
071 import java.util.Set;
072
073
082 public class Transformer {
083
084 public Transformer(String errorTemplatePropertyKey, boolean restricted) {
085 Set<String> langTypes = TemplateManagerUtil.getSupportedLanguageTypes(
086 errorTemplatePropertyKey);
087
088 for (String langType : langTypes) {
089 String errorTemplateId = PropsUtil.get(
090 errorTemplatePropertyKey, new Filter(langType));
091
092 if (Validator.isNotNull(errorTemplateId)) {
093 _errorTemplateIds.put(langType, errorTemplateId);
094 }
095 }
096
097 _restricted = restricted;
098 }
099
100 public Transformer(
101 String transformerListenerPropertyKey, String errorTemplatePropertyKey,
102 boolean restricted) {
103
104 this(errorTemplatePropertyKey, restricted);
105
106 ClassLoader classLoader = PortalClassLoaderUtil.getClassLoader();
107
108 Set<String> transformerListenerClassNames = SetUtil.fromArray(
109 PropsUtil.getArray(transformerListenerPropertyKey));
110
111 for (String transformerListenerClassName :
112 transformerListenerClassNames) {
113
114 try {
115 if (_log.isDebugEnabled()) {
116 _log.debug(
117 "Instantiating transformer listener " +
118 transformerListenerClassName);
119 }
120
121 TransformerListener transformerListener =
122 (TransformerListener)InstanceFactory.newInstance(
123 classLoader, transformerListenerClassName);
124
125 _transformerListeners.add(transformerListener);
126 }
127 catch (Exception e) {
128 _log.error(e, e);
129 }
130 }
131 }
132
133 public String transform(
134 ThemeDisplay themeDisplay, Map<String, Object> contextObjects,
135 String script, String langType)
136 throws Exception {
137
138 if (Validator.isNull(langType)) {
139 return null;
140 }
141
142 long companyId = 0;
143 long companyGroupId = 0;
144 long scopeGroupId = 0;
145 long siteGroupId = 0;
146
147 if (themeDisplay != null) {
148 companyId = themeDisplay.getCompanyId();
149 companyGroupId = themeDisplay.getCompanyGroupId();
150 scopeGroupId = themeDisplay.getScopeGroupId();
151 siteGroupId = themeDisplay.getSiteGroupId();
152 }
153
154 String templateId = String.valueOf(contextObjects.get("template_id"));
155
156 templateId = getTemplateId(
157 templateId, companyId, companyGroupId, scopeGroupId);
158
159 Template template = getTemplate(templateId, script, langType);
160
161 UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
162
163 try {
164 prepareTemplate(themeDisplay, template);
165
166 long classNameId = 0;
167
168 if (contextObjects != null) {
169 for (String key : contextObjects.keySet()) {
170 template.put(key, contextObjects.get(key));
171 }
172
173 classNameId = GetterUtil.getLong(
174 contextObjects.get(TemplateConstants.CLASS_NAME_ID));
175 }
176
177 template.put("company", getCompany(themeDisplay, companyId));
178 template.put("companyId", companyId);
179 template.put("device", getDevice(themeDisplay));
180
181 String templatesPath = getTemplatesPath(
182 companyId, scopeGroupId, classNameId);
183
184 template.put(
185 "permissionChecker",
186 PermissionThreadLocal.getPermissionChecker());
187 template.put(
188 "randomNamespace",
189 StringUtil.randomId() + StringPool.UNDERLINE);
190 template.put("scopeGroupId", scopeGroupId);
191 template.put("siteGroupId", siteGroupId);
192 template.put("templatesPath", templatesPath);
193
194
195
196 template.put("groupId", scopeGroupId);
197 template.put("journalTemplatesPath", templatesPath);
198
199 mergeTemplate(template, unsyncStringWriter, false);
200 }
201 catch (Exception e) {
202 throw new TransformException("Unhandled exception", e);
203 }
204
205 return unsyncStringWriter.toString();
206 }
207
208 public String transform(
209 ThemeDisplay themeDisplay, Map<String, String> tokens,
210 String viewMode, String languageId, Document document,
211 PortletRequestModel portletRequestModel, String script,
212 String langType)
213 throws Exception {
214
215 return doTransform(
216 themeDisplay, tokens, viewMode, languageId, document,
217 portletRequestModel, script, langType, false);
218 }
219
220 public String transform(
221 ThemeDisplay themeDisplay, Map<String, String> tokens,
222 String viewMode, String languageId, Document document,
223 PortletRequestModel portletRequestModel, String script,
224 String langType, boolean propagateException)
225 throws Exception {
226
227 return doTransform(
228 themeDisplay, tokens, viewMode, languageId, document,
229 portletRequestModel, script, langType, propagateException);
230 }
231
232 protected String doTransform(
233 ThemeDisplay themeDisplay, Map<String, String> tokens,
234 String viewMode, String languageId, Document document,
235 PortletRequestModel portletRequestModel, String script,
236 String langType, boolean propagateException)
237 throws Exception {
238
239
240
241 if (_log.isDebugEnabled()) {
242 _log.debug("Language " + languageId);
243 }
244
245 if (Validator.isNull(viewMode)) {
246 viewMode = Constants.VIEW;
247 }
248
249 if (_logTokens.isDebugEnabled()) {
250 String tokensString = PropertiesUtil.list(tokens);
251
252 _logTokens.debug(tokensString);
253 }
254
255 if (_logTransformBefore.isDebugEnabled()) {
256 _logTransformBefore.debug(document);
257 }
258
259 for (TransformerListener transformerListener : _transformerListeners) {
260
261
262
263 if (_logXmlBeforeListener.isDebugEnabled()) {
264 _logXmlBeforeListener.debug(document);
265 }
266
267 if (transformerListener != null) {
268 document = transformerListener.onXml(
269 document, languageId, tokens);
270
271 if (_logXmlAfterListener.isDebugEnabled()) {
272 _logXmlAfterListener.debug(document);
273 }
274 }
275
276
277
278 if (_logScriptBeforeListener.isDebugEnabled()) {
279 _logScriptBeforeListener.debug(script);
280 }
281
282 if (transformerListener != null) {
283 script = transformerListener.onScript(
284 script, document, languageId, tokens);
285
286 if (_logScriptAfterListener.isDebugEnabled()) {
287 _logScriptAfterListener.debug(script);
288 }
289 }
290 }
291
292
293
294 String output = null;
295
296 if (Validator.isNull(langType)) {
297 output = LocalizationUtil.getLocalization(
298 document.asXML(), languageId);
299 }
300 else {
301 long companyId = 0;
302 long companyGroupId = 0;
303 long articleGroupId = 0;
304 long classNameId = 0;
305
306 if (tokens != null) {
307 companyId = GetterUtil.getLong(tokens.get("company_id"));
308 companyGroupId = GetterUtil.getLong(
309 tokens.get("company_group_id"));
310 articleGroupId = GetterUtil.getLong(
311 tokens.get("article_group_id"));
312 classNameId = GetterUtil.getLong(
313 tokens.get(TemplateConstants.CLASS_NAME_ID));
314 }
315
316 long scopeGroupId = 0;
317 long siteGroupId = 0;
318
319 if (themeDisplay != null) {
320 companyId = themeDisplay.getCompanyId();
321 companyGroupId = themeDisplay.getCompanyGroupId();
322 scopeGroupId = themeDisplay.getScopeGroupId();
323 siteGroupId = themeDisplay.getSiteGroupId();
324 }
325
326 String templateId = tokens.get("template_id");
327
328 templateId = getTemplateId(
329 templateId, companyId, companyGroupId, articleGroupId);
330
331 Template template = getTemplate(
332 templateId, tokens, languageId, document, script, langType);
333
334 UnsyncStringWriter unsyncStringWriter = new UnsyncStringWriter();
335
336 try {
337 if (document != null) {
338 Element rootElement = document.getRootElement();
339
340 List<TemplateNode> templateNodes = getTemplateNodes(
341 themeDisplay, rootElement);
342
343 if (templateNodes != null) {
344 for (TemplateNode templateNode : templateNodes) {
345 template.put(templateNode.getName(), templateNode);
346 }
347 }
348
349 if (portletRequestModel != null) {
350 template.put("request", portletRequestModel.toMap());
351
352 if (langType.equals(TemplateConstants.LANG_TYPE_XSL)) {
353 Document requestDocument = SAXReaderUtil.read(
354 portletRequestModel.toXML());
355
356 Element requestElement =
357 requestDocument.getRootElement();
358
359 template.put("xmlRequest", requestElement.asXML());
360 }
361 }
362 else {
363 Element requestElement = rootElement.element("request");
364
365 template.put(
366 "request", insertRequestVariables(requestElement));
367
368 if (langType.equals(TemplateConstants.LANG_TYPE_XSL)) {
369 template.put("xmlRequest", requestElement.asXML());
370 }
371 }
372 }
373
374 template.put("articleGroupId", articleGroupId);
375 template.put("company", getCompany(themeDisplay, companyId));
376 template.put("companyId", companyId);
377 template.put("device", getDevice(themeDisplay));
378
379 String templatesPath = getTemplatesPath(
380 companyId, articleGroupId, classNameId);
381
382 Locale locale = LocaleUtil.fromLanguageId(languageId);
383
384 template.put("locale", locale);
385
386 template.put(
387 "permissionChecker",
388 PermissionThreadLocal.getPermissionChecker());
389 template.put(
390 "randomNamespace",
391 StringUtil.randomId() + StringPool.UNDERLINE);
392 template.put("scopeGroupId", scopeGroupId);
393 template.put("siteGroupId", siteGroupId);
394 template.put("templatesPath", templatesPath);
395 template.put("viewMode", viewMode);
396
397
398
399 template.put("groupId", articleGroupId);
400 template.put("journalTemplatesPath", templatesPath);
401
402 mergeTemplate(template, unsyncStringWriter, propagateException);
403 }
404 catch (Exception e) {
405 if (e instanceof DocumentException) {
406 throw new TransformException(
407 "Unable to read XML document", e);
408 }
409 else if (e instanceof IOException) {
410 throw new TransformException("Error reading template", e);
411 }
412 else if (e instanceof TransformException) {
413 throw (TransformException)e;
414 }
415 else {
416 throw new TransformException("Unhandled exception", e);
417 }
418 }
419
420 output = unsyncStringWriter.toString();
421 }
422
423
424
425 for (TransformerListener transformerListener : _transformerListeners) {
426
427
428
429 if (_logOutputBeforeListener.isDebugEnabled()) {
430 _logOutputBeforeListener.debug(output);
431 }
432
433 output = transformerListener.onOutput(output, languageId, tokens);
434
435 if (_logOutputAfterListener.isDebugEnabled()) {
436 _logOutputAfterListener.debug(output);
437 }
438 }
439
440 if (_logTransfromAfter.isDebugEnabled()) {
441 _logTransfromAfter.debug(output);
442 }
443
444 return output;
445 }
446
447 protected Company getCompany(ThemeDisplay themeDisplay, long companyId)
448 throws Exception {
449
450 if (themeDisplay != null) {
451 return themeDisplay.getCompany();
452 }
453
454 return CompanyLocalServiceUtil.getCompany(companyId);
455 }
456
457 protected Device getDevice(ThemeDisplay themeDisplay) {
458 if (themeDisplay != null) {
459 return themeDisplay.getDevice();
460 }
461
462 return UnknownDevice.getInstance();
463 }
464
465 protected TemplateResource getErrorTemplateResource(String langType) {
466 try {
467 Class<?> clazz = getClass();
468
469 ClassLoader classLoader = clazz.getClassLoader();
470
471 String errorTemplateId = _errorTemplateIds.get(langType);
472
473 URL url = classLoader.getResource(errorTemplateId);
474
475 return new URLTemplateResource(errorTemplateId, url);
476 }
477 catch (Exception e) {
478 }
479
480 return null;
481 }
482
483 protected Template getTemplate(
484 String templateId, Map<String, String> tokens, String languageId,
485 Document document, String script, String langType)
486 throws Exception {
487
488 TemplateResource templateResource = null;
489
490 if (langType.equals(TemplateConstants.LANG_TYPE_XSL)) {
491 XSLURIResolver xslURIResolver = new JournalXSLURIResolver(
492 tokens, languageId);
493
494 templateResource = new XSLTemplateResource(
495 templateId, script, xslURIResolver, document.asXML());
496 }
497 else {
498 templateResource = new StringTemplateResource(templateId, script);
499 }
500
501 TemplateResource errorTemplateResource = getErrorTemplateResource(
502 langType);
503
504 return TemplateManagerUtil.getTemplate(
505 langType, templateResource, errorTemplateResource, _restricted);
506 }
507
508 protected Template getTemplate(
509 String templateId, String script, String langType)
510 throws Exception {
511
512 TemplateResource templateResource = new StringTemplateResource(
513 templateId, script);
514
515 TemplateResource errorTemplateResource = getErrorTemplateResource(
516 langType);
517
518 return TemplateManagerUtil.getTemplate(
519 langType, templateResource, errorTemplateResource, _restricted);
520 }
521
522 protected String getTemplateId(
523 String templateId, long companyId, long companyGroupId, long groupId) {
524
525 StringBundler sb = new StringBundler(5);
526
527 sb.append(companyId);
528 sb.append(StringPool.POUND);
529
530 if (companyGroupId > 0) {
531 sb.append(companyGroupId);
532 }
533 else {
534 sb.append(groupId);
535 }
536
537 sb.append(StringPool.POUND);
538 sb.append(templateId);
539
540 return sb.toString();
541 }
542
543 protected List<TemplateNode> getTemplateNodes(
544 ThemeDisplay themeDisplay, Element element)
545 throws Exception {
546
547 List<TemplateNode> templateNodes = new ArrayList<TemplateNode>();
548
549 Map<String, TemplateNode> prototypeTemplateNodes =
550 new HashMap<String, TemplateNode>();
551
552 List<Element> dynamicElementElements = element.elements(
553 "dynamic-element");
554
555 for (Element dynamicElementElement : dynamicElementElements) {
556 Element dynamicContentElement = dynamicElementElement.element(
557 "dynamic-content");
558
559 String data = StringPool.BLANK;
560
561 if (dynamicContentElement != null) {
562 data = dynamicContentElement.getText();
563 }
564
565 String name = dynamicElementElement.attributeValue(
566 "name", StringPool.BLANK);
567
568 if (name.length() == 0) {
569 throw new TransformException(
570 "Element missing \"name\" attribute");
571 }
572
573 String type = dynamicElementElement.attributeValue(
574 "type", StringPool.BLANK);
575
576 Map<String, String> attributes = new HashMap<String, String>();
577
578 if (dynamicContentElement != null) {
579 for (Attribute attribute : dynamicContentElement.attributes()) {
580 attributes.put(attribute.getName(), attribute.getValue());
581 }
582 }
583
584 TemplateNode templateNode = new TemplateNode(
585 themeDisplay, name, StringUtil.stripCDATA(data), type,
586 attributes);
587
588 if (dynamicElementElement.element("dynamic-element") != null) {
589 templateNode.appendChildren(
590 getTemplateNodes(themeDisplay, dynamicElementElement));
591 }
592 else if ((dynamicContentElement != null) &&
593 (dynamicContentElement.element("option") != null)) {
594
595 List<Element> optionElements = dynamicContentElement.elements(
596 "option");
597
598 for (Element optionElement : optionElements) {
599 templateNode.appendOption(
600 StringUtil.stripCDATA(optionElement.getText()));
601 }
602 }
603
604 TemplateNode prototypeTemplateNode = prototypeTemplateNodes.get(
605 name);
606
607 if (prototypeTemplateNode == null) {
608 prototypeTemplateNode = templateNode;
609
610 prototypeTemplateNodes.put(name, prototypeTemplateNode);
611
612 templateNodes.add(templateNode);
613 }
614
615 prototypeTemplateNode.appendSibling(templateNode);
616 }
617
618 return templateNodes;
619 }
620
621 protected String getTemplatesPath(
622 long companyId, long groupId, long classNameId) {
623
624 StringBundler sb = new StringBundler(7);
625
626 sb.append(TemplateConstants.TEMPLATE_SEPARATOR);
627 sb.append(StringPool.SLASH);
628 sb.append(companyId);
629 sb.append(StringPool.SLASH);
630 sb.append(groupId);
631 sb.append(StringPool.SLASH);
632 sb.append(classNameId);
633
634 return sb.toString();
635 }
636
637 protected Map<String, Object> insertRequestVariables(Element element) {
638 Map<String, Object> map = new HashMap<String, Object>();
639
640 if (element == null) {
641 return map;
642 }
643
644 for (Element childElement : element.elements()) {
645 String name = childElement.getName();
646
647 if (name.equals("attribute")) {
648 Element nameElement = childElement.element("name");
649 Element valueElement = childElement.element("value");
650
651 map.put(nameElement.getText(), valueElement.getText());
652 }
653 else if (name.equals("parameter")) {
654 Element nameElement = childElement.element("name");
655
656 List<Element> valueElements = childElement.elements("value");
657
658 if (valueElements.size() == 1) {
659 Element valueElement = valueElements.get(0);
660
661 map.put(nameElement.getText(), valueElement.getText());
662 }
663 else {
664 List<String> values = new ArrayList<String>();
665
666 for (Element valueElement : valueElements) {
667 values.add(valueElement.getText());
668 }
669
670 map.put(nameElement.getText(), values);
671 }
672 }
673 else {
674 List<Element> elements = childElement.elements();
675
676 if (!elements.isEmpty()) {
677 map.put(name, insertRequestVariables(childElement));
678 }
679 else {
680 map.put(name, childElement.getText());
681 }
682 }
683 }
684
685 return map;
686 }
687
688 protected void mergeTemplate(
689 Template template, UnsyncStringWriter unsyncStringWriter,
690 boolean propagateException)
691 throws Exception {
692
693 VelocityTaglib velocityTaglib = (VelocityTaglib)template.get(
694 PortletDisplayTemplateConstants.TAGLIB_LIFERAY);
695
696 if (velocityTaglib != null) {
697 velocityTaglib.setTemplate(template);
698 }
699
700 if (propagateException) {
701 template.doProcessTemplate(unsyncStringWriter);
702 }
703 else {
704 template.processTemplate(unsyncStringWriter);
705 }
706 }
707
708 protected void prepareTemplate(ThemeDisplay themeDisplay, Template template)
709 throws Exception {
710
711 if (themeDisplay == null) {
712 return;
713 }
714
715 template.prepare(themeDisplay.getRequest());
716 }
717
718 private static final Log _log = LogFactoryUtil.getLog(Transformer.class);
719
720 private static final Log _logOutputAfterListener = LogFactoryUtil.getLog(
721 Transformer.class.getName() + ".OutputAfterListener");
722 private static final Log _logOutputBeforeListener = LogFactoryUtil.getLog(
723 Transformer.class.getName() + ".OutputBeforeListener");
724 private static final Log _logScriptAfterListener = LogFactoryUtil.getLog(
725 Transformer.class.getName() + ".ScriptAfterListener");
726 private static final Log _logScriptBeforeListener = LogFactoryUtil.getLog(
727 Transformer.class.getName() + ".ScriptBeforeListener");
728 private static final Log _logTokens = LogFactoryUtil.getLog(
729 Transformer.class.getName() + ".Tokens");
730 private static final Log _logTransformBefore = LogFactoryUtil.getLog(
731 Transformer.class.getName() + ".TransformBefore");
732 private static final Log _logTransfromAfter = LogFactoryUtil.getLog(
733 Transformer.class.getName() + ".TransformAfter");
734 private static final Log _logXmlAfterListener = LogFactoryUtil.getLog(
735 Transformer.class.getName() + ".XmlAfterListener");
736 private static final Log _logXmlBeforeListener = LogFactoryUtil.getLog(
737 Transformer.class.getName() + ".XmlBeforeListener");
738
739 private final Map<String, String> _errorTemplateIds =
740 new HashMap<String, String>();
741 private final boolean _restricted;
742 private final Set<TransformerListener> _transformerListeners =
743 new HashSet<TransformerListener>();
744
745 }