Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile 4680 #4274

Merged
merged 2 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/langindex.json
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,7 @@
"core.login.missingfirstname": "moodle",
"core.login.missinglastname": "moodle",
"core.login.mobileservicesnotenabled": "local_moodlemobileapp",
"core.login.morewaystologin": "local_moodlemobileapp",
"core.login.mustconfirm": "moodle",
"core.login.newaccount": "moodle",
"core.login.notloggedin": "local_moodlemobileapp",
Expand Down
27 changes: 10 additions & 17 deletions src/core/classes/sites/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export class CoreSite extends CoreAuthenticatedSite {
* Check if the user authenticated in the site using an OAuth method.
*
* @returns Whether the user authenticated in the site using an OAuth method.
* @deprecated since 5.0. Use getOAuthId instead.
*/
isOAuth(): boolean {
return this.oauthId != null && this.oauthId !== undefined;
Expand Down Expand Up @@ -268,7 +269,6 @@ export class CoreSite extends CoreAuthenticatedSite {
*
* @param component Component name.
* @param componentId Component id.
* @returns Promise resolved when the entries are deleted.
*/
async deleteComponentFromCache(component: string, componentId?: number): Promise<void> {
if (!component) {
Expand All @@ -284,7 +284,7 @@ export class CoreSite extends CoreAuthenticatedSite {
await this.cacheTable.delete(params);
}

/*
/**
* Uploads a file using Cordova File API.
*
* @param filePath File path.
Expand Down Expand Up @@ -366,13 +366,17 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param url The url to be fixed.
* @returns Promise resolved with the fixed URL.
*/
checkAndFixPluginfileURL(url: string): Promise<string> {
return this.checkTokenPluginFile(url).then(() => this.fixPluginfileURL(url));
async checkAndFixPluginfileURL(url: string): Promise<string> {
// Resolve the checking promise to make sure it's finished.
await this.checkTokenPluginFile(url);

// The previous promise (tokenPluginFileWorks) result will be used here.
return this.fixPluginfileURL(url);
}

/**
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
* Uses CoreUtilsProvider.fixPluginfileURL, passing site's token.
* Uses CoreUrl.fixPluginfileURL, passing site's token.
*
* @param url The url to be fixed.
* @returns Fixed URL.
Expand All @@ -386,17 +390,13 @@ export class CoreSite extends CoreAuthenticatedSite {

/**
* Deletes site's DB.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteDB(): Promise<void> {
await CoreDB.deleteDB('Site-' + this.id);
}

/**
* Deletes site's folder.
*
* @returns Promise to be resolved when the DB is deleted.
*/
async deleteFolder(): Promise<void> {
if (!CoreFile.isAvailable() || !this.id) {
Expand Down Expand Up @@ -466,7 +466,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param url The URL to open.
* @param alertMessage If defined, an alert will be shown before opening the browser.
* @param options Other options.
* @returns Promise resolved when done, rejected otherwise.
*/
async openInBrowserWithAutoLogin(
url: string,
Expand Down Expand Up @@ -598,8 +597,6 @@ export class CoreSite extends CoreAuthenticatedSite {

/**
* Invalidates config WS call.
*
* @returns Promise resolved when the data is invalidated.
*/
async invalidateConfig(): Promise<void> {
await this.invalidateWsCacheForKey(this.getConfigCacheKey());
Expand Down Expand Up @@ -728,7 +725,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* Deletes a site setting.
*
* @param name The config name.
* @returns Promise resolved when done.
*/
async deleteSiteConfig(name: string): Promise<void> {
await this.configTable.deleteByPrimaryKey({ name });
Expand Down Expand Up @@ -760,13 +756,12 @@ export class CoreSite extends CoreAuthenticatedSite {
*
* @param name The config name.
* @param value The config value. Can only store number or strings.
* @returns Promise resolved when done.
*/
async setLocalSiteConfig(name: string, value: number | string): Promise<void> {
await this.configTable.insert({ name, value });
}

/*
/**
* Check if tokenpluginfile script works in the site.
*
* @param url URL to check.
Expand Down Expand Up @@ -802,7 +797,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* Deletes last viewed records based on some conditions.
*
* @param conditions Conditions.
* @returns Promise resolved when done.
*/
async deleteLastViewed(conditions?: Partial<CoreSiteLastViewedDBRecord>): Promise<void> {
await this.lastViewedTable.delete(conditions);
Expand Down Expand Up @@ -853,7 +847,6 @@ export class CoreSite extends CoreAuthenticatedSite {
* @param id ID.
* @param value Last viewed item value.
* @param options Options.
* @returns Promise resolved when done.
*/
async storeLastViewed(
component: string,
Expand Down
2 changes: 2 additions & 0 deletions src/core/features/login/components/components.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreLoginMethodsComponent } from './login-methods/login-methods';
import { CoreLoginExceededAttemptsComponent } from '@features/login/components/exceeded-attempts/exceeded-attempts';
import { CoreLoginIdentityProviderComponent } from './identity-provider/identity-provider';

@NgModule({
declarations: [
Expand All @@ -24,6 +25,7 @@ import { CoreLoginExceededAttemptsComponent } from '@features/login/components/e
],
imports: [
CoreSharedModule,
CoreLoginIdentityProviderComponent,
],
exports: [
CoreLoginExceededAttemptsComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ion-button class="ion-text-wrap ion-margin core-oauth-provider" (click)="openOAuth()" [ariaLabel]="provider.name" expand="block"
fill="outline">
@if (provider.iconurl) {
<img [src]="provider.iconurl" alt="" width="32" height="32" slot="start" aria-hidden="true" (error)="provider.iconurl = ''" />
}
<ion-label>{{ provider.name }}</ion-label>
</ion-button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// (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';
crazyserver marked this conversation as resolved.
Show resolved Hide resolved
import { CoreSiteIdentityProvider } from '@classes/sites/unauthenticated-site';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSharedModule } from '@/core/shared.module';
import { CoreRedirectPayload } from '@services/navigator';

@Component({
selector: 'core-identity-provider',
templateUrl: 'identity-provider.html',
standalone: true,
imports: [
CoreSharedModule,
],
})
export class CoreLoginIdentityProviderComponent {

@Input({ required: true }) provider!: CoreSiteIdentityProvider;
@Input() launchurl = '';
@Input() siteUrl = '';
@Input() redirectData?: CoreRedirectPayload;

/**
* The button has been clicked.
*/
async openOAuth(): Promise<void> {
const result = await CoreLoginHelper.openBrowserForOAuthLogin(
this.siteUrl,
this.provider,
this.launchurl,
this.redirectData,
);

if (!result) {
CoreDomUtils.showErrorModal('Invalid data.');
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
<!-- Identity providers. -->
<ion-list *ngIf="identityProviders.length" class="core-login-identity-providers">
<h2 class="item-heading">{{ 'core.login.potentialidps' | translate }}</h2>
<ion-button [fill]="'outline'" *ngFor="let provider of identityProviders" class="ion-text-wrap ion-margin core-oauth-provider"
(click)="oauthClicked(provider)" [ariaLabel]="provider.name" expand="block">
<img *ngIf="provider.iconurl" [src]="provider.iconurl" alt="" width="32" height="32" slot="start" aria-hidden="true">
<ion-label>{{ provider.name }}</ion-label>
</ion-button>
<core-identity-provider *ngFor="let provider of identityProviders" [provider]="provider" [launchurl]="siteConfig?.launchurl"
[redirectData]="redirectData" [siteUrl]="siteUrl" />
</ion-list>
67 changes: 43 additions & 24 deletions src/core/features/login/components/login-methods/login-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { toBoolean } from '@/core/transforms/boolean';
import { Component, Input, OnInit } from '@angular/core';
import { CorePromisedValue } from '@classes/promised-value';
import { CoreSite } from '@classes/sites/site';
import { CoreSiteIdentityProvider, CoreSitePublicConfigResponse } from '@classes/sites/unauthenticated-site';
import { CoreLoginHelper, CoreLoginMethod } from '@features/login/services/login-helper';
import { CoreRedirectPayload } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreSitesFactory } from '@services/sites-factory';
import { CoreDomUtils } from '@services/utils/dom';

@Component({
selector: 'core-login-methods',
Expand All @@ -28,27 +27,31 @@ import { CoreDomUtils } from '@services/utils/dom';
})
export class CoreLoginMethodsComponent implements OnInit {

@Input({ transform: toBoolean }) reconnect = false;
@Input() siteUrl = '';
@Input() siteConfig?: CoreSitePublicConfigResponse;
@Input() redirectData?: CoreRedirectPayload;
@Input() site?: CoreSite; // Defined when the user is reconnecting.
@Input() showLoginForm = true;

isBrowserSSO = false;
showScanQR = false;
loginMethods: CoreLoginMethod[] = [];
identityProviders: CoreSiteIdentityProvider[] = [];

protected currentLoginProvider?: CoreSiteIdentityProvider;
protected isReady = new CorePromisedValue<void>();

/**
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
if (this.reconnect) {
if (this.site) {
this.siteUrl = this.site.getURL();

this.loginMethods = await CoreLoginHelper.getLoginMethods();

const currentSite = CoreSites.getCurrentSite();
const defaultMethod = await CoreLoginHelper.getDefaultLoginMethod();
if (currentSite?.isLoggedOut() && defaultMethod) {
if (this.site.isLoggedOut() && defaultMethod) {
await defaultMethod.action();
}
}
Expand All @@ -59,25 +62,29 @@ export class CoreLoginMethodsComponent implements OnInit {
// Identity providers won't be shown if login on browser.
if (!this.isBrowserSSO) {
this.identityProviders = await CoreLoginHelper.getValidIdentityProvidersForSite(
CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig),
this.site ?? CoreSitesFactory.makeUnauthenticatedSite(this.siteUrl, this.siteConfig),
);
}

if (this.reconnect) {
if (this.site) {
this.showScanQR = CoreLoginHelper.displayQRInSiteScreen();

// The identity provider set in the site will be shown at the top.
const oAuthId = this.site.getOAuthId();
this.currentLoginProvider = CoreLoginHelper.findIdentityProvider(this.identityProviders, oAuthId);
}

// If still false or credentials screen.
if (!this.reconnect || !this.showScanQR) {
if (!this.site || !this.showScanQR) {
this.showScanQR = await CoreLoginHelper.displayQRInCredentialsScreen(this.siteConfig.tool_mobile_qrcodetype);
}
}

this.isReady.resolve();
}

/**
* Show instructions and scan QR code.
*
* @returns Promise resolved when done.
*/
async showInstructionsAndScanQR(): Promise<void> {
try {
Expand All @@ -90,21 +97,33 @@ export class CoreLoginMethodsComponent implements OnInit {
}

/**
* An OAuth button was clicked.
* Get the current login, removing the identity provider from the list.
*
* @param provider The provider that was clicked.
* @returns Current login.
*/
async oauthClicked(provider: CoreSiteIdentityProvider): Promise<void> {
const result = await CoreLoginHelper.openBrowserForOAuthLogin(
this.siteUrl,
provider,
this.siteConfig?.launchurl,
this.redirectData,
);

if (!result) {
CoreDomUtils.showErrorModal('Invalid data.');
async extractCurrentLogin(): Promise<CoreLoginMethodsCurrentLogin | undefined> {
await this.isReady;

if (!this.currentLoginProvider) {
return;
}

// Remove the identity provider from the array.
this.identityProviders = this.identityProviders.filter((provider) =>
provider.url !== this.currentLoginProvider?.url);

const showOther = !!(this.showLoginForm || this.isBrowserSSO) &&
!!(this.loginMethods.length || this.identityProviders.length || this.showScanQR);

return {
provider: this.currentLoginProvider,
showOther,
};
}

}

export type CoreLoginMethodsCurrentLogin = {
provider: CoreSiteIdentityProvider;
showOther: boolean;
};
1 change: 1 addition & 0 deletions src/core/features/login/lang.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"missingfirstname": "Missing given name",
"missinglastname": "Missing last name",
"mobileservicesnotenabled": "Mobile services are not enabled on the site.",
"morewaystologin": "More ways to log in",
"mustconfirm": "You need to confirm your account",
"newaccount": "New account",
"notloggedin": "You need to be logged in.",
Expand Down
2 changes: 2 additions & 0 deletions src/core/features/login/login-reconnect-lazy.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CoreSharedModule } from '@/core/shared.module';
import { CoreLoginComponentsModule } from '@features/login/components/components.module';
import { CoreLoginReconnectPage } from '@features/login/pages/reconnect/reconnect';
import { CoreSiteLogoComponent } from '@/core/components/site-logo/site-logo';
import { CoreLoginIdentityProviderComponent } from './components/identity-provider/identity-provider';

const routes: Routes = [
{
Expand All @@ -33,6 +34,7 @@ const routes: Routes = [
CoreSharedModule,
CoreLoginComponentsModule,
CoreSiteLogoComponent,
CoreLoginIdentityProviderComponent,
],
declarations: [
CoreLoginReconnectPage,
Expand Down
Loading
Loading