MOBILE-2768 policy: Support accepting all types of policies
parent
c7262f715e
commit
85e6446f4b
|
@ -2282,12 +2282,19 @@
|
||||||
"core.phone": "moodle",
|
"core.phone": "moodle",
|
||||||
"core.pictureof": "moodle",
|
"core.pictureof": "moodle",
|
||||||
"core.play": "local_moodlemobileapp",
|
"core.play": "local_moodlemobileapp",
|
||||||
|
"core.policy.agreepolicies": "tool_policy",
|
||||||
|
"core.policy.backtotop": "tool_policy",
|
||||||
|
"core.policy.consentpagetitle": "tool_policy",
|
||||||
"core.policy.havereadandagreepolicy": "local_moodlemobileapp",
|
"core.policy.havereadandagreepolicy": "local_moodlemobileapp",
|
||||||
|
"core.policy.idontagree": "tool_policy",
|
||||||
|
"core.policy.mustagreetocontinue": "tool_policy",
|
||||||
"core.policy.policyacceptmandatory": "local_moodlemobileapp",
|
"core.policy.policyacceptmandatory": "local_moodlemobileapp",
|
||||||
"core.policy.policyagree": "moodle",
|
"core.policy.policyagree": "moodle",
|
||||||
"core.policy.policyagreement": "moodle",
|
"core.policy.policyagreement": "moodle",
|
||||||
"core.policy.policyagreementclick": "moodle",
|
"core.policy.policyagreementclick": "moodle",
|
||||||
|
"core.policy.refertofullpolicytext": "tool_policy",
|
||||||
"core.policy.sitepolicynotagreederror": "local_moodlemobileapp",
|
"core.policy.sitepolicynotagreederror": "local_moodlemobileapp",
|
||||||
|
"core.policy.steppolicies": "tool_policy",
|
||||||
"core.previous": "moodle",
|
"core.previous": "moodle",
|
||||||
"core.proceed": "moodle",
|
"core.proceed": "moodle",
|
||||||
"core.publicprofile": "moodle",
|
"core.publicprofile": "moodle",
|
||||||
|
|
|
@ -671,7 +671,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite {
|
||||||
this.triggerSiteEvent(CoreEvents.SITE_POLICY_NOT_AGREED, {});
|
this.triggerSiteEvent(CoreEvents.SITE_POLICY_NOT_AGREED, {});
|
||||||
error.message = Translate.instant('core.policy.sitepolicynotagreederror');
|
error.message = Translate.instant('core.policy.sitepolicynotagreederror');
|
||||||
|
|
||||||
throw new CoreWSError(error);
|
throw new CoreSilentError(error);
|
||||||
} else if (error.errorcode === 'dmlwriteexception' && CoreTextUtils.hasUnicodeData(data)) {
|
} else if (error.errorcode === 'dmlwriteexception' && CoreTextUtils.hasUnicodeData(data)) {
|
||||||
if (!this.cleanUnicode) {
|
if (!this.cleanUnicode) {
|
||||||
// Try again cleaning unicode.
|
// Try again cleaning unicode.
|
||||||
|
|
|
@ -2,7 +2,10 @@
|
||||||
<ng-container *ngFor="let error of errorKeys">
|
<ng-container *ngFor="let error of errorKeys">
|
||||||
<div *ngIf="control.hasError(error)" class="core-input-error">
|
<div *ngIf="control.hasError(error)" class="core-input-error">
|
||||||
<ng-container *ngIf="error !== 'pattern'">
|
<ng-container *ngIf="error !== 'pattern'">
|
||||||
<span *ngIf="errorMessages && errorMessages[error]">{{ errorMessages[error] | translate }}</span>
|
<span *ngIf="errorMessages && errorMessages[error]">
|
||||||
|
<ion-icon *ngIf="error === 'required'" name="fas-circle-exclamation" aria-hidden="true" />
|
||||||
|
{{ errorMessages[error] | translate }}
|
||||||
|
</span>
|
||||||
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'max' && control.errors?.max">
|
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'max' && control.errors?.max">
|
||||||
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
|
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -62,10 +62,6 @@ const appRoutes: Routes = [
|
||||||
CoreLoginHelper.passwordChangeForced(data.siteId);
|
CoreLoginHelper.passwordChangeForced(data.siteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data) => {
|
|
||||||
CoreLoginHelper.sitePolicyNotAgreed(data.siteId);
|
|
||||||
});
|
|
||||||
|
|
||||||
await CoreLoginHelper.initialize();
|
await CoreLoginHelper.initialize();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
|
import { CorePolicyViewPolicyModalComponent } from './policy-modal/policy-modal';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CorePolicyViewPolicyModalComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSharedModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CorePolicyViewPolicyModalComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CorePolicyComponentsModule {}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title *ngIf="policy">
|
||||||
|
<h1>{{ policy.name }}</h1>
|
||||||
|
</ion-title>
|
||||||
|
<ion-buttons slot="end">
|
||||||
|
<ion-button fill="clear" (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||||
|
<ion-icon name="fas-xmark" slot="icon-only" aria-hidden=true />
|
||||||
|
</ion-button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-item *ngIf="policy" class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="policy.summary" contextLevel="system" [contextInstanceId]="0" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item *ngIf="policy" class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="policy.content" contextLevel="system" [contextInstanceId]="0" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,37 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
import { CorePolicySitePolicy } from '@features/policy/services/policy';
|
||||||
|
import { ModalController } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modal to view a policy.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-policy-view-policy-modal',
|
||||||
|
templateUrl: 'policy-modal.html',
|
||||||
|
})
|
||||||
|
export class CorePolicyViewPolicyModalComponent {
|
||||||
|
|
||||||
|
@Input() policy?: CorePolicySitePolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close modal.
|
||||||
|
*/
|
||||||
|
closeModal(): void {
|
||||||
|
ModalController.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,15 @@
|
||||||
{
|
{
|
||||||
|
"agreepolicies": "Please agree to the following policies",
|
||||||
|
"backtotop": "Back to top",
|
||||||
|
"consentpagetitle": "Consent",
|
||||||
"havereadandagreepolicy": "I have read and agree to the {{policyname}}",
|
"havereadandagreepolicy": "I have read and agree to the {{policyname}}",
|
||||||
|
"idontagree": "No thanks, I decline {{$a}}",
|
||||||
|
"mustagreetocontinue": "Before continuing you need to acknowledge all these policies.",
|
||||||
"policyacceptmandatory": "I understand and agree to the mandatory site policies",
|
"policyacceptmandatory": "I understand and agree to the mandatory site policies",
|
||||||
"policyagree": "You must agree to this policy to continue using this site. Do you agree?",
|
"policyagree": "You must agree to this policy to continue using this site. Do you agree?",
|
||||||
"policyagreement": "Site policy agreement",
|
"policyagreement": "Site policy agreement",
|
||||||
"policyagreementclick": "Link to site policy agreement",
|
"policyagreementclick": "Link to site policy agreement",
|
||||||
"sitepolicynotagreederror": "Site policy not agreed."
|
"refertofullpolicytext": "Please refer to the full {{$a}} if you would like to review the text.",
|
||||||
|
"sitepolicynotagreederror": "Site policy not agreed.",
|
||||||
|
"steppolicies": "Policy {{$a.numpolicy}} out of {{$a.totalpolicies}}"
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,22 @@
|
||||||
</ion-header>
|
</ion-header>
|
||||||
<ion-content class="limited-width">
|
<ion-content class="limited-width">
|
||||||
<core-loading [hideUntil]="policyLoaded">
|
<core-loading [hideUntil]="policyLoaded">
|
||||||
<form *ngIf="policyForm" [formGroup]="policyForm" (ngSubmit)="submitAcceptances($event)">
|
<form *ngIf="policiesForm" [formGroup]="policiesForm" (ngSubmit)="submitAcceptances($event)">
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="core-site-policy-top-bar" *ngIf="stepData">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>{{ 'core.policy.policyagreement' | translate }}</h2>
|
<p>{{ 'core.policy.steppolicies' | translate:{ $a:stepData } }}</p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="title">
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
<p *ngIf="subTitle">{{ subTitle }}</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
<!-- Accepting all policies at once, using a URL to view them. -->
|
||||||
<ng-container *ngIf="isPoliciesURL && sitePoliciesURL">
|
<ng-container *ngIf="isPoliciesURL && sitePoliciesURL">
|
||||||
<ion-card class="core-info-card">
|
<ion-card class="core-info-card">
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
|
@ -48,18 +57,104 @@
|
||||||
</div>
|
</div>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-checkbox name="agreepolicy" formControlName="agreepolicy">
|
<ion-checkbox name="agreepolicy" formControlName="agreepolicy">
|
||||||
<span *ngIf="isManageAcceptancesAvailable" [core-mark-required]="true">
|
<p *ngIf="isManageAcceptancesAvailable" [core-mark-required]="true">
|
||||||
{{ 'core.policy.havereadandagreepolicy' | translate:{ policyname:'core.policy.policyagreement' | translate } }}
|
{{ 'core.policy.havereadandagreepolicy' | translate:{ policyname:'core.policy.policyagreement' | translate } }}
|
||||||
</span>
|
</p>
|
||||||
<span *ngIf="!isManageAcceptancesAvailable" [core-mark-required]="true">
|
<p *ngIf="!isManageAcceptancesAvailable" [core-mark-required]="true">
|
||||||
{{ 'core.policy.policyacceptmandatory' | translate }}
|
{{ 'core.policy.policyacceptmandatory' | translate }}
|
||||||
</span>
|
</p>
|
||||||
</ion-checkbox>
|
</ion-checkbox>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-button type="submit" class="ion-text-wrap ion-margin-horizontal" expand="block" [disabled]="!policyForm.valid">
|
</ng-container>
|
||||||
|
|
||||||
|
<!-- Accepting policies one by one , either in same page or using a consent form at the end. -->
|
||||||
|
<ng-container *ngIf="!isPoliciesURL && (currentPolicy || showConsentForm)">
|
||||||
|
<ng-container *ngIf="currentPolicy">
|
||||||
|
<ion-item class="ion-text-wrap core-site-policy-summary">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="currentPolicy.summary" contextLevel="system" [contextInstanceId]="0"
|
||||||
|
(afterRender)="checkScroll()" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap core-site-policy-content">
|
||||||
|
<ion-label>
|
||||||
|
<core-format-text [text]="currentPolicy.content" contextLevel="system" [contextInstanceId]="0"
|
||||||
|
(afterRender)="checkScroll()" />
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container *ngIf="showConsentForm">
|
||||||
|
<ng-container *ngFor="let policy of pendingPolicies">
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-label>
|
||||||
|
<h2>{{ policy.name }}</h2>
|
||||||
|
<div [collapsible-item]="64">
|
||||||
|
<core-format-text [text]="policy.summary" contextLevel="system" [contextInstanceId]="0"
|
||||||
|
(afterRender)="checkScroll()" />
|
||||||
|
</div>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item *ngIf="policy.referToFullPolicyText" class="ion-text-wrap" button detail="false"
|
||||||
|
(click)="viewFullPolicy(policy)">
|
||||||
|
<ion-label>
|
||||||
|
<p [innerHTML]="policy.referToFullPolicyText"></p>
|
||||||
|
</ion-label>
|
||||||
|
</ion-item>
|
||||||
|
<ng-container *ngTemplateOutlet="policyForm; context: {policy: policy}" />
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ion-item class="core-site-policy-bottom-bar" *ngIf="stepData || hasScroll">
|
||||||
|
<ion-label>
|
||||||
|
<p *ngIf="stepData">{{ 'core.policy.steppolicies' | translate:{ $a:stepData } }}</p>
|
||||||
|
</ion-label>
|
||||||
|
<ion-button *ngIf="hasScroll" class="core-site-policy-go-top-button" color="secondary" slot="end"
|
||||||
|
(click)="scrollTop($event)" [attr.aria-label]="'core.policy.backtotop' | translate">
|
||||||
|
<ion-icon name="fas-chevron-up" slot="icon-only" aria-hidden="true" />
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
|
||||||
|
<ng-container *ngIf="agreeInOwnPage">
|
||||||
|
<ng-container *ngTemplateOutlet="policyForm; context: {policy: currentPolicy}" />
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<div class="core-site-policy-buttons ion-margin-horizontal">
|
||||||
|
<ion-button *ngIf="(isPoliciesURL && sitePoliciesURL) || showConsentForm || agreeInOwnPage" type="submit"
|
||||||
|
class="ion-text-wrap" [expand]="isTablet ? null : 'block'"
|
||||||
|
[disabled]="(isPoliciesURL || agreeInOwnPage) && !policiesForm.valid">
|
||||||
{{ 'core.continue' | translate }}
|
{{ 'core.continue' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ng-container>
|
<ion-button *ngIf="!isPoliciesURL && currentPolicy && !agreeInOwnPage" class="ion-text-wrap"
|
||||||
|
[expand]="isTablet ? null : 'block'" (click)="nextPolicy($event)">
|
||||||
|
{{ 'core.next' | translate }}
|
||||||
|
</ion-button>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
</ion-content>
|
</ion-content>
|
||||||
|
|
||||||
|
<ng-template #policyForm let-policy="policy">
|
||||||
|
<ion-item class="ion-text-wrap" *ngIf="!policy.optional && policiesForm">
|
||||||
|
<ion-checkbox [formControl]="policiesForm.controls['agreepolicy' + policy.versionid]">
|
||||||
|
<p [core-mark-required]="true">
|
||||||
|
{{ 'core.policy.havereadandagreepolicy' | translate:{ policyname:policy.name } }}
|
||||||
|
</p>
|
||||||
|
</ion-checkbox>
|
||||||
|
<core-input-errors [control]="policiesForm.controls['agreepolicy' + policy.versionid]" [errorMessages]="policiesErrors" />
|
||||||
|
</ion-item>
|
||||||
|
<ion-radio-group *ngIf="policy.optional && policiesForm" [formControl]="policiesForm.controls['agreepolicy' + policy.versionid]">
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-radio [value]="1">
|
||||||
|
<p>{{ 'core.policy.havereadandagreepolicy' | translate:{ policyname:policy.name } }}</p>
|
||||||
|
</ion-radio>
|
||||||
|
</ion-item>
|
||||||
|
<ion-item class="ion-text-wrap">
|
||||||
|
<ion-radio [value]="0">
|
||||||
|
<p>{{ 'core.policy.idontagree' | translate:{ $a:policy.name } }}</p>
|
||||||
|
</ion-radio>
|
||||||
|
<core-input-errors [control]="policiesForm.controls['agreepolicy' + policy.versionid]" [errorMessages]="policiesErrors" />
|
||||||
|
</ion-item>
|
||||||
|
</ion-radio-group>
|
||||||
|
</ng-template>
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
margin: 0 16px;
|
margin: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -20,6 +24,17 @@
|
||||||
--inner-border-width: 0;
|
--inner-border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.core-site-policy-top-bar, .core-site-policy-bottom-bar {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-site-policy-top-bar ion-label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.core-site-policy-bottom-bar ion-label {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.core-site-policy-link {
|
.core-site-policy-link {
|
||||||
p {
|
p {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
@ -38,8 +53,29 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ion-button[type="submit"] {
|
.core-site-policy-content ion-label {
|
||||||
margin-bottom: 12px;;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.core-site-policy-go-top-button {
|
||||||
|
--border-radius: 50%;
|
||||||
|
--box-shadow: 0 3px 5px -1px rgba(0, 0, 0, .2), 0 6px 10px 0 rgba(0, 0, 0, .14), 0 1px 18px 0 rgba(0, 0, 0, .12);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-site-policy-buttons {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
ion-button {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item ::ng-deep ion-label p span {
|
||||||
|
color: var(--core-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,9 +12,9 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { ChangeDetectorRef, Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||||
|
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
|
@ -23,8 +23,14 @@ import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CorePolicy } from '@features/policy/services/policy';
|
import { CorePolicy, CorePolicyAgreementStyle, CorePolicySitePolicy } from '@features/policy/services/policy';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { CoreUrlUtils } from '@services/utils/url';
|
||||||
|
import { IonContent } from '@ionic/angular';
|
||||||
|
import { CoreScreen } from '@services/screen';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { CoreDom } from '@singletons/dom';
|
||||||
|
import { CorePolicyViewPolicyModalComponent } from '@features/policy/components/policy-modal/policy-modal';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page to accept a site policy.
|
* Page to accept a site policy.
|
||||||
|
@ -34,18 +40,38 @@ import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
templateUrl: 'site-policy.html',
|
templateUrl: 'site-policy.html',
|
||||||
styleUrls: ['site-policy.scss'],
|
styleUrls: ['site-policy.scss'],
|
||||||
})
|
})
|
||||||
export class CorePolicySitePolicyPage implements OnInit {
|
export class CorePolicySitePolicyPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
|
@ViewChild(IonContent) content?: IonContent;
|
||||||
|
|
||||||
siteName?: string;
|
siteName?: string;
|
||||||
isManageAcceptancesAvailable = false;
|
isManageAcceptancesAvailable = false;
|
||||||
|
policyLoaded?: boolean;
|
||||||
|
policiesForm?: FormGroup;
|
||||||
isPoliciesURL = false;
|
isPoliciesURL = false;
|
||||||
|
title = '';
|
||||||
|
subTitle?: string;
|
||||||
|
hasScroll = false;
|
||||||
|
isTablet = false;
|
||||||
|
|
||||||
|
// Variables for accepting policies using a URL.
|
||||||
sitePoliciesURL?: string;
|
sitePoliciesURL?: string;
|
||||||
showInline?: boolean;
|
showInline?: boolean;
|
||||||
policyLoaded?: boolean;
|
|
||||||
policyForm?: FormGroup;
|
// Variables for accepting policies one by one.
|
||||||
|
currentPolicy?: SitePolicy;
|
||||||
|
pendingPolicies?: SitePolicy[];
|
||||||
|
agreeInOwnPage = false;
|
||||||
|
numPolicy = 1;
|
||||||
|
showConsentForm = false;
|
||||||
|
stepData?: {numpolicy: number; totalpolicies: number};
|
||||||
|
policiesErrors = { required: Translate.instant('core.policy.mustagreetocontinue') };
|
||||||
|
|
||||||
protected siteId?: string;
|
protected siteId?: string;
|
||||||
protected currentSite!: CoreSite;
|
protected currentSite!: CoreSite;
|
||||||
|
protected layoutSubscription?: Subscription;
|
||||||
|
|
||||||
|
constructor(protected elementRef: ElementRef, protected changeDetector: ChangeDetectorRef) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
|
@ -73,26 +99,23 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.isTablet = CoreScreen.isTablet;
|
||||||
|
this.layoutSubscription = CoreScreen.layoutObservable.subscribe(() => {
|
||||||
|
this.isTablet = CoreScreen.isTablet;
|
||||||
|
});
|
||||||
|
|
||||||
this.isManageAcceptancesAvailable = await CorePolicy.isManageAcceptancesAvailable(this.siteId);
|
this.isManageAcceptancesAvailable = await CorePolicy.isManageAcceptancesAvailable(this.siteId);
|
||||||
this.isPoliciesURL = this.isManageAcceptancesAvailable ?
|
this.isPoliciesURL = this.isManageAcceptancesAvailable ?
|
||||||
(await this.currentSite.getConfig('sitepolicyhandler')) !== 'tool_policy' :
|
(await this.currentSite.getConfig('sitepolicyhandler')) !== 'tool_policy' :
|
||||||
true; // Site doesn't support managing acceptances, just display it as a URL.
|
true; // Site doesn't support managing acceptances, just display it as a URL.
|
||||||
|
|
||||||
if (this.isPoliciesURL) {
|
if (this.isPoliciesURL) {
|
||||||
this.initFormForPoliciesURL();
|
|
||||||
|
|
||||||
await this.fetchSitePoliciesURL();
|
await this.fetchSitePoliciesURL();
|
||||||
} else {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreAnalytics.logEvent({
|
this.initFormForPoliciesURL();
|
||||||
type: CoreAnalyticsEventType.VIEW_ITEM,
|
} else {
|
||||||
ws: 'auth_email_get_signup_settings',
|
await this.fetchNextPoliciesToAccept();
|
||||||
name: Translate.instant('core.policy.policyagreement'),
|
}
|
||||||
data: { category: 'policy' },
|
|
||||||
url: '/user/policy.php',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,6 +124,9 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async fetchSitePoliciesURL(): Promise<void> {
|
protected async fetchSitePoliciesURL(): Promise<void> {
|
||||||
|
this.title = Translate.instant('core.policy.policyagreement');
|
||||||
|
this.subTitle = undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.sitePoliciesURL = await CorePolicy.getSitePoliciesURL(this.siteId);
|
this.sitePoliciesURL = await CorePolicy.getSitePoliciesURL(this.siteId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -122,13 +148,103 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
} finally {
|
} finally {
|
||||||
this.policyLoaded = true;
|
this.policyLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoreAnalytics.logEvent({
|
||||||
|
type: CoreAnalyticsEventType.VIEW_ITEM,
|
||||||
|
ws: 'auth_email_get_signup_settings',
|
||||||
|
name: Translate.instant('core.policy.policyagreement'),
|
||||||
|
data: { category: 'policy' },
|
||||||
|
url: '/user/policy.php',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the next site policies to accept.
|
||||||
|
*
|
||||||
|
* @returns Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async fetchNextPoliciesToAccept(): Promise<void> {
|
||||||
|
try {
|
||||||
|
this.scrollTop();
|
||||||
|
|
||||||
|
const pendingPolicies = await CorePolicy.getNextPendingPolicies({
|
||||||
|
readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
|
siteId: this.siteId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add some calculated data.
|
||||||
|
this.pendingPolicies = pendingPolicies.map((policy: SitePolicy) => {
|
||||||
|
policy.referToFullPolicyText = Translate.instant('core.policy.refertofullpolicytext', {
|
||||||
|
$a: `<span class="core-site-policy-full-policy-link">${policy.name}</span>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return policy;
|
||||||
|
});
|
||||||
|
|
||||||
|
const policy = this.pendingPolicies[0];
|
||||||
|
if (!policy) {
|
||||||
|
// No more policies to accept.
|
||||||
|
await this.finishAcceptingPolicies();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initFormForPendingPolicies();
|
||||||
|
|
||||||
|
this.agreeInOwnPage = policy.agreementstyle === CorePolicyAgreementStyle.OwnPage;
|
||||||
|
this.showConsentForm = false;
|
||||||
|
this.numPolicy = 1;
|
||||||
|
this.setCurrentPolicy(policy);
|
||||||
|
this.policyLoaded = true;
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'Error getting site policy.');
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log in analytics viewing a certain policy.
|
||||||
|
*/
|
||||||
|
protected logAnalyticsPolicyView(): void {
|
||||||
|
if (!this.currentPolicy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const analyticsParams: Record<string, string | number> = {
|
||||||
|
versionid: this.currentPolicy.versionid,
|
||||||
|
};
|
||||||
|
if (!this.agreeInOwnPage) {
|
||||||
|
analyticsParams.numpolicy = this.numPolicy;
|
||||||
|
analyticsParams.totalpolicies = this.pendingPolicies?.length ?? this.numPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreAnalytics.logEvent({
|
||||||
|
type: CoreAnalyticsEventType.VIEW_ITEM,
|
||||||
|
ws: 'tool_policy_get_user_acceptances',
|
||||||
|
name: this.currentPolicy.name,
|
||||||
|
data: analyticsParams,
|
||||||
|
url: CoreUrlUtils.addParamsToUrl('/admin/tool/policy/view.php', analyticsParams),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log in analytics viewing the consent form.
|
||||||
|
*/
|
||||||
|
protected logAnalyticsConsentFormView(): void {
|
||||||
|
CoreAnalytics.logEvent({
|
||||||
|
type: CoreAnalyticsEventType.VIEW_ITEM,
|
||||||
|
ws: 'tool_policy_get_user_acceptances',
|
||||||
|
name: Translate.instant('core.policy.consentpagetitle'),
|
||||||
|
data: {},
|
||||||
|
url: CoreUrlUtils.addParamsToUrl('/admin/tool/policy/index.php'),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init the form to accept the policies using a URL.
|
* Init the form to accept the policies using a URL.
|
||||||
*/
|
*/
|
||||||
protected initFormForPoliciesURL(): void {
|
protected initFormForPoliciesURL(): void {
|
||||||
this.policyForm = new FormGroup({
|
this.policiesForm = new FormGroup({
|
||||||
agreepolicy: new FormControl(false, {
|
agreepolicy: new FormControl(false, {
|
||||||
validators: Validators.requiredTrue,
|
validators: Validators.requiredTrue,
|
||||||
nonNullable: true,
|
nonNullable: true,
|
||||||
|
@ -136,6 +252,26 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the form to accept the current pending policies.
|
||||||
|
*/
|
||||||
|
protected initFormForPendingPolicies(): void {
|
||||||
|
this.policiesForm = new FormGroup({});
|
||||||
|
|
||||||
|
this.pendingPolicies?.forEach(policy => {
|
||||||
|
if (policy.optional) {
|
||||||
|
this.policiesForm?.addControl('agreepolicy' + policy.versionid, new FormControl<number | undefined>(undefined, {
|
||||||
|
validators: Validators.required,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this.policiesForm?.addControl('agreepolicy' + policy.versionid, new FormControl(false, {
|
||||||
|
validators: Validators.requiredTrue,
|
||||||
|
nonNullable: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel.
|
* Cancel.
|
||||||
*
|
*
|
||||||
|
@ -147,6 +283,68 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
await CoreNavigator.navigate('/login/sites', { reset: true });
|
await CoreNavigator.navigate('/login/sites', { reset: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load next policy.
|
||||||
|
*
|
||||||
|
* @param event Event.
|
||||||
|
*/
|
||||||
|
nextPolicy(event: Event): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (!this.pendingPolicies) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scrollTop();
|
||||||
|
|
||||||
|
if (this.numPolicy < this.pendingPolicies.length) {
|
||||||
|
this.numPolicy++;
|
||||||
|
this.setCurrentPolicy(this.pendingPolicies[this.numPolicy - 1]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All policies seen, display the consent form.
|
||||||
|
this.currentPolicy = undefined;
|
||||||
|
this.stepData = undefined;
|
||||||
|
this.showConsentForm = true;
|
||||||
|
this.title = Translate.instant('core.policy.consentpagetitle');
|
||||||
|
this.subTitle = Translate.instant('core.policy.agreepolicies');
|
||||||
|
|
||||||
|
this.logAnalyticsConsentFormView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set current policy.
|
||||||
|
*/
|
||||||
|
protected setCurrentPolicy(policy?: CorePolicySitePolicy): void {
|
||||||
|
if (!policy) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasScroll = false;
|
||||||
|
this.currentPolicy = policy;
|
||||||
|
this.title = policy.name || '';
|
||||||
|
this.subTitle = undefined;
|
||||||
|
this.stepData = !this.agreeInOwnPage ?
|
||||||
|
{ numpolicy: this.numPolicy, totalpolicies: this.pendingPolicies?.length ?? this.numPolicy } :
|
||||||
|
undefined;
|
||||||
|
|
||||||
|
this.logAnalyticsPolicyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the content has scroll.
|
||||||
|
*/
|
||||||
|
protected async checkScroll(): Promise<void> {
|
||||||
|
await CoreUtils.wait(400);
|
||||||
|
|
||||||
|
const scrollElement = await this.content?.getScrollElement();
|
||||||
|
|
||||||
|
this.hasScroll = !!scrollElement && scrollElement.scrollHeight > scrollElement.clientHeight;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit the acceptances to one or several policies.
|
* Submit the acceptances to one or several policies.
|
||||||
*
|
*
|
||||||
|
@ -157,27 +355,131 @@ export class CorePolicySitePolicyPage implements OnInit {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (!this.policyForm?.valid) {
|
if (!this.policiesForm?.valid) {
|
||||||
|
for (const name in this.policiesForm?.controls) {
|
||||||
|
this.policiesForm.controls[name].markAsDirty();
|
||||||
|
}
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
|
||||||
|
// Scroll to the first element with errors.
|
||||||
|
const errorFound = await CoreDom.scrollToInputError(
|
||||||
|
this.elementRef.nativeElement,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!errorFound) {
|
||||||
|
// Input not found, show an error modal.
|
||||||
|
CoreDomUtils.showErrorModal('core.policy.mustagreetocontinue', true);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
const modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (!this.isPoliciesURL) {
|
||||||
|
await this.acceptPendingPolicies();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await CorePolicy.acceptMandatorySitePolicies(this.siteId);
|
await CorePolicy.acceptMandatorySitePolicies(this.siteId);
|
||||||
|
|
||||||
// Success accepting, go to site initial page.
|
await this.finishAcceptingPolicies();
|
||||||
// Invalidate cache since some WS don't return error if site policy is not accepted.
|
|
||||||
await CoreUtils.ignoreErrors(this.currentSite.invalidateWsCache());
|
|
||||||
|
|
||||||
CoreEvents.trigger(CoreEvents.SITE_POLICY_AGREED, {}, this.siteId);
|
|
||||||
|
|
||||||
await CoreNavigator.navigateToSiteHome();
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'Error accepting site policy.');
|
CoreDomUtils.showErrorModalDefault(error, 'Error accepting site policies.');
|
||||||
} finally {
|
} finally {
|
||||||
modal.dismiss();
|
modal.dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept current pending policies.
|
||||||
|
*/
|
||||||
|
protected async acceptPendingPolicies(): Promise<void> {
|
||||||
|
if (!this.pendingPolicies) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const acceptances: Record<number, number> = {};
|
||||||
|
|
||||||
|
this.pendingPolicies?.forEach(policy => {
|
||||||
|
const control = this.policiesForm?.controls['agreepolicy' + policy.versionid];
|
||||||
|
if (!control) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.optional) {
|
||||||
|
if (control.value === null || control.value === undefined) {
|
||||||
|
// Not answered, this code shouldn't be reached. Display error.
|
||||||
|
CoreDomUtils.showErrorModal('core.policy.mustagreetocontinue', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptances[policy.versionid] = control.value;
|
||||||
|
} else {
|
||||||
|
if (!control.value) {
|
||||||
|
// Not answered, this code shouldn't be reached. Display error.
|
||||||
|
CoreDomUtils.showErrorModal('core.policy.mustagreetocontinue', true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptances[policy.versionid] = 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await CorePolicy.setUserAcceptances(acceptances, this.siteId);
|
||||||
|
|
||||||
|
await this.fetchNextPoliciesToAccept();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All mandatory policies have been accepted, go to site initial page.
|
||||||
|
*/
|
||||||
|
protected async finishAcceptingPolicies(): Promise<void> {
|
||||||
|
// Invalidate cache since some WS don't return error if site policy is not accepted.
|
||||||
|
await CoreUtils.ignoreErrors(this.currentSite.invalidateWsCache());
|
||||||
|
|
||||||
|
CoreEvents.trigger(CoreEvents.SITE_POLICY_AGREED, {}, this.siteId);
|
||||||
|
|
||||||
|
await CoreNavigator.navigateToSiteHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to top.
|
||||||
|
*
|
||||||
|
* @param event Event.
|
||||||
|
*/
|
||||||
|
scrollTop(event?: Event): void {
|
||||||
|
event?.preventDefault();
|
||||||
|
event?.stopPropagation();
|
||||||
|
|
||||||
|
this.content?.scrollToTop(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View the full policy.
|
||||||
|
*
|
||||||
|
* @param policy Policy.
|
||||||
|
*/
|
||||||
|
viewFullPolicy(policy: CorePolicySitePolicy): void {
|
||||||
|
CoreDomUtils.openModal({
|
||||||
|
component: CorePolicyViewPolicyModalComponent,
|
||||||
|
componentProps: { policy },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.layoutSubscription?.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SitePolicy = CorePolicySitePolicy & {
|
||||||
|
referToFullPolicyText?: string;
|
||||||
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
|
||||||
import { CoreSharedModule } from '@/core/shared.module';
|
import { CoreSharedModule } from '@/core/shared.module';
|
||||||
import { CorePolicySitePolicyPage } from '@features/policy/pages/site-policy/site-policy';
|
import { CorePolicySitePolicyPage } from '@features/policy/pages/site-policy/site-policy';
|
||||||
import { SITE_POLICY_PAGE_NAME } from './constants';
|
import { SITE_POLICY_PAGE_NAME } from './constants';
|
||||||
|
import { CorePolicyComponentsModule } from './components/components.module';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
|
@ -30,6 +31,7 @@ const routes: Routes = [
|
||||||
imports: [
|
imports: [
|
||||||
CoreSharedModule,
|
CoreSharedModule,
|
||||||
RouterModule.forChild(routes),
|
RouterModule.forChild(routes),
|
||||||
|
CorePolicyComponentsModule,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
CorePolicySitePolicyPage,
|
CorePolicySitePolicyPage,
|
||||||
|
|
|
@ -12,26 +12,13 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { APP_INITIALIZER, NgModule, Type } from '@angular/core';
|
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
|
|
||||||
import { AppRoutingModule } from '@/app/app-routing.module';
|
import { AppRoutingModule } from '@/app/app-routing.module';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { POLICY_PAGE_NAME } from './constants';
|
import { POLICY_PAGE_NAME } from './constants';
|
||||||
|
|
||||||
/**
|
|
||||||
* Get policy services.
|
|
||||||
*
|
|
||||||
* @returns Policy services.
|
|
||||||
*/
|
|
||||||
export async function getPolicyServices(): Promise<Type<unknown>[]> {
|
|
||||||
const { CorePolicyService } = await import('@features/policy/services/policy');
|
|
||||||
|
|
||||||
return [
|
|
||||||
CorePolicyService,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
path: POLICY_PAGE_NAME,
|
path: POLICY_PAGE_NAME,
|
||||||
|
|
|
@ -89,6 +89,38 @@ export class CorePolicyService {
|
||||||
return sitePolicy;
|
return sitePolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next policies to accept.
|
||||||
|
*
|
||||||
|
* @param options Options
|
||||||
|
* @returns Next pending policies
|
||||||
|
*/
|
||||||
|
async getNextPendingPolicies(options: CoreSitesCommonWSOptions = {}): Promise<CorePolicySitePolicy[]> {
|
||||||
|
const policies = await this.getUserAcceptances(options);
|
||||||
|
|
||||||
|
const pendingPolicies: CorePolicySitePolicy[] = [];
|
||||||
|
|
||||||
|
for (const i in policies) {
|
||||||
|
const policy = policies[i];
|
||||||
|
const hasAccepted = policy.acceptance?.status === 1;
|
||||||
|
const hasDeclined = policy.acceptance?.status === 0;
|
||||||
|
|
||||||
|
if (hasAccepted || (hasDeclined && policy.optional === 1)) {
|
||||||
|
// Policy already answered, ignore.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (policy.agreementstyle === CorePolicyAgreementStyle.OwnPage) {
|
||||||
|
// Policy needs to be accepted on its own page, it's the next policy to accept.
|
||||||
|
return [policy];
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPolicies.push(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pendingPolicies;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user acceptances.
|
* Get user acceptances.
|
||||||
*
|
*
|
||||||
|
@ -177,7 +209,7 @@ export class CorePolicyService {
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await site.write<CorePolicySetAcceptancesWSResponse>('tool_policy_get_user_acceptances', data);
|
const response = await site.write<CorePolicySetAcceptancesWSResponse>('tool_policy_set_acceptances_status', data);
|
||||||
if (response.warnings?.length) {
|
if (response.warnings?.length) {
|
||||||
throw new CoreWSError(response.warnings[0]);
|
throw new CoreWSError(response.warnings[0]);
|
||||||
}
|
}
|
||||||
|
@ -260,3 +292,11 @@ type CorePolicySetAcceptancesWSResponse = {
|
||||||
policyagreed: number; // Whether the user has provided acceptance to all current site policies. 1 if yes, 0 if not.
|
policyagreed: number; // Whether the user has provided acceptance to all current site policies. 1 if yes, 0 if not.
|
||||||
warnings?: CoreWSExternalWarning[];
|
warnings?: CoreWSExternalWarning[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agreement style.
|
||||||
|
*/
|
||||||
|
export enum CorePolicyAgreementStyle {
|
||||||
|
ConsentPage = 0, // Policy to be accepted together with others on the consent page.
|
||||||
|
OwnPage = 1, // Policy to be accepted on its own page before reaching the consent page.
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue