diff --git a/src/app/app.scss b/src/app/app.scss
index 34fa86dbd..81f002012 100644
--- a/src/app/app.scss
+++ b/src/app/app.scss
@@ -119,3 +119,8 @@ ion-icon.icon-accessory {
padding-bottom: 10px;
font-style: italic;
}
+
+.content {
+ // Set bgcolor in content instead of overriding $background-color because that variable is applied to a lot of places.
+ background-color: $gray-light;
+}
diff --git a/src/core/login/pages/sites/sites.html b/src/core/login/pages/sites/sites.html
new file mode 100644
index 000000000..138e2158b
--- /dev/null
+++ b/src/core/login/pages/sites/sites.html
@@ -0,0 +1,30 @@
+
+
+ {{ 'mm.settings.sites' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{site.fullName}}
+
+ {{site.siteUrl}}
+ {{site.badge}}
+
+
+
+
diff --git a/src/core/login/pages/sites/sites.module.ts b/src/core/login/pages/sites/sites.module.ts
new file mode 100644
index 000000000..52f9e6601
--- /dev/null
+++ b/src/core/login/pages/sites/sites.module.ts
@@ -0,0 +1,33 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// 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 { IonicPageModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreLoginSitesPage } from './sites';
+import { CoreLoginModule } from '../../login.module';
+import { CoreDirectivesModule } from '../../../../directives/directives.module';
+
+@NgModule({
+ declarations: [
+ CoreLoginSitesPage,
+ ],
+ imports: [
+ CoreDirectivesModule,
+ CoreLoginModule,
+ IonicPageModule.forChild(CoreLoginSitesPage),
+ TranslateModule.forChild()
+ ],
+})
+export class CoreLoginSitesPageModule {}
diff --git a/src/core/login/pages/sites/sites.scss b/src/core/login/pages/sites/sites.scss
new file mode 100644
index 000000000..7bb74215b
--- /dev/null
+++ b/src/core/login/pages/sites/sites.scss
@@ -0,0 +1,3 @@
+page-core-login-sites {
+
+}
diff --git a/src/core/login/pages/sites/sites.ts b/src/core/login/pages/sites/sites.ts
new file mode 100644
index 000000000..cbbbc498d
--- /dev/null
+++ b/src/core/login/pages/sites/sites.ts
@@ -0,0 +1,150 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// 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 } from '@angular/core';
+import { IonicPage, NavController } from 'ionic-angular';
+import { TranslateService } from '@ngx-translate/core';
+import { CoreLoggerProvider } from '../../../../providers/logger';
+import { CoreSitesProvider, CoreSiteBasicInfo } from '../../../../providers/sites';
+import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
+import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
+import { CoreLoginHelperProvider } from '../../providers/helper';
+
+/**
+ * Page that displays the list of stored sites.
+ */
+@IonicPage()
+@Component({
+ selector: 'page-core-login-sites',
+ templateUrl: 'sites.html',
+})
+export class CoreLoginSitesPage {
+ sites: CoreSiteBasicInfo[];
+ showDelete: boolean;
+ protected logger;
+
+ constructor(private navCtrl: NavController, private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
+ private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
+ private translate: TranslateService, logger: CoreLoggerProvider) {
+ this.logger = logger.getInstance('CoreLoginSitesPage');
+ }
+
+ /**
+ * View loaded.
+ */
+ ionViewDidLoad() {
+ this.sitesProvider.getSites().then((sites) => {
+ // Remove protocol from the url to show more url text.
+ sites = sites.map((site) => {
+ site.siteUrl = site.siteUrl.replace(/^https?:\/\//, '');
+ site.badge = 10;
+ // @todo: Implement it once push notifications addon is implemented.
+ // if ($mmaPushNotifications) {
+ // $mmaPushNotifications.getSiteCounter(site.id).then(function(number) {
+ // site.badge = number;
+ // });
+ // }
+ return site;
+ });
+
+ // Sort sites by url and fullname.
+ this.sites = sites.sort((a, b) => {
+ // First compare by site url without the protocol.
+ let compareA = a.siteUrl.toLowerCase(),
+ compareB = b.siteUrl.toLowerCase(),
+ compare = compareA.localeCompare(compareB);
+
+ if (compare !== 0) {
+ return compare;
+ }
+
+ // If site url is the same, use fullname instead.
+ compareA = a.fullName.toLowerCase().trim();
+ compareB = b.fullName.toLowerCase().trim();
+ return compareA.localeCompare(compareB);
+ });
+
+ this.showDelete = false;
+ }).catch(() => {
+ // Shouldn't happen.
+ });
+ }
+
+ /**
+ * Go to the page to add a site.
+ */
+ add() : void {
+ this.loginHelper.goToAddSite(this.navCtrl, false);
+ }
+
+ /**
+ * Delete a site.
+ *
+ * @param {Event} e Click event.
+ * @param {number} index Position of the site.
+ */
+ deleteSite(e: Event, index: number) : void {
+ e.stopPropagation();
+
+ let site = this.sites[index],
+ siteName = site.siteName;
+
+ this.textUtils.formatText(siteName).then((siteName) => {
+ this.domUtils.showConfirm(this.translate.instant('mm.login.confirmdeletesite', {sitename: siteName})).then(() => {
+ this.sitesProvider.deleteSite(site.id).then(() => {
+ this.sites.splice(index, 1);
+ this.showDelete = false;
+
+ // If there are no sites left, go to add site.
+ this.sitesProvider.hasNoSites().then(() => {
+ this.loginHelper.goToAddSite(this.navCtrl, true);
+ });
+ }).catch((error) => {
+ this.logger.error('Error deleting site ' + site.id, error);
+ this.domUtils.showErrorModalDefault(error, 'Delete site failed.');
+ this.domUtils.showErrorModal('mm.login.errordeletesite', true);
+ });
+ }).catch(() => {
+ // User cancelled, nothing to do.
+ });
+ });
+ }
+
+ /**
+ * Login in a site.
+ *
+ * @param {string} siteId The site ID.
+ */
+ login(siteId: string) : void {
+ let modal = this.domUtils.showModalLoading();
+
+ this.sitesProvider.loadSite(siteId).then(() => {
+ if (!this.loginHelper.isSiteLoggedOut()) {
+ return this.loginHelper.goToSiteInitialPage(this.navCtrl, true);
+ }
+ }).catch((error) => {
+ this.logger.error('Error loading site ' + siteId, error);
+ this.domUtils.showErrorModalDefault(error, 'Error loading site.');
+ }).finally(() => {
+ modal.dismiss();
+ });
+ }
+
+ /**
+ * Toggle delete.
+ */
+ toggleDelete() : void {
+ this.showDelete = !this.showDelete;
+ }
+}
diff --git a/src/directives/external-content.ts b/src/directives/external-content.ts
index e89d550e5..a91ff93ce 100644
--- a/src/directives/external-content.ts
+++ b/src/directives/external-content.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { Directive, Input, OnInit, ElementRef } from '@angular/core';
+import { Directive, Input, AfterViewInit, ElementRef } from '@angular/core';
import { Platform } from 'ionic-angular';
import { CoreAppProvider } from '../providers/app';
import { CoreLoggerProvider } from '../providers/logger';
@@ -32,7 +32,7 @@ import { CoreUrlUtilsProvider } from '../providers/utils/url';
@Directive({
selector: '[core-external-content]'
})
-export class CoreExternalContentDirective implements OnInit {
+export class CoreExternalContentDirective implements AfterViewInit {
@Input() siteId?: string; // Site ID to use.
@Input() component?: string; // Component to link the file to.
@Input() componentId?: string|number; // Component ID to use in conjunction with the component.
@@ -49,9 +49,9 @@ export class CoreExternalContentDirective implements OnInit {
}
/**
- * Function executed when the component is initialized.
+ * View has been initialized
*/
- ngOnInit() {
+ ngAfterViewInit() {
let currentSite = this.sitesProvider.getCurrentSite(),
siteId = this.siteId || (currentSite && currentSite.getId()),
targetAttr,
diff --git a/src/directives/format-text.ts b/src/directives/format-text.ts
index 5c84bf3b7..bcedffd61 100644
--- a/src/directives/format-text.ts
+++ b/src/directives/format-text.ts
@@ -88,7 +88,7 @@ export class CoreFormatTextDirective implements OnInit {
extContent.componentId = this.componentId;
extContent.siteId = this.siteId;
- extContent.ngOnInit();
+ extContent.ngAfterViewInit();
}
/**
diff --git a/src/lang/en.json b/src/lang/en.json
index 415978003..3b9f8ba52 100644
--- a/src/lang/en.json
+++ b/src/lang/en.json
@@ -1,5 +1,6 @@
{
"accounts": "Accounts",
+ "add": "Add",
"allparticipants": "All participants",
"android": "Android",
"areyousure": "Are you sure?",
diff --git a/src/providers/filepool.ts b/src/providers/filepool.ts
index 1dfb5166b..da1208c79 100644
--- a/src/providers/filepool.ts
+++ b/src/providers/filepool.ts
@@ -1435,7 +1435,14 @@ export class CoreFilepoolProvider {
protected getFileUrlByUrl(siteId: string, fileUrl: string, component: string, componentId?: string|number, mode = 'url',
timemodified = 0, checkSize = true, downloadUnknown?: boolean, options: any = {}) : Promise {
let fileId,
- revision;
+ revision,
+ addToQueue = (fileUrl) => {
+ // Add the file to queue if needed and ignore errors.
+ this.addToQueueIfNeeded(siteId, fileUrl, component, componentId, timemodified, checkSize,
+ downloadUnknown, options).catch(() => {
+ // Ignore errors.
+ });
+ };
return this.fixPluginfileURL(siteId, fileUrl).then((fixedUrl) => {
fileUrl = fixedUrl;
@@ -1447,14 +1454,12 @@ export class CoreFilepoolProvider {
if (typeof entry === 'undefined') {
// We do not have the file, add it to the queue, and return real URL.
- this.addToQueueIfNeeded(
- siteId, fileUrl, component, componentId, timemodified, checkSize, downloadUnknown, options);
+ addToQueue(fileUrl);
response = fileUrl;
} else if (this.isFileOutdated(entry, revision, timemodified) && this.appProvider.isOnline()) {
// The file is outdated, we add to the queue and return real URL.
- this.addToQueueIfNeeded(
- siteId, fileUrl, component, componentId, timemodified, checkSize, downloadUnknown, options);
+ addToQueue(fileUrl);
response = fileUrl;
} else {
// We found the file entry, now look for the file on disk.
@@ -1471,8 +1476,7 @@ export class CoreFilepoolProvider {
// We could not retrieve the file, delete the entries associated with that ID.
this.logger.debug('File ' + fileId + ' not found on disk');
this.removeFileById(siteId, fileId);
- this.addToQueueIfNeeded(
- siteId, fileUrl, component, componentId, timemodified, checkSize, downloadUnknown, options);
+ addToQueue(fileUrl);
if (this.appProvider.isOnline()) {
// We still have a chance to serve the right content.
@@ -1486,8 +1490,7 @@ export class CoreFilepoolProvider {
return response;
}, () => {
// We do not have the file in store yet. Add to queue and return the fixed URL.
- this.addToQueueIfNeeded(
- siteId, fileUrl, component, componentId, timemodified, checkSize, downloadUnknown, options);
+ addToQueue(fileUrl);
return fileUrl;
});
});
diff --git a/src/providers/sites.ts b/src/providers/sites.ts
index bdbb7007b..7ad1545c6 100644
--- a/src/providers/sites.ts
+++ b/src/providers/sites.ts
@@ -47,6 +47,7 @@ export interface CoreSiteBasicInfo {
fullName: string;
siteName: string;
avatar: string;
+ badge?: number;
};
/*
@@ -750,17 +751,23 @@ export class CoreSitesProvider {
* @param {String[]} [ids] IDs of the sites to get. If not defined, return all sites.
* @return {Promise} Promise resolved when the sites are retrieved.
*/
- getSites(ids: string[]) : Promise {
+ getSites(ids?: string[]) : Promise {
return this.appDB.getAllRecords(this.SITES_TABLE).then((sites) => {
let formattedSites = [];
sites.forEach((site) => {
if (!ids || ids.indexOf(site.id) > -1) {
+ // Try to parse info.
+ let siteInfo = site.info;
+ try {
+ siteInfo = siteInfo ? JSON.parse(siteInfo) : siteInfo;
+ } catch(ex) {}
+
const basicInfo: CoreSiteBasicInfo = {
id: site.id,
siteUrl: site.siteUrl,
- fullName: site.info.fullname,
- siteName: site.info.sitename,
- avatar: site.info.userpictureurl
+ fullName: siteInfo && siteInfo.fullname,
+ siteName: siteInfo && siteInfo.sitename,
+ avatar: siteInfo && siteInfo.userpictureurl
};
formattedSites.push(basicInfo);
}