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