1window.FS = window.FS || {}; 2window.FS.Profile = (function() { 3 var throwError = function(message) { 4 return function(error) { 5 var msg = 'An error occurred while ' + message; 6 if (error && error.message) { 7 msg += ': ' + error.message; 8 } else { 9 msg += '.'; 10 } 11 throw new Error(msg); 12 }; 13 }; 14 15 var getPath = function(path, obj) { 16 var dotPos = path.indexOf('.'); 17 var parPos = [path.indexOf('['), path.indexOf(']')]; 18 var head, tail, idx; 19 20 // If the first symbol we encounter is a '.' 21 if(dotPos > -1 && (dotPos < parPos[0] || parPos[0] === -1)) { 22 head = path.slice(0, dotPos); 23 tail = path.slice(dotPos + 1); 24 if(obj[head]) { 25 if(tail) { 26 return getPath(tail, obj[head]); 27 } 28 return obj[head]; 29 } 30 return undefined; 31 } 32 33 // If the first symbol we encounter is a '[' (and there is a ']' after) 34 if(parPos[0] > -1 && parPos[1] > -1 && parPos[0] > dotPos && parPos[0] < parPos[1]) { 35 head = path.slice(0, parPos[0]); 36 idx = parseInt(path.slice(parPos[0] + 1, parPos[1]), 10); 37 tail = path.slice(parPos[1] + 1); 38 if((idx || idx === 0) && obj[idx]) { 39 if(tail) { 40 return getPath(tail, obj[idx]); 41 } 42 return obj[idx]; 43 } 44 return undefined; 45 } 46 47 // Otherwise, return it by key 48 return obj && path && obj[path] || undefined; 49 }; 50 51 var timesSaved = 0; 52 53 var Profile = function() { 54 this._profile = null; 55 this._ucrn = null; 56 this._xref = []; 57 }; 58 59 Profile.prototype.addressFields = ['Flat', 'House', 'Street', 'Locality', 'Town', 'Post_Town', 'County', 'Postcode', 'UPRN', 'country_id']; 60 61 Profile.prototype.getFullAddress = function(){ 62 return this.get().then(function(userProfile){ 63 if(userProfile.address && userProfile.address_id){ 64 var root = userProfile.address[userProfile.address_id], 65 full_address_array = [root.flat , root.house, root.street, root.town, root.county, root.postcode, root.country]; 66 67 return _.reduce(full_address_array, function(prev, curr, index, orig){ 68 var comma = (prev == "") ? "" : ","; 69 return (curr !== "" ) ? (prev + comma + curr) : ""; 70 }, ""); 71 } 72 else{return "";} 73 }); 74 }; 75 76 Profile.prototype.getPostcode = function(){ 77 return this.get().then(function(userProfile){ 78 if(userProfile.address && userProfile.address_id){ 79 return userProfile.address[userProfile.address_id].postcode; 80 } 81 else{return "";} 82 }); 83 }; 84 85 Profile.prototype.getUPRN = function(){ 86 return this.get().then(function(userProfile){ 87 if(userProfile.address && userProfile.address_id){ 88 return userProfile.address[userProfile.address_id].uprn || ""; 89 } 90 else{return "";} 91 }); 92 }; 93 94 Profile.prototype._fetchXrefs = _.once(function() { 95 return FS.API.get('self-getxref') 96 .caught(throwError('fetching your cross-references')) 97 .then(function(data) { 98 if(data.id) { 99 this._ucrn = data.id; 100 } 101 102 if(data.references) { 103 this._xref = data.references; 104 } 105 106 return { 107 ucrn: this._ucrn, 108 xref: this._xref 109 }; 110 }) 111 .caught(function(){ 112 return ""; 113 }); 114 }); 115 116 Profile.prototype.transformDrupalErrors = function(error){ 117 switch (error) { 118 case !!error.match(/No current user id/g): 119 error = "You cannot change your email when registering an account"; 120 break; 121 } 122 123 return error; 124 }; 125 126 127 Profile.prototype._fetchProfile = _.memoize(function() { 128 return FS.Auth.isLoggedIn() 129 .then(function(session) { 130 if(!session.is_authenticated) { 131 throw new Error('You are not logged in.'); 132 } 133 134 return FS.API.get('Self-Sphinx'); 135 }) 136 .caught(throwError('fetching your profile data')) 137 .then(function(sphinx) { 138 if(parseInt(sphinx.is_error, 10)) { 139 throw new Error(sphinx.error); 140 } 141 142 var profile = sphinx.count && _.find(sphinx.values, function(p) { return p.id; }); 143 144 if(profile) { 145 this._profile = _.clone(profile); 146 } 147 148 return profile ? this._profile : false;
149 }) 150 .caught(throwError('reading your profile data')); 151 }, function() { return timesSaved; }); 152 153 /* 154 * @method FS.Profile.get 155 * @param <String> path: A JS expression describing the element of the profile to return. 156 * i.e. 'id' or 'account.status' 157 * @returns A Promise to get the profile item represented by path. If path is not specified, the whole profile is returned. 158 */ 159 Profile.prototype.get = function(path) { 160 return (this._profile ? Promise.resolve(this._profile) : this._fetchProfile()) 161 .caught(function(error) { 162 return {}; 163 }) 164 .then(function(profile) { 165 return _.clone(path ? getPath(path, profile) : profile); 166 }); 167 }; 168 169 /* 170 * @method FS.Profile.getForce 171 * Forces clearing profile result of previous fetching operation 172 * @param <String> path: A JS expression describing the element of the profile to return. 173 * i.e. 'id' or 'account.status' 174 * @returns A Promise to get the profile item represented by path. If path is not specified, the whole profile is returned. 175 */ 176 Profile.prototype.getForce = function (path) { 177 delete this._profile; 178 timesSaved++; 179 return this.get(path); 180 }; 181 182 /* 183 * @method FS.Profile.getXref 184 * @param <String|Object|Function> match: If a string, then match on type name. Otherwise, pass as comparator to _.where 185 * @param <String> path: An optional JS expression describing the element of the xref to return. 186 */ 187 Profile.prototype.getXref = function(match, path) { 188 var method = 'find'; 189 if(_.isString(match)) { 190 match = { type: match }; 191 } 192 if(_.isObject(match)) { 193 method += 'Where'; 194 } 195 196 return this._fetchXrefs() 197 .then(function(xrefs) { 198 if(!match && !path) return xrefs; 199 200 var xref = _[method](xrefs.xref || [], match); 201 202 return path ? getPath(path, xref) : xref; 203 }); 204 }; 205 206 Profile.prototype._getPrimary = function(args) { 207 return this.get(args.type + '_id') 208 .bind(this) 209 .then(function(id) { 210 var subPath = [args.type, id]; 211 if(args.path) { 212 subPath.push(args.path); 213 } 214 return Promise.props({ 215 "isInprofile": this.get(subPath.join('.')), 216 "session": FS.Auth.isLoggedIn() 217 }) 218 .bind(this) 219 .then(function(data){ 220 if (data.isInprofile) return data.isInprofile; 221 else { 222 return data.session ? this.createPath(data.session, args.session_path) : undefined; 223 } 224 225 }); 226 }); 227 }; 228 229 Profile.prototype.createPath = function(obj, keyPath) { 230 231 var result; 232 _.each(keyPath, function(key){ 233 var scope = result ? result : obj; 234 result = scope[key]; 235 }); 236 237 return result; 238 239 }; 240 241 /* 242 * @method FS.Profile.getPrimaryPhone 243 * @param <String> path: An optional JS expression describing the element of the phone to return. 244 */ 245 /* 246 * @method FS.Profile.getPrimaryAddress 247 * @param <String> path: An optional JS expression describing the element of the address to return. 248 */ 249 /* 250 * @method FS.Profile.getPrimaryEmail 251 * @param <String> path: An optional JS expression describing the element of the email to return. 252 * @param <array> session_path: An optional JS expression describing the path to data within the session object. 253 */ 254 255 var primaryDetails = [{ 256 "name": 'Phone' 257 }, { 258 "name": 'Address' 259 }, { 260 "name": 'Email', 261 "path": ["email"], 262 "session_path": ["user", "email"] 263 }]; 264 265 _.each(primaryDetails, function(details) { 266 Profile.prototype['getPrimary' + details.name] = _.partial(Profile.prototype._getPrimary, { 267 "type" : details.name.toLowerCase(), 268 "path" : details.path, 269 "session_path" : details.session_path 270 }); 271 }); 272 273 /* 274 * @method FS.Profile._fromProfileForm 275 * @param <Object> newProfile: Profile data from the profile wizard form 276 * @returns 277 */ 278 Profile.prototype._fromProfileForm = function(profile) { 279 // All of these values take hard-coded defaults 280 var output = { 281 contact_type: profile.contact_type || 'Individual', 282 preferred_language: 'en_GB', 283 'custom_Account_Information[Self-Service_Account]': 'yes' 284 }; 285 286 if (profile.AllowSharingAcrossOrganisation) { 287 output['custom_Additional_Details[Consent_To_Share_]'] = (profile.AllowSharingAcrossOrganisation === 'Yes') ? 1 : 0; 288 }; 289 290 if(profile.id) { 291 output.contact_id = profile.id; 292 } 293 294 if(profile.ucrn) { 295 output['custom_Account_Information[UCRN]'] = profile.ucrn; 296 } 297 298 // Set basic profile details 299 output.title = profile.title;
300 output.first_name = profile.first_name; 301 output.last_name = profile.last_name; 302 output.prefix_id = parseInt(profile.prefix_id, 10) || 0; 303 304 if(profile.gender_id === 'Male'){ 305 output.gender_id = 2; 306 } 307 else if(profile.gender_id === 'Female'){ 308 output.gender_id = 1; 309 } 310 else{ 311 output.gender_id = null; 312 } 313 314 // Set default contact method 315 output['custom_Additional_Details[Preferred_Contact_Method]'] = parseInt(profile.preferred_contact_method_id || profile.contact_method_id, 10) || 0; 316 317 // Set date of birth 318 if (profile.birth_date) { 319 output.birth_date = profile.birth_date.split(/[^0-9]/g).reverse().join('-'); 320 } 321 322 // Set phone numbers 323 var phoneCount = 0; 324 _.each([1, 2], function(i) { 325 var phone = profile['phone' + i]; 326 if(phone) { 327 var id = profile['phone' + i + '_id'] || phoneCount++; 328 var set = function(k, v) { 329 output['phone[' + id + '][' + k + ']'] = v; 330 }; 331 set('phone', phone); 332 set('phone_type_id', i === 2 ? 3 : 1); 333 set('is_billing', 1); 334 set('is_primary', 1); 335 set('location_type_id', 1); 336 } 337 }); 338 339 // Set email address 340 var emailId = profile.email_id || 0; 341 var setEmail = function(k, v) { 342 output['email[' + emailId + '][' + k + ']'] = v; 343 }; 344 setEmail('email', profile.email); 345 setEmail('is_billing', 1); 346 setEmail('is_primary', 1); 347 setEmail('location_type_id', 1); 348 setEmail('is_bulkmail', 0); 349 if(profile.email) output['email'] = profile.email; 350 351 // Set address 352 var addressId = profile.address_id || 0; 353 var setAddress = function(k, v) { 354 output['custom_Address[' + addressId + '][' + k + ']'] = v; 355 }; 356 _.each(['Flat', 'House', 'Street', 'Locality', 'Town', 'Post_Town', 'County', 'Postcode', 'UPRN', 'country_id'], function(field) { 357 var lower = field.toLowerCase(); 358 if(profile.address){ 359 return lower ? setAddress(field, profile.address[lower]) : ""; 360 } 361 else{ 362 return lower ? setAddress(field, profile[lower]) : ""; 363 } 364 }); 365 366 setAddress('Start_Date_', moment().format('YYYY-MM-DD')); 367 setAddress('is_primary', 1); 368 setAddress('Search_for_street_Postcode', [ 369 output.address_house, output.address_street, output.address_postcode 370 ].join('::')); 371 setAddress('location_type_id', 1); 372 setAddress('address_type', 1); 373 374 // Add extra fields for internal sites 375 if(profile.national_insurance) output['custom_Additional_Details[National_Insurance]'] = profile.national_insurance; 376 if(profile.manager) output['custom_Additional_Details[Manager]'] = profile.manager; 377 if(profile.department) output['custom_Additional_Details[Department]'] = profile.department; 378 if(profile.employee_number) output['custom_Additional_Details[Employee_Number]'] = profile.employee_number; 379 380 return output; 381 }; 382 383 Profile.prototype.save = function(newProfile, raw) { 384 if(!raw) { 385 newProfile = this._fromProfileForm(newProfile); 386 } 387 388 var apiToUse = 'self-' + (newProfile.contact_id ? 'updatecustomer' : 'createcustomer-userchain'); 389 return FS.API.postform(apiToUse, {"product": "self"} , newProfile) 390 .caught(throwError('saving your profile')) 391 .bind(this) 392 .then(function(result) { 393 if(parseInt(result.is_error, 10)) { 394 throw new Error(result.error); 395 } 396 timesSaved++; 397 return this._fetchProfile(); 398 }); 399 }; 400 return new Profile(); 401}());
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.