-
Notifications
You must be signed in to change notification settings - Fork 2
/
angular-bbis-services.js
639 lines (554 loc) · 28.6 KB
/
angular-bbis-services.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
angular.module('bbis.api', [])
.config(['$provide', function ($provide) {
/**
* Extends promises to have the .success() and .error() shortcuts
* so that wrappers/custom methods can be called in the same way as the
* methods returning $http calls directly.
*
* Credit to @naturalethic at stackoverflow.
* http://stackoverflow.com/questions/16797209/how-can-i-extend-q-promise-in-angularjs-with-a-succes-and-error
*/
$provide.decorator('$q', ['$delegate', function ($delegate) {
var defer = $delegate.defer;
$delegate.defer = function () {
var deferred = defer();
deferred.promise.success = function (fn) {
deferred.promise.then(fn);
return deferred.promise;
};
deferred.promise.error = function (fn) {
deferred.promise.then(null, fn);
return deferred.promise;
};
return deferred;
};
return $delegate;
}]);
}])
.factory('BbisApi', ['QueryService', 'UserService', 'CountryService', 'CodeTableService', 'ImageService', 'DonationService', function (QueryService, UserService, CountryService, CodeTableService, ImageService, DonationService) {
/**
* @class BbisApi wraps the other services so that they can be included at the same time instead of injecting them each individually.
* Note that they can still be injected individually.
*/
return {
QueryService: QueryService,
UserService: UserService,
CountryService: CountryService,
CodeTableService: CodeTableService,
ImageService: ImageService,
DonationService: DonationService
}
}])
.factory('QueryService', ['$http', '$q', function ($http, $q) {
/**
* @class QueryService Provides methods for retrieving query execution results from the CRM using calls to the BBIS REST services.
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var QueryService = function (options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.baseUrl = url + "WebApi";
};
/**
* Attempts to infer the type of the field and returns the cast type.
* @param {string} A field value returned as a string.
*/
function setType(variable) {
if (variable === 'True' || variable === 'False') {
variable = (variable === 'True');
} else if ($.isNumeric(variable)) {
variable = parseFloat(variable);
} else if (Date.parse(variable)) {
variable = new Date(variable);
}
return variable;
}
QueryService.prototype = {
/**
* Returns the result of executing the specified, published query. The query is executed as
* the last user to save the query. The results are cached and may be up to 10 minutes old. The
* results are limited to 5000 rows.
* @param {String} queryId The Id of a query whose results should be returned.
* @param {Array} filters An array of the filters to filter the results to rows that contain the
* specified value in the specified column. When specifying a column, use the name that is found
* in the query response. Depending on the type of column being filtered, the results may filter to rows
* that exactly match the specified value instead of those that contain the specified value. Summary
* columns such as MAX, MIN, and COUNT cannot be used as filters and will be ignored. The array should
* contain objects with the following properties:
* <ul>
* <li><tt>columnName</tt> : The name of the column that should be used to filter the results.</li>
* <li><tt>value</tt> : The value that the specified column should be filtered by.</li>
* </ul>
* @returns The promise will resolve with an object with the following properties:
* <ul>
* <li><tt>Fields</tt> : An array of objects describing the fields returned from the query. The objects
* have have the following properties:
* <ul>
* <li><tt>Name</tt> : The name of the column in the query.</li>
* <li><tt>Caption</tt> : The caption of the column defined in the query.</li>
* </ul>
* </li>
* <li><tt>Rows</tt> : An array of objects describing the rows returned from the query. The objects
* have have the following properties:
* <ul>
* <li><tt>Values</tt> : An array of the values returned for the row. The values are in the same order as the array of fields.</li>
* </ul>
* </li>
* </ul>
*/
getResults: function (queryId, filters) {
var columnName,
i,
filter,
value,
queryString,
url = this.baseUrl + "/Query/" + queryId;
if (filters) {
queryString = '';
for (i = 0; i < filters.length; i++) {
filter = filters[i];
columnName = filter.columnName;
value = filter.value;
if (columnName && value) {
if (queryString.length > 0) {
queryString += '&';
}
queryString += encodeURIComponent(columnName) + "=" + encodeURIComponent(value);
}
}
if (queryString.length > 0) {
url += '?' + queryString;
}
}
return $http.get(url);
},
/**
* Returns result data retrieved from BLACKBAUD.api.QueryService transformed into an array of objects.
*
* @param {String} queryId The Id of a query whose results should be returned.
* @param {Array} filters An array of the filters to filter the results to rows that contain the
* specified value in the specified column. When specifying a column, use the name that is found
* in the query response. Depending on the type of column being filtered, the results may filter to rows
* that exactly match the specified value instead of those that contain the specified value. Summary
* columns such as MAX, MIN, and COUNT cannot be used as filters and will be ignored. The array should
* contain objects with the following properties:
* <ul>
* <li><tt>columnName</tt> : The name of the column that should be used to filter the results.</li>
* <li><tt>value</tt> : The value that the specified column should be filtered by.</li>
* </ul>
* @param {Boolean} enableTypeConversion Default: true. If set to false, all data will be returned as a string.
*/
getResultsAsObjects: function (id, columnFilter, enableTypeConversion) {
var deferred = $q.defer();
this.getResults(id, columnFilter).success(function (results) {
var objects = [];
var fields = results.Fields;
var rows = results.Rows;
angular.forEach(rows, function (row) {
var obj = {};
for (var i = 0, j = row.Values.length; i < j; i++) {
obj[fields[i].Caption] = enableTypeConversion
? row.values[i]
: setType(row.Values[i]);
}
objects.push(obj);
});
deferred.resolve(objects);
});
return deferred.promise;
},
/**
* Returns the results of many queries as a single set. Useful if filtering on the same column many times.
*
* For example, calling the default getResults with two filters on the 'System ID' column will yield only 1 result:
* getResults(queryListId, [ { 'System ID': 123 }, { 'System ID': 456 } ] yeilds only the record matching 'System ID' 123.
*
* Likely want both records.
* getManyAsObjects(queryListId, [ { 'System ID': 123 }, { 'System ID': 456 } ] yeilds both 123 and 456 records.
*
* The filters do not need to be on the same column.
* getManyAsObjects(queryListId, [ { 'System ID': 123 }, { 'Name': 'MyFund' } ] yeilds both System ID = 123 and Name = MyFund records.
*
* @param {String} queryId The Id of a query whose results should be returned.
* @param {Array} filters An array of the filters to filter the results to rows that contain the
* specified value in the specified column. When specifying a column, use the name that is found
* in the query response. Depending on the type of column being filtered, the results may filter to rows
* that exactly match the specified value instead of those that contain the specified value. Summary
* columns such as MAX, MIN, and COUNT cannot be used as filters and will be ignored. The array should
* contain objects with the following properties:
* <ul>
* <li><tt>columnName</tt> : The name of the column that should be used to filter the results.</li>
* <li><tt>value</tt> : The value that the specified column should be filtered by.</li>
* </ul>
*/
getManyAsObjects: function (id, columnFilters) {
var self = this;
var deferred = $q.defer();
var queries = $.map(columnFilters, function (filter) {
return self.getResultsAsObjects(id, [filter]);
});
$q.all(queries).then(function (results) {
var combinedResults = [];
angular.forEach(results, function (objects) {
combinedResults = combinedResults.concat(objects);
});
deferred.resolve(combinedResults);
});
return deferred.promise;
}
};
return QueryService;
}])
.factory('UserService', ['$http', 'filterFilter', '$q', function ($http, filterFilter, $q) {
/**
* @class UserService Provides methods for retrieving information about the currently logged in BBIS user from the CRM using calls to the BBIS REST services.
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var UserService = function (options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.baseUrl = url + "WebApi";
};
UserService.prototype = {
/**
* Returns profile information for the current user. If the user is not logged in,
* then it may be populated based on the current email-recipient information.
* @returns An object with the following properties:
* <ul>
* <li><tt>Addresses</tt> : An array of objects describing the addresses of the
* current user. The objects have have the following properties:
* <ul>
* <li><tt>City</tt> : The address's city.</li>
* <li><tt>Country</tt> : The address's country.</li>
* <li><tt>Id</tt> : The Id of the address.</li>
* <li><tt>IsPrimary</tt> : Boolean indicating if the address is the user's primary address.</li>
* <li><tt>PostalCode</tt> : The address's postal code.</li>
* <li><tt>State</tt> : The address's state.</li>
* <li><tt>StreedAddress</tt> : The address's street address.</li>
* <li><tt>Type</tt> : The Id of the type of the address.</li>
* </ul>
* </li>
* <li><tt>EmailAddresses</tt> : An array of objects describing the email addresses of the
* current user. The objects have have the following properties:
* <ul>
* <li><tt>EmailAddress</tt> : The email address.</li>
* <li><tt>Id</tt> : The Id of the email address.</li>
* <li><tt>IsPrimary</tt> : Boolean indicating if the email address is the user's primary email address.</li>
* <li><tt>Type</tt> : The Id of the type of the email address.</li>
* </ul>
* </li>
* <li><tt>FirstName</tt> : The first name of the current user.</li>
* <li><tt>LastName</tt> : The last name of the current user.</li>
* <li><tt>Phone</tt> : The phone number of the current user.</li>
* <li><tt>Title</tt> : The title of the current user.</li>
* </ul>
*/
getProfile: function () {
return $http.get(this.baseUrl + '/User');
},
/**
* Returns profile information for the current user. If the user is not logged in,
* then it may be populated based on the current email-recipient information.
*
* This is the same as getProfile except it plucks out the primary email and home
* address and puts their fields into the profile object returned.
*
* The resulting format is suitable to be passed as the Donor object in a DonationQuery call.
*/
getDonorProfile: function () {
var deferred = $q.defer();
this.getProfile().success(function (profile) {
var user = {
Title: profile.Title,
FirstName: profile.FirstName,
LastName: profile.LastName,
Phone: profile.Phone,
Address: {}
};
// Email and Addresses come in arrays and we only want the primary.
if (profile.EmailAddresses && profile.EmailAddresses.length) {
var primaryEmail = filterFilter(profile.EmailAddresses, { IsPrimary: true })[0];
user.EmailAddress = primaryEmail.EmailAddress;
}
if (profile.Addresses && profile.Addresses.length) {
var primaryAddress = filterFilter(profile.Addresses, { IsPrimary: true })[0];
user.Address.StreetAddress = primaryAddress.StreetAddress;
user.Address.City = primaryAddress.City;
user.Address.State = primaryAddress.State;
user.Address.PostalCode = primaryAddress.PostalCode;
user.Address.Country = primaryAddress.Country || 'United States';
}
deferred.resolve(user);
});
return deferred.promise;
}
};
return UserService;
}])
.factory('CountryService', ['$http', function ($http) {
/**
* @class CountryService Provides methods for getting state and country information from the CRM using calls to the BBIS REST services.
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var CountryService = function (options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.baseUrl = url + "WebApi";
};
CountryService.prototype = {
/**
* Returns a list of all active countries alphabetized by their description.
* @returns An array of objects with the following properties:
* <ul>
* <li><tt>Id</tt> : The Id of a country.</li>
* <li><tt>Abbreviation</tt> : The abbreviation of a country.</li>
* <li><tt>Description</tt> : The description of a country.</li>
* </ul>
*/
getCountries: function () {
return $http.get(this.baseUrl + '/Country');
},
/**
* Returns a list of states associated with a specified country, alphabetized by their description.
* @param {String} countryId The Id of a country whose states should be returned.
* @returns An array of objects with the following properties:
* <ul>
* <li><tt>Id</tt> : The Id of a state.</li>
* <li><tt>Abbreviation</tt> : The abbreviation of a state.</li>
* <li><tt>Description</tt> : The description of a state.</li>
* </ul>
*/
getStates: function (countryId) {
var url = this.baseUrl + "/Country/" + countryId + "/State";
return $http.get(url);
},
/**
* Returns the internationalized address captions associated with a specified country.
* @param {String} countryId The Id of a country whose address captions should be returned.
* @returns An object with the following properties:
* <ul>
* <li><tt>AddressLines</tt> : The caption for the address lines.</li>
* <li><tt>City</tt> : The caption for the city.</li>
* <li><tt>State</tt> : The caption for the state.</li>
* <li><tt>PostCode</tt> : The caption for the postal code.</li>
* </ul>
*/
getAddressCaptions: function (countryId) {
var url = this.baseUrl + "/Country/" + countryId + "/AddressCaptions";
return $http.get(url);
}
};
return CountryService;
}])
.factory('CodeTableService', ['$http', function ($http) {
/**
* @class CodeTableService Provides methods for retrieving code table entries from the CRM using calls to the BBIS REST services.
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var CodeTableService = function (options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.baseUrl = url + "WebApi";
}
CodeTableService.prototype = {
/**
* Returns the active code table entries for the specified code table Id ordered as configured in the CRM.
* @returns An array of objects with the following properties:
* <ul>
* <li><tt>Id</tt> : The Id of a code table entry.</li>
* <li><tt>Description</tt> : The description of a code table entry.</li>
* </ul>
*/
getEntries: function (codeTableId) {
var url = this.baseUrl + "/CodeTable/" + codeTableId;
return $http.get(url);
}
};
return CodeTableService;
}])
.factory('ImageService', ['$http', function ($http) {
/**
* @class ImageService Provides methods for getting information about images in the image gallery
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var ImageService = function (options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.baseUrl = url + "WebApi";
};
ImageService.prototype = {
/**
* Returns a list of images in the specified folder. Only approved images that the user has rights
* to view will be returned.
* @param {String} folderGUID The GUID of an image folder whose images should be returned.
* @returns An array of objects with the following properties:
* <ul>
* <li><tt>Url</tt> : The Url of the image.</li>
* <li><tt>Caption</tt> : The caption of the image.</li>
* </ul>
*/
getImagesByFolderGUID: function (folderGUID) {
var url = this.baseUrl + "/Images?FolderGUID=" + encodeURIComponent(folderGUID);
return $http.get(url);
},
/**
* Returns a list of images with the specified tag. Only approved images that the user has rights
* to view will be returned.
* @param {String} tag The tag whose images should be returned.
* @returns n array of objects with the following properties:
* <ul>
* <li><tt>Url</tt> : The Url of the image.</li>
* <li><tt>Caption</tt> : The caption of the image.</li>
* </ul>
*/
getImagesByTag: function (tag) {
var url = this.baseUrl + "/Images?Tag=" + encodeURIComponent(tag);
return $http.get(url);
},
/**
* Returns a list of images in the specified folder. Only approved images that the user has rights
* to view will be returned.
* @param {String} folderPath The path of an image folder whose images should be returned. Example: Folder1/SubFolder1/SubFolder2
* @returns An array of objects with the following properties:
* <ul>
* <li><tt>Url</tt> : The Url of the image.</li>
* <li><tt>Caption</tt> : The caption of the image.</li>
* </ul>
*/
getImagesByFolder: function (folderPath) {
var url = this.baseUrl + "/Images/" + encodeURIComponent(folderPath);
return $http.get(url);
}
};
return ImageService;
}])
.factory('DonationService', ['$http', '$q', function ($http, $q) {
/**
* @class DonationService Provides methods needed for taking donations and retrieving confirmation information.
* @param {Integer} partId The Id of an Advanced Donation Form part that will be used as a context for all method calls.
* @param {Object} options An object literal containing one or more of the following optional properties:
* <ul>
* <li><tt>url</tt> : The URL of the BBIS site from which the data will be retrieved. This value is optional when accessed from a BBIS page. The default value will be the BBIS URL of the current page.</li>
* <li><tt>crossDomain</tt> : Indicates the BBIS url specified is from a separate domain than the current page. When True, the class will handle the complexities of making cross domain requests to retrieve data. The default value is False.</li>
* </ul>
*/
var DonationService = function (partId, options) {
var url;
if (typeof options === "string")
url = options;
else if (typeof options === "object")
url = options.url;
url = url || BLACKBAUD.api.pageInformation.rootPath;
this.contextId = partId;
this.baseUrl = url + 'WebApi/' + this.contextId + '/Donation';
};
DonationService.prototype = {
/**
* Validates the specified donation parameter. If valid, creates a donation transaction
* and saves it to the database. Stores the Id of the new donation in session so the user
* has permissions for the donation transaction. If the payment type is credit card,
* communicates with Blackbaud secured payment to set up a check out URI and adds that
* URI to the return object. For credit card payment, the donation is saved in a Pending
* state. For payments not using a credit card, the donation is saved in a Completed state
* and an acknowledgement email is sent.
* For credit card payments, the page is redirected to the Blackbaud secured payment checkout
* page after calling the successCallback function.
* @param {Object} donation An object describing the donation transaction to be created.
* @returns An object describing the donation that was created.
*/
createDonation: function (donation) {
var url = this.baseUrl + '/Create';
return $http.post(url, donation).success(function (donation) {
if (donation.BBSPCheckoutUri)
location.href = donation.BBSPCheckoutUri;
else if (donation.PaymentPageUri)
location.href = donation.PaymentPageUri;
return donation;
});
},
/**
* Ensures the user has access to this donation by finding the Id in their session.
* If so, returns information about the donation
* @param {String} id The GUID id of the donation whose information should be returned.
* @returns An object describing the donation with the specified Id.
*/
getDonation: function (id) {
var url = this.baseUrl + '/' + id;
return $http.get(url);
},
/**
* Signals the application to confirm that the credit card donation has been paid in Blackbaud secured
* payment and to complete the creation of the donation. Updates the donation status and sends the acknowledgment email.
* @param {String} id The GUID id of the donation that should be completed.
* @returns An object describing the donation with the specified Id after its status
* has been updated.
*/
completeBBSPDonation: function (id) {
var url = this.baseUrl + '/' + id + '/CompleteBBSPDonation';
return $http.post(url);
},
/**
* Gets HTML for a confirmation screen for the completed donation. Ensures the user has access to this
* donation by finding the Id in their session. If so, loads the donation and merges the fields
* into the part’s confirmation block
* @param {String} id The GUID id of the donation that should be completed.
* @returns A string containing the confirmation HTML from the part with details
* from the specified donation merged in.
*/
getDonationConfirmationHtml: function (id) {
var deferred = $q.defer();
var url = this.baseUrl + '/' + id + '/ConfirmationHtml';
$http.get(url).success(function (result) {
// Has a buch of \t, \n and \"
var fixedHtml = result
.replace(/\\n/g, '') // \n
.replace(/\\t/g, '') // \t
.replace(/\\/g, '') // \ in front of "s
.substring(1, result.length - 1); // 1st and last "
deferred.resolve(fixedHtml);
});
return deferred.promise;
}
};
return DonationService;
}]);