commit
e026563934
|
@ -514,7 +514,7 @@ export class AddonCalendarProvider {
|
|||
notificationtime: e.notificationtime || -1
|
||||
};
|
||||
|
||||
return db.insertOrUpdateRecord(this.EVENTS_TABLE, eventRecord, { id: eventRecord.id });
|
||||
return db.insertRecord(this.EVENTS_TABLE, eventRecord);
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -539,7 +539,7 @@ export class AddonCalendarProvider {
|
|||
|
||||
event.notificationtime = time;
|
||||
|
||||
return site.getDb().insertOrUpdateRecord(this.EVENTS_TABLE, event, { id: event.id }).then(() => {
|
||||
return site.getDb().insertRecord(this.EVENTS_TABLE, event).then(() => {
|
||||
return this.scheduleEventNotification(event, time);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,10 +22,6 @@ import { AddonCompetencyComponentsModule } from './components/components.module'
|
|||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||
import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
||||
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -44,24 +40,10 @@ import { CoreCoursesProvider } from '@core/courses/providers/courses';
|
|||
export class AddonCompetencyModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: AddonCompetencyMainMenuHandler,
|
||||
courseOptionsDelegate: CoreCourseOptionsDelegate, courseOptionHandler: AddonCompetencyCourseOptionHandler,
|
||||
userDelegate: CoreUserDelegate, userHandler: AddonCompetencyUserHandler,
|
||||
eventsProvider: CoreEventsProvider, sitesProvider: CoreSitesProvider) {
|
||||
userDelegate: CoreUserDelegate, userHandler: AddonCompetencyUserHandler) {
|
||||
|
||||
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
|
||||
courseOptionHandler.clearCoursesNavCache();
|
||||
userHandler.clearUsersNavCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
|
||||
eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED, () => {
|
||||
courseOptionHandler.clearCoursesNavCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, () => {
|
||||
userHandler.clearUsersNavCache();
|
||||
}, sitesProvider.getCurrentSiteId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,17 +26,8 @@ export class AddonCompetencyCourseOptionHandler implements CoreCourseOptionsHand
|
|||
name = 'AddonCompetency';
|
||||
priority = 700;
|
||||
|
||||
protected coursesNavEnabledCache = {};
|
||||
|
||||
constructor(private competencyProvider: AddonCompetencyProvider) {}
|
||||
|
||||
/**
|
||||
* Clear courses nav cache.
|
||||
*/
|
||||
clearCoursesNavCache(): void {
|
||||
this.coursesNavEnabledCache = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled ona site level.
|
||||
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||
|
@ -63,15 +54,8 @@ export class AddonCompetencyCourseOptionHandler implements CoreCourseOptionsHand
|
|||
return navOptions.competencies;
|
||||
}
|
||||
|
||||
if (typeof this.coursesNavEnabledCache[courseId] != 'undefined') {
|
||||
return this.coursesNavEnabledCache[courseId];
|
||||
}
|
||||
|
||||
return this.competencyProvider.isPluginForCourseEnabled(courseId).then((competencies) => {
|
||||
const enabled = competencies ? !competencies.canmanagecoursecompetencies : false;
|
||||
this.coursesNavEnabledCache[courseId] = enabled;
|
||||
|
||||
return enabled;
|
||||
return competencies ? !competencies.canmanagecoursecompetencies : false;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate';
|
||||
import { CoreSitesProvider } from '@providers/sites';
|
||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||
import { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
import { AddonCompetencyProvider } from './competency';
|
||||
|
||||
/**
|
||||
|
@ -30,13 +32,15 @@ export class AddonCompetencyUserHandler implements CoreUserProfileHandler {
|
|||
usersNavEnabledCache = {};
|
||||
|
||||
constructor(private linkHelper: CoreContentLinksHelperProvider, protected sitesProvider: CoreSitesProvider,
|
||||
private competencyProvider: AddonCompetencyProvider) {
|
||||
private competencyProvider: AddonCompetencyProvider, eventsProvider: CoreEventsProvider) {
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearUsersNavCache.bind(this));
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, this.clearUsersNavCache.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear users nav cache.
|
||||
*/
|
||||
clearUsersNavCache(): void {
|
||||
private clearUsersNavCache(): void {
|
||||
this.participantsNavEnabledCache = {};
|
||||
this.usersNavEnabledCache = {};
|
||||
}
|
||||
|
|
|
@ -148,11 +148,7 @@ export class AddonMessagesOfflineProvider {
|
|||
deviceoffline: this.appProvider.isOnline() ? 0 : 1
|
||||
};
|
||||
|
||||
return site.getDb().insertOrUpdateRecord(this.MESSAGES_TABLE, entry, {
|
||||
touserid: toUserId,
|
||||
smallmessage: message,
|
||||
timecreated: entry.timecreated
|
||||
}).then(() => {
|
||||
return site.getDb().insertRecord(this.MESSAGES_TABLE, entry).then(() => {
|
||||
return entry;
|
||||
});
|
||||
});
|
||||
|
@ -173,11 +169,7 @@ export class AddonMessagesOfflineProvider {
|
|||
data = { deviceoffline: value ? 1 : 0 };
|
||||
|
||||
messages.forEach((message) => {
|
||||
promises.push(db.insertOrUpdateRecord(this.MESSAGES_TABLE, data, {
|
||||
touserid: message.touserid,
|
||||
smallmessage: message.smallmessage,
|
||||
timecreated: message.timecreated
|
||||
}));
|
||||
promises.push(db.insertRecord(this.MESSAGES_TABLE, data));
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
// (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 { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { AddonNotesListComponent } from './list/list';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonNotesListComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CorePipesModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: [
|
||||
AddonNotesListComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonNotesListComponent
|
||||
]
|
||||
})
|
||||
export class AddonNotesComponentsModule {}
|
|
@ -0,0 +1,43 @@
|
|||
<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-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">
|
||||
|
||||
<ion-select [(ngModel)]="type" (ngModelChange)="typeChanged()" interface="popover">
|
||||
<ion-option value="site">{{ 'addon.notes.sitenotes' | translate }}</ion-option>
|
||||
<ion-option value="course">{{ 'addon.notes.coursenotes' | translate }}</ion-option>
|
||||
<ion-option value="personal">{{ 'addon.notes.personalnotes' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
|
||||
<div class="core-warning-card" icon-start *ngIf="hasOffline">
|
||||
<ion-icon name="warning"></ion-icon>
|
||||
{{ 'core.thereisdatatosync' | translate:{$a: 'addon.notes.notes' | translate | lowercase } }}
|
||||
</div>
|
||||
|
||||
<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 core-user-link [userId]="note.userid" [courseId]="note.courseid" [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,171 @@
|
|||
// (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, Input, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
||||
import { Content } 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 { AddonNotesProvider } from '../../providers/notes';
|
||||
import { AddonNotesSyncProvider } from '../../providers/notes-sync';
|
||||
|
||||
/**
|
||||
* Component that displays the notes of a course.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-notes-list',
|
||||
templateUrl: 'list.html',
|
||||
})
|
||||
export class AddonNotesListComponent implements OnInit, OnDestroy {
|
||||
@Input() courseId: number;
|
||||
|
||||
@ViewChild(Content) content: Content;
|
||||
|
||||
protected syncObserver: any;
|
||||
|
||||
type = 'course';
|
||||
refreshIcon = 'spinner';
|
||||
syncIcon = 'spinner';
|
||||
notes: any[];
|
||||
hasOffline = false;
|
||||
notesLoaded = false;
|
||||
|
||||
constructor(private domUtils: CoreDomUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
||||
sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
|
||||
private notesProvider: AddonNotesProvider, private notesSync: AddonNotesSyncProvider) {
|
||||
// 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): 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 = notes.some((note) => note.offline);
|
||||
|
||||
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 = 'sync';
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when the type has changed.
|
||||
*/
|
||||
typeChanged(): void {
|
||||
this.notesLoaded = false;
|
||||
this.refreshIcon = 'spinner';
|
||||
this.syncIcon = 'spinner';
|
||||
this.fetchNotes(true).then(() => {
|
||||
this.notesProvider.logView(this.courseId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to synchronize 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.syncObserver && this.syncObserver.off();
|
||||
}
|
||||
}
|
|
@ -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,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 { 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 { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||
|
||||
@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) {
|
||||
// Register handlers.
|
||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||
userDelegate.registerHandler(userHandler);
|
||||
cronDelegate.register(syncHandler);
|
||||
}
|
||||
}
|
|
@ -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 block 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,74 @@
|
|||
// (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 { AddonNotesListComponent } from '../components/list/list';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into the course main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonNotesCourseOptionHandler implements CoreCourseOptionsHandler {
|
||||
name = 'AddonNotes';
|
||||
priority = 200;
|
||||
|
||||
constructor(private notesProvider: AddonNotesProvider) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.isPluginEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
return this.notesProvider.isPluginViewNotesEnabledForCourse(courseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: AddonNotesListComponent,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
// (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
|
||||
};
|
||||
|
||||
return site.getDb().insertRecord(this.NOTES_TABLE, data).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,301 @@
|
|||
// (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 utils: 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((error) => {
|
||||
if (this.utils.isWebServiceError(error)) {
|
||||
// It's a WebService error, the user cannot send the message so don't store it.
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// 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.
|
||||
*/
|
||||
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).then((response) => {
|
||||
if (response && response[0] && response[0].noteid === -1) {
|
||||
// There was an error, and it should be translated already.
|
||||
return Promise.reject(this.utils.createFakeWSError(response[0].errormessage));
|
||||
}
|
||||
|
||||
// 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 notes 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.
|
||||
*/
|
||||
isPluginEnabled(siteId?: string): Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return site.canUseAdvancedFeature('enablenotes');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 this.utils.promiseWorks(site.read('core_notes_create_notes', data));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.utils.promiseWorks(this.getNotes(courseId, false, true, siteId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,107 @@
|
|||
// (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 { CoreEventsProvider } from '@providers/events';
|
||||
import { CoreUserProvider } from '@core/user/providers/user';
|
||||
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, eventsProvider: CoreEventsProvider) {
|
||||
eventsProvider.on(CoreEventsProvider.LOGOUT, this.clearAddNoteCache.bind(this));
|
||||
eventsProvider.on(CoreUserProvider.PROFILE_REFRESHED, (data) => {
|
||||
this.clearAddNoteCache(data.courseId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear add note cache.
|
||||
* If a courseId is specified, it will only delete the entry for that course.
|
||||
*
|
||||
* @param {number} [courseId] Course ID.
|
||||
*/
|
||||
private 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.isPluginEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -326,8 +326,8 @@ export class AddonPushNotificationsProvider {
|
|||
this.onMessageReceived(notification);
|
||||
});
|
||||
|
||||
pushObject.on('registration').subscribe((registrationId: any) => {
|
||||
this.pushID = registrationId;
|
||||
pushObject.on('registration').subscribe((data: any) => {
|
||||
this.pushID = data.registrationId;
|
||||
this.registerDeviceOnMoodle().catch((error) => {
|
||||
this.logger.warn('Can\'t register device', error);
|
||||
});
|
||||
|
@ -402,7 +402,7 @@ export class AddonPushNotificationsProvider {
|
|||
number: value
|
||||
};
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.BADGE_TABLE, entry, {siteid: siteId, addon: addon}).then(() => {
|
||||
return this.appDB.insertRecord(this.BADGE_TABLE, entry).then(() => {
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ import { AddonModFolderModule } from '@addon/mod/folder/folder.module';
|
|||
import { AddonModPageModule } from '@addon/mod/page/page.module';
|
||||
import { AddonModUrlModule } from '@addon/mod/url/url.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';
|
||||
|
||||
|
@ -165,6 +166,7 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonModPageModule,
|
||||
AddonModUrlModule,
|
||||
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);
|
||||
|
|
|
@ -780,7 +780,7 @@ export class CoreSite {
|
|||
entry.key = preSets.cacheKey;
|
||||
}
|
||||
|
||||
return this.db.insertOrUpdateRecord(this.WS_CACHE_TABLE, entry, { id: id });
|
||||
return this.db.insertRecord(this.WS_CACHE_TABLE, entry);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -625,7 +625,7 @@ export class SQLiteDB {
|
|||
questionMarks = ',?'.repeat(keys.length).substr(1);
|
||||
|
||||
return [
|
||||
`INSERT INTO ${table} (${fields}) VALUES (${questionMarks})`,
|
||||
`INSERT OR REPLACE INTO ${table} (${fields}) VALUES (${questionMarks})`,
|
||||
keys.map((key) => data[key])
|
||||
];
|
||||
}
|
||||
|
@ -644,24 +644,6 @@ export class SQLiteDB {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or update a record.
|
||||
*
|
||||
* @param {string} table The database table.
|
||||
* @param {object} data An object with the fields to insert/update: fieldname=>fieldvalue.
|
||||
* @param {object} conditions The conditions to check if the record already exists (and to update it).
|
||||
* @return {Promise<any>} Promise resolved with done.
|
||||
*/
|
||||
insertOrUpdateRecord(table: string, data: object, conditions: object): Promise<any> {
|
||||
return this.getRecord(table, conditions).then(() => {
|
||||
// It exists, update it.
|
||||
return this.updateRecords(table, data, conditions);
|
||||
}).catch(() => {
|
||||
// Doesn't exist, insert it.
|
||||
return this.insertRecord(table, data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a record into a table and return the "rowId" field.
|
||||
*
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, OnInit, OnDestroy, ElementRef } from '@angular/core';
|
||||
import { Component, Input, OnInit, OnDestroy, ElementRef, Optional } from '@angular/core';
|
||||
import { PopoverController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||
import { CoreContextMenuItemComponent } from './context-menu-item';
|
||||
import { CoreContextMenuPopoverComponent } from './context-menu-popover';
|
||||
import { CoreTabComponent } from '@components/tabs/tab';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
/**
|
||||
|
@ -40,7 +41,7 @@ export class CoreContextMenuComponent implements OnInit, OnDestroy {
|
|||
protected parentContextMenu: CoreContextMenuComponent;
|
||||
|
||||
constructor(private translate: TranslateService, private popoverCtrl: PopoverController, elementRef: ElementRef,
|
||||
private domUtils: CoreDomUtilsProvider) {
|
||||
private domUtils: CoreDomUtilsProvider, @Optional() public coreTab: CoreTabComponent) {
|
||||
// Create the stream and subscribe to it. We ignore successive changes during 250ms.
|
||||
this.itemsChangedStream = new Subject<void>();
|
||||
this.itemsChangedStream.auditTime(250).subscribe(() => {
|
||||
|
|
|
@ -134,10 +134,12 @@ export class CoreNavBarButtonsComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
// Both containers have a context menu. Merge them to prevent having 2 menus at the same time.
|
||||
const mainContextMenuInstance = this.domUtils.getInstanceByElement(mainContextMenu),
|
||||
secondaryContextMenuInstance = this.domUtils.getInstanceByElement(secondaryContextMenu);
|
||||
const mainContextMenuInstance: CoreContextMenuComponent = this.domUtils.getInstanceByElement(mainContextMenu),
|
||||
secondaryContextMenuInstance: CoreContextMenuComponent = this.domUtils.getInstanceByElement(secondaryContextMenu);
|
||||
|
||||
if (mainContextMenuInstance && secondaryContextMenuInstance) {
|
||||
// Check that both context menus belong to the same core-tab. We shouldn't merge menus from different tabs.
|
||||
if (mainContextMenuInstance && secondaryContextMenuInstance &&
|
||||
mainContextMenuInstance.coreTab === secondaryContextMenuInstance.coreTab) {
|
||||
this.mergedContextMenu = secondaryContextMenuInstance;
|
||||
|
||||
this.mergedContextMenu.mergeContextMenus(mainContextMenuInstance);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"languages": {"ar": "عربي", "bg": "Български", "ca": "Català", "cs": "Čeština", "da": "Dansk", "de": "Deutsch", "de-du": "Deutsch - Du", "el": "Ελληνικά", "en": "English", "es": "Español", "es-mx": "Español - México", "eu": "Euskara", "fa": "فارسی", "fr" : "Français", "he" : "עברית", "hu": "magyar", "it": "Italiano", "lt" : "Lietuvių", "ja": "日本語","nl": "Nederlands", "pl": "Polski", "pt-br": "Português - Brasil", "pt": "Português - Portugal", "ro": "Română", "ru": "Русский", "sr-cr": "Српски", "sr-lt": "Srpski", "sv": "Svenska", "tr" : "Türkçe", "uk" : "Українська", "zh-cn" : "简体中文", "zh-tw" : "正體中文"},
|
||||
"wsservice" : "moodle_mobile_app",
|
||||
"wsextservice" : "local_mobile",
|
||||
"demo_sites": {"student": {"url": "http://school.demo.moodle.net", "username": "student", "password": "moodle"}, "teacher": {"url": "http://school.demo.moodle.net", "username": "teacher", "password": "moodle"}},
|
||||
"demo_sites": {"student": {"url": "https://school.demo.moodle.net", "username": "student", "password": "moodle"}, "teacher": {"url": "https://school.demo.moodle.net", "username": "teacher", "password": "moodle"}},
|
||||
"gcmpn": "694767596569",
|
||||
"customurlscheme": "moodlemobile",
|
||||
"siteurl": "",
|
||||
|
|
|
@ -699,7 +699,7 @@ export class CoreCourseProvider {
|
|||
previousDownloadTime: previousDownloadTime
|
||||
};
|
||||
|
||||
return site.getDb().insertOrUpdateRecord(this.COURSE_STATUS_TABLE, data, { id: courseId });
|
||||
return site.getDb().insertRecord(this.COURSE_STATUS_TABLE, data);
|
||||
}
|
||||
}).then(() => {
|
||||
// Success inserting, trigger event.
|
||||
|
|
|
@ -417,7 +417,9 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
|
|||
courseId: courseId,
|
||||
time: this.timeUtils.timestamp()
|
||||
};
|
||||
site.getDb().insertOrUpdateRecord(this.CHECK_UPDATES_TIMES_TABLE, entry, { courseId: courseId });
|
||||
site.getDb().insertRecord(this.CHECK_UPDATES_TIMES_TABLE, entry).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
|
||||
return this.treatCheckUpdatesResult(data.toCheck, response, result);
|
||||
}).catch((error) => {
|
||||
|
|
|
@ -671,7 +671,7 @@ export class LocalNotificationsMock extends LocalNotifications {
|
|||
notification = Object.assign({}, notification); // Clone the object.
|
||||
notification.triggered = !!triggered;
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.DESKTOP_NOTIFS_TABLE, notification, { id: notification.id });
|
||||
return this.appDB.insertRecord(this.DESKTOP_NOTIFS_TABLE, notification);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -383,7 +383,7 @@ export class CoreUserProvider {
|
|||
profileimageurl: avatar
|
||||
};
|
||||
|
||||
return site.getDb().insertOrUpdateRecord(this.USERS_TABLE, userRecord, { id: userId });
|
||||
return site.getDb().insertRecord(this.USERS_TABLE, userRecord);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -81,6 +81,6 @@ export class CoreConfigProvider {
|
|||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
set(name: string, value: boolean | number | string): Promise<any> {
|
||||
return this.appDB.insertOrUpdateRecord(this.TABLE_NAME, { name: name, value: value }, { name: name });
|
||||
return this.appDB.insertRecord(this.TABLE_NAME, { name: name, value: value });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -459,7 +459,7 @@ export class CoreCronDelegate {
|
|||
value: time
|
||||
};
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.CRON_TABLE, entry, { id: id });
|
||||
return this.appDB.insertRecord(this.CRON_TABLE, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -477,7 +477,7 @@ export class CoreFilepoolProvider {
|
|||
componentId: componentId || ''
|
||||
};
|
||||
|
||||
return db.insertOrUpdateRecord(this.LINKS_TABLE, newEntry, { fileId: fileId });
|
||||
return db.insertRecord(this.LINKS_TABLE, newEntry);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -544,7 +544,7 @@ export class CoreFilepoolProvider {
|
|||
values.fileId = fileId;
|
||||
|
||||
return this.sitesProvider.getSiteDb(siteId).then((db) => {
|
||||
return db.insertOrUpdateRecord(this.FILES_TABLE, values, { fileId: fileId });
|
||||
return db.insertRecord(this.FILES_TABLE, values);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2766,7 +2766,7 @@ export class CoreFilepoolProvider {
|
|||
// The package already has this status, no need to change it.
|
||||
promise = Promise.resolve();
|
||||
} else {
|
||||
promise = site.getDb().insertOrUpdateRecord(this.PACKAGES_TABLE, packageEntry, { id: packageId });
|
||||
promise = site.getDb().insertRecord(this.PACKAGES_TABLE, packageEntry);
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
|
|
|
@ -500,6 +500,6 @@ export class CoreLocalNotificationsProvider {
|
|||
at: parseInt(notification.at, 10)
|
||||
};
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.TRIGGERED_TABLE, entry, { id: notification.id });
|
||||
return this.appDB.insertRecord(this.TRIGGERED_TABLE, entry);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -623,7 +623,7 @@ export class CoreSitesProvider {
|
|||
loggedOut: 0
|
||||
};
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.SITES_TABLE, entry, { id: id });
|
||||
return this.appDB.insertRecord(this.SITES_TABLE, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -895,7 +895,7 @@ export class CoreSitesProvider {
|
|||
siteId: siteId
|
||||
};
|
||||
|
||||
return this.appDB.insertOrUpdateRecord(this.CURRENT_SITE_TABLE, entry, { id: 1 }).then(() => {
|
||||
return this.appDB.insertRecord(this.CURRENT_SITE_TABLE, entry).then(() => {
|
||||
this.eventsProvider.trigger(CoreEventsProvider.LOGIN, {}, siteId);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ export class CoreSyncProvider {
|
|||
data.component = component;
|
||||
data.id = id;
|
||||
|
||||
return db.insertOrUpdateRecord(this.SYNC_TABLE, data, { component: component, id: id });
|
||||
return db.insertRecord(this.SYNC_TABLE, data);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue