paulb@31 | 1 | #!/usr/bin/env python |
paulb@31 | 2 | |
paulb@31 | 3 | "A WebStack questionnaire application." |
paulb@31 | 4 | |
paulb@31 | 5 | import WebStack.Generic |
paulb@31 | 6 | import XSLForms.Fields |
paulb@31 | 7 | import XSLForms.Output |
paulb@31 | 8 | import XSLOutput |
paulb@31 | 9 | import libxml2dom |
paulb@31 | 10 | import os |
paulb@31 | 11 | |
paulb@31 | 12 | # NOTE: Move these functions into a common utility library. |
paulb@31 | 13 | |
paulb@31 | 14 | def add_elements(positions, element_name, element_parent_name=None): |
paulb@31 | 15 | if not positions: |
paulb@31 | 16 | return |
paulb@31 | 17 | for position in positions: |
paulb@31 | 18 | if element_parent_name: |
paulb@31 | 19 | parent_elements = position.xpath(element_parent_name) |
paulb@31 | 20 | if not parent_elements: |
paulb@31 | 21 | parent_element = position.ownerDocument.createElementNS(None, element_parent_name) |
paulb@31 | 22 | position.appendChild(parent_element) |
paulb@31 | 23 | else: |
paulb@31 | 24 | parent_element = parent_elements[0] |
paulb@31 | 25 | else: |
paulb@31 | 26 | parent_element = position |
paulb@31 | 27 | parent_element.appendChild(position.ownerDocument.createElementNS(None, element_name)) |
paulb@31 | 28 | |
paulb@31 | 29 | def remove_elements(positions): |
paulb@31 | 30 | if not positions: |
paulb@31 | 31 | return |
paulb@31 | 32 | for position in positions: |
paulb@31 | 33 | position.parentNode.removeChild(position) |
paulb@31 | 34 | |
paulb@31 | 35 | # NOTE: This is so much like ConfiguratorResource that some common parts could |
paulb@31 | 36 | # NOTE: be extracted. |
paulb@31 | 37 | |
paulb@31 | 38 | class QuestionnaireEditorResource: |
paulb@31 | 39 | |
paulb@31 | 40 | "A resource providing a questionnaire editor." |
paulb@31 | 41 | |
paulb@31 | 42 | resource_dir = os.path.join(os.path.split(__file__)[0], "Resources") |
paulb@31 | 43 | encoding = "utf-8" |
paulb@31 | 44 | |
paulb@31 | 45 | def respond(self, trans): |
paulb@31 | 46 | |
paulb@31 | 47 | # Produce stylesheet. |
paulb@31 | 48 | |
paulb@31 | 49 | if trans.get_path_info().split("/")[-1] == "styles.css": |
paulb@31 | 50 | trans.set_content_type(WebStack.Generic.ContentType("text/css", self.encoding)) |
paulb@31 | 51 | out = trans.get_response_stream() |
paulb@31 | 52 | f = open(os.path.join(self.resource_dir, "styles.css"), "rb") |
paulb@31 | 53 | out.write(f.read()) |
paulb@31 | 54 | f.close() |
paulb@31 | 55 | raise WebStack.Generic.EndOfResponse |
paulb@31 | 56 | |
paulb@31 | 57 | global XSLForms # NOTE: Strangely required to avoid UnboundLocalError! |
paulb@31 | 58 | method = trans.get_request_method() |
paulb@31 | 59 | |
paulb@31 | 60 | # Only obtain field information according to the stated method. |
paulb@31 | 61 | |
paulb@31 | 62 | if method == "GET": |
paulb@31 | 63 | fields = XSLForms.Fields.Fields(encoding="iso-8859-1", values_are_lists=1) |
paulb@31 | 64 | parameters = trans.get_fields_from_path() |
paulb@31 | 65 | documents = fields.make_documents(parameters.items()) |
paulb@31 | 66 | elif method == "POST": |
paulb@31 | 67 | fields = XSLForms.Fields.Fields(encoding=self.encoding, values_are_lists=1) |
paulb@31 | 68 | parameters = trans.get_fields_from_body(self.encoding) |
paulb@31 | 69 | documents = fields.make_documents(parameters.items()) |
paulb@31 | 70 | else: |
paulb@31 | 71 | trans.set_response_code(405) |
paulb@31 | 72 | raise WebStack.Generic.EndOfResponse |
paulb@31 | 73 | |
paulb@31 | 74 | # Ensure the presence of a document. |
paulb@31 | 75 | |
paulb@31 | 76 | if documents.has_key("questionnaire"): |
paulb@31 | 77 | questionnaire = documents["questionnaire"] |
paulb@31 | 78 | else: |
paulb@31 | 79 | questionnaire = fields.new_instance("questionnaire") |
paulb@31 | 80 | |
paulb@31 | 81 | # Add and remove elements according to the selectors found. |
paulb@31 | 82 | |
paulb@31 | 83 | selectors = fields.get_selectors(parameters.items(), documents) |
paulb@31 | 84 | remove_elements(selectors.get("remove-question")) |
paulb@31 | 85 | add_elements(selectors.get("add-choice"), "choice") |
paulb@31 | 86 | remove_elements(selectors.get("remove-choice")) |
paulb@31 | 87 | |
paulb@31 | 88 | # Add questions using the normal request parameter. |
paulb@31 | 89 | |
paulb@31 | 90 | if parameters.has_key("add-question"): |
paulb@31 | 91 | new_question = questionnaire.ownerDocument.createElementNS(None, "question") |
paulb@31 | 92 | questionnaire.xpath("questionnaire")[0].appendChild(new_question) |
paulb@31 | 93 | |
paulb@31 | 94 | # Send a response according to certain parameters. |
paulb@31 | 95 | # When exported, an XML version of the data is returned. |
paulb@31 | 96 | |
paulb@31 | 97 | if parameters.has_key("export"): |
paulb@31 | 98 | trans.set_content_type(WebStack.Generic.ContentType("text/xml", self.encoding)) |
paulb@31 | 99 | libxml2dom.toStream(questionnaire, trans.get_response_stream(), trans.get_response_stream_encoding()) |
paulb@31 | 100 | |
paulb@31 | 101 | # When not exported, the data is transformed to produce a normal Web |
paulb@31 | 102 | # page. |
paulb@31 | 103 | |
paulb@31 | 104 | else: |
paulb@31 | 105 | |
paulb@31 | 106 | # Start the response. |
paulb@31 | 107 | |
paulb@31 | 108 | trans.set_content_type(WebStack.Generic.ContentType("text/html", self.encoding)) |
paulb@31 | 109 | |
paulb@31 | 110 | # Ensure that an output stylesheet exists. |
paulb@31 | 111 | |
paulb@31 | 112 | trans_xsl = os.path.join(self.resource_dir, "question_output.xsl") |
paulb@31 | 113 | template_xml = os.path.join(self.resource_dir, "question_template.xhtml") |
paulb@31 | 114 | |
paulb@31 | 115 | if not os.path.exists(trans_xsl) or \ |
paulb@31 | 116 | os.path.getmtime(trans_xsl) < os.path.getmtime(template_xml): |
paulb@31 | 117 | |
paulb@31 | 118 | import XSLForms.Prepare |
paulb@31 | 119 | template_xml = os.path.join(self.resource_dir, "question_template.xhtml") |
paulb@31 | 120 | XSLForms.Prepare.make_stylesheet(template_xml, trans_xsl) |
paulb@31 | 121 | |
paulb@31 | 122 | # Complete the response. |
paulb@31 | 123 | |
paulb@31 | 124 | proc = XSLOutput.Processor([trans_xsl]) |
paulb@31 | 125 | proc.send_output(trans.get_response_stream(), trans.get_response_stream_encoding(), |
paulb@31 | 126 | questionnaire) |
paulb@31 | 127 | |
paulb@31 | 128 | # vim: tabstop=4 expandtab shiftwidth=4 |