MOBILE-2319 notes: Migrate notes
parent
2fffb47c2c
commit
d29712258a
|
@ -0,0 +1,41 @@
|
|||
// (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 { CommonModule } from '@angular/common';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { AddonNotesTypesComponent } from './types/types';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonNotesTypesComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
AddonNotesTypesComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonNotesTypesComponent
|
||||
]
|
||||
})
|
||||
export class AddonNotesComponentsModule {}
|
|
@ -0,0 +1,15 @@
|
|||
<core-split-view>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<a ion-item (click)="openList('site')" [title]="'addon.notes.sitenotes' | translate" [class.core-split-item-selected]="type == 'site'">
|
||||
{{ 'addon.notes.sitenotes' | translate }}
|
||||
</a>
|
||||
<a ion-item (click)="openList('course')" [title]="'addon.notes.coursenotes' | translate" [class.core-split-item-selected]="type == 'course'">
|
||||
{{ 'addon.notes.coursenotes' | translate }}
|
||||
</a>
|
||||
<a ion-item (click)="openList('personal')" [title]="'addon.notes.personalnotes' | translate" [class.core-split-item-selected]="type == 'personal'">
|
||||
{{ 'addon.notes.personalnotes' | translate }}
|
||||
</a>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</core-split-view>
|
|
@ -0,0 +1,62 @@
|
|||
// (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 { AfterViewInit, Component, Input, OnInit, ViewChild } from '@angular/core';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
|
||||
/**
|
||||
* Component that displays the competencies of a course.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-notes-types',
|
||||
templateUrl: 'types.html',
|
||||
})
|
||||
export class AddonNotesTypesComponent implements AfterViewInit, OnInit {
|
||||
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||
|
||||
@Input() courseId: number;
|
||||
|
||||
protected type: string;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
const siteHomeId = this.sitesProvider.getCurrentSite().getSiteHomeId();
|
||||
this.type = (this.courseId == siteHomeId ? 'site' : 'course');
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ngAfterViewInit(): void {
|
||||
if (this.splitviewCtrl.isOn()) {
|
||||
this.openList(this.type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a list of notes.
|
||||
*
|
||||
* @param {string} type
|
||||
*/
|
||||
openList(type: string): void {
|
||||
this.type = type;
|
||||
this.splitviewCtrl.push('AddonNotesListPage', {courseId: this.courseId, type: type});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"addnewnote": "Add a new note",
|
||||
"coursenotes": "Course notes",
|
||||
"eventnotecreated": "Note created",
|
||||
"nonotes": "There are no notes of this type yet.",
|
||||
"note": "Note",
|
||||
"notes": "Notes",
|
||||
"personalnotes": "Personal notes",
|
||||
"publishstate": "Context",
|
||||
"sitenotes": "Site notes",
|
||||
"userwithid": "User with ID {{id}}",
|
||||
"warningnotenotsent": "Couldn't add note(s) to course {{course}}. {{error}}"
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
// (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 { AddonNotesProvider } from './providers/notes';
|
||||
import { AddonNotesOfflineProvider } from './providers/notes-offline';
|
||||
import { AddonNotesSyncProvider } from './providers/notes-sync';
|
||||
import { AddonNotesCourseOptionHandler } from './providers/course-option-handler';
|
||||
import { AddonNotesSyncCronHandler } from './providers/sync-cron-handler';
|
||||
import { AddonNotesUserHandler } from './providers/user-handler';
|
||||
import { AddonNotesComponentsModule } from './components/components.module';
|
||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreCronDelegate } from '@providers/cron';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
imports: [
|
||||
AddonNotesComponentsModule
|
||||
],
|
||||
providers: [
|
||||
AddonNotesProvider,
|
||||
AddonNotesOfflineProvider,
|
||||
AddonNotesSyncProvider,
|
||||
AddonNotesCourseOptionHandler,
|
||||
AddonNotesSyncCronHandler,
|
||||
AddonNotesUserHandler
|
||||
]
|
||||
})
|
||||
export class AddonNotesModule {
|
||||
constructor(courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonNotesCourseOptionHandler,
|
||||
userDelegate: CoreUserDelegate, userHandler: AddonNotesUserHandler, cronDelegate: CoreCronDelegate,
|
||||
syncHandler: AddonNotesSyncCronHandler, eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
|
||||
// Register handlers.
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
cronDelegate.register(syncHandler);
|
||||
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
|
||||
courseOptionHandler.clearCoursesNavCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
|
||||
eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED, () => {
|
||||
courseOptionHandler.clearCoursesNavCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, () => {
|
||||
userHandler.clearAddNoteCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'addon.notes.addnewnote' | 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 padding>
|
||||
<form name="itemEdit" (ngSubmit)="addNote()">
|
||||
<ion-item>
|
||||
<ion-label>{{ 'addon.notes.publishstate' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="publishState" name="publishState">
|
||||
<ion-option value="personal">{{ 'addon.notes.personalnotes' | translate }}</ion-option>
|
||||
<ion-option value="course">{{ 'addon.notes.coursenotes' | translate }}</ion-option>
|
||||
<ion-option value="site">{{ 'addon.notes.sitenotes' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-textarea placeholder="{{ 'addon.notes.note' | translate }}" rows="5" [(ngModel)]="text" name="text" required="required"></ion-textarea>
|
||||
</ion-item>
|
||||
<button ion-button full margin-vertical type="submit" [disabled]="processing || text.length < 2">
|
||||
{{ 'addon.notes.addnewnote' | translate }}
|
||||
</button>
|
||||
</form>
|
||||
</ion-content>
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { AddonNotesAddPage } from './add';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonNotesAddPage
|
||||
],
|
||||
imports: [
|
||||
CoreDirectivesModule,
|
||||
IonicPageModule.forChild(AddonNotesAddPage),
|
||||
TranslateModule.forChild()
|
||||
]
|
||||
})
|
||||
export class AddonNotesAddPageModule {}
|
|
@ -0,0 +1,69 @@
|
|||
// (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, ViewController, NavParams } from 'ionic-angular';
|
||||
import { CoreAppProvider } from '@providers/app';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { AddonNotesProvider } from '../../providers/notes';
|
||||
|
||||
/**
|
||||
* Component that displays a text area for composing a note.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-notes-add' })
|
||||
@Component({
|
||||
selector: 'page-addon-notes-add',
|
||||
templateUrl: 'add.html',
|
||||
})
|
||||
export class AddonNotesAddPage {
|
||||
userId: number;
|
||||
courseId: number;
|
||||
publishState = 'personal';
|
||||
text = '';
|
||||
processing = false;
|
||||
|
||||
constructor(params: NavParams, private viewCtrl: ViewController, private appProvider: CoreAppProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private notesProvider: AddonNotesProvider) {
|
||||
this.userId = params.get('userId');
|
||||
this.courseId = params.get('courseId');
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the note or store it offline.
|
||||
*/
|
||||
addNote(): void {
|
||||
this.appProvider.closeKeyboard();
|
||||
const loadingModal = this.domUtils.showModalLoading('core.sending', true);
|
||||
// Freeze the add note button.
|
||||
this.processing = true;
|
||||
this.notesProvider.addNote(this.userId, this.courseId, this.publishState, this.text).then((sent) => {
|
||||
this.viewCtrl.dismiss().finally(() => {
|
||||
const message = sent ? 'addon.notes.eventnotecreated' : 'core.datastoredoffline';
|
||||
this.domUtils.showAlertTranslated('core.success', message);
|
||||
});
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModal(error);
|
||||
this.processing = false;
|
||||
}).finally(() => {
|
||||
loadingModal.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Close modal.
|
||||
*/
|
||||
closeModal(): void {
|
||||
this.viewCtrl.dismiss();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'addon.notes.notes' | translate }}</ion-title>
|
||||
<ion-buttons end></ion-buttons>
|
||||
</ion-navbar>
|
||||
<core-navbar-buttons end>
|
||||
<core-context-menu>
|
||||
<core-context-menu-item [hidden]="!(notesLoaded && !hasOffline)" [priority]="100" [content]="'core.refresh' | translate" (action)="refreshNotes(false)" [iconAction]="refreshIcon" [closeOnClick]="true"></core-context-menu-item>
|
||||
<core-context-menu-item [hidden]="!(notesLoaded && hasOffline)" [priority]="100" [content]="'core.settings.synchronizenow' | translate" (action)="refreshNotes(true)" [iconAction]="syncIcon" [closeOnClick]="false"></core-context-menu-item>
|
||||
</core-context-menu>
|
||||
</core-navbar-buttons>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="notesLoaded" (ionRefresh)="refreshNotes(false, $event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="notesLoaded" class="core-loading-center">
|
||||
|
||||
<p class="core-warning-card" *ngIf="hasOffline">
|
||||
<ion-icon name="alert" color="warning" padding-right></ion-icon> {{ 'core.thereisdatatosync' | translate:{$a: 'addon.notes.notes' | translate | lowercase } }}
|
||||
</p>
|
||||
|
||||
<core-empty-box *ngIf="notes && notes.length == 0" icon="list" [message]="'addon.notes.nonotes' | translate"></core-empty-box>
|
||||
|
||||
<ion-list *ngIf="notes && notes.length > 0">
|
||||
<ion-card *ngFor="let note of notes">
|
||||
<ion-item text-wrap>
|
||||
<ion-avatar item-start>
|
||||
<img [src]="note.userprofileimageurl || 'assets/img/user-avatar.png'" core-external-content (click)="openUserProfile(note.userid)" [alt]="'core.pictureof' | translate:{$a: note.userfullname}" role="presentation">
|
||||
</ion-avatar>
|
||||
<h2>{{note.userfullname}}</h2>
|
||||
<p *ngIf="!note.offline" item-end>{{note.lastmodified | coreDateDayOrTime}}</p>
|
||||
<p *ngIf="note.offline" item-end><ion-icon name="clock"></ion-icon> {{ 'core.notsent' | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<core-format-text [clean]="true" [text]="note.content"></core-format-text>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,35 @@
|
|||
// (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 { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { AddonNotesListPage } from './list';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonNotesListPage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule,
|
||||
IonicPageModule.forChild(AddonNotesListPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class AddonNotesListPageModule {}
|
|
@ -0,0 +1,173 @@
|
|||
// (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, OnDestroy, Optional, ViewChild } from '@angular/core';
|
||||
import { Content, IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { AddonNotesProvider } from '../../providers/notes';
|
||||
import { AddonNotesSyncProvider } from '../../providers/notes-sync';
|
||||
|
||||
/**
|
||||
* Page that displays the list of notes.
|
||||
*/
|
||||
@IonicPage({ segment: 'addon-notes-list' })
|
||||
@Component({
|
||||
selector: 'page-addon-notes-list',
|
||||
templateUrl: 'list.html',
|
||||
})
|
||||
export class AddonNotesListPage implements OnDestroy {
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
protected courseId = 0;
|
||||
protected syncObserver: any;
|
||||
|
||||
type = '';
|
||||
refreshIcon = 'spinner';
|
||||
syncIcon = 'spinner';
|
||||
notes: any[];
|
||||
hasOffline = false;
|
||||
notesLoaded = false;
|
||||
|
||||
constructor(navParams: NavParams, private navCtrl: NavController, @Optional() private svComponent: CoreSplitViewComponent,
|
||||
private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
||||
sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
||||
private notesProvider: AddonNotesProvider, private notesSync: AddonNotesSyncProvider) {
|
||||
this.courseId = navParams.get('courseId') || sitesProvider.getCurrentSiteHomeId();
|
||||
this.type = navParams.get('type');
|
||||
// Refresh data if notes are synchronized automatically.
|
||||
this.syncObserver = eventsProvider.on(AddonNotesSyncProvider.AUTO_SYNCED, (data) => {
|
||||
if (data.courseId == this.courseId) {
|
||||
// Show the sync warnings.
|
||||
this.showSyncWarnings(data.warnings);
|
||||
|
||||
// Refresh the data.
|
||||
this.notesLoaded = false;
|
||||
this.refreshIcon = 'spinner';
|
||||
this.syncIcon = 'spinner';
|
||||
|
||||
this.content.scrollToTop();
|
||||
this.fetchNotes(false);
|
||||
}
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad(): void {
|
||||
this.fetchNotes(true).then(() => {
|
||||
this.notesProvider.logView(this.courseId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch notes
|
||||
* @param {boolean} sync When to resync notes.
|
||||
* @param {boolean} [showErrors] When to display errors or not.
|
||||
* @return {Promise<any>} Promise with the notes,
|
||||
*/
|
||||
private fetchNotes(sync: boolean, showErrors?: boolean): Promise<any> {
|
||||
const promise = sync ? this.syncNotes(showErrors) : Promise.resolve();
|
||||
|
||||
return promise.catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
return this.notesProvider.getNotes(this.courseId).then((notes) => {
|
||||
notes = notes[this.type + 'notes'] || [];
|
||||
|
||||
this.hasOffline = this.notesProvider.hasOfflineNote(notes);
|
||||
|
||||
return this.notesProvider.getNotesUserData(notes, this.courseId).then((notes) => {
|
||||
this.notes = notes;
|
||||
});
|
||||
});
|
||||
}).catch((message) => {
|
||||
this.domUtils.showErrorModal(message);
|
||||
}).finally(() => {
|
||||
this.notesLoaded = true;
|
||||
this.refreshIcon = 'refresh';
|
||||
this.syncIcon = 'loop';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh notes on PTR.
|
||||
*
|
||||
* @param {boolean} showErrors Whether to display errors or not.
|
||||
* @param {any} refresher Refresher instance.
|
||||
*/
|
||||
refreshNotes(showErrors: boolean, refresher?: any): void {
|
||||
this.refreshIcon = 'spinner';
|
||||
this.syncIcon = 'spinner';
|
||||
this.notesProvider.invalidateNotes(this.courseId).finally(() => {
|
||||
this.fetchNotes(true, showErrors).finally(() => {
|
||||
if (refresher) {
|
||||
refresher.complete();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to syncrhonize course notes.
|
||||
*
|
||||
* @param {boolean} showErrors Whether to display errors or not.
|
||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
||||
*/
|
||||
private syncNotes(showErrors: boolean): Promise<any> {
|
||||
return this.notesSync.syncNotes(this.courseId).then((warnings) => {
|
||||
this.showSyncWarnings(warnings);
|
||||
}).catch((error) => {
|
||||
if (showErrors) {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.errorsync', true);
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show sync warnings if any.
|
||||
*
|
||||
* @param {string[]} warnings the warnings
|
||||
*/
|
||||
private showSyncWarnings(warnings: string[]): void {
|
||||
const message = this.textUtils.buildMessage(warnings);
|
||||
if (message) {
|
||||
this.domUtils.showErrorModal(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the profile of a user.
|
||||
*
|
||||
* @param {number} userId
|
||||
*/
|
||||
openUserProfile(userId: number): void {
|
||||
// Decide which navCtrl to use. If this page is inside a split view, use the split view's master nav.
|
||||
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||
navCtrl.push('CoreUserProfilePage', {userId, courseId: this.courseId});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.syncObserver && this.syncObserver.off();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
// (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, Injector } from '@angular/core';
|
||||
import { AddonNotesProvider } from './notes';
|
||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||
import { CoreCourseOptionsHandler, CoreCourseOptionsHandlerData } from '@core/course/providers/options-delegate';
|
||||
import { AddonNotesTypesComponent } from '../components/types/types';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into the course main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'AddonNotes';
|
||||
priority = 200;
|
||||
protected coursesNavEnabledCache = {};
|
||||
|
||||
constructor(private notesProvider: AddonNotesProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear courses nav cache.
|
||||
*/
|
||||
clearCoursesNavCache(): void {
|
||||
this.coursesNavEnabledCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.notesProvider.isPluginViewNotesEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {any} accessData Access type and data. Default, guest, ...
|
||||
* @param {any} [navOptions] Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
}
|
||||
|
||||
if (navOptions && typeof navOptions.notes != 'undefined') {
|
||||
return navOptions.notes;
|
||||
}
|
||||
|
||||
if (typeof this.coursesNavEnabledCache[courseId] != 'undefined') {
|
||||
return this.coursesNavEnabledCache[courseId];
|
||||
}
|
||||
|
||||
return this.notesProvider.isPluginViewNotesEnabledForCourse(courseId).then((enabled) => {
|
||||
this.coursesNavEnabledCache[courseId] = enabled;
|
||||
|
||||
return enabled;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @return {CoreCourseOptionsHandlerData} Data.
|
||||
*/
|
||||
getDisplayData?(injector: Injector, courseId: number): CoreCourseOptionsHandlerData {
|
||||
return {
|
||||
title: 'addon.notes.notes',
|
||||
class: 'addon-notes-course-handler',
|
||||
component: AddonNotesTypesComponent,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
// (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 { CoreTimeUtilsProvider } from '@providers/utils/time';
|
||||
|
||||
/**
|
||||
* Service to handle offline notes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesOfflineProvider {
|
||||
protected logger;
|
||||
|
||||
// Variables for database.
|
||||
protected NOTES_TABLE = 'addon_notes_offline_notes';
|
||||
protected tablesSchema = [
|
||||
{
|
||||
name: this.NOTES_TABLE,
|
||||
columns: [
|
||||
{
|
||||
name: 'userid',
|
||||
type: 'INTEGER'
|
||||
},
|
||||
{
|
||||
name: 'courseid',
|
||||
type: 'INTEGER'
|
||||
},
|
||||
{
|
||||
name: 'publishstate',
|
||||
type: 'TEXT',
|
||||
},
|
||||
{
|
||||
name: 'content',
|
||||
type: 'TEXT'
|
||||
},
|
||||
{
|
||||
name: 'format',
|
||||
type: 'INTEGER'
|
||||
},
|
||||
{
|
||||
name: 'created',
|
||||
type: 'INTEGER'
|
||||
},
|
||||
{
|
||||
name: 'lastmodified',
|
||||
type: 'INTEGER'
|
||||
}
|
||||
],
|
||||
primaryKeys: ['userid', 'content', 'created']
|
||||
}
|
||||
];
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider) {
|
||||
this.logger = logger.getInstance('AddonNotesOfflineProvider');
|
||||
this.sitesProvider.createTablesFromSchema(this.tablesSchema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a note.
|
||||
*
|
||||
* @param {number} userId User ID the note is about.
|
||||
* @param {string} content The note content.
|
||||
* @param {number} timecreated The time the note was created.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if deleted, rejected if failure.
|
||||
*/
|
||||
deleteNote(userId: number, content: string, timecreated: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().deleteRecords(this.NOTES_TABLE, {
|
||||
userid: userId,
|
||||
content: content,
|
||||
created: timecreated
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all offline notes.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with notes.
|
||||
*/
|
||||
getAllNotes(siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(this.NOTES_TABLE);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an offline note.
|
||||
*
|
||||
* @param {number} userId User ID the note is about.
|
||||
* @param {string} content The note content.
|
||||
* @param {number} timecreated The time the note was created.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved with the notes.
|
||||
*/
|
||||
getNote(userId: number, content: string, timecreated: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecord(this.NOTES_TABLE, {
|
||||
userid: userId,
|
||||
content: content,
|
||||
created: timecreated
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offline notes for a certain course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with notes.
|
||||
*/
|
||||
getNotesForCourse(courseId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(this.NOTES_TABLE, {courseid: courseId});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offline notes for a certain user.
|
||||
*
|
||||
* @param {number} userId User ID the notes are about.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with notes.
|
||||
*/
|
||||
getNotesForUser(userId: number, siteId?: string): Promise<any[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(this.NOTES_TABLE, {userid: userId});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get offline notes with a certain publish state (Personal, Site or Course).
|
||||
*
|
||||
* @param {string} state Publish state ('personal', 'site' or 'course').
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any[]>} Promise resolved with notes.
|
||||
*/
|
||||
getNotesWithPublishState(state: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.getDb().getRecords(this.NOTES_TABLE, {publishstate: state});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are offline notes for a certain course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: true if has offline notes, false otherwise.
|
||||
*/
|
||||
hasNotesForCourse(courseId: number, siteId?: string): Promise<boolean> {
|
||||
return this.getNotesForCourse(courseId, siteId).then((notes) => {
|
||||
return !!notes.length;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are offline notes for a certain user.
|
||||
*
|
||||
* @param {number} userId User ID the notes are about.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: true if has offline notes, false otherwise.
|
||||
*/
|
||||
hasNotesForUser(userId: number, siteId?: string): Promise<boolean> {
|
||||
return this.getNotesForUser(userId, siteId).then((notes) => {
|
||||
return !!notes.length;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are offline notes with a certain publish state (Personal, Site or Course).
|
||||
*
|
||||
* @param {string} state Publish state ('personal', 'site' or 'course').
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: true if has offline notes, false otherwise.
|
||||
*/
|
||||
hasNotesWithPublishState(state: string, siteId?: string): Promise<boolean> {
|
||||
return this.getNotesWithPublishState(state, siteId).then((notes) => {
|
||||
return !!notes.length;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a note to be sent later.
|
||||
*
|
||||
* @param {number} userId User ID the note is about.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} state Publish state ('personal', 'site' or 'course').
|
||||
* @param {string} content The note content.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if stored, rejected if failure.
|
||||
*/
|
||||
saveNote(userId: number, courseId: number, state: string, content: string, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const now = this.timeUtils.timestamp();
|
||||
const data = {
|
||||
userid: userId,
|
||||
courseid: courseId,
|
||||
publishstate: state,
|
||||
content: content,
|
||||
format: 1,
|
||||
created: now,
|
||||
lastmodified: now
|
||||
};
|
||||
const conditions = {
|
||||
userid: userId,
|
||||
content: content,
|
||||
created: now
|
||||
};
|
||||
|
||||
return site.getDb().insertOrUpdateRecord(this.NOTES_TABLE, data, conditions).then(() => {
|
||||
return data;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
// (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 { AddonNotesOfflineProvider } from './notes-offline';
|
||||
import { AddonNotesProvider } from './notes';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSyncProvider } from '@providers/sync';
|
||||
|
||||
/**
|
||||
* Service to sync notes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesSyncProvider extends CoreSyncBaseProvider {
|
||||
|
||||
static AUTO_SYNCED = 'addon_notes_autom_synced';
|
||||
|
||||
constructor(protected sitesProvider: CoreSitesProvider, protected loggerProvider: CoreLoggerProvider,
|
||||
protected appProvider: CoreAppProvider, private notesOffline: AddonNotesOfflineProvider,
|
||||
private eventsProvider: CoreEventsProvider, private notesProvider: AddonNotesProvider,
|
||||
private coursesProvider: CoreCoursesProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||
syncProvider: CoreSyncProvider, protected textUtils: CoreTextUtilsProvider) {
|
||||
super('AddonNotesSync', sitesProvider, loggerProvider, appProvider, syncProvider, textUtils);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to synchronize all the notes 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.
|
||||
*/
|
||||
syncAllNotes(siteId?: string): Promise<any> {
|
||||
return this.syncOnSites('all notes', this.syncAllNotesFunc.bind(this), [], siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize all the notes in a certain site
|
||||
*
|
||||
* @param {string} siteId Site ID to sync.
|
||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected if sync fails.
|
||||
*/
|
||||
private syncAllNotesFunc(siteId: string): Promise<any> {
|
||||
return this.notesOffline.getAllNotes(siteId).then((notes) => {
|
||||
// Get all the courses to be synced.
|
||||
const courseIds = [];
|
||||
notes.forEach((note) => {
|
||||
if (courseIds.indexOf(note.courseid) == -1) {
|
||||
courseIds.push(note.courseid);
|
||||
}
|
||||
});
|
||||
|
||||
// Sync all courses.
|
||||
const promises = courseIds.map((courseId) => {
|
||||
return this.syncNotesIfNeeded(courseId, siteId).then((warnings) => {
|
||||
if (typeof warnings != 'undefined') {
|
||||
// Sync successful, send event.
|
||||
this.eventsProvider.trigger(AddonNotesSyncProvider.AUTO_SYNCED, {
|
||||
courseId: courseId,
|
||||
warnings: warnings
|
||||
}, siteId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync course notes only if a certain time has passed since the last time.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the notes are synced or if they don't need to be synced.
|
||||
*/
|
||||
private syncNotesIfNeeded(courseId: number, siteId?: string): Promise<void> {
|
||||
return this.isSyncNeeded(courseId, siteId).then((needed) => {
|
||||
if (needed) {
|
||||
return this.syncNotes(courseId, siteId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize notes of a course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved if sync is successful, rejected otherwise.
|
||||
*/
|
||||
syncNotes(courseId: number, siteId?: string): Promise<any> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
if (this.isSyncing(courseId, siteId)) {
|
||||
// There's already a sync ongoing for notes, return the promise.
|
||||
return this.getOngoingSync(courseId, siteId);
|
||||
}
|
||||
|
||||
this.logger.debug('Try to sync notes for course ' + courseId);
|
||||
|
||||
const warnings = [];
|
||||
|
||||
// Get offline notes to be sent.
|
||||
const syncPromise = this.notesOffline.getNotesForCourse(courseId, siteId).then((notes) => {
|
||||
if (!notes.length) {
|
||||
// Nothing to sync.
|
||||
return;
|
||||
} else if (!this.appProvider.isOnline()) {
|
||||
// Cannot sync in offline.
|
||||
return Promise.reject(this.translate.instant('core.networkerrormsg'));
|
||||
}
|
||||
|
||||
const errors = [];
|
||||
|
||||
// Format the notes to be sent.
|
||||
const notesToSend = notes.map((note) => {
|
||||
return {
|
||||
userid: note.userid,
|
||||
publishstate: note.publishstate,
|
||||
courseid: note.courseid,
|
||||
text: note.content,
|
||||
format: note.format
|
||||
};
|
||||
});
|
||||
|
||||
// Send the notes.
|
||||
return this.notesProvider.addNotesOnline(notesToSend, siteId).then((response) => {
|
||||
// Search errors in the response.
|
||||
response.forEach((entry) => {
|
||||
if (entry.noteid === -1 && errors.indexOf(entry.errormessage) == -1) {
|
||||
errors.push(entry.errormessage);
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch the notes from server to be sure they're up to date.
|
||||
return this.notesProvider.invalidateNotes(courseId, siteId).then(() => {
|
||||
return this.notesProvider.getNotes(courseId, false, true, siteId);
|
||||
}).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
}).catch((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// It's a WebService error, this means the user cannot send notes.
|
||||
errors.push(error);
|
||||
} else {
|
||||
// Not a WebService error, reject the synchronization to try again.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
}).then(() => {
|
||||
// Notes were sent, delete them from local DB.
|
||||
const promises = notes.map((note) => {
|
||||
return this.notesOffline.deleteNote(note.userid, note.content, note.created, siteId);
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
}).then(() => {
|
||||
if (errors && errors.length) {
|
||||
// At least an error occurred, get course name and add errors to warnings array.
|
||||
return this.coursesProvider.getUserCourse(courseId, true, siteId).catch(() => {
|
||||
// Ignore errors.
|
||||
return {};
|
||||
}).then((course) => {
|
||||
errors.forEach((error) => {
|
||||
warnings.push(this.translate.instant('addon.notes.warningnotenotsent', {
|
||||
course: course.fullname ? course.fullname : courseId,
|
||||
error: error
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}).then(() => {
|
||||
// All done, return the warnings.
|
||||
return warnings;
|
||||
});
|
||||
|
||||
return this.addOngoingSync(courseId, syncPromise, siteId);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
// (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 { CoreAppProvider } from '@providers/app';
|
||||
import { CoreLoggerProvider } from '@providers/logger';
|
||||
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreSiteWSPreSets } from '@classes/site';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonNotesOfflineProvider } from './notes-offline';
|
||||
|
||||
/**
|
||||
* Service to handle notes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesProvider {
|
||||
|
||||
protected ROOT_CACHE_KEY = 'mmaNotes:';
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private appProvider: CoreAppProvider,
|
||||
private utilsProvider: CoreUtilsProvider, private translate: TranslateService, private userProvider: CoreUserProvider,
|
||||
private notesOffline: AddonNotesOfflineProvider) {
|
||||
this.logger = logger.getInstance('AddonNotesProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a note.
|
||||
*
|
||||
* @param {number} userId User ID of the person to add the note.
|
||||
* @param {number} courseId Course ID where the note belongs.
|
||||
* @param {string} publishState Personal, Site or Course.
|
||||
* @param {string} noteText The note text.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: true if note was sent to server, false if stored in device.
|
||||
*/
|
||||
addNote(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise<boolean> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
// Convenience function to store a note to be synchronized later.
|
||||
const storeOffline = (): Promise<any> => {
|
||||
return this.notesOffline.saveNote(userId, courseId, publishState, noteText, siteId).then(() => {
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
if (!this.appProvider.isOnline()) {
|
||||
// App is offline, store the note.
|
||||
return storeOffline();
|
||||
}
|
||||
|
||||
// Send note to server.
|
||||
return this.addNoteOnline(userId, courseId, publishState, noteText, siteId).then(() => {
|
||||
return true;
|
||||
}).catch((data) => {
|
||||
if (data.wserror) {
|
||||
// It's a WebService error, the user cannot add the note so don't store it.
|
||||
return Promise.reject(data.error);
|
||||
} else {
|
||||
// Error sending note, store it to retry later.
|
||||
return storeOffline();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a note. It will fail if offline or cannot connect.
|
||||
*
|
||||
* @param {number} userId User ID of the person to add the note.
|
||||
* @param {number} courseId Course ID where the note belongs.
|
||||
* @param {string} publishState Personal, Site or Course.
|
||||
* @param {string} noteText The note text.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when added, rejected otherwise. Reject param is an object with:
|
||||
* - error: The error message.
|
||||
* - wserror: True if it's an error returned by the WebService, false otherwise.
|
||||
*/
|
||||
addNoteOnline(userId: number, courseId: number, publishState: string, noteText: string, siteId?: string): Promise<any> {
|
||||
const notes = [
|
||||
{
|
||||
courseid: courseId,
|
||||
format: 1,
|
||||
publishstate: publishState,
|
||||
text: noteText,
|
||||
userid: userId
|
||||
}
|
||||
];
|
||||
|
||||
return this.addNotesOnline(notes, siteId).catch((error) => {
|
||||
return Promise.reject({
|
||||
error: error,
|
||||
wserror: this.utilsProvider.isWebServiceError(error)
|
||||
});
|
||||
}).then((response) => {
|
||||
if (response && response[0] && response[0].noteid === -1) {
|
||||
// There was an error, and it should be translated already.
|
||||
return Promise.reject({
|
||||
error: response[0].errormessage,
|
||||
wserror: true
|
||||
});
|
||||
}
|
||||
|
||||
// A note was added, invalidate the course notes.
|
||||
return this.invalidateNotes(courseId, siteId).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add several notes. It will fail if offline or cannot connect.
|
||||
*
|
||||
* @param {any[]} notes Notes to save.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when added, rejected otherwise. Promise resolved doesn't mean that notes
|
||||
* have been added, the resolve param can contain errors for notes not sent.
|
||||
*/
|
||||
addNotesOnline(notes: any[], siteId?: string): Promise<any> {
|
||||
if (!notes || !notes.length) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const data = {
|
||||
notes: notes
|
||||
};
|
||||
|
||||
return site.write('core_notes_create_notes', data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the add note plugin is enabled for a certain site.
|
||||
*
|
||||
* This method is called quite often and thus should only perform a quick
|
||||
* check, we should not be calling WS from here.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false or rejected otherwise.
|
||||
*/
|
||||
isPluginAddNoteEnabled(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!site.canUseAdvancedFeature('enablenotes')) {
|
||||
return false;
|
||||
} else if (!site.wsAvailable('core_notes_create_notes')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the add note plugin is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId ID of the course.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false or rejected otherwise.
|
||||
*/
|
||||
isPluginAddNoteEnabledForCourse(courseId: number, siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
// The only way to detect if it's enabled is to perform a WS call.
|
||||
// We use an invalid user ID (-1) to avoid saving the note if the user has permissions.
|
||||
const data = {
|
||||
notes: [
|
||||
{
|
||||
userid: -1,
|
||||
publishstate: 'personal',
|
||||
courseid: courseId,
|
||||
text: '',
|
||||
format: 1
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
/* Use .read to cache data and be able to check it in offline. This means that, if a user loses the capabilities
|
||||
to add notes, he'll still see the option in the app. */
|
||||
return site.read('core_notes_create_notes', data).then(() => {
|
||||
// User can add notes.
|
||||
return true;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the read notes plugin is enabled for the current site.
|
||||
*
|
||||
* This method is called quite often and thus should only perform a quick
|
||||
* check, we should not be calling WS from here.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false or rejected otherwise.
|
||||
*/
|
||||
isPluginViewNotesEnabled(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!site.canUseAdvancedFeature('enablenotes')) {
|
||||
return false;
|
||||
} else if (!site.wsAvailable('core_notes_get_course_notes')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the read notes plugin is enabled for a certain course.
|
||||
*
|
||||
* @param {number} courseId ID of the course.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false or rejected otherwise.
|
||||
*/
|
||||
isPluginViewNotesEnabledForCourse(courseId: number, siteId?: string): Promise<boolean> {
|
||||
return this.getNotes(courseId, false, true, siteId).then(() => {
|
||||
return true;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cache key for the get notes call.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the notes from.
|
||||
* @return {string} Cache key.
|
||||
*/
|
||||
getNotesCacheKey(courseId: number): string {
|
||||
return this.ROOT_CACHE_KEY + 'notes:' + courseId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users notes for a certain site, course and personal notes.
|
||||
*
|
||||
* @param {number} courseId ID of the course to get the notes from.
|
||||
* @param {boolean} [ignoreCache] True when we should not get the value from the cache.
|
||||
* @param {boolean} [onlyOnline] True to return only online notes, false to return both online and offline.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise to be resolved when the notes are retrieved.
|
||||
*/
|
||||
getNotes(courseId: number, ignoreCache?: boolean, onlyOnline?: boolean, siteId?: string): Promise<any> {
|
||||
this.logger.debug('Get notes for course ' + courseId);
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const data = {
|
||||
courseid: courseId
|
||||
};
|
||||
const preSets: CoreSiteWSPreSets = {
|
||||
cacheKey: this.getNotesCacheKey(courseId)
|
||||
};
|
||||
|
||||
if (ignoreCache) {
|
||||
preSets.getFromCache = false;
|
||||
preSets.emergencyCache = false;
|
||||
}
|
||||
|
||||
return site.read('core_notes_get_course_notes', data, preSets).then((notes) => {
|
||||
if (onlyOnline) {
|
||||
return notes;
|
||||
}
|
||||
|
||||
// Get offline notes and add them to the list.
|
||||
return this.notesOffline.getNotesForCourse(courseId, siteId).then((offlineNotes) => {
|
||||
offlineNotes.forEach((note) => {
|
||||
const fieldName = note.publishstate + 'notes';
|
||||
if (!notes[fieldName]) {
|
||||
notes[fieldName] = [];
|
||||
}
|
||||
note.offline = true;
|
||||
// Add note to the start of array since last notes are shown first.
|
||||
notes[fieldName].unshift(note);
|
||||
});
|
||||
|
||||
return notes;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user data for notes since they only have userid.
|
||||
*
|
||||
* @param {any[]} notes Notes to get the data for.
|
||||
* @param {number} courseId ID of the course the notes belong to.
|
||||
* @return {Promise<any>} Promise always resolved. Resolve param is the formatted notes.
|
||||
*/
|
||||
getNotesUserData(notes: any[], courseId: number): Promise<any> {
|
||||
const promises = notes.map((note) => {
|
||||
// Get the user profile to retrieve the user image.
|
||||
return this.userProvider.getProfile(note.userid, note.courseid, true).then((user) => {
|
||||
note.userfullname = user.fullname;
|
||||
note.userprofileimageurl = user.profileimageurl || null;
|
||||
}).catch(() => {
|
||||
note.userfullname = this.translate.instant('addon.notes.userwithid', {id: note.userid});
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => {
|
||||
return notes;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a list of notes, check if any of them is an offline note.
|
||||
*
|
||||
* @param {any[]} notes List of notes.
|
||||
* @return {boolean} True if at least 1 note is offline, false otherwise.
|
||||
*/
|
||||
hasOfflineNote(notes: any[]): boolean {
|
||||
if (!notes || !notes.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0, len = notes.length; i < len; i++) {
|
||||
if (notes[i].offline) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate get notes WS call.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when data is invalidated.
|
||||
*/
|
||||
invalidateNotes(courseId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.invalidateWsCacheForKey(this.getNotesCacheKey(courseId));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Report notes as being viewed.
|
||||
*
|
||||
* @param {number} courseId ID of the course.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when the WS call is successful.
|
||||
*/
|
||||
logView(courseId: number, siteId?: string): Promise<any> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
const params = {
|
||||
courseid: courseId,
|
||||
userid: 0
|
||||
};
|
||||
|
||||
return site.write('core_notes_view_notes', params);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// (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 { AddonNotesSyncProvider } from './notes-sync';
|
||||
|
||||
/**
|
||||
* Synchronization cron handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesSyncCronHandler implements CoreCronHandler {
|
||||
name = 'AddonNotesSyncCronHandler';
|
||||
|
||||
constructor(private notesSync: AddonNotesSyncProvider) {}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return {Promise<any>} Promise resolved when done, rejected if failure.
|
||||
*/
|
||||
execute(siteId?: string): Promise<any> {
|
||||
return this.notesSync.syncAllNotes(siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the time between consecutive executions.
|
||||
*
|
||||
* @return {number} Time between consecutive executions (in ms).
|
||||
*/
|
||||
getInterval(): number {
|
||||
return 600000; // 10 minutes.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// (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 { ModalController } from 'ionic-angular';
|
||||
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { AddonNotesProvider } from './notes';
|
||||
|
||||
/**
|
||||
* Profile notes handler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesUserHandler implements CoreUserProfileHandler {
|
||||
name = 'AddonNotes';
|
||||
priority = 200;
|
||||
type = CoreUserDelegate.TYPE_COMMUNICATION;
|
||||
addNoteEnabledCache = {};
|
||||
|
||||
constructor(private modalCtrl: ModalController, private sitesProvider: CoreSitesProvider,
|
||||
private notesProvider: AddonNotesProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear add note cache.
|
||||
* If a courseId is specified, it will only delete the entry for that course.
|
||||
*
|
||||
* @param {number} [courseId] Course ID.
|
||||
*/
|
||||
clearAddNoteCache(courseId?: number): void {
|
||||
if (courseId) {
|
||||
delete this.addNoteEnabledCache[courseId];
|
||||
} else {
|
||||
this.addNoteEnabledCache = {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return this.notesProvider.isPluginAddNoteEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if handler is enabled for this user in this context.
|
||||
*
|
||||
* @param {any} user User to check.
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {any} [navOptions] Course navigation options for current user. See $mmCourses#getUserNavigationOptions.
|
||||
* @param {any} [admOptions] Course admin options for current user. See $mmCourses#getUserAdministrationOptions.
|
||||
* @return {boolean|Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise.
|
||||
*/
|
||||
isEnabledForUser(user: any, courseId: number, navOptions?: any, admOptions?: any): boolean | Promise<boolean> {
|
||||
// Active course required.
|
||||
if (!courseId || user.id == this.sitesProvider.getCurrentSiteUserId()) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (typeof this.addNoteEnabledCache[courseId] != 'undefined') {
|
||||
return this.addNoteEnabledCache[courseId];
|
||||
}
|
||||
|
||||
return this.notesProvider.isPluginAddNoteEnabledForCourse(courseId).then((enabled) => {
|
||||
this.addNoteEnabledCache[courseId] = enabled;
|
||||
|
||||
return enabled;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return {CoreUserProfileHandlerData} Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(user: any, courseId: number): CoreUserProfileHandlerData {
|
||||
return {
|
||||
icon: 'list',
|
||||
title: 'addon.notes.addnewnote',
|
||||
class: 'addon-notes-handler',
|
||||
action: (event, navCtrl, user, courseId): void => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const modal = this.modalCtrl.create('AddonNotesAddPage', { userId: user.id, courseId });
|
||||
modal.present();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -79,6 +79,7 @@ import { AddonModLabelModule } from '@addon/mod/label/label.module';
|
|||
import { AddonModResourceModule } from '@addon/mod/resource/resource.module';
|
||||
import { AddonModFolderModule } from '@addon/mod/folder/folder.module';
|
||||
import { AddonMessagesModule } from '@addon/messages/messages.module';
|
||||
import { AddonNotesModule } from '../addon/notes/notes.module';
|
||||
import { AddonPushNotificationsModule } from '@addon/pushnotifications/pushnotifications.module';
|
||||
import { AddonRemoteThemesModule } from '@addon/remotethemes/remotethemes.module';
|
||||
|
||||
|
@ -161,6 +162,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonModResourceModule,
|
||||
AddonModFolderModule,
|
||||
AddonMessagesModule,
|
||||
AddonNotesModule,
|
||||
AddonPushNotificationsModule,
|
||||
AddonRemoteThemesModule
|
||||
],
|
||||
|
|
|
@ -151,11 +151,11 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Check if a sync is needed: if a certain time has passed since the last time.
|
||||
*
|
||||
* @param {string} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether sync is needed.
|
||||
*/
|
||||
isSyncNeeded(id: string, siteId?: string): Promise<boolean> {
|
||||
isSyncNeeded(id: string | number, siteId?: string): Promise<boolean> {
|
||||
return this.getSyncTime(id, siteId).then((time) => {
|
||||
return Date.now() - this.syncInterval >= time;
|
||||
});
|
||||
|
@ -178,12 +178,12 @@ export class CoreSyncBaseProvider {
|
|||
/**
|
||||
* Set the synchronization warnings.
|
||||
*
|
||||
* @param {string} id Unique sync identifier per component.
|
||||
* @param {string | number} id Unique sync identifier per component.
|
||||
* @param {string[]} warnings Warnings to set.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
setSyncWarnings(id: string, warnings: string[], siteId?: string): Promise<any> {
|
||||
setSyncWarnings(id: string | number, warnings: string[], siteId?: string): Promise<any> {
|
||||
const warningsText = JSON.stringify(warnings || []);
|
||||
|
||||
return this.syncProvider.insertOrUpdateSyncRecord(this.component, id, { warnings: warningsText }, siteId);
|
||||
|
@ -245,5 +245,5 @@ export class CoreSyncBaseProvider {
|
|||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue