paulb@235 | 1 | #!/usr/bin/env python |
paulb@235 | 2 | |
paulb@235 | 3 | "A job candidate editing application." |
paulb@235 | 4 | |
paulb@235 | 5 | import WebStack.Generic |
paulb@405 | 6 | from WebStack.Repositories.Directory import DirectoryRepository |
paulb@353 | 7 | import XSLForms.Resources.WebResources |
paulb@235 | 8 | import XSLForms.Utils |
paulb@235 | 9 | import os |
paulb@402 | 10 | import libxml2dom |
paulb@235 | 11 | |
paulb@235 | 12 | # Site map imports. |
paulb@235 | 13 | |
paulb@235 | 14 | from WebStack.Resources.ResourceMap import MapResource |
paulb@235 | 15 | from WebStack.Resources.Static import DirectoryResource |
paulb@235 | 16 | |
paulb@235 | 17 | # Resource classes. |
paulb@235 | 18 | |
paulb@402 | 19 | class AdminResource(XSLForms.Resources.WebResources.XSLFormsResource): |
paulb@402 | 20 | |
paulb@402 | 21 | "A resource providing administration facilities for job candidate profiles." |
paulb@402 | 22 | |
paulb@402 | 23 | resource_dir = os.path.join(os.path.split(__file__)[0], "Resources") |
paulb@402 | 24 | encoding = "utf-8" |
paulb@402 | 25 | template_resources = { |
paulb@402 | 26 | "admin" : ("admin_template.xhtml", "admin_output.xsl") |
paulb@402 | 27 | } |
paulb@402 | 28 | init_resources = { |
paulb@402 | 29 | "admin" : ("admin_template.xhtml", "admin_input.xsl") |
paulb@402 | 30 | } |
paulb@402 | 31 | |
paulb@402 | 32 | def respond_to_form(self, trans, form): |
paulb@402 | 33 | |
paulb@402 | 34 | """ |
paulb@402 | 35 | Respond to a request having the given transaction 'trans' and the given |
paulb@402 | 36 | 'form' information. |
paulb@402 | 37 | """ |
paulb@402 | 38 | |
paulb@402 | 39 | parameters = form.get_parameters() |
paulb@402 | 40 | documents = form.get_documents() |
paulb@402 | 41 | |
paulb@402 | 42 | # Get the "show" and "edit" resource paths. |
paulb@402 | 43 | # NOTE: These should be obtained from the site map. |
paulb@402 | 44 | |
paulb@402 | 45 | parts = trans.get_processed_virtual_path_info(self.path_encoding).split("/") |
paulb@402 | 46 | show_path = "/".join(parts[:-1] + ["show"]) |
paulb@402 | 47 | edit_path = "/".join(parts[:-1] + ["edit"]) |
paulb@402 | 48 | |
paulb@402 | 49 | # Ensure the presence of a document. |
paulb@402 | 50 | |
paulb@402 | 51 | form_is_new = 0 |
paulb@402 | 52 | if documents.has_key("admin"): |
paulb@402 | 53 | admin = documents["admin"] |
paulb@402 | 54 | else: |
paulb@402 | 55 | admin = form.new_instance("admin") |
paulb@402 | 56 | form_is_new = 1 |
paulb@402 | 57 | |
paulb@402 | 58 | # Redirect if one of the CVs is to be shown or edited. |
paulb@402 | 59 | |
paulb@402 | 60 | selectors = form.get_selectors() |
paulb@402 | 61 | if selectors.has_key("show"): |
paulb@402 | 62 | name = selectors["show"][0].getAttribute("name") |
paulb@402 | 63 | self._redirect(trans, show_path + "?name=%s" % name.encode(self.path_encoding)) |
paulb@402 | 64 | elif selectors.has_key("edit"): |
paulb@402 | 65 | name = selectors["edit"][0].getAttribute("name") |
paulb@402 | 66 | self._redirect(trans, edit_path + "?name=%s" % name.encode(self.path_encoding)) |
paulb@402 | 67 | |
paulb@402 | 68 | # Add and remove elements according to the selectors found. |
paulb@402 | 69 | |
paulb@402 | 70 | XSLForms.Utils.remove_elements(selectors.get("remove")) |
paulb@402 | 71 | XSLForms.Utils.add_elements(selectors.get("new"), "cv", "cvs") |
paulb@402 | 72 | |
paulb@402 | 73 | # Transform, adding enumerations/ranges. |
paulb@402 | 74 | |
paulb@402 | 75 | init_xsl = self.prepare_initialiser("admin") |
paulb@402 | 76 | admin = self.get_result([init_xsl], admin) |
paulb@402 | 77 | |
paulb@402 | 78 | # Synchronise the session with the CVs found. |
paulb@402 | 79 | |
paulb@402 | 80 | cvs = admin.xpath("/admin/cvs")[0] |
paulb@405 | 81 | session = DirectoryRepository(os.path.join(self.resource_dir, "candidates")) |
paulb@405 | 82 | #session = trans.get_session() |
paulb@402 | 83 | for key in session.keys(): |
paulb@402 | 84 | if key.startswith("candidate-"): |
paulb@402 | 85 | name = key[len("candidate-"):] |
paulb@402 | 86 | # NOTE: Apostrophes not quoted. |
paulb@402 | 87 | if not cvs.xpath("cv[@name = '%s']" % name): |
paulb@402 | 88 | if form_is_new: |
paulb@402 | 89 | cv = admin.createElement("cv") |
paulb@402 | 90 | cv.setAttribute("name", name) |
paulb@402 | 91 | cvs.appendChild(cv) |
paulb@402 | 92 | else: |
paulb@402 | 93 | del session[key] |
paulb@402 | 94 | |
paulb@402 | 95 | # Start the response. |
paulb@402 | 96 | |
paulb@402 | 97 | trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding)) |
paulb@402 | 98 | |
paulb@402 | 99 | # Ensure that an output stylesheet exists. |
paulb@402 | 100 | |
paulb@402 | 101 | trans_xsl = self.prepare_output("admin") |
paulb@402 | 102 | stylesheet_parameters = {} |
paulb@402 | 103 | |
paulb@402 | 104 | # Complete the response. |
paulb@402 | 105 | |
paulb@402 | 106 | self.send_output(trans, [trans_xsl], admin, stylesheet_parameters) |
paulb@402 | 107 | |
paulb@402 | 108 | def _redirect(self, trans, path): |
paulb@402 | 109 | trans.set_response_code(302) |
paulb@402 | 110 | trans.set_header_value("Location", path) |
paulb@402 | 111 | raise WebStack.Generic.EndOfResponse |
paulb@402 | 112 | |
paulb@402 | 113 | class DisplayResource(XSLForms.Resources.WebResources.XSLFormsResource): |
paulb@402 | 114 | |
paulb@402 | 115 | "A resource providing editing facilities for a job candidate profile." |
paulb@402 | 116 | |
paulb@402 | 117 | resource_dir = os.path.join(os.path.split(__file__)[0], "Resources") |
paulb@402 | 118 | encoding = "utf-8" |
paulb@402 | 119 | template_resources = { |
paulb@402 | 120 | "candidate_display" : ("candidate_display_template.xhtml", "candidate_display_output.xsl") |
paulb@402 | 121 | } |
paulb@402 | 122 | init_resources = { |
paulb@402 | 123 | "candidate" : ("candidate_template.xhtml", "candidate_input.xsl") |
paulb@402 | 124 | } |
paulb@402 | 125 | document_resources = { |
paulb@402 | 126 | "status" : "candidate_status.xml" |
paulb@402 | 127 | } |
paulb@402 | 128 | |
paulb@402 | 129 | def respond_to_form(self, trans, form): |
paulb@402 | 130 | |
paulb@402 | 131 | """ |
paulb@402 | 132 | Respond to a request having the given transaction 'trans' and the given |
paulb@402 | 133 | 'form' information. |
paulb@402 | 134 | """ |
paulb@402 | 135 | |
paulb@402 | 136 | parameters = form.get_parameters() |
paulb@402 | 137 | documents = form.get_documents() |
paulb@402 | 138 | fields = trans.get_fields_from_path() |
paulb@402 | 139 | name = fields.get("name", [u"None"])[0] |
paulb@402 | 140 | |
paulb@402 | 141 | # Ensure the presence of a document. |
paulb@402 | 142 | |
paulb@402 | 143 | if documents.has_key("candidate"): |
paulb@402 | 144 | candidate = documents["candidate"] |
paulb@402 | 145 | else: |
paulb@405 | 146 | session = DirectoryRepository(os.path.join(self.resource_dir, "candidates")) |
paulb@405 | 147 | #session = trans.get_session(create=0) |
paulb@402 | 148 | if session is None or not session.has_key("candidate-%s" % name.encode("utf-8")): |
paulb@402 | 149 | candidate = form.new_instance("candidate") |
paulb@402 | 150 | else: |
paulb@402 | 151 | candidate = libxml2dom.parseString(session["candidate-%s" % name.encode("utf-8")]) |
paulb@402 | 152 | |
paulb@402 | 153 | # Transform, adding enumerations/ranges. |
paulb@402 | 154 | |
paulb@402 | 155 | init_xsl = self.prepare_initialiser("candidate") |
paulb@402 | 156 | status_xml = self.prepare_document("status") |
paulb@402 | 157 | candidate = self.get_result([init_xsl], candidate, references={"status" : status_xml}) |
paulb@402 | 158 | |
paulb@402 | 159 | # Start the response. |
paulb@402 | 160 | |
paulb@402 | 161 | trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding)) |
paulb@402 | 162 | |
paulb@402 | 163 | # Ensure that an output stylesheet exists. |
paulb@402 | 164 | # Handle the "show" operation. |
paulb@402 | 165 | |
paulb@402 | 166 | trans_xsl = self.prepare_output("candidate_display") |
paulb@402 | 167 | stylesheet_parameters = {} |
paulb@402 | 168 | |
paulb@402 | 169 | # Complete the response. |
paulb@402 | 170 | |
paulb@402 | 171 | self.send_output(trans, [trans_xsl], candidate, stylesheet_parameters) |
paulb@402 | 172 | |
paulb@353 | 173 | class CandidateResource(XSLForms.Resources.WebResources.XSLFormsResource): |
paulb@235 | 174 | |
paulb@235 | 175 | "A resource providing editing facilities for a job candidate profile." |
paulb@235 | 176 | |
paulb@235 | 177 | resource_dir = os.path.join(os.path.split(__file__)[0], "Resources") |
paulb@235 | 178 | encoding = "utf-8" |
paulb@235 | 179 | template_resources = { |
paulb@249 | 180 | "candidate" : ("candidate_template.xhtml", "candidate_output.xsl") |
paulb@235 | 181 | } |
paulb@249 | 182 | init_resources = { |
paulb@249 | 183 | "candidate" : ("candidate_template.xhtml", "candidate_input.xsl") |
paulb@235 | 184 | } |
paulb@235 | 185 | document_resources = { |
paulb@235 | 186 | "status" : "candidate_status.xml" |
paulb@235 | 187 | } |
paulb@235 | 188 | |
paulb@235 | 189 | def respond_to_form(self, trans, form): |
paulb@235 | 190 | |
paulb@235 | 191 | """ |
paulb@235 | 192 | Respond to a request having the given transaction 'trans' and the given |
paulb@235 | 193 | 'form' information. |
paulb@235 | 194 | """ |
paulb@235 | 195 | |
paulb@235 | 196 | parameters = form.get_parameters() |
paulb@235 | 197 | documents = form.get_documents() |
paulb@402 | 198 | fields = trans.get_fields_from_path() |
paulb@402 | 199 | name = fields.get("name", [u"None"])[0] |
paulb@402 | 200 | |
paulb@402 | 201 | # Get the "show" resource path. |
paulb@402 | 202 | # NOTE: This should be obtained from the site map. |
paulb@402 | 203 | |
paulb@402 | 204 | parts = trans.get_processed_virtual_path_info(self.path_encoding).split("/") |
paulb@402 | 205 | show_path = "/".join(parts[:-1] + ["show"]) |
paulb@402 | 206 | admin_path = "/".join(parts[:-1] + [""]) |
paulb@235 | 207 | |
paulb@235 | 208 | # Ensure the presence of a document. |
paulb@235 | 209 | |
paulb@235 | 210 | if documents.has_key("candidate"): |
paulb@235 | 211 | candidate = documents["candidate"] |
paulb@235 | 212 | else: |
paulb@405 | 213 | session = DirectoryRepository(os.path.join(self.resource_dir, "candidates")) |
paulb@405 | 214 | #session = trans.get_session(create=0) |
paulb@402 | 215 | if session is None or not session.has_key("candidate-%s" % name.encode("utf-8")): |
paulb@402 | 216 | candidate = form.new_instance("candidate") |
paulb@402 | 217 | else: |
paulb@402 | 218 | candidate = libxml2dom.parseString(session["candidate-%s" % name.encode("utf-8")]) |
paulb@235 | 219 | |
paulb@235 | 220 | # Add and remove elements according to the selectors found. |
paulb@235 | 221 | |
paulb@235 | 222 | selectors = form.get_selectors() |
paulb@235 | 223 | XSLForms.Utils.remove_elements(selectors.get("remove")) |
paulb@235 | 224 | XSLForms.Utils.add_elements(selectors.get("add-skill"), "skill", "skills") |
paulb@235 | 225 | XSLForms.Utils.add_elements(selectors.get("add-qualification"), "qualification", "qualifications") |
paulb@235 | 226 | XSLForms.Utils.add_elements(selectors.get("add-employment"), "employment", "experience") |
paulb@235 | 227 | |
paulb@235 | 228 | # Transform, adding enumerations/ranges. |
paulb@235 | 229 | |
paulb@249 | 230 | init_xsl = self.prepare_initialiser("candidate") |
paulb@235 | 231 | status_xml = self.prepare_document("status") |
paulb@249 | 232 | candidate = self.get_result([init_xsl], candidate, references={"status" : status_xml}) |
paulb@235 | 233 | |
paulb@402 | 234 | # Redirect if the CV is to be shown. |
paulb@402 | 235 | |
paulb@402 | 236 | if parameters.has_key("show"): |
paulb@402 | 237 | |
paulb@402 | 238 | # Save the candidate information. |
paulb@402 | 239 | |
paulb@405 | 240 | session = DirectoryRepository(os.path.join(self.resource_dir, "candidates")) |
paulb@405 | 241 | #session = trans.get_session() |
paulb@402 | 242 | session["candidate-%s" % name.encode("utf-8")] = candidate.toString() |
paulb@402 | 243 | self._redirect(trans, show_path + "?name=%s" % name.encode(self.path_encoding)) |
paulb@402 | 244 | |
paulb@402 | 245 | # Redirect if the administration interface is to be used. |
paulb@402 | 246 | |
paulb@402 | 247 | elif parameters.has_key("admin"): |
paulb@402 | 248 | |
paulb@402 | 249 | # Save the candidate information. |
paulb@402 | 250 | |
paulb@405 | 251 | session = DirectoryRepository(os.path.join(self.resource_dir, "candidates")) |
paulb@405 | 252 | #session = trans.get_session() |
paulb@402 | 253 | session["candidate-%s" % name.encode("utf-8")] = candidate.toString() |
paulb@402 | 254 | self._redirect(trans, admin_path) |
paulb@402 | 255 | |
paulb@235 | 256 | # Start the response. |
paulb@235 | 257 | |
paulb@235 | 258 | trans.set_content_type(WebStack.Generic.ContentType("application/xhtml+xml", self.encoding)) |
paulb@235 | 259 | |
paulb@235 | 260 | # Ensure that an output stylesheet exists. |
paulb@235 | 261 | |
paulb@402 | 262 | trans_xsl = self.prepare_output("candidate") |
paulb@402 | 263 | stylesheet_parameters = {} |
paulb@235 | 264 | |
paulb@235 | 265 | # Complete the response. |
paulb@235 | 266 | |
paulb@235 | 267 | self.send_output(trans, [trans_xsl], candidate, stylesheet_parameters) |
paulb@235 | 268 | |
paulb@402 | 269 | def _redirect(self, trans, path): |
paulb@402 | 270 | trans.set_response_code(302) |
paulb@402 | 271 | trans.set_header_value("Location", path) |
paulb@402 | 272 | raise WebStack.Generic.EndOfResponse |
paulb@402 | 273 | |
paulb@235 | 274 | # Site map initialisation. |
paulb@235 | 275 | |
paulb@235 | 276 | def get_site(): |
paulb@235 | 277 | |
paulb@235 | 278 | "Return a simple Web site resource." |
paulb@235 | 279 | |
paulb@235 | 280 | # Get the main resource and the directory used by the application. |
paulb@235 | 281 | |
paulb@235 | 282 | candidate_resource = CandidateResource() |
paulb@402 | 283 | display_resource = DisplayResource() |
paulb@402 | 284 | admin_resource = AdminResource() |
paulb@235 | 285 | directory = candidate_resource.resource_dir |
paulb@235 | 286 | |
paulb@235 | 287 | # Make a simple Web site. |
paulb@235 | 288 | |
paulb@235 | 289 | resource = MapResource({ |
paulb@402 | 290 | "edit" : candidate_resource, |
paulb@402 | 291 | "show" : display_resource, |
paulb@402 | 292 | "" : admin_resource |
paulb@235 | 293 | }) |
paulb@235 | 294 | |
paulb@235 | 295 | return resource |
paulb@235 | 296 | |
paulb@235 | 297 | # vim: tabstop=4 expandtab shiftwidth=4 |