forked from EVOgeek/Vmeda.Online
		
	MOBILE-2652 glossary: Edit online entries
This commit is contained in:
		
							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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user