commit
357c18f3a9
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
],
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{
|
||||
"appsettings": "App settings",
|
||||
"changesite": "Change site",
|
||||
"help": "Help",
|
||||
"logout": "Log out",
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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 {}
|
|
@ -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()) {
|
|
@ -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>
|
|
@ -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 {}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue