MOBILE-2768 policy: Move site policy code to new feature

main
Dani Palou 2024-02-07 11:38:13 +01:00
parent e6ce19a91a
commit 521dc9eb6d
16 changed files with 287 additions and 104 deletions

View File

@ -2155,11 +2155,6 @@
"core.login.passwordforgotten": "moodle", "core.login.passwordforgotten": "moodle",
"core.login.passwordforgotteninstructions2": "moodle", "core.login.passwordforgotteninstructions2": "moodle",
"core.login.passwordrequired": "local_moodlemobileapp", "core.login.passwordrequired": "local_moodlemobileapp",
"core.login.policyaccept": "moodle",
"core.login.policyacceptmandatory": "local_moodlemobileapp",
"core.login.policyagree": "moodle",
"core.login.policyagreement": "moodle",
"core.login.policyagreementclick": "moodle",
"core.login.potentialidps": "auth", "core.login.potentialidps": "auth",
"core.login.profileinvaliddata": "admin", "core.login.profileinvaliddata": "admin",
"core.login.recaptchachallengeimage": "local_moodlemobileapp", "core.login.recaptchachallengeimage": "local_moodlemobileapp",
@ -2183,7 +2178,6 @@
"core.login.sitehasredirect": "local_moodlemobileapp", "core.login.sitehasredirect": "local_moodlemobileapp",
"core.login.siteinmaintenance": "local_moodlemobileapp", "core.login.siteinmaintenance": "local_moodlemobileapp",
"core.login.sitenotallowed": "local_moodlemobileapp", "core.login.sitenotallowed": "local_moodlemobileapp",
"core.login.sitepolicynotagreederror": "local_moodlemobileapp",
"core.login.siteurl": "local_moodlemobileapp", "core.login.siteurl": "local_moodlemobileapp",
"core.login.siteurlrequired": "local_moodlemobileapp", "core.login.siteurlrequired": "local_moodlemobileapp",
"core.login.startsignup": "moodle", "core.login.startsignup": "moodle",
@ -2288,6 +2282,12 @@
"core.phone": "moodle", "core.phone": "moodle",
"core.pictureof": "moodle", "core.pictureof": "moodle",
"core.play": "local_moodlemobileapp", "core.play": "local_moodlemobileapp",
"core.policy.policyaccept": "moodle",
"core.policy.policyacceptmandatory": "local_moodlemobileapp",
"core.policy.policyagree": "moodle",
"core.policy.policyagreement": "moodle",
"core.policy.policyagreementclick": "moodle",
"core.policy.sitepolicynotagreederror": "local_moodlemobileapp",
"core.previous": "moodle", "core.previous": "moodle",
"core.proceed": "moodle", "core.proceed": "moodle",
"core.publicprofile": "moodle", "core.publicprofile": "moodle",

View File

@ -669,7 +669,7 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite {
} else if (error.errorcode === 'sitepolicynotagreed') { } else if (error.errorcode === 'sitepolicynotagreed') {
// Site policy not agreed, trigger event. // Site policy not agreed, trigger event.
this.triggerSiteEvent(CoreEvents.SITE_POLICY_NOT_AGREED, {}); this.triggerSiteEvent(CoreEvents.SITE_POLICY_NOT_AGREED, {});
error.message = Translate.instant('core.login.sitepolicynotagreederror'); error.message = Translate.instant('core.policy.sitepolicynotagreederror');
throw new CoreWSError(error); throw new CoreWSError(error);
} else if (error.errorcode === 'dmlwriteexception' && CoreTextUtils.hasUnicodeData(data)) { } else if (error.errorcode === 'dmlwriteexception' && CoreTextUtils.hasUnicodeData(data)) {

View File

@ -46,6 +46,7 @@ import { CoreUserToursModule } from './usertours/user-tours.module';
import { CoreViewerModule } from './viewer/viewer.module'; import { CoreViewerModule } from './viewer/viewer.module';
import { CoreXAPIModule } from './xapi/xapi.module'; import { CoreXAPIModule } from './xapi/xapi.module';
import { CoreReportBuilderModule } from './reportbuilder/reportbuilder.module'; import { CoreReportBuilderModule } from './reportbuilder/reportbuilder.module';
import { CorePolicyModule } from './policy/policy.module';
@NgModule({ @NgModule({
imports: [ imports: [
@ -80,6 +81,7 @@ import { CoreReportBuilderModule } from './reportbuilder/reportbuilder.module';
CoreViewerModule, CoreViewerModule,
CoreXAPIModule, CoreXAPIModule,
CoreReportBuilderModule, CoreReportBuilderModule,
CorePolicyModule,
// Import last to allow overrides. // Import last to allow overrides.
CoreEmulatorModule, CoreEmulatorModule,

View File

@ -90,11 +90,6 @@
"passwordforgotten": "Forgotten password", "passwordforgotten": "Forgotten password",
"passwordforgotteninstructions2": "To reset your password, submit your username or your email address below. If we can find you in the database, an email will be sent to your email address, with instructions how to get access again.", "passwordforgotteninstructions2": "To reset your password, submit your username or your email address below. If we can find you in the database, an email will be sent to your email address, with instructions how to get access again.",
"passwordrequired": "Password required", "passwordrequired": "Password required",
"policyaccept": "I understand and agree",
"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?",
"policyagreement": "Site policy agreement",
"policyagreementclick": "Link to site policy agreement",
"potentialidps": "Log in using your account on:", "potentialidps": "Log in using your account on:",
"profileinvaliddata": "Invalid value", "profileinvaliddata": "Invalid value",
"recaptchachallengeimage": "reCAPTCHA challenge image", "recaptchachallengeimage": "reCAPTCHA challenge image",
@ -118,7 +113,6 @@
"sitehasredirect": "Your site contains at least one HTTP redirect. The app cannot follow redirects, this could be the issue that's preventing the app from connecting to your site.", "sitehasredirect": "Your site contains at least one HTTP redirect. The app cannot follow redirects, this could be the issue that's preventing the app from connecting to your site.",
"siteinmaintenance": "Your site is in maintenance mode", "siteinmaintenance": "Your site is in maintenance mode",
"sitenotallowed": "This site is no longer available.", "sitenotallowed": "This site is no longer available.",
"sitepolicynotagreederror": "Site policy not agreed.",
"siteurl": "Site URL", "siteurl": "Site URL",
"siteurlrequired": "Site URL required i.e <i>https://campus.example.edu</i>", "siteurlrequired": "Site URL required i.e <i>https://campus.example.edu</i>",
"startsignup": "Create new account", "startsignup": "Create new account",

View File

@ -20,7 +20,6 @@ import { hasSitesGuard } from './guards/has-sites';
import { CoreLoginComponentsModule } from './components/components.module'; import { CoreLoginComponentsModule } from './components/components.module';
import { CoreLoginHelper } from './services/login-helper'; import { CoreLoginHelper } from './services/login-helper';
import { CoreLoginForgottenPasswordPage } from '@features/login/pages/forgotten-password/forgotten-password'; import { CoreLoginForgottenPasswordPage } from '@features/login/pages/forgotten-password/forgotten-password';
import { CoreLoginSitePolicyPage } from '@features/login/pages/site-policy/site-policy';
import { CoreUserComponentsModule } from '@features/user/components/components.module'; import { CoreUserComponentsModule } from '@features/user/components/components.module';
import { CoreLoginEmailSignupPage } from '@features/login/pages/email-signup/email-signup'; import { CoreLoginEmailSignupPage } from '@features/login/pages/email-signup/email-signup';
import { CoreLoginSitePage } from '@features/login/pages/site/site'; import { CoreLoginSitePage } from '@features/login/pages/site/site';
@ -54,10 +53,6 @@ const routes: Routes = [
path: 'changepassword', path: 'changepassword',
component: CoreLoginChangePasswordPage, component: CoreLoginChangePasswordPage,
}, },
{
path: 'sitepolicy',
component: CoreLoginSitePolicyPage,
},
{ {
path: 'emailsignup', path: 'emailsignup',
component: CoreLoginEmailSignupPage, component: CoreLoginEmailSignupPage,
@ -77,7 +72,6 @@ const routes: Routes = [
], ],
declarations: [ declarations: [
CoreLoginForgottenPasswordPage, CoreLoginForgottenPasswordPage,
CoreLoginSitePolicyPage,
CoreLoginSitePage, CoreLoginSitePage,
CoreLoginSitesPage, CoreLoginSitesPage,
CoreLoginChangePasswordPage, CoreLoginChangePasswordPage,

View File

@ -183,19 +183,19 @@
<ng-container *ngIf="settings.sitepolicy"> <ng-container *ngIf="settings.sitepolicy">
<ion-item-divider class="ion-text-wrap"> <ion-item-divider class="ion-text-wrap">
<ion-label> <ion-label>
<h2>{{ 'core.login.policyagreement' | translate }}</h2> <h2>{{ 'core.policy.policyagreement' | translate }}</h2>
</ion-label> </ion-label>
</ion-item-divider> </ion-item-divider>
<ion-item class="ion-text-wrap"> <ion-item class="ion-text-wrap">
<ion-label> <ion-label>
<a [href]="settings.sitepolicy" core-link capture="false"> <a [href]="settings.sitepolicy" core-link capture="false">
{{ 'core.login.policyagreementclick' | translate }} {{ 'core.policy.policyagreementclick' | translate }}
</a> </a>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item class="ion-text-wrap"> <ion-item class="ion-text-wrap">
<ion-checkbox labelPlacement="start" justify="space-between" name="policyagreed" formControlName="policyagreed"> <ion-checkbox labelPlacement="start" justify="space-between" name="policyagreed" formControlName="policyagreed">
<p [core-mark-required]="true">{{ 'core.login.policyacceptmandatory' | translate }}</p> <p [core-mark-required]="true">{{ 'core.policy.policyacceptmandatory' | translate }}</p>
</ion-checkbox> </ion-checkbox>
<core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors" /> <core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors" />
</ion-item> </ion-item>

View File

@ -118,7 +118,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
}; };
this.passwordErrors = { required: 'core.login.passwordrequired' }; this.passwordErrors = { required: 'core.login.passwordrequired' };
this.emailErrors = { required: 'core.login.missingemail' }; this.emailErrors = { required: 'core.login.missingemail' };
this.policyErrors = { required: 'core.login.policyagree' }; this.policyErrors = { required: 'core.policy.policyagree' };
this.email2Errors = { this.email2Errors = {
required: 'core.login.missingemail', required: 'core.login.missingemail',
pattern: 'core.login.emailnotmatch', pattern: 'core.login.emailnotmatch',
@ -215,11 +215,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
* @returns Promise resolved when done. * @returns Promise resolved when done.
*/ */
protected async getSignupSettings(): Promise<void> { protected async getSignupSettings(): Promise<void> {
this.settings = await CoreWS.callAjax<AuthEmailSignupSettings>( this.settings = await CoreLoginHelper.getEmailSignupSettings(this.site.getURL());
'auth_email_get_signup_settings',
{},
{ siteUrl: this.site.getURL() },
);
if (CoreUserProfileFieldDelegate.hasRequiredUnsupportedField(this.settings.profilefields)) { if (CoreUserProfileFieldDelegate.hasRequiredUnsupportedField(this.settings.profilefields)) {
this.allRequiredSupported = false; this.allRequiredSupported = false;

View File

@ -57,6 +57,7 @@ import {
IDENTITY_PROVIDER_FEATURE_NAME_PREFIX, IDENTITY_PROVIDER_FEATURE_NAME_PREFIX,
} from '../constants'; } from '../constants';
import { LazyRoutesModule } from '@/app/app-routing.module'; import { LazyRoutesModule } from '@/app/app-routing.module';
import { CorePolicy } from '@features/policy/services/policy';
/** /**
* Helper provider that provides some common features regarding authentication. * Helper provider that provides some common features regarding authentication.
@ -87,30 +88,10 @@ export class CoreLoginHelperProvider {
* *
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @returns Promise resolved if success, rejected if failure. * @returns Promise resolved if success, rejected if failure.
* @deprecated since 4.4. Use CorePolicy.acceptMandatoryPolicies instead.
*/ */
async acceptSitePolicy(siteId?: string): Promise<void> { async acceptSitePolicy(siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId); return CorePolicy.acceptMandatorySitePolicies(siteId);
const result = await site.write<AgreeSitePolicyResult>('core_user_agree_site_policy', {});
if (result.status) {
return;
}
if (!result.warnings?.length) {
throw new CoreError('Cannot agree site policy');
}
// Check if there is a warning 'alreadyagreed'.
const found = result.warnings.some((warning) => warning.warningcode === 'alreadyagreed');
if (found) {
// Policy already agreed, treat it as a success.
return;
}
// Another warning, reject.
throw new CoreWSError(result.warnings[0]);
} }
/** /**
@ -286,36 +267,25 @@ export class CoreLoginHelperProvider {
return params && params.oauthsso !== undefined ? Number(params.oauthsso) : undefined; return params && params.oauthsso !== undefined ? Number(params.oauthsso) : undefined;
} }
/**
* Get email signup settings.
*
* @param siteUrl Site URL.
* @returns Signup settings.
*/
async getEmailSignupSettings(siteUrl: string): Promise<AuthEmailSignupSettings> {
return await CoreWS.callAjax('auth_email_get_signup_settings', {}, { siteUrl });
}
/** /**
* Get the site policy. * Get the site policy.
* *
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the site policy. * @returns Promise resolved with the site policy.
* @deprecated since 4.4. Use CorePolicy.getSitePoliciesURL instead.
*/ */
async getSitePolicy(siteId?: string): Promise<string> { async getSitePolicy(siteId?: string): Promise<string> {
const site = await CoreSites.getSite(siteId); return CorePolicy.getSitePoliciesURL(siteId);
let sitePolicy: string | undefined;
try {
// Try to get the latest config, maybe the site policy was just added or has changed.
sitePolicy = await site.getConfig('sitepolicy', true);
} catch (error) {
// Cannot get config, try to get the site policy using auth_email_get_signup_settings.
const settings = <AuthEmailSignupSettings> await CoreWS.callAjax(
'auth_email_get_signup_settings',
{},
{ siteUrl: site.getURL() },
);
sitePolicy = settings.sitepolicy;
}
if (!sitePolicy) {
throw new CoreError('Cannot retrieve site policy');
}
return sitePolicy;
} }
/** /**
@ -1067,20 +1037,11 @@ export class CoreLoginHelperProvider {
* Function called when site policy is not agreed. Reserved for core use. * Function called when site policy is not agreed. Reserved for core use.
* *
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @returns void
* @deprecated since 4.4. Use CorePolicy.goToAcceptSitePolicies instead.
*/ */
sitePolicyNotAgreed(siteId?: string): void { sitePolicyNotAgreed(siteId?: string): void {
siteId = siteId || CoreSites.getCurrentSiteId(); return CorePolicy.goToAcceptSitePolicies(siteId);
if (!siteId || siteId != CoreSites.getCurrentSiteId()) {
// Only current site allowed.
return;
}
// If current page is already site policy, stop.
if (CoreNavigator.isCurrent('/login/sitepolicy')) {
return;
}
CoreNavigator.navigate('/login/sitepolicy', { params: { siteId }, reset: true });
} }
/** /**
@ -1535,14 +1496,6 @@ export type CoreLoginSSOData = CoreRedirectPayload & {
ssoUrlParams?: CoreUrlParams; // Other params added to the login url. ssoUrlParams?: CoreUrlParams; // Other params added to the login url.
}; };
/**
* Result of WS core_user_agree_site_policy.
*/
type AgreeSitePolicyResult = {
status: boolean; // Status: true only if we set the policyagreed to 1 for the user.
warnings?: CoreWSExternalWarning[];
};
/** /**
* Result of WS auth_email_get_signup_settings. * Result of WS auth_email_get_signup_settings.
*/ */

View File

@ -0,0 +1,17 @@
// (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.
// Routing.
export const POLICY_PAGE_NAME = 'policy';
export const SITE_POLICY_PAGE_NAME = 'sitepolicy';

View File

@ -0,0 +1,8 @@
{
"policyaccept": "I understand and agree",
"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?",
"policyagreement": "Site policy agreement",
"policyagreementclick": "Link to site policy agreement",
"sitepolicynotagreederror": "Site policy not agreed."
}

View File

@ -5,7 +5,7 @@
</ion-buttons> </ion-buttons>
<ion-title> <ion-title>
<h1>{{ 'core.login.policyagreement' | translate }}</h1> <h1>{{ 'core.policy.policyagreement' | translate }}</h1>
</ion-title> </ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
@ -14,13 +14,13 @@
<ion-list *ngIf="sitePolicy"> <ion-list *ngIf="sitePolicy">
<ion-item class="ion-text-wrap"> <ion-item class="ion-text-wrap">
<ion-label> <ion-label>
<p>{{ 'core.login.policyagree' | translate }}</p> <p>{{ 'core.policy.policyagree' | translate }}</p>
</ion-label> </ion-label>
</ion-item> </ion-item>
<ion-item class="ion-text-wrap"> <ion-item class="ion-text-wrap">
<ion-label> <ion-label>
<p> <p>
<a [href]="sitePolicy" core-link [capture]="false">{{ 'core.login.policyagreementclick' | translate }}</a> <a [href]="sitePolicy" core-link [capture]="false">{{ 'core.policy.policyagreementclick' | translate }}</a>
</p> </p>
</ion-label> </ion-label>
</ion-item> </ion-item>
@ -28,10 +28,10 @@
<core-iframe [src]="sitePolicy" /> <core-iframe [src]="sitePolicy" />
</ion-card> </ion-card>
<ion-button class="ion-text-wrap ion-margin-horizontal" expand="block" (click)="accept()"> <ion-button class="ion-text-wrap ion-margin-horizontal" expand="block" (click)="accept()">
{{ 'core.login.policyacceptmandatory' | translate }} {{ 'core.policy.policyacceptmandatory' | translate }}
</ion-button> </ion-button>
<ion-button class="ion-text-wrap ion-margin-horizontal ion-margin-bottom" expand="block" fill="outline" (click)="cancel()"> <ion-button class="ion-text-wrap ion-margin-horizontal ion-margin-bottom" expand="block" fill="outline" (click)="cancel()">
{{ 'core.login.cancel' | translate }} {{ 'core.cancel' | translate }}
</ion-button> </ion-button>
</ion-list> </ion-list>
</core-loading> </core-loading>

View File

@ -18,22 +18,22 @@ import { CoreSites } 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';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreSite } from '@classes/sites/site'; import { CoreSite } from '@classes/sites/site';
import { CoreNavigator } from '@services/navigator'; 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';
/** /**
* Page to accept a site policy. * Page to accept a site policy.
*/ */
@Component({ @Component({
selector: 'page-core-login-site-policy', selector: 'page-core-policy-site-policy',
templateUrl: 'site-policy.html', templateUrl: 'site-policy.html',
styleUrls: ['site-policy.scss'], styleUrls: ['site-policy.scss'],
}) })
export class CoreLoginSitePolicyPage implements OnInit { export class CorePolicySitePolicyPage implements OnInit {
sitePolicy?: string; sitePolicy?: string;
showInline?: boolean; showInline?: boolean;
@ -76,7 +76,7 @@ export class CoreLoginSitePolicyPage implements OnInit {
*/ */
protected async fetchSitePolicy(): Promise<void> { protected async fetchSitePolicy(): Promise<void> {
try { try {
this.sitePolicy = await CoreLoginHelper.getSitePolicy(this.siteId); this.sitePolicy = await CorePolicy.getSitePoliciesURL(this.siteId);
} catch (error) { } catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'Error getting site policy.'); CoreDomUtils.showErrorModalDefault(error, 'Error getting site policy.');
this.cancel(); this.cancel();
@ -100,7 +100,7 @@ export class CoreLoginSitePolicyPage implements OnInit {
CoreAnalytics.logEvent({ CoreAnalytics.logEvent({
type: CoreAnalyticsEventType.VIEW_ITEM, type: CoreAnalyticsEventType.VIEW_ITEM,
ws: 'auth_email_get_signup_settings', ws: 'auth_email_get_signup_settings',
name: Translate.instant('core.login.policyagreement'), name: Translate.instant('core.policy.policyagreement'),
data: { category: 'policy' }, data: { category: 'policy' },
url: '/user/policy.php', url: '/user/policy.php',
}); });
@ -126,7 +126,7 @@ export class CoreLoginSitePolicyPage implements OnInit {
const modal = await CoreDomUtils.showModalLoading('core.sending', true); const modal = await CoreDomUtils.showModalLoading('core.sending', true);
try { try {
await CoreLoginHelper.acceptSitePolicy(this.siteId); await CorePolicy.acceptMandatorySitePolicies(this.siteId);
// Success accepting, go to site initial page. // Success accepting, go to site initial page.
// Invalidate cache since some WS don't return error if site policy is not accepted. // Invalidate cache since some WS don't return error if site policy is not accepted.

View File

@ -0,0 +1,38 @@
// (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 { RouterModule, Routes } from '@angular/router';
import { CoreSharedModule } from '@/core/shared.module';
import { CorePolicySitePolicyPage } from '@features/policy/pages/site-policy/site-policy';
import { SITE_POLICY_PAGE_NAME } from './constants';
const routes: Routes = [
{
path: SITE_POLICY_PAGE_NAME,
component: CorePolicySitePolicyPage,
},
];
@NgModule({
imports: [
CoreSharedModule,
RouterModule.forChild(routes),
],
declarations: [
CorePolicySitePolicyPage,
],
})
export class CorePolicyLazyModule {}

View File

@ -0,0 +1,60 @@
// (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 { APP_INITIALIZER, NgModule, Type } from '@angular/core';
import { Routes } from '@angular/router';
import { AppRoutingModule } from '@/app/app-routing.module';
import { CoreEvents } from '@singletons/events';
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 = [
{
path: POLICY_PAGE_NAME,
loadChildren: () => import('./policy-lazy.module').then(m => m.CorePolicyLazyModule),
},
];
@NgModule({
imports: [
AppRoutingModule.forChild(routes),
],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
useValue: async () => {
CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, async (data) => {
const { CorePolicy } = await import('@features/policy/services/policy');
CorePolicy.goToAcceptSitePolicies(data.siteId);
});
},
},
],
})
export class CorePolicyModule {}

View File

@ -0,0 +1,121 @@
// (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 { Injectable } from '@angular/core';
import { CoreError } from '@classes/errors/error';
import { CoreWSError } from '@classes/errors/wserror';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreWSExternalWarning } from '@services/ws';
import { makeSingleton } from '@singletons';
import { POLICY_PAGE_NAME, SITE_POLICY_PAGE_NAME } from '../constants';
/**
* Service that provides some common features regarding policies.
*/
@Injectable({ providedIn: 'root' })
export class CorePolicyService {
/**
* Accept all mandatory site policies.
*
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved if success, rejected if failure.
*/
async acceptMandatorySitePolicies(siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
const result = await site.write<AgreeSitePolicyResult>('core_user_agree_site_policy', {});
if (result.status) {
return;
}
if (!result.warnings?.length) {
throw new CoreError('Cannot agree site policy');
}
// Check if there is a warning 'alreadyagreed'.
const found = result.warnings.some((warning) => warning.warningcode === 'alreadyagreed');
if (found) {
// Policy already agreed, treat it as a success.
return;
}
// Another warning, reject.
throw new CoreWSError(result.warnings[0]);
}
/**
* Get the URL to view the site policy (or all the site policies in a single page if there's more than one).
*
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the site policy.
*/
async getSitePoliciesURL(siteId?: string): Promise<string> {
const site = await CoreSites.getSite(siteId);
let sitePolicy: string | undefined;
try {
// Try to get the latest config, maybe the site policy was just added or has changed.
sitePolicy = await site.getConfig('sitepolicy', true);
} catch (error) {
// Cannot get config, try to get the site policy using signup settings.
const settings = await CoreLoginHelper.getEmailSignupSettings(site.getURL());
sitePolicy = settings.sitepolicy;
}
if (!sitePolicy) {
throw new CoreError('Cannot retrieve site policy');
}
return sitePolicy;
}
/**
* Open page to accept site policies.
*
* @param siteId Site ID. If not defined, current site.
*/
goToAcceptSitePolicies(siteId?: string): void {
siteId = siteId || CoreSites.getCurrentSiteId();
if (!siteId || siteId != CoreSites.getCurrentSiteId()) {
// Only current site allowed.
return;
}
const routePath = `/${POLICY_PAGE_NAME}/${SITE_POLICY_PAGE_NAME}`;
// If current page is already site policy, stop.
if (CoreNavigator.isCurrent(routePath)) {
return;
}
CoreNavigator.navigate(routePath, { params: { siteId }, reset: true });
}
}
export const CorePolicy = makeSingleton(CorePolicyService);
/**
* Result of WS core_user_agree_site_policy.
*/
type AgreeSitePolicyResult = {
status: boolean; // Status: true only if we set the policyagreed to 1 for the user.
warnings?: CoreWSExternalWarning[];
};