commit
7cce817d12
|
@ -71,7 +71,7 @@ export class AddonModDataSearchComponent implements OnInit {
|
||||||
this.search.advanced?.forEach((field) => {
|
this.search.advanced?.forEach((field) => {
|
||||||
if (typeof field != 'undefined') {
|
if (typeof field != 'undefined') {
|
||||||
this.advancedIndexed[field.name] = field.value
|
this.advancedIndexed[field.name] = field.value
|
||||||
? CoreTextUtils.parseJSON(field.value)
|
? CoreTextUtils.parseJSON(field.value, '')
|
||||||
: '';
|
: '';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -112,7 +112,8 @@ export class AddonModDataFieldFileHandlerService implements AddonModDataFieldHan
|
||||||
offlineContent: CoreFormFields,
|
offlineContent: CoreFormFields,
|
||||||
offlineFiles?: FileEntry[],
|
offlineFiles?: FileEntry[],
|
||||||
): AddonModDataEntryField {
|
): AddonModDataEntryField {
|
||||||
const uploadedFilesResult: CoreFileUploaderStoreFilesResult = <CoreFileUploaderStoreFilesResult>offlineContent?.file;
|
const uploadedFilesResult: CoreFileUploaderStoreFilesResult | undefined =
|
||||||
|
<CoreFileUploaderStoreFilesResult | undefined> offlineContent?.file;
|
||||||
|
|
||||||
if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) {
|
if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) {
|
||||||
originalContent.content = offlineFiles[0].name;
|
originalContent.content = offlineFiles[0].name;
|
||||||
|
|
|
@ -121,8 +121,8 @@ export class AddonModDataFieldLatlongHandlerService implements AddonModDataField
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields<string>): AddonModDataEntryField {
|
overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields<string>): AddonModDataEntryField {
|
||||||
originalContent.content = offlineContent[0] || '';
|
originalContent.content = offlineContent['0'] || '';
|
||||||
originalContent.content1 = offlineContent[1] || '';
|
originalContent.content1 = offlineContent['1'] || '';
|
||||||
|
|
||||||
return originalContent;
|
return originalContent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,8 @@ export class AddonModDataFieldPictureHandlerService implements AddonModDataField
|
||||||
offlineContent: CoreFormFields,
|
offlineContent: CoreFormFields,
|
||||||
offlineFiles?: FileEntry[],
|
offlineFiles?: FileEntry[],
|
||||||
): AddonModDataEntryField {
|
): AddonModDataEntryField {
|
||||||
const uploadedFilesResult: CoreFileUploaderStoreFilesResult = <CoreFileUploaderStoreFilesResult>offlineContent?.file;
|
const uploadedFilesResult: CoreFileUploaderStoreFilesResult | undefined =
|
||||||
|
<CoreFileUploaderStoreFilesResult | undefined> offlineContent?.file;
|
||||||
|
|
||||||
if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) {
|
if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) {
|
||||||
originalContent.content = offlineFiles[0].name;
|
originalContent.content = offlineFiles[0].name;
|
||||||
|
@ -165,7 +166,7 @@ export class AddonModDataFieldPictureHandlerService implements AddonModDataField
|
||||||
originalContent.files = [uploadedFilesResult.online[0]];
|
originalContent.files = [uploadedFilesResult.online[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
originalContent.content1 = <string>offlineContent.alttext || '';
|
originalContent.content1 = <string> offlineContent.alttext || '';
|
||||||
|
|
||||||
return originalContent;
|
return originalContent;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { AddonModDataField, AddonModDataSubfieldData } from '@addons/mod/data/services/data';
|
import { AddonModDataEntryField, AddonModDataField, AddonModDataSubfieldData } from '@addons/mod/data/services/data';
|
||||||
import { Injectable, Type } from '@angular/core';
|
import { Injectable, Type } from '@angular/core';
|
||||||
import { CoreFormFields } from '@singletons/form';
|
import { CoreFormFields } from '@singletons/form';
|
||||||
import { Translate, makeSingleton } from '@singletons';
|
import { Translate, makeSingleton } from '@singletons';
|
||||||
|
@ -59,5 +59,14 @@ export class AddonModDataFieldUrlHandlerService extends AddonModDataFieldTextHan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields<string>): AddonModDataEntryField {
|
||||||
|
originalContent.content = offlineContent['0'] || '';
|
||||||
|
|
||||||
|
return originalContent;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
export const AddonModDataFieldUrlHandler = makeSingleton(AddonModDataFieldUrlHandlerService);
|
export const AddonModDataFieldUrlHandler = makeSingleton(AddonModDataFieldUrlHandlerService);
|
||||||
|
|
|
@ -92,9 +92,9 @@ export class AddonModDataHelperProvider {
|
||||||
|
|
||||||
if (offlineContent.subfield) {
|
if (offlineContent.subfield) {
|
||||||
offlineContents[offlineContent.fieldid][offlineContent.subfield] =
|
offlineContents[offlineContent.fieldid][offlineContent.subfield] =
|
||||||
CoreTextUtils.parseJSON(offlineContent.value);
|
CoreTextUtils.parseJSON(offlineContent.value, '');
|
||||||
} else {
|
} else {
|
||||||
offlineContents[offlineContent.fieldid][''] = CoreTextUtils.parseJSON(offlineContent.value);
|
offlineContents[offlineContent.fieldid][''] = CoreTextUtils.parseJSON(offlineContent.value, '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -632,7 +632,7 @@ export class AddonModDataHelperProvider {
|
||||||
const folderPath = await AddonModDataOffline.getEntryFieldFolder(dataId, entryId, fieldId, siteId);
|
const folderPath = await AddonModDataOffline.getEntryFieldFolder(dataId, entryId, fieldId, siteId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return CoreFileUploader.getStoredFiles(folderPath);
|
return await CoreFileUploader.getStoredFiles(folderPath);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore not found files.
|
// Ignore not found files.
|
||||||
return [];
|
return [];
|
||||||
|
|
|
@ -88,9 +88,9 @@ export class AddonModDataOfflineProvider {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
|
|
||||||
entry.fields.forEach((field) => {
|
entry.fields.forEach((field) => {
|
||||||
const value = CoreTextUtils.parseJSON<CoreFileUploaderStoreFilesResult>(field.value);
|
const value = CoreTextUtils.parseJSON<CoreFileUploaderStoreFilesResult | null>(field.value, null);
|
||||||
|
|
||||||
if (!value.offline) {
|
if (!value || !value.offline) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -345,8 +345,8 @@ export class AddonModDataSyncProvider extends CoreCourseActivitySyncBaseProvider
|
||||||
try {
|
try {
|
||||||
await Promise.all(editAction.fields.map(async (field) => {
|
await Promise.all(editAction.fields.map(async (field) => {
|
||||||
// Upload Files if asked.
|
// Upload Files if asked.
|
||||||
const value = CoreTextUtils.parseJSON<CoreFileUploaderStoreFilesResult>(field.value || '');
|
const value = CoreTextUtils.parseJSON<CoreFileUploaderStoreFilesResult | null>(field.value || '', null);
|
||||||
if (value.online || value.offline) {
|
if (value && (value.online || value.offline)) {
|
||||||
let files: CoreFileEntry[] = value.online || [];
|
let files: CoreFileEntry[] = value.online || [];
|
||||||
|
|
||||||
const offlineFiles = value.offline
|
const offlineFiles = value.offline
|
||||||
|
|
|
@ -155,7 +155,7 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
|
||||||
<ion-card *ngIf="completed">
|
<ion-card *ngIf="completed && (access!.canviewanalysis || hasNextPage)">
|
||||||
<ion-grid>
|
<ion-grid>
|
||||||
<ion-row class="ion-align-items-center">
|
<ion-row class="ion-align-items-center">
|
||||||
<ion-col *ngIf="access!.canviewanalysis">
|
<ion-col *ngIf="access!.canviewanalysis">
|
||||||
|
|
|
@ -98,7 +98,8 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ion-list>
|
</ion-list>
|
||||||
|
|
||||||
<core-empty-box *ngIf="entries.empty" icon="fas-list" [message]="'addon.mod_glossary.noentriesfound' | translate">
|
<core-empty-box *ngIf="entries.empty && (!isSearch || hasSearched)" icon="fas-list"
|
||||||
|
[message]="'addon.mod_glossary.noentriesfound' | translate">
|
||||||
</core-empty-box>
|
</core-empty-box>
|
||||||
|
|
||||||
<core-infinite-loading [enabled]="!entries.completed" [error]="loadMoreError" (action)="loadMoreEntries($event)">
|
<core-infinite-loading [enabled]="!entries.completed" [error]="loadMoreError" (action)="loadMoreEntries($event)">
|
||||||
|
|
|
@ -64,6 +64,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
moduleName = 'glossary';
|
moduleName = 'glossary';
|
||||||
|
|
||||||
isSearch = false;
|
isSearch = false;
|
||||||
|
hasSearched = false;
|
||||||
canAdd = false;
|
canAdd = false;
|
||||||
loadMoreError = false;
|
loadMoreError = false;
|
||||||
loadingMessage?: string;
|
loadingMessage?: string;
|
||||||
|
@ -429,6 +430,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
toggleSearch(): void {
|
toggleSearch(): void {
|
||||||
if (this.isSearch) {
|
if (this.isSearch) {
|
||||||
this.isSearch = false;
|
this.isSearch = false;
|
||||||
|
this.hasSearched = false;
|
||||||
this.entries.setOnlineEntries(this.fetchedEntries, this.fetchedEntriesCanLoadMore);
|
this.entries.setOnlineEntries(this.fetchedEntries, this.fetchedEntriesCanLoadMore);
|
||||||
this.switchMode(this.fetchMode!);
|
this.switchMode(this.fetchMode!);
|
||||||
} else {
|
} else {
|
||||||
|
@ -488,6 +490,7 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
|
||||||
'ASC',
|
'ASC',
|
||||||
);
|
);
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
this.hasSearched = true;
|
||||||
this.loadContent();
|
this.loadContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { CoreSupressEventsDirective } from './supress-events';
|
||||||
import { CoreUserLinkDirective } from './user-link';
|
import { CoreUserLinkDirective } from './user-link';
|
||||||
import { CoreAriaButtonClickDirective } from './aria-button';
|
import { CoreAriaButtonClickDirective } from './aria-button';
|
||||||
import { CoreOnResizeDirective } from './on-resize';
|
import { CoreOnResizeDirective } from './on-resize';
|
||||||
|
import { CoreDownloadFileDirective } from './download-file';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -41,6 +42,7 @@ import { CoreOnResizeDirective } from './on-resize';
|
||||||
CoreUserLinkDirective,
|
CoreUserLinkDirective,
|
||||||
CoreAriaButtonClickDirective,
|
CoreAriaButtonClickDirective,
|
||||||
CoreOnResizeDirective,
|
CoreOnResizeDirective,
|
||||||
|
CoreDownloadFileDirective,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CoreAutoFocusDirective,
|
CoreAutoFocusDirective,
|
||||||
|
@ -55,6 +57,7 @@ import { CoreOnResizeDirective } from './on-resize';
|
||||||
CoreUserLinkDirective,
|
CoreUserLinkDirective,
|
||||||
CoreAriaButtonClickDirective,
|
CoreAriaButtonClickDirective,
|
||||||
CoreOnResizeDirective,
|
CoreOnResizeDirective,
|
||||||
|
CoreDownloadFileDirective,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class CoreDirectivesModule {}
|
export class CoreDirectivesModule {}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { Directive, Input, OnInit, ElementRef } from '@angular/core';
|
||||||
|
import { CoreFileHelper } from '@services/file-helper';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreWSFile } from '@services/ws';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directive to allow downloading and open a file. When the item with this directive is clicked, the file will be
|
||||||
|
* downloaded (if needed) and opened.
|
||||||
|
*/
|
||||||
|
@Directive({
|
||||||
|
selector: '[core-download-file]',
|
||||||
|
})
|
||||||
|
export class CoreDownloadFileDirective implements OnInit {
|
||||||
|
|
||||||
|
@Input('core-download-file') file?: CoreWSFile; // The file to download.
|
||||||
|
@Input() component?: string; // Component to link the file to.
|
||||||
|
@Input() componentId?: string | number; // Component ID to use in conjunction with the component.
|
||||||
|
|
||||||
|
protected element: HTMLElement;
|
||||||
|
|
||||||
|
constructor(element: ElementRef) {
|
||||||
|
this.element = element.nativeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component being initialized.
|
||||||
|
*/
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.element.addEventListener('click', async (ev: Event) => {
|
||||||
|
if (!this.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
const modal = await CoreDomUtils.showModalLoading();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await CoreFileHelper.downloadAndOpenFile(this.file, this.component, this.componentId);
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -277,7 +277,10 @@ export class CoreUtilsProvider {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(source)) {
|
if (this.valueIsFileEntry(source)) {
|
||||||
|
// Don't clone FileEntry. It has a lot of depth and they shouldn't be modified.
|
||||||
|
return source;
|
||||||
|
} else if (Array.isArray(source)) {
|
||||||
// Clone the array and all the entries.
|
// Clone the array and all the entries.
|
||||||
const newArray = [] as unknown as T;
|
const newArray = [] as unknown as T;
|
||||||
for (let i = 0; i < source.length; i++) {
|
for (let i = 0; i < source.length; i++) {
|
||||||
|
@ -750,6 +753,18 @@ export class CoreUtilsProvider {
|
||||||
return 'isFile' in file;
|
return 'isFile' in file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an unknown value is a FileEntry.
|
||||||
|
*
|
||||||
|
* @param value Value to check.
|
||||||
|
* @return Type guard indicating if the file is a FileEntry.
|
||||||
|
*/
|
||||||
|
valueIsFileEntry(file: unknown): file is FileEntry {
|
||||||
|
// We cannot use instanceof because FileEntry is a type. Check some of the properties.
|
||||||
|
return !!(file && typeof file == 'object' && 'isFile' in file && 'filesystem' in file &&
|
||||||
|
'toInternalURL' in file && 'copyTo' in file);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a value is an object.
|
* Check if a value is an object.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue