Project

General

Profile

« Previous | Next » 

Revision 60536

[Admin | New-UI]: Finish personal-info. Delete old curator component

View differences:

modules/uoa-admin-portal/branches/new-UI/src/app/pages/curator/curator-routing.module.ts
1
import { NgModule } from '@angular/core';
2
import {RouterModule} from '@angular/router';
3
import {CuratorComponent} from './curator.component';
4
import {IsCommunity} from '../../openaireLibrary/connect/communityGuard/isCommunity.guard';
5
import {ConnectAdminLoginGuard} from '../../openaireLibrary/connect/communityGuard/connectAdminLoginGuard.guard';
6

  
7
@NgModule({
8
    imports: [
9
        RouterModule.forChild([
10
            { path: '', canActivate: [IsCommunity, ConnectAdminLoginGuard], component: CuratorComponent}
11
        ])
12
    ]
13
})
14
export class CuratorRoutingModule { }
modules/uoa-admin-portal/branches/new-UI/src/app/pages/curator/curator.component.html
1
<ng-template #buttons>
2
  <!--<a class="portal-link uk-margin-small-right" (click)="privacy()">Privacy policy statement</a>-->
3
  <button class="uk-button uk-margin-small-right" (click)="resetMessages(); resetForm()">Cancel</button>
4
  <button *ngIf="enabled && (hasChanged || affiliationsChanged)" class="uk-button uk-button-primary" (click)="resetMessages(); updateCurator()">Save</button>
5
  <button *ngIf="!enabled || (!hasChanged && !affiliationsChanged)" class="uk-button uk-button-default" disabled>Save</button>
6
</ng-template>
7
<div class="uk-padding uk-padding-remove-top uk-text-large uk-text-center uk-width">Manage Personal Info</div>
8
<div class="uk-margin-large-bottom uk-flex uk-grid-divider" uk-grid>
9
  <div *ngIf="showLoading" class="uk-animation-fade uk-width-1-1" role="alert">
10
    <span class="loading-gif uk-align-center"></span>
11
  </div>
12
  <div *ngIf="curatorId != null && curator != null && !showLoading" class="uk-width-1-1 uk-margin-left">
13
    <div class="uk-alert uk-alert-primary uk-flex uk-flex-middle">
14
      <span class="uk-icon uk-margin-small-right" uk-icon="info"></span>
15
      <div>
16
        Your personal info will be visible in the Curators' page of your Community Gateway.
17
        Read <a (click)="privacy()">privacy policy statement</a>.<br>
18
        <span *ngIf="!newCurator && !curatorsEnabled">
19
          Curators' page is disabled. Please enable it <a routerLink="/pages" routerLinkActive="router-link-active" [queryParams]="{communityId: communityId, type: 'other'}">here</a>.
20
        </span>
21
      </div>
22
    </div>
23
  </div>
24
  <table *ngIf="curatorId != null && curator != null && !showLoading" class="uk-width-1-2@m uk-width-1-1@s uk-align-center">
25
    <tbody class="uk-table uk-align-center">
26
      <tr *ngIf="curator.name != null">
27
        <td for="name" class="uk-text-bold uk-text-right">
28
          Name
29
          <span class="uk-text-danger uk-text-bold">
30
            *
31
          </span>
32
          :
33
        </td>
34
        <td class="uk-text-left uk-width-1-1">
35
          <div *ngIf="!curator.name || curator.name === ''" class=" uk-text-danger uk-text-small style=display:none"> Please add name. </div>
36
          <input  type="text"
37
                  class="form-control uk-input" id="name"
38
                  [(ngModel)]="curator.name" (input)="resetMessages(); onNameChange()" #name="ngModel" required>
39
        </td>
40
      </tr>
41
      <tr *ngIf="photo != null">
42
        <td for="photo" class="uk-text-bold uk-align-right">Photo:</td>
43
        <td class="uk-text-left">
44
          <div class="uk-flex uk-flex-middle" uk-grid>
45
            <div class="uk-width-auto@l">
46
              <img class="uk-border-circle curator-photo" src="{{photo}}" alt="Curator Photo">
47
            </div>
48
            <div class="uk-width-expand@l uk-grid-margin-small" uk-grid>
49
              <div uk-form-custom class="uk-width-auto">
50
                <input id="photo" type="file"  (change)="fileChangeEvent($event)" (input)="resetMessages(); change()"/>
51
                <button class="uk-button  portal-button" type="button" tabindex="-1">
52
                  Upload a photo
53
                </button>
54
              </div>
55
              <div *ngIf="photo !== 'assets/common-assets/curator-default.png'" class="uk-width-auto">
56
                <button class="uk-button uk-button-danger" type="button" (click)="resetMessages(); removePhotoModal.open()">
57
                  Remove
58
                </button>
59
              </div>
60
            </div>
61
          </div>
62
          <div class="uk-margin-small-top uk-text-warning">
63
            Maximum photo resolution is 256x256 pixels.
64
          </div>
65
        </td>
66
      </tr>
67
      <tr *ngIf="curator.bio != null">
68
        <td for="bio" class="uk-text-bold uk-text-right">Biography:</td>
69
        <td class="uk-text-left">
70
          <textarea placeholder={{curator.bio}} type="text"
71
                    class="form-control uk-textarea" rows="6"
72
                    id="bio"
73
                    [(ngModel)]="curator.bio"
74
                    (input)="resetMessages(); change()">
75
          </textarea>
76
        </td>
77
      </tr>
78
      <tr *ngIf="curator.bio != null">
79
        <td class="uk-text-right"></td>
80
        <td class="uk-text-left">
81
        <div class="uk-text-danger uk-text-bold">
82
          * Required fields
83
        </div>
84
        </td>
85
      </tr>
86
      <tr>
87
        <td class="uk-text-right"></td>
88
        <td>
89
          <div *ngIf="updateErrorMessage" class="uk-alert uk-alert-danger" role="alert">{{updateErrorMessage}}</div>
90
          <div *ngIf="successfulSaveMessage" class="uk-alert uk-alert-success" role="alert">{{successfulSaveMessage}}</div>
91
      </tr>
92
    </tbody>
93
  </table>
94
  <div *ngIf="curatorId != null && curator != null && !showLoading" class="uk-width-1-2@m uk-width-1-1@s">
95
    <affiliations [curatorAffiliations]="true" [affiliations]="curator.affiliations"
96
                  (affiliationsChange)="affiliationsChanged = $event"
97
                  (resetCuratorMessages)="resetMessages();">
98
    </affiliations>
99
  </div>
100
</div>
101
<div class="uk-float-right uk-flex uk-flex-middle uk-visible@m" style="z-index: 100; bottom: 45px; position: fixed; right: 45px;">
102
  <ng-container *ngTemplateOutlet="buttons"></ng-container>
103
</div>
104
<div class="uk-float-right uk-margin-bottom uk-hidden@m">
105
  <ng-container *ngTemplateOutlet="buttons"></ng-container>
106
</div>
107
<modal-alert #removePhotoModal  (alertOutput)="removePhoto()">
108
  Your photo will be removed after you save your data. Are you sure you want to proceed?
109
</modal-alert>
110
<modal-alert #privacyStatement  (alertOutput)="privacyStatement.cancel()">
111
  <div class="">
112
    Your personal data and photo are processed by OpenAIRE in conformity with personal data protection legal framework.
113
    They  will be stored safely in our system for as long as OpenAIRE exists. Since you press the  "save" button,
114
    you give us the consent to make them public in your Community Gateway to let users know who is
115
    configuring the platform. You always have the right to exercise your rights and ask for access,
116
    rectification, erasure and restriction of your data. Please contact <a href="mailto:rcd-helpdesk@openaire.eu">rcd-helpdesk@openaire.eu</a> if
117
    you have any inquiries.
118
  </div>
119
</modal-alert>
modules/uoa-admin-portal/branches/new-UI/src/app/pages/curator/curator.component.ts
1
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
2
import {ActivatedRoute, Router} from '@angular/router';
3

  
4
import {EnvProperties} from '../../openaireLibrary/utils/properties/env-properties';
5

  
6
import {Session, User} from '../../openaireLibrary/login/utils/helper.class';
7
import {LoginErrorCodes} from '../../openaireLibrary/login/utils/guardHelper.class';
8
import {CuratorService} from '../../openaireLibrary/connect/curators/curator.service';
9
import {Curator} from '../../openaireLibrary/utils/entities/CuratorInfo';
10
import {HelperFunctions} from '../../openaireLibrary/utils/HelperFunctions.class';
11
import {UtilitiesService} from '../../openaireLibrary/services/utilities.service';
12
import {HelpContentService} from '../../services/help-content.service';
13
import {AlertModal} from '../../openaireLibrary/utils/modal/alert';
14
import {UserManagementService} from '../../openaireLibrary/services/user-management.service';
15
import {Title} from '@angular/platform-browser';
16

  
17
@Component({
18
  selector: 'curator',
19
  templateUrl: './curator.component.html',
20
})
21

  
22
export class CuratorComponent implements OnInit {
23

  
24
  public showLoading = true;
25
  public updateErrorMessage = '';
26
  public successfulSaveMessage = '';
27

  
28
  public curatorsEnabled = false;
29
  public newCurator = false;
30

  
31
  public communityId = null;
32

  
33
  public affiliationsChanged = false;
34
  public hasChanged = false;
35
  public curatorId = null;
36
  public curator: Curator = null;
37
  public photo: any = null;
38
  public properties: EnvProperties = null;
39
  public user: User;
40

  
41
  private file: File = null;
42
  private maxsize: number = 200 * 1024;
43
  public enabled = true;
44
  private deletePhoto = false;
45

  
46
  @ViewChild('privacyStatement') privacyStatement: AlertModal;
47

  
48
  constructor(private element: ElementRef,
49
              private route: ActivatedRoute,
50
              private _router: Router,
51
              private title: Title,
52
              private curatorService: CuratorService,
53
              private utilitiesService: UtilitiesService,
54
              private helpContentService: HelpContentService,
55
              private userManagementService: UserManagementService) {
56
  }
57

  
58

  
59
  ngOnInit() {
60
    this.route.data.subscribe((data: { envSpecific: EnvProperties }) => {
61
      this.properties = data.envSpecific;
62
      if (!Session.isLoggedIn()) {
63
        this._router.navigate(['/user-info'], {
64
          queryParams: {'errorCode': LoginErrorCodes.NOT_VALID, 'redirectUrl': this._router.url}
65
        });
66
      } else {
67
        this.route.queryParams.subscribe((params) => {
68
          this.communityId = params['communityId'];
69
          this.title.setTitle('Administration Dashboard | Personal Info');
70
          this.showLoading = true;
71
          this.updateErrorMessage = '';
72
          this.userManagementService.getUserInfo().subscribe(user => {
73
            this.user = user;
74
            if(this.user) {
75
              this.curatorId = this.user.id;
76
              this.getCurator();
77
            }
78
          });
79
        });
80
      }
81
    });
82

  
83
  }
84

  
85
  public getCurator() {
86
    this.curatorService.getCurator(this.properties).subscribe(
87
      curator => {
88
        if (curator && Object.keys(curator).length > 0) {
89
          this.curator = curator;
90
          this.curator.email = this.user.email;
91
          if (this.curator.photo && this.curator.photo !== '') {
92
            this.photo = this.properties.utilsService + '/download/' + this.curator.photo;
93
          } else {
94
            this.photo = 'assets/common-assets/curator-default.png';
95
          }
96
          this.curatorsPageStatus();
97
          this.showLoading = false;
98
          HelperFunctions.scroll();
99
        } else {
100
          this.newCurator = true;
101
          this.curator = new Curator();
102
          this.curator._id = this.curatorId;
103
          this.curator.email = this.user.email;
104
          this.curator.name = this.user.fullname;
105
          this.curator.affiliations = [];
106
          this.curator.bio = '';
107
          this.curator.photo = null;
108
          this.photo = 'assets/common-assets/curator-default.png';
109
          this.showLoading = false;
110
          HelperFunctions.scroll();
111
        }
112
      },
113
      error => {
114
      }
115
    );
116
  }
117

  
118

  
119
  public resetForm() {
120
    if (!Session.isLoggedIn()) {
121
      this._router.navigate(['/user-info'], {
122
        queryParams:
123
          {'errorCode': LoginErrorCodes.NOT_VALID, 'redirectUrl': this._router.url}
124
      });
125
    } else {
126
      if (this.curatorId != null && this.curatorId !== '') {
127
        this.showLoading = true;
128
        this.updateErrorMessage = '';
129
        this.getCurator();
130
      }
131
      this.resetChange();
132
    }
133
  }
134

  
135
  private curatorsPageStatus() {
136
    this.curatorsEnabled = false;
137
    this.helpContentService.getCommunityFull(this.communityId, this.properties.adminToolsAPIURL).subscribe((community) => {
138
      for (let page of community.pages) {
139
        if (page['route'] === '/curators') {
140
          this.curatorsEnabled = page['isEnabled'];
141
          return;
142
        }
143
      }
144
    });
145
  }
146

  
147
  private change() {
148
    this.hasChanged = true;
149
    this.affiliationsChanged = true;
150
  }
151

  
152
  private resetChange() {
153
    this.hasChanged = false;
154
    this.affiliationsChanged = false;
155
  }
156

  
157
  public resetMessages() {
158
    this.successfulSaveMessage = '';
159
    this.updateErrorMessage = '';
160
  }
161

  
162
  handleUpdateError(message: string, error) {
163
    this.resetMessages();
164
    this.updateErrorMessage = message;
165
    console.log('Server responded: ' + error);
166

  
167
    this.showLoading = false;
168
  }
169

  
170
  handleSuccessfulSave(message) {
171
    this.resetMessages();
172
    this.showLoading = false;
173
    HelperFunctions.scroll();
174
    this.successfulSaveMessage = message;
175
  }
176

  
177
  fileChangeEvent(event) {
178
    this.showLoading = true;
179
    if (event.target.files && event.target.files[0]) {
180
      this.file = event.target.files[0];
181
      if (this.file.type !== 'image/png' && this.file.type !== 'image/jpeg') {
182
        this.handleUpdateError('You must choose a file with type: image/png or image/jpeg!', null);
183
        this.file = null;
184
      } else if (this.file.size > this.maxsize) {
185
        this.handleUpdateError('File exceeds size\'s limit! Maximum resolution is 256x256 pixels.', null);
186
        this.file = null;
187
      } else {
188
        this.updateErrorMessage = '';
189
        const reader = new FileReader();
190
        reader.readAsDataURL(this.file);
191
        reader.onload = () => {
192
          this.photo = reader.result;
193
          this.showLoading = false;
194
          HelperFunctions.scroll();
195
        };
196
      }
197
    }
198
  }
199

  
200
  saveCurator() {
201
    this.curatorService.updateCurator(this.properties, this.curator).subscribe((curator) => {
202
        if (curator) {
203
          this.handleSuccessfulSave('Your data has been saved successfully!');
204
          this.newCurator = false;
205
          this.file = null;
206
          this.deletePhoto = false;
207
          this.resetChange();
208
        }
209
      },
210
      error => {
211
        this.handleUpdateError('An error has occurred. Try again later!', error);
212
        this.resetChange();
213
      });
214
  }
215

  
216

  
217
  updateCurator() {
218
    if (!Session.isLoggedIn()) {
219
      this._router.navigate(['/user-info'], {
220
        queryParams:
221
          {'errorCode': LoginErrorCodes.NOT_VALID, 'redirectUrl': this._router.url}
222
      });
223
    } else {
224
      if ((this.hasChanged || this.affiliationsChanged) && this.curator && this.curator.name && this.curator.name !== '') {
225
        this.showLoading = true;
226
        if (this.file) {
227
          this.utilitiesService.uploadPhoto(this.properties.utilsService + '/upload/' + this.curator._id, this.file).subscribe((res) => {
228
              if (this.curator.photo && this.curator.photo !== '') {
229
                this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
230
              }
231
              this.curator.photo = res.filename;
232
              this.saveCurator();
233
            }, error => {
234
              this.handleUpdateError('An error has occurred during photo uploading.', error);
235
            }
236
          );
237
        } else {
238
          if (this.deletePhoto) {
239
            if(this.curator.photo && this.curator.photo != '') {
240
              this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
241
              this.curator.photo = '';
242
            }
243
          }
244
          this.saveCurator();
245
        }
246
      }
247
    }
248
  }
249

  
250
  onNameChange() {
251
    this.hasChanged = true;
252
    this.enabled = !(!this.curator.name || this.curator.name === '');
253
  }
254

  
255
  removePhoto() {
256
    this.deletePhoto = true;
257
    this.hasChanged = true;
258
    this.file = null;
259
    this.photo = 'assets/common-assets/curator-default.png';
260
  }
261

  
262
  privacy() {
263
    this.privacyStatement.cancelButton = false;
264
    this.privacyStatement.okButtonText = 'Close';
265
    this.privacyStatement.alertTitle = 'Privacy policy statement';
266
    this.privacyStatement.open();
267
  }
268
}
modules/uoa-admin-portal/branches/new-UI/src/app/pages/curator/curator.module.ts
1
import {NgModule} from '@angular/core';
2
import {CommonModule} from '@angular/common';
3
import {FormsModule} from '@angular/forms';
4
import {RouterModule} from '@angular/router';
5

  
6
import {CuratorComponent} from './curator.component';
7

  
8
import {CuratorRoutingModule} from './curator-routing.module';
9
import {IsCommunity} from '../../openaireLibrary/connect/communityGuard/isCommunity.guard';
10
import {ConnectAdminLoginGuard} from '../../openaireLibrary/connect/communityGuard/connectAdminLoginGuard.guard';
11
import {CuratorService} from '../../openaireLibrary/connect/curators/curator.service';
12
import {AlertModalModule} from '../../openaireLibrary/utils/modal/alertModal.module';
13
import {UtilitiesService} from '../../openaireLibrary/services/utilities.service';
14
import {AffiliationsModule} from "../affiliations/affiliations.module";
15

  
16
@NgModule({
17
  imports: [
18
    CuratorRoutingModule, CommonModule, FormsModule, RouterModule,
19
    AlertModalModule, AffiliationsModule
20
  ],
21
  declarations: [
22
    CuratorComponent
23
  ],
24
  providers: [
25
    CuratorService, UtilitiesService, IsCommunity, ConnectAdminLoginGuard
26
  ],
27
  exports: [
28
    CuratorComponent
29
  ]
30
})
31

  
32
export class CuratorModule { }
modules/uoa-admin-portal/branches/new-UI/src/app/pages/users/personal-info/personal-info.component.ts
5 5
import {CuratorService} from "../../../openaireLibrary/connect/curators/curator.service";
6 6
import {UtilitiesService} from "../../../openaireLibrary/services/utilities.service";
7 7
import {UserManagementService} from "../../../openaireLibrary/services/user-management.service";
8
import {FormArray, FormBuilder, FormGroup, Validators} from "@angular/forms";
9
import {Subscriber} from "rxjs";
10
import {EnvProperties} from "../../../openaireLibrary/utils/properties/env-properties";
11
import {properties} from "../../../../environments/environment";
12
import {User} from "../../../openaireLibrary/login/utils/helper.class";
13
import {Affiliation, Curator} from "../../../openaireLibrary/utils/entities/CuratorInfo";
14
import {HelpContentService} from "../../../services/help-content.service";
15
import {Page} from "../../../domain/page";
8 16

  
17
declare var UIkit;
18

  
9 19
@Component({
10 20
  selector: 'personal-info',
11 21
  template: `
......
14 24
        <users-tabs tab="personal"></users-tabs>
15 25
      </div>
16 26
      <div inner>
17
        test
27
        <div class="uk-card-header">
28
          <div class="uk-flex uk-child-width-1-1 uk-child-width-1-2@m uk-grid" uk-grid>
29
            <div>
30
              <div class="uk-text-small title">
31
                Personal Info & Affiliations
32
                <span *ngIf="!loading && (curatorFb && curatorFb.dirty && !newCurator)"> (unsaved changes)</span>
33
              </div>
34
            </div>
35
            <div class="uk-text-right@m uk-text-center">
36
              <button class="uk-button uk-button-secondary outlined uk-margin-right"
37
                      (click)="reset()"
38
                      [disabled]="loading || !hasChanged">Reset
39
              </button>
40
              <button class="uk-button uk-button-secondary"
41
                      (click)="updateCurator()"
42
                      [disabled]="loading || !hasChanged || curatorFb.invalid">Save
43
              </button>
44
            </div>
45
          </div>
46
        </div>
47
        <div class="uk-card uk-card-default uk-position-relative" style="min-height: 60vh">
48
          <div [class.hidden]="loading" class="uk-overflow-auto uk-padding-remove-bottom uk-padding-large">
49
            <form *ngIf="curatorFb" [formGroup]="curatorFb">
50
              <div class="uk-grid uk-margin-large-bottom" uk-grid>
51
                <div class="uk-grid uk-width-1-1 uk-flex-middle" uk-grid>
52
                  <div>
53
                    <div class="image">
54
                      <img [src]="photo"/>
55
                      <input #fileInput id="photo" type="file" class="uk-hidden" (change)="fileChangeEvent($event)"/>
56
                      <icon class="uk-text-secondary clickable" name="photo" ratio="1.5"
57
                            (click)="$event.stopPropagation();uploadPhoto(fileInput);$event.preventDefault()"></icon>
58
                      <div *ngIf="curator.photo || file" #element
59
                           uk-dropdown="mode: click; pos: bottom-left; delay-hide: 0; flip: false">
60
                        <ul class="uk-nav uk-dropdown-nav">
61
                          <li><a (click)="fileInput.click();hide(element)">Upload a new photo</a></li>
62
                          <li><a (click)="removePhoto();hide(element)">Remove this photo</a></li>
63
                        </ul>
64
                      </div>
65
                    </div>
66
                  </div>
67
                  <div class="uk-width-expand">
68
                    <h5 class="uk-text-secondary uk-text-bold">{{user.firstname + ' ' + user.lastname}}</h5>
69
                    <!--<div>
70
                      <span class="connected">Connected with: </span>
71
                      <span class="uk-text-secondary uk-text-bold">{{user.email}}</span>
72
                    </div>-->
73
                  </div>
74
                </div>
75
                <div dashboard-input class="uk-width-1-1" label="Display Name" placeholder="Write your name"
76
                     [formInput]="curatorFb.get('name')"></div>
77
                <div dashboard-input class="uk-width-1-1" label="Biography" placeholder="Write biography..."
78
                     [formInput]="curatorFb.get('bio')" type="textarea" rows="4"></div>
79
                <div class="uk-width-1-1">
80
                  <h5 class="uk-margin-large uk-text-bold">My Affiliations</h5>
81
                  <div class="uk-flex uk-flex-center">
82
                    <a (click)="editAffiliationOpen()" class="uk-flex uk-flex-middle uk-text-uppercase">
83
                      <button class="large uk-icon-button uk-button-secondary">
84
                        <icon name="add"></icon>
85
                      </button>
86
                      <button class="uk-button uk-button-link uk-margin-small-left uk-text-secondary">
87
                        Add New Affiliation
88
                      </button>
89
                    </a>
90
                  </div>
91
                  <div class="uk-margin-medium">
92
                    <div *ngFor="let affiliation of affiliations.controls; let i=index"
93
                         class="uk-card uk-card-default uk-card-body uk-text-small uk-margin-bottom">
94
                      <div class="uk-grid uk-grid-divider uk-flex-middle" uk-grid>
95
                        <div class="uk-width-expand@m uk-width-1-1 uk-grid uk-flex-middle" uk-grid>
96
                          <div class="uk-width-small">
97
                            <img [src]="affiliation.value.logo_url">
98
                          </div>
99
                          <div class="uk-width-auto">
100
                            <h6>{{affiliation.value.name}}</h6>
101
                            URL: <a [href]="affiliation.value.website_url">{{affiliation.value.website_url}}</a>
102
                          </div>
103
                        </div>
104
                        <div class="uk-width-auto@m uk-width-1-1">
105
                          <div class="uk-width-1-1 uk-flex uk-flex-center">
106
                            <div class="uk-padding-small uk-padding-remove-horizontal">
107
                              <a (click)="editAffiliationOpen(i)" class="uk-button action uk-flex uk-flex-middle">
108
                                <icon name="edit" ratio="0.7"></icon>
109
                                <span class="uk-margin-small-left">Edit Affiliation</span>
110
                              </a>
111
                              <a (click)="deleteAffiliationOpen(i)" class="uk-button action uk-flex uk-flex-middle uk-margin-small-top">
112
                                <icon name="remove" ratio="0.7"></icon>
113
                                <span class="uk-margin-small-left">Delete Affiliation</span>
114
                              </a>
115
                            </div>
116
                          </div>
117
                        </div>
118
                      </div>
119
                    </div>
120
                  </div>
121
                </div>
122
                <div class="uk-width-1-1 uk-text-small">
123
                  Your personal info will be visible in the Curators' page of your Community Gateway.
124
                  Read <a (click)="privacy()">privacy policy statement</a>.
125
                </div>
126
              </div>
127
            </form>
128
          </div>
129
          <div *ngIf="loading" class="uk-position-center">
130
            <loading *ngIf="loading"></loading>
131
          </div>
132
        </div>
18 133
      </div>
19 134
    </div>
20
  `
135
    <modal-alert #privacyStatement (alertOutput)="privacyStatement.cancel()">
136
      <div class="uk-text-small">
137
        Your personal data and photo are processed by OpenAIRE in conformity with personal data protection legal
138
        framework.
139
        They will be stored safely in our system for as long as OpenAIRE exists. Since you press the "save" button,
140
        you give us the consent to make them public in your Community Gateway to let users know who is
141
        configuring the platform. You always have the right to exercise your rights and ask for access,
142
        rectification, erasure and restriction of your data. Please contact <a href="mailto:rcd-helpdesk@openaire.eu">rcd-helpdesk@openaire.eu</a>
143
        if you have any inquiries.
144
      </div>
145
    </modal-alert>
146
    <modal-alert #affiliationModal [okDisabled]="affiliationFb && affiliationFb.invalid" (alertOutput)="editAffiliation()">
147
      <form *ngIf="affiliationFb" [formGroup]="affiliationFb">
148
        <div class="uk-grid uk-padding-large uk-padding-remove-horizontal uk-child-width-1-1" uk-grid>
149
          <div dashboard-input label="Name" placeholder="Write affiliation's name" [formInput]="affiliationFb.get('name')"></div>
150
          <div dashboard-input label="Logo URL" placeholder="Write your affiliation's logo URL" [formInput]="affiliationFb.get('logo_url')"></div>
151
          <div dashboard-input label="Website URL" placeholder="Write your affiliation's website URL" [formInput]="affiliationFb.get('website_url')"></div>
152
        </div>
153
      </form>
154
    </modal-alert>
155
    <modal-alert #removeAffiliationModal (alertOutput)="removeAffiliation()">
156
    </modal-alert>
157
    <modal-alert #enableCuratorsModal (alertOutput)="enableCurators()">
158
      <div class="uk-padding uk-padding-remove-horizontal">
159
        Your personal information has been successfully saved.<br><br>
160
        This information will be visible in <span class="uk-text-bold">Curators page</span> of Research Community Dashboard, which is <span class="uk-text-bold">disabled</span>.
161
        Do you want to <span class="uk-text-bold">enable</span> it now?
162
      </div>
163
    </modal-alert>
164
  `,
165
  styleUrls: ['personal-info.component.css']
21 166
})
22 167
export class PersonalInfoComponent implements OnInit, OnDestroy {
23

  
168
  /** Curator information */
169
  public loading = false;
170
  public user: User;
171
  public curator: Curator;
172
  public curatorFb: FormGroup;
173
  public properties: EnvProperties = properties;
174
  public curatorsPage: Page;
175
  public newCurator = false;
176
  public communityId: string;
177
  /** Photo */
178
  public photo: any = null;
179
  private photoChanged: boolean = false;
180
  public file: File = null;
181
  private maxsize: number = 200 * 1024;
182
  private deletePhoto = false;
183
  private subs: any[] = [];
184
  /** Affiliations */
185
  public affiliationFb: FormGroup;
186
  public index: number = -1;
187
  @ViewChild('fileInput') fileInput: ElementRef;
24 188
  @ViewChild('privacyStatement') privacyStatement: AlertModal;
25

  
189
  @ViewChild('affiliationModal') affiliationModal: AlertModal;
190
  @ViewChild('removeAffiliationModal') removeAffiliationModal: AlertModal;
191
  @ViewChild('enableCuratorsModal') enableCuratorsModal: AlertModal;
192
  
26 193
  constructor(private element: ElementRef,
27 194
              private route: ActivatedRoute,
28 195
              private _router: Router,
29 196
              private title: Title,
197
              private fb: FormBuilder,
30 198
              private curatorService: CuratorService,
31 199
              private utilitiesService: UtilitiesService,
200
              private helpContentService: HelpContentService,
32 201
              private userManagementService: UserManagementService) {
33 202
  }
34

  
35

  
203
  
204
  
36 205
  ngOnInit() {
206
    this.subs.push(this.route.params.subscribe(params => {
207
      this.communityId = params['community'];
208
      this.subs.push(this.userManagementService.getUserInfo().subscribe(user => {
209
        this.user = user;
210
        if (this.user) {
211
          this.loading = true;
212
          this.subs.push(this.curatorService.getCurator(properties).subscribe(curator => {
213
            this.initCurator(curator);
214
            this.reset();
215
            this.loading = false;
216
          }, error => {
217
            if (error.status === 404) {
218
              this.initCurator(null);
219
              this.reset();
220
            } else {
221
              console.error(error);
222
            }
223
            this.loading = false;
224
          }));
225
        }
226
      }));
227
    }));
37 228
  }
38 229
  
39 230
  ngOnDestroy() {
231
    this.subs.forEach(subscription => {
232
      if (subscription instanceof Subscriber) {
233
        subscription.unsubscribe();
234
      }
235
    })
40 236
  }
237
  
238
  hide(element: any) {
239
    UIkit.dropdown(element).hide();
240
  }
241
  
242
  initCurator(curator: Curator) {
243
    if (curator) {
244
      this.curator = curator;
245
      this.curator.email = this.user.email;
246
    } else {
247
      this.newCurator = true;
248
      this.curator = new Curator();
249
      this.curator._id = this.user.id;
250
      this.curator.email = this.user.email;
251
      this.curator.name = this.user.fullname;
252
      this.curator.affiliations = [];
253
      this.curator.bio = '';
254
      this.curator.photo = null;
255
    }
256
    this.curatorsPageStatus();
257
  }
258
  
259
  reset() {
260
    this.photoChanged = false;
261
    this.file = null;
262
    if (this.fileInput) {
263
      this.fileInput.nativeElement.value = null;
264
    }
265
    let affiliations: FormArray = this.fb.array([]);
266
    this.curator.affiliations.forEach(affiliation => {
267
      affiliations.push(this.fb.group({
268
        id: this.fb.control(affiliation.id),
269
        name: this.fb.control(affiliation.name, Validators.required),
270
        logo_url: this.fb.control(affiliation.logo_url, Validators.required),
271
        website_url: this.fb.control(affiliation.website_url, Validators.required),
272
      }));
273
    });
274
    this.curatorFb = this.fb.group({
275
      _id: this.fb.control(this.curator._id),
276
      name: this.fb.control(this.curator.name, Validators.required),
277
      bio: this.fb.control(this.curator.bio),
278
      email: this.fb.control(this.curator.email),
279
      photo: this.fb.control(this.curator.photo),
280
      affiliations: affiliations
281
    });
282
    if (this.curator.photo) {
283
      this.photo = this.properties.utilsService + '/download/' + this.curator.photo;
284
    } else {
285
      this.photo = 'assets/common-assets/curator-default.png';
286
    }
287
  }
288
  
289
  get affiliations(): FormArray {
290
    return this.curatorFb.get('affiliations') as FormArray;
291
  }
292
  
293
  saveCurator() {
294
    this.curatorService.updateCurator(this.properties, this.curatorFb.value).subscribe((curator) => {
295
        if (curator) {
296
          UIkit.notification('Your data has been <b>saved successfully</b>', {
297
            status: 'success',
298
            timeout: 6000,
299
            pos: 'bottom-right'
300
          });
301
          this.newCurator = false;
302
          this.deletePhoto = false;
303
          this.initCurator(curator);
304
          this.reset();
305
          if(!this.curatorsEnabled) {
306
            this.curatorsEnabledOpen();
307
          }
308
          this.loading = false;
309
        }
310
      },
311
      error => {
312
        this.handleUpdateError('An error has occurred. Try again later!');
313
        this.reset();
314
      });
315
  }
316
  
317
  
318
  updateCurator() {
319
    if (this.curatorFb.valid) {
320
      this.loading = true;
321
      if (this.file) {
322
        this.utilitiesService.uploadPhoto(this.properties.utilsService + '/upload/' + this.curator._id, this.file).subscribe((res) => {
323
            if (this.curator.photo) {
324
              this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
325
            }
326
            this.curatorFb.get('photo').setValue(res.filename);
327
            this.saveCurator();
328
          }, error => {
329
            this.handleUpdateError('An error has occurred during photo uploading.');
330
          }
331
        );
332
      } else {
333
        if (this.deletePhoto && this.curator.photo) {
334
          this.utilitiesService.deletePhoto(this.properties.utilsService + '/delete/' + this.curator.photo).subscribe();
335
          this.curatorFb.get('photo').setValue(null);
336
        }
337
        this.saveCurator();
338
      }
339
    }
340
  }
341
  
342
  private curatorsPageStatus() {
343
    this.helpContentService.getCommunityPagesByRoute(this.communityId, '/curators', this.properties.adminToolsAPIURL).subscribe((page) => {
344
      this.curatorsPage = page;
345
    });
346
  }
347
  
348
  public get curatorsEnabled(): boolean {
349
    return !!this.curatorsPage && this.curatorsPage.isEnabled;
350
  }
351
  
352
  fileChangeEvent(event) {
353
    this.loading = true;
354
    if (event.target.files && event.target.files[0]) {
355
      this.file = event.target.files[0];
356
      if (this.file.type !== 'image/png' && this.file.type !== 'image/jpeg') {
357
        this.handleUpdateError('You must choose a file with type: image/png or image/jpeg!');
358
        this.file = null;
359
      } else if (this.file.size > this.maxsize) {
360
        this.handleUpdateError('File exceeds size\'s limit! Maximum size 200KB.');
361
        this.file = null;
362
      } else {
363
        const reader = new FileReader();
364
        reader.readAsDataURL(this.file);
365
        reader.onload = () => {
366
          this.photo = reader.result;
367
          this.photoChanged = true;
368
          this.loading = false;
369
        };
370
      }
371
    } else {
372
      this.loading = false;
373
    }
374
  }
375
  
376
  removePhoto() {
377
    this.deletePhoto = true;
378
    this.file = null;
379
    this.fileInput.nativeElement.value = null;
380
    this.photoChanged = !!this.curator.photo;
381
    this.photo = 'assets/common-assets/curator-default.png';
382
  }
383
  
384
  handleUpdateError(message: string) {
385
    UIkit.notification(message, {
386
      status: 'danger',
387
      timeout: 6000,
388
      pos: 'bottom-right'
389
    });
390
    this.loading = false;
391
  }
392
  
393
  privacy() {
394
    this.privacyStatement.cancelButton = false;
395
    this.privacyStatement.okButtonText = 'Close';
396
    this.privacyStatement.alertTitle = 'Privacy policy statement';
397
    this.privacyStatement.open();
398
  }
399
  
400
  uploadPhoto(fileInput: HTMLInputElement) {
401
    if (!this.curator.photo && !this.file) {
402
      fileInput.click();
403
    }
404
  }
405
  
406
  get hasChanged(): boolean {
407
    return (this.curatorFb && this.curatorFb.dirty) || this.newCurator || this.photoChanged;
408
  }
409
  
410
  editAffiliationOpen(index = -1) {
411
    this.index = index;
412
    let affiliation: Affiliation = new Affiliation();
413
    if(index === -1) {
414
      this.affiliationModal.alertTitle = 'Add Affiliation';
415
      this.affiliationModal.okButtonText = 'Add';
416
    } else {
417
      this.affiliationModal.alertTitle = 'Edit Affiliation';
418
      this.affiliationModal.okButtonText = 'Update';
419
      affiliation = this.affiliations.at(index).value;
420
    }
421
    this.affiliationFb = this.fb.group({
422
      id: this.fb.control(affiliation.id),
423
      name: this.fb.control(affiliation.name, Validators.required),
424
      logo_url: this.fb.control(affiliation.logo_url, Validators.required),
425
      website_url: this.fb.control(affiliation.website_url, Validators.required)
426
    });
427
    this.affiliationModal.okButtonLeft = false;
428
    this.affiliationModal.cancelButtonText = 'Cancel';
429
    this.affiliationModal.open();
430
  }
431
  
432
  deleteAffiliationOpen(index: number) {
433
    this.index = index;
434
    let affiliation: Affiliation = this.affiliations.at(index).value;
435
    this.removeAffiliationModal.alertTitle = 'Delete Affiliation';
436
    this.removeAffiliationModal.message = 'Do you want to remove <b>' +
437
      affiliation.name +  '</b> from your Affiliations?';
438
    this.removeAffiliationModal.okButtonText = 'Yes';
439
    this.removeAffiliationModal.cancelButtonText = 'No';
440
    this.removeAffiliationModal.open();
441
  }
442
  
443
  editAffiliation() {
444
    if(this.index === -1) {
445
      this.affiliations.push(this.affiliationFb);
446
    } else {
447
      this.affiliations.at(this.index).setValue(this.affiliationFb.value);
448
    }
449
    this.curatorFb.markAsDirty();
450
  }
451
  
452
  removeAffiliation() {
453
    this.affiliations.removeAt(this.index);
454
    this.curatorFb.markAsDirty();
455
  }
456
  
457
  private curatorsEnabledOpen() {
458
    this.enableCuratorsModal.okButtonLeft = false;
459
    this.enableCuratorsModal.alertTitle = 'Enable Curators Page';
460
    this.enableCuratorsModal.okButtonText = 'Yes';
461
    this.enableCuratorsModal.cancelButtonText = 'No';
462
    this.enableCuratorsModal.open();
463
  }
464
  
465
  enableCurators() {
466
    this.helpContentService.togglePages(this.communityId, [this.curatorsPage._id], true, this.properties.adminToolsAPIURL).subscribe(() => {
467
      this.curatorsPage.isEnabled = true;
468
    });
469
  }
41 470
}
modules/uoa-admin-portal/branches/new-UI/src/app/pages/users/personal-info/personal-info.component.css
1
form {
2
  font-family: "Roboto", sans-serif;
3
}
4

  
5
form .connected {
6
  color: rgba(var(--text-color-rgb), 0.5);
7
}
8

  
9
form .image {
10
  position: relative;
11
  width: 120px;
12
  height: 120px;
13
}
14

  
15
form .image icon {
16
  position: absolute;
17
  top: 100%;
18
  left: 50%;
19
  transform: translate(-50%, -50%);
20
}
21

  
22
form .image img {
23
  box-shadow: 0 3px 6px #00000029;
24
  border-radius: 50%;
25
  margin-bottom: 10px;
26
  width: 120px;
27
  height: 120px;
28
  object-fit: cover;
29
}
30

  
modules/uoa-admin-portal/branches/new-UI/src/app/pages/users/personal-info/personal-info.module.ts
1 1
import {NgModule} from '@angular/core';
2 2
import {CommonModule} from '@angular/common';
3
import {FormsModule} from '@angular/forms';
3
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
4 4
import {RouterModule} from '@angular/router';
5 5

  
6 6
import {PersonalInfoComponent} from './personal-info.component';
......
9 9
import {UtilitiesService} from "../../../openaireLibrary/services/utilities.service";
10 10
import {PageContentModule} from "../../../openaireLibrary/dashboard/sharedComponents/page-content/page-content.module";
11 11
import {UsersTabsModule} from "../users-tabs.module";
12
import {LoadingModule} from "../../../openaireLibrary/utils/loading/loading.module";
13
import {InputModule} from "../../../openaireLibrary/sharedComponents/input/input.module";
14
import {IconsModule} from "../../../openaireLibrary/utils/icons/icons.module";
15
import {IconsService} from "../../../openaireLibrary/utils/icons/icons.service";
16
import {edit, photo, remove} from "../../../openaireLibrary/utils/icons/icons";
12 17

  
13 18
@NgModule({
14 19
  imports: [
15 20
    CommonModule, FormsModule, RouterModule.forChild([
16 21
      {path: '', component: PersonalInfoComponent}
17 22
    ]),
18
    AlertModalModule, PageContentModule, UsersTabsModule
23
    AlertModalModule, PageContentModule, UsersTabsModule, LoadingModule, ReactiveFormsModule, InputModule, IconsModule
19 24
  ],
20 25
  declarations: [
21 26
    PersonalInfoComponent
......
28 33
  ]
29 34
})
30 35

  
31
export class PersonalInfoModule { }
36
export class PersonalInfoModule {
37
  constructor(private iconsService: IconsService) {
38
    this.iconsService.registerIcons([photo, edit, remove])
39
  }
40
}
modules/uoa-admin-portal/branches/new-UI/src/app/pages/users/users-managers/users-managers.component.ts
12 12
  template: `
13 13
    <role-users [id]="communityId" [type]="type" [name]="name"
14 14
                [link]="link" [role]="'manager'" [message]="message" [emailComposer]="emailComposer">
15
<!--      <ul class="uk-tab customTabs admin uk-flex uk-flex-center uk-flex-left@m" uk-tab>-->
16
<!--        <li class="uk-active"><a><span class="title">Managers</span></a></li>-->
17
<!--        <li><a routerLink="../subscribers"><span class="title">Subscribers</span></a></li>-->
18
<!--      </ul>-->
19 15
      <users-tabs tab="manager"></users-tabs>
20 16
    </role-users>
21 17
  `
modules/uoa-admin-portal/branches/new-UI/src/app/pages/users/users-tabs.component.ts
34 34
      if (params && params['community']) {
35 35
        let communityPid = params['community'];
36 36
        this.subscriptions.push(this.userManagementService.getUserInfo().subscribe(user => {
37
          this.isAManager = Session.isKindOfCommunityManager(user);
37
          this.isAManager = Session.isManager("community", communityPid, user);
38 38
          this.canManageNotifications = Session.isManager("community", communityPid, user)// || Session.isCommunityCurator(user);
39 39
        }));
40 40
      }
modules/uoa-admin-portal/branches/new-UI/src/app/pages/affiliations/affiliations.component.ts
8 8
import {Affiliation} from '../../openaireLibrary/utils/entities/CuratorInfo';
9 9
import {HelperFunctions} from '../../openaireLibrary/utils/HelperFunctions.class';
10 10
import {AlertModal} from '../../openaireLibrary/utils/modal/alert';
11
import {UtilitiesService} from '../../openaireLibrary/services/utilities.service';
12 11
import {AffiliationService} from "../../openaireLibrary/connect/affiliations/affiliation.service";
13 12
import {HelpContentService} from "../../services/help-content.service";
14 13
import {Title} from '@angular/platform-browser';
modules/uoa-admin-portal/branches/new-UI/src/app/pages/usernotifications/manage-user-notifications.service.ts
14 14

  
15 15
    getUserNotifications(properties: EnvProperties, pid: string) {
16 16
      let url: string = properties.adminToolsAPIURL +  properties.adminToolsPortalType + '/'+ pid + '/notifications';
17
        return this.http.get<UserNotificationsRights>(url, CustomOptions.registryOptions())//.map(res => <any> res.json())
18
                                 // .pipe(map(res => this.parseUserNotifications(res, email)));
17
        return this.http.get<UserNotificationsRights>(url, CustomOptions.registryOptions());
19 18
    }
20 19

  
21 20
    updateUserNotifications(properties: EnvProperties, pid: string, userNotificationsRights: any) {
......
23 22
        //let options = new RequestOptions({headers: headers});
24 23
        let body = JSON.stringify(userNotificationsRights);
25 24
        let url: string = properties.adminToolsAPIURL +  properties.adminToolsPortalType + '/' + pid + '/notifications';
26
        return this.http.post(url, body, CustomOptions.getAuthOptionsWithBody());
25
        return this.http.post(url, body, CustomOptions.registryOptions());
27 26
                        //.do(request => console.log("Insert Response:"+request.status));
28 27
    }
29 28

  
modules/uoa-admin-portal/branches/new-UI/src/app/pages/community-info/profile/profile.component.ts
17 17
      <div inner>
18 18
        <div *ngIf="community">
19 19
          <div class="uk-card-header">
20
            <div class="uk-flex uk-flex-middle uk-child-width-1-1 uk-child-width-1-2@m uk-grid" uk-grid>
20
            <div class="uk-flex uk-child-width-1-1 uk-child-width-1-2@m uk-grid" uk-grid>
21 21
              <div>
22
                <div class="uk-text-small">
22
                <div class="uk-text-small title">
23 23
                  Manage Community Profile
24 24
                </div>
25 25
                <div>
26 26
                  <span class="uk-text-bold">{{community.shortTitle}}</span>
27
                  <span *ngIf="editCommunityComponent.dirty && !loading">(unsaved changes)</span>
27
                  <span *ngIf="editCommunityComponent.dirty && !loading"> (unsaved changes)</span>
28 28
                </div>
29 29
              </div>
30 30
              <div class="uk-text-right@m uk-text-center">
......
44 44
              <edit-community #editCommunityComponent [maxHeight]="'60vh'" [paddingLarge]="true"></edit-community>
45 45
            </div>
46 46
            <div *ngIf="loading" class="uk-position-center">
47
              <loading *ngIf="loading"></loading>
47
              <loading></loading>
48 48
            </div>
49 49
          </div>
50 50
        </div>
......
55 55
export class ProfileComponent implements OnInit, OnDestroy {
56 56
  
57 57
  public community: CommunityInfo;
58
  public alias: string[];
59 58
  public properties: EnvProperties = properties;
60 59
  public loading: boolean = false;
61 60
  private subscriptions: any[] = [];
modules/uoa-admin-portal/branches/new-UI/src/app/services/help-content.service.ts
281 281
  //   //.map(res => <Array<Page>> res.json())
282 282
  //     .pipe(catchError(this.handleError));
283 283
  // }
284

  
284
  
285
  getCommunityPagesByRoute(pid: string, route: string, helpContentUrl:string) {
286
    return this.http.get<Array<Page>>(helpContentUrl + properties.adminToolsPortalType + '/'+pid+'/pages'
287
      + (route ? '?page_route='+route : ''))
288
      .pipe(map(pages => (pages.length>0?pages[0]:null)), catchError(this.handleError));
289
  }
290
  
285 291
  // Replacing getCommunityPages
286 292
  getCommunityPagesByType(pid: string, type: string, helpContentUrl:string) {
287 293
    return this.http.get<Array<Page>>(helpContentUrl + properties.adminToolsPortalType + '/'+pid+'/pages'
288 294
                                        + (type ? '?page_type='+type : ''))
289
    //.map(res => <Array<Page>> res.json())
290 295
      .pipe(catchError(this.handleError));
291 296
  }
292 297
  // End of replacing getCommunityPages

Also available in: Unified diff