source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/TransformingReceptionist.java@ 33726

Last change on this file since 33726 was 33726, checked in by ak19, 4 years ago

Changing lowercase utf-8 parameter to uppercase UTF-8 in case that matters in identifying the exact encoding we want the XMLConverter to use.

  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1package org.greenstone.gsdl3.core;
2
3import java.io.File;
4import java.io.FileReader;
5import java.io.Serializable;
6import java.io.StringWriter;
7import java.util.ArrayList;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.regex.Pattern;
11import java.util.regex.Matcher;
12
13import javax.xml.transform.Transformer;
14import javax.xml.transform.TransformerException;
15import javax.xml.transform.TransformerFactory;
16import javax.xml.transform.dom.DOMSource;
17import javax.xml.transform.stream.StreamResult;
18
19import org.apache.commons.lang3.StringUtils;
20import org.apache.log4j.Logger;
21import org.apache.xerces.parsers.DOMParser;
22import org.greenstone.gsdl3.action.Action;
23import org.greenstone.gsdl3.util.GSConstants;
24import org.greenstone.gsdl3.util.GSFile;
25import org.greenstone.gsdl3.util.GSParams;
26import org.greenstone.gsdl3.util.GSXML;
27import org.greenstone.gsdl3.util.GSXSLT;
28import org.greenstone.gsdl3.util.UserContext;
29import org.greenstone.gsdl3.util.XMLConverter;
30import org.greenstone.gsdl3.util.XMLTransformer;
31import org.greenstone.gsdl3.util.XSLTUtil;
32import org.greenstone.util.GlobalProperties;
33import org.w3c.dom.Comment;
34import org.w3c.dom.Document;
35import org.w3c.dom.Element;
36import org.w3c.dom.Node;
37import org.w3c.dom.NodeList;
38import org.w3c.dom.Text;
39import org.xml.sax.InputSource;
40
41/**
42 * A receptionist that uses xslt to transform the page_data before returning it.
43 * . Receives requests consisting of an xml representation of cgi args, and
44 * returns the page of data - in html by default. The requests are processed by
45 * the appropriate action class
46 *
47 * @see Action
48 */
49public class TransformingReceptionist extends Receptionist
50{
51 protected static final String EXPAND_GSF_FILE = "expand-gsf.xsl";
52 protected static final String EXPAND_GSF_PASS1_FILE = "expand-gsf-pass1.xsl";
53 protected static final String EXPAND_GSLIB_FILE = "expand-gslib.xsl";
54 protected static final String GSLIB_FILE = "gslib.xsl";
55
56 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.TransformingReceptionist.class.getName());
57
58 /** The expand-gslib.xsl file is in a fixed location */
59 static final String expand_gslib_filepath = GlobalProperties.getGSDL3Home() + File.separatorChar + "interfaces" + File.separatorChar + "core" + File.separatorChar + "transform" + File.separatorChar + EXPAND_GSLIB_FILE;
60
61 /** the list of xslt to use for actions */
62 protected HashMap<String, String> xslt_map = null;
63
64 /** a transformer class to transform xml using xslt */
65 protected XMLTransformer transformer = null;
66
67 protected TransformerFactory transformerFactory = null;
68 protected DOMParser parser = null;
69
70 protected HashMap<String, ArrayList<String>> _metadataRequiredMap = new HashMap<String, ArrayList<String>>();
71
72 boolean _debug = true;
73
74 public TransformingReceptionist()
75 {
76 super();
77 this.xslt_map = new HashMap<String, String>();
78 this.transformer = new XMLTransformer();
79 try
80 {
81 transformerFactory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
82 this.converter = new XMLConverter();
83 //transformerFactory.setURIResolver(new MyUriResolver()) ;
84
85 parser = new DOMParser();
86 parser.setFeature("http://xml.org/sax/features/validation", false);
87 // don't try and load external DTD - no need if we are not validating, and may cause connection errors if a proxy is not set up.
88 parser.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
89 // a performance test showed that having this on lead to increased
90 // memory use for small-medium docs, and not much gain for large
91 // docs.
92 // http://www.sosnoski.com/opensrc/xmlbench/conclusions.html
93 parser.setFeature("http://apache.org/xml/features/dom/defer-node-expansion", false);
94 parser.setFeature("http://apache.org/xml/features/continue-after-fatal-error", true);
95 // setting a handler for when fatal errors, errors or warnings happen during xml parsing
96 // call XMLConverter's getParseErrorMessage() to get the errorstring that can be rendered as web page
97 this.parser.setErrorHandler(new XMLConverter.ParseErrorHandler());
98 }
99 catch (Exception e)
100 {
101 e.printStackTrace();
102 }
103 }
104
105 /** configures the receptionist - adding in setting up the xslt map */
106 public boolean configure()
107 {
108 if (!super.configure()) {
109
110 return false;
111 }
112
113 logger.info("configuring the TransformingReceptionist");
114
115 // find the config file containing a list of actions
116 File interface_config_file = new File(GSFile.interfaceConfigFile(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))));
117 Document config_doc = this.converter.getDOM(interface_config_file, "UTF-8");
118 Element config_elem = config_doc.getDocumentElement();
119
120 // Find the actions again so that we can set up the xslt map
121 Element action_list = (Element) GSXML.getChildByTagName(config_elem, GSXML.ACTION_ELEM + GSXML.LIST_MODIFIER);
122 NodeList actions = action_list.getElementsByTagName(GSXML.ACTION_ELEM);
123
124 for (int i = 0; i < actions.getLength(); i++)
125 {
126 Element action = (Element) actions.item(i);
127 String class_name = action.getAttribute("class");
128 String action_name = action.getAttribute("name");
129
130 // now do the xslt map
131 String xslt = action.getAttribute("xslt");
132 if (!xslt.equals(""))
133 {
134 this.xslt_map.put(action_name, xslt);
135 }
136 NodeList subactions = action.getElementsByTagName(GSXML.SUBACTION_ELEM);
137 for (int j = 0; j < subactions.getLength(); j++)
138 {
139 Element subaction = (Element) subactions.item(j);
140 String subname = subaction.getAttribute(GSXML.NAME_ATT);
141 String subxslt = subaction.getAttribute("xslt");
142
143 String map_key = action_name + ":" + subname;
144 logger.debug("adding in to xslt map, " + map_key + "->" + subxslt);
145 this.xslt_map.put(map_key, subxslt);
146 }
147 }
148
149 getRequiredMetadataNamesFromXSLFiles();
150
151 return true;
152 }
153
154 protected void getRequiredMetadataNamesFromXSLFiles()
155 {
156 ArrayList<File> xslFiles = GSFile.getAllXSLFiles((String) this.config_params.get((String) this.config_params.get(GSConstants.SITE_NAME)));
157
158 HashMap<String, ArrayList<String>> includes = new HashMap<String, ArrayList<String>>();
159 HashMap<String, ArrayList<File>> files = new HashMap<String, ArrayList<File>>();
160 HashMap<String, ArrayList<String>> metaNames = new HashMap<String, ArrayList<String>>();
161
162 //First exploratory pass
163 for (File currentFile : xslFiles)
164 {
165
166 String full_filename = currentFile.getPath();
167 int sep_pos = full_filename.lastIndexOf(File.separator)+1;
168 String local_filename = full_filename.substring(sep_pos);
169 if (local_filename.startsWith(".")) {
170 logger.warn("Greenstone does not normally rely on 'dot' files for XSL transformations.\n Is the following file intended to be part of the digital library installation?\n XSL File being read in:\n " + currentFile.getPath());
171 }
172
173 Document currentDoc = this.converter.getDOM(currentFile);
174 if (currentDoc == null)
175 {
176 // Can happen if an editor creates an auto-save temporary file
177 // (such as #header.xsl#) that is not well formed XML
178 continue;
179 }
180
181 HashSet<String> extra_meta_names = new HashSet<String>();
182 GSXSLT.findExtraMetadataNames(currentDoc.getDocumentElement(), extra_meta_names);
183 ArrayList<String> names = new ArrayList<String>(extra_meta_names);
184
185 metaNames.put(currentFile.getAbsolutePath(), names);
186
187 NodeList includeElems = currentDoc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "include");
188 NodeList importElems = currentDoc.getElementsByTagNameNS(GSXML.XSL_NAMESPACE, "import");
189
190
191 ArrayList<String> includeAndImportList = new ArrayList<String>();
192 for (int i = 0; i < includeElems.getLength(); i++)
193 {
194 includeAndImportList.add(((Element) includeElems.item(i)).getAttribute(GSXML.HREF_ATT));
195 }
196 for (int i = 0; i < importElems.getLength(); i++)
197 {
198 includeAndImportList.add(((Element) importElems.item(i)).getAttribute(GSXML.HREF_ATT));
199 }
200 includes.put(currentFile.getAbsolutePath(), includeAndImportList);
201
202 String filename = currentFile.getName();
203 if (files.get(filename) == null)
204 {
205 ArrayList<File> fileList = new ArrayList<File>();
206 fileList.add(currentFile);
207 files.put(currentFile.getName(), fileList);
208 }
209 else
210 {
211 ArrayList<File> fileList = files.get(filename);
212 fileList.add(currentFile);
213 }
214 }
215
216 //Second pass
217 for (File currentFile : xslFiles)
218 {
219 ArrayList<File> filesToGet = new ArrayList<File>();
220 filesToGet.add(currentFile);
221
222 ArrayList<String> fullNameList = new ArrayList<String>();
223
224 while (filesToGet.size() > 0)
225 {
226 File currentFileTemp = filesToGet.remove(0);
227
228 //Add the names from this file
229 ArrayList<String> currentNames = metaNames.get(currentFileTemp.getAbsolutePath());
230 if (currentNames == null)
231 {
232 continue;
233 }
234
235 fullNameList.addAll(currentNames);
236
237 ArrayList<String> includedHrefs = includes.get(currentFileTemp.getAbsolutePath());
238
239 for (String href : includedHrefs)
240 {
241 int lastSepIndex = href.lastIndexOf("/");
242 if (lastSepIndex != -1)
243 {
244 href = href.substring(lastSepIndex + 1);
245 }
246
247 ArrayList<File> filesToAdd = files.get(href);
248 if (filesToAdd != null)
249 {
250 filesToGet.addAll(filesToAdd);
251 }
252 }
253 }
254
255 _metadataRequiredMap.put(currentFile.getAbsolutePath(), fullNameList);
256 }
257 }
258
259 protected void preProcessRequest(Element request)
260 {
261 String action = request.getAttribute(GSXML.ACTION_ATT);
262 String subaction = request.getAttribute(GSXML.SUBACTION_ATT);
263
264 String name = null;
265 if (!subaction.equals(""))
266 {
267 String key = action + ":" + subaction;
268 name = this.xslt_map.get(key);
269 }
270 // try the action by itself
271 if (name == null)
272 {
273 name = this.xslt_map.get(action);
274 }
275
276 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
277 String collection = "";
278
279 if (cgi_param_list != null)
280 {
281 // Don't waste time getting all the parameters
282 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
283 collection = (String) params.get(GSParams.COLLECTION);
284 if (collection == null)
285 {
286 collection = "";
287 }
288 }
289
290 ArrayList<File> stylesheets = GSFile.getStylesheetFiles(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, name);
291
292 Document doc = XMLConverter.newDOM();
293 Element extraMetadataList = doc.createElement(GSXML.EXTRA_METADATA + GSXML.LIST_MODIFIER);
294 HashSet<String> name_set = new HashSet<String>();
295 for (File stylesheet : stylesheets)
296 {
297 ArrayList<String> requiredMetadata = _metadataRequiredMap.get(stylesheet.getAbsolutePath());
298
299 if (requiredMetadata != null)
300 {
301 for (String metadataString : requiredMetadata)
302 {
303 if (!name_set.contains(metadataString)) {
304 name_set.add(metadataString);
305 Element metadataElem = doc.createElement(GSXML.EXTRA_METADATA);
306 metadataElem.setAttribute(GSXML.NAME_ATT, metadataString);
307 extraMetadataList.appendChild(metadataElem);
308 }
309 }
310 }
311 }
312 request.appendChild(request.getOwnerDocument().importNode(extraMetadataList, true));
313 }
314
315 protected Node postProcessPage(Element page)
316 {
317 // might need to add some data to the page
318 addExtraInfo(page);
319
320
321 // transform the page using xslt
322
323 String currentInterface = (String) config_params.get(GSConstants.INTERFACE_NAME);
324
325 Element request = (Element) GSXML.getChildByTagName(page, GSXML.PAGE_REQUEST_ELEM);
326 String output = request.getAttribute(GSXML.OUTPUT_ATT);
327
328 boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
329 //logger.info("Client side transforms allowed? " + allowsClientXSLT);
330
331 if (useClientXSLT)
332 {
333 // if not specified, output defaults to 'html', but this isn't what we want when useClientXSLT is on
334 if (output.equals("html")) {
335 output = "xsltclient";
336 }
337 }
338 Node transformed_page = transformPage(page,currentInterface,output);
339
340 if (useClientXSLT) {
341 return transformed_page;
342 }
343 // if the user has specified they want only a part of the full page then subdivide it
344 boolean subdivide = false;
345 String excerptID = null;
346 String excerptTag = null;
347 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
348 if (cgi_param_list != null)
349 {
350 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
351 if ((excerptID = (String) params.get(GSParams.EXCERPT_ID)) != null)
352 {
353 subdivide = true;
354 }
355 if ((excerptTag = (String) params.get(GSParams.EXCERPT_TAG)) != null)
356 {
357 subdivide = true;
358 }
359 }
360
361 if (subdivide)
362 {
363 Node subdivided_page = subdivide(transformed_page, excerptID, excerptTag);
364 if (subdivided_page != null)
365 {
366 return subdivided_page;
367 }
368 else return null;
369 }
370
371 return transformed_page;
372 }
373
374 protected Node subdivide(Node transformed_page, String excerptID, String excerptTag)
375 {
376 if (excerptID != null)
377 {
378 Node selectedElement = getNodeByIdRecursive(transformed_page, excerptID);
379 modifyNodesByTagRecursive(selectedElement, "a");
380 return selectedElement;
381 }
382 else if (excerptTag != null)
383 {
384 Node selectedElement = getNodeByTagRecursive(transformed_page, excerptTag);
385 return selectedElement;
386 }
387 return transformed_page;
388 }
389
390 protected Node getNodeByIdRecursive(Node parent, String id)
391 {
392 if (parent.hasAttributes() && ((Element) parent).getAttribute("id").equals(id))
393 {
394 return parent;
395 }
396
397 NodeList children = parent.getChildNodes();
398 for (int i = 0; i < children.getLength(); i++)
399 {
400 Node result = null;
401 if ((result = getNodeByIdRecursive(children.item(i), id)) != null)
402 {
403 return result;
404 }
405 }
406 return null;
407 }
408
409 protected Node getNodeByTagRecursive(Node parent, String tag)
410 {
411 if (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag))
412 {
413 return parent;
414 }
415
416 NodeList children = parent.getChildNodes();
417 for (int i = 0; i < children.getLength(); i++)
418 {
419 Node result = null;
420 if ((result = getNodeByTagRecursive(children.item(i), tag)) != null)
421 {
422 return result;
423 }
424 }
425 return null;
426 }
427
428 protected Node modifyNodesByTagRecursive(Node parent, String tag)
429 {
430 if (parent == null || (parent.getNodeType() == Node.ELEMENT_NODE && ((Element) parent).getTagName().equals(tag)))
431 {
432 return parent;
433 }
434
435 NodeList children = parent.getChildNodes();
436 for (int i = 0; i < children.getLength(); i++)
437 {
438 Node result = null;
439 if ((result = modifyNodesByTagRecursive(children.item(i), tag)) != null)
440 {
441 //TODO: DO SOMETHING HERE?
442 }
443 }
444 return null;
445 }
446
447 protected void replaceNodeWithInterfaceText(Document doc, String interface_name, String lang,
448 Element elem, String attr_name, String attr_val)
449 {
450 String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
451 String pattern_str_4arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*,\\s*(.+?)\\s*\\)$";
452
453 Pattern pattern3 = Pattern.compile(pattern_str_3arg);
454 Matcher matcher3 = pattern3.matcher(attr_val);
455 if (matcher3.find()) {
456 String dict_key = matcher3.group(1);
457 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
458
459 Node parent_node = elem.getParentNode();
460
461 Text replacement_text_node = doc.createTextNode(dict_val);
462 parent_node.replaceChild(replacement_text_node,elem);
463 }
464 else {
465 Pattern pattern4 = Pattern.compile(pattern_str_4arg);
466 Matcher matcher4 = pattern4.matcher(attr_val);
467 StringBuffer string_buffer4 = new StringBuffer();
468
469 if (matcher4.find()) {
470 String dict_key = matcher4.group(1);
471 String args = matcher4.group(2);
472 args = args.replaceAll("\\$","\\\\\\$");
473
474 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
475
476 matcher4.appendReplacement(string_buffer4, "js:getInterfaceTextSubstituteArgs('"+dict_val+"',string("+args+"))");
477 matcher4.appendTail(string_buffer4);
478
479 attr_val = string_buffer4.toString();
480 elem.setAttribute(attr_name,attr_val);
481 }
482 else {
483 logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
484 attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
485 elem.setAttribute(attr_name,attr_val);
486 }
487 }
488
489 }
490
491 protected void resolveExtendedNamespaceAttributesXSLT(Document doc, String interface_name, String lang)
492 {
493 String[] attr_list = new String[] {"select","test"};
494
495 // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
496 // go through nodeList in reverse to avoid the 'skipping' problem, due to
497 // replaceChild() calls removing items from the "live" nodeList
498
499 NodeList nodeList = doc.getElementsByTagName("*");
500 for (int i=nodeList.getLength()-1; i>=0; i--) {
501 Node node = nodeList.item(i);
502 if (node.getNodeType() == Node.ELEMENT_NODE) {
503 Element elem = (Element)node;
504 for (String attr_name : attr_list) {
505 if (elem.hasAttribute(attr_name)) {
506 String attr_val = elem.getAttribute(attr_name);
507
508 if (attr_val.startsWith("util:getInterfaceText(")) {
509 // replace the node with dictionary lookup
510 replaceNodeWithInterfaceText(doc, interface_name,lang, elem,attr_name,attr_val);
511 }
512 else if (attr_val.contains("util:")) {
513
514 attr_val = attr_val.replaceAll("util:getInterfaceStringsAsJavascript\\(.+?,.+?,\\s*(.+?)\\)","$1");
515
516 //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","'escapeNLandQ $1'");
517 //attr_val = attr_val.replaceAll("util:escapeNewLinesAndQuotes\\(\\s*(.+?)\\s*\\)","$1");
518
519 // 'contains()' supported in XSLT 1.0, so OK to change any util:contains() into contains()
520 attr_val = attr_val.replaceAll("util:(contains\\(.+?\\))","$1");
521
522 elem.setAttribute(attr_name,attr_val);
523 }
524
525 if (attr_val.contains("java:")) {
526 if (attr_val.indexOf("getInterfaceTextSubstituteArgs")>=4) {
527
528 attr_val = attr_val.replaceAll("java:.+?\\.(\\w+)\\((.*?)\\)$","js:$1($2)");
529 }
530
531 elem.setAttribute(attr_name,attr_val);
532 }
533 }
534 }
535
536 }
537 }
538 }
539
540
541 protected void resolveExtendedNamespaceAttributesXML(Document doc, String interface_name, String lang)
542 {
543 String[] attr_list = new String[] {"src", "href"};
544
545 // http://stackoverflow.com/questions/13220520/javascript-replace-child-loop-issue
546 // go through nodeList in reverse to avoid the 'skipping' problem, due to
547 // replaceChild() calls removing items from the "live" nodeList
548
549 NodeList nodeList = doc.getElementsByTagName("*");
550 for (int i=nodeList.getLength()-1; i>=0; i--) {
551 Node node = nodeList.item(i);
552 if (node.getNodeType() == Node.ELEMENT_NODE) {
553 Element elem = (Element)node;
554 for (String attr_name : attr_list) {
555 if (elem.hasAttribute(attr_name)) {
556 String attr_val = elem.getAttribute(attr_name);
557
558 if (attr_val.contains("util:getInterfaceText(")) {
559 String pattern_str_3arg = "util:getInterfaceText\\([^,]+,[^,]+,\\s*'(.+?)'\\s*\\)";
560 Pattern pattern3 = Pattern.compile(pattern_str_3arg);
561 Matcher matcher3 = pattern3.matcher(attr_val);
562
563 StringBuffer string_buffer3 = new StringBuffer();
564
565 boolean found_match = false;
566
567 while (matcher3.find()) {
568 found_match = true;
569 String dict_key = matcher3.group(1);
570 String dict_val = XSLTUtil.getInterfaceText(interface_name,lang,dict_key);
571
572 matcher3.appendReplacement(string_buffer3, dict_val);
573 }
574 matcher3.appendTail(string_buffer3);
575
576 if (found_match) {
577 attr_val = string_buffer3.toString();
578 elem.setAttribute(attr_name,attr_val);
579 }
580 else {
581 logger.error("Failed to find match in attribute: " + attr_name + "=\"" + attr_val + "\"");
582 attr_val = attr_val.replaceAll("util:getInterfaceText\\(.+?,.+?,\\s*(.+?)\\s*\\)","$1");
583 elem.setAttribute(attr_name,attr_val);
584 }
585 }
586 else if (attr_val.contains("util:")) {
587
588 logger.error("Encountered unexpected 'util:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
589 }
590
591 if (attr_val.contains("java:")) {
592 // make anything java: safe from the point of an XSLT without extensions
593 logger.error("Encountered unexpected 'java:' prefix exension: " + attr_name + "=\"" + attr_val + "\"");
594
595 }
596 }
597 }
598
599 }
600 }
601 }
602
603
604
605 /**
606 * overwrite this to add any extra info that might be needed in the page
607 * before transformation
608 */
609 protected void addExtraInfo(Element page)
610 {
611 }
612
613 /**
614 * transform the page using xslt.
615 * we need to get any format element out of the page and add it to the xslt before transforming
616 */
617 protected Node transformPage(Element page_xml, String currentInterface, String output)
618 {
619 _debug = false;
620
621 Element request = (Element) GSXML.getChildByTagName(page_xml, GSXML.PAGE_REQUEST_ELEM);
622
623 //logger.info("Current output mode is: " + output + ", current interface name is: " + currentInterface);
624
625 if (output.equals("xsltclient"))
626 {
627 return generateXSLTClientOutput(request);
628 }
629
630 String action = request.getAttribute(GSXML.ACTION_ATT);
631 String subaction = request.getAttribute(GSXML.SUBACTION_ATT);
632
633 // we should choose how to transform the data based on output, eg diff
634 // choice for html, and wml??
635 // for now, if output=xml, we don't transform the page, we just return
636 // the page xml
637 Document page_with_xslt_params_doc = null;
638
639 if (output.equals("xml") || (output.equals("json")) || output.equals("clientside"))
640 {
641 // Append the xsltparams to the page
642 page_with_xslt_params_doc = converter.newDOM();
643 // Import into new document first!
644 Node page_with_xslt_params = page_with_xslt_params_doc.importNode(page_xml, true);
645 page_with_xslt_params_doc.appendChild(page_with_xslt_params);
646 Element xslt_params = page_with_xslt_params_doc.createElement("xsltparams");
647 page_with_xslt_params.appendChild(xslt_params);
648
649 GSXML.addParameter2ToList(xslt_params, "library_name", (String) config_params.get(GSConstants.LIBRARY_NAME));
650 GSXML.addParameter2ToList(xslt_params, "interface_name", (String) config_params.get(GSConstants.INTERFACE_NAME));
651 GSXML.addParameter2ToList(xslt_params, "site_name", (String) config_params.get(GSConstants.SITE_NAME));
652 Boolean useClientXSLT = (Boolean) config_params.get(GSConstants.USE_CLIENT_SIDE_XSLT);
653 GSXML.addParameter2ToList(xslt_params, "use_client_side_xslt", useClientXSLT.toString());
654 GSXML.addParameter2ToList(xslt_params, "filepath", GlobalProperties.getGSDL3Home());
655
656 if ((output.equals("xml")) || output.equals("json"))
657 {
658 // Just return the page XML
659 // in the case of "json", calling method responsible for converting to JSON-string
660 return page_with_xslt_params_doc.getDocumentElement();
661 }
662 // in the case of client side, later on we'll use this doc with xslt params added,
663 // along with the xsl.
664 }
665
666 Element cgi_param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
667 String collection = "";
668 String inline_template = "";
669 if (cgi_param_list != null)
670 {
671 // Don't waste time getting all the parameters
672 HashMap<String, Serializable> params = GSXML.extractParams(cgi_param_list, false);
673 collection = (String) params.get(GSParams.COLLECTION);
674 if (collection == null)
675 {
676 collection = "";
677 }
678
679 inline_template = (String) params.get(GSParams.INLINE_TEMPLATE);
680 String debug_p = (String) params.get(GSParams.DEBUG);
681 if (debug_p != null && (debug_p.equals("on") || debug_p.equals("1") || debug_p.equals("true")))
682 {
683 String[] groups = new UserContext(request).getGroups();
684
685 boolean found = false;
686 for (String g : groups)
687 {
688 if (g.equals("administrator"))
689 {
690 found = true;
691 break;
692 }
693 if (!collection.equals("")) {
694 if (g.equals("all-collections-editor")) {
695 found = true;
696 break;
697 }
698
699 if (g.equals(collection+"-collection-editor")) {
700 found = true;
701 break;
702 }
703 }
704 }
705 if (found)
706 {
707 _debug = true;
708 }
709 }
710 }
711
712 config_params.put("collName", collection);
713
714 // find the appropriate stylesheet (based on action/subaction) - eg a=p&sa=home will be home.xsl
715 // This mapping is defined in interfaceConfig.xsl
716 // All versions of the stylesheet (base interface, interface, site, collection) are
717 // merged together into one document
718 Document page_xsl = getXSLTDocument(action, subaction, collection);
719 String page_xsl_filename = getXSLTFilename(action, subaction); // for debug purposes
720 if (page_xsl == null)
721 {
722 logger.error("Couldn't find and/or load the stylesheet ("+page_xsl_filename+") for a="+action+", sa="+subaction+", in collection "+collection);
723 return XMLTransformer.constructErrorXHTMLPage("Couldn't find and/or load the stylesheet \""+page_xsl_filename+"\" for a="+action+", sa="+subaction+", in collection "+collection);
724 }
725
726 if (output.equals("xsl1")) {
727 // if we just output the page_xsl directly then there may be unescaped & in the javascript,
728 // and the page won't display properly
729 return converter.getDOM(getStringFromDocument(page_xsl));
730 }
731
732
733 // put the page into a document - this is necessary for xslt to get
734 // the paths right if you have paths relative to the document root
735 // eg /page.
736 Document page_xml_doc = XMLConverter.newDOM();
737 page_xml_doc.appendChild(page_xml_doc.importNode(page_xml, true));
738 Element page_response = (Element) GSXML.getChildByTagName(page_xml, GSXML.PAGE_RESPONSE_ELEM);
739 Element format_elem = (Element) GSXML.getChildByTagName(page_response, GSXML.FORMAT_ELEM);
740
741 if (output.equals("format1"))
742 {
743 return format_elem;
744 }
745
746 // do we have language attribute for page?
747 String lang_att = page_xml.getAttribute(GSXML.LANG_ATT);
748 if (lang_att != null && lang_att.length() > 0)
749 {
750 config_params.put("lang", lang_att);
751 }
752
753 if (format_elem != null)
754 {
755 //page_response.removeChild(format_elem);
756
757 // need to transform the format info
758 // run expand-gsf.xsl over the format_elem. We need to do this now to turn
759 // eg gsf:template into xsl:template so that the merging works properly.
760 // xsl:templates will get merged
761 // into the main stylesheet, but gsf:templates won't.
762
763 Document format_doc = XMLConverter.newDOM();
764 format_doc.appendChild(format_doc.importNode(format_elem, true));
765
766 if (_debug) {
767
768 String siteHome = GSFile.siteHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME));
769 GSXSLT.insertDebugElements(format_doc, GSFile.collectionConfigFile(siteHome, collection));
770 }
771
772 // should we be doing the double pass here too?
773 Node result = transformGSFElements(collection, format_doc, EXPAND_GSF_FILE);
774 // Since we started creating documents with DocTypes, we can end up with
775 // Document objects here. But we will be working with an Element instead,
776 // so we grab the DocumentElement() of the Document object in such a case.
777 Element new_format;
778 if (result.getNodeType() == Node.DOCUMENT_NODE)
779 {
780 new_format = ((Document) result).getDocumentElement();
781 }
782 else
783 {
784 new_format = (Element) result;
785 }
786
787 if (output.equals("format"))
788 {
789 return new_format;
790 }
791
792 // add the extracted format statements in to the main stylesheet
793 if (_debug)
794 {
795 String siteHome = GSFile.siteHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME));
796 GSXSLT.mergeStylesheetsDebug(page_xsl, new_format, true, true, "OTHER1", GSFile.collectionConfigFile(siteHome, collection));
797 }
798 else
799 {
800 GSXSLT.mergeStylesheets(page_xsl, new_format, true);
801 }
802
803
804 }
805
806 if (output.equals("xsl2")) {
807 return converter.getDOM(getStringFromDocument(page_xsl));
808 }
809
810 Document inline_template_doc = null;
811 if (inline_template != null)
812 {
813 try
814 {
815 inline_template_doc = this.converter.getDOM("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<xsl:stylesheet version=\"1.0\" "+GSXML.ALL_NAMESPACES_ATTS + ">" + inline_template + "</xsl:stylesheet>", "UTF-8");
816
817 if (_debug)
818 {
819 GSXSLT.mergeStylesheetsDebug(page_xsl, inline_template_doc.getDocumentElement(), true, true, "OTHER2", "INLINE");
820 }
821 else
822 {
823 //GSXSLT.mergeStylesheets(skinAndLibraryDoc, inlineTemplateDoc.getDocumentElement(), true);
824 GSXSLT.mergeStylesheets(page_xsl, inline_template_doc.getDocumentElement(), true);
825 }
826 }
827 catch (Exception ex)
828 {
829 ex.printStackTrace();
830 }
831 }
832
833
834 if (output.equals("ilt")) {
835 return converter.getDOM(getStringFromDocument(inline_template_doc));
836 }
837 if (output.equals("xsl3")) {
838 return converter.getDOM(getStringFromDocument(page_xsl));
839 }
840
841 // once we are here, have got the main page xsl loaded up. Have added in any format statements from the source xml, and added in any inline template which came through cgi params.
842
843 // next we load in the import and include files. - these, too, go through the inheritance cascade (base interface, interface, site, collection) before being added into the main document
844
845 if (_debug)
846 {
847 GSXSLT.inlineImportAndIncludeFilesDebug(page_xsl, null, _debug, page_xsl_filename, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
848 }
849 else
850 {
851 GSXSLT.inlineImportAndIncludeFiles(page_xsl, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
852
853 }
854
855 if (output.equals("xsl4")) {
856 return converter.getDOM(getStringFromDocument(page_xsl));
857 }
858
859 // The next step is to process the page_xsl + gslib_xsl by
860 // expand-gslib.xsl to expand all the gslib elements
861
862 Document expand_gslib_xsl_doc;
863 try
864 {
865 // interfaces/core/transform/expand-gslib.xsl
866 // this takes skinandLibraryXsl, copies skinXSL, merges elements of libraryXsl into it, and replaces gslib elements
867 expand_gslib_xsl_doc = getDoc(expand_gslib_filepath);
868 String errMsg = ((XMLConverter.ParseErrorHandler) parser.getErrorHandler()).getErrorMessage();
869 if (errMsg != null)
870 {
871 return XMLTransformer.constructErrorXHTMLPage("error loading file: " + expand_gslib_filepath + "\n" + errMsg);
872 }
873 }
874 catch (java.io.FileNotFoundException e)
875 {
876 return fileNotFoundErrorPage(e.getMessage());
877 }
878 catch (Exception e)
879 {
880 e.printStackTrace();
881 System.out.println("error loading "+expand_gslib_filepath);
882 return XMLTransformer.constructErrorXHTMLPage("Error loading file: "+ expand_gslib_filepath+"\n" + e.getMessage());
883 }
884
885 // gslib.xsl
886 Document gslib_xsl_doc = null;
887 try
888 {
889 gslib_xsl_doc = GSXSLT.mergedXSLTDocumentCascade(GSLIB_FILE, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, _debug);
890 }
891 catch (Exception e)
892 {
893 e.printStackTrace();
894 System.out.println("error loading gslib xslt");
895 return XMLTransformer.constructErrorXHTMLPage("error loading gslib xslt\n" + e.getMessage());
896 }
897
898 if (output.equals("gslib-expander")) {
899 return converter.getDOM(getStringFromDocument(expand_gslib_xsl_doc));
900 }
901 if (output.equals("gslib1")) {
902 return converter.getDOM(getStringFromDocument(gslib_xsl_doc));
903 }
904 // Combine the skin file and library variables/templates into one document.
905 // Please note: We dont just use xsl:import because the preprocessing stage
906 // needs to know what's available in the library.
907
908
909 // add in all gslib.xsl's include and import files
910 // debug?? use debug method?? or does it not make sense here??
911 GSXSLT.inlineImportAndIncludeFiles(gslib_xsl_doc, null, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces);
912 if (output.equals("gslib")) {
913 return converter.getDOM(getStringFromDocument(gslib_xsl_doc));
914 }
915
916
917 Document pageAndGslibXsl = null;
918 Document pageAndGslibDoc = converter.newDOM();
919
920 // now, we transform all the gslib elements
921 {
922
923
924 pageAndGslibXsl = converter.newDOM();
925 Element root = pageAndGslibXsl.createElement("pageAndGslibXsl");
926 pageAndGslibXsl.appendChild(root);
927
928 Element s = pageAndGslibXsl.createElement("pageXsl");
929 s.appendChild(pageAndGslibXsl.importNode(page_xsl.getDocumentElement(), true));
930 root.appendChild(s);
931
932 Element l = pageAndGslibXsl.createElement("gslibXsl");
933 if (gslib_xsl_doc != null)
934 {
935 Element gslib_xsl_el = gslib_xsl_doc.getDocumentElement();
936 l.appendChild(pageAndGslibXsl.importNode(gslib_xsl_el, true));
937 }
938 root.appendChild(l);
939
940 if (output.equals("xsl5")) {
941 return converter.getDOM(getStringFromDocument(pageAndGslibXsl));
942 }
943 // actually merge the gslib file with the page
944 XMLTransformer preProcessor = new XMLTransformer();
945 preProcessor.transform_withResultNode(expand_gslib_xsl_doc, pageAndGslibXsl, pageAndGslibDoc);
946
947 }
948 if (output.equals("xsl6")) {
949 return converter.getDOM(getStringFromDocument(pageAndGslibDoc));
950 }
951
952 pageAndGslibDoc = (Document) transformGSFElements(collection, pageAndGslibDoc, EXPAND_GSF_PASS1_FILE);
953
954 if (output.equals("xsl7")) {
955 return converter.getDOM(getStringFromDocument(pageAndGslibDoc));
956 }
957
958 pageAndGslibDoc = (Document) transformGSFElements(collection, pageAndGslibDoc, EXPAND_GSF_FILE);
959
960 if (output.equals("xsl") || output.equals("skinandlibdocfinal"))
961 {
962 return converter.getDOM(getStringFromDocument(pageAndGslibDoc));
963 }
964
965 if (output.equals("clientside"))
966 {
967
968 // Go through and 'fix up' any 'util:...' or 'java:...' attributes the pageAndGslibDoc has
969 String lang = (String)config_params.get("lang");
970 resolveExtendedNamespaceAttributesXSLT(pageAndGslibDoc,currentInterface,lang); // test= and select= attributes
971 resolveExtendedNamespaceAttributesXML(pageAndGslibDoc,currentInterface,lang); // href= and src= attributes
972 Node skinAndLibFinal = converter.getDOM(getStringFromDocument(pageAndGslibDoc));
973
974 // Send XML and skinandlibdoc down the line together
975 Document finalDoc = converter.newDOM();
976 Node finalDocSkin = finalDoc.importNode(pageAndGslibDoc.getDocumentElement(), true);
977 Node finalDocXML = finalDoc.importNode(page_with_xslt_params_doc.getDocumentElement(), true);
978 Element root = finalDoc.createElement("skinlibfinalPlusXML");
979 root.appendChild(finalDocSkin);
980 root.appendChild(finalDocXML);
981 finalDoc.appendChild(root);
982 return (Node) finalDoc.getDocumentElement();
983 }
984
985 ///logger.debug("final xml is ");
986 ///logger.debug(XMLConverter.getPrettyString(page_xml_doc));
987
988 ///logger.debug("final xsl is");
989 ///logger.debug(XMLConverter.getPrettyString(pageAndGslibDoc));
990
991 // The transformer will now work out the resulting doctype from any set in the (merged) stylesheets and
992 // will set this in the output document it creates. So don't pass in any docWithDocType to the transformer
993
994 // Here, we finally transform the page xml source with the complete xsl file
995 Node finalResult = this.transformer.transform(pageAndGslibDoc, page_xml_doc, config_params);
996
997 if (_debug)
998 {
999 GSXSLT.fixTables((Document) finalResult);
1000 }
1001
1002 return finalResult;
1003
1004 }
1005
1006 protected Node generateXSLTClientOutput(Element request) {
1007
1008 // DocType defaults in case the skin doesn't have an "xsl:output" element
1009 String qualifiedName = "html";
1010 String publicID = "-//W3C//DTD HTML 4.01 Transitional//EN";
1011 String systemID = "http://www.w3.org/TR/html4/loose.dtd";
1012
1013 // We need to create an empty document with a predefined DocType,
1014 // that will then be used for the transformation by the DOMResult
1015 Document docWithDoctype = converter.newDOM(qualifiedName, publicID, systemID);
1016 String baseURL = request.getAttribute(GSXML.BASE_URL);
1017
1018 // If you're just getting the client-side transform page, why bother with the rest of this?
1019 Element html = docWithDoctype.createElement("html");
1020 Element img = docWithDoctype.createElement("img");
1021 img.setAttribute("src", "loading.gif"); // Make it dynamic
1022 img.setAttribute("alt", "Please wait...");
1023 Text title_text = docWithDoctype.createTextNode("Please wait..."); // Make this language dependent
1024 Element head = docWithDoctype.createElement("head");
1025
1026 // e.g., <base href="http://localhost:8383/greenstone3/" /><!-- [if lte IE 6]></base><![endif] -->
1027 Element base = docWithDoctype.createElement("base");
1028 base.setAttribute("href",baseURL);
1029 Comment opt_end_base = docWithDoctype.createComment("[if lte IE 6]></base><![endif]");
1030
1031 Element title = docWithDoctype.createElement("title");
1032 title.appendChild(title_text);
1033
1034 Element body = docWithDoctype.createElement("body");
1035
1036 Element jquery_script = docWithDoctype.createElement("script");
1037 jquery_script.setAttribute("src", "jquery-1.10-min.js");
1038 jquery_script.setAttribute("type", "text/javascript");
1039 Comment jquery_comment = docWithDoctype.createComment("jQuery");
1040 jquery_script.appendChild(jquery_comment);
1041
1042 Element saxonce_script = docWithDoctype.createElement("script");
1043 saxonce_script.setAttribute("src", "Saxonce/Saxonce.nocache.js");
1044 saxonce_script.setAttribute("type", "text/javascript");
1045 Comment saxonce_comment = docWithDoctype.createComment("SaxonCE");
1046 saxonce_script.appendChild(saxonce_comment);
1047
1048 Element xsltutil_script = docWithDoctype.createElement("script");
1049 xsltutil_script.setAttribute("src", "xslt-util.js");
1050 xsltutil_script.setAttribute("type", "text/javascript");
1051 Comment xsltutil_comment = docWithDoctype.createComment("JavaScript version of XSLTUtil.java");
1052 xsltutil_script.appendChild(xsltutil_comment);
1053
1054 Element script = docWithDoctype.createElement("script");
1055 Comment script_comment = docWithDoctype.createComment("Filler for browser");
1056 script.setAttribute("src", "client-side-xslt.js");
1057 script.setAttribute("type", "text/javascript");
1058 script.appendChild(script_comment);
1059
1060 Element pagevar = docWithDoctype.createElement("script");
1061 Element style = docWithDoctype.createElement("style");
1062 style.setAttribute("type", "text/css");
1063 Text style_text = docWithDoctype.createTextNode("body { text-align: center; padding: 50px; font: 14pt Arial, sans-serif; font-weight: bold; }");
1064 pagevar.setAttribute("type", "text/javascript");
1065 Text page_var_text = docWithDoctype.createTextNode("var placeholder = true;");
1066
1067 html.appendChild(head);
1068 head.appendChild(base); head.appendChild(opt_end_base);
1069 head.appendChild(title);
1070 head.appendChild(style);
1071 style.appendChild(style_text);
1072 html.appendChild(body);
1073 head.appendChild(pagevar);
1074 head.appendChild(jquery_script);
1075 head.appendChild(saxonce_script);
1076 head.appendChild(xsltutil_script);
1077 head.appendChild(script);
1078 pagevar.appendChild(page_var_text);
1079
1080 body.appendChild(img);
1081 docWithDoctype.appendChild(html);
1082
1083 return (Node) docWithDoctype;
1084
1085 }
1086
1087 // transform the xsl with xsl to replace all gsf elements. We do this in 2 stages -
1088 // first do just a text pass, that way we can have gsf elements in the text content
1089 protected Node transformGSFElements(String collection, Document source_xsl, String expand_gsf_filename) {
1090
1091 String expand_gsf_file = GSFile.stylesheetFile(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, expand_gsf_filename);
1092 Document expand_gsf_doc = this.converter.getDOM(new File(expand_gsf_file));
1093
1094 if (expand_gsf_doc != null)
1095 {
1096 return this.transformer.transform(expand_gsf_doc, source_xsl, config_params);
1097 }
1098 return source_xsl;
1099
1100 }
1101
1102 // method to convert Document to a proper XML string for debug purposes only
1103 protected String getStringFromDocument(Document doc)
1104 {
1105 String content = "";
1106 try
1107 {
1108 DOMSource domSource = new DOMSource(doc);
1109 StringWriter writer = new StringWriter();
1110 StreamResult result = new StreamResult(writer);
1111 TransformerFactory tf = TransformerFactory.newInstance();
1112 Transformer transformer = tf.newTransformer();
1113 transformer.transform(domSource, result);
1114 content = writer.toString();
1115 System.out.println("Change the & to &Amp; for proper debug display");
1116 content = StringUtils.replace(content, "&", "&amp;");
1117 writer.flush();
1118 }
1119 catch (TransformerException ex)
1120 {
1121 ex.printStackTrace();
1122 return null;
1123 }
1124 return content;
1125 }
1126
1127 protected synchronized Document getDoc(String docName) throws Exception
1128 {
1129 File xslt_file = new File(docName);
1130
1131 FileReader reader = new FileReader(xslt_file);
1132 InputSource xml_source = new InputSource(reader);
1133 this.parser.parse(xml_source);
1134 Document doc = this.parser.getDocument();
1135
1136 return doc;
1137 }
1138
1139 protected String getXSLTFilename(String action, String subaction) {
1140 String name = null;
1141 if (!subaction.equals(""))
1142 {
1143 String key = action + ":" + subaction;
1144 name = this.xslt_map.get(key);
1145 }
1146 // try the action by itself
1147 if (name == null)
1148 {
1149 name = this.xslt_map.get(action);
1150 }
1151 if (name == null)
1152 {
1153 // so we can reandomly create any named page
1154 if (action.equals("p") && !subaction.equals(""))
1155 {
1156 // TODO: pages/ won't work for interface other than default!!
1157 name = "pages/" + subaction + ".xsl";
1158 }
1159
1160 }
1161 return name;
1162 }
1163
1164
1165 protected Document getXSLTDocument(String action, String subaction, String collection)
1166 {
1167 String name = getXSLTFilename(action, subaction);
1168 Document finalDoc = null;
1169 if(name != null)
1170 {
1171 // this finds all the stylesheets named "name" and merges them together, in the order of
1172 // base interface, current interface, site, collection - the latter overriding the former.
1173 // templates with the same name will replace earlier versions
1174 finalDoc = GSXSLT.mergedXSLTDocumentCascade(name, (String) this.config_params.get(GSConstants.SITE_NAME), collection, (String) this.config_params.get(GSConstants.INTERFACE_NAME), base_interfaces, _debug);
1175 }
1176 return finalDoc;
1177 }
1178
1179 // returns the path to the gslib.xsl file that is applicable for the current interface
1180 protected String getGSLibXSLFilename()
1181 {
1182 return GSFile.xmlTransformDir(GSFile.interfaceHome(GlobalProperties.getGSDL3Home(), (String) this.config_params.get(GSConstants.INTERFACE_NAME))) + File.separatorChar + "gslib.xsl";
1183 }
1184
1185 // Call this when a FileNotFoundException could be thrown when loading an xsl (xml) file.
1186 // Returns an error xhtml page indicating which xsl (or other xml) file is missing.
1187 protected Document fileNotFoundErrorPage(String filenameMessage)
1188 {
1189 String errorMessage = "ERROR missing file: " + filenameMessage;
1190 Element errPage = XMLTransformer.constructErrorXHTMLPage(errorMessage);
1191 logger.error(errorMessage);
1192 return errPage.getOwnerDocument();
1193 }
1194}
Note: See TracBrowser for help on using the repository browser.