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@638 | 7 | from XSLForms.Resources.WebResources import \ |
paulb@638 | 8 | XSLFormsResource, input, output, resources, prepare_resources as xslforms_prepare_resources |
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 |
paul@684 | 15 | from WebStack.Resources.Selectors import EncodingSelector, StoreSelector |
paulb@235 | 16 | from WebStack.Resources.Static import DirectoryResource |
paulb@235 | 17 | |
paulb@560 | 18 | # Configuration setting. |
paulb@560 | 19 | |
paulb@560 | 20 | encoding = "utf-8" |
paulb@560 | 21 | |
paulb@235 | 22 | # Resource classes. |
paulb@235 | 23 | |
paulb@634 | 24 | class AdminResource(XSLFormsResource): |
paulb@402 | 25 | |
paulb@402 | 26 | "A resource providing administration facilities for job candidate profiles." |
paulb@402 | 27 | |
paulb@634 | 28 | resource_dir = resources(__file__) |
paulb@402 | 29 | template_resources = { |
paulb@634 | 30 | "admin" : output("admin_template.xhtml") |
paulb@402 | 31 | } |
paulb@402 | 32 | init_resources = { |
paulb@634 | 33 | "admin" : input("admin_template.xhtml") |
paulb@402 | 34 | } |
paulb@402 | 35 | |
paulb@638 | 36 | def select_activity(self, trans, form): |
paulb@642 | 37 | form.set_activity("admin") |
paulb@402 | 38 | |
paulb@638 | 39 | def create_document(self, trans, form): |
paulb@638 | 40 | is_new = XSLFormsResource.create_document(self, trans, form) |
paulb@638 | 41 | trans.get_attributes()["form_is_new"] = is_new |
paulb@638 | 42 | return is_new |
paulb@402 | 43 | |
paulb@638 | 44 | def respond_to_input(self, trans, form): |
paulb@402 | 45 | |
paulb@402 | 46 | # Get the "show" and "edit" resource paths. |
paulb@402 | 47 | # NOTE: These should be obtained from the site map. |
paulb@402 | 48 | |
paulb@560 | 49 | vpath = trans.get_processed_virtual_path_info() |
paulb@560 | 50 | show_path = trans.get_path_without_info() + trans.update_path(vpath, "show") |
paulb@560 | 51 | edit_path = trans.get_path_without_info() + trans.update_path(vpath, "edit") |
paulb@402 | 52 | |
paulb@402 | 53 | # Redirect if one of the CVs is to be shown or edited. |
paulb@402 | 54 | |
paulb@402 | 55 | selectors = form.get_selectors() |
paulb@402 | 56 | if selectors.has_key("show"): |
paulb@402 | 57 | name = selectors["show"][0].getAttribute("name") |
paul@684 | 58 | if name: |
paul@684 | 59 | trans.redirect(trans.encode_path(show_path) + |
paul@684 | 60 | "?name=%s" % trans.encode_path(name)) |
paulb@402 | 61 | elif selectors.has_key("edit"): |
paulb@402 | 62 | name = selectors["edit"][0].getAttribute("name") |
paul@684 | 63 | if name: |
paul@684 | 64 | trans.redirect(trans.encode_path(edit_path) + |
paul@684 | 65 | "?name=%s" % trans.encode_path(name)) |
paulb@402 | 66 | |
paul@684 | 67 | # Add and remove elements (and documents) according to the selectors found. |
paulb@402 | 68 | |
paul@684 | 69 | self.remove_documents(trans, selectors.get("remove")) |
paulb@638 | 70 | self.remove_elements(selectors.get("remove")) |
paulb@638 | 71 | self.add_elements(selectors.get("new"), "cv", "cvs") |
paulb@402 | 72 | |
paulb@638 | 73 | def respond_to_document(self, trans, form): |
paulb@402 | 74 | |
paulb@642 | 75 | admin = form.get_document() |
paul@684 | 76 | repository = trans.get_attributes()["store"] |
paulb@402 | 77 | |
paulb@408 | 78 | # Synchronise the repository with the CVs found. |
paulb@402 | 79 | |
paulb@402 | 80 | cvs = admin.xpath("/admin/cvs")[0] |
paul@684 | 81 | for key in repository.keys(): |
paulb@402 | 82 | if key.startswith("candidate-"): |
paulb@402 | 83 | name = key[len("candidate-"):] |
paulb@402 | 84 | # NOTE: Apostrophes not quoted. |
paulb@402 | 85 | if not cvs.xpath("cv[@name = '%s']" % name): |
paulb@638 | 86 | if trans.get_attributes()["form_is_new"]: |
paulb@402 | 87 | cv = admin.createElement("cv") |
paulb@402 | 88 | cv.setAttribute("name", name) |
paulb@402 | 89 | cvs.appendChild(cv) |
paulb@402 | 90 | else: |
paul@684 | 91 | del repository[key] |
paul@684 | 92 | |
paul@684 | 93 | def remove_documents(self, trans, selected): |
paul@684 | 94 | repository = trans.get_attributes()["store"] |
paul@684 | 95 | |
paul@684 | 96 | for element in selected or []: |
paul@684 | 97 | name = element.getAttribute("name") |
paul@684 | 98 | docname = "candidate-%s" % name |
paul@684 | 99 | if repository.has_key(docname): |
paul@684 | 100 | del repository[docname] |
paulb@402 | 101 | |
paulb@638 | 102 | class CandidateUtils: |
paulb@638 | 103 | |
paulb@638 | 104 | "Methods used by candidate-related resources." |
paulb@638 | 105 | |
paulb@638 | 106 | def select_activity(self, trans, form): |
paulb@642 | 107 | form.set_activity("candidate") |
paulb@402 | 108 | |
paulb@638 | 109 | def create_document(self, trans, form): |
paulb@638 | 110 | documents = form.get_documents() |
paulb@638 | 111 | fields = trans.get_fields_from_path() |
paulb@638 | 112 | name = fields.get("name", [u"None"])[0] |
paul@684 | 113 | repository = trans.get_attributes()["store"] |
paulb@402 | 114 | |
paulb@638 | 115 | # Ensure the presence of a document. |
paulb@402 | 116 | |
paulb@638 | 117 | if documents.has_key("candidate"): |
paulb@642 | 118 | form.set_document(documents["candidate"]) |
paulb@638 | 119 | else: |
paul@684 | 120 | if repository is None or not repository.has_key("candidate-%s" % name): |
paulb@642 | 121 | form.set_document(form.new_instance("candidate")) |
paulb@638 | 122 | else: |
paul@684 | 123 | form.set_document(libxml2dom.parseString(repository["candidate-%s" % name])) |
paulb@402 | 124 | |
paulb@638 | 125 | def init_document(self, trans, form): |
paulb@638 | 126 | status_xml = self.prepare_document("status") |
paulb@638 | 127 | XSLFormsResource.init_document(self, trans, form, references={"status" : status_xml}) |
paulb@402 | 128 | |
paulb@638 | 129 | class DisplayResource(CandidateUtils, XSLFormsResource): |
paulb@402 | 130 | |
paulb@402 | 131 | "A resource providing editing facilities for a job candidate profile." |
paulb@402 | 132 | |
paulb@634 | 133 | resource_dir = resources(__file__) |
paulb@402 | 134 | template_resources = { |
paulb@638 | 135 | "candidate" : output("candidate_display_template.xhtml") |
paulb@402 | 136 | } |
paulb@402 | 137 | init_resources = { |
paulb@634 | 138 | "candidate" : input("candidate_template.xhtml") |
paulb@402 | 139 | } |
paulb@402 | 140 | document_resources = { |
paulb@402 | 141 | "status" : "candidate_status.xml" |
paulb@402 | 142 | } |
paulb@402 | 143 | |
paulb@638 | 144 | class CandidateResource(CandidateUtils, XSLFormsResource): |
paulb@235 | 145 | |
paulb@235 | 146 | "A resource providing editing facilities for a job candidate profile." |
paulb@235 | 147 | |
paulb@634 | 148 | resource_dir = resources(__file__) |
paulb@235 | 149 | template_resources = { |
paulb@634 | 150 | "candidate" : output("candidate_template.xhtml") |
paulb@235 | 151 | } |
paulb@249 | 152 | init_resources = { |
paulb@634 | 153 | "candidate" : input("candidate_template.xhtml") |
paulb@235 | 154 | } |
paulb@235 | 155 | document_resources = { |
paulb@235 | 156 | "status" : "candidate_status.xml" |
paulb@235 | 157 | } |
paulb@235 | 158 | |
paulb@638 | 159 | def respond_to_input(self, trans, form): |
paulb@498 | 160 | |
paulb@638 | 161 | # Add and remove elements according to the selectors found. |
paulb@235 | 162 | |
paulb@638 | 163 | selectors = form.get_selectors() |
paulb@638 | 164 | self.remove_elements(selectors.get("remove")) |
paulb@638 | 165 | self.add_elements(selectors.get("add-skill"), "skill", "skills") |
paulb@638 | 166 | self.add_elements(selectors.get("add-qualification"), "qualification", "qualifications") |
paulb@638 | 167 | self.add_elements(selectors.get("add-employment"), "employment", "experience") |
paulb@235 | 168 | |
paulb@638 | 169 | def respond_to_document(self, trans, form): |
paulb@638 | 170 | |
paulb@642 | 171 | candidate = form.get_document() |
paulb@235 | 172 | parameters = form.get_parameters() |
paul@684 | 173 | files = form.get_files() |
paulb@560 | 174 | fields = trans.get_fields_from_path() |
paulb@402 | 175 | name = fields.get("name", [u"None"])[0] |
paulb@402 | 176 | |
paul@684 | 177 | repository = trans.get_attributes()["store"] |
paul@684 | 178 | image_repository = trans.get_attributes()["images"] |
paul@684 | 179 | |
paul@684 | 180 | # Find uploaded pictures and place them in the image repository. |
paul@684 | 181 | |
paul@684 | 182 | for element in candidate.xpath("//*[@new-picture]"): |
paul@684 | 183 | filename = element.getAttributeNS(self.EMPTY_NAMESPACE, "new-picture") |
paul@684 | 184 | index = int(element.getAttributeNS(self.FILE_NAMESPACE, "new-picture")) |
paul@684 | 185 | |
paul@684 | 186 | if filename.find(".") != -1: |
paul@684 | 187 | suffix = filename.split(".")[-1] |
paul@684 | 188 | saved_filename = "candidate-%s.%s" % (name, suffix) |
paul@684 | 189 | image_repository[saved_filename] = files[index][1] # content |
paul@684 | 190 | element.setAttribute("picture", saved_filename) |
paul@684 | 191 | break |
paul@684 | 192 | |
paulb@402 | 193 | # Get the "show" resource path. |
paulb@402 | 194 | # NOTE: This should be obtained from the site map. |
paulb@402 | 195 | |
paulb@560 | 196 | vpath = trans.get_processed_virtual_path_info() |
paulb@560 | 197 | show_path = trans.get_path_without_info() + trans.update_path(vpath, "show") |
paulb@560 | 198 | admin_path = trans.get_path_without_info() + trans.update_path(vpath, "") |
paulb@235 | 199 | |
paulb@402 | 200 | # Redirect if the CV is to be shown. |
paulb@402 | 201 | |
paulb@402 | 202 | if parameters.has_key("show"): |
paulb@402 | 203 | |
paulb@402 | 204 | # Save the candidate information. |
paulb@402 | 205 | |
paul@684 | 206 | repository["candidate-%s" % name] = candidate.toString() |
paulb@560 | 207 | trans.redirect(trans.encode_path(show_path) + |
paulb@560 | 208 | "?name=%s" % trans.encode_path(name)) |
paulb@402 | 209 | |
paulb@402 | 210 | # Redirect if the administration interface is to be used. |
paulb@402 | 211 | |
paulb@402 | 212 | elif parameters.has_key("admin"): |
paulb@402 | 213 | |
paulb@402 | 214 | # Save the candidate information. |
paulb@402 | 215 | |
paul@684 | 216 | repository["candidate-%s" % name] = candidate.toString() |
paulb@560 | 217 | trans.redirect(trans.encode_path(admin_path)) |
paulb@402 | 218 | |
paulb@235 | 219 | # Site map initialisation. |
paulb@235 | 220 | |
paulb@513 | 221 | def get_site(fsencoding=None, use_cwd=0): |
paulb@235 | 222 | |
paulb@235 | 223 | "Return a simple Web site resource." |
paulb@235 | 224 | |
paulb@513 | 225 | if use_cwd: |
paulb@513 | 226 | resource_dir = os.getcwd() |
paulb@513 | 227 | else: |
paulb@634 | 228 | resource_dir = resources(__file__) |
paul@684 | 229 | |
paul@684 | 230 | images_dir = os.path.join(resource_dir, "candidate-images") |
paul@684 | 231 | |
paulb@498 | 232 | repository = DirectoryRepository(os.path.join(resource_dir, "candidates"), fsencoding) |
paul@684 | 233 | image_repository = DirectoryRepository(images_dir, fsencoding) |
paulb@498 | 234 | |
paulb@235 | 235 | # Get the main resource and the directory used by the application. |
paulb@235 | 236 | |
paul@684 | 237 | candidate_resource = CandidateResource() |
paul@684 | 238 | display_resource = DisplayResource() |
paul@684 | 239 | admin_resource = AdminResource() |
paulb@235 | 240 | |
paulb@235 | 241 | # Make a simple Web site. |
paulb@235 | 242 | |
paulb@235 | 243 | resource = MapResource({ |
paulb@402 | 244 | "edit" : candidate_resource, |
paulb@402 | 245 | "show" : display_resource, |
paul@684 | 246 | "" : admin_resource, |
paul@684 | 247 | "images" : DirectoryResource(images_dir, {"png" : "image/png", "jpg" : "image/jpeg", "jpeg" : "image/jpeg"}) |
paulb@235 | 248 | }) |
paulb@235 | 249 | |
paul@684 | 250 | # Return the site with some extra boilerplate. |
paul@684 | 251 | |
paul@684 | 252 | return \ |
paul@684 | 253 | StoreSelector( |
paul@684 | 254 | StoreSelector( |
paul@684 | 255 | EncodingSelector(resource, encoding), |
paul@684 | 256 | repository), |
paul@684 | 257 | image_repository, |
paul@684 | 258 | "images" |
paul@684 | 259 | ) |
paulb@235 | 260 | |
paulb@513 | 261 | # Resource preparation ahead of time - useful for making installations. |
paulb@513 | 262 | |
paulb@513 | 263 | def prepare_resources(): |
paulb@513 | 264 | for cls in [AdminResource, DisplayResource, CandidateResource]: |
paulb@638 | 265 | xslforms_prepare_resources(cls) |
paulb@513 | 266 | |
paulb@235 | 267 | # vim: tabstop=4 expandtab shiftwidth=4 |