Merge pull request #2272 from crazyserver/MOBILE-3325

Mobile 3325
main
Juan Leyva 2020-02-10 17:27:59 +01:00 committed by GitHub
commit 357c18f3a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 586 additions and 227 deletions

View File

@ -1760,7 +1760,6 @@
"core.login.visitchangepassword": "local_moodlemobileapp",
"core.login.webservicesnotenabled": "local_moodlemobileapp",
"core.lostconnection": "local_moodlemobileapp",
"core.mainmenu.appsettings": "local_moodlemobileapp",
"core.mainmenu.changesite": "local_moodlemobileapp",
"core.mainmenu.help": "moodle",
"core.mainmenu.logout": "moodle",
@ -1891,6 +1890,7 @@
"core.sending": "chat",
"core.serverconnection": "error",
"core.settings.about": "local_moodlemobileapp",
"core.settings.appsettings": "local_moodlemobileapp",
"core.settings.appversion": "local_moodlemobileapp",
"core.settings.cannotsyncoffline": "local_moodlemobileapp",
"core.settings.cannotsyncwithoutwifi": "local_moodlemobileapp",
@ -1941,6 +1941,7 @@
"core.settings.navigatoruseragent": "local_moodlemobileapp",
"core.settings.networkstatus": "local_moodlemobileapp",
"core.settings.opensourcelicenses": "local_moodlemobileapp",
"core.settings.preferences": "moodle",
"core.settings.privacypolicy": "local_moodlemobileapp",
"core.settings.publisher": "local_moodlemobileapp",
"core.settings.pushid": "local_moodlemobileapp",
@ -1951,8 +1952,10 @@
"core.settings.siteinfo": "local_moodlemobileapp",
"core.settings.sites": "moodle",
"core.settings.spaceusage": "local_moodlemobileapp",
"core.settings.spaceusagehelp": "local_moodlemobileapp",
"core.settings.synchronization": "local_moodlemobileapp",
"core.settings.synchronizenow": "local_moodlemobileapp",
"core.settings.synchronizenowhelp": "local_moodlemobileapp",
"core.settings.syncsettings": "local_moodlemobileapp",
"core.settings.total": "moodle",
"core.settings.wificonnection": "local_moodlemobileapp",

View File

@ -2,7 +2,7 @@
<h2>{{ 'addon.block_activitymodules.pluginname' | translate }}</h2>
</ion-item-divider>
<core-loading [hideUntil]="loaded" class="core-loading-center">
<a ion-item text-wrap *ngFor="let entry of entries" class="item-media" detail-none [navPush]="'CoreCourseListModTypePage'" [navParams]="{title: entry.name, courseId: instanceId, modName: entry.modName}">
<a ion-item text-wrap *ngFor="let entry of entries" class="item-media" detail-none navPush="CoreCourseListModTypePage" [navParams]="{title: entry.name, courseId: instanceId, modName: entry.modName}">
<img item-start [src]="entry.icon" alt="" role="presentation" class="core-module-icon">
{{ entry.name }}
</a>

View File

@ -23,7 +23,7 @@
<!-- List of files. -->
<ion-list *ngIf="files && files.length > 0">
<ng-container *ngFor="let file of files">
<a *ngIf="file.isdir" ion-item class="item-media" [navPush]="'AddonFilesListPage'" [navParams]="{path: file.link, title: file.filename}">
<a *ngIf="file.isdir" ion-item class="item-media" navPush="AddonFilesListPage" [navParams]="{path: file.link, title: file.filename}">
<img [src]="file.imgPath" alt="" role="presentation" item-start>
<p>{{file.filename}}</p>
</a>

View File

@ -1,6 +1,6 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'addon.messages.messagepreferences' | translate }}</ion-title>
<ion-title>{{ 'addon.messages.messages' | translate }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content>

View File

@ -44,9 +44,9 @@ export class AddonMessagesSettingsHandler implements CoreSettingsHandler {
getDisplayData(): CoreSettingsHandlerData {
return {
icon: 'chatbubbles',
title: 'addon.messages.messagepreferences',
title: 'addon.messages.messages',
page: 'AddonMessagesSettingsPage',
class: 'addon-messages-settings-handler'
class: 'addon-messages-settings-handler',
};
}

View File

@ -17,7 +17,7 @@
<ion-list *ngIf="contents && contents.length > 0">
<ng-container *ngFor="let file of contents">
<a *ngIf="file.type === 'folder'" ion-item class="item-media" [navPush]="'AddonModFolderIndexPage'" [navParams]="{path: file.filepath, courseId: courseId, module: file}">
<a *ngIf="file.type === 'folder'" ion-item class="item-media" navPush="AddonModFolderIndexPage" [navParams]="{path: file.filepath, courseId: courseId, module: file}">
<ion-icon name="folder" item-start></ion-icon>
<h2>{{file.name}}</h2>
</a>

View File

@ -244,7 +244,7 @@
{{ 'addon.mod_lesson.overview' | translate }}
</ion-card-header>
<a ion-item text-wrap *ngFor="let student of overview.students" [navPush]="'AddonModLessonUserRetakePage'" [navParams]="{courseId: courseId, lessonId: lesson.id, userId: student.id}">
<a ion-item text-wrap *ngFor="let student of overview.students" navPush="AddonModLessonUserRetakePage" [navParams]="{courseId: courseId, lessonId: lesson.id, userId: student.id}">
<ion-avatar core-user-avatar [user]="student" item-start [userId]="student.id" [courseId]="courseId"></ion-avatar>
<h2>{{ student.fullname }}</h2>
<core-progress-bar [progress]="student.bestgrade"></core-progress-bar>

View File

@ -52,7 +52,7 @@
</ion-row>
</ion-item>
<!-- List of attempts. -->
<a ion-item text-wrap *ngFor="let attempt of attempts" [ngClass]='{"addon-mod_quiz-highlighted core-white-push-arrow": attempt.highlightGrade}' [navPush]="'AddonModQuizAttemptPage'" [navParams]="{courseId: courseId, quizId: quiz.id, attemptId: attempt.id}" [attr.aria-label]="'core.seemoredetail' | translate">
<a ion-item text-wrap *ngFor="let attempt of attempts" [ngClass]='{"addon-mod_quiz-highlighted core-white-push-arrow": attempt.highlightGrade}' navPush="AddonModQuizAttemptPage" [navParams]="{courseId: courseId, quizId: quiz.id, attemptId: attempt.id}" [attr.aria-label]="'core.seemoredetail' | translate">
<ion-row align-items-center>
<ion-col text-center *ngIf="quiz.showAttemptColumn && attempt.preview">{{ 'addon.mod_quiz.preview' | translate }}</ion-col>
<ion-col text-center *ngIf="quiz.showAttemptColumn && !attempt.preview">{{ attempt.attempt }}</ion-col>

View File

@ -31,7 +31,7 @@
<p><core-format-text [component]="component" [componentId]="componentId" [text]="attempt.feedback" contextLevel="module" [contextInstanceId]="quiz.coursemodule" [courseId]="courseId"></core-format-text></p>
</ion-item>
<ion-item *ngIf="quiz.showReviewColumn && attempt.finished">
<button ion-button block icon-start [navPush]="'AddonModQuizReviewPage'" [navParams]="{courseId: courseId, quizId: quiz.id, attemptId: attempt.id}">
<button ion-button block icon-start navPush="AddonModQuizReviewPage" [navParams]="{courseId: courseId, quizId: quiz.id, attemptId: attempt.id}">
<ion-icon name="search"></ion-icon>
{{ 'addon.mod_quiz.review' | translate }}
</button>

View File

@ -1,6 +1,6 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'addon.notifications.notificationpreferences' | translate }}</ion-title>
<ion-title>{{ 'addon.notifications.notifications' | translate }}</ion-title>
<ion-buttons end>
</ion-buttons>
</ion-navbar>

View File

@ -49,9 +49,9 @@ export class AddonNotificationsSettingsHandler implements CoreSettingsHandler {
getDisplayData(): CoreSettingsHandlerData {
return {
icon: 'notifications',
title: 'addon.notifications.notificationpreferences',
title: 'addon.notifications.notifications',
page: 'AddonNotificationsSettingsPage',
class: 'addon-notifications-settings-handler'
class: 'addon-notifications-settings-handler',
};
}
}

View File

@ -106,6 +106,9 @@ ion-app.app-root {
.item h2 {
text-overflow: inherit;
overflow: inherit;
ion-icon, core-icon {
vertical-align: bottom;
}
}
.core-nav-item-selected, .item.core-nav-item-selected {

View File

@ -1760,7 +1760,6 @@
"core.login.visitchangepassword": "Do you want to visit the site to change the password?",
"core.login.webservicesnotenabled": "Web services are not enabled in your site. Please contact your site administrator if you think they should be enabled.",
"core.lostconnection": "Your authentication token is invalid or has expired. You will have to reconnect to the site.",
"core.mainmenu.appsettings": "App settings",
"core.mainmenu.changesite": "Change site",
"core.mainmenu.help": "Help",
"core.mainmenu.logout": "Log out",
@ -1891,6 +1890,7 @@
"core.sending": "Sending",
"core.serverconnection": "Error connecting to the server",
"core.settings.about": "About",
"core.settings.appsettings": "App settings",
"core.settings.appversion": "App version",
"core.settings.cannotsyncoffline": "Cannot synchronise offline.",
"core.settings.cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.",
@ -1941,6 +1941,7 @@
"core.settings.navigatoruseragent": "Navigator userAgent",
"core.settings.networkstatus": "Internet connection status",
"core.settings.opensourcelicenses": "Open Source Licences",
"core.settings.preferences": "Preferences",
"core.settings.privacypolicy": "Privacy policy",
"core.settings.publisher": "Publisher",
"core.settings.pushid": "Push notifications ID",
@ -1951,8 +1952,10 @@
"core.settings.siteinfo": "Site info",
"core.settings.sites": "Sites",
"core.settings.spaceusage": "Space usage",
"core.settings.spaceusagehelp": "Deleting the stored information of the site will remove all the site offline data. This information allows you to use the app when offline. ",
"core.settings.synchronization": "Synchronisation",
"core.settings.synchronizenow": "Synchronise now",
"core.settings.synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.",
"core.settings.syncsettings": "Synchronisation settings",
"core.settings.total": "Total",
"core.settings.wificonnection": "Wi-Fi connection",

View File

@ -3,8 +3,8 @@
<ion-title>{{ 'core.login.login' | translate }}</ion-title>
<ion-buttons end>
<button *ngIf="isFixedUrlSet" ion-button icon-only [navPush]="'CoreSettingsListPage'" [attr.aria-label]="'core.mainmenu.appsettings' | translate">
<ion-icon name="cog"></ion-icon>
<button *ngIf="isFixedUrlSet" ion-button icon-only navPush="CoreAppSettingsPage" [attr.aria-label]="'core.settings.appsettings' | translate">
<core-icon name="fa-cogs"></core-icon>
</button>
</ion-buttons>
</ion-navbar>

View File

@ -3,8 +3,8 @@
<ion-title>{{ 'core.login.connecttomoodle' | translate }}</ion-title>
<ion-buttons end>
<button ion-button icon-only [navPush]="'CoreSettingsListPage'" [attr.aria-label]="'core.mainmenu.appsettings' | translate">
<ion-icon name="cog"></ion-icon>
<button ion-button icon-only navPush="CoreAppSettingsPage" [attr.aria-label]="'core.settings.appsettings' | translate">
<core-icon name="fa-cogs"></core-icon>
</button>
<button ion-button icon-only (click)="showHelp()" [attr.aria-label]="'core.help' | translate">
<ion-icon name="help-circle"></ion-icon>

View File

@ -17,6 +17,7 @@ import { IonicPageModule } from 'ionic-angular';
import { CoreLoginSitePage } from './site';
import { TranslateModule } from '@ngx-translate/core';
import { CoreDirectivesModule } from '@directives/directives.module';
import { CoreComponentsModule } from '@components/components.module';
@NgModule({
declarations: [
@ -24,6 +25,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
],
imports: [
CoreDirectivesModule,
CoreComponentsModule,
IonicPageModule.forChild(CoreLoginSitePage),
TranslateModule.forChild()
]

View File

@ -3,8 +3,8 @@
<ion-title>{{ 'core.settings.sites' | translate }}</ion-title>
<ion-buttons end>
<button ion-button icon-only [navPush]="'CoreSettingsListPage'" [attr.aria-label]="'core.mainmenu.appsettings' | translate">
<ion-icon name="cog"></ion-icon>
<button ion-button icon-only navPush="CoreAppSettingsPage" [attr.aria-label]="'core.settings.appsettings' | translate">
<core-icon name="fa-cogs"></core-icon>
</button>
<button *ngIf="sites && sites.length > 0" ion-button icon-only (click)="toggleDelete()" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="create" ios="md-create"></ion-icon>

View File

@ -17,6 +17,7 @@ import { IonicPageModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreLoginSitesPage } from './sites';
import { CoreDirectivesModule } from '@directives/directives.module';
import { CoreComponentsModule } from '@components/components.module';
@NgModule({
declarations: [
@ -24,6 +25,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
],
imports: [
CoreDirectivesModule,
CoreComponentsModule,
IonicPageModule.forChild(CoreLoginSitesPage),
TranslateModule.forChild()
],

View File

@ -1,5 +1,4 @@
{
"appsettings": "App settings",
"changesite": "Change site",
"help": "Help",
"logout": "Log out",

View File

@ -17,35 +17,41 @@
</ion-item>
<a ion-item *ngFor="let handler of handlers" [ngClass]="['core-moremenu-handler', handler.class || '']" (click)="openHandler(handler)" title="{{ handler.title | translate }}" detail-push>
<core-icon [name]="handler.icon" item-start></core-icon>
<p>{{ handler.title | translate}}</p>
<h2>{{ handler.title | translate}}</h2>
<ion-badge item-end *ngIf="handler.showBadge" [hidden]="handler.loading || !handler.badge">{{handler.badge}}</ion-badge>
<ion-spinner item-end *ngIf="handler.showBadge && handler.loading"></ion-spinner>
</a>
<div *ngFor="let item of customItems" class="core-moremenu-customitem">
<a ion-item *ngIf="item.type != 'embedded'" [href]="item.url" core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'" title="{{item.label}}">
<core-icon [name]="item.icon" item-start></core-icon>
<p>{{item.label}}</p>
<h2>{{item.label}}</h2>
</a>
<a ion-item *ngIf="item.type == 'embedded'" (click)="openItem(item)" title="{{item.label}}">
<core-icon [name]="item.icon" item-start></core-icon>
<p>{{item.label}}</p>
<h2>{{item.label}}</h2>
</a>
</div>
<a *ngIf="showWeb" ion-item [href]="siteInfo.siteurl" core-link autoLogin="yes" title="{{ 'core.mainmenu.website' | translate }}">
<ion-icon name="globe" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.website' | translate }}</p>
<h2>{{ 'core.mainmenu.website' | translate }}</h2>
</a>
<a *ngIf="showHelp" ion-item [href]="docsUrl" core-link autoLogin="no" title="{{ 'core.mainmenu.help' | translate }}">
<ion-icon name="help-buoy" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.help' | translate }}</p>
<h2>{{ 'core.mainmenu.help' | translate }}</h2>
</a>
<a ion-item (click)="openSettings()" title="{{ 'core.mainmenu.appsettings' | translate }}">
<ion-icon name="cog" item-start aria-hidden="true"></ion-icon>
<p>{{ 'core.mainmenu.appsettings' | translate }}</p>
<a ion-item (click)="openSitePreferences()" title="{{ 'core.settings.preferences' | translate }}">
<core-icon name="fa-wrench" item-start></core-icon>
<h2>{{ 'core.settings.preferences' | translate }}</h2>
</a>
<a ion-item (click)="logout()" title="{{ logoutLabel | translate }}">
<a ion-item (click)="logout()" title="{{ logoutLabel | translate }}">
<ion-icon name="log-out" item-start aria-hidden="true"></ion-icon>
<p>{{ logoutLabel | translate }}</p>
<h2>{{ logoutLabel | translate }}</h2>
</a>
<ion-item-divider></ion-item-divider>
<a ion-item (click)="openAppSettings()" title="{{ 'core.settings.appsettings' | translate }}">
<core-icon name="fa-cogs" item-start></core-icon>
<h2>{{ 'core.settings.appsettings' | translate }}</h2>
</a>
</ion-list>
</ion-content>

View File

@ -142,10 +142,17 @@ export class CoreMainMenuMorePage implements OnDestroy {
}
/**
* Open settings page.
* Open app settings page.
*/
openSettings(): void {
this.navCtrl.push('CoreSettingsListPage');
openAppSettings(): void {
this.navCtrl.push('CoreAppSettingsPage');
}
/**
* Open site settings page.
*/
openSitePreferences(): void {
this.navCtrl.push('CoreSitePreferencesPage');
}
/**

View File

@ -1,5 +1,6 @@
{
"about": "About",
"appsettings": "App settings",
"appversion": "App version",
"cannotsyncoffline": "Cannot synchronise offline.",
"cannotsyncwithoutwifi": "Cannot synchronise because the current settings only allow to synchronise when connected to Wi-Fi. Please connect to a Wi-Fi network.",
@ -56,12 +57,15 @@
"entriesincache": "{{$a}} entries in cache",
"screen": "Screen information",
"settings": "Settings",
"preferences": "Preferences",
"showdownloadoptions": "Show download options",
"siteinfo": "Site info",
"sites": "Sites",
"spaceusage": "Space usage",
"spaceusagehelp": "Deleting the stored information of the site will remove all the site offline data. This information allows you to use the app when offline. ",
"synchronization": "Synchronisation",
"synchronizenow": "Synchronise now",
"synchronizenowhelp": "Synchronising a site will send pending changes and all offline activity stored in the device and will synchronise some data like messages and notifications.",
"syncsettings": "Synchronisation settings",
"total": "Total",
"wificonnection": "Wi-Fi connection"

View File

@ -1,6 +1,6 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'core.settings.settings' | translate}}</ion-title>
<ion-title>{{ 'core.settings.appsettings' | translate}}</ion-title>
<ion-buttons end>
</ion-buttons>
</ion-navbar>
@ -10,29 +10,23 @@
<ion-list>
<a ion-item (click)="openHandler('CoreSettingsGeneralPage')" [title]="'core.settings.general' | translate" [class.core-split-item-selected]="'CoreSettingsGeneralPage' == selectedPage" detail-push>
<ion-icon name="construct" item-start></ion-icon>
<p>{{ 'core.settings.general' | translate }}</p>
<h2>{{ 'core.settings.general' | translate }}</h2>
</a>
<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>
<h2>{{ 'core.settings.spaceusage' | translate }}</h2>
</a>
<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>
<h2>{{ 'core.settings.synchronization' | translate }}</h2>
</a>
<a ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage" detail-push>
<ion-icon name="folder" item-start></ion-icon>
<p>{{ 'core.sharedfiles.sharedfiles' | translate }}</p>
<h2>{{ 'core.sharedfiles.sharedfiles' | translate }}</h2>
</a>
<a ion-item *ngFor="let handler of handlers" [ngClass]="['core-settings-handler', handler.class]" (click)="openHandler(handler.page, handler.params)" [title]="handler.title | translate" detail-push [class.core-split-item-selected]="handler.page == selectedPage">
<core-icon [name]="handler.icon" item-start *ngIf="handler.icon"></core-icon>
<p>{{ handler.title | translate}}</p>
</a>
<a ion-item (click)="openHandler('CoreSettingsAboutPage')" [title]="'core.settings.about' | translate" [class.core-split-item-selected]="'CoreSettingsAboutPage' == selectedPage" detail-push>
<ion-icon name="contacts" item-start></ion-icon>
<p>{{ 'core.settings.about' | translate }}</p>
<h2>{{ 'core.settings.about' | translate }}</h2>
</a>
</ion-list>
</ion-content>

View File

@ -15,19 +15,19 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreSettingsListPage } from './list';
import { CoreAppSettingsPage } from './app';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
@NgModule({
declarations: [
CoreSettingsListPage
CoreAppSettingsPage
],
imports: [
CoreComponentsModule,
CoreDirectivesModule,
IonicPageModule.forChild(CoreSettingsListPage),
IonicPageModule.forChild(CoreAppSettingsPage),
TranslateModule.forChild()
],
})
export class CoreSettingsListPageModule {}
export class CoreAppSettingsPageModule {}

View File

@ -14,25 +14,24 @@
import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavParams, Platform } from 'ionic-angular';
import { CoreSettingsDelegate, CoreSettingsHandlerData } from '../../providers/delegate';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
/**
* Page that displays the list of settings pages.
* Page that displays the list of app settings pages.
*/
@IonicPage({segment: 'core-settings-list'})
@IonicPage({segment: 'core-settings-app'})
@Component({
selector: 'page-core-settings-list',
templateUrl: 'list.html',
selector: 'page-core-settings-app',
templateUrl: 'app.html',
})
export class CoreSettingsListPage {
export class CoreAppSettingsPage {
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
handlers: CoreSettingsHandlerData[];
isIOS: boolean;
selectedPage: string;
constructor(private settingsDelegate: CoreSettingsDelegate, platorm: Platform, navParams: NavParams) {
constructor(platorm: Platform,
navParams: NavParams) {
this.isIOS = platorm.is('ios');
this.selectedPage = navParams.get('page') || false;
@ -42,7 +41,6 @@ export class CoreSettingsListPage {
* View loaded.
*/
ionViewDidLoad(): void {
this.handlers = this.settingsDelegate.getHandlers();
if (this.selectedPage) {
this.openHandler(this.selectedPage);
} else if (this.splitviewCtrl.isOn()) {

View File

@ -0,0 +1,52 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'core.settings.preferences' | translate}}</ion-title>
<ion-buttons end>
</ion-buttons>
</ion-navbar>
</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>
<ion-item *ngIf="siteInfo" text-wrap>
<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>
</ion-item>
<ion-item-divider></ion-item-divider>
<a ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true, siteId: siteId, hideSitePicker: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage" detail-push>
<ion-icon name="folder" item-start></ion-icon>
<h2>{{ 'core.sharedfiles.sharedfiles' | translate }}</h2>
<ion-badge item-end>{{ iosSharedFiles }}</ion-badge>
</a>
<a ion-item *ngFor="let handler of handlers" [ngClass]="['core-settings-handler', handler.class]" (click)="openHandler(handler.page, handler.params)" [title]="handler.title | translate" detail-push [class.core-split-item-selected]="handler.page == selectedPage">
<core-icon [name]="handler.icon" item-start *ngIf="handler.icon"></core-icon>
<h2>{{ handler.title | translate}}</h2>
</a>
<ion-card class="with-borders">
<ion-item text-wrap *ngIf="spaceUsage">
<h2 text-wrap>{{ 'core.settings.spaceusage' | translate }} <ion-icon name="information-circle" color="info" [attr.aria-label]="'core.info' | translate" (click)="showSpaceInfo()"></ion-icon></h2>
<p *ngIf="spaceUsage.spaceUsage != null">{{ spaceUsage.spaceUsage | coreBytesToSize }}</p>
<p *ngIf="spaceUsage.cacheEntries != null">{{ 'core.settings.entriesincache' | translate: { $a: spaceUsage.cacheEntries } }}</p>
<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>
<ion-item text-wrap>
<h2>{{ 'core.settings.synchronizenow' | translate }} <ion-icon name="information-circle" color="info" [attr.aria-label]="'core.info' | translate" (click)="showSyncInfo()"></ion-icon></h2>
<button ion-button icon-only clear item-end *ngIf="!isSynchronizing()" (click)="synchronize()" [title]="siteName" [attr.aria-label]="'core.settings.synchronizenow' | translate">
<ion-icon name="sync"></ion-icon>
</button>
<ion-spinner item-end *ngIf="isSynchronizing()"></ion-spinner>
</ion-item>
</ion-card>
</ion-list>
</core-loading>
</ion-content>
</core-split-view>

View File

@ -0,0 +1,35 @@
// (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 { IonicPageModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreSitePreferencesPage } from './site';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
import { CorePipesModule } from '@pipes/pipes.module';
@NgModule({
declarations: [
CoreSitePreferencesPage,
],
imports: [
CoreComponentsModule,
CoreDirectivesModule,
CorePipesModule,
IonicPageModule.forChild(CoreSitePreferencesPage),
TranslateModule.forChild(),
],
})
export class CoreSitePreferencesPageModule {}

View File

@ -0,0 +1,198 @@
// (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, ViewChild } from '@angular/core';
import { IonicPage, NavParams, Platform } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreSettingsDelegate, CoreSettingsHandlerData } from '../../providers/delegate';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider, CoreSiteBasicInfo } from '@providers/sites';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreSplitViewComponent } from '@components/split-view/split-view';
import { CoreSharedFilesProvider } from '@core/sharedfiles/providers/sharedfiles';
import { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../providers/helper';
/**
* Page that displays the list of site settings pages.
*/
@IonicPage({segment: 'core-site-preferences'})
@Component({
selector: 'page-core-site-preferences',
templateUrl: 'site.html',
})
export class CoreSitePreferencesPage {
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
handlers: CoreSettingsHandlerData[];
isIOS: boolean;
selectedPage: string;
siteId: string;
siteInfo: CoreSiteBasicInfo[] = [];
siteName: string;
siteUrl: string;
spaceUsage: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0
};
loaded = false;
iosSharedFiles: number;
protected sitesObserver: any;
protected isDestroyed = false;
constructor(protected settingsDelegate: CoreSettingsDelegate,
protected settingsHelper: CoreSettingsHelper,
protected sitesProvider: CoreSitesProvider,
protected domUtils: CoreDomUtilsProvider,
protected eventsProvider: CoreEventsProvider,
protected sharedFilesProvider: CoreSharedFilesProvider,
protected translate: TranslateService,
platorm: Platform,
navParams: NavParams,
) {
this.isIOS = platorm.is('ios');
this.selectedPage = navParams.get('page') || false;
this.sitesObserver = this.eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (data) => {
if (data.siteId == this.siteId) {
this.refreshData();
}
});
}
/**
* View loaded.
*/
ionViewDidLoad(): void {
this.fetchData().finally(() => {
this.loaded = true;
if (this.selectedPage) {
this.openHandler(this.selectedPage);
} else if (this.splitviewCtrl.isOn()) {
if (this.isIOS) {
this.openHandler('CoreSharedFilesListPage', {manage: true, siteId: this.siteId, hideSitePicker: true});
} else if (this.handlers.length > 0) {
this.openHandler(this.handlers[0].page, this.handlers[0].params);
}
}
});
}
/**
* Fetch Data.
*/
protected async fetchData(): Promise<void> {
this.handlers = this.settingsDelegate.getHandlers();
const currentSite = this.sitesProvider.getCurrentSite();
this.siteId = currentSite.id;
this.siteInfo = currentSite.getInfo();
this.siteName = currentSite.getSiteName();
this.siteUrl = currentSite.getURL();
const promises = [];
promises.push(this.settingsHelper.getSiteSpaceUsage(this.siteId).then((spaceUsage) => this.spaceUsage = spaceUsage));
if (this.isIOS) {
promises.push(this.sharedFilesProvider.getSiteSharedFiles(this.siteId).then((files) =>
this.iosSharedFiles = files.length
));
}
await Promise.all(promises);
}
/**
* Syncrhonizes the site.
*/
synchronize(siteId: string): void {
// Using syncOnlyOnWifi false to force manual sync.
this.settingsHelper.synchronizeSite(false, this.siteId).catch((error) => {
if (this.isDestroyed) {
return;
}
this.domUtils.showErrorModalDefault(error, 'core.settings.errorsyncsite', true);
});
}
/**
* Returns true if site is beeing synchronized.
*
* @return True if site is beeing synchronized, false otherwise.
*/
isSynchronizing(): boolean {
return this.siteId && !!this.settingsHelper.getSiteSyncPromise(this.siteId);
}
/**
* Refresh the data.
*
* @param refresher Refresher.
*/
refreshData(refresher?: any): void {
this.fetchData().finally(() => {
refresher && 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.siteName, this.siteId).then((newInfo) => {
this.spaceUsage = newInfo;
}).catch(() => {
// Ignore cancelled confirmation modal.
});
}
/**
* Open a handler.
*
* @param page Page to open.
* @param params Params of the page to open.
*/
openHandler(page: string, params?: any): void {
this.selectedPage = page;
this.splitviewCtrl.push(page, params);
}
/**
* Show information about space usage actions.
*/
showSpaceInfo(): void {
this.domUtils.showAlert(this.translate.instant('core.help'),
this.translate.instant('core.settings.spaceusagehelp'));
}
/**
* Show information about sync actions.
*/
showSyncInfo(): void {
this.domUtils.showAlert(this.translate.instant('core.help'),
this.translate.instant('core.settings.synchronizenowhelp'));
}
/**
* Page destroyed.
*/
ngOnDestroy(): void {
this.isDestroyed = true;
this.sitesObserver && this.sitesObserver.off();
}
}

View File

@ -1,13 +1,20 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'core.settings.spaceusage' | translate }}</ion-title>
<ion-buttons end>
</ion-buttons>
</ion-navbar>
</ion-header>
<core-navbar-buttons>
<button ion-button icon-only (click)="showInfo()" [attr.aria-label]="'core.info' | translate">
<ion-icon name="information-circle"></ion-icon>
</button>
</core-navbar-buttons>
<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 +26,11 @@
<ion-icon name="trash"></ion-icon>
</button>
</ion-item>
<ion-item-divider>
<p>{{ 'core.settings.total' | translate }}</p>
<ion-item-divider *ngIf="totals">
<h2>{{ 'core.settings.total' | translate }}</h2>
<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

@ -15,14 +15,9 @@
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 { CoreSitesProvider, CoreSiteBasicInfo } 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 { CoreSettingsHelper, CoreSiteSpaceUsage } from '../../providers/helper';
/**
* Page that displays the space usage settings.
@ -34,20 +29,19 @@ 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,
protected domUtils: CoreDomUtilsProvider,
protected translate: TranslateService,
) {
this.currentSiteId = this.sitesProvider.getCurrentSiteId();
}
@ -55,66 +49,36 @@ export class CoreSettingsSpaceUsagePage {
* View loaded.
*/
ionViewDidLoad(): void {
this.fetchData().finally(() => {
this.usageLoaded = true;
this.loadSiteData().finally(() => {
this.loaded = true;
});
}
/**
* Convenience function to calculate each site's usage, and the total usage.
* Convenience function to load site data/usage and calculate the totals.
*
* @return Resolved when done.
*/
protected calculateSizeUsage(): Promise<any> {
return this.sitesProvider.getSortedSites().then((sites) => {
this.sites = sites;
protected async loadSiteData(): Promise<void> {
// Calculate total usage.
let totalSize = 0;
let totalEntries = 0;
// Get space usage.
const promises = this.sites.map((siteEntry) => {
return this.sitesProvider.getSite(siteEntry.id).then((site) => {
const proms2 = [];
this.sites = await this.sitesProvider.getSortedSites();
proms2.push(this.calcSiteClearRows(site).then((rows) => {
siteEntry.cacheEntries = rows;
}));
// Get space usage.
await Promise.all(this.sites.map(async (site) => {
const siteInfo = await this.settingsHelper.getSiteSpaceUsage(site.id);
proms2.push(site.getSpaceUsage().then((size) => {
siteEntry.spaceUsage = size;
}));
site.cacheEntries = siteInfo.cacheEntries;
site.spaceUsage = siteInfo.spaceUsage;
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);
this.totals.spaceUsage = totalSize;
this.totals.cacheEntries = totalEntries;
}
/**
@ -123,96 +87,40 @@ export class CoreSettingsSpaceUsagePage {
* @param refresher Refresher.
*/
refreshData(refresher: any): void {
this.fetchData().finally(() => {
this.loadSiteData().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);
}).catch(() => {
// Ignore cancelled confirmation modal.
});
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.
});
}
/**
* Show information about space usage actions.
*/
showInfo(): void {
this.domUtils.showAlert(this.translate.instant('core.help'),
this.translate.instant('core.settings.spaceusagehelp'));
}
}
/**
* 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

@ -1,12 +1,19 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'core.settings.synchronization' | translate }}</ion-title>
<ion-buttons end>
</ion-buttons>
</ion-navbar>
</ion-header>
<core-navbar-buttons>
<button ion-button icon-only (click)="showInfo()" [attr.aria-label]="'core.info' | translate">
<ion-icon name="information-circle"></ion-icon>
</button>
</core-navbar-buttons>
<ion-content>
<core-loading [hideUntil]="sitesLoaded">
<ion-item-divider>
<p>{{ 'core.settings.syncsettings' | translate }}</p>
<h2>{{ 'core.settings.syncsettings' | translate }}</h2>
</ion-item-divider>
<ion-item text-wrap>
<ion-label>{{ 'core.settings.enablesyncwifi' | translate }}</ion-label>
@ -14,7 +21,7 @@
</ion-toggle>
</ion-item>
<ion-item-divider>
<p>{{ 'core.settings.sites' | translate }}</p>
<h2>{{ 'core.settings.sites' | translate }}</h2>
</ion-item-divider>
<ion-item *ngFor="let site of sites" [class.core-primary-selected-item]="site.id == currentSiteId" text-wrap>
<h2><core-format-text [text]="site.siteName" clean="true" [siteId]="site.id"></core-format-text></h2>

View File

@ -14,6 +14,7 @@
import { Component, OnDestroy } from '@angular/core';
import { IonicPage } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreConstants } from '@core/constants';
import { CoreEventsProvider } from '@providers/events';
import { CoreSitesProvider, CoreSiteBasicInfo } from '@providers/sites';
@ -36,11 +37,15 @@ export class CoreSettingsSynchronizationPage implements OnDestroy {
sitesObserver: any;
currentSiteId = '';
syncOnlyOnWifi = false;
isDestroyed = false;
protected isDestroyed = false;
constructor(private configProvider: CoreConfigProvider, private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
private settingsHelper: CoreSettingsHelper) {
constructor(protected configProvider: CoreConfigProvider,
protected eventsProvider: CoreEventsProvider,
protected sitesProvider: CoreSitesProvider,
protected domUtils: CoreDomUtilsProvider,
protected settingsHelper: CoreSettingsHelper,
protected translate: TranslateService,
) {
this.currentSiteId = this.sitesProvider.getCurrentSiteId();
@ -85,7 +90,8 @@ export class CoreSettingsSynchronizationPage implements OnDestroy {
* @param siteId Site ID.
*/
synchronize(siteId: string): void {
this.settingsHelper.synchronizeSite(this.syncOnlyOnWifi, siteId).catch((error) => {
// Using syncOnlyOnWifi false to force manual sync.
this.settingsHelper.synchronizeSite(false, siteId).catch((error) => {
if (this.isDestroyed) {
return;
}
@ -103,6 +109,14 @@ export class CoreSettingsSynchronizationPage implements OnDestroy {
return !!this.settingsHelper.getSiteSyncPromise(siteId);
}
/**
* Show information about sync actions.
*/
showInfo(): void {
this.domUtils.showAlert(this.translate.instant('core.help'),
this.translate.instant('core.settings.synchronizenowhelp'));
}
/**
* Page destroyed.
*/

View File

@ -18,13 +18,24 @@ 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 +45,19 @@ 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,101 @@ 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
};
siteName = await this.filterProvider.formatText(siteName, {clean: true, singleLine: true, filter: false}, [], siteId);
const title = this.translate.instant('core.settings.deletesitefilestitle');
const message = this.translate.instant('core.settings.deletesitefiles', {sitename: siteName});
await this.domUtils.showConfirm(message, title);
const site = await this.sitesProvider.getSite(siteId);
// Clear cache tables.
const cleanSchemas = this.sitesProvider.getSiteTableSchemasToClear(site);
const promises = cleanSchemas.map((name) => 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(async (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);
siteInfo.spaceUsage = await site.getSpaceUsage();
}
}).then(async () => {
this.eventsProvider.trigger(CoreEventsProvider.SITE_STORAGE_DELETED, {}, site.getId());
siteInfo.cacheEntries = await this.calcSiteClearRows(site);
}));
await Promise.all(promises);
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> {
const site = await this.sitesProvider.getSite(siteId);
// Get space usage.
const siteInfo: CoreSiteSpaceUsage = {
cacheEntries: 0,
spaceUsage: 0,
};
await Promise.all([
this.calcSiteClearRows(site).then((rows) => siteInfo.cacheEntries = rows),
site.getSpaceUsage().then((size) => siteInfo.spaceUsage = size),
]);
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(site);
let totalEntries = 0;
await Promise.all(clearTables.map(async (name) =>
totalEntries += await site.getDb().countRecords(name)
));
return totalEntries;
}
/**
* Get a certain processor from a list of processors.
*

View File

@ -14,7 +14,7 @@
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
</ion-refresher>
<!-- Allow selecting the site to view. -->
<core-site-picker [hidden]="!filesLoaded" [initialSite]="siteId" (siteSelected)="changeSite($event)"></core-site-picker>
<core-site-picker *ngIf="showSitePicker" [hidden]="!filesLoaded" [initialSite]="siteId" (siteSelected)="changeSite($event)"></core-site-picker>
<core-loading [hideUntil]="filesLoaded">
<ion-list *ngIf="files && files.length > 0">
<ng-container *ngFor="let file of files; let idx = index">

View File

@ -35,6 +35,7 @@ export class CoreSharedFilesListPage implements OnInit, OnDestroy {
isModal: boolean;
manage: boolean;
pick: boolean; // To pick a file you MUST use a modal.
showSitePicker: boolean;
path = '';
title: string;
filesLoaded: boolean;
@ -52,6 +53,7 @@ export class CoreSharedFilesListPage implements OnInit, OnDestroy {
this.manage = !!navParams.get('manage');
this.pick = !!navParams.get('pick');
this.path = navParams.get('path') || '';
this.showSitePicker = !navParams.get('hideSitePicker');
}
/**

View File

@ -1,4 +1,4 @@
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesAvailableCoursesPage'">
<a *ngIf="show" ion-item text-wrap navPush="CoreCoursesAvailableCoursesPage">
<core-icon name="fa-graduation-cap" fixed-width item-start></core-icon>
<h2>{{ 'core.courses.availablecourses' | translate}}</h2>
</a>

View File

@ -1,4 +1,4 @@
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesCategoriesPage'">
<a *ngIf="show" ion-item text-wrap navPush="CoreCoursesCategoriesPage">
<ion-icon name="folder" item-start></ion-icon>
<h2>{{ 'core.courses.categories' | translate}}</h2>
</a>

View File

@ -1,4 +1,4 @@
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesSearchPage'">
<a *ngIf="show" ion-item text-wrap navPush="CoreCoursesSearchPage">
<ion-icon name="search" item-start></ion-icon>
<h2>{{ 'core.courses.searchcourses' | translate}}</h2>
</a>

View File

@ -1,4 +1,4 @@
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesMyCoursesPage'">
<a *ngIf="show" ion-item text-wrap navPush="CoreCoursesMyCoursesPage">
<core-icon name="fa-graduation-cap" fixed-width item-start></core-icon>
<h2>{{ 'core.courses.mycourses' | translate}}</h2>
</a>