MOBILE-2652 glossary: Edit online entries
parent
3c443a26c4
commit
9391fe4122
|
@ -16,11 +16,13 @@ import { Component, OnInit, ViewChild, ElementRef, Optional } from '@angular/cor
|
|||
import { FormControl } from '@angular/forms';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreNetworkError } from '@classes/errors/network-error';
|
||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||
import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader';
|
||||
import { CanLeave } from '@guards/can-leave';
|
||||
import { CoreFileEntry } from '@services/file-helper';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreNetwork } from '@services/network';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
|
@ -31,6 +33,7 @@ import { CoreForms } from '@singletons/form';
|
|||
import {
|
||||
AddonModGlossary,
|
||||
AddonModGlossaryCategory,
|
||||
AddonModGlossaryEntry,
|
||||
AddonModGlossaryEntryOption,
|
||||
AddonModGlossaryGlossary,
|
||||
AddonModGlossaryProvider,
|
||||
|
@ -92,6 +95,11 @@ export class AddonModGlossaryEditPage implements OnInit, CanLeave {
|
|||
const timecreated = Number(entrySlug.slice(4));
|
||||
this.editorExtraParams.timecreated = timecreated;
|
||||
this.handler = new AddonModGlossaryOfflineFormHandler(this, timecreated);
|
||||
} else if (entrySlug) {
|
||||
const { entry } = await AddonModGlossary.getEntry(Number(entrySlug));
|
||||
|
||||
this.editorExtraParams.timecreated = entry.timecreated;
|
||||
this.handler = new AddonModGlossaryOnlineFormHandler(this, entry);
|
||||
} else {
|
||||
this.handler = new AddonModGlossaryNewFormHandler(this);
|
||||
}
|
||||
|
@ -584,6 +592,77 @@ class AddonModGlossaryNewFormHandler extends AddonModGlossaryFormHandler {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to manage the form data for an online entry.
|
||||
*/
|
||||
class AddonModGlossaryOnlineFormHandler extends AddonModGlossaryFormHandler {
|
||||
|
||||
private entry: AddonModGlossaryEntry;
|
||||
|
||||
constructor(page: AddonModGlossaryEditPage, entry: AddonModGlossaryEntry) {
|
||||
super(page);
|
||||
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async loadData(): Promise<void> {
|
||||
const data = this.page.data;
|
||||
|
||||
data.concept = this.entry.concept;
|
||||
data.definition = this.entry.definition || '';
|
||||
data.timecreated = this.entry.timecreated;
|
||||
data.usedynalink = this.entry.usedynalink;
|
||||
|
||||
if (data.usedynalink) {
|
||||
data.casesensitive = this.entry.casesensitive;
|
||||
data.fullmatch = this.entry.fullmatch;
|
||||
}
|
||||
|
||||
// Treat offline attachments if any.
|
||||
if (this.entry.attachments) {
|
||||
data.attachments = this.entry.attachments;
|
||||
}
|
||||
|
||||
this.page.originalData = {
|
||||
concept: data.concept,
|
||||
definition: data.definition,
|
||||
attachments: data.attachments.slice(),
|
||||
timecreated: data.timecreated,
|
||||
categories: data.categories.slice(),
|
||||
aliases: data.aliases,
|
||||
usedynalink: data.usedynalink,
|
||||
casesensitive: data.casesensitive,
|
||||
fullmatch: data.fullmatch,
|
||||
};
|
||||
|
||||
this.page.definitionControl.setValue(data.definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async save(glossary: AddonModGlossaryGlossary): Promise<boolean> {
|
||||
if (!CoreNetwork.isOnline()) {
|
||||
throw new CoreNetworkError();
|
||||
}
|
||||
|
||||
const data = this.page.data;
|
||||
const options = this.getSaveOptions(glossary);
|
||||
const definition = CoreTextUtils.formatHtmlLines(data.definition);
|
||||
|
||||
// Save entry data.
|
||||
await AddonModGlossary.updateEntry(glossary.id, this.entry.id, data.concept, definition, options);
|
||||
|
||||
CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'glossary' });
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Form data.
|
||||
*/
|
||||
|
|
|
@ -240,10 +240,12 @@ export class AddonModGlossaryEntryPage implements OnInit, OnDestroy {
|
|||
try {
|
||||
const result = await AddonModGlossary.getEntry(entryId);
|
||||
const canDeleteEntries = CoreNetwork.isOnline() && await AddonModGlossary.canDeleteEntries();
|
||||
const canUpdateEntries = CoreNetwork.isOnline() && await AddonModGlossary.canUpdateEntries();
|
||||
|
||||
this.onlineEntry = result.entry;
|
||||
this.ratingInfo = result.ratinginfo;
|
||||
this.canDelete = canDeleteEntries && !!result.permissions?.candelete;
|
||||
this.canEdit = canUpdateEntries && !!result.permissions?.canupdate;
|
||||
|
||||
await this.loadGlossary();
|
||||
} catch (error) {
|
||||
|
|
|
@ -619,6 +619,18 @@ export class AddonModGlossaryProvider {
|
|||
return site.wsAvailable('mod_glossary_delete_entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the site can update glossary entries.
|
||||
*
|
||||
* @param siteId Site id.
|
||||
* @returns Whether the site can update entries.
|
||||
*/
|
||||
async canUpdateEntries(siteId?: string): Promise<boolean> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
return site.wsAvailable('mod_glossary_update_entry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the whole fetch of the entries using the proper function and arguments.
|
||||
*
|
||||
|
@ -910,6 +922,43 @@ export class AddonModGlossaryProvider {
|
|||
return response.entryid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing entry on a glossary.
|
||||
*
|
||||
* @param glossaryId Glossary ID.
|
||||
* @param entryId Entry ID.
|
||||
* @param concept Glossary entry concept.
|
||||
* @param definition Glossary entry concept definition.
|
||||
* @param options Options for the entry.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
*/
|
||||
async updateEntry(
|
||||
glossaryId: number,
|
||||
entryId: number,
|
||||
concept: string,
|
||||
definition: string,
|
||||
options?: Record<string, AddonModGlossaryEntryOption>,
|
||||
siteId?: string,
|
||||
): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
const params: AddonModGlossaryUpdateEntryWSParams = {
|
||||
entryid: entryId,
|
||||
concept: concept,
|
||||
definition: definition,
|
||||
definitionformat: 1,
|
||||
options: CoreUtils.objectToArrayOfObjects(options || {}, 'name', 'value'),
|
||||
};
|
||||
|
||||
const response = await site.write<AddonModGlossaryUpdateEntryWSResponse>('mod_glossary_update_entry', params);
|
||||
|
||||
if (!response.result) {
|
||||
throw new CoreError(response.warnings?.[0].message ?? 'Error updating entry');
|
||||
}
|
||||
|
||||
CoreEvents.trigger(GLOSSARY_ENTRY_UPDATED, { glossaryId, entryId }, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete entry.
|
||||
*
|
||||
|
@ -1339,6 +1388,35 @@ export type AddonModGlossaryAddEntryWSResponse = {
|
|||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Params of mod_glossary_update_entry WS.
|
||||
*/
|
||||
export type AddonModGlossaryUpdateEntryWSParams = {
|
||||
entryid: number; // Glossary entry id to update.
|
||||
concept: string; // Glossary concept.
|
||||
definition: string; // Glossary concept definition.
|
||||
definitionformat: number; // Definition format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
|
||||
options?: { // Optional settings.
|
||||
name: string; // The allowed keys (value format) are:
|
||||
// inlineattachmentsid (int); the draft file area id for inline attachments
|
||||
// attachmentsid (int); the draft file area id for attachments
|
||||
// categories (comma separated int); comma separated category ids
|
||||
// aliases (comma separated str); comma separated aliases
|
||||
// usedynalink (bool); whether the entry should be automatically linked.
|
||||
// casesensitive (bool); whether the entry is case sensitive.
|
||||
// fullmatch (bool); whether to match whole words only.
|
||||
value: string | number; // The value of the option (validated inside the function).
|
||||
}[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Data returned by mod_glossary_update_entry WS.
|
||||
*/
|
||||
export type AddonModGlossaryUpdateEntryWSResponse = {
|
||||
result: boolean; // The update result.
|
||||
warnings?: CoreWSExternalWarning[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Params of mod_glossary_view_glossary WS.
|
||||
*/
|
||||
|
|
|
@ -157,7 +157,39 @@ Feature: Test basic usage of glossary in app
|
|||
Scenario: Edit entries (basic info)
|
||||
Given I entered the glossary activity "Test glossary" on course "Course 1" as "student1" in the app
|
||||
|
||||
# TODO online
|
||||
# Online
|
||||
When I press "Add a new entry" in the app
|
||||
And I set the following fields to these values in the app:
|
||||
| Concept | Cashew |
|
||||
| Definition | Cashew is a fruit |
|
||||
And I press "Save" in the app
|
||||
Then I should find "Cashew" in the app
|
||||
|
||||
When I press "Cashew" in the app
|
||||
And I press "Edit entry" in the app
|
||||
Then the field "Concept" matches value "Cashew" in the app
|
||||
And the field "Definition" matches value "Cashew is a fruit" in the app
|
||||
|
||||
When I set the following fields to these values in the app:
|
||||
| Concept | Coconut |
|
||||
| Definition | Coconut is a fruit |
|
||||
And I press "This entry should be automatically linked" "ion-toggle" in the app
|
||||
And I press "This entry is case sensitive" "ion-toggle" in the app
|
||||
And I press "Match whole words only" "ion-toggle" in the app
|
||||
And I press "Save" in the app
|
||||
Then I should find "Coconut is a fruit" in the app
|
||||
But I should not find "Cashew is a fruit" in the app
|
||||
|
||||
When I press "Edit entry" in the app
|
||||
Then "This entry should be automatically linked" "ion-toggle" should be selected in the app
|
||||
And "This entry is case sensitive" "ion-toggle" should be selected in the app
|
||||
And "Match whole words only" "ion-toggle" should be selected in the app
|
||||
|
||||
When I press "Save" in the app
|
||||
And I press the back button in the app
|
||||
Then I should find "Coconut" in the app
|
||||
And I should find "Potato" in the app
|
||||
But I should not find "Cashew" in the app
|
||||
|
||||
# Offline
|
||||
When I press "Add a new entry" in the app
|
||||
|
|
Loading…
Reference in New Issue