diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts
index 186ba464c..5a05c71b2 100644
--- a/src/core/features/login/pages/email-signup/email-signup.ts
+++ b/src/core/features/login/pages/email-signup/email-signup.ts
@@ -118,7 +118,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
};
this.passwordErrors = { required: 'core.login.passwordrequired' };
this.emailErrors = { required: 'core.login.missingemail' };
- this.policyErrors = { required: 'core.login.policyagree' };
+ this.policyErrors = { required: 'core.policy.policyagree' };
this.email2Errors = {
required: 'core.login.missingemail',
pattern: 'core.login.emailnotmatch',
@@ -215,11 +215,7 @@ export class CoreLoginEmailSignupPage implements OnInit {
* @returns Promise resolved when done.
*/
protected async getSignupSettings(): Promise {
- this.settings = await CoreWS.callAjax(
- 'auth_email_get_signup_settings',
- {},
- { siteUrl: this.site.getURL() },
- );
+ this.settings = await CoreLoginHelper.getEmailSignupSettings(this.site.getURL());
if (CoreUserProfileFieldDelegate.hasRequiredUnsupportedField(this.settings.profilefields)) {
this.allRequiredSupported = false;
diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts
index c80fc521e..a2aded067 100644
--- a/src/core/features/login/services/login-helper.ts
+++ b/src/core/features/login/services/login-helper.ts
@@ -57,6 +57,7 @@ import {
IDENTITY_PROVIDER_FEATURE_NAME_PREFIX,
} from '../constants';
import { LazyRoutesModule } from '@/app/app-routing.module';
+import { CorePolicy } from '@features/policy/services/policy';
/**
* 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.
* @returns Promise resolved if success, rejected if failure.
+ * @deprecated since 4.4. Use CorePolicy.acceptMandatoryPolicies instead.
*/
async acceptSitePolicy(siteId?: string): Promise {
- const site = await CoreSites.getSite(siteId);
-
- const result = await site.write('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]);
-
+ return CorePolicy.acceptMandatorySitePolicies(siteId);
}
/**
@@ -286,36 +267,25 @@ export class CoreLoginHelperProvider {
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 {
+ return await CoreWS.callAjax('auth_email_get_signup_settings', {}, { siteUrl });
+ }
+
/**
* Get the site policy.
*
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the site policy.
+ * @deprecated since 4.4. Use CorePolicy.getSitePoliciesURL instead.
*/
async getSitePolicy(siteId?: string): Promise {
- 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 auth_email_get_signup_settings.
- const settings = 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;
+ return CorePolicy.getSitePoliciesURL(siteId);
}
/**
@@ -1067,20 +1037,11 @@ export class CoreLoginHelperProvider {
* Function called when site policy is not agreed. Reserved for core use.
*
* @param siteId Site ID. If not defined, current site.
+ * @returns void
+ * @deprecated since 4.4. Use CorePolicy.goToAcceptSitePolicies instead.
*/
sitePolicyNotAgreed(siteId?: string): void {
- siteId = siteId || CoreSites.getCurrentSiteId();
- 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 });
+ return CorePolicy.goToAcceptSitePolicies(siteId);
}
/**
@@ -1535,14 +1496,6 @@ export type CoreLoginSSOData = CoreRedirectPayload & {
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.
*/
diff --git a/src/core/features/policy/constants.ts b/src/core/features/policy/constants.ts
new file mode 100644
index 000000000..c341a326a
--- /dev/null
+++ b/src/core/features/policy/constants.ts
@@ -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';
diff --git a/src/core/features/policy/lang.json b/src/core/features/policy/lang.json
new file mode 100644
index 000000000..66fdf8295
--- /dev/null
+++ b/src/core/features/policy/lang.json
@@ -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."
+}
diff --git a/src/core/features/login/pages/site-policy/site-policy.html b/src/core/features/policy/pages/site-policy/site-policy.html
similarity index 78%
rename from src/core/features/login/pages/site-policy/site-policy.html
rename to src/core/features/policy/pages/site-policy/site-policy.html
index 76ca02525..b17608f30 100644
--- a/src/core/features/login/pages/site-policy/site-policy.html
+++ b/src/core/features/policy/pages/site-policy/site-policy.html
@@ -5,7 +5,7 @@
-
- {{ errorMessages[error] | translate }}
+
+
+ {{ errorMessages[error] | translate }}
+
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
diff --git a/src/core/features/login/login.module.ts b/src/core/features/login/login.module.ts
index 4a1e6244d..7d15b2175 100644
--- a/src/core/features/login/login.module.ts
+++ b/src/core/features/login/login.module.ts
@@ -62,10 +62,6 @@ const appRoutes: Routes = [
CoreLoginHelper.passwordChangeForced(data.siteId);
});
- CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data) => {
- CoreLoginHelper.sitePolicyNotAgreed(data.siteId);
- });
-
await CoreLoginHelper.initialize();
},
},
diff --git a/src/core/features/policy/components/components.module.ts b/src/core/features/policy/components/components.module.ts
new file mode 100644
index 000000000..21c3b118b
--- /dev/null
+++ b/src/core/features/policy/components/components.module.ts
@@ -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 {}
diff --git a/src/core/features/policy/components/policy-modal/policy-modal.html b/src/core/features/policy/components/policy-modal/policy-modal.html
new file mode 100644
index 000000000..b555e7f34
--- /dev/null
+++ b/src/core/features/policy/components/policy-modal/policy-modal.html
@@ -0,0 +1,24 @@
+
+
+
+
{{ policy.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/features/policy/components/policy-modal/policy-modal.ts b/src/core/features/policy/components/policy-modal/policy-modal.ts
new file mode 100644
index 000000000..d9f9a6da2
--- /dev/null
+++ b/src/core/features/policy/components/policy-modal/policy-modal.ts
@@ -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();
+ }
+
+}
diff --git a/src/core/features/policy/lang.json b/src/core/features/policy/lang.json
index 12b54abe9..fd53e1f17 100644
--- a/src/core/features/policy/lang.json
+++ b/src/core/features/policy/lang.json
@@ -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}}",
+ "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",
"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."
+ "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}}"
}
diff --git a/src/core/features/policy/pages/site-policy/site-policy.html b/src/core/features/policy/pages/site-policy/site-policy.html
index 160a2b81b..8054a867e 100644
--- a/src/core/features/policy/pages/site-policy/site-policy.html
+++ b/src/core/features/policy/pages/site-policy/site-policy.html
@@ -17,13 +17,22 @@
-
+
diff --git a/src/core/features/policy/pages/acceptances/acceptances.scss b/src/core/features/policy/pages/acceptances/acceptances.scss
new file mode 100644
index 000000000..eca3e66bd
--- /dev/null
+++ b/src/core/features/policy/pages/acceptances/acceptances.scss
@@ -0,0 +1,129 @@
+@use "theme/globals" as *;
+
+:host {
+ .core-policy-revision ion-badge {
+ @include margin-horizontal(0px, 4px);
+ }
+
+ .core-policy-responseby-name {
+ color: var(--core-link-color);
+ text-decoration: none;
+ }
+
+ .core-policy-user-agreement-info {
+ display: flex;
+ flex-flow: row nowrap;
+ align-items: center;
+ justify-content: flex-start;
+
+ ion-icon {
+ @include margin-horizontal(0px, 8px);
+ font-size: 1rem;
+ }
+
+ ion-button {
+ --background: transparent;
+ color: var(--ion-color-primary);
+ margin-top: 0px;
+ margin-bottom: 0px;
+ text-transform: none;
+
+ &:first-child:not(:last-child) {
+ @include margin-horizontal(null, 0px);
+ }
+ &:not(:first-child) {
+ @include margin-horizontal(0px, null);
+ }
+ }
+ }
+
+ .core-policy-mobile-container {
+ .core-policy-title div[slot="start"] {
+ display: flex;
+ align-items: center;
+ margin: 0px;
+
+ ion-icon {
+ font-size: 1.125rem;
+ margin: 0px;
+ }
+ }
+
+ .core-policy-details {
+ .item:not(.core-policy-title) {
+ --background: var(--gray-100);
+ }
+
+ .core-policy-user-agreement.core-policy-agreement-has-actions {
+ ion-label {
+ margin-bottom: 0px;
+ }
+
+ .core-policy-user-agreement-status {
+ line-height: 44px;
+ }
+ }
+
+ .core-policy-user-agreement-info {
+ flex-wrap: wrap;
+ justify-content: flex-end;
+
+ .core-policy-user-agreement-status {
+ flex-grow: 1;
+ }
+ }
+ }
+ }
+
+ table.core-policy-tablet-container {
+ th {
+ &:first-child {
+ @include padding-horizontal(24px, 0px);
+ }
+ }
+
+ th, td {
+ min-width: 200px;
+ text-wrap: nowrap;
+ }
+
+ p {
+ margin: 0px;
+ }
+
+ .core-policy-title {
+ display: flex;
+ align-items: center;
+
+ ion-icon {
+ font-size: 1.125rem;
+ padding: 13px;
+ margin: 3px;
+ }
+
+ .core-policy-icon-placeholder {
+ visibility: hidden;
+ }
+ }
+
+ .core-policy-revision p {
+ margin-bottom: 4px;
+ }
+
+ }
+
+ .core-policy-mobile-container .core-policy-title .expandable-status-icon,
+ .core-policy-tablet-container .core-policy-title ion-icon {
+ min-height: auto;
+ min-width: auto;
+ font-size: 1.125rem;
+ padding: 13px;
+ }
+
+ .core-policy-title .expandable-status-icon {
+ border-radius: 50%;
+ &:hover {
+ background: var(--secondary);
+ }
+ }
+}
diff --git a/src/core/features/policy/pages/acceptances/acceptances.ts b/src/core/features/policy/pages/acceptances/acceptances.ts
new file mode 100644
index 000000000..0212671ec
--- /dev/null
+++ b/src/core/features/policy/pages/acceptances/acceptances.ts
@@ -0,0 +1,284 @@
+// (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, OnDestroy, OnInit } from '@angular/core';
+
+import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
+import { CoreDomUtils } from '@services/utils/dom';
+import { CoreUtils } from '@services/utils/utils';
+import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
+import { Translate } from '@singletons';
+import { CorePolicy, CorePolicySitePolicy, CorePolicyStatus } from '@features/policy/services/policy';
+import { CorePolicyViewPolicyModalComponent } from '@features/policy/components/policy-modal/policy-modal';
+import { CoreTime } from '@singletons/time';
+import { CoreScreen } from '@services/screen';
+import { Subscription } from 'rxjs';
+import { CORE_DATAPRIVACY_PAGE_NAME } from '@features/dataprivacy/constants';
+import { CoreNavigator } from '@services/navigator';
+import { CoreDataPrivacy } from '@features/dataprivacy/services/dataprivacy';
+
+/**
+ * Page to view user acceptances.
+ */
+@Component({
+ selector: 'page-core-policy-acceptances',
+ templateUrl: 'acceptances.html',
+ styleUrls: ['acceptances.scss'],
+})
+export class CorePolicyAcceptancesPage implements OnInit, OnDestroy {
+
+ dataLoaded = false;
+ policies: ActiveSitePolicy[] = [];
+ activeStatus = CorePolicyStatus.Active;
+ isTablet = false;
+ hasOnBehalf = false;
+ canContactDPO = false;
+
+ protected logView: () => void;
+ protected layoutSubscription?: Subscription;
+
+ constructor() {
+ this.logView = CoreTime.once(() => {
+ const currentUserId = CoreSites.getCurrentSiteUserId();
+
+ CoreAnalytics.logEvent({
+ type: CoreAnalyticsEventType.VIEW_ITEM_LIST,
+ ws: 'tool_policy_get_user_acceptances',
+ name: Translate.instant('core.policy.policiesagreements'),
+ data: { userid: currentUserId },
+ url: `/admin/tool/policy/user.php?userid=${currentUserId}`,
+ });
+ });
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnInit(): void {
+ this.isTablet = CoreScreen.isTablet;
+ this.layoutSubscription = CoreScreen.layoutObservable.subscribe(() => {
+ this.isTablet = CoreScreen.isTablet;
+ });
+
+ this.fetchCanContactDPO();
+ this.fetchAcceptances().finally(() => {
+ this.dataLoaded = true;
+ });
+ }
+
+ /**
+ * Check if user can contact DPO.
+ */
+ protected async fetchCanContactDPO(): Promise {
+ this.canContactDPO = await CoreUtils.ignoreErrors(CoreDataPrivacy.isEnabled(), false);
+ }
+
+ /**
+ * Fetch the policies and acceptances.
+ *
+ * @returns Promise resolved when done.
+ */
+ protected async fetchAcceptances(): Promise {
+ try {
+ const allPolicies = await CorePolicy.getUserAcceptances();
+
+ this.hasOnBehalf = false;
+
+ const policiesById = allPolicies.reduce((groupedPolicies, policy) => {
+ const formattedPolicy = this.formatSitePolicy(policy);
+ this.hasOnBehalf = this.hasOnBehalf || formattedPolicy.onBehalf;
+
+ groupedPolicies[policy.policyid] = groupedPolicies[policy.policyid] || [];
+ groupedPolicies[policy.policyid].push(formattedPolicy);
+
+ return groupedPolicies;
+ }, > {});
+
+ this.policies = [];
+ for (const policyId in policiesById) {
+ const policyVersions = policiesById[policyId];
+
+ let activePolicy: ActiveSitePolicy | undefined =
+ policyVersions.find((policy) => policy.status === CorePolicyStatus.Active);
+ if (!activePolicy) {
+ // No active policy, it shouldn't happen. Use the one with highest versionid.
+ policyVersions.sort((a, b) => b.versionid - a.versionid);
+ activePolicy = policyVersions[0];
+ }
+
+ activePolicy.previousVersions = policyVersions.filter(policy => policy !== activePolicy);
+ this.policies.push(activePolicy);
+ }
+
+ this.logView();
+ } catch (error) {
+ CoreDomUtils.showErrorModalDefault(error, 'Error getting policies.');
+ }
+ }
+
+ /**
+ * Format a site policy, adding some calculated data.
+ *
+ * @param policy Policy to format.
+ * @param expanded Whether the policy should be expanded or not.
+ * @returns Formatted policy.
+ */
+ protected formatSitePolicy(policy: CorePolicySitePolicy, expanded = false): SitePolicy {
+ const hasAccepted = policy.acceptance?.status === 1;
+ const hasDeclined = policy.acceptance?.status === 0;
+ const onBehalf = !!policy.acceptance && policy.acceptance.usermodified !== CoreSites.getCurrentSiteUserId();
+
+ return {
+ ...policy,
+ expanded,
+ hasAccepted,
+ hasDeclined,
+ onBehalf,
+ hasActions: hasDeclined || !hasAccepted || !!policy.optional,
+ };
+ }
+
+ /**
+ * Refresh the data.
+ *
+ * @param refresher Refresher.
+ */
+ async refreshAcceptances(refresher?: HTMLIonRefresherElement): Promise {
+ await CoreUtils.ignoreErrors(CorePolicy.invalidateAcceptances());
+
+ await CoreUtils.ignoreErrors(this.fetchAcceptances());
+
+ refresher?.complete();
+ }
+
+ /**
+ * Toogle the visibility of a policy (expand/collapse).
+ *
+ * @param event Event.
+ * @param policy Policy.
+ */
+ toggle(event: Event, policy: SitePolicy): void {
+ event.preventDefault();
+ event.stopPropagation();
+ policy.expanded = !policy.expanded;
+ }
+
+ /**
+ * View the full policy.
+ *
+ * @param event Event.
+ * @param policy Policy.
+ */
+ viewFullPolicy(event: Event, policy: CorePolicySitePolicy): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ CoreDomUtils.openModal({
+ component: CorePolicyViewPolicyModalComponent,
+ componentProps: { policy },
+ });
+ }
+
+ /**
+ * Set the acceptance of a policy.
+ *
+ * @param event Event.
+ * @param policy Policy
+ * @param accept Whether to accept or not.
+ */
+ async setAcceptance(event: Event, policy: SitePolicy, accept: boolean): Promise {
+ event.preventDefault();
+ event.stopPropagation();
+
+ const modal = await CoreDomUtils.showModalLoading('core.sending', true);
+
+ try {
+ await CorePolicy.setUserAcceptances({ [policy.versionid]: accept ? 1 : 0 });
+
+ await this.updatePolicyAcceptance(policy, accept);
+ } catch (error) {
+ CoreDomUtils.showErrorModalDefault(error, 'Error changing policy status.');
+ } finally {
+ modal.dismiss();
+ }
+ }
+
+ /**
+ * Update the acceptance data for a certain policy.
+ *
+ * @param policy Policy to update.
+ * @param accepted Whether the policy has just been accepted or declined.
+ */
+ protected async updatePolicyAcceptance(policy: SitePolicy, accepted: boolean): Promise {
+ try {
+ const policies = await CorePolicy.getUserAcceptances({ readingStrategy: CoreSitesReadingStrategy.ONLY_NETWORK });
+
+ const newPolicy = policies.find((p) => p.versionid === policy.versionid);
+
+ if (!newPolicy) {
+ throw new Error('Policy not found.');
+ }
+
+ policy.acceptance = newPolicy.acceptance;
+ } catch (error) {
+ // Error updating the acceptance, calculate it in the app.
+ policy.acceptance = {
+ status: accepted ? 1 : 0,
+ lang: policy.acceptance?.lang ?? 'en',
+ timemodified: Date.now(),
+ usermodified: CoreSites.getCurrentSiteUserId(),
+ };
+ }
+
+ Object.assign(policy, this.formatSitePolicy(policy, policy.expanded));
+ }
+
+ /**
+ * Open page to contact DPO.
+ *
+ * @param event Event.
+ */
+ openContactDPO(event: Event): void {
+ event.preventDefault();
+ event.stopPropagation();
+
+ CoreNavigator.navigateToSitePath(CORE_DATAPRIVACY_PAGE_NAME);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ ngOnDestroy(): void {
+ this.layoutSubscription?.unsubscribe();
+ }
+
+}
+
+/**
+ * Site policy with some calculated data.
+ */
+type SitePolicy = CorePolicySitePolicy & {
+ expanded: boolean;
+ hasAccepted: boolean;
+ hasDeclined: boolean;
+ onBehalf: boolean;
+ hasActions: boolean;
+};
+
+/**
+ * Active site policy with some calculated data.
+ */
+type ActiveSitePolicy = SitePolicy & {
+ previousVersions?: SitePolicy[];
+};
diff --git a/src/core/features/policy/pages/site-policy/site-policy.scss b/src/core/features/policy/pages/site-policy/site-policy.scss
index 9d5a17fa8..b19040ec6 100644
--- a/src/core/features/policy/pages/site-policy/site-policy.scss
+++ b/src/core/features/policy/pages/site-policy/site-policy.scss
@@ -73,7 +73,7 @@
}
}
- .item ::ng-deep ion-label p span {
+ .item ::ng-deep ion-label .core-site-policy-full-policy-link {
color: var(--core-link-color);
}
diff --git a/src/core/features/policy/policy-lazy.module.ts b/src/core/features/policy/policy-lazy.module.ts
index eb49beae2..660117563 100644
--- a/src/core/features/policy/policy-lazy.module.ts
+++ b/src/core/features/policy/policy-lazy.module.ts
@@ -17,14 +17,19 @@ 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';
+import { ACCEPTANCES_PAGE_NAME, SITE_POLICY_PAGE_NAME } from './constants';
import { CorePolicyComponentsModule } from './components/components.module';
+import { CorePolicyAcceptancesPage } from './pages/acceptances/acceptances';
const routes: Routes = [
{
path: SITE_POLICY_PAGE_NAME,
component: CorePolicySitePolicyPage,
},
+ {
+ path: ACCEPTANCES_PAGE_NAME,
+ component: CorePolicyAcceptancesPage,
+ },
];
@NgModule({
@@ -35,6 +40,7 @@ const routes: Routes = [
],
declarations: [
CorePolicySitePolicyPage,
+ CorePolicyAcceptancesPage,
],
})
export class CorePolicyLazyModule {}
diff --git a/src/core/features/policy/policy.module.ts b/src/core/features/policy/policy.module.ts
index 54ace343d..407713570 100644
--- a/src/core/features/policy/policy.module.ts
+++ b/src/core/features/policy/policy.module.ts
@@ -18,6 +18,11 @@ import { Routes } from '@angular/router';
import { AppRoutingModule } from '@/app/app-routing.module';
import { CoreEvents } from '@singletons/events';
import { POLICY_PAGE_NAME } from './constants';
+import { CoreUserDelegate } from '@features/user/services/user-delegate';
+import { CorePolicyUserHandler } from './services/handlers/user';
+import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
+import { CorePolicyAcceptancesLinkHandler } from './services/handlers/acceptances-link';
+import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
const routes: Routes = [
{
@@ -29,12 +34,16 @@ const routes: Routes = [
@NgModule({
imports: [
AppRoutingModule.forChild(routes),
+ CoreMainMenuTabRoutingModule.forChild(routes),
],
providers: [
{
provide: APP_INITIALIZER,
multi: true,
useValue: async () => {
+ CoreUserDelegate.registerHandler(CorePolicyUserHandler.instance);
+ CoreContentLinksDelegate.registerHandler(CorePolicyAcceptancesLinkHandler.instance);
+
CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, async (data) => {
const { CorePolicy } = await import('@features/policy/services/policy');
diff --git a/src/core/features/policy/services/handlers/acceptances-link.ts b/src/core/features/policy/services/handlers/acceptances-link.ts
new file mode 100644
index 000000000..047de6610
--- /dev/null
+++ b/src/core/features/policy/services/handlers/acceptances-link.ts
@@ -0,0 +1,63 @@
+// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
+import { CoreNavigator } from '@services/navigator';
+import { makeSingleton } from '@singletons';
+import { CorePolicy } from '../policy';
+import { ACCEPTANCES_PAGE_NAME, POLICY_PAGE_NAME } from '@features/policy/constants';
+import { CoreSites } from '@services/sites';
+
+/**
+ * Handler to treat links to policy acceptances page.
+ */
+@Injectable({ providedIn: 'root' })
+export class CorePolicyAcceptancesLinkHandlerService extends CoreContentLinksHandlerBase {
+
+ name = 'CorePolicyAcceptancesLinkHandler';
+ pattern = /\/admin\/tool\/policy\/user\.php/;
+ featureName = 'CoreUserDelegate_CorePolicy';
+
+ /**
+ * @inheritdoc
+ */
+ getActions(): CoreContentLinksAction[] {
+ return [{
+ action: async (siteId: string): Promise => {
+ await CoreNavigator.navigateToSitePath(`/${POLICY_PAGE_NAME}/${ACCEPTANCES_PAGE_NAME}`, { siteId });
+ },
+ }];
+ }
+
+ /**
+ * @inheritdoc
+ */
+ async isEnabled(siteId: string, url: string, params: Record): Promise {
+ const site = await CoreSites.getSite(siteId);
+ const userId = Number(params.userid);
+
+ if (userId && userId !== site.getUserId()) {
+ // Only viewing your own policies is supported.
+ return false;
+ }
+
+ return CorePolicy.isManageAcceptancesAvailable(siteId);
+ }
+
+}
+
+export const CorePolicyAcceptancesLinkHandler = makeSingleton(CorePolicyAcceptancesLinkHandlerService);
diff --git a/src/core/features/policy/services/handlers/user.ts b/src/core/features/policy/services/handlers/user.ts
new file mode 100644
index 000000000..730f8401a
--- /dev/null
+++ b/src/core/features/policy/services/handlers/user.ts
@@ -0,0 +1,85 @@
+// (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 {
+ CoreUserDelegateContext,
+ CoreUserProfileHandlerType,
+ CoreUserProfileHandler,
+ CoreUserProfileHandlerData,
+} from '@features/user/services/user-delegate';
+import { CorePolicy } from '../policy';
+import { CoreSites } from '@services/sites';
+import { makeSingleton } from '@singletons';
+import { CoreNavigator } from '@services/navigator';
+import { CoreUserProfile } from '@features/user/services/user';
+import { ACCEPTANCES_PAGE_NAME, POLICY_PAGE_NAME } from '@features/policy/constants';
+
+/**
+ * Handler to inject an option into user menu.
+ */
+@Injectable({ providedIn: 'root' })
+export class CorePolicyUserHandlerService implements CoreUserProfileHandler {
+
+ type = CoreUserProfileHandlerType.LIST_ACCOUNT_ITEM;
+ name = 'CorePolicy';
+ priority = 50;
+
+ /**
+ * @inheritdoc
+ */
+ async isEnabled(): Promise {
+ const wsAvailable = await CorePolicy.isManageAcceptancesAvailable();
+ if (!wsAvailable) {
+ return false;
+ }
+
+ const policyHandler = await CoreSites.getCurrentSite()?.getConfig('sitepolicyhandler');
+
+ return policyHandler === 'tool_policy';
+ }
+
+ /**
+ * @inheritdoc
+ */
+ async isEnabledForContext(context: CoreUserDelegateContext): Promise {
+ return context === CoreUserDelegateContext.USER_MENU;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ async isEnabledForUser(user: CoreUserProfile): Promise {
+ return user.id == CoreSites.getCurrentSiteUserId();
+ }
+
+ /**
+ * @inheritdoc
+ */
+ getDisplayData(): CoreUserProfileHandlerData {
+ return {
+ icon: 'fas-file-shield',
+ title: 'core.policy.policiesagreements',
+ class: 'core-policy-user-handler',
+ action: (event): void => {
+ event.preventDefault();
+ event.stopPropagation();
+ CoreNavigator.navigateToSitePath(`/${POLICY_PAGE_NAME}/${ACCEPTANCES_PAGE_NAME}`);
+ },
+ };
+ }
+
+}
+
+export const CorePolicyUserHandler = makeSingleton(CorePolicyUserHandlerService);
diff --git a/src/core/features/policy/services/policy.ts b/src/core/features/policy/services/policy.ts
index c5caa910e..82caaafeb 100644
--- a/src/core/features/policy/services/policy.ts
+++ b/src/core/features/policy/services/policy.ts
@@ -127,14 +127,15 @@ export class CorePolicyService {
* @param options Options
* @returns List of policies with their acceptances.
*/
- async getUserAcceptances(options: CoreSitesCommonWSOptions = {}): Promise {
+ async getUserAcceptances(options: CorePolicyGetAcceptancesOptions = {}): Promise {
const site = await CoreSites.getSite(options.siteId);
+ const userId = options.userId || site.getUserId();
const data: CorePolicyGetUserAcceptancesWSParams = {
- userid: site.getUserId(),
+ userid: userId,
};
const preSets = {
- cacheKey: this.getUserAcceptancesCacheKey(site.getUserId()),
+ cacheKey: this.getUserAcceptancesCacheKey(userId),
updateFrequency: CoreSite.FREQUENCY_RARELY,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy),
};
@@ -179,6 +180,18 @@ export class CorePolicyService {
CoreNavigator.navigate(routePath, { params: { siteId }, reset: true });
}
+ /**
+ * Invalidate acceptances WS call.
+ *
+ * @param options Options.
+ * @returns Promise resolved when data is invalidated.
+ */
+ async invalidateAcceptances(options: {userId?: number; siteId?: string} = {}): Promise {
+ const site = await CoreSites.getSite(options.siteId);
+
+ await site.invalidateWsCacheForKey(this.getUserAcceptancesCacheKey(options.userId || site.getUserId()));
+ }
+
/**
* Check whether a site allows getting and setting acceptances.
*
@@ -221,6 +234,13 @@ export class CorePolicyService {
export const CorePolicy = makeSingleton(CorePolicyService);
+/**
+ * Options for get policy acceptances.
+ */
+type CorePolicyGetAcceptancesOptions = CoreSitesCommonWSOptions & {
+ userId?: number; // User ID. If not defined, current user.
+};
+
/**
* Result of WS core_user_agree_site_policy.
*/
@@ -260,6 +280,9 @@ export type CorePolicySitePolicy = {
content?: string; // The policy content.
contentformat: number; // Content format (1 = HTML, 0 = MOODLE, 2 = PLAIN, or 4 = MARKDOWN).
acceptance?: CorePolicySitePolicyAcceptance; // Acceptance status for the given user.
+ canaccept: boolean; // Whether the policy can be accepted.
+ candecline: boolean; // Whether the policy can be declined.
+ canrevoke: boolean; // Whether the policy can be revoked.
};
/**
@@ -300,3 +323,12 @@ 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.
}
+
+/**
+ * Status of a policy.
+ */
+export enum CorePolicyStatus {
+ Draft = 0,
+ Active = 1,
+ Archived = 2,
+}
diff --git a/src/core/features/policy/tests/behat/consent.feature b/src/core/features/policy/tests/behat/consent.feature
new file mode 100755
index 000000000..b95dc091a
--- /dev/null
+++ b/src/core/features/policy/tests/behat/consent.feature
@@ -0,0 +1,179 @@
+@core_policy @app @javascript @lms_from4.4
+Feature: Test accepting pending policies on signup
+
+ Background:
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student | User | One | one@example.com |
+
+ Scenario: Accept policy using default handler
+ Given the following config values are set as admin:
+ | sitepolicyhandler | |
+ | sitepolicy | https://moodle.org/invalidfile.pdf |
+ When I launch the app
+ And I set the field "Your site" to "$WWWROOT" in the app
+ And I press "Connect to your site" in the app
+ And I set the following fields to these values in the app:
+ | Username | student |
+ | Password | student |
+ And I press "Log in" near "Lost password?" in the app
+ Then I should find "You must agree to this policy to continue using this site" in the app
+ But I should not be able to press "Continue" in the app
+ And I should not be able to press "User account" in the app
+
+ When I press "Link to site policy agreement" in the app
+ And I press "OK" in the app
+ Then the app should have opened a browser tab with url "moodle.org"
+
+ When I close the browser tab opened by the app
+ And I press "I have read and agree to the Site policy agreement" in the app
+ And I press "Continue" in the app
+ Then I should be able to press "User account" in the app
+
+ When I press "User account" in the app
+ Then I should not find "Policies and agreements" in the app
+
+ Scenario: Accept policy using tool_policy
+ Given the following config values are set as admin:
+ | sitepolicyhandler | tool_policy |
+ And the following policies exist:
+ | name | agreementstyle | optional | revision | content | summary |
+ | Mandatory policy own page | 1 | 0 | mo v1 | Content mand own page | Summ mand own page |
+ | Optional policy own page | 1 | 1 | oo v1 | Content opt own page | Summ opt own page |
+ | Mandatory policy consent page | 0 | 0 | mc v1 | Content mand consent page | Summ mand consent page |
+ | Optional policy consent page | 0 | 1 | oc v1 | Content opt consent page | Summ opt consent page |
+ When I launch the app
+ And I set the field "Your site" to "$WWWROOT" in the app
+ And I press "Connect to your site" in the app
+ And I set the following fields to these values in the app:
+ | Username | student |
+ | Password | student |
+ And I press "Log in" near "Lost password?" in the app
+ Then I should find "Mandatory policy own page" in the app
+ And I should find "Summ mand own page" in the app
+ And I should find "Content mand own page" in the app
+ But I should not be able to press "Continue" in the app
+ And I should not be able to press "User account" in the app
+
+ When I press "I have read and agree to the Mandatory policy own page" in the app
+ And I press "I have read and agree to the Mandatory policy own page" in the app
+ Then I should find "Before continuing you need to acknowledge all these policies" in the app
+ But I should not be able to press "Continue" in the app
+
+ When I press "I have read and agree to the Mandatory policy own page" in the app
+ And I press "Continue" in the app
+ Then I should find "Optional policy own page" in the app
+ And I should find "Summ opt own page" in the app
+ And I should find "Content opt own page" in the app
+ But I should not be able to press "Continue" in the app
+
+ When I press "No thanks, I decline Optional policy own page" in the app
+ And I press "Continue" in the app
+ Then I should find "Policy 1 out of 2" in the app
+ And I should find "Mandatory policy consent page" in the app
+ And I should find "Summ mand consent page" in the app
+ And I should find "Content mand consent page" in the app
+ But I should not find "I have read and agree" in the app
+
+ When I press "Next" in the app
+ Then I should find "Policy 2 out of 2" in the app
+ And I should find "Optional policy consent page" in the app
+ And I should find "Summ opt consent page" in the app
+ And I should find "Content opt consent page" in the app
+ But I should not find "No thanks, I decline" in the app
+
+ When I press "Next" in the app
+ Then I should find "Please agree to the following policies" in the app
+ And I should find "Mandatory policy consent page" in the app
+ And I should find "Summ mand consent page" in the app
+ And I should find "Optional policy consent page" in the app
+ And I should find "Summ opt consent page" in the app
+ But I should not find "Content mand consent page" in the app
+ And I should not find "Content opt consent page" in the app
+
+ When I press "Please refer to the full Mandatory policy consent page" in the app
+ Then I should find "Content mand consent page" in the app
+ But I should not find "Content opt consent page" in the app
+
+ When I press "Close" in the app
+ And I press "Please refer to the full Optional policy consent page" in the app
+ Then I should find "Content opt consent page" in the app
+ But I should not find "Content mand consent page" in the app
+
+ When I press "Close" in the app
+ And I press "Continue" in the app
+ Then I should find "Before continuing you need to acknowledge all these policies" in the app
+
+ When I press "I have read and agree to the Mandatory policy consent page" in the app
+ And I press "Continue" in the app
+ Then I should find "Before continuing you need to acknowledge all these policies" in the app
+
+ When I press "I have read and agree to the Optional policy consent page" in the app
+ And I press "Continue" in the app
+ Then I should be able to press "User account" in the app
+
+ # TODO: Add a new version for a policy and check that the user is prompted to accept it.
+ # This is currently not possible with the current step to create policies.
+
+ # View policies and agreements. Do it in this Scenario because there is no generator to set acceptances.
+ When I press "User account" in the app
+ And I press "Policies and agreements" in the app
+ Then I should find "Mandatory policy own page" in the app
+ And I should find "Optional policy own page" in the app
+ And I should find "Mandatory policy consent page" in the app
+ And I should find "Optional policy consent page" in the app
+ But I should not find "Revision" in the app
+ And I should not find "Summ mand own page" in the app
+ And I should not find "Content mand own page" in the app
+
+ When I press "View policy Mandatory policy own page" in the app
+ Then I should find "Summ mand own page" in the app
+ And I should find "Content mand own page" in the app
+ But I should not find "Summ opt own page" in the app
+
+ When I press "Close" in the app
+ And I press "Expand" within "Mandatory policy own page" "ion-item" in the app
+ Then I should find "mo v1" in the app
+ And I should find "Active" in the app
+ And I should find "Accepted" in the app
+ But I should not be able to press "Withdraw" in the app
+
+ When I press "Collapse" within "Mandatory policy own page" "ion-item" in the app
+ And I press "Expand" within "Optional policy own page" "ion-item" in the app
+ Then I should find "oo v1" in the app
+ And I should find "Active" in the app
+ And I should find "Declined" in the app
+
+ When I press "Accept" in the app
+ Then I should find "Accepted" in the app
+ But I should not find "Declined" in the app
+
+ When I press "Withdraw" in the app
+ Then I should find "Declined" in the app
+ But I should not find "Accepted" in the app
+
+ # Test tablet view now.
+ When I press the back button in the app
+ And I change viewport size to "1200x640" in the app
+ And I press "User account" in the app
+ And I press "Policies and agreements" in the app
+ Then I should find "Mandatory policy own page" in the app
+ And I should find "Optional policy own page" in the app
+ And I should find "Mandatory policy consent page" in the app
+ And I should find "Optional policy consent page" in the app
+ And I should find "mo v1" within "Mandatory policy own page" "tr" in the app
+ And I should find "Active" within "Mandatory policy own page" "tr" in the app
+ And I should find "Accepted" within "Mandatory policy own page" "tr" in the app
+ And I should find "Declined" within "Optional policy own page" "tr" in the app
+ But I should not be able to press "Withdraw" within "Mandatory policy own page" "tr" in the app
+ And I should not find "Summ mand own page" in the app
+ And I should not find "Content mand own page" in the app
+
+ When I press "Mandatory policy own page" in the app
+ Then I should find "Summ mand own page" in the app
+ And I should find "Content mand own page" in the app
+ But I should not find "Summ opt own page" in the app
+
+ When I press "Close" in the app
+ And I press "Accept" within "Optional policy own page" "tr" in the app
+ Then I should find "Accepted" within "Optional policy own page" "tr" in the app
diff --git a/src/core/features/policy/tests/behat/contactdpo.feature b/src/core/features/policy/tests/behat/contactdpo.feature
new file mode 100755
index 000000000..54e426413
--- /dev/null
+++ b/src/core/features/policy/tests/behat/contactdpo.feature
@@ -0,0 +1,28 @@
+@core_policy @app @javascript @lms_from4.4
+Feature: Test contact DPO from acceptances page
+
+ Background:
+ Given the following config values are set as admin:
+ | sitepolicyhandler | tool_policy |
+ And the following "users" exist:
+ | username | firstname | lastname | email |
+ | student | User | One | one@example.com |
+
+ Scenario: Cannot contact DPO if not enabled
+ When I entered the app as "student"
+ And I press the user menu button in the app
+ And I press "Policies and agreements" in the app
+ Then I should find "For any questions about the policies please contact the privacy officer" in the app
+ But I should not be able to press "Contact" in the app
+
+ Scenario: Can contact DPO if enabled
+ Given the following config values are set as admin:
+ | contactdataprotectionofficer | 1 | tool_dataprivacy |
+ When I entered the app as "student"
+ And I press the user menu button in the app
+ And I press "Policies and agreements" in the app
+ Then I should find "For any questions about the policies please contact the privacy officer" in the app
+
+ When I press "Contact" in the app
+ Then I should find "Data privacy" in the app
+ And I should be able to press "Contact the privacy officer" in the app
diff --git a/src/core/lang.json b/src/core/lang.json
index 5138ebfac..7180aa284 100644
--- a/src/core/lang.json
+++ b/src/core/lang.json
@@ -55,6 +55,7 @@
"connectionlost": "Connection to site lost",
"considereddigitalminor": "You are too young to create an account on this site.",
"contactsupport": "Contact support",
+ "contactverb": "Contact",
"content": "Content",
"contenteditingsynced": "The content you are editing has been synced.",
"continue": "Continue",
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 4ac4722f3..f23baa8c4 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -942,7 +942,7 @@ ion-card {
display: flex;
flex-direction: row;
justify-content: flex-end;
- margin: 0 8px 8px 8px;
+ margin: 0px 8px 8px 8px;
ion-button {
&[fill="outline"] {
@@ -1633,9 +1633,10 @@ ion-item.item.divider {
}
ion-item-divider.item,
-ion-item.item {
+ion-item.item,
+td {
.expandable-status-icon {
- font-size: 18px;
+ font-size: 1.125rem;
@include core-transition(transform, 200ms);
@include margin-horizontal(null, 16px);
From 62a04239cb9eb0dead1844d9c038f12ce778436b Mon Sep 17 00:00:00 2001
From: Dani Palou
Date: Mon, 4 Mar 2024 10:24:52 +0100
Subject: [PATCH 8/8] MOBILE-2768 policy: Decouple policy service from initial
bundle
---
src/core/features/login/services/login-helper.ts | 9 +++++++--
.../policy/services/handlers/acceptances-link.ts | 3 ++-
src/core/features/policy/services/handlers/user.ts | 3 ++-
3 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts
index a2aded067..98fb1c72c 100644
--- a/src/core/features/login/services/login-helper.ts
+++ b/src/core/features/login/services/login-helper.ts
@@ -57,7 +57,6 @@ import {
IDENTITY_PROVIDER_FEATURE_NAME_PREFIX,
} from '../constants';
import { LazyRoutesModule } from '@/app/app-routing.module';
-import { CorePolicy } from '@features/policy/services/policy';
/**
* Helper provider that provides some common features regarding authentication.
@@ -91,6 +90,8 @@ export class CoreLoginHelperProvider {
* @deprecated since 4.4. Use CorePolicy.acceptMandatoryPolicies instead.
*/
async acceptSitePolicy(siteId?: string): Promise {
+ const { CorePolicy } = await import('@features/policy/services/policy');
+
return CorePolicy.acceptMandatorySitePolicies(siteId);
}
@@ -285,6 +286,8 @@ export class CoreLoginHelperProvider {
* @deprecated since 4.4. Use CorePolicy.getSitePoliciesURL instead.
*/
async getSitePolicy(siteId?: string): Promise {
+ const { CorePolicy } = await import('@features/policy/services/policy');
+
return CorePolicy.getSitePoliciesURL(siteId);
}
@@ -1040,7 +1043,9 @@ export class CoreLoginHelperProvider {
* @returns void
* @deprecated since 4.4. Use CorePolicy.goToAcceptSitePolicies instead.
*/
- sitePolicyNotAgreed(siteId?: string): void {
+ async sitePolicyNotAgreed(siteId?: string): Promise {
+ const { CorePolicy } = await import('@features/policy/services/policy');
+
return CorePolicy.goToAcceptSitePolicies(siteId);
}
diff --git a/src/core/features/policy/services/handlers/acceptances-link.ts b/src/core/features/policy/services/handlers/acceptances-link.ts
index 047de6610..69485e126 100644
--- a/src/core/features/policy/services/handlers/acceptances-link.ts
+++ b/src/core/features/policy/services/handlers/acceptances-link.ts
@@ -18,7 +18,6 @@ import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
-import { CorePolicy } from '../policy';
import { ACCEPTANCES_PAGE_NAME, POLICY_PAGE_NAME } from '@features/policy/constants';
import { CoreSites } from '@services/sites';
@@ -55,6 +54,8 @@ export class CorePolicyAcceptancesLinkHandlerService extends CoreContentLinksHan
return false;
}
+ const { CorePolicy } = await import('@features/policy/services/policy');
+
return CorePolicy.isManageAcceptancesAvailable(siteId);
}
diff --git a/src/core/features/policy/services/handlers/user.ts b/src/core/features/policy/services/handlers/user.ts
index 730f8401a..89159a9f4 100644
--- a/src/core/features/policy/services/handlers/user.ts
+++ b/src/core/features/policy/services/handlers/user.ts
@@ -19,7 +19,6 @@ import {
CoreUserProfileHandler,
CoreUserProfileHandlerData,
} from '@features/user/services/user-delegate';
-import { CorePolicy } from '../policy';
import { CoreSites } from '@services/sites';
import { makeSingleton } from '@singletons';
import { CoreNavigator } from '@services/navigator';
@@ -40,6 +39,8 @@ export class CorePolicyUserHandlerService implements CoreUserProfileHandler {
* @inheritdoc
*/
async isEnabled(): Promise {
+ const { CorePolicy } = await import('@features/policy/services/policy');
+
const wsAvailable = await CorePolicy.isManageAcceptancesAvailable();
if (!wsAvailable) {
return false;