diff --git a/src/app/core/login/login-routing.module.ts b/src/app/core/login/login-routing.module.ts
index 775de336d..8b1ae6f9a 100644
--- a/src/app/core/login/login-routing.module.ts
+++ b/src/app/core/login/login-routing.module.ts
@@ -18,6 +18,7 @@ import { RouterModule, Routes } from '@angular/router';
import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page';
import { CoreLoginInitPage } from './pages/init/init.page';
import { CoreLoginSitePage } from './pages/site/site.page';
+import { CoreLoginSitesPage } from './pages/sites/sites.page';
const routes: Routes = [
{
@@ -32,6 +33,10 @@ const routes: Routes = [
path: 'credentials',
component: CoreLoginCredentialsPage,
},
+ {
+ path: 'sites',
+ component: CoreLoginSitesPage,
+ },
];
@NgModule({
diff --git a/src/app/core/login/login.module.ts b/src/app/core/login/login.module.ts
index 0c18f5430..89a65c544 100644
--- a/src/app/core/login/login.module.ts
+++ b/src/app/core/login/login.module.ts
@@ -26,6 +26,7 @@ import { CoreLoginRoutingModule } from './login-routing.module';
import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page';
import { CoreLoginInitPage } from './pages/init/init.page';
import { CoreLoginSitePage } from './pages/site/site.page';
+import { CoreLoginSitesPage } from './pages/sites/sites.page';
import { CoreLoginHelperProvider } from './services/helper';
@NgModule({
@@ -43,6 +44,7 @@ import { CoreLoginHelperProvider } from './services/helper';
CoreLoginCredentialsPage,
CoreLoginInitPage,
CoreLoginSitePage,
+ CoreLoginSitesPage,
],
providers: [
CoreLoginHelperProvider,
diff --git a/src/app/core/login/pages/credentials/credentials.page.ts b/src/app/core/login/pages/credentials/credentials.page.ts
index 3bcc2f00e..6bb45942c 100644
--- a/src/app/core/login/pages/credentials/credentials.page.ts
+++ b/src/app/core/login/pages/credentials/credentials.page.ts
@@ -248,7 +248,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
CoreLoginHelper.instance.treatUserTokenError(siteUrl, error, username, password);
if (error.loggedout) {
- // @todo Go to sites page.
+ this.navCtrl.navigateRoot('/login/sites');
} else if (error.errorcode == 'forcepasswordchangenotice') {
// Reset password field.
this.credForm.controls.password.reset();
diff --git a/src/app/core/login/pages/init/init.page.ts b/src/app/core/login/pages/init/init.page.ts
index 762be4743..2391f6c98 100644
--- a/src/app/core/login/pages/init/init.page.ts
+++ b/src/app/core/login/pages/init/init.page.ts
@@ -90,7 +90,7 @@ export class CoreLoginInitPage implements OnInit {
// return this.loginHelper.goToSiteInitialPage();
// }
- await this.navCtrl.navigateRoot('/login/site');
+ await this.navCtrl.navigateRoot('/login/sites');
}
}
diff --git a/src/app/core/login/pages/site/site.page.ts b/src/app/core/login/pages/site/site.page.ts
index 432c52a12..783f77060 100644
--- a/src/app/core/login/pages/site/site.page.ts
+++ b/src/app/core/login/pages/site/site.page.ts
@@ -321,7 +321,7 @@ export class CoreLoginSitePage implements OnInit {
CoreLoginHelper.instance.treatUserTokenError(siteData.url, error, siteData.username, siteData.password);
if (error.loggedout) {
- // @todo Send the user to sites page.
+ this.navCtrl.navigateRoot('/login/sites');
}
} finally {
modal.dismiss();
diff --git a/src/app/core/login/pages/sites/sites.html b/src/app/core/login/pages/sites/sites.html
new file mode 100644
index 000000000..be0a4b1b5
--- /dev/null
+++ b/src/app/core/login/pages/sites/sites.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ {{ 'core.settings.sites' | translate }}
+
+
+
+ 0" icon-only (click)="toggleDelete()" [attr.aria-label]="'core.delete' | translate">
+
+
+
+
+
+
+
+
+
+
+
+ {{site.fullName}}
+
+ {{site.siteUrl}}
+ {{site.badge}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/core/login/pages/sites/sites.page.ts b/src/app/core/login/pages/sites/sites.page.ts
new file mode 100644
index 000000000..8605780a1
--- /dev/null
+++ b/src/app/core/login/pages/sites/sites.page.ts
@@ -0,0 +1,145 @@
+// (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 { CoreDomUtils } from '@/app/services/utils/dom';
+import { CoreUtils } from '@/app/services/utils/utils';
+import { Component, OnInit } from '@angular/core';
+
+import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
+import { CoreLogger } from '@singletons/logger';
+import { CoreLoginHelper } from '../../services/helper';
+
+/**
+ * Page that displays a "splash screen" while the app is being initialized.
+ */
+@Component({
+ selector: 'page-core-login-sites',
+ templateUrl: 'sites.html',
+ styleUrls: ['sites.scss'],
+})
+export class CoreLoginSitesPage implements OnInit {
+
+ sites: CoreSiteBasicInfo[] = [];
+ showDelete = false;
+
+ protected logger: CoreLogger;
+
+ constructor() {
+ this.logger = CoreLogger.getInstance('CoreLoginSitesPage');
+ }
+
+ /**
+ * Component being initialized.
+ *
+ * @return Promise resolved when done.
+ */
+ async ngOnInit(): Promise {
+ const sites = await CoreUtils.instance.ignoreErrors(CoreSites.instance.getSortedSites());
+
+ if (!sites || sites.length == 0) {
+ CoreLoginHelper.instance.goToAddSite(true);
+
+ return;
+ }
+
+ // Remove protocol from the url to show more url text.
+ this.sites = sites.map((site) => {
+ site.siteUrl = site.siteUrl.replace(/^https?:\/\//, '');
+ site.badge = 0;
+ // @todo: getSiteCounter.
+
+ return site;
+ });
+
+ this.showDelete = false;
+ }
+
+ /**
+ * Go to the page to add a site.
+ */
+ add(): void {
+ CoreLoginHelper.instance.goToAddSite(false, true);
+ }
+
+ /**
+ * Delete a site.
+ *
+ * @param e Click event.
+ * @param index Position of the site.
+ * @return Promise resolved when done.
+ */
+ async deleteSite(e: Event, index: number): Promise {
+ e.stopPropagation();
+
+ const site = this.sites[index];
+ const siteName = site.siteName || '';
+
+ // @todo: Format text: siteName.
+
+ try {
+ await CoreDomUtils.instance.showDeleteConfirm('core.login.confirmdeletesite', { sitename: siteName });
+ } catch (error) {
+ // User cancelled, stop.
+ return;
+ }
+
+ try {
+ await CoreSites.instance.deleteSite(site.id);
+
+ this.sites.splice(index, 1);
+ this.showDelete = false;
+
+ // If there are no sites left, go to add site.
+ const hasSites = await CoreSites.instance.hasSites();
+
+ if (!hasSites) {
+ CoreLoginHelper.instance.goToAddSite(true, true);
+ }
+ } catch (error) {
+ this.logger.error('Error deleting site ' + site.id, error);
+ CoreDomUtils.instance.showErrorModalDefault(error, 'core.login.errordeletesite', true);
+ }
+ }
+
+ /**
+ * Login in a site.
+ *
+ * @param siteId The site ID.
+ * @return Promise resolved when done.
+ */
+ async login(siteId: string): Promise {
+ const modal = await CoreDomUtils.instance.showModalLoading();
+
+ try {
+ const loggedIn = await CoreSites.instance.loadSite(siteId);
+
+ if (loggedIn) {
+ return CoreLoginHelper.instance.goToSiteInitialPage();
+ }
+ } catch (error) {
+ this.logger.error('Error loading site ' + siteId, error);
+ CoreDomUtils.instance.showErrorModalDefault(error, 'Error loading site.');
+ } finally {
+ modal.dismiss();
+ }
+ }
+
+ /**
+ * Toggle delete.
+ */
+ toggleDelete(): void {
+ this.showDelete = !this.showDelete;
+ }
+
+}
diff --git a/src/app/core/login/pages/sites/sites.scss b/src/app/core/login/pages/sites/sites.scss
new file mode 100644
index 000000000..abe0054de
--- /dev/null
+++ b/src/app/core/login/pages/sites/sites.scss
@@ -0,0 +1,3 @@
+.item-ios .item-button[icon-only] ion-icon {
+ font-size: 2.1em;
+}
diff --git a/src/app/core/login/services/helper.ts b/src/app/core/login/services/helper.ts
index cd3f3dbdf..c01d9caf8 100644
--- a/src/app/core/login/services/helper.ts
+++ b/src/app/core/login/services/helper.ts
@@ -55,7 +55,8 @@ export class CoreLoginHelperProvider {
waitingForBrowser = false;
constructor(
- private location: Location,
+ protected location: Location,
+ protected navCtrl: NavController,
) {
this.logger = CoreLogger.getInstance('CoreLoginHelper');
@@ -314,7 +315,7 @@ export class CoreLoginHelperProvider {
*/
getLogoutLabel(site?: CoreSite): string {
site = site || CoreSites.instance.getCurrentSite();
- const config = site?.getStoredConfig();
+ const config = site?.getStoredConfig();
return 'core.mainmenu.' + (config && config.tool_mobile_forcelogout == '1' ? 'logout' : 'changesite');
}
@@ -342,7 +343,7 @@ export class CoreLoginHelperProvider {
try {
// Try to get the latest config, maybe the site policy was just added or has changed.
- sitePolicy = await site.getConfig('sitepolicy', true);
+ 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.instance.callAjax(
@@ -413,8 +414,30 @@ export class CoreLoginHelperProvider {
* @return Promise resolved when done.
*/
async goToAddSite(setRoot?: boolean, showKeyboard?: boolean): Promise {
- // @todo
- return Promise.resolve();
+ let pageRoute: string;
+ let params: Params;
+
+ if (this.isFixedUrlSet()) {
+ // Fixed URL is set, go to credentials page.
+ const fixedSites = this.getFixedSites();
+ const url = typeof fixedSites == 'string' ? fixedSites : fixedSites[0].url;
+
+ pageRoute = '/login/credentials';
+ params = { siteUrl: url };
+ } else {
+ pageRoute = '/login/site';
+ params = { showKeyboard: showKeyboard };
+ }
+
+ if (setRoot) {
+ await this.navCtrl.navigateRoot(pageRoute, {
+ queryParams: params,
+ });
+ } else {
+ await this.navCtrl.navigateForward(pageRoute, {
+ queryParams: params,
+ });
+ }
}
/**
diff --git a/src/assets/img/user-avatar.png b/src/assets/img/user-avatar.png
new file mode 100644
index 000000000..0345e26c8
Binary files /dev/null and b/src/assets/img/user-avatar.png differ