MOBILE-2768 policy: Support accepting all types of policies
This commit is contained in:
		
							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();
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								src/core/features/policy/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/core/features/policy/components/components.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user