@cleavelandprice/ngx-lib 2.1.3 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/README.md +615 -615
  2. package/esm2020/cleavelandprice-ngx-lib.mjs +5 -0
  3. package/{esm2015/public_api.js → esm2020/public_api.mjs} +1 -1
  4. package/esm2020/src/authentication/authentication.module.mjs +112 -0
  5. package/esm2020/src/authentication/components/login/login.component.mjs +40 -0
  6. package/esm2020/src/authentication/components/login-form/login-form.component.mjs +45 -0
  7. package/esm2020/src/authentication/model/authentication-token.mjs +2 -0
  8. package/{esm2015/src/authentication/model/authentication.service.config.js → esm2020/src/authentication/model/authentication.service.config.mjs} +1 -1
  9. package/esm2020/src/authentication/services/authentication.service.mjs +138 -0
  10. package/esm2020/src/dialog/components/_dialog/dialog.component.mjs +83 -0
  11. package/esm2020/src/dialog/components/confirmation-dialog/confirmation-dialog.component.mjs +21 -0
  12. package/esm2020/src/dialog/components/input-dialog/input-dialog.component.mjs +21 -0
  13. package/esm2020/src/dialog/components/message-dialog/message-dialog.component.mjs +21 -0
  14. package/esm2020/src/dialog/dialog.module.mjs +74 -0
  15. package/{esm2015/src/dialog/model/confirmation-dialog-mode.enum.js → esm2020/src/dialog/model/confirmation-dialog-mode.enum.mjs} +1 -1
  16. package/esm2020/src/email/email.module.mjs +47 -0
  17. package/esm2020/src/email/model/email-dto.mjs +2 -0
  18. package/{esm2015/src/email/model/email-sender.js → esm2020/src/email/model/email-sender.mjs} +2 -1
  19. package/{esm2015/src/email/model/email.js → esm2020/src/email/model/email.mjs} +2 -1
  20. package/{esm2015/src/email/model/email.service.config.js → esm2020/src/email/model/email.service.config.mjs} +1 -1
  21. package/esm2020/src/email/services/email.service.mjs +31 -0
  22. package/esm2020/src/employee/employee.module.mjs +91 -0
  23. package/esm2020/src/employee/model/employee.mjs +2 -0
  24. package/{esm2015/src/employee/model/employee.service.config.js → esm2020/src/employee/model/employee.service.config.mjs} +1 -1
  25. package/esm2020/src/employee/pipes/departments.pipe.mjs +24 -0
  26. package/esm2020/src/employee/pipes/employee-by-dn.pipe.mjs +17 -0
  27. package/esm2020/src/employee/pipes/employee-photo-url.pipe.mjs +27 -0
  28. package/esm2020/src/employee/pipes/employees-with-property-value.pipe.mjs +17 -0
  29. package/esm2020/src/employee/pipes/filter-employees.pipe.mjs +22 -0
  30. package/esm2020/src/employee/pipes/is-member-of.pipe.mjs +17 -0
  31. package/esm2020/src/employee/pipes/sort-employees.pipe.mjs +44 -0
  32. package/esm2020/src/employee/services/employee.service.mjs +41 -0
  33. package/esm2020/src/file-upload/components/upload/upload.component.mjs +34 -0
  34. package/esm2020/src/file-upload/components/upload-dialog/upload-dialog.component.mjs +102 -0
  35. package/{esm2015/src/file-upload/model/database-file-info.js → esm2020/src/file-upload/model/database-file-info.mjs} +2 -1
  36. package/{esm2015/src/file-upload/model/file-upload-request.js → esm2020/src/file-upload/model/file-upload-request.mjs} +2 -1
  37. package/esm2020/src/file-upload/model/filesystem-file-info.mjs +2 -0
  38. package/esm2020/src/file-upload/model/upload.service.config.mjs +3 -0
  39. package/esm2020/src/file-upload/services/upload.service.mjs +93 -0
  40. package/esm2020/src/file-upload/upload.module.mjs +93 -0
  41. package/{esm2015/src/sharepoint/model/sharepoint-calendar-event.js → esm2020/src/sharepoint/model/sharepoint-calendar-event.mjs} +2 -1
  42. package/esm2020/src/sharepoint/model/sharepoint-customer-visit.mjs +2 -0
  43. package/{esm2015/src/sharepoint/model/sharepoint-employee-picture.js → esm2020/src/sharepoint/model/sharepoint-employee-picture.mjs} +2 -1
  44. package/esm2020/src/sharepoint/model/sharepoint-list-item.mjs +2 -0
  45. package/esm2020/src/sharepoint/model/sharepoint-list.mjs +2 -0
  46. package/esm2020/src/sharepoint/model/sharepoint-person.mjs +2 -0
  47. package/esm2020/src/sharepoint/model/sharepoint-picture.mjs +2 -0
  48. package/{esm2015/src/sharepoint/model/sharepoint.service.config.js → esm2020/src/sharepoint/model/sharepoint.service.config.mjs} +1 -1
  49. package/esm2020/src/sharepoint/pipes/sharepoint-photo-url.pipe.mjs +16 -0
  50. package/esm2020/src/sharepoint/pipes/sharepoint-url.pipe.mjs +16 -0
  51. package/esm2020/src/sharepoint/services/sharepoint.service.mjs +77 -0
  52. package/esm2020/src/sharepoint/sharepoint.module.mjs +69 -0
  53. package/esm2020/src/url/pipes/safe-url.pipe.mjs +21 -0
  54. package/esm2020/src/url/pipes/uri-scheme.pipe.mjs +103 -0
  55. package/esm2020/src/url/url.module.mjs +38 -0
  56. package/fesm2015/cleavelandprice-ngx-lib.mjs +1414 -0
  57. package/fesm2015/cleavelandprice-ngx-lib.mjs.map +1 -0
  58. package/fesm2020/cleavelandprice-ngx-lib.mjs +1386 -0
  59. package/fesm2020/cleavelandprice-ngx-lib.mjs.map +1 -0
  60. package/index.d.ts +5 -0
  61. package/package.json +22 -10
  62. package/src/authentication/authentication.module.d.ts +17 -0
  63. package/src/authentication/components/login/login.component.d.ts +6 -1
  64. package/src/authentication/components/login-form/login-form.component.d.ts +3 -0
  65. package/src/authentication/model/authentication-token.d.ts +1 -8
  66. package/src/authentication/services/authentication.service.d.ts +12 -2
  67. package/src/dialog/components/_dialog/dialog.component.d.ts +3 -0
  68. package/src/dialog/components/confirmation-dialog/confirmation-dialog.component.d.ts +3 -0
  69. package/src/dialog/components/input-dialog/input-dialog.component.d.ts +3 -0
  70. package/src/dialog/components/message-dialog/message-dialog.component.d.ts +3 -0
  71. package/src/dialog/dialog.module.d.ts +14 -0
  72. package/src/email/email.module.d.ts +5 -0
  73. package/src/email/services/email.service.d.ts +3 -0
  74. package/src/employee/employee.module.d.ts +12 -0
  75. package/src/employee/pipes/departments.pipe.d.ts +3 -0
  76. package/src/employee/pipes/employee-by-dn.pipe.d.ts +3 -0
  77. package/src/employee/pipes/employee-photo-url.pipe.d.ts +3 -0
  78. package/src/employee/pipes/employees-with-property-value.pipe.d.ts +3 -0
  79. package/src/employee/pipes/filter-employees.pipe.d.ts +3 -0
  80. package/src/employee/pipes/is-member-of.pipe.d.ts +3 -0
  81. package/src/employee/pipes/sort-employees.pipe.d.ts +3 -0
  82. package/src/employee/services/employee.service.d.ts +3 -0
  83. package/src/file-upload/components/upload/upload.component.d.ts +3 -0
  84. package/src/file-upload/components/upload-dialog/upload-dialog.component.d.ts +3 -0
  85. package/src/file-upload/services/upload.service.d.ts +3 -0
  86. package/src/file-upload/upload.module.d.ts +13 -0
  87. package/src/sharepoint/model/sharepoint-calendar-event.d.ts +3 -3
  88. package/src/sharepoint/model/sharepoint-customer-visit.d.ts +5 -5
  89. package/src/sharepoint/model/sharepoint-employee-picture.d.ts +2 -3
  90. package/src/sharepoint/model/sharepoint-list-item.d.ts +9 -9
  91. package/src/sharepoint/model/sharepoint-list.d.ts +8 -0
  92. package/src/sharepoint/model/sharepoint-person.d.ts +9 -0
  93. package/src/sharepoint/model/sharepoint-picture.d.ts +11 -8
  94. package/src/sharepoint/model/sharepoint.service.config.d.ts +2 -5
  95. package/src/sharepoint/pipes/sharepoint-photo-url.pipe.d.ts +3 -0
  96. package/src/sharepoint/pipes/sharepoint-url.pipe.d.ts +3 -0
  97. package/src/sharepoint/services/sharepoint.service.d.ts +11 -6
  98. package/src/sharepoint/sharepoint.module.d.ts +9 -4
  99. package/src/url/pipes/safe-url.pipe.d.ts +3 -0
  100. package/src/url/pipes/uri-scheme.pipe.d.ts +3 -0
  101. package/src/url/url.module.d.ts +7 -0
  102. package/bundles/cleavelandprice-ngx-lib.umd.js +0 -1360
  103. package/bundles/cleavelandprice-ngx-lib.umd.js.map +0 -1
  104. package/bundles/cleavelandprice-ngx-lib.umd.min.js +0 -2
  105. package/bundles/cleavelandprice-ngx-lib.umd.min.js.map +0 -1
  106. package/cleavelandprice-ngx-lib.d.ts +0 -5
  107. package/cleavelandprice-ngx-lib.metadata.json +0 -1
  108. package/esm2015/cleavelandprice-ngx-lib.js +0 -6
  109. package/esm2015/src/authentication/authentication.module.js +0 -84
  110. package/esm2015/src/authentication/components/login/login.component.js +0 -41
  111. package/esm2015/src/authentication/components/login-form/login-form.component.js +0 -41
  112. package/esm2015/src/authentication/model/authentication-token.js +0 -1
  113. package/esm2015/src/authentication/services/authentication.service.js +0 -129
  114. package/esm2015/src/dialog/components/_dialog/dialog.component.js +0 -75
  115. package/esm2015/src/dialog/components/confirmation-dialog/confirmation-dialog.component.js +0 -19
  116. package/esm2015/src/dialog/components/input-dialog/input-dialog.component.js +0 -19
  117. package/esm2015/src/dialog/components/message-dialog/message-dialog.component.js +0 -19
  118. package/esm2015/src/dialog/dialog.module.js +0 -54
  119. package/esm2015/src/email/email.module.js +0 -41
  120. package/esm2015/src/email/model/email-dto.js +0 -1
  121. package/esm2015/src/email/services/email.service.js +0 -30
  122. package/esm2015/src/employee/employee.module.js +0 -73
  123. package/esm2015/src/employee/model/employee.js +0 -1
  124. package/esm2015/src/employee/pipes/departments.pipe.js +0 -20
  125. package/esm2015/src/employee/pipes/employee-by-dn.pipe.js +0 -13
  126. package/esm2015/src/employee/pipes/employee-photo-url.pipe.js +0 -23
  127. package/esm2015/src/employee/pipes/employees-with-property-value.pipe.js +0 -13
  128. package/esm2015/src/employee/pipes/filter-employees.pipe.js +0 -18
  129. package/esm2015/src/employee/pipes/is-member-of.pipe.js +0 -13
  130. package/esm2015/src/employee/pipes/sort-employees.pipe.js +0 -40
  131. package/esm2015/src/employee/services/employee.service.js +0 -40
  132. package/esm2015/src/file-upload/components/upload/upload.component.js +0 -34
  133. package/esm2015/src/file-upload/components/upload-dialog/upload-dialog.component.js +0 -101
  134. package/esm2015/src/file-upload/model/filesystem-file-info.js +0 -1
  135. package/esm2015/src/file-upload/model/upload.service.config.js +0 -3
  136. package/esm2015/src/file-upload/services/upload.service.js +0 -91
  137. package/esm2015/src/file-upload/upload.module.js +0 -73
  138. package/esm2015/src/sharepoint/model/sharepoint-customer-visit.js +0 -1
  139. package/esm2015/src/sharepoint/model/sharepoint-list-item.js +0 -1
  140. package/esm2015/src/sharepoint/model/sharepoint-list-items-request.js +0 -1
  141. package/esm2015/src/sharepoint/model/sharepoint-picture.js +0 -1
  142. package/esm2015/src/sharepoint/model/sharepoint-uri-request.js +0 -1
  143. package/esm2015/src/sharepoint/model/sharepoint-user-request.js +0 -1
  144. package/esm2015/src/sharepoint/model/sharepoint-user.js +0 -1
  145. package/esm2015/src/sharepoint/pipes/sharepoint-photo-url.pipe.js +0 -12
  146. package/esm2015/src/sharepoint/pipes/sharepoint-url.pipe.js +0 -12
  147. package/esm2015/src/sharepoint/services/sharepoint.service.js +0 -71
  148. package/esm2015/src/sharepoint/sharepoint.module.js +0 -65
  149. package/esm2015/src/url/pipes/safe-url.pipe.js +0 -20
  150. package/esm2015/src/url/pipes/uri-scheme.pipe.js +0 -99
  151. package/esm2015/src/url/url.module.js +0 -31
  152. package/fesm2015/cleavelandprice-ngx-lib.js +0 -1247
  153. package/fesm2015/cleavelandprice-ngx-lib.js.map +0 -1
  154. package/src/sharepoint/model/sharepoint-list-items-request.d.ts +0 -10
  155. package/src/sharepoint/model/sharepoint-uri-request.d.ts +0 -5
  156. package/src/sharepoint/model/sharepoint-user-request.d.ts +0 -6
  157. package/src/sharepoint/model/sharepoint-user.d.ts +0 -8
package/README.md CHANGED
@@ -1,616 +1,616 @@
1
- # ngx-lib
2
-
3
- ngx-lib is an Angular 6+ library providing foundational functionality to Cleaveland/Price applications.
4
- Use of the library requires some simple configuration of the module(s) that you plan to consume in your application.
5
-
6
- ### _Please note that this library is not intended to be used for non-Cleaveland/Price applications._
7
- The Angular services in this library make use of back-end web services that are proprietary and not part of this library.
8
- While the code in this library is publicly available, it's unlikely to be highly useful to developers who are not part of Cleaveland/Price. However, you can certainly use this as a reference to indicate how you might want to develop the public APIs of your back-end services. You're welcome to use this library as you see fit, just know that a lot of the "heavy lifting" (interfacing with Active Directory, SharePoint, Exchange, etc.) is done by web services that are not available via this library.
9
-
10
- ## Installation
11
-
12
- To install this library, run:
13
-
14
- ```bash
15
- $ npm install @cleavelandprice/ngx-lib
16
- ```
17
-
18
- Please note that some modules may have additional requirements. Those requirements are documented in the module-specific sections of this document.
19
-
20
- ## Prerequisites
21
-
22
- Ngx-lib requires the following packages that are not installed by default:
23
-
24
- * @angular/cdk
25
- * @angular/material
26
- * hammerjs (_third-party, required by Angular Material_)
27
- * @auth0/angular-jwt (_third-party, required for Authentication token management_)
28
-
29
- These packages must be installed prior to using ngx-lib.
30
-
31
- ```bash
32
- npm install @angular/cdk @angular/material
33
- npm install hammerjs
34
- npm install @auth0/angular-jwt
35
- ```
36
-
37
- ## Consuming ngx-lib
38
-
39
- After installing ngx-lib, import the module(s) that you need in your `AppModule` file:
40
-
41
- ```typescript
42
- import { BrowserModule } from '@angular/platform-browser';
43
- import { HttpClientModule } from '@angular/common/http'; // Required by ngx-lib modules that call web services
44
- import { NgModule } from '@angular/core';
45
- import { AppComponent } from './app.component';
46
-
47
- // Cleaveland/Price desired module(s)
48
- import {
49
- AuthenticationModule,
50
- AuthenticationServiceConfig,
51
- DialogModule,
52
- EmailModule,
53
- EmailServiceConfig,
54
- EmployeeModule,
55
- SharePointModule,
56
- UploadModule,
57
- UrlModule
58
- } from '@cleavelandprice/ngx-lib';
59
-
60
- // Define the base URL for C/P API endpoints
61
- // Definining this prefix here makes it easier to define the module-specific Urls
62
- // i.e. The Email module will utilize a different endpoint than the SharePoint module
63
- const webServices = 'http://SomeServer/api';
64
-
65
- // Define a configuration for the Email module
66
- const emailConfig = {
67
- emailApiUrl: `${webServices}/email`,
68
- server: 'EmailServerAddress',
69
- smtpPort: 25,
70
- sender: {
71
- displayName: 'IT Application Process',
72
- emailAddress: 'address@domain.com',
73
- userName: 'emailuser',
74
- password: 'emailpassword'
75
- }
76
- };
77
-
78
- @NgModule({
79
- declarations: [
80
- AppComponent
81
- ],
82
- imports: [
83
- BrowserModule,
84
-
85
- // Import the desired module(s)
86
- // For the Email module, pass in the configuration that we defined above
87
- // At this point, all of the functionality exported by each module will be available throughout this app
88
- // This includes services, components, pipes, interfaces, classes, etc.
89
- DialogModule,
90
- UrlModule,
91
- SharePointModule.forRoot({
92
- sharePointApiUrl: `${webServices}/sharepoint`,
93
- userName: MySharePointUser,
94
- password: MySharePointPassword
95
- }),
96
- AuthenticationModule.forRoot({ authenticationApiUrl: `${webServices}/authenticate` }),
97
- EmailModule.forRoot(emailConfig),
98
- EmployeeModule.forRoot({ employeeApiUrl: `${webServices}/users` }),
99
-
100
- // Note: UploadModule has more configuration options available than the minimum that is shown here.
101
- // See the Using UploadModule section for more details
102
-
103
- // Configure UploadModule to upload to the filesystem
104
- UploadModule.forRoot({
105
- uploadApiUrl: `${webServices}/upload/filesystem`,
106
-
107
- // Note: uploadPath is optional for UploadModule
108
- // If not specified, files will be uploaded to the "Uploads" folder of the web API
109
- uploadPath: '\\\\server\\share'
110
- })
111
- // OR... Configure UploadModule to upload to the database
112
- /* UploadModule.forRoot({
113
- uploadApiUrl: `${webServices}/upload/database`,
114
-
115
- // Note: dbAppId is required for database uploads
116
- // It is used to uniquely identify an upload as belonging to a particular application
117
- dbAppId: 'myAppId'
118
- })*/
119
- ],
120
- providers: [],
121
- bootstrap: [AppComponent]
122
- })
123
- export class AppModule { }
124
- ```
125
-
126
- Once modules are imported, you can use their components, directives, services, and pipes in your Angular application:
127
-
128
- ```xml
129
- <!-- app.component.html example -->
130
- <nav>
131
- <cp-login></cp-login>
132
- </nav>
133
- ```
134
- ---
135
- ### Using AuthenticationModule
136
- The Authentication module provides a component and a service that allows you to perform Active/Directory authentication. You can tap into this functionality "manually" - meaning that you construct the interface to acquire user credentials and pass it into the service call, and you have complete control over the user interface before, during, and after authentication.
137
-
138
- Or, you may choose to simply use the `cp-login` component, which performs all of the heavy lifting for you. By simply including the `cp-login` tag in your HTML markup, the Authentication module provides its own login interface, built with Angular Material components, and displays the user's photo after authentication. This method requires no manual coding or user interface construction. It's well suited as a drop-in component for a toolbar.
139
-
140
- Regardless of which of the two approaches you decide to take, the Authentication module will handle management of the authentication token, saving it in the browser's Local Storage and automatically retrieving it the next time the user visits the page - preventing them from having to re-authenticate each time.
141
-
142
- The Authentication module also allows the developer to (optionally) specify an Active Directory group that a user must belong to in order to be considered an administrator of the application. When the web service returns the authentication token, an Admin flag will be set to true or false depending on whether or not the user belongs to the group specified. It is then up to the developer to customize the user's experience within the app based on this value. In some cases, it may even be desireable for the app to restrict login to only members of the specified group. In such a case, you would perform the authentication and implement some additional logic after authentication completes via the observable `subscribe()` method. If the user isn't an Admin (because they don't belong to the group), you would call the `logout()` method of the AuthenticationService and display a message informing them that they can't login without the required group membership.
143
-
144
- ##### LoginComponent requirements
145
- If you plan to use the `cp-login` tag in your HTML markup, please note that it requires [font-awesome](https://www.npmjs.com/package/font-awesome). Your application will need to install the font-awesome package and reference the font-awesome style sheet.
146
-
147
- Since the LoginComponent also displays a login form that utiliizes Angular Material components, you will need to install [@angular/material](https://www.npmjs.com/package/@angular/material) and reference an Angular Material theme stylesheet. Some Material components also require [HammerJS](https://www.npmjs.com/package/hammerjs). So you'll need to include that package as well and add an `import` statement for it to your main.ts file. For more thorough up-to-date documentation on Angular Material, please visit [the Angular Material site](https://material.angular.io).
148
-
149
- **Installing font-awesome**
150
- ```
151
- npm install font-awesome
152
- ```
153
-
154
- **Installing Angular Material** (four packages required)
155
- ```
156
- npm install @angular/material @angular/cdk @angular/animations hammerjs
157
- ```
158
-
159
- **styles.css**
160
- ```
161
- /* font-awesome stylesheet */
162
- @import "~font-awesome/css/font-awesome.min.css";
163
-
164
- /* Angular Material 'Indigo-Pink' theme (or choose one that you like) */
165
- @import "~@angular/material/prebuilt-themes/indigo-pink.css";
166
- ```
167
-
168
- **main.ts**
169
- ```typescript
170
- import 'hammerjs';
171
- ```
172
-
173
- #### Using the Authentication service (manual login/logout)
174
- ```typescript
175
- import { AuthenticationService } from '@cleavelandprice/ngx-lib';
176
-
177
- @Component({
178
- selector: 'app-authentication',
179
- template: `
180
- <input type="text" [(ngModel)]="username">
181
- <input type="text" [(ngModel)]="password">
182
- <img *ngIf="authenticationService.authenticated && authenticationService.authenticatedUser.photoUrl"
183
- [src]="authenticationService.authenticatedUser.photoUrl" />
184
- <button (click)="login()">Login</button>
185
- <button (click)="logout()">Logout</button>
186
- `
187
- })
188
- export class AuthenticationComponent implements OnInit {
189
- username: string;
190
- password: string;
191
- adminGroup = 'App Admins'; // Optional AD group specifying who is an admin of this app
192
- requiredGroup: boolean; // Optional login restriction
193
-
194
- // Inject the AuthenticationService into this component
195
- constructor(public authenticationService: AuthenticationService) {}
196
-
197
- ngOnInit(): void {
198
- // If we're requiring the user to be in a certain group to login, then check it after login
199
- // If not a valid member, then log them out
200
- this.authenticationService.authenticatedChanged
201
- .subscribe(() => {
202
- if (this.authenticationService.authenticated) {
203
- if (this.adminGroup && this.requiredGroup) {
204
- if (!this.authenticationService.authenticatedUser.admin) {
205
- this.logout();
206
- alert(`Sorry bro, you're not a member of '${this.adminGroup}'`);
207
- return;
208
- }
209
- }
210
- alert('Logged in');
211
- } else {
212
- alert('Logged out');
213
- }
214
- });
215
- }
216
-
217
- login() {
218
- if (this.username && this.password) {
219
- this.authenticationService.login(this.username, this.password, this.adminGroup);
220
- }
221
- }
222
-
223
- logout() {
224
- this.authenticationService.logout();
225
- }
226
- }
227
- ```
228
-
229
- ##### Interfaces provided by AuthenticationModule
230
- * AuthenticationToken
231
- * AuthenticationServiceConfig
232
- ---
233
- ### Using DialogModule
234
- The Dialog module provides a simple way to display 3 types of dialog boxes built with Angular Material components:
235
-
236
- 1. **Message Box** displays a message to the user.
237
- The only button displayed is an OK button.
238
- 2. **Confirmation Box** displays a simple question for the user and returns a true/false value indicating which button they selected.
239
- The buttons can be of the following types: `Ok/Cancel` (default option), `Yes/No`, `True/False`, 'Custom'.
240
- Regardless of which option is selected by the developer, it will always return a true/false value, with `Ok`, `Yes`, `True` = true and `Cancel`, `No`, `False` = false.
241
- If ConfirmationDialogMode is set to Custom, then you need to pass in a string array property called customTrueFalseText, with the first element being the text for the True button, and the second being the text for the False button.
242
- 3. **Input Box** displays a dialog box with an input field that the user can type into.
243
- The user can then click Ok, or Cancel.
244
- The developer may provide a placeholder for the input field.
245
-
246
- All three types of dialogs allow the developer to specify the Title and the Message to be displayed. Additionally, the developer may optionally provide a message to be displayed via a checkbox. If the `checkboxMessage` is provided, then the dialog box will include a checkbox at the bottom and the value (true/false) will be returned to the calling code. This also applies to dialogs of type Message Box - which otherwise doesn't return a value. Once the value is returned, it's up to the developer to decide what to do with the checkbox value. Typically, this is used for options like *Don't show me this message in the future*, etc.
247
-
248
- To prevent the user from closing the dialog without responding (i.e. clicking outside of the dialog), simply pass in the optional `disableClose` parameter provided by Angular Material (this is native functionality provided by Angular).
249
-
250
- ```typescript
251
- import { MatDialog } from '@angular/material/dialog';
252
- import {
253
- ConfirmationDialogComponent,
254
- ConfirmationDialogMode,
255
- InputDialogComponent,
256
- MessageDialogComponent
257
- } from '@cleavelandprice/ngx-lib';
258
- @Component({
259
- selector: 'app-dialogs',
260
- template: `
261
- <input type="text" [(ngModel)]="title">
262
- <input type="text" [(ngModel)]="message">
263
- <input type="text" [(ngModel)]="checkboxMessage">
264
- <input type="text" [(ngModel)]="placeholder">
265
- `
266
- })
267
- export class DialogsComponent {
268
- confirmationMode = ConfirmationDialogMode.OkCancel;
269
- title: string;
270
- message: string;
271
- checkboxMessage: string; // Optional
272
- placeholder: string; // For input box
273
- disableClose = false; // Optional Angular Material option to specify modal
274
-
275
- // MatDialog provided by Angular Material
276
- constructor(public dialog: MatDialog) { }
277
-
278
- messageBox(): void {
279
- this.dialog
280
- .open(MessageDialogComponent, {
281
- data: {
282
- title: this.title,
283
- message: this.message,
284
- checkboxMessage: this.checkboxMessage
285
- },
286
- disableClose: this.disableClose
287
- })
288
- .afterClosed()
289
- .subscribe(response => {
290
- if (this.checkboxMessage) {
291
- console.log(response);
292
- }
293
- });
294
- }
295
-
296
- confirmationBox(): void {
297
- this.dialog
298
- .open(ConfirmationDialogComponent, {
299
- data: {
300
- title: this.title,
301
- message: this.message,
302
- checkboxMessage: this.checkboxMessage,
303
- mode: this.confirmationMode
304
- },
305
- disableClose: this.disableClose
306
- })
307
- .afterClosed()
308
- .subscribe(response => console.log(response));
309
- }
310
-
311
- inputBox(): void {
312
- this.dialog
313
- .open(InputDialogComponent, {
314
- data: {
315
- title: this.title,
316
- message: this.message,
317
- checkboxMessage: this.checkboxMessage,
318
- placeholder: this.placeholder
319
- },
320
- disableClose: this.disableClose
321
- })
322
- .afterClosed()
323
- .subscribe(response => console.log(response));
324
- }
325
- }
326
- ```
327
-
328
- ##### Enumerations provided by DialogModule
329
- * ConfirmationDialogMode
330
- ---
331
- ### Using EmailModule
332
- The Email module provides a simple mechanism for sending emails via the Cleaveland/Price web services.
333
-
334
- ```typescript
335
- import { EmailService, Email } from '@cleavelandprice/ngx-lib';
336
-
337
- @Component({
338
- selector: 'app-email',
339
- template: '<button (click)="sendMail()">Send Email</button>'
340
- })
341
- export class EmailComponent {
342
- // Inject the EmailService into this component
343
- constructor(private emailService: EmailService) { }
344
-
345
- sendEmail(): void {
346
- // Define an email object
347
- const email: Email = {
348
- subject: 'Test Email',
349
- recipients: {
350
- to: ['recipient@domain.com']
351
- },
352
- content: { html: '<html><body>hooray!</body></html>' }
353
- };
354
-
355
- // Send the email
356
- this.emailService.send(email).subscribe();
357
- }
358
- }
359
- ```
360
-
361
- ##### Interfaces provided by EmailModule
362
- * Email
363
- * EmailServiceConfig
364
- ---
365
- ### Using EmployeesModule
366
- The Employees module provides a service for retrieving a list of employees from Active Directory (or a single employee), and a wealth of information about employees. Additionally, the module also provides some pre-built pipes for things like filtering, sorting, and determining group membership.
367
-
368
- ```typescript
369
- import { EmployeeService, Employee } from '@cleavelandprice/ngx-lib';
370
- @Component({
371
- selector: 'app-employees',
372
- template: `
373
- <div *ngFor="let emp of employees">
374
- <div>{{ emp.displayName }}</div>
375
- <div>{{ emp.department }}</div>
376
- </div>`
377
- })
378
- export class EmployeesComponent implements OnInit {
379
- employees: Employee[];
380
-
381
- // Inject the EmployeeService into this component
382
- constructor(private employeeService: EmployeeService) { }
383
-
384
- ngOnInit(): void {
385
- this.employeeService.getEmployees(true)
386
- .subscribe(employees => this.employees = employees);
387
- }
388
- }
389
-
390
- ```
391
-
392
- #### Employee pipes
393
- 1. **departments** returns a list of unique departments.
394
- `<div *ngFor="let dept of employees | departments">{{ dept}}</div>`
395
-
396
- 2. **employeeByDn** returns an employee matching an Active Directory distinguishedName value.
397
- `{{ (emp.manager | employeeByDn).displayName }}`
398
-
399
- 3. **employeePhotoUrl** returns the Url of an employee's photo (from SharePoint), provided an array of SharePointEmployeePhoto (see SharePointModule section for information about retrieving employee photos).
400
- `<div *ngFor="let emp of employees"><img [src]="emp | employeePhotoUrl:photos"></div>`
401
-
402
- 4. **employeesWithPropertyValue** returns a list of employees with a given Active Directory attribute value (exact match).
403
- `<div *ngFor="let emp of employees | employeesWithPropertyValue:'department':'Information Technology'">{{ emp.displayName }}</div>`
404
-
405
- 5. **filterEmployees** returns a filtered list of employees based on a given search string (partial or exact). The pipe will check for matches based on displayName, sAMAccountName, department, and title.
406
- `<div *ngFor="let emp of employees | filterEmployees:'daniel">{{ emp.displayName }}</div>`
407
-
408
- 6. **isMemberOf** returns a true/false value indicating whether or not an employee is a member of the specified Active Directory group.
409
- `<div *ngFor="let emp of employees" [hidden]="!(emp | isMemberOf:'Domain Admins')"></div>`
410
-
411
- 7. **sortEmployees** returns a list of employees sorted by the specified Active Directory attribute. By default, employees are sorted by displayName. If the value of the attribue is a number or a date, you should also provide a `dataType` parameter. The default dataType is "string". Additionally, you can provide a true/false value as a final parameter to specify whether or not you want the array to be sorted in reverse order.
412
- `<div *ngFor="let emp of employees | sortEmployees:'lastName'"></div>`
413
- `<div *ngFor="let emp of employees | sortEmployees:'lastName':'string':true"></div>`
414
- `<div *ngFor="let emp of employees | sortEmployees:'employeeNumber':'number'"></div>`
415
- `<div *ngFor="let emp of employees | sortEmployees:'hireDate':'date'"></div>`
416
-
417
- ##### Interfaces provided by EmployeeModule
418
- * Employee
419
- * EmployeeServiceConfig
420
- ---
421
- ### Using SharePointModule
422
- The SharePointModule provides a service that can be used to retrieve data from SharePoint. Built-in methods allow for quick retrieval of employee photos, people (employees with SharePoint accounts), and customer visits. Photos and customer visits come directly from SharePoint lists. Methods are provided to retrieve them because they are commonly needed in many applications. However, you can retrieve data from any list on any SharePoint site by constructing a `SharePointList` request value and passing it to the `getListItems()` method.
423
-
424
- ```typescript
425
- import { SharePointService,
426
- SharePointList,
427
- SharePointPerson,
428
- SharePointPicture,
429
- SharePointEmployeePicture,
430
- SharePointCustomerVisit,
431
- SharePointListItem
432
- } from '@cleavelandprice/ngx-lib';
433
-
434
- @Component({
435
- selector: 'app-sharepoint',
436
- template: `
437
- <div *ngIf="documents">
438
- <h1>Documents</h1>
439
- <div *ngFor="let doc of documents">
440
- <div>{{ doc.ows_Title }}</div>
441
- </div>
442
- </div>
443
-
444
- <div *ngIf="visits">
445
- <h1>Customer Visits</h1>
446
- <div *ngFor="let visit of visits">
447
- <div>{{ visit.ows_Title }}</div>
448
- {{ visit.ows_EventDate | date:'shortDate' }}
449
- </div>
450
- </div>
451
-
452
- <div *ngIf="people">
453
- <h1>People on SharePoint</h1>
454
- <div *ngFor="let person of people">
455
- <div>{{ person.displayName }}</div>
456
- <div>{{ person.department }} </div>
457
- <div>{{ person.accountName }}</div>
458
- </div>
459
- </div>
460
-
461
- <div *ngIf="photos">
462
- <h1>Employee Photos from SharePoint - with hover tooltips</h1>
463
- <img *ngFor="let photo of photos" [src]="photo | sharePointPhotoUrl" alt="" [title]="photo.ows_Title">
464
- </div>
465
-
466
- `
467
- })
468
- export class SharePointComponent implements OnInit {
469
- photos: SharePointEmployeePicture[];
470
- people: SharePointPerson[];
471
- visits: SharePointCustomerVisit[];
472
- documents: SharePointListItem[]; // retrieved via a custom request
473
-
474
- // Inject the SharePointService into this component
475
- constructor(private sharepointService: SharePointService) { }
476
-
477
- ngOnInit() {
478
- this.getCustomerVisits();
479
- this.getPeople();
480
- this.getPhotos();
481
- this.getDocuments();
482
- }
483
-
484
- private getCustomerVisits(): void {
485
- this.sharepointService.getCustomerVisits()
486
- .subscribe(data => this.visits = data);
487
- }
488
-
489
- private getPeople(): void {
490
- this.sharepointService.getPeople(true)
491
- .subscribe(data => this.people = data);
492
- }
493
-
494
- private getPhotos(): void {
495
- this.sharepointService.getPhotos(true)
496
- .subscribe(data => this.photos = data);
497
- }
498
-
499
- private getDocuments(): void {
500
- // Define a SharePointList to be used in the request
501
- // NOTE: the 'site' property is only needed if the library exists in a subsite (different Url than the one used to configure the service)
502
- const list: SharePointList = {
503
- list: 'Documents',
504
- view: 'All Documents',
505
- site: 'http://MySharePointSiteUrl'
506
- };
507
-
508
- this.sharepointService.getListItems(list)
509
- .subscribe(data => this.documents = data);
510
- }
511
- }
512
- ```
513
-
514
- ##### Interfaces provided by SharePointModule
515
- * SharePointCalendarEvent
516
- * SharePointCustomerVisit
517
- * SharePointEmployeePicture
518
- * SharePointListItem
519
- * SharePointList
520
- * SharePointPerson
521
- * SharePointPicture
522
- * SharePointServiceConfig
523
- ---
524
- ### Using UploadModule
525
- The UploadModule provides a component that can be used to upload one or more files simultaneously. Upload status is displayed via an Angular Material progress bar. By default, the UploadService (automatically called by the component) will attempt to upload files to the **Uploads** folder of the API (if configured in filesystem mode). Alternatively, an **uploadPath** configuration parameter can be provided to specify a more appropriate application-specific upload location. If upload failures occur, the most likely cause is a permissions problem (the API may not have write permissions to the destination).
526
-
527
- If UploadModule is configured to store files in the database instead of the filesystem, the API will store them in the **ITDev** database in the **FileUpload.Uploads** table. In database mode, the **dbAppId** and the **file name** are combined to form a composite key as a unique identifier.
528
-
529
- **dbAppId** is typically only configured once - during initial configuration of UploadModule in your **app.module.ts** file. However, because a developer may wish to use multiple application identifiers within a single application, the `UploadService` allows **dbAppId** to be changed during runtime. See example code below.
530
-
531
- When the user attempts to add a file to the upload component (prior to sending it to the server), the component will perform a call to the server to check for the existence of a file of the same name. If it already exists, the file will be rejected by the component and a message will be displayed. Files that do not already exist will be added to the component, ready to be sent to the server. In database mode, the **dbAppId** is combined with the file name to check for existence. Multiple files of the same name are only valid if the **dbAppId** is unique.
532
-
533
- ```typescript
534
- import { Component } from '@angular/core';
535
- @Component({
536
- selector: 'app-upload',
537
- template: '<cp-upload></cp-upload>'
538
- })
539
- export class MyUploadComponent {
540
- }
541
- ```
542
- If you'd like to launch the file upload dialog component manually (choosing to not use the Upload button that `cp-upload` gives you), you can bypass `cp-upload` and implement your own mechanism (such as a custom button, etc.). To do that, you will need to import `MatDialogModule` from `@angular/material/dialog` in you **app.module.ts** file as a pre-requisite. The following is an example component that manually launches the file upload dialog through a custom button. In this example, the width of the dialog is configured by the developer - just to show that you have more control over the user interface.
543
-
544
- ```typescript
545
- import { Component } from '@angular/core';
546
- import { MatDialog } from '@angular/material/dialog';
547
- import { UploadDialogComponent } from '@cleavelandprice/ngx-lib';
548
- @Component({
549
- selector: 'app-upload',
550
- template: '<button (click)="openDialog()">Open Upload Dialog</button>'
551
- })
552
- export class MyUploadComponent {
553
- constructor(private dialog: MatDialog) { }
554
-
555
- openDialog(): void {
556
- this.dialog.open(UploadDialogComponent, { width: '25%' });
557
- }
558
- }
559
- ```
560
-
561
- To change the **dbAppId** value during runtime, import `UploadService` and set the value of `dbAppId` prior to uploading files that should be stored with that identifier.
562
-
563
- ```typescript
564
- import { Component } from '@angular/core';
565
- import { UploadService } from '@cleavelandprice/ngx-lib';
566
- @Component({
567
- selector: 'app-upload',
568
- template: '<button (click)="changeAppId()">Change Database App Id</button>'
569
- })
570
- export class MyUploadComponent {
571
- constructor(private uploadService: UploadService) { }
572
-
573
- changeAppId(): void {
574
- this.uploadService.dbAppId = 'newAppId';
575
- }
576
- }
577
- ```
578
-
579
- In addition to the minimum configuration settings, the user interface of UploadModule can be further customized by providing the following values in the call to `.forRoot` in **app.module.ts**.
580
- * uploadButtonText
581
- * uploadDialogTitle
582
- * uploadDialogAddFilesButtonText
583
- * uploadDialogCancelButtonText
584
- * uploadDialogUploadButtonText
585
- * uploadDialogFinishButtonText
586
-
587
- ##### Interfaces provided by UploadModule
588
- * UploadServiceConfig
589
- ---
590
- ### Using UrlModule
591
- The UrlModule provides pipes that allow manipulation of urls used as href values in links within your application.
592
-
593
- ```typescript
594
- import { Component } from '@angular/core';
595
- @Component({
596
- selector: 'app-url',
597
- template: '<a [href]="url | uriScheme | safeUrl">Open File</a>'
598
- })
599
- export class MyUploadComponent {
600
- url = 'http://sharepoint-server/document1.docx';
601
- }
602
- ```
603
-
604
- #### Url pipes
605
- 1. **uriScheme** prefixes urls for Microsoft Office files (determined by extension) with a URI scheme string that instructs the browser to open the document in the associated app, rather that download it.
606
- `<a [href]="'http://server/file.docx' | uriScheme">Open in Microsoft Word</a>`
607
-
608
- 2. **safeUrl** whitelists your url so that angular doesn't render it with an 'unsafe:' prefix in the DOM.
609
- `<a [href]="'http://server/file.docx' | uriScheme | safeUrl">Open in Microsoft Word</a>`
610
-
611
- ---
612
- ## License
613
-
614
- MIT © [Dan Rullo](mailto:drullo@cleavelandprice.com)
615
-
1
+ # ngx-lib
2
+
3
+ ngx-lib is an Angular 6+ library providing foundational functionality to Cleaveland/Price applications.
4
+ Use of the library requires some simple configuration of the module(s) that you plan to consume in your application.
5
+
6
+ ### _Please note that this library is not intended to be used for non-Cleaveland/Price applications._
7
+ The Angular services in this library make use of back-end web services that are proprietary and not part of this library.
8
+ While the code in this library is publicly available, it's unlikely to be highly useful to developers who are not part of Cleaveland/Price. However, you can certainly use this as a reference to indicate how you might want to develop the public APIs of your back-end services. You're welcome to use this library as you see fit, just know that a lot of the "heavy lifting" (interfacing with Active Directory, SharePoint, Exchange, etc.) is done by web services that are not available via this library.
9
+
10
+ ## Installation
11
+
12
+ To install this library, run:
13
+
14
+ ```bash
15
+ $ npm install @cleavelandprice/ngx-lib
16
+ ```
17
+
18
+ Please note that some modules may have additional requirements. Those requirements are documented in the module-specific sections of this document.
19
+
20
+ ## Prerequisites
21
+
22
+ Ngx-lib requires the following packages that are not installed by default:
23
+
24
+ * @angular/cdk
25
+ * @angular/material
26
+ * hammerjs (_third-party, required by Angular Material_)
27
+ * @auth0/angular-jwt (_third-party, required for Authentication token management_)
28
+
29
+ These packages must be installed prior to using ngx-lib.
30
+
31
+ ```bash
32
+ npm install @angular/cdk @angular/material
33
+ npm install hammerjs
34
+ npm install @auth0/angular-jwt
35
+ ```
36
+
37
+ ## Consuming ngx-lib
38
+
39
+ After installing ngx-lib, import the module(s) that you need in your `AppModule` file:
40
+
41
+ ```typescript
42
+ import { BrowserModule } from '@angular/platform-browser';
43
+ import { HttpClientModule } from '@angular/common/http'; // Required by ngx-lib modules that call web services
44
+ import { NgModule } from '@angular/core';
45
+ import { AppComponent } from './app.component';
46
+
47
+ // Cleaveland/Price desired module(s)
48
+ import {
49
+ AuthenticationModule,
50
+ AuthenticationServiceConfig,
51
+ DialogModule,
52
+ EmailModule,
53
+ EmailServiceConfig,
54
+ EmployeeModule,
55
+ SharePointModule,
56
+ UploadModule,
57
+ UrlModule
58
+ } from '@cleavelandprice/ngx-lib';
59
+
60
+ // Define the base URL for C/P API endpoints
61
+ // Definining this prefix here makes it easier to define the module-specific Urls
62
+ // i.e. The Email module will utilize a different endpoint than the SharePoint module
63
+ const webServices = 'http://SomeServer/api';
64
+
65
+ // Define a configuration for the Email module
66
+ const emailConfig = {
67
+ emailApiUrl: `${webServices}/email`,
68
+ server: 'EmailServerAddress',
69
+ smtpPort: 25,
70
+ sender: {
71
+ displayName: 'IT Application Process',
72
+ emailAddress: 'address@domain.com',
73
+ userName: 'emailuser',
74
+ password: 'emailpassword'
75
+ }
76
+ };
77
+
78
+ @NgModule({
79
+ declarations: [
80
+ AppComponent
81
+ ],
82
+ imports: [
83
+ BrowserModule,
84
+
85
+ // Import the desired module(s)
86
+ // For the Email module, pass in the configuration that we defined above
87
+ // At this point, all of the functionality exported by each module will be available throughout this app
88
+ // This includes services, components, pipes, interfaces, classes, etc.
89
+ DialogModule,
90
+ UrlModule,
91
+ SharePointModule.forRoot({
92
+ sharePointApiUrl: `${webServices}/sharepoint`,
93
+ userName: MySharePointUser,
94
+ password: MySharePointPassword
95
+ }),
96
+ AuthenticationModule.forRoot({ authenticationApiUrl: `${webServices}/authenticate` }),
97
+ EmailModule.forRoot(emailConfig),
98
+ EmployeeModule.forRoot({ employeeApiUrl: `${webServices}/users` }),
99
+
100
+ // Note: UploadModule has more configuration options available than the minimum that is shown here.
101
+ // See the Using UploadModule section for more details
102
+
103
+ // Configure UploadModule to upload to the filesystem
104
+ UploadModule.forRoot({
105
+ uploadApiUrl: `${webServices}/upload/filesystem`,
106
+
107
+ // Note: uploadPath is optional for UploadModule
108
+ // If not specified, files will be uploaded to the "Uploads" folder of the web API
109
+ uploadPath: '\\\\server\\share'
110
+ })
111
+ // OR... Configure UploadModule to upload to the database
112
+ /* UploadModule.forRoot({
113
+ uploadApiUrl: `${webServices}/upload/database`,
114
+
115
+ // Note: dbAppId is required for database uploads
116
+ // It is used to uniquely identify an upload as belonging to a particular application
117
+ dbAppId: 'myAppId'
118
+ })*/
119
+ ],
120
+ providers: [],
121
+ bootstrap: [AppComponent]
122
+ })
123
+ export class AppModule { }
124
+ ```
125
+
126
+ Once modules are imported, you can use their components, directives, services, and pipes in your Angular application:
127
+
128
+ ```xml
129
+ <!-- app.component.html example -->
130
+ <nav>
131
+ <cp-login></cp-login>
132
+ </nav>
133
+ ```
134
+ ---
135
+ ### Using AuthenticationModule
136
+ The Authentication module provides a component and a service that allows you to perform Active/Directory authentication. You can tap into this functionality "manually" - meaning that you construct the interface to acquire user credentials and pass it into the service call, and you have complete control over the user interface before, during, and after authentication.
137
+
138
+ Or, you may choose to simply use the `cp-login` component, which performs all of the heavy lifting for you. By simply including the `cp-login` tag in your HTML markup, the Authentication module provides its own login interface, built with Angular Material components, and displays the user's photo after authentication. This method requires no manual coding or user interface construction. It's well suited as a drop-in component for a toolbar.
139
+
140
+ Regardless of which of the two approaches you decide to take, the Authentication module will handle management of the authentication token, saving it in the browser's Local Storage and automatically retrieving it the next time the user visits the page - preventing them from having to re-authenticate each time.
141
+
142
+ The Authentication module also allows the developer to (optionally) specify an Active Directory group that a user must belong to in order to be considered an administrator of the application. When the web service returns the authentication token, an Admin flag will be set to true or false depending on whether or not the user belongs to the group specified. It is then up to the developer to customize the user's experience within the app based on this value. In some cases, it may even be desireable for the app to restrict login to only members of the specified group. In such a case, you would perform the authentication and implement some additional logic after authentication completes via the observable `subscribe()` method. If the user isn't an Admin (because they don't belong to the group), you would call the `logout()` method of the AuthenticationService and display a message informing them that they can't login without the required group membership.
143
+
144
+ ##### LoginComponent requirements
145
+ If you plan to use the `cp-login` tag in your HTML markup, please note that it requires [font-awesome](https://www.npmjs.com/package/font-awesome). Your application will need to install the font-awesome package and reference the font-awesome style sheet.
146
+
147
+ Since the LoginComponent also displays a login form that utiliizes Angular Material components, you will need to install [@angular/material](https://www.npmjs.com/package/@angular/material) and reference an Angular Material theme stylesheet. Some Material components also require [HammerJS](https://www.npmjs.com/package/hammerjs). So you'll need to include that package as well and add an `import` statement for it to your main.ts file. For more thorough up-to-date documentation on Angular Material, please visit [the Angular Material site](https://material.angular.io).
148
+
149
+ **Installing font-awesome**
150
+ ```
151
+ npm install font-awesome
152
+ ```
153
+
154
+ **Installing Angular Material** (four packages required)
155
+ ```
156
+ npm install @angular/material @angular/cdk @angular/animations hammerjs
157
+ ```
158
+
159
+ **styles.css**
160
+ ```
161
+ /* font-awesome stylesheet */
162
+ @import "~font-awesome/css/font-awesome.min.css";
163
+
164
+ /* Angular Material 'Indigo-Pink' theme (or choose one that you like) */
165
+ @import "~@angular/material/prebuilt-themes/indigo-pink.css";
166
+ ```
167
+
168
+ **main.ts**
169
+ ```typescript
170
+ import 'hammerjs';
171
+ ```
172
+
173
+ #### Using the Authentication service (manual login/logout)
174
+ ```typescript
175
+ import { AuthenticationService } from '@cleavelandprice/ngx-lib';
176
+
177
+ @Component({
178
+ selector: 'app-authentication',
179
+ template: `
180
+ <input type="text" [(ngModel)]="username">
181
+ <input type="text" [(ngModel)]="password">
182
+ <img *ngIf="authenticationService.authenticated && authenticationService.authenticatedUser.photoUrl"
183
+ [src]="authenticationService.authenticatedUser.photoUrl" />
184
+ <button (click)="login()">Login</button>
185
+ <button (click)="logout()">Logout</button>
186
+ `
187
+ })
188
+ export class AuthenticationComponent implements OnInit {
189
+ username: string;
190
+ password: string;
191
+ adminGroup = 'App Admins'; // Optional AD group specifying who is an admin of this app
192
+ requiredGroup: boolean; // Optional login restriction
193
+
194
+ // Inject the AuthenticationService into this component
195
+ constructor(public authenticationService: AuthenticationService) {}
196
+
197
+ ngOnInit(): void {
198
+ // If we're requiring the user to be in a certain group to login, then check it after login
199
+ // If not a valid member, then log them out
200
+ this.authenticationService.authenticatedChanged
201
+ .subscribe(() => {
202
+ if (this.authenticationService.authenticated) {
203
+ if (this.adminGroup && this.requiredGroup) {
204
+ if (!this.authenticationService.authenticatedUser.admin) {
205
+ this.logout();
206
+ alert(`Sorry bro, you're not a member of '${this.adminGroup}'`);
207
+ return;
208
+ }
209
+ }
210
+ alert('Logged in');
211
+ } else {
212
+ alert('Logged out');
213
+ }
214
+ });
215
+ }
216
+
217
+ login() {
218
+ if (this.username && this.password) {
219
+ this.authenticationService.login(this.username, this.password, this.adminGroup);
220
+ }
221
+ }
222
+
223
+ logout() {
224
+ this.authenticationService.logout();
225
+ }
226
+ }
227
+ ```
228
+
229
+ ##### Interfaces provided by AuthenticationModule
230
+ * AuthenticationToken
231
+ * AuthenticationServiceConfig
232
+ ---
233
+ ### Using DialogModule
234
+ The Dialog module provides a simple way to display 3 types of dialog boxes built with Angular Material components:
235
+
236
+ 1. **Message Box** displays a message to the user.
237
+ The only button displayed is an OK button.
238
+ 2. **Confirmation Box** displays a simple question for the user and returns a true/false value indicating which button they selected.
239
+ The buttons can be of the following types: `Ok/Cancel` (default option), `Yes/No`, `True/False`, 'Custom'.
240
+ Regardless of which option is selected by the developer, it will always return a true/false value, with `Ok`, `Yes`, `True` = true and `Cancel`, `No`, `False` = false.
241
+ If ConfirmationDialogMode is set to Custom, then you need to pass in a string array property called customTrueFalseText, with the first element being the text for the True button, and the second being the text for the False button.
242
+ 3. **Input Box** displays a dialog box with an input field that the user can type into.
243
+ The user can then click Ok, or Cancel.
244
+ The developer may provide a placeholder for the input field.
245
+
246
+ All three types of dialogs allow the developer to specify the Title and the Message to be displayed. Additionally, the developer may optionally provide a message to be displayed via a checkbox. If the `checkboxMessage` is provided, then the dialog box will include a checkbox at the bottom and the value (true/false) will be returned to the calling code. This also applies to dialogs of type Message Box - which otherwise doesn't return a value. Once the value is returned, it's up to the developer to decide what to do with the checkbox value. Typically, this is used for options like *Don't show me this message in the future*, etc.
247
+
248
+ To prevent the user from closing the dialog without responding (i.e. clicking outside of the dialog), simply pass in the optional `disableClose` parameter provided by Angular Material (this is native functionality provided by Angular).
249
+
250
+ ```typescript
251
+ import { MatDialog } from '@angular/material/dialog';
252
+ import {
253
+ ConfirmationDialogComponent,
254
+ ConfirmationDialogMode,
255
+ InputDialogComponent,
256
+ MessageDialogComponent
257
+ } from '@cleavelandprice/ngx-lib';
258
+ @Component({
259
+ selector: 'app-dialogs',
260
+ template: `
261
+ <input type="text" [(ngModel)]="title">
262
+ <input type="text" [(ngModel)]="message">
263
+ <input type="text" [(ngModel)]="checkboxMessage">
264
+ <input type="text" [(ngModel)]="placeholder">
265
+ `
266
+ })
267
+ export class DialogsComponent {
268
+ confirmationMode = ConfirmationDialogMode.OkCancel;
269
+ title: string;
270
+ message: string;
271
+ checkboxMessage: string; // Optional
272
+ placeholder: string; // For input box
273
+ disableClose = false; // Optional Angular Material option to specify modal
274
+
275
+ // MatDialog provided by Angular Material
276
+ constructor(public dialog: MatDialog) { }
277
+
278
+ messageBox(): void {
279
+ this.dialog
280
+ .open(MessageDialogComponent, {
281
+ data: {
282
+ title: this.title,
283
+ message: this.message,
284
+ checkboxMessage: this.checkboxMessage
285
+ },
286
+ disableClose: this.disableClose
287
+ })
288
+ .afterClosed()
289
+ .subscribe(response => {
290
+ if (this.checkboxMessage) {
291
+ console.log(response);
292
+ }
293
+ });
294
+ }
295
+
296
+ confirmationBox(): void {
297
+ this.dialog
298
+ .open(ConfirmationDialogComponent, {
299
+ data: {
300
+ title: this.title,
301
+ message: this.message,
302
+ checkboxMessage: this.checkboxMessage,
303
+ mode: this.confirmationMode
304
+ },
305
+ disableClose: this.disableClose
306
+ })
307
+ .afterClosed()
308
+ .subscribe(response => console.log(response));
309
+ }
310
+
311
+ inputBox(): void {
312
+ this.dialog
313
+ .open(InputDialogComponent, {
314
+ data: {
315
+ title: this.title,
316
+ message: this.message,
317
+ checkboxMessage: this.checkboxMessage,
318
+ placeholder: this.placeholder
319
+ },
320
+ disableClose: this.disableClose
321
+ })
322
+ .afterClosed()
323
+ .subscribe(response => console.log(response));
324
+ }
325
+ }
326
+ ```
327
+
328
+ ##### Enumerations provided by DialogModule
329
+ * ConfirmationDialogMode
330
+ ---
331
+ ### Using EmailModule
332
+ The Email module provides a simple mechanism for sending emails via the Cleaveland/Price web services.
333
+
334
+ ```typescript
335
+ import { EmailService, Email } from '@cleavelandprice/ngx-lib';
336
+
337
+ @Component({
338
+ selector: 'app-email',
339
+ template: '<button (click)="sendMail()">Send Email</button>'
340
+ })
341
+ export class EmailComponent {
342
+ // Inject the EmailService into this component
343
+ constructor(private emailService: EmailService) { }
344
+
345
+ sendEmail(): void {
346
+ // Define an email object
347
+ const email: Email = {
348
+ subject: 'Test Email',
349
+ recipients: {
350
+ to: ['recipient@domain.com']
351
+ },
352
+ content: { html: '<html><body>hooray!</body></html>' }
353
+ };
354
+
355
+ // Send the email
356
+ this.emailService.send(email).subscribe();
357
+ }
358
+ }
359
+ ```
360
+
361
+ ##### Interfaces provided by EmailModule
362
+ * Email
363
+ * EmailServiceConfig
364
+ ---
365
+ ### Using EmployeesModule
366
+ The Employees module provides a service for retrieving a list of employees from Active Directory (or a single employee), and a wealth of information about employees. Additionally, the module also provides some pre-built pipes for things like filtering, sorting, and determining group membership.
367
+
368
+ ```typescript
369
+ import { EmployeeService, Employee } from '@cleavelandprice/ngx-lib';
370
+ @Component({
371
+ selector: 'app-employees',
372
+ template: `
373
+ <div *ngFor="let emp of employees">
374
+ <div>{{ emp.displayName }}</div>
375
+ <div>{{ emp.department }}</div>
376
+ </div>`
377
+ })
378
+ export class EmployeesComponent implements OnInit {
379
+ employees: Employee[];
380
+
381
+ // Inject the EmployeeService into this component
382
+ constructor(private employeeService: EmployeeService) { }
383
+
384
+ ngOnInit(): void {
385
+ this.employeeService.getEmployees(true)
386
+ .subscribe(employees => this.employees = employees);
387
+ }
388
+ }
389
+
390
+ ```
391
+
392
+ #### Employee pipes
393
+ 1. **departments** returns a list of unique departments.
394
+ `<div *ngFor="let dept of employees | departments">{{ dept}}</div>`
395
+
396
+ 2. **employeeByDn** returns an employee matching an Active Directory distinguishedName value.
397
+ `{{ (emp.manager | employeeByDn).displayName }}`
398
+
399
+ 3. **employeePhotoUrl** returns the Url of an employee's photo (from SharePoint), provided an array of SharePointEmployeePhoto (see SharePointModule section for information about retrieving employee photos).
400
+ `<div *ngFor="let emp of employees"><img [src]="emp | employeePhotoUrl:photos"></div>`
401
+
402
+ 4. **employeesWithPropertyValue** returns a list of employees with a given Active Directory attribute value (exact match).
403
+ `<div *ngFor="let emp of employees | employeesWithPropertyValue:'department':'Information Technology'">{{ emp.displayName }}</div>`
404
+
405
+ 5. **filterEmployees** returns a filtered list of employees based on a given search string (partial or exact). The pipe will check for matches based on displayName, sAMAccountName, department, and title.
406
+ `<div *ngFor="let emp of employees | filterEmployees:'daniel">{{ emp.displayName }}</div>`
407
+
408
+ 6. **isMemberOf** returns a true/false value indicating whether or not an employee is a member of the specified Active Directory group.
409
+ `<div *ngFor="let emp of employees" [hidden]="!(emp | isMemberOf:'Domain Admins')"></div>`
410
+
411
+ 7. **sortEmployees** returns a list of employees sorted by the specified Active Directory attribute. By default, employees are sorted by displayName. If the value of the attribue is a number or a date, you should also provide a `dataType` parameter. The default dataType is "string". Additionally, you can provide a true/false value as a final parameter to specify whether or not you want the array to be sorted in reverse order.
412
+ `<div *ngFor="let emp of employees | sortEmployees:'lastName'"></div>`
413
+ `<div *ngFor="let emp of employees | sortEmployees:'lastName':'string':true"></div>`
414
+ `<div *ngFor="let emp of employees | sortEmployees:'employeeNumber':'number'"></div>`
415
+ `<div *ngFor="let emp of employees | sortEmployees:'hireDate':'date'"></div>`
416
+
417
+ ##### Interfaces provided by EmployeeModule
418
+ * Employee
419
+ * EmployeeServiceConfig
420
+ ---
421
+ ### Using SharePointModule
422
+ The SharePointModule provides a service that can be used to retrieve data from SharePoint. Built-in methods allow for quick retrieval of employee photos, people (employees with SharePoint accounts), and customer visits. Photos and customer visits come directly from SharePoint lists. Methods are provided to retrieve them because they are commonly needed in many applications. However, you can retrieve data from any list on any SharePoint site by constructing a `SharePointList` request value and passing it to the `getListItems()` method.
423
+
424
+ ```typescript
425
+ import { SharePointService,
426
+ SharePointList,
427
+ SharePointPerson,
428
+ SharePointPicture,
429
+ SharePointEmployeePicture,
430
+ SharePointCustomerVisit,
431
+ SharePointListItem
432
+ } from '@cleavelandprice/ngx-lib';
433
+
434
+ @Component({
435
+ selector: 'app-sharepoint',
436
+ template: `
437
+ <div *ngIf="documents">
438
+ <h1>Documents</h1>
439
+ <div *ngFor="let doc of documents">
440
+ <div>{{ doc.ows_Title }}</div>
441
+ </div>
442
+ </div>
443
+
444
+ <div *ngIf="visits">
445
+ <h1>Customer Visits</h1>
446
+ <div *ngFor="let visit of visits">
447
+ <div>{{ visit.ows_Title }}</div>
448
+ {{ visit.ows_EventDate | date:'shortDate' }}
449
+ </div>
450
+ </div>
451
+
452
+ <div *ngIf="people">
453
+ <h1>People on SharePoint</h1>
454
+ <div *ngFor="let person of people">
455
+ <div>{{ person.displayName }}</div>
456
+ <div>{{ person.department }} </div>
457
+ <div>{{ person.accountName }}</div>
458
+ </div>
459
+ </div>
460
+
461
+ <div *ngIf="photos">
462
+ <h1>Employee Photos from SharePoint - with hover tooltips</h1>
463
+ <img *ngFor="let photo of photos" [src]="photo | sharePointPhotoUrl" alt="" [title]="photo.ows_Title">
464
+ </div>
465
+
466
+ `
467
+ })
468
+ export class SharePointComponent implements OnInit {
469
+ photos: SharePointEmployeePicture[];
470
+ people: SharePointPerson[];
471
+ visits: SharePointCustomerVisit[];
472
+ documents: SharePointListItem[]; // retrieved via a custom request
473
+
474
+ // Inject the SharePointService into this component
475
+ constructor(private sharepointService: SharePointService) { }
476
+
477
+ ngOnInit() {
478
+ this.getCustomerVisits();
479
+ this.getPeople();
480
+ this.getPhotos();
481
+ this.getDocuments();
482
+ }
483
+
484
+ private getCustomerVisits(): void {
485
+ this.sharepointService.getCustomerVisits()
486
+ .subscribe(data => this.visits = data);
487
+ }
488
+
489
+ private getPeople(): void {
490
+ this.sharepointService.getPeople(true)
491
+ .subscribe(data => this.people = data);
492
+ }
493
+
494
+ private getPhotos(): void {
495
+ this.sharepointService.getPhotos(true)
496
+ .subscribe(data => this.photos = data);
497
+ }
498
+
499
+ private getDocuments(): void {
500
+ // Define a SharePointList to be used in the request
501
+ // NOTE: the 'site' property is only needed if the library exists in a subsite (different Url than the one used to configure the service)
502
+ const list: SharePointList = {
503
+ list: 'Documents',
504
+ view: 'All Documents',
505
+ site: 'http://MySharePointSiteUrl'
506
+ };
507
+
508
+ this.sharepointService.getListItems(list)
509
+ .subscribe(data => this.documents = data);
510
+ }
511
+ }
512
+ ```
513
+
514
+ ##### Interfaces provided by SharePointModule
515
+ * SharePointCalendarEvent
516
+ * SharePointCustomerVisit
517
+ * SharePointEmployeePicture
518
+ * SharePointListItem
519
+ * SharePointList
520
+ * SharePointPerson
521
+ * SharePointPicture
522
+ * SharePointServiceConfig
523
+ ---
524
+ ### Using UploadModule
525
+ The UploadModule provides a component that can be used to upload one or more files simultaneously. Upload status is displayed via an Angular Material progress bar. By default, the UploadService (automatically called by the component) will attempt to upload files to the **Uploads** folder of the API (if configured in filesystem mode). Alternatively, an **uploadPath** configuration parameter can be provided to specify a more appropriate application-specific upload location. If upload failures occur, the most likely cause is a permissions problem (the API may not have write permissions to the destination).
526
+
527
+ If UploadModule is configured to store files in the database instead of the filesystem, the API will store them in the **ITDev** database in the **FileUpload.Uploads** table. In database mode, the **dbAppId** and the **file name** are combined to form a composite key as a unique identifier.
528
+
529
+ **dbAppId** is typically only configured once - during initial configuration of UploadModule in your **app.module.ts** file. However, because a developer may wish to use multiple application identifiers within a single application, the `UploadService` allows **dbAppId** to be changed during runtime. See example code below.
530
+
531
+ When the user attempts to add a file to the upload component (prior to sending it to the server), the component will perform a call to the server to check for the existence of a file of the same name. If it already exists, the file will be rejected by the component and a message will be displayed. Files that do not already exist will be added to the component, ready to be sent to the server. In database mode, the **dbAppId** is combined with the file name to check for existence. Multiple files of the same name are only valid if the **dbAppId** is unique.
532
+
533
+ ```typescript
534
+ import { Component } from '@angular/core';
535
+ @Component({
536
+ selector: 'app-upload',
537
+ template: '<cp-upload></cp-upload>'
538
+ })
539
+ export class MyUploadComponent {
540
+ }
541
+ ```
542
+ If you'd like to launch the file upload dialog component manually (choosing to not use the Upload button that `cp-upload` gives you), you can bypass `cp-upload` and implement your own mechanism (such as a custom button, etc.). To do that, you will need to import `MatDialogModule` from `@angular/material/dialog` in you **app.module.ts** file as a pre-requisite. The following is an example component that manually launches the file upload dialog through a custom button. In this example, the width of the dialog is configured by the developer - just to show that you have more control over the user interface.
543
+
544
+ ```typescript
545
+ import { Component } from '@angular/core';
546
+ import { MatDialog } from '@angular/material/dialog';
547
+ import { UploadDialogComponent } from '@cleavelandprice/ngx-lib';
548
+ @Component({
549
+ selector: 'app-upload',
550
+ template: '<button (click)="openDialog()">Open Upload Dialog</button>'
551
+ })
552
+ export class MyUploadComponent {
553
+ constructor(private dialog: MatDialog) { }
554
+
555
+ openDialog(): void {
556
+ this.dialog.open(UploadDialogComponent, { width: '25%' });
557
+ }
558
+ }
559
+ ```
560
+
561
+ To change the **dbAppId** value during runtime, import `UploadService` and set the value of `dbAppId` prior to uploading files that should be stored with that identifier.
562
+
563
+ ```typescript
564
+ import { Component } from '@angular/core';
565
+ import { UploadService } from '@cleavelandprice/ngx-lib';
566
+ @Component({
567
+ selector: 'app-upload',
568
+ template: '<button (click)="changeAppId()">Change Database App Id</button>'
569
+ })
570
+ export class MyUploadComponent {
571
+ constructor(private uploadService: UploadService) { }
572
+
573
+ changeAppId(): void {
574
+ this.uploadService.dbAppId = 'newAppId';
575
+ }
576
+ }
577
+ ```
578
+
579
+ In addition to the minimum configuration settings, the user interface of UploadModule can be further customized by providing the following values in the call to `.forRoot` in **app.module.ts**.
580
+ * uploadButtonText
581
+ * uploadDialogTitle
582
+ * uploadDialogAddFilesButtonText
583
+ * uploadDialogCancelButtonText
584
+ * uploadDialogUploadButtonText
585
+ * uploadDialogFinishButtonText
586
+
587
+ ##### Interfaces provided by UploadModule
588
+ * UploadServiceConfig
589
+ ---
590
+ ### Using UrlModule
591
+ The UrlModule provides pipes that allow manipulation of urls used as href values in links within your application.
592
+
593
+ ```typescript
594
+ import { Component } from '@angular/core';
595
+ @Component({
596
+ selector: 'app-url',
597
+ template: '<a [href]="url | uriScheme | safeUrl">Open File</a>'
598
+ })
599
+ export class MyUploadComponent {
600
+ url = 'http://sharepoint-server/document1.docx';
601
+ }
602
+ ```
603
+
604
+ #### Url pipes
605
+ 1. **uriScheme** prefixes urls for Microsoft Office files (determined by extension) with a URI scheme string that instructs the browser to open the document in the associated app, rather that download it.
606
+ `<a [href]="'http://server/file.docx' | uriScheme">Open in Microsoft Word</a>`
607
+
608
+ 2. **safeUrl** whitelists your url so that angular doesn't render it with an 'unsafe:' prefix in the DOM.
609
+ `<a [href]="'http://server/file.docx' | uriScheme | safeUrl">Open in Microsoft Word</a>`
610
+
611
+ ---
612
+ ## License
613
+
614
+ MIT © [Dan Rullo](mailto:drullo@cleavelandprice.com)
615
+
616
616
  This project was built using the [ng-packagr](https://github.com/dherges/ng-packagr) project.