forked from EVOgeek/Vmeda.Online
commit
a833cd6807
|
@ -489,6 +489,12 @@
|
||||||
"addon.mod_forum.couldnotadd": "forum",
|
"addon.mod_forum.couldnotadd": "forum",
|
||||||
"addon.mod_forum.cutoffdatereached": "forum",
|
"addon.mod_forum.cutoffdatereached": "forum",
|
||||||
"addon.mod_forum.discussion": "forum",
|
"addon.mod_forum.discussion": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbycreatedasc": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbycreateddesc": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbylastpostasc": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbylastpostdesc": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbyrepliesasc": "forum",
|
||||||
|
"addon.mod_forum.discussionlistsortbyrepliesdesc": "forum",
|
||||||
"addon.mod_forum.discussionlocked": "forum",
|
"addon.mod_forum.discussionlocked": "forum",
|
||||||
"addon.mod_forum.discussionpinned": "forum",
|
"addon.mod_forum.discussionpinned": "forum",
|
||||||
"addon.mod_forum.discussionsubscription": "forum",
|
"addon.mod_forum.discussionsubscription": "forum",
|
||||||
|
@ -1734,6 +1740,7 @@
|
||||||
"core.sizemb": "moodle",
|
"core.sizemb": "moodle",
|
||||||
"core.sizetb": "local_moodlemobileapp",
|
"core.sizetb": "local_moodlemobileapp",
|
||||||
"core.sorry": "local_moodlemobileapp",
|
"core.sorry": "local_moodlemobileapp",
|
||||||
|
"core.sort": "moodle",
|
||||||
"core.sortby": "moodle",
|
"core.sortby": "moodle",
|
||||||
"core.start": "grouptool",
|
"core.start": "grouptool",
|
||||||
"core.strftimedate": "langconfig",
|
"core.strftimedate": "langconfig",
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
<core-context-menu-item *ngIf="loaded && (hasOffline || hasOfflineRatings) && isOnline" [priority]="600" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
<core-context-menu-item *ngIf="loaded && (hasOffline || hasOfflineRatings) && isOnline" [priority]="600" [content]="'core.settings.synchronizenow' | translate" (action)="doRefresh(null, $event, true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
<core-context-menu-item *ngIf="prefetchStatusIcon" [priority]="500" [content]="prefetchText" (action)="prefetch()" [iconAction]="prefetchStatusIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||||
<core-context-menu-item *ngIf="size" [priority]="400" [content]="size" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item>
|
<core-context-menu-item *ngIf="size" [priority]="400" [content]="size" [iconDescription]="'cube'" (action)="removeFiles()" [iconAction]="'trash'"></core-context-menu-item>
|
||||||
|
<core-context-menu-item *ngIf="sortingAvailable" [priority]="300" [content]="'core.sort' | translate" (action)="showSortOrderSelector($event)" iconAction="fa-sort"></core-context-menu-item>
|
||||||
</core-context-menu>
|
</core-context-menu>
|
||||||
</core-navbar-buttons>
|
</core-navbar-buttons>
|
||||||
|
|
||||||
|
@ -30,6 +31,21 @@
|
||||||
<ion-icon name="information-circle"></ion-icon> {{ availabilityMessage }}
|
<ion-icon name="information-circle"></ion-icon> {{ availabilityMessage }}
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
|
<core-empty-box *ngIf="forum && discussions.length == 0" icon="chatbubbles" [message]="'addon.mod_forum.forumnodiscussionsyet' | translate" class="core-empty-box-clickable">
|
||||||
|
<div padding *ngIf="forum.cancreatediscussions">
|
||||||
|
<button ion-button block (click)="openNewDiscussion()">
|
||||||
|
{{ 'addon.mod_forum.addanewdiscussion' | translate }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</core-empty-box>
|
||||||
|
|
||||||
|
<div text-wrap *ngIf="sortingAvailable && selectedSortOrder" ion-row padding-horizontal padding-top margin-bottom>
|
||||||
|
<button *ngIf="sortingAvailable" ion-button padding-horizontal icon-end ion-col (click)="showSortOrderSelector($event)" color="light" class="core-button-select button-no-uppercase" [attr.aria-label]="('core.sort' | translate)" aria-haspopup="true" [attr.aria-expanded]="sortOrderSelectorExpanded" aria-controls="addon-mod-forum-sort-order-selector" id="addon-mod-forum-sort-order-button">
|
||||||
|
<span class="core-section-selector-text">{{ selectedSortOrder.label | translate }}</span>
|
||||||
|
<ion-icon name="arrow-dropdown" ios="md-arrow-dropdown"></ion-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ng-container *ngIf="forum && discussions.length > 0">
|
<ng-container *ngIf="forum && discussions.length > 0">
|
||||||
<ion-card *ngFor="let discussion of offlineDiscussions" (click)="openNewDiscussion(discussion.timecreated)" [class.addon-forum-discussion-selected]="discussion.timecreated == -selectedDiscussion">
|
<ion-card *ngFor="let discussion of offlineDiscussions" (click)="openNewDiscussion(discussion.timecreated)" [class.addon-forum-discussion-selected]="discussion.timecreated == -selectedDiscussion">
|
||||||
<ion-item text-wrap>
|
<ion-item text-wrap>
|
||||||
|
@ -86,14 +102,6 @@
|
||||||
</ion-card>
|
</ion-card>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<core-empty-box *ngIf="forum && discussions.length == 0" icon="chatbubbles" [message]="'addon.mod_forum.forumnodiscussionsyet' | translate" class="core-empty-box-clickable">
|
|
||||||
<div padding *ngIf="forum.cancreatediscussions">
|
|
||||||
<button ion-button block (click)="openNewDiscussion()">
|
|
||||||
{{ 'addon.mod_forum.addanewdiscussion' | translate }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</core-empty-box>
|
|
||||||
|
|
||||||
<core-infinite-loading [enabled]="canLoadMore" (action)="fetchMoreDiscussions($event)" [error]="loadMoreError"></core-infinite-loading>
|
<core-infinite-loading [enabled]="canLoadMore" (action)="fetchMoreDiscussions($event)" [error]="loadMoreError"></core-infinite-loading>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,15 @@ ion-app.app-root addon-mod-forum-index {
|
||||||
.addon-forum-discussion-selected {
|
.addon-forum-discussion-selected {
|
||||||
border-top: 5px solid $core-splitview-selected;
|
border-top: 5px solid $core-splitview-selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-forum-star {
|
.addon-forum-star {
|
||||||
color: $core-star-color;
|
color: $core-star-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.core-button-select .core-section-selector-text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
line-height: 2em;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Component, Optional, Injector, ViewChild } from '@angular/core';
|
import { Component, Optional, Injector, ViewChild } from '@angular/core';
|
||||||
import { Content, NavController } from 'ionic-angular';
|
import { Content, ModalController, NavController } from 'ionic-angular';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component';
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
|
@ -52,6 +52,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
addDiscussionText = this.translate.instant('addon.mod_forum.addanewdiscussion');
|
addDiscussionText = this.translate.instant('addon.mod_forum.addanewdiscussion');
|
||||||
availabilityMessage: string;
|
availabilityMessage: string;
|
||||||
|
|
||||||
|
sortingAvailable: boolean;
|
||||||
|
sortOrders = [];
|
||||||
|
selectedSortOrder = null;
|
||||||
|
sortOrderSelectorExpanded = false;
|
||||||
|
|
||||||
protected syncEventName = AddonModForumSyncProvider.AUTO_SYNCED;
|
protected syncEventName = AddonModForumSyncProvider.AUTO_SYNCED;
|
||||||
protected page = 0;
|
protected page = 0;
|
||||||
protected trackPosts = false;
|
protected trackPosts = false;
|
||||||
|
@ -69,6 +74,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
constructor(injector: Injector,
|
constructor(injector: Injector,
|
||||||
@Optional() protected content: Content,
|
@Optional() protected content: Content,
|
||||||
protected navCtrl: NavController,
|
protected navCtrl: NavController,
|
||||||
|
protected modalCtrl: ModalController,
|
||||||
protected groupsProvider: CoreGroupsProvider,
|
protected groupsProvider: CoreGroupsProvider,
|
||||||
protected userProvider: CoreUserProvider,
|
protected userProvider: CoreUserProvider,
|
||||||
protected forumProvider: AddonModForumProvider,
|
protected forumProvider: AddonModForumProvider,
|
||||||
|
@ -79,6 +85,9 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
protected prefetchHandler: AddonModForumPrefetchHandler,
|
protected prefetchHandler: AddonModForumPrefetchHandler,
|
||||||
protected ratingOffline: CoreRatingOfflineProvider) {
|
protected ratingOffline: CoreRatingOfflineProvider) {
|
||||||
super(injector);
|
super(injector);
|
||||||
|
|
||||||
|
this.sortingAvailable = this.forumProvider.isDiscussionListSortingAvailable();
|
||||||
|
this.sortOrders = this.forumProvider.getAvailableSortOrders();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,7 +171,9 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise<any> {
|
||||||
this.loadMoreError = false;
|
this.loadMoreError = false;
|
||||||
|
|
||||||
return this.forumProvider.getForum(this.courseId, this.module.id).then((forum) => {
|
const promises = [];
|
||||||
|
|
||||||
|
promises.push(this.forumProvider.getForum(this.courseId, this.module.id).then((forum) => {
|
||||||
this.forum = forum;
|
this.forum = forum;
|
||||||
|
|
||||||
this.description = forum.intro || this.description;
|
this.description = forum.intro || this.description;
|
||||||
|
@ -212,7 +223,11 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
this.canAddDiscussion = this.forum.cancreatediscussions && !cutoffDateReached;
|
this.canAddDiscussion = this.forum.cancreatediscussions && !cutoffDateReached;
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}).then(() => {
|
}));
|
||||||
|
|
||||||
|
promises.push(this.fetchSortOrderPreference());
|
||||||
|
|
||||||
|
return Promise.all(promises).then(() => {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this.fetchOfflineDiscussion(),
|
this.fetchOfflineDiscussion(),
|
||||||
this.fetchDiscussions(refresh),
|
this.fetchDiscussions(refresh),
|
||||||
|
@ -291,7 +306,7 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
this.page = 0;
|
this.page = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.forumProvider.getDiscussions(this.forum.id, this.page).then((response) => {
|
return this.forumProvider.getDiscussions(this.forum.id, this.selectedSortOrder.value, this.page).then((response) => {
|
||||||
let promise;
|
let promise;
|
||||||
if (this.usesGroups) {
|
if (this.usesGroups) {
|
||||||
promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions);
|
promise = this.forumProvider.formatDiscussionsGroups(this.forum.cmid, response.discussions);
|
||||||
|
@ -366,6 +381,27 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to fetch the sort order preference.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected fetchSortOrderPreference(): Promise<any> {
|
||||||
|
let promise;
|
||||||
|
if (this.sortingAvailable) {
|
||||||
|
promise = this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER).then((value) => {
|
||||||
|
return value ? parseInt(value, 10) : null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Use default.
|
||||||
|
promise = Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((value) => {
|
||||||
|
this.selectedSortOrder = this.sortOrders.find((sortOrder) => sortOrder.value === value) || this.sortOrders[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform the invalidate content function.
|
* Perform the invalidate content function.
|
||||||
*
|
*
|
||||||
|
@ -382,6 +418,10 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
promises.push(this.forumProvider.invalidateAccessInformation(this.forum.id));
|
promises.push(this.forumProvider.invalidateAccessInformation(this.forum.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.sortingAvailable) {
|
||||||
|
promises.push(this.userProvider.invalidateUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER));
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,6 +524,37 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
this.selectedDiscussion = 0;
|
this.selectedDiscussion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display the sort order selector modal.
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event Event.
|
||||||
|
*/
|
||||||
|
showSortOrderSelector(event: MouseEvent): void {
|
||||||
|
if (!this.sortingAvailable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = { sortOrders: this.sortOrders, selected: this.selectedSortOrder.value };
|
||||||
|
const modal = this.modalCtrl.create('AddonModForumSortOrderSelectorPage', params);
|
||||||
|
modal.onDidDismiss((sortOrder) => {
|
||||||
|
this.sortOrderSelectorExpanded = false;
|
||||||
|
|
||||||
|
if (sortOrder && sortOrder.value != this.selectedSortOrder.value) {
|
||||||
|
this.selectedSortOrder = sortOrder;
|
||||||
|
this.page = 0;
|
||||||
|
this.userProvider.setUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER, sortOrder.value.toFixed(0))
|
||||||
|
.then(() => {
|
||||||
|
this.showLoadingAndFetch();
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Error updating preference.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.present({ev: event});
|
||||||
|
this.sortOrderSelectorExpanded = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being destroyed.
|
* Component being destroyed.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
"couldnotadd": "Could not add your post due to an unknown error",
|
"couldnotadd": "Could not add your post due to an unknown error",
|
||||||
"cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
|
"cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
|
||||||
"discussion": "Discussion",
|
"discussion": "Discussion",
|
||||||
|
"discussionlistsortbycreatedasc": "Sort by creation date in ascending order",
|
||||||
|
"discussionlistsortbycreateddesc": "Sort by creation date in descending order",
|
||||||
|
"discussionlistsortbylastpostasc": "Sort by last post creation date in ascending order",
|
||||||
|
"discussionlistsortbylastpostdesc": "Sort by last post creation date in descending order",
|
||||||
|
"discussionlistsortbyrepliesasc": "Sort by number of replies in ascending order",
|
||||||
|
"discussionlistsortbyrepliesdesc": "Sort by number of replies in descending order",
|
||||||
"discussionlocked": "This discussion has been locked so you can no longer reply to it.",
|
"discussionlocked": "This discussion has been locked so you can no longer reply to it.",
|
||||||
"discussionpinned": "Pinned",
|
"discussionpinned": "Pinned",
|
||||||
"discussionsubscription": "Discussion subscription",
|
"discussionsubscription": "Discussion subscription",
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'core.sort' | translate }}</ion-title>
|
||||||
|
<ion-buttons end>
|
||||||
|
<button ion-button icon-only (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
|
||||||
|
<ion-icon name="close"></ion-icon>
|
||||||
|
</button>
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-list id="addon-mod-forum-sort-selector" role="menu" aria-labelledby="addon-mod-forum-sort-order-button">
|
||||||
|
<ng-container *ngFor="let sortOrder of sortOrders">
|
||||||
|
<a ion-item text-wrap (click)="selectSortOrder(sortOrder)" [class.core-primary-selected-item]="selected == sortOrder.value" detail-none role="menuitem" [attr.aria-label]="sortOrder.label | translate">
|
||||||
|
<h2><core-format-text [text]="sortOrder.label | translate"></core-format-text></h2>
|
||||||
|
</a>
|
||||||
|
</ng-container>
|
||||||
|
</ion-list>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,33 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { IonicPageModule } from 'ionic-angular';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { AddonModForumSortOrderSelectorPage } from './sort-order-selector';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AddonModForumSortOrderSelectorPage,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
IonicPageModule.forChild(AddonModForumSortOrderSelectorPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonModForumSortOrderSelectorPagePageModule {}
|
|
@ -0,0 +1,51 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { IonicPage, NavParams, ViewController } from 'ionic-angular';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the sort selector.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'addon-mod-forum-sort-order-selector' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-addon-mod-forum-sort-order-selector',
|
||||||
|
templateUrl: 'sort-order-selector.html',
|
||||||
|
})
|
||||||
|
export class AddonModForumSortOrderSelectorPage {
|
||||||
|
|
||||||
|
sortOrders = [];
|
||||||
|
selected: number;
|
||||||
|
|
||||||
|
constructor(navParams: NavParams, private viewCtrl: ViewController) {
|
||||||
|
this.sortOrders = navParams.get('sortOrders');
|
||||||
|
this.selected = navParams.get('selected');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the modal.
|
||||||
|
*/
|
||||||
|
closeModal(): void {
|
||||||
|
this.viewCtrl.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a sort order.
|
||||||
|
*
|
||||||
|
* @param {any} sortOrder Selected sort order.
|
||||||
|
*/
|
||||||
|
selectSortOrder(sortOrder: any): void {
|
||||||
|
this.viewCtrl.dismiss(sortOrder);
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreAppProvider } from '@providers/app';
|
import { CoreAppProvider } from '@providers/app';
|
||||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreGroupsProvider } from '@providers/groups';
|
import { CoreGroupsProvider } from '@providers/groups';
|
||||||
|
@ -38,6 +38,14 @@ export class AddonModForumProvider {
|
||||||
static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_lock_discussion';
|
static CHANGE_DISCUSSION_EVENT = 'addon_mod_forum_lock_discussion';
|
||||||
static MARK_READ_EVENT = 'addon_mod_forum_mark_read';
|
static MARK_READ_EVENT = 'addon_mod_forum_mark_read';
|
||||||
|
|
||||||
|
static PREFERENCE_SORTORDER = 'forum_discussionlistsortorder';
|
||||||
|
static SORTORDER_LASTPOST_DESC = 1;
|
||||||
|
static SORTORDER_LASTPOST_ASC = 2;
|
||||||
|
static SORTORDER_CREATED_DESC = 3;
|
||||||
|
static SORTORDER_CREATED_ASC = 4;
|
||||||
|
static SORTORDER_REPLIES_DESC = 5;
|
||||||
|
static SORTORDER_REPLIES_ASC = 6;
|
||||||
|
|
||||||
protected ROOT_CACHE_KEY = 'mmaModForum:';
|
protected ROOT_CACHE_KEY = 'mmaModForum:';
|
||||||
|
|
||||||
constructor(private appProvider: CoreAppProvider,
|
constructor(private appProvider: CoreAppProvider,
|
||||||
|
@ -58,7 +66,7 @@ export class AddonModForumProvider {
|
||||||
* @return {string} Cache key.
|
* @return {string} Cache key.
|
||||||
*/
|
*/
|
||||||
protected getCanAddDiscussionCacheKey(forumId: number, groupId: number): string {
|
protected getCanAddDiscussionCacheKey(forumId: number, groupId: number): string {
|
||||||
return this.getCommonCanAddDiscussionCacheKey(forumId) + ':' + groupId;
|
return this.getCommonCanAddDiscussionCacheKey(forumId) + groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +76,7 @@ export class AddonModForumProvider {
|
||||||
* @return {string} Cache key.
|
* @return {string} Cache key.
|
||||||
*/
|
*/
|
||||||
protected getCommonCanAddDiscussionCacheKey(forumId: number): string {
|
protected getCommonCanAddDiscussionCacheKey(forumId: number): string {
|
||||||
return this.ROOT_CACHE_KEY + 'canadddiscussion:' + forumId;
|
return this.ROOT_CACHE_KEY + 'canadddiscussion:' + forumId + ':';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,10 +113,17 @@ export class AddonModForumProvider {
|
||||||
* Get cache key for forum discussions list WS calls.
|
* Get cache key for forum discussions list WS calls.
|
||||||
*
|
*
|
||||||
* @param {number} forumId Forum ID.
|
* @param {number} forumId Forum ID.
|
||||||
* @return {string} Cache key.
|
* @param {number} sortOrder Sort order.
|
||||||
|
* @return {string} Cache key.
|
||||||
*/
|
*/
|
||||||
protected getDiscussionsListCacheKey(forumId: number): string {
|
protected getDiscussionsListCacheKey(forumId: number, sortOrder: number): string {
|
||||||
return this.ROOT_CACHE_KEY + 'discussions:' + forumId;
|
let key = this.ROOT_CACHE_KEY + 'discussions:' + forumId;
|
||||||
|
|
||||||
|
if (sortOrder != AddonModForumProvider.SORTORDER_LASTPOST_DESC) {
|
||||||
|
key += ':' + sortOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -452,10 +467,64 @@ export class AddonModForumProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether discussion lists can be sorted.
|
||||||
|
*
|
||||||
|
* @param {CoreSite} [site] Site. If not defined, current site.
|
||||||
|
* @return {boolean} True if discussion lists can be sorted.
|
||||||
|
*/
|
||||||
|
isDiscussionListSortingAvailable(site?: CoreSite): boolean {
|
||||||
|
site = site || this.sitesProvider.getCurrentSite();
|
||||||
|
|
||||||
|
return site.isVersionGreaterEqualThan('3.7');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the list of available sort orders.
|
||||||
|
*
|
||||||
|
* @return {{label: string, value: number}[]} List of sort orders.
|
||||||
|
*/
|
||||||
|
getAvailableSortOrders(): {label: string, value: number}[] {
|
||||||
|
const sortOrders = [
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbylastpostdesc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_LASTPOST_DESC
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (this.isDiscussionListSortingAvailable()) {
|
||||||
|
sortOrders.push(
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbylastpostasc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_LASTPOST_ASC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbycreateddesc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_CREATED_DESC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbycreatedasc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_CREATED_ASC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbyrepliesdesc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_REPLIES_DESC
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'addon.mod_forum.discussionlistsortbyrepliesasc',
|
||||||
|
value: AddonModForumProvider.SORTORDER_REPLIES_ASC
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortOrders;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get forum discussions.
|
* Get forum discussions.
|
||||||
*
|
*
|
||||||
* @param {number} forumId Forum ID.
|
* @param {number} forumId Forum ID.
|
||||||
|
* @param {number} [sortOrder] Sort order.
|
||||||
* @param {number} [page=0] Page.
|
* @param {number} [page=0] Page.
|
||||||
* @param {boolean} [forceCache] True to always get the value from cache. false otherwise.
|
* @param {boolean} [forceCache] True to always get the value from cache. false otherwise.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
@ -463,23 +532,59 @@ export class AddonModForumProvider {
|
||||||
* - discussions: List of discussions.
|
* - discussions: List of discussions.
|
||||||
* - canLoadMore: True if there may be more discussions to load.
|
* - canLoadMore: True if there may be more discussions to load.
|
||||||
*/
|
*/
|
||||||
getDiscussions(forumId: number, page: number = 0, forceCache?: boolean, siteId?: string): Promise<any> {
|
getDiscussions(forumId: number, sortOrder?: number, page: number = 0, forceCache?: boolean, siteId?: string): Promise<any> {
|
||||||
|
sortOrder = sortOrder || AddonModForumProvider.SORTORDER_LASTPOST_DESC;
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
const params = {
|
let method = 'mod_forum_get_forum_discussions_paginated';
|
||||||
|
const params: any = {
|
||||||
forumid: forumId,
|
forumid: forumId,
|
||||||
sortby: 'timemodified',
|
|
||||||
sortdirection: 'DESC',
|
|
||||||
page: page,
|
page: page,
|
||||||
perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE
|
perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE
|
||||||
};
|
};
|
||||||
const preSets: any = {
|
|
||||||
cacheKey: this.getDiscussionsListCacheKey(forumId)
|
if (site.wsAvailable('mod_forum_get_forum_discussions')) {
|
||||||
|
// Since Moodle 3.7.
|
||||||
|
method = 'mod_forum_get_forum_discussions';
|
||||||
|
params.sortorder = sortOrder;
|
||||||
|
} else {
|
||||||
|
if (sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) {
|
||||||
|
params.sortby = 'timemodified';
|
||||||
|
params.sortdirection = 'DESC';
|
||||||
|
} else {
|
||||||
|
// Sorting not supported with the old WS method.
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder)
|
||||||
};
|
};
|
||||||
if (forceCache) {
|
if (forceCache) {
|
||||||
preSets.omitExpires = true;
|
preSets.omitExpires = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return site.read('mod_forum_get_forum_discussions_paginated', params, preSets).then((response) => {
|
return site.read(method, params, preSets).catch((error) => {
|
||||||
|
// Try to get the data from cache stored with the old WS method.
|
||||||
|
if (!this.appProvider.isOnline() && method == 'mod_forum_get_forum_discussion' &&
|
||||||
|
sortOrder == AddonModForumProvider.SORTORDER_LASTPOST_DESC) {
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
forumid: forumId,
|
||||||
|
page: page,
|
||||||
|
perpage: AddonModForumProvider.DISCUSSIONS_PER_PAGE,
|
||||||
|
sortby: 'timemodified',
|
||||||
|
sortdirection: 'DESC'
|
||||||
|
};
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getDiscussionsListCacheKey(forumId, sortOrder),
|
||||||
|
omitExpires: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('mod_forum_get_forum_discussions_paginated', params, preSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}).then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
this.storeUserData(response.discussions);
|
this.storeUserData(response.discussions);
|
||||||
|
|
||||||
|
@ -499,7 +604,8 @@ export class AddonModForumProvider {
|
||||||
* If a page fails, the discussions until that page will be returned along with a flag indicating an error occurred.
|
* If a page fails, the discussions until that page will be returned along with a flag indicating an error occurred.
|
||||||
*
|
*
|
||||||
* @param {number} forumId Forum ID.
|
* @param {number} forumId Forum ID.
|
||||||
* @param {boolean} forceCache True to always get the value from cache, false otherwise.
|
* @param {number} [sortOrder] Sort order.
|
||||||
|
* @param {boolean} [forceCache] True to always get the value from cache, false otherwise.
|
||||||
* @param {number} [numPages] Number of pages to get. If not defined, all pages.
|
* @param {number} [numPages] Number of pages to get. If not defined, all pages.
|
||||||
* @param {number} [startPage] Page to start. If not defined, first page.
|
* @param {number} [startPage] Page to start. If not defined, first page.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
@ -507,8 +613,8 @@ export class AddonModForumProvider {
|
||||||
* - discussions: List of discussions.
|
* - discussions: List of discussions.
|
||||||
* - error: True if an error occurred, false otherwise.
|
* - error: True if an error occurred, false otherwise.
|
||||||
*/
|
*/
|
||||||
getDiscussionsInPages(forumId: number, forceCache?: boolean, numPages?: number, startPage?: number, siteId?: string)
|
getDiscussionsInPages(forumId: number, sortOrder?: number, forceCache?: boolean, numPages?: number, startPage?: number,
|
||||||
: Promise<any> {
|
siteId?: string): Promise<any> {
|
||||||
if (typeof numPages == 'undefined') {
|
if (typeof numPages == 'undefined') {
|
||||||
numPages = -1;
|
numPages = -1;
|
||||||
}
|
}
|
||||||
|
@ -525,7 +631,7 @@ export class AddonModForumProvider {
|
||||||
|
|
||||||
const getPage = (page: number): Promise<any> => {
|
const getPage = (page: number): Promise<any> => {
|
||||||
// Get page discussions.
|
// Get page discussions.
|
||||||
return this.getDiscussions(forumId, page, forceCache, siteId).then((response) => {
|
return this.getDiscussions(forumId, sortOrder, page, forceCache, siteId).then((response) => {
|
||||||
result.discussions = result.discussions.concat(response.discussions);
|
result.discussions = result.discussions.concat(response.discussions);
|
||||||
numPages--;
|
numPages--;
|
||||||
|
|
||||||
|
@ -569,22 +675,32 @@ export class AddonModForumProvider {
|
||||||
invalidateContent(moduleId: number, courseId: number): Promise<any> {
|
invalidateContent(moduleId: number, courseId: number): Promise<any> {
|
||||||
// Get the forum first, we need the forum ID.
|
// Get the forum first, we need the forum ID.
|
||||||
return this.getForum(courseId, moduleId).then((forum) => {
|
return this.getForum(courseId, moduleId).then((forum) => {
|
||||||
// We need to get the list of discussions to be able to invalidate their posts.
|
const promises = [];
|
||||||
return this.getDiscussionsInPages(forum.id, true).then((response) => {
|
|
||||||
// Now invalidate the WS calls.
|
|
||||||
const promises = [];
|
|
||||||
|
|
||||||
promises.push(this.invalidateForumData(courseId));
|
promises.push(this.invalidateForumData(courseId));
|
||||||
promises.push(this.invalidateDiscussionsList(forum.id));
|
promises.push(this.invalidateDiscussionsList(forum.id));
|
||||||
promises.push(this.invalidateCanAddDiscussion(forum.id));
|
promises.push(this.invalidateCanAddDiscussion(forum.id));
|
||||||
promises.push(this.invalidateAccessInformation(forum.id));
|
promises.push(this.invalidateAccessInformation(forum.id));
|
||||||
|
|
||||||
response.discussions.forEach((discussion) => {
|
this.getAvailableSortOrders().forEach((sortOrder) => {
|
||||||
promises.push(this.invalidateDiscussionPosts(discussion.discussion));
|
// We need to get the list of discussions to be able to invalidate their posts.
|
||||||
});
|
promises.push(this.getDiscussionsInPages(forum.id, sortOrder.value, true).then((response) => {
|
||||||
|
// Now invalidate the WS calls.
|
||||||
|
const promises = [];
|
||||||
|
|
||||||
return this.utils.allPromises(promises);
|
response.discussions.forEach((discussion) => {
|
||||||
|
promises.push(this.invalidateDiscussionPosts(discussion.discussion));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.utils.allPromises(promises);
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.isDiscussionListSortingAvailable()) {
|
||||||
|
promises.push(this.userProvider.invalidateUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.utils.allPromises(promises);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -623,7 +739,9 @@ export class AddonModForumProvider {
|
||||||
*/
|
*/
|
||||||
invalidateDiscussionsList(forumId: number, siteId?: string): Promise<any> {
|
invalidateDiscussionsList(forumId: number, siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
return site.invalidateWsCacheForKey(this.getDiscussionsListCacheKey(forumId));
|
return this.utils.allPromises(this.getAvailableSortOrders().map((sortOrder) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getDiscussionsListCacheKey(forumId, sortOrder.value));
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -162,7 +162,7 @@ export class AddonModForumHelperProvider {
|
||||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
const findDiscussion = (page: number): Promise<any> => {
|
const findDiscussion = (page: number): Promise<any> => {
|
||||||
return this.forumProvider.getDiscussions(forumId, page, false, siteId).then((response) => {
|
return this.forumProvider.getDiscussions(forumId, undefined, page, false, siteId).then((response) => {
|
||||||
if (response.discussions && response.discussions.length > 0) {
|
if (response.discussions && response.discussions.length > 0) {
|
||||||
const discussion = response.discussions.find((discussion) => discussion.id == discussionId);
|
const discussion = response.discussions.find((discussion) => discussion.id == discussionId);
|
||||||
if (discussion) {
|
if (discussion) {
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { CoreGroupsProvider } from '@providers/groups';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonModForumProvider } from './forum';
|
import { AddonModForumProvider } from './forum';
|
||||||
import { AddonModForumSyncProvider } from './sync';
|
import { AddonModForumSyncProvider } from './sync';
|
||||||
import { CoreRatingProvider } from '@core/rating/providers/rating';
|
import { CoreRatingProvider, CoreRatingInfo } from '@core/rating/providers/rating';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to prefetch forums.
|
* Handler to prefetch forums.
|
||||||
|
@ -107,26 +107,36 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
* @return {Promise<any[]>} Promise resolved with array of posts.
|
* @return {Promise<any[]>} Promise resolved with array of posts.
|
||||||
*/
|
*/
|
||||||
protected getPostsForPrefetch(forum: any): Promise<any[]> {
|
protected getPostsForPrefetch(forum: any): Promise<any[]> {
|
||||||
// Get discussions in first 2 pages.
|
const posts = {};
|
||||||
return this.forumProvider.getDiscussionsInPages(forum.id, false, 2).then((response) => {
|
const ratingInfos: CoreRatingInfo[] = [];
|
||||||
if (response.error) {
|
|
||||||
return Promise.reject(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const promises = [];
|
const promises = this.forumProvider.getAvailableSortOrders().map((sortOrder) => {
|
||||||
let posts = [];
|
// Get discussions in first 2 pages.
|
||||||
|
return this.forumProvider.getDiscussionsInPages(forum.id, sortOrder.value, false, 2).then((response) => {
|
||||||
|
if (response.error) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
response.discussions.forEach((discussion) => {
|
const promises = [];
|
||||||
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion).then((response) => {
|
|
||||||
posts = posts.concat(response.posts);
|
|
||||||
|
|
||||||
return this.ratingProvider.prefetchRatings('module', forum.cmid, forum.scale, forum.course,
|
response.discussions.forEach((discussion) => {
|
||||||
response.ratinginfo);
|
promises.push(this.forumProvider.getDiscussionPosts(discussion.discussion).then((response) => {
|
||||||
}));
|
response.posts.forEach((post) => {
|
||||||
|
posts[post.id] = post;
|
||||||
|
});
|
||||||
|
ratingInfos.push(response.ratinginfo);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return Promise.all(promises).then(() => {
|
return Promise.all(promises).then(() => {
|
||||||
return posts;
|
const ratingInfo = this.ratingProvider.mergeRatingInfos(ratingInfos);
|
||||||
|
|
||||||
|
return this.ratingProvider.prefetchRatings('module', forum.cmid, forum.scale, forum.course, ratingInfo).then(() => {
|
||||||
|
return this.utils.objectToArray(posts);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -210,6 +220,11 @@ export class AddonModForumPrefetchHandler extends CoreCourseActivityPrefetchHand
|
||||||
// Prefetch access information.
|
// Prefetch access information.
|
||||||
promises.push(this.forumProvider.getAccessInformation(forum.id));
|
promises.push(this.forumProvider.getAccessInformation(forum.id));
|
||||||
|
|
||||||
|
// Prefetch sort order preference.
|
||||||
|
if (this.forumProvider.isDiscussionListSortingAvailable()) {
|
||||||
|
promises.push(this.userProvider.getUserPreference(AddonModForumProvider.PREFERENCE_SORTORDER));
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -489,6 +489,12 @@
|
||||||
"addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error",
|
"addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error",
|
||||||
"addon.mod_forum.cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
|
"addon.mod_forum.cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
|
||||||
"addon.mod_forum.discussion": "Discussion",
|
"addon.mod_forum.discussion": "Discussion",
|
||||||
|
"addon.mod_forum.discussionlistsortbycreatedasc": "Sort by creation date in ascending order",
|
||||||
|
"addon.mod_forum.discussionlistsortbycreateddesc": "Sort by creation date in descending order",
|
||||||
|
"addon.mod_forum.discussionlistsortbylastpostasc": "Sort by last post creation date in ascending order",
|
||||||
|
"addon.mod_forum.discussionlistsortbylastpostdesc": "Sort by last post creation date in descending order",
|
||||||
|
"addon.mod_forum.discussionlistsortbyrepliesasc": "Sort by number of replies in ascending order",
|
||||||
|
"addon.mod_forum.discussionlistsortbyrepliesdesc": "Sort by number of replies in descending order",
|
||||||
"addon.mod_forum.discussionlocked": "This discussion has been locked so you can no longer reply to it.",
|
"addon.mod_forum.discussionlocked": "This discussion has been locked so you can no longer reply to it.",
|
||||||
"addon.mod_forum.discussionpinned": "Pinned",
|
"addon.mod_forum.discussionpinned": "Pinned",
|
||||||
"addon.mod_forum.discussionsubscription": "Discussion subscription",
|
"addon.mod_forum.discussionsubscription": "Discussion subscription",
|
||||||
|
@ -1734,6 +1740,7 @@
|
||||||
"core.sizemb": "MB",
|
"core.sizemb": "MB",
|
||||||
"core.sizetb": "TB",
|
"core.sizetb": "TB",
|
||||||
"core.sorry": "Sorry...",
|
"core.sorry": "Sorry...",
|
||||||
|
"core.sort": "Sort",
|
||||||
"core.sortby": "Sort by",
|
"core.sortby": "Sort by",
|
||||||
"core.start": "Start",
|
"core.start": "Start",
|
||||||
"core.strftimedate": "%d %B %Y",
|
"core.strftimedate": "%d %B %Y",
|
||||||
|
|
|
@ -326,6 +326,44 @@ export class CoreRatingProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to merge two or more rating infos of the same instance.
|
||||||
|
*
|
||||||
|
* @param {CoreRatingInfo[]} ratingInfos Array of rating infos.
|
||||||
|
* @return {CoreRatingInfo} Merged rating info or null.
|
||||||
|
*/
|
||||||
|
mergeRatingInfos(ratingInfos: CoreRatingInfo[]): CoreRatingInfo {
|
||||||
|
let result: CoreRatingInfo = null;
|
||||||
|
const scales = {};
|
||||||
|
const ratings = {};
|
||||||
|
|
||||||
|
ratingInfos.forEach((ratingInfo) => {
|
||||||
|
if (!ratingInfo) {
|
||||||
|
// Skip null rating infos.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
result = Object.assign({}, ratingInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
(ratingInfo.scales || []).forEach((scale) => {
|
||||||
|
scales[scale.id] = scale;
|
||||||
|
});
|
||||||
|
|
||||||
|
(ratingInfo.ratings || []).forEach((rating) => {
|
||||||
|
ratings[rating.itemid] = rating;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
result.scales = this.utils.objectToArray(scales);
|
||||||
|
result.ratings = this.utils.objectToArray(ratings);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefetch individual ratings.
|
* Prefetch individual ratings.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of offline user preferences.
|
||||||
|
*/
|
||||||
|
export interface CoreUserOfflinePreference {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
onlinevalue: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle offline user preferences.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreUserOfflineProvider {
|
||||||
|
|
||||||
|
// Variables for database.
|
||||||
|
static PREFERENCES_TABLE = 'user_preferences';
|
||||||
|
protected siteSchema: CoreSiteSchema = {
|
||||||
|
name: 'CoreUserOfflineProvider',
|
||||||
|
version: 1,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
name: CoreUserOfflineProvider.PREFERENCES_TABLE,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
type: 'TEXT',
|
||||||
|
unique: true,
|
||||||
|
notNull: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'value',
|
||||||
|
type: 'TEXT'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'onlinevalue',
|
||||||
|
type: 'TEXT'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private sitesProvider: CoreSitesProvider) {
|
||||||
|
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get preferences that were changed offline.
|
||||||
|
*
|
||||||
|
* @return {Promise<CoreUserOfflinePreference[]>} Promise resolved with list of preferences.
|
||||||
|
*/
|
||||||
|
getChangedPreferences(siteId?: string): Promise<CoreUserOfflinePreference[]> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.getDb().getRecordsSelect(CoreUserOfflineProvider.PREFERENCES_TABLE, 'value != onlineValue');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an offline preference.
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @return {Promise<CoreUserOfflinePreference>} Promise resolved with the preference, rejected if not found.
|
||||||
|
*/
|
||||||
|
getPreference(name: string, siteId?: string): Promise<CoreUserOfflinePreference> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const conditions = { name };
|
||||||
|
|
||||||
|
return site.getDb().getRecord(CoreUserOfflineProvider.PREFERENCES_TABLE, conditions);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set an offline preference.
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @param {string} value Value of the preference.
|
||||||
|
* @param {string} onlineValue Online value of the preference. If unedfined, preserve previously stored value.
|
||||||
|
* @return {Promise<CoreUserPreference>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
setPreference(name: string, value: string, onlineValue?: string, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
let promise: Promise<string>;
|
||||||
|
if (typeof onlineValue == 'undefined') {
|
||||||
|
promise = this.getPreference(name, site.id).then((preference) => preference.onlinevalue);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve(onlineValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.then((onlineValue) => {
|
||||||
|
const record = { name, value, onlinevalue: onlineValue };
|
||||||
|
|
||||||
|
return site.getDb().insertRecord(CoreUserOfflineProvider.PREFERENCES_TABLE, record);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreCronHandler } from '@providers/cron';
|
||||||
|
import { CoreUserSyncProvider } from './sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronization cron handler.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreUserSyncCronHandler implements CoreCronHandler {
|
||||||
|
name = 'CoreUserSyncCronHandler';
|
||||||
|
|
||||||
|
constructor(private userSync: CoreUserSyncProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the process.
|
||||||
|
* Receives the ID of the site affected, undefined for all sites.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] ID of the site affected, undefined for all sites.
|
||||||
|
* @param {boolean} [force] Wether the execution is forced (manual sync).
|
||||||
|
* @return {Promise<any>} Promise resolved when done, rejected if failure.
|
||||||
|
*/
|
||||||
|
execute(siteId?: string, force?: boolean): Promise<any> {
|
||||||
|
return this.userSync.syncPreferences(siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the time between consecutive executions.
|
||||||
|
*
|
||||||
|
* @return {number} Time between consecutive executions (in ms).
|
||||||
|
*/
|
||||||
|
getInterval(): number {
|
||||||
|
return 300000; // 5 minutes.
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
// (C) Copyright 2015 Martin Dougiamas
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { CoreSyncBaseProvider } from '@classes/base-sync';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
import { CoreUserOfflineProvider } from './offline';
|
||||||
|
import { CoreUserProvider } from './user';
|
||||||
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||||
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreSyncProvider } from '@providers/sync';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to sync user preferences.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreUserSyncProvider extends CoreSyncBaseProvider {
|
||||||
|
|
||||||
|
static AUTO_SYNCED = 'core_user_autom_synced';
|
||||||
|
|
||||||
|
constructor(loggerProvider: CoreLoggerProvider, sitesProvider: CoreSitesProvider, appProvider: CoreAppProvider,
|
||||||
|
translate: TranslateService, syncProvider: CoreSyncProvider, textUtils: CoreTextUtilsProvider,
|
||||||
|
private userOffline: CoreUserOfflineProvider, private userProvider: CoreUserProvider,
|
||||||
|
private utils: CoreUtilsProvider, timeUtils: CoreTimeUtilsProvider) {
|
||||||
|
super('CoreUserSync', loggerProvider, sitesProvider, appProvider, syncProvider, textUtils, translate, timeUtils);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to synchronize user preferences in a certain site or in all sites.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID to sync. If not defined, sync all sites.
|
||||||
|
* @return {Promise<any>} Promise resolved if sync is successful, rejected if sync fails.
|
||||||
|
*/
|
||||||
|
syncPreferences(siteId?: string): Promise<any> {
|
||||||
|
const syncFunctionLog = 'all user preferences';
|
||||||
|
|
||||||
|
return this.syncOnSites(syncFunctionLog, this.syncPreferencesFunc.bind(this), [], siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync user preferences of a site.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID to sync. If not defined, sync all sites.
|
||||||
|
* @param {Promise<any>} Promise resolved if sync is successful, rejected if sync fails.
|
||||||
|
*/
|
||||||
|
protected syncPreferencesFunc(siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
const syncId = 'preferences';
|
||||||
|
|
||||||
|
if (this.isSyncing(syncId, siteId)) {
|
||||||
|
// There's already a sync ongoing, return the promise.
|
||||||
|
return this.getOngoingSync(syncId, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const warnings = [];
|
||||||
|
|
||||||
|
this.logger.debug(`Try to sync user preferences`);
|
||||||
|
|
||||||
|
const syncPromise = this.userOffline.getChangedPreferences(siteId).then((preferences) => {
|
||||||
|
return this.utils.allPromises(preferences.map((preference) => {
|
||||||
|
return this.userProvider.getUserPreferenceOnline(preference.name, siteId).then((onlineValue) => {
|
||||||
|
if (preference.onlinevalue != onlineValue) {
|
||||||
|
// Prefernce was changed on web while the app was offline, do not sync.
|
||||||
|
return this.userOffline.setPreference(preference.name, onlineValue, onlineValue, siteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.userProvider.setUserPreference(name, preference.value, siteId).catch((error) => {
|
||||||
|
if (this.utils.isWebServiceError(error)) {
|
||||||
|
warnings.push(this.textUtils.getErrorMessageFromError(error));
|
||||||
|
} else {
|
||||||
|
// Couldn't connect to server, reject.
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}).then(() => {
|
||||||
|
// All done, return the warnings.
|
||||||
|
return warnings;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.addOngoingSync(syncId, syncPromise, siteId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,11 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreFilepoolProvider } from '@providers/filepool';
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
||||||
import { CoreLoggerProvider } from '@providers/logger';
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
import { CoreSitesProvider, CoreSiteSchema } from '@providers/sites';
|
||||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
import { CoreUserOfflineProvider } from './offline';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to provide user functionalities.
|
* Service to provide user functionalities.
|
||||||
|
@ -59,7 +61,8 @@ export class CoreUserProvider {
|
||||||
protected logger;
|
protected logger;
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
||||||
private filepoolProvider: CoreFilepoolProvider) {
|
private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider,
|
||||||
|
private userOffline: CoreUserOfflineProvider) {
|
||||||
this.logger = logger.getInstance('CoreUserProvider');
|
this.logger = logger.getInstance('CoreUserProvider');
|
||||||
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
this.sitesProvider.registerSiteSchema(this.siteSchema);
|
||||||
}
|
}
|
||||||
|
@ -272,6 +275,67 @@ export class CoreUserProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user preference (online or offline).
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||||
|
* @return {string} Preference value or null if preference not set.
|
||||||
|
*/
|
||||||
|
getUserPreference(name: string, siteId?: string): Promise<string> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
return this.userOffline.getPreference(name, siteId).catch(() => {
|
||||||
|
return null;
|
||||||
|
}).then((preference) => {
|
||||||
|
if (preference && !this.appProvider.isOnline()) {
|
||||||
|
// Offline, return stored value.
|
||||||
|
return preference.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getUserPreferenceOnline(name, siteId).then((wsValue) => {
|
||||||
|
if (preference && preference.value != preference.onlinevalue && preference.onlinevalue == wsValue) {
|
||||||
|
// Sync is pending for this preference, return stored value.
|
||||||
|
return preference.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.userOffline.setPreference(name, wsValue, wsValue).then(() => {
|
||||||
|
return wsValue;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for a user preference WS call.
|
||||||
|
*
|
||||||
|
* @param {string} name Preference name.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getUserPreferenceCacheKey(name: string): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'preference:' + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a user preference online.
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||||
|
* @return {string} Preference value or null if preference not set.
|
||||||
|
*/
|
||||||
|
getUserPreferenceOnline(name: string, siteId?: string): Promise<string> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = { name };
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getUserPreferenceCacheKey(data.name)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_user_get_user_preferences', data, preSets).then((result) => {
|
||||||
|
return result.preferences[0] ? result.preferences[0].value : null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates user WS calls.
|
* Invalidates user WS calls.
|
||||||
*
|
*
|
||||||
|
@ -298,6 +362,19 @@ export class CoreUserProvider {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate user preference.
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateUserPreference(name: string, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getUserPreferenceCacheKey(name));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if course participants is disabled in a certain site.
|
* Check if course participants is disabled in a certain site.
|
||||||
*
|
*
|
||||||
|
@ -455,6 +532,45 @@ export class CoreUserProvider {
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a user preference (online or offline).
|
||||||
|
*
|
||||||
|
* @param {string} name Name of the preference.
|
||||||
|
* @param {string} value Value of the preference.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved on success.
|
||||||
|
*/
|
||||||
|
setUserPreference(name: string, value: string, siteId?: string): Promise<any> {
|
||||||
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||||
|
|
||||||
|
let isOnline = this.appProvider.isOnline();
|
||||||
|
let promise: Promise<any>;
|
||||||
|
|
||||||
|
if (isOnline) {
|
||||||
|
const preferences = [{type: name, value}];
|
||||||
|
promise = this.updateUserPreferences(preferences, undefined, undefined, siteId).catch((error) => {
|
||||||
|
// Preference not saved online.
|
||||||
|
isOnline = false;
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return promise.finally(() => {
|
||||||
|
// Update stored online value if saved online.
|
||||||
|
const onlineValue = isOnline ? value : undefined;
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
this.userOffline.setPreference(name, value, onlineValue),
|
||||||
|
this.invalidateUserPreference(name).catch(() => {
|
||||||
|
// Ignore error.
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a preference for a user.
|
* Update a preference for a user.
|
||||||
*
|
*
|
||||||
|
@ -478,13 +594,14 @@ export class CoreUserProvider {
|
||||||
/**
|
/**
|
||||||
* Update some preferences for a user.
|
* Update some preferences for a user.
|
||||||
*
|
*
|
||||||
* @param {any} preferences List of preferences.
|
* @param {{name: string, value: string}[]} preferences List of preferences.
|
||||||
* @param {boolean} [disableNotifications] Whether to disable all notifications. Undefined to not update this value.
|
* @param {boolean} [disableNotifications] Whether to disable all notifications. Undefined to not update this value.
|
||||||
* @param {number} [userId] User ID. If not defined, site's current user.
|
* @param {number} [userId] User ID. If not defined, site's current user.
|
||||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
* @return {Promise<any>} Promise resolved if success.
|
* @return {Promise<any>} Promise resolved if success.
|
||||||
*/
|
*/
|
||||||
updateUserPreferences(preferences: any, disableNotifications: boolean, userId?: number, siteId?: string): Promise<any> {
|
updateUserPreferences(preferences: {type: string, value: string}[], disableNotifications?: boolean, userId?: number,
|
||||||
|
siteId?: string): Promise<any> {
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
userId = userId || site.getUserId();
|
userId = userId || site.getUserId();
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,10 @@ import { CoreUserParticipantsCourseOptionHandler } from './providers/course-opti
|
||||||
import { CoreUserParticipantsLinkHandler } from './providers/participants-link-handler';
|
import { CoreUserParticipantsLinkHandler } from './providers/participants-link-handler';
|
||||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||||
import { CoreUserComponentsModule } from './components/components.module';
|
import { CoreUserComponentsModule } from './components/components.module';
|
||||||
|
import { CoreCronDelegate } from '@providers/cron';
|
||||||
|
import { CoreUserOfflineProvider } from './providers/offline';
|
||||||
|
import { CoreUserSyncProvider } from './providers/sync';
|
||||||
|
import { CoreUserSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const CORE_USER_PROVIDERS: any[] = [
|
export const CORE_USER_PROVIDERS: any[] = [
|
||||||
|
@ -33,6 +37,8 @@ export const CORE_USER_PROVIDERS: any[] = [
|
||||||
CoreUserProfileFieldDelegate,
|
CoreUserProfileFieldDelegate,
|
||||||
CoreUserProvider,
|
CoreUserProvider,
|
||||||
CoreUserHelperProvider,
|
CoreUserHelperProvider,
|
||||||
|
CoreUserOfflineProvider,
|
||||||
|
CoreUserSyncProvider
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -46,10 +52,13 @@ export const CORE_USER_PROVIDERS: any[] = [
|
||||||
CoreUserProfileFieldDelegate,
|
CoreUserProfileFieldDelegate,
|
||||||
CoreUserProvider,
|
CoreUserProvider,
|
||||||
CoreUserHelperProvider,
|
CoreUserHelperProvider,
|
||||||
|
CoreUserOfflineProvider,
|
||||||
|
CoreUserSyncProvider,
|
||||||
CoreUserProfileMailHandler,
|
CoreUserProfileMailHandler,
|
||||||
CoreUserProfileLinkHandler,
|
CoreUserProfileLinkHandler,
|
||||||
CoreUserParticipantsCourseOptionHandler,
|
CoreUserParticipantsCourseOptionHandler,
|
||||||
CoreUserParticipantsLinkHandler
|
CoreUserParticipantsLinkHandler,
|
||||||
|
CoreUserSyncCronHandler,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreUserModule {
|
export class CoreUserModule {
|
||||||
|
@ -57,12 +66,14 @@ export class CoreUserModule {
|
||||||
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, userProvider: CoreUserProvider,
|
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider, userProvider: CoreUserProvider,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreUserProfileLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreUserProfileLinkHandler,
|
||||||
courseOptionHandler: CoreUserParticipantsCourseOptionHandler, linkHandler: CoreUserParticipantsLinkHandler,
|
courseOptionHandler: CoreUserParticipantsCourseOptionHandler, linkHandler: CoreUserParticipantsLinkHandler,
|
||||||
courseOptionsDelegate: CoreCourseOptionsDelegate) {
|
courseOptionsDelegate: CoreCourseOptionsDelegate, cronDelegate: CoreCronDelegate,
|
||||||
|
syncHandler: CoreUserSyncCronHandler) {
|
||||||
|
|
||||||
userDelegate.registerHandler(userProfileMailHandler);
|
userDelegate.registerHandler(userProfileMailHandler);
|
||||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||||
contentLinksDelegate.registerHandler(userLinkHandler);
|
contentLinksDelegate.registerHandler(userLinkHandler);
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
|
cronDelegate.register(syncHandler);
|
||||||
|
|
||||||
eventsProvider.on(CoreEventsProvider.USER_DELETED, (data) => {
|
eventsProvider.on(CoreEventsProvider.USER_DELETED, (data) => {
|
||||||
// Search for userid in params.
|
// Search for userid in params.
|
||||||
|
|
|
@ -223,6 +223,7 @@
|
||||||
"sizemb": "MB",
|
"sizemb": "MB",
|
||||||
"sizetb": "TB",
|
"sizetb": "TB",
|
||||||
"sorry": "Sorry...",
|
"sorry": "Sorry...",
|
||||||
|
"sort": "Sort",
|
||||||
"sortby": "Sort by",
|
"sortby": "Sort by",
|
||||||
"start": "Start",
|
"start": "Start",
|
||||||
"strftimedate": "%d %B %Y",
|
"strftimedate": "%d %B %Y",
|
||||||
|
|
Loading…
Reference in New Issue