MOBILE-2652 glossary: Clean up edit form
parent
5de4cfbbd2
commit
39a6d67c25
|
@ -259,7 +259,7 @@ export class AddonModGlossaryEntriesSource extends CoreRoutedItemsManagerSource<
|
||||||
const entries: AddonModGlossaryEntryItem[] = [];
|
const entries: AddonModGlossaryEntryItem[] = [];
|
||||||
|
|
||||||
if (page === 0) {
|
if (page === 0) {
|
||||||
const offlineEntries = await AddonModGlossaryOffline.getGlossaryNewEntries(glossary.id);
|
const offlineEntries = await AddonModGlossaryOffline.getGlossaryOfflineEntries(glossary.id);
|
||||||
|
|
||||||
offlineEntries.sort((a, b) => a.concept.localeCompare(b.concept));
|
offlineEntries.sort((a, b) => a.concept.localeCompare(b.concept));
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<form #editFormEl *ngIf="glossary">
|
<form #editFormEl *ngIf="glossary">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-label position="stacked">{{ 'addon.mod_glossary.concept' | translate }}</ion-label>
|
<ion-label position="stacked">{{ 'addon.mod_glossary.concept' | translate }}</ion-label>
|
||||||
<ion-input type="text" [placeholder]="'addon.mod_glossary.concept' | translate" [(ngModel)]="entry.concept" name="concept">
|
<ion-input type="text" [placeholder]="'addon.mod_glossary.concept' | translate" [(ngModel)]="data.concept" name="concept">
|
||||||
</ion-input>
|
</ion-input>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
{{ 'addon.mod_glossary.categories' | translate }}
|
{{ 'addon.mod_glossary.categories' | translate }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-select [(ngModel)]="options.categories" multiple="true" interface="action-sheet"
|
<ion-select [(ngModel)]="data.categories" multiple="true" interface="action-sheet"
|
||||||
[placeholder]="'addon.mod_glossary.categories' | translate" name="categories"
|
[placeholder]="'addon.mod_glossary.categories' | translate" name="categories"
|
||||||
[interfaceOptions]="{header: 'addon.mod_glossary.categories' | translate}">
|
[interfaceOptions]="{header: 'addon.mod_glossary.categories' | translate}">
|
||||||
<ion-select-option *ngFor="let category of categories" [value]="category.id">
|
<ion-select-option *ngFor="let category of categories" [value]="category.id">
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
{{ 'addon.mod_glossary.aliases' | translate }}
|
{{ 'addon.mod_glossary.aliases' | translate }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-textarea [(ngModel)]="options.aliases" rows="1" [core-auto-rows]="options.aliases" name="aliases">
|
<ion-textarea [(ngModel)]="data.aliases" rows="1" [core-auto-rows]="data.aliases" name="aliases">
|
||||||
</ion-textarea>
|
</ion-textarea>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
<h2>{{ 'addon.mod_glossary.attachment' | translate }}</h2>
|
<h2>{{ 'addon.mod_glossary.attachment' | translate }}</h2>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<core-attachments [files]="attachments" [component]="component" [componentId]="glossary.coursemodule" [allowOffline]="true"
|
<core-attachments [files]="data.attachments" [component]="component" [componentId]="glossary.coursemodule" [allowOffline]="true"
|
||||||
[courseId]="courseId">
|
[courseId]="courseId">
|
||||||
</core-attachments>
|
</core-attachments>
|
||||||
<ng-container *ngIf="glossary.usedynalink">
|
<ng-container *ngIf="glossary.usedynalink">
|
||||||
|
@ -62,19 +62,19 @@
|
||||||
</ion-item-divider>
|
</ion-item-divider>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>{{ 'addon.mod_glossary.entryusedynalink' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.entryusedynalink' | translate }}</ion-label>
|
||||||
<ion-toggle [(ngModel)]="options.usedynalink" name="usedynalink"></ion-toggle>
|
<ion-toggle [(ngModel)]="data.usedynalink" name="usedynalink"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>{{ 'addon.mod_glossary.casesensitive' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.casesensitive' | translate }}</ion-label>
|
||||||
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.casesensitive" name="casesensitive">
|
<ion-toggle [disabled]="!data.usedynalink" [(ngModel)]="data.casesensitive" name="casesensitive">
|
||||||
</ion-toggle>
|
</ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label>{{ 'addon.mod_glossary.fullmatch' | translate }}</ion-label>
|
<ion-label>{{ 'addon.mod_glossary.fullmatch' | translate }}</ion-label>
|
||||||
<ion-toggle [disabled]="!options.usedynalink" [(ngModel)]="options.fullmatch" name="fullmatch"></ion-toggle>
|
<ion-toggle [disabled]="!data.usedynalink" [(ngModel)]="data.fullmatch" name="fullmatch"></ion-toggle>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
<ion-button class="ion-margin" expand="block" [disabled]="!entry.concept || !entry.definition" (click)="save()">
|
<ion-button class="ion-margin" expand="block" [disabled]="!data.concept || !data.definition" (click)="save()">
|
||||||
{{ 'core.save' | translate }}
|
{{ 'core.save' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
|
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
|
||||||
import { CanLeave } from '@guards/can-leave';
|
import { CanLeave } from '@guards/can-leave';
|
||||||
import { FileEntry } from '@ionic-native/file/ngx';
|
import { CoreFileEntry } from '@services/file-helper';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
@ -36,8 +36,6 @@ import {
|
||||||
AddonModGlossaryCategory,
|
AddonModGlossaryCategory,
|
||||||
AddonModGlossaryEntryOption,
|
AddonModGlossaryEntryOption,
|
||||||
AddonModGlossaryGlossary,
|
AddonModGlossaryGlossary,
|
||||||
AddonModGlossaryNewEntry,
|
|
||||||
AddonModGlossaryNewEntryWithFiles,
|
|
||||||
AddonModGlossaryProvider,
|
AddonModGlossaryProvider,
|
||||||
} from '../../services/glossary';
|
} from '../../services/glossary';
|
||||||
import { AddonModGlossaryHelper } from '../../services/glossary-helper';
|
import { AddonModGlossaryHelper } from '../../services/glossary-helper';
|
||||||
|
@ -59,32 +57,29 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
courseId!: number;
|
courseId!: number;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
glossary?: AddonModGlossaryGlossary;
|
glossary?: AddonModGlossaryGlossary;
|
||||||
attachments: FileEntry[] = [];
|
|
||||||
definitionControl = new FormControl();
|
definitionControl = new FormControl();
|
||||||
categories: AddonModGlossaryCategory[] = [];
|
categories: AddonModGlossaryCategory[] = [];
|
||||||
editorExtraParams: Record<string, unknown> = {};
|
editorExtraParams: Record<string, unknown> = {};
|
||||||
entry: AddonModGlossaryNewEntry = {
|
data: AddonModGlossaryFormData = {
|
||||||
concept: '',
|
concept: '',
|
||||||
definition: '',
|
definition: '',
|
||||||
timecreated: 0,
|
timecreated: 0,
|
||||||
};
|
attachments: [],
|
||||||
|
categories: [],
|
||||||
entries?: AddonModGlossaryEditEntriesSwipeManager;
|
|
||||||
|
|
||||||
options = {
|
|
||||||
categories: <string[]> [],
|
|
||||||
aliases: '',
|
aliases: '',
|
||||||
usedynalink: false,
|
usedynalink: false,
|
||||||
casesensitive: false,
|
casesensitive: false,
|
||||||
fullmatch: false,
|
fullmatch: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
entries?: AddonModGlossaryEditEntriesSwipeManager;
|
||||||
|
|
||||||
protected timecreated!: number;
|
protected timecreated!: number;
|
||||||
protected concept = '';
|
protected concept = '';
|
||||||
protected syncId?: string;
|
protected syncId?: string;
|
||||||
protected syncObserver?: CoreEventObserver;
|
protected syncObserver?: CoreEventObserver;
|
||||||
protected isDestroyed = false;
|
protected isDestroyed = false;
|
||||||
protected originalData?: AddonModGlossaryNewEntryWithFiles;
|
protected originalData?: AddonModGlossaryFormData;
|
||||||
protected saved = false;
|
protected saved = false;
|
||||||
|
|
||||||
constructor(protected route: ActivatedRoute, @Optional() protected splitView: CoreSplitViewComponent) {}
|
constructor(protected route: ActivatedRoute, @Optional() protected splitView: CoreSplitViewComponent) {}
|
||||||
|
@ -164,54 +159,64 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = await AddonModGlossaryOffline.getNewEntry(this.glossary.id, this.concept, this.timecreated);
|
const entry = await AddonModGlossaryOffline.getOfflineEntry(this.glossary.id, this.concept, this.timecreated);
|
||||||
|
|
||||||
this.entry.concept = entry.concept || '';
|
this.data.concept = entry.concept || '';
|
||||||
this.entry.definition = entry.definition || '';
|
this.data.definition = entry.definition || '';
|
||||||
this.entry.timecreated = entry.timecreated;
|
this.data.timecreated = entry.timecreated;
|
||||||
|
|
||||||
this.originalData = {
|
this.originalData = {
|
||||||
concept: this.entry.concept,
|
concept: this.data.concept,
|
||||||
definition: this.entry.definition,
|
definition: this.data.definition,
|
||||||
files: [],
|
attachments: this.data.attachments.slice(),
|
||||||
timecreated: entry.timecreated,
|
timecreated: entry.timecreated,
|
||||||
|
categories: this.data.categories.slice(),
|
||||||
|
aliases: this.data.aliases,
|
||||||
|
usedynalink: this.data.usedynalink,
|
||||||
|
casesensitive: this.data.casesensitive,
|
||||||
|
fullmatch: this.data.fullmatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (entry.options) {
|
if (entry.options) {
|
||||||
this.options.categories = (entry.options.categories && (<string> entry.options.categories).split(',')) || [];
|
this.data.categories = (entry.options.categories && (<string> entry.options.categories).split(',')) || [];
|
||||||
this.options.aliases = <string> entry.options.aliases || '';
|
this.data.aliases = <string> entry.options.aliases || '';
|
||||||
this.options.usedynalink = !!entry.options.usedynalink;
|
this.data.usedynalink = !!entry.options.usedynalink;
|
||||||
if (this.options.usedynalink) {
|
|
||||||
this.options.casesensitive = !!entry.options.casesensitive;
|
if (this.data.usedynalink) {
|
||||||
this.options.fullmatch = !!entry.options.fullmatch;
|
this.data.casesensitive = !!entry.options.casesensitive;
|
||||||
|
this.data.fullmatch = !!entry.options.fullmatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat offline attachments if any.
|
// Treat offline attachments if any.
|
||||||
if (entry.attachments?.offline) {
|
if (entry.attachments?.offline) {
|
||||||
this.attachments = await AddonModGlossaryHelper.getStoredFiles(this.glossary.id, entry.concept, entry.timecreated);
|
this.data.attachments = await AddonModGlossaryHelper.getStoredFiles(
|
||||||
|
this.glossary.id,
|
||||||
|
entry.concept,
|
||||||
|
entry.timecreated,
|
||||||
|
);
|
||||||
|
|
||||||
this.originalData.files = this.attachments.slice();
|
this.originalData.attachments = this.data.attachments.slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.definitionControl.setValue(this.entry.definition);
|
this.definitionControl.setValue(this.data.definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the form data.
|
* Reset the form data.
|
||||||
*/
|
*/
|
||||||
protected resetForm(): void {
|
protected resetForm(): void {
|
||||||
this.entry.concept = '';
|
|
||||||
this.entry.definition = '';
|
|
||||||
this.entry.timecreated = 0;
|
|
||||||
this.originalData = undefined;
|
this.originalData = undefined;
|
||||||
|
|
||||||
this.options.categories = [];
|
this.data.concept = '';
|
||||||
this.options.aliases = '';
|
this.data.definition = '';
|
||||||
this.options.usedynalink = false;
|
this.data.timecreated = 0;
|
||||||
this.options.casesensitive = false;
|
this.data.categories = [];
|
||||||
this.options.fullmatch = false;
|
this.data.aliases = '';
|
||||||
this.attachments.length = 0; // Empty the array.
|
this.data.usedynalink = false;
|
||||||
|
this.data.casesensitive = false;
|
||||||
|
this.data.fullmatch = false;
|
||||||
|
this.data.attachments.length = 0; // Empty the array.
|
||||||
|
|
||||||
this.definitionControl.setValue('');
|
this.definitionControl.setValue('');
|
||||||
}
|
}
|
||||||
|
@ -222,7 +227,7 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @param text The new text.
|
* @param text The new text.
|
||||||
*/
|
*/
|
||||||
onDefinitionChange(text: string): void {
|
onDefinitionChange(text: string): void {
|
||||||
this.entry.definition = text;
|
this.data.definition = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -235,13 +240,13 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AddonModGlossaryHelper.hasEntryDataChanged(this.entry, this.attachments, this.originalData)) {
|
if (this.hasDataChanged()) {
|
||||||
// Show confirmation if some data has been modified.
|
// Show confirmation if some data has been modified.
|
||||||
await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit'));
|
await CoreDomUtils.showConfirm(Translate.instant('core.confirmcanceledit'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the local files from the tmp folder.
|
// Delete the local files from the tmp folder.
|
||||||
CoreFileUploader.clearTmpFiles(this.attachments);
|
CoreFileUploader.clearTmpFiles(this.data.attachments);
|
||||||
|
|
||||||
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
|
CoreForms.triggerFormCancelledEvent(this.formElement, CoreSites.getCurrentSiteId());
|
||||||
|
|
||||||
|
@ -252,11 +257,11 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* Save the entry.
|
* Save the entry.
|
||||||
*/
|
*/
|
||||||
async save(): Promise<void> {
|
async save(): Promise<void> {
|
||||||
let definition = this.entry.definition;
|
let definition = this.data.definition;
|
||||||
let entryId: number | undefined;
|
let entryId: number | undefined;
|
||||||
const timecreated = this.entry.timecreated || Date.now();
|
const timecreated = this.data.timecreated || Date.now();
|
||||||
|
|
||||||
if (!this.entry.concept || !definition) {
|
if (!this.data.concept || !definition) {
|
||||||
CoreDomUtils.showErrorModal('addon.mod_glossary.fillfields', true);
|
CoreDomUtils.showErrorModal('addon.mod_glossary.fillfields', true);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -274,23 +279,23 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
const { saveOffline, attachmentsResult } = await this.uploadAttachments(timecreated);
|
const { saveOffline, attachmentsResult } = await this.uploadAttachments(timecreated);
|
||||||
|
|
||||||
const options: Record<string, AddonModGlossaryEntryOption> = {
|
const options: Record<string, AddonModGlossaryEntryOption> = {
|
||||||
aliases: this.options.aliases,
|
aliases: this.data.aliases,
|
||||||
categories: this.options.categories.join(','),
|
categories: this.data.categories.join(','),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.glossary.usedynalink) {
|
if (this.glossary.usedynalink) {
|
||||||
options.usedynalink = this.options.usedynalink ? 1 : 0;
|
options.usedynalink = this.data.usedynalink ? 1 : 0;
|
||||||
if (this.options.usedynalink) {
|
if (this.data.usedynalink) {
|
||||||
options.casesensitive = this.options.casesensitive ? 1 : 0;
|
options.casesensitive = this.data.casesensitive ? 1 : 0;
|
||||||
options.fullmatch = this.options.fullmatch ? 1 : 0;
|
options.fullmatch = this.data.fullmatch ? 1 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveOffline) {
|
if (saveOffline) {
|
||||||
if (this.entry && !this.glossary.allowduplicatedentries) {
|
if (this.data && !this.glossary.allowduplicatedentries) {
|
||||||
// Check if the entry is duplicated in online or offline mode.
|
// Check if the entry is duplicated in online or offline mode.
|
||||||
const isUsed = await AddonModGlossary.isConceptUsed(this.glossary.id, this.entry.concept, {
|
const isUsed = await AddonModGlossary.isConceptUsed(this.glossary.id, this.data.concept, {
|
||||||
timeCreated: this.entry.timecreated,
|
timeCreated: this.data.timecreated,
|
||||||
cmId: this.cmId,
|
cmId: this.cmId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -301,9 +306,9 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save entry in offline.
|
// Save entry in offline.
|
||||||
await AddonModGlossaryOffline.addNewEntry(
|
await AddonModGlossaryOffline.addOfflineEntry(
|
||||||
this.glossary.id,
|
this.glossary.id,
|
||||||
this.entry.concept,
|
this.data.concept,
|
||||||
definition,
|
definition,
|
||||||
this.courseId,
|
this.courseId,
|
||||||
options,
|
options,
|
||||||
|
@ -311,33 +316,33 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
timecreated,
|
timecreated,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
this.entry,
|
this.data,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Try to send it to server.
|
// Try to send it to server.
|
||||||
// Don't allow offline if there are attachments since they were uploaded fine.
|
// Don't allow offline if there are attachments since they were uploaded fine.
|
||||||
await AddonModGlossary.addEntry(
|
await AddonModGlossary.addEntry(
|
||||||
this.glossary.id,
|
this.glossary.id,
|
||||||
this.entry.concept,
|
this.data.concept,
|
||||||
definition,
|
definition,
|
||||||
this.courseId,
|
this.courseId,
|
||||||
options,
|
options,
|
||||||
attachmentsResult,
|
attachmentsResult,
|
||||||
{
|
{
|
||||||
timeCreated: timecreated,
|
timeCreated: timecreated,
|
||||||
discardEntry: this.entry,
|
discardEntry: this.data,
|
||||||
allowOffline: !this.attachments.length,
|
allowOffline: !this.data.attachments.length,
|
||||||
checkDuplicates: !this.glossary.allowduplicatedentries,
|
checkDuplicates: !this.glossary.allowduplicatedentries,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the local files from the tmp folder.
|
// Delete the local files from the tmp folder.
|
||||||
CoreFileUploader.clearTmpFiles(this.attachments);
|
CoreFileUploader.clearTmpFiles(this.data.attachments);
|
||||||
|
|
||||||
if (entryId) {
|
if (entryId) {
|
||||||
// Data sent to server, delete stored files (if any).
|
// Data sent to server, delete stored files (if any).
|
||||||
AddonModGlossaryHelper.deleteStoredFiles(this.glossary.id, this.entry.concept, timecreated);
|
AddonModGlossaryHelper.deleteStoredFiles(this.glossary.id, this.data.concept, timecreated);
|
||||||
CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'glossary' });
|
CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'glossary' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,6 +372,24 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the form data has changed.
|
||||||
|
*
|
||||||
|
* @returns True if data has changed, false otherwise.
|
||||||
|
*/
|
||||||
|
protected hasDataChanged(): boolean {
|
||||||
|
if (!this.originalData || this.originalData.concept === undefined) {
|
||||||
|
// There is no original data.
|
||||||
|
return !!(this.data.definition || this.data.concept || this.data.attachments.length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.originalData.definition != this.data.definition || this.originalData.concept != this.data.concept) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CoreFileUploader.areFileListDifferent(this.data.attachments, this.originalData.attachments);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload entry attachments if any.
|
* Upload entry attachments if any.
|
||||||
*
|
*
|
||||||
|
@ -376,7 +399,7 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
protected async uploadAttachments(
|
protected async uploadAttachments(
|
||||||
timecreated: number,
|
timecreated: number,
|
||||||
): Promise<{saveOffline: boolean; attachmentsResult?: number | CoreFileUploaderStoreFilesResult}> {
|
): Promise<{saveOffline: boolean; attachmentsResult?: number | CoreFileUploaderStoreFilesResult}> {
|
||||||
if (!this.attachments.length || !this.glossary) {
|
if (!this.data.attachments.length || !this.glossary) {
|
||||||
return {
|
return {
|
||||||
saveOffline: false,
|
saveOffline: false,
|
||||||
};
|
};
|
||||||
|
@ -384,7 +407,7 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const attachmentsResult = await CoreFileUploader.uploadOrReuploadFiles(
|
const attachmentsResult = await CoreFileUploader.uploadOrReuploadFiles(
|
||||||
this.attachments,
|
this.data.attachments,
|
||||||
AddonModGlossaryProvider.COMPONENT,
|
AddonModGlossaryProvider.COMPONENT,
|
||||||
this.glossary.id,
|
this.glossary.id,
|
||||||
);
|
);
|
||||||
|
@ -401,9 +424,9 @@ export class AddonModGlossaryEditPage implements OnInit, OnDestroy, CanLeave {
|
||||||
// Cannot upload them in online, save them in offline.
|
// Cannot upload them in online, save them in offline.
|
||||||
const attachmentsResult = await AddonModGlossaryHelper.storeFiles(
|
const attachmentsResult = await AddonModGlossaryHelper.storeFiles(
|
||||||
this.glossary.id,
|
this.glossary.id,
|
||||||
this.entry.concept,
|
this.data.concept,
|
||||||
timecreated,
|
timecreated,
|
||||||
this.attachments,
|
this.data.attachments,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -439,3 +462,18 @@ class AddonModGlossaryEditEntriesSwipeManager extends AddonModGlossaryEntriesSwi
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form data.
|
||||||
|
*/
|
||||||
|
type AddonModGlossaryFormData = {
|
||||||
|
concept: string;
|
||||||
|
definition: string;
|
||||||
|
timecreated: number;
|
||||||
|
attachments: CoreFileEntry[];
|
||||||
|
categories: string[];
|
||||||
|
aliases: string;
|
||||||
|
usedynalink: boolean;
|
||||||
|
casesensitive: boolean;
|
||||||
|
fullmatch: boolean;
|
||||||
|
};
|
||||||
|
|
|
@ -18,7 +18,6 @@ import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fi
|
||||||
import { CoreFile } from '@services/file';
|
import { CoreFile } from '@services/file';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { AddonModGlossaryOffline } from './glossary-offline';
|
import { AddonModGlossaryOffline } from './glossary-offline';
|
||||||
import { AddonModGlossaryNewEntry, AddonModGlossaryNewEntryWithFiles } from './glossary';
|
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
import { CoreFileEntry } from '@services/file-helper';
|
import { CoreFileEntry } from '@services/file-helper';
|
||||||
|
|
||||||
|
@ -58,31 +57,6 @@ export class AddonModGlossaryHelperProvider {
|
||||||
return CoreFileUploader.getStoredFiles(folderPath);
|
return CoreFileUploader.getStoredFiles(folderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the data of an entry has changed.
|
|
||||||
*
|
|
||||||
* @param entry Current data.
|
|
||||||
* @param files Files attached.
|
|
||||||
* @param original Original content.
|
|
||||||
* @returns True if data has changed, false otherwise.
|
|
||||||
*/
|
|
||||||
hasEntryDataChanged(
|
|
||||||
entry: AddonModGlossaryNewEntry,
|
|
||||||
files: CoreFileEntry[],
|
|
||||||
original?: AddonModGlossaryNewEntryWithFiles,
|
|
||||||
): boolean {
|
|
||||||
if (!original || original.concept === undefined) {
|
|
||||||
// There is no original data.
|
|
||||||
return !!(entry.definition || entry.concept || files.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (original.definition != entry.definition || original.concept != entry.concept) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CoreFileUploader.areFileListDifferent(files, original.files);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a list of files (either online files or local files), store the local files in a local folder
|
* Given a list of files (either online files or local files), store the local files in a local folder
|
||||||
* to be submitted later.
|
* to be submitted later.
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { AddonModGlossaryEntryOption, GLOSSARY_ENTRY_ADDED } from './glossary';
|
||||||
export class AddonModGlossaryOfflineProvider {
|
export class AddonModGlossaryOfflineProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a new entry.
|
* Delete an offline entry.
|
||||||
*
|
*
|
||||||
* @param glossaryId Glossary ID.
|
* @param glossaryId Glossary ID.
|
||||||
* @param concept Glossary entry concept.
|
* @param concept Glossary entry concept.
|
||||||
|
@ -39,7 +39,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @returns Promise resolved if deleted, rejected if failure.
|
* @returns Promise resolved if deleted, rejected if failure.
|
||||||
*/
|
*/
|
||||||
async deleteNewEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<void> {
|
async deleteOfflineEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<void> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
const conditions: Partial<AddonModGlossaryOfflineEntryDBRecord> = {
|
const conditions: Partial<AddonModGlossaryOfflineEntryDBRecord> = {
|
||||||
|
@ -52,12 +52,12 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all the stored new entries from all the glossaries.
|
* Get all the stored offline entries from all the glossaries.
|
||||||
*
|
*
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @returns Promise resolved with entries.
|
* @returns Promise resolved with entries.
|
||||||
*/
|
*/
|
||||||
async getAllNewEntries(siteId?: string): Promise<AddonModGlossaryOfflineEntry[]> {
|
async getAllOfflineEntries(siteId?: string): Promise<AddonModGlossaryOfflineEntry[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
const records = await site.getDb().getRecords<AddonModGlossaryOfflineEntryDBRecord>(OFFLINE_ENTRIES_TABLE_NAME);
|
const records = await site.getDb().getRecords<AddonModGlossaryOfflineEntryDBRecord>(OFFLINE_ENTRIES_TABLE_NAME);
|
||||||
|
@ -66,7 +66,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a stored new entry.
|
* Get a stored offline entry.
|
||||||
*
|
*
|
||||||
* @param glossaryId Glossary ID.
|
* @param glossaryId Glossary ID.
|
||||||
* @param concept Glossary entry concept.
|
* @param concept Glossary entry concept.
|
||||||
|
@ -74,7 +74,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @returns Promise resolved with entry.
|
* @returns Promise resolved with entry.
|
||||||
*/
|
*/
|
||||||
async getNewEntry(
|
async getOfflineEntry(
|
||||||
glossaryId: number,
|
glossaryId: number,
|
||||||
concept: string,
|
concept: string,
|
||||||
timeCreated: number,
|
timeCreated: number,
|
||||||
|
@ -101,7 +101,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
* @param userId User the entries belong to. If not defined, current user in site.
|
* @param userId User the entries belong to. If not defined, current user in site.
|
||||||
* @returns Promise resolved with entries.
|
* @returns Promise resolved with entries.
|
||||||
*/
|
*/
|
||||||
async getGlossaryNewEntries(glossaryId: number, siteId?: string, userId?: number): Promise<AddonModGlossaryOfflineEntry[]> {
|
async getGlossaryOfflineEntries(glossaryId: number, siteId?: string, userId?: number): Promise<AddonModGlossaryOfflineEntry[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
const conditions: Partial<AddonModGlossaryOfflineEntryDBRecord> = {
|
const conditions: Partial<AddonModGlossaryOfflineEntryDBRecord> = {
|
||||||
|
@ -144,7 +144,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's only one entry, check that is not the one we are editing.
|
// If there's only one entry, check that is not the one we are editing.
|
||||||
return CoreUtils.promiseFails(this.getNewEntry(glossaryId, concept, timeCreated, siteId));
|
return CoreUtils.promiseFails(this.getOfflineEntry(glossaryId, concept, timeCreated, siteId));
|
||||||
} catch {
|
} catch {
|
||||||
// No offline data found, return false.
|
// No offline data found, return false.
|
||||||
return false;
|
return false;
|
||||||
|
@ -152,7 +152,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a new entry to be sent later.
|
* Save an offline entry to be sent later.
|
||||||
*
|
*
|
||||||
* @param glossaryId Glossary ID.
|
* @param glossaryId Glossary ID.
|
||||||
* @param concept Glossary entry concept.
|
* @param concept Glossary entry concept.
|
||||||
|
@ -166,7 +166,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
* @param discardEntry The entry provided will be discarded if found.
|
* @param discardEntry The entry provided will be discarded if found.
|
||||||
* @returns Promise resolved if stored, rejected if failure.
|
* @returns Promise resolved if stored, rejected if failure.
|
||||||
*/
|
*/
|
||||||
async addNewEntry(
|
async addOfflineEntry(
|
||||||
glossaryId: number,
|
glossaryId: number,
|
||||||
concept: string,
|
concept: string,
|
||||||
definition: string,
|
definition: string,
|
||||||
|
@ -195,7 +195,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
|
|
||||||
// If editing an offline entry, delete previous first.
|
// If editing an offline entry, delete previous first.
|
||||||
if (discardEntry) {
|
if (discardEntry) {
|
||||||
await this.deleteNewEntry(glossaryId, discardEntry.concept, discardEntry.timecreated, site.getId());
|
await this.deleteOfflineEntry(glossaryId, discardEntry.concept, discardEntry.timecreated, site.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
await site.getDb().insertRecord(OFFLINE_ENTRIES_TABLE_NAME, entry);
|
await site.getDb().insertRecord(OFFLINE_ENTRIES_TABLE_NAME, entry);
|
||||||
|
@ -222,7 +222,7 @@ export class AddonModGlossaryOfflineProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the path to the folder where to store files for a new offline entry.
|
* Get the path to the folder where to store files for an offline entry.
|
||||||
*
|
*
|
||||||
* @param glossaryId Glossary ID.
|
* @param glossaryId Glossary ID.
|
||||||
* @param concept The name of the entry.
|
* @param concept The name of the entry.
|
||||||
|
|
|
@ -50,10 +50,9 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv
|
||||||
*
|
*
|
||||||
* @param siteId Site ID to sync. If not defined, sync all sites.
|
* @param siteId Site ID to sync. If not defined, sync all sites.
|
||||||
* @param force Wether to force sync not depending on last execution.
|
* @param force Wether to force sync not depending on last execution.
|
||||||
* @returns Promise resolved if sync is successful, rejected if sync fails.
|
|
||||||
*/
|
*/
|
||||||
syncAllGlossaries(siteId?: string, force?: boolean): Promise<void> {
|
async syncAllGlossaries(siteId?: string, force?: boolean): Promise<void> {
|
||||||
return this.syncOnSites('all glossaries', (siteId) => this.syncAllGlossariesFunc(!!force, siteId), siteId);
|
await this.syncOnSites('all glossaries', (siteId) => this.syncAllGlossariesFunc(!!force, siteId), siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,7 +60,6 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv
|
||||||
*
|
*
|
||||||
* @param force Wether to force sync not depending on last execution.
|
* @param force Wether to force sync not depending on last execution.
|
||||||
* @param siteId Site ID to sync.
|
* @param siteId Site ID to sync.
|
||||||
* @returns Promise resolved if sync is successful, rejected if sync fails.
|
|
||||||
*/
|
*/
|
||||||
protected async syncAllGlossariesFunc(force: boolean, siteId: string): Promise<void> {
|
protected async syncAllGlossariesFunc(force: boolean, siteId: string): Promise<void> {
|
||||||
siteId = siteId || CoreSites.getCurrentSiteId();
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
||||||
|
@ -73,14 +71,13 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sync entried of all glossaries on a site.
|
* Sync entries of all glossaries on a site.
|
||||||
*
|
*
|
||||||
* @param force Wether to force sync not depending on last execution.
|
* @param force Wether to force sync not depending on last execution.
|
||||||
* @param siteId Site ID to sync.
|
* @param siteId Site ID to sync.
|
||||||
* @returns Promise resolved if sync is successful, rejected if sync fails.
|
|
||||||
*/
|
*/
|
||||||
protected async syncAllGlossariesEntries(force: boolean, siteId: string): Promise<void> {
|
protected async syncAllGlossariesEntries(force: boolean, siteId: string): Promise<void> {
|
||||||
const entries = await AddonModGlossaryOffline.getAllNewEntries(siteId);
|
const entries = await AddonModGlossaryOffline.getAllOfflineEntries(siteId);
|
||||||
|
|
||||||
// Do not sync same glossary twice.
|
// Do not sync same glossary twice.
|
||||||
const treated: Record<number, boolean> = {};
|
const treated: Record<number, boolean> = {};
|
||||||
|
@ -180,7 +177,7 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv
|
||||||
|
|
||||||
// Get offline responses to be sent.
|
// Get offline responses to be sent.
|
||||||
const entries = await CoreUtils.ignoreErrors(
|
const entries = await CoreUtils.ignoreErrors(
|
||||||
AddonModGlossaryOffline.getGlossaryNewEntries(glossaryId, siteId, userId),
|
AddonModGlossaryOffline.getGlossaryOfflineEntries(glossaryId, siteId, userId),
|
||||||
<AddonModGlossaryOfflineEntry[]> [],
|
<AddonModGlossaryOfflineEntry[]> [],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -285,11 +282,10 @@ export class AddonModGlossarySyncProvider extends CoreCourseActivitySyncBaseProv
|
||||||
* @param concept Glossary entry concept.
|
* @param concept Glossary entry concept.
|
||||||
* @param timeCreated Time to allow duplicated entries.
|
* @param timeCreated Time to allow duplicated entries.
|
||||||
* @param siteId Site ID. If not defined, current site.
|
* @param siteId Site ID. If not defined, current site.
|
||||||
* @returns Promise resolved when deleted.
|
|
||||||
*/
|
*/
|
||||||
protected async deleteAddEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<void> {
|
protected async deleteAddEntry(glossaryId: number, concept: string, timeCreated: number, siteId?: string): Promise<void> {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
AddonModGlossaryOffline.deleteNewEntry(glossaryId, concept, timeCreated, siteId),
|
AddonModGlossaryOffline.deleteOfflineEntry(glossaryId, concept, timeCreated, siteId),
|
||||||
AddonModGlossaryHelper.deleteStoredFiles(glossaryId, concept, timeCreated, siteId),
|
AddonModGlossaryHelper.deleteStoredFiles(glossaryId, concept, timeCreated, siteId),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,6 @@ import { makeSingleton, Translate } from '@singletons';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { AddonModGlossaryEntryDBRecord, ENTRIES_TABLE_NAME } from './database/glossary';
|
import { AddonModGlossaryEntryDBRecord, ENTRIES_TABLE_NAME } from './database/glossary';
|
||||||
import { AddonModGlossaryOffline } from './glossary-offline';
|
import { AddonModGlossaryOffline } from './glossary-offline';
|
||||||
import { CoreFileEntry } from '@services/file-helper';
|
|
||||||
|
|
||||||
export const GLOSSARY_ENTRY_ADDED = 'addon_mod_glossary_entry_added';
|
export const GLOSSARY_ENTRY_ADDED = 'addon_mod_glossary_entry_added';
|
||||||
export const GLOSSARY_ENTRY_DELETED = 'addon_mod_glossary_entry_deleted';
|
export const GLOSSARY_ENTRY_DELETED = 'addon_mod_glossary_entry_deleted';
|
||||||
|
@ -827,7 +826,7 @@ export class AddonModGlossaryProvider {
|
||||||
throw new CoreError('Error adding entry.');
|
throw new CoreError('Error adding entry.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await AddonModGlossaryOffline.addNewEntry(
|
await AddonModGlossaryOffline.addOfflineEntry(
|
||||||
glossaryId,
|
glossaryId,
|
||||||
concept,
|
concept,
|
||||||
definition,
|
definition,
|
||||||
|
@ -850,7 +849,7 @@ export class AddonModGlossaryProvider {
|
||||||
|
|
||||||
// If we are editing an offline entry, discard previous first.
|
// If we are editing an offline entry, discard previous first.
|
||||||
if (otherOptions.discardEntry) {
|
if (otherOptions.discardEntry) {
|
||||||
await AddonModGlossaryOffline.deleteNewEntry(
|
await AddonModGlossaryOffline.deleteOfflineEntry(
|
||||||
glossaryId,
|
glossaryId,
|
||||||
otherOptions.discardEntry.concept,
|
otherOptions.discardEntry.concept,
|
||||||
otherOptions.discardEntry.timecreated,
|
otherOptions.discardEntry.timecreated,
|
||||||
|
@ -1377,22 +1376,6 @@ export type AddonModGlossaryDiscardedEntry = {
|
||||||
timecreated: number;
|
timecreated: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry to be added.
|
|
||||||
*/
|
|
||||||
export type AddonModGlossaryNewEntry = {
|
|
||||||
concept: string;
|
|
||||||
definition: string;
|
|
||||||
timecreated: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Entry to be added, including attachments.
|
|
||||||
*/
|
|
||||||
export type AddonModGlossaryNewEntryWithFiles = AddonModGlossaryNewEntry & {
|
|
||||||
files: CoreFileEntry[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options to pass to the different get entries functions.
|
* Options to pass to the different get entries functions.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -6,7 +6,7 @@ information provided here is intended especially for developers.
|
||||||
- CoreIconComponent has been removed after deprecation period: Use CoreFaIconDirective instead.
|
- CoreIconComponent has been removed after deprecation period: Use CoreFaIconDirective instead.
|
||||||
- The courseSummaryComponent property has been removed from the CoreCourseFormatComponent component, and the getCourseSummaryComponent method from the CoreCourseFormatHandler interface.
|
- The courseSummaryComponent property has been removed from the CoreCourseFormatComponent component, and the getCourseSummaryComponent method from the CoreCourseFormatHandler interface.
|
||||||
- Font Awesome icon library has been updated to 6.3.0.
|
- Font Awesome icon library has been updated to 6.3.0.
|
||||||
- Some methods in AddonModGlossaryProvider have changed their signatures to remove unused parameters.
|
- Some methods in glossary addon services have changed.
|
||||||
|
|
||||||
=== 4.1.0 ===
|
=== 4.1.0 ===
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue