MOBILE-3325 settings: Move space usage onto site settings

main
Pau Ferrer Ocaña 2020-01-28 15:23:00 +01:00
parent fa72fa4f02
commit d4ee108040
6 changed files with 286 additions and 177 deletions

View File

@ -7,11 +7,29 @@
</ion-header>
<core-split-view>
<ion-content>
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="loaded">
<ion-list>
<a ion-item (click)="openHandler('CoreSettingsSpaceUsagePage')" [title]="'core.settings.spaceusage' | translate" [class.core-split-item-selected]="'CoreSettingsSpaceUsagePage' == selectedPage" detail-push>
<ion-icon name="stats" item-start></ion-icon>
<p>{{ 'core.settings.spaceusage' | translate }}</p>
<a *ngIf="siteInfo" ion-item core-user-link [userId]="siteInfo.userid" text-wrap>
<ion-avatar core-user-avatar [user]="siteInfo" item-start></ion-avatar>
<h2>{{siteInfo.fullname}}</h2>
<ion-note class="core-note-block"><core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true"></core-format-text></ion-note>
<ion-note class="core-note-block">{{ siteUrl }}</ion-note>
</a>
<ion-item text-wrap *ngIf="spaceUsage">
<ion-icon name="stats" item-start></ion-icon>
<h2 text-wrap>{{ 'core.settings.spaceusage' | translate }}</h2>
<div item-end>
<p *ngIf="spaceUsage.spaceUsage != null" text-end>{{ spaceUsage.spaceUsage | coreBytesToSize }}</p>
<p *ngIf="spaceUsage.cacheEntries != null" text-end>{{ 'core.settings.entriesincache' | translate: { $a: spaceUsage.cacheEntries } }}</p>
</div>
<button ion-button icon-only clear color="danger" item-end (click)="deleteSiteStorage()" [hidden]="!spaceUsage.spaceUsage > '0' && !spaceUsage.cacheEntries > '0'" [attr.aria-label]="'core.settings.deletesitefilestitle' | translate">
<ion-icon name="trash"></ion-icon>
</button>
</ion-item>
<a ion-item (click)="openHandler('CoreSettingsSynchronizationPage')" [title]="'core.settings.synchronization' | translate" [class.core-split-item-selected]="'CoreSettingsSynchronizationPage' == selectedPage" detail-push>
<ion-icon name="sync" item-start></ion-icon>
<p>{{ 'core.settings.synchronization' | translate }}</p>
@ -26,5 +44,6 @@
<p>{{ handler.title | translate}}</p>
</a>
</ion-list>
</core-loading>
</ion-content>
</core-split-view>

View File

@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
import { CoreSiteSettingsPage } from './site';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
import { CorePipesModule } from '@pipes/pipes.module';
@NgModule({
declarations: [
@ -26,6 +27,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
imports: [
CoreComponentsModule,
CoreDirectivesModule,
CorePipesModule,
IonicPageModule.forChild(CoreSiteSettingsPage),
TranslateModule.forChild()
],

View File

@ -15,6 +15,9 @@
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavParams, Platform } from 'ionic-angular';
import { CoreSettingsDelegate, CoreSettingsHandlerData } from '../../providers/delegate';
import { CoreSite } from '@classes/site';
import { CoreSitesProvider } from '@providers/sites';
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../providers/helper';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
/**
@ -31,24 +34,78 @@ export class CoreSiteSettingsPage {
handlers: CoreSettingsHandlerData[];
isIOS: boolean;
selectedPage: string;
currentSite: CoreSite;
siteInfo: any;
siteName: string;
siteUrl: string;
spaceUsage: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0
};
loaded = false;
constructor(protected settingsDelegate: CoreSettingsDelegate,
protected settingsHelper: CoreSettingsHelper,
protected sitesProvider: CoreSitesProvider,
platorm: Platform,
navParams: NavParams) {
constructor(private settingsDelegate: CoreSettingsDelegate, platorm: Platform, navParams: NavParams) {
this.isIOS = platorm.is('ios');
this.selectedPage = navParams.get('page') || false;
}
/**
* View loaded.
*/
ionViewDidLoad(): void {
this.handlers = this.settingsDelegate.getHandlers();
this.fetchData().finally(() => {
this.loaded = true;
});
if (this.selectedPage) {
this.openHandler(this.selectedPage);
} else if (this.splitviewCtrl.isOn()) {
this.openHandler('CoreSettingsGeneralPage');
}
}
/**
* View loaded.
*/
protected async fetchData(): Promise<void> {
this.handlers = this.settingsDelegate.getHandlers();
this.currentSite = this.sitesProvider.getCurrentSite();
this.siteInfo = this.currentSite.getInfo();
this.siteName = this.currentSite.getSiteName();
this.siteUrl = this.currentSite.getURL();
return this.settingsHelper.getSiteSpaceUsage(this.sitesProvider.getCurrentSiteId()).then((spaceUsage) => {
this.spaceUsage = spaceUsage;
});
}
/**
* Refresh the data.
*
* @param refresher Refresher.
*/
refreshData(refresher: any): void {
this.fetchData().finally(() => {
refresher.complete();
});
}
/**
* Deletes files of a site and the tables that can be cleared.
*
* @param siteData Site object with space usage.
*/
deleteSiteStorage(): void {
this.settingsHelper.deleteSiteStorage(this.currentSite.getSiteName(), this.currentSite.getId()).then((newInfo) => {
this.spaceUsage = newInfo;
}).catch(() => {
// Ignore cancelled confirmation modal.
});
}
/**

View File

@ -4,10 +4,10 @@
</ion-navbar>
</ion-header>
<ion-content>
<ion-refresher [enabled]="usageLoaded" (ionRefresh)="refreshData($event)">
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<core-loading [hideUntil]="usageLoaded">
<core-loading [hideUntil]="loaded">
<ion-item *ngFor="let site of sites" [class.core-primary-selected-item]="site.id == currentSiteId">
<h2 text-wrap><core-format-text [text]="site.siteName" clean="true" [siteId]="site.id"></core-format-text></h2>
<p text-wrap>{{ site.fullName }}</p>
@ -19,11 +19,11 @@
<ion-icon name="trash"></ion-icon>
</button>
</ion-item>
<ion-item-divider>
<ion-item-divider *ngIf="totals">
<p>{{ 'core.settings.total' | translate }}</p>
<div item-end>
<p>{{ totalUsage | coreBytesToSize }}</p>
<p>{{ 'core.settings.entriesincache' | translate: { $a: totalEntries } }}</p>
<p>{{ totals.spaceUsage | coreBytesToSize }}</p>
<p>{{ 'core.settings.entriesincache' | translate: { $a: totals.cacheEntries } }}</p>
</div>
</ion-item-divider>
</core-loading>

View File

@ -14,15 +14,8 @@
import { Component, } from '@angular/core';
import { IonicPage } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreAppProvider } from '@providers/app';
import { CoreEventsProvider } from '@providers/events';
import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreSitesProvider } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreFilterProvider } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
import { CoreSitesProvider, CoreSiteBasicInfo } from '@providers/sites';
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../providers/helper';
/**
* Page that displays the space usage settings.
@ -34,20 +27,16 @@ import { CoreSite } from '@classes/site';
})
export class CoreSettingsSpaceUsagePage {
usageLoaded = false;
loaded = false;
sites = [];
currentSiteId = '';
totalUsage = 0;
totalEntries = 0;
totals: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0
};
constructor(private filePoolProvider: CoreFilepoolProvider,
private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider,
private filterProvider: CoreFilterProvider,
private translate: TranslateService,
private domUtils: CoreDomUtilsProvider,
appProvider: CoreAppProvider,
private courseProvider: CoreCourseProvider) {
constructor(protected sitesProvider: CoreSitesProvider,
protected settingsHelper: CoreSettingsHelper) {
this.currentSiteId = this.sitesProvider.getCurrentSiteId();
}
@ -55,8 +44,8 @@ export class CoreSettingsSpaceUsagePage {
* View loaded.
*/
ionViewDidLoad(): void {
this.fetchData().finally(() => {
this.usageLoaded = true;
this.calculateSizeUsage().finally(() => {
this.loaded = true;
});
}
@ -65,56 +54,28 @@ export class CoreSettingsSpaceUsagePage {
*
* @return Resolved when done.
*/
protected calculateSizeUsage(): Promise<any> {
protected async calculateSizeUsage(): Promise<void> {
// Calculate total usage.
let totalSize = 0,
totalEntries = 0;
return this.sitesProvider.getSortedSites().then((sites) => {
this.sites = sites;
// Get space usage.
const promises = this.sites.map((siteEntry) => {
return this.sitesProvider.getSite(siteEntry.id).then((site) => {
const proms2 = [];
return Promise.all(this.sites.map((site) => {
return this.settingsHelper.getSiteSpaceUsage(site.id).then((siteInfo) => {
site.cacheEntries = siteInfo.cacheEntries;
site.spaceUsage = siteInfo.spaceUsage;
proms2.push(this.calcSiteClearRows(site).then((rows) => {
siteEntry.cacheEntries = rows;
}));
proms2.push(site.getSpaceUsage().then((size) => {
siteEntry.spaceUsage = size;
}));
return Promise.all(proms2);
});
});
return Promise.all(promises);
});
}
/**
* Convenience function to calculate total usage.
*/
protected calculateTotalUsage(): void {
let totalSize = 0,
totalEntries = 0;
this.sites.forEach((site) => {
totalSize += (site.spaceUsage ? parseInt(site.spaceUsage, 10) : 0);
totalEntries += (site.cacheEntries ? parseInt(site.cacheEntries, 10) : 0);
});
this.totalUsage = totalSize;
this.totalEntries = totalEntries;
}
/**
* Convenience function to calculate space usage.
*
* @return Resolved when done.
*/
protected fetchData(): Promise<any> {
const promises = [
this.calculateSizeUsage().then(() => this.calculateTotalUsage()),
];
return Promise.all(promises);
}));
}).then(() => {
this.totals.spaceUsage = totalSize;
this.totals.cacheEntries = totalEntries;
});
}
/**
@ -123,96 +84,32 @@ export class CoreSettingsSpaceUsagePage {
* @param refresher Refresher.
*/
refreshData(refresher: any): void {
this.fetchData().finally(() => {
this.calculateSizeUsage().finally(() => {
refresher.complete();
});
}
/**
* Convenience function to update site size, along with total usage.
*
* @param site Site object with space usage.
* @param newUsage New space usage of the site in bytes.
*/
protected updateSiteUsage(site: any, newUsage: number): void {
const oldUsage = site.spaceUsage;
site.spaceUsage = newUsage;
this.totalUsage -= oldUsage - newUsage;
}
/**
* Calculate the number of rows to be deleted on a site.
*
* @param site Site object.
* @return If there are rows to delete or not.
*/
protected calcSiteClearRows(site: CoreSite): Promise<number> {
const clearTables = this.sitesProvider.getSiteTableSchemasToClear(site);
let totalEntries = 0;
const promises = clearTables.map((name) => {
return site.getDb().countRecords(name).then((rows) => {
totalEntries += rows;
});
});
return Promise.all(promises).then(() => {
return totalEntries;
});
}
/**
* Deletes files of a site and the tables that can be cleared.
*
* @param siteData Site object with space usage.
*/
deleteSiteStorage(siteData: any): void {
this.filterProvider.formatText(siteData.siteName, {clean: true, singleLine: true, filter: false}, [], siteData.id)
.then((siteName) => {
const title = this.translate.instant('core.settings.deletesitefilestitle');
const message = this.translate.instant('core.settings.deletesitefiles', {sitename: siteName});
this.domUtils.showConfirm(message, title).then(() => {
return this.sitesProvider.getSite(siteData.id);
}).then((site) => {
// Clear cache tables.
const cleanSchemas = this.sitesProvider.getSiteTableSchemasToClear(site);
const promises = cleanSchemas.map((name) => {
return site.getDb().deleteRecords(name);
});
promises.push(site.deleteFolder().then(() => {
this.filePoolProvider.clearAllPackagesStatus(site.id);
this.filePoolProvider.clearFilepool(site.id);
this.updateSiteUsage(siteData, 0);
this.courseProvider.clearAllCoursesStatus(site.id);
}).catch((error) => {
if (error && error.code === FileError.NOT_FOUND_ERR) {
// Not found, set size 0.
this.filePoolProvider.clearAllPackagesStatus(site.id);
this.updateSiteUsage(siteData, 0);
} else {
// Error, recalculate the site usage.
this.domUtils.showErrorModal('core.settings.errordeletesitefiles', true);
site.getSpaceUsage().then((size) => {
this.updateSiteUsage(siteData, size);
});
}
}).finally(() => {
this.eventsProvider.trigger(CoreEventsProvider.SITE_STORAGE_DELETED, {}, site.getId());
this.calcSiteClearRows(site).then((rows) => {
siteData.cacheEntries = rows;
});
}));
return Promise.all(promises);
deleteSiteStorage(siteData: CoreSiteBasicInfoWithUsage): void {
this.settingsHelper.deleteSiteStorage(siteData.siteName, siteData.id).then((newInfo) => {
this.totals.spaceUsage -= siteData.spaceUsage - newInfo.spaceUsage;
this.totals.spaceUsage -= siteData.cacheEntries - newInfo.cacheEntries;
siteData.spaceUsage = newInfo.spaceUsage;
siteData.cacheEntries = newInfo.cacheEntries;
}).catch(() => {
// Ignore cancelled confirmation modal.
});
});
}
}
/**
* Basic site info with space usage and cache entries that can be erased.
*/
export interface CoreSiteBasicInfoWithUsage extends CoreSiteBasicInfo {
cacheEntries?: number; // Number of cached entries that can be cleared.
spaceUsage?: number; // Space used in this site.
}

View File

@ -18,14 +18,26 @@ import { CoreCronDelegate } from '@providers/cron';
import { CoreEventsProvider } from '@providers/events';
import { CoreFilepoolProvider } from '@providers/filepool';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSite } from '@classes/site';
import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreConstants } from '@core/constants';
import { CoreConfigProvider } from '@providers/config';
import { CoreFilterProvider } from '@core/filter/providers/filter';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreConfigConstants } from '../../../configconstants';
import { TranslateService } from '@ngx-translate/core';
import { CoreSite } from '@classes/site';
/**
* Object with space usage and cache entries that can be erased.
*/
export interface CoreSiteSpaceUsage {
cacheEntries?: number; // Number of cached entries that can be cleared.
spaceUsage?: number; // Space used in this site.
}
/**
* Settings helper service.
*/
@ -34,10 +46,18 @@ export class CoreSettingsHelper {
protected logger;
protected syncPromises = {};
constructor(loggerProvider: CoreLoggerProvider, private appProvider: CoreAppProvider, private cronDelegate: CoreCronDelegate,
private eventsProvider: CoreEventsProvider, private filePoolProvider: CoreFilepoolProvider,
private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, private translate: TranslateService,
private configProvider: CoreConfigProvider) {
constructor(loggerProvider: CoreLoggerProvider,
protected appProvider: CoreAppProvider,
protected cronDelegate: CoreCronDelegate,
protected domUtils: CoreDomUtilsProvider,
protected eventsProvider: CoreEventsProvider,
protected filePoolProvider: CoreFilepoolProvider,
protected sitesProvider: CoreSitesProvider,
protected utils: CoreUtilsProvider,
protected translate: TranslateService,
protected configProvider: CoreConfigProvider,
protected filterProvider: CoreFilterProvider,
protected courseProvider: CoreCourseProvider) {
this.logger = loggerProvider.getInstance('CoreSettingsHelper');
if (!CoreConfigConstants.forceColorScheme) {
@ -63,6 +83,120 @@ export class CoreSettingsHelper {
}
}
/**
* Deletes files of a site and the tables that can be cleared.
*
* @param siteName Site Name.
* @param siteId: Site ID.
* @return Resolved with detailed new info when done.
*/
async deleteSiteStorage(siteName: string, siteId: string): Promise<CoreSiteSpaceUsage> {
const siteInfo: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0
};
return this.filterProvider.formatText(siteName, {clean: true, singleLine: true, filter: false}, [], siteId)
.then((siteName) => {
const title = this.translate.instant('core.settings.deletesitefilestitle');
const message = this.translate.instant('core.settings.deletesitefiles', {sitename: siteName});
return this.domUtils.showConfirm(message, title).then(() => {
return this.sitesProvider.getSite(siteId);
}).then((site) => {
// Clear cache tables.
const cleanSchemas = this.sitesProvider.getSiteTableSchemasToClear();
const promises = cleanSchemas.map((name) => {
return site.getDb().deleteRecords(name);
});
promises.push(site.deleteFolder().then(() => {
this.filePoolProvider.clearAllPackagesStatus(site.id);
this.filePoolProvider.clearFilepool(site.id);
this.courseProvider.clearAllCoursesStatus(site.id);
siteInfo.spaceUsage = 0;
}).catch((error) => {
if (error && error.code === FileError.NOT_FOUND_ERR) {
// Not found, set size 0.
this.filePoolProvider.clearAllPackagesStatus(site.id);
siteInfo.spaceUsage = 0;
} else {
// Error, recalculate the site usage.
this.domUtils.showErrorModal('core.settings.errordeletesitefiles', true);
return site.getSpaceUsage().then((size) => {
siteInfo.spaceUsage = size;
});
}
}).then(() => {
this.eventsProvider.trigger(CoreEventsProvider.SITE_STORAGE_DELETED, {}, site.getId());
return this.calcSiteClearRows(site).then((rows) => {
siteInfo.cacheEntries = rows;
});
}));
return Promise.all(promises).then(() => {
return siteInfo;
});
});
});
}
/**
* Calculates each site's usage, and the total usage.
*
* @param siteId ID of the site. Current site if undefined.
* @return Resolved with detailed info when done.
*/
async getSiteSpaceUsage(siteId?: string): Promise<CoreSiteSpaceUsage> {
return this.sitesProvider.getSite(siteId).then((site) => {
// Get space usage.
const promises = [];
const siteInfo: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0
};
promises.push(this.calcSiteClearRows(site).then((rows) => {
siteInfo.cacheEntries = rows;
}));
promises.push(site.getSpaceUsage().then((size) => {
siteInfo.spaceUsage = size;
}));
return Promise.all(promises).then(() => {
return siteInfo;
});
});
}
/**
* Calculate the number of rows to be deleted on a site.
*
* @param site Site object.
* @return If there are rows to delete or not.
*/
protected async calcSiteClearRows(site: CoreSite): Promise<number> {
const clearTables = this.sitesProvider.getSiteTableSchemasToClear();
let totalEntries = 0;
const promises = clearTables.map((name) => {
return site.getDb().countRecords(name).then((rows) => {
totalEntries += rows;
});
});
return Promise.all(promises).then(() => {
return totalEntries;
});
}
/**
* Get a certain processor from a list of processors.
*