218 lines
8.0 KiB
TypeScript
218 lines
8.0 KiB
TypeScript
// (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 { CoreConstants } from '@/core/constants';
|
|
import { Injectable } from '@angular/core';
|
|
import { CoreError } from '@classes/errors/error';
|
|
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
|
|
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
|
|
import { CoreApp } from '@services/app';
|
|
import { CoreFile } from '@services/file';
|
|
import { CoreFileHelper } from '@services/file-helper';
|
|
import { CoreFilepool } from '@services/filepool';
|
|
import { CoreSites } from '@services/sites';
|
|
import { CoreDomUtils } from '@services/utils/dom';
|
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
|
import { CoreUtilsOpenFileOptions } from '@services/utils/utils';
|
|
import { makeSingleton, Translate } from '@singletons';
|
|
import { CoreText } from '@singletons/text';
|
|
import { AddonModResource, AddonModResourceProvider } from './resource';
|
|
|
|
/**
|
|
* Service that provides helper functions for resources.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class AddonModResourceHelperProvider {
|
|
|
|
/**
|
|
* Get the HTML to display an embedded resource.
|
|
*
|
|
* @param module The module object.
|
|
* @return Promise resolved with the HTML.
|
|
*/
|
|
async getEmbeddedHtml(module: CoreCourseModuleData): Promise<string> {
|
|
const contents = await CoreCourse.getModuleContents(module);
|
|
|
|
const result = await CoreCourseHelper.downloadModuleWithMainFileIfNeeded(
|
|
module,
|
|
module.course,
|
|
AddonModResourceProvider.COMPONENT,
|
|
module.id,
|
|
contents,
|
|
);
|
|
|
|
return CoreMimetypeUtils.getEmbeddedHtml(contents[0], result.path);
|
|
}
|
|
|
|
/**
|
|
* Download all the files needed and returns the src of the iframe.
|
|
*
|
|
* @param module The module object.
|
|
* @return Promise resolved with the iframe src.
|
|
*/
|
|
async getIframeSrc(module: CoreCourseModuleData): Promise<string> {
|
|
if (!module.contents?.length) {
|
|
throw new CoreError('No contents available in module');
|
|
}
|
|
|
|
const mainFile = module.contents[0];
|
|
let mainFilePath = mainFile.filename;
|
|
|
|
if (mainFile.filepath !== '/') {
|
|
mainFilePath = mainFile.filepath.substring(1) + mainFilePath;
|
|
}
|
|
|
|
try {
|
|
const dirPath = await CoreFilepool.getPackageDirUrlByUrl(CoreSites.getCurrentSiteId(), module.url!);
|
|
|
|
// This URL is going to be injected in an iframe, we need trustAsResourceUrl to make it work in a browser.
|
|
return CoreText.concatenatePaths(dirPath, mainFilePath);
|
|
} catch (e) {
|
|
// Error getting directory, there was an error downloading or we're in browser. Return online URL.
|
|
if (CoreApp.isOnline() && mainFile.fileurl) {
|
|
// This URL is going to be injected in an iframe, we need this to make it work.
|
|
return CoreSites.getRequiredCurrentSite().checkAndFixPluginfileURL(mainFile.fileurl);
|
|
}
|
|
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the resource has to be displayed embedded.
|
|
*
|
|
* @param module The module object.
|
|
* @param display The display mode (if available).
|
|
* @return Whether the resource should be displayed embeded.
|
|
*/
|
|
isDisplayedEmbedded(module: CoreCourseModuleData, display: number): boolean {
|
|
const currentSite = CoreSites.getCurrentSite();
|
|
|
|
if (!CoreFile.isAvailable() ||
|
|
(currentSite && !currentSite.isVersionGreaterEqualThan('3.7') && this.isNextcloudFile(module))) {
|
|
return false;
|
|
}
|
|
|
|
let ext: string | undefined;
|
|
if (module.contentsinfo) {
|
|
ext = CoreMimetypeUtils.getExtension(module.contentsinfo.mimetypes[0]);
|
|
} else if (module.contents?.length) {
|
|
ext = CoreMimetypeUtils.getFileExtension(module.contents[0].filename);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return (display == CoreConstants.RESOURCELIB_DISPLAY_EMBED || display == CoreConstants.RESOURCELIB_DISPLAY_AUTO) &&
|
|
CoreMimetypeUtils.canBeEmbedded(ext);
|
|
}
|
|
|
|
/**
|
|
* Whether the resource has to be displayed in an iframe.
|
|
*
|
|
* @param module The module object.
|
|
* @return Whether the resource should be displayed in an iframe.
|
|
*/
|
|
isDisplayedInIframe(module: CoreCourseModuleData): boolean {
|
|
if (!CoreFile.isAvailable()) {
|
|
return false;
|
|
}
|
|
|
|
let mimetype: string | undefined;
|
|
|
|
if (module.contentsinfo) {
|
|
mimetype = module.contentsinfo.mimetypes[0];
|
|
} else if (module.contents) {
|
|
const ext = CoreMimetypeUtils.getFileExtension(module.contents[0].filename);
|
|
mimetype = CoreMimetypeUtils.getMimeType(ext);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return mimetype == 'text/html' || mimetype == 'application/xhtml+xml';
|
|
}
|
|
|
|
/**
|
|
* Check if main file of resource is downloadable.
|
|
*
|
|
* @param module Module instance.
|
|
* @param siteId Site ID. If not defined, current site.
|
|
* @return Promise resolved with boolean: whether main file is downloadable.
|
|
*/
|
|
async isMainFileDownloadable(module: CoreCourseModuleData, siteId?: string): Promise<boolean> {
|
|
const contents = await CoreCourse.getModuleContents(module);
|
|
if (!contents.length) {
|
|
throw new CoreError(Translate.instant('core.filenotfound'));
|
|
}
|
|
|
|
siteId = siteId || CoreSites.getCurrentSiteId();
|
|
|
|
const mainFile = contents[0];
|
|
const timemodified = CoreFileHelper.getFileTimemodified(mainFile);
|
|
|
|
return CoreFilepool.isFileDownloadable(siteId, mainFile.fileurl, timemodified);
|
|
}
|
|
|
|
/**
|
|
* Check if the resource is a Nextcloud file.
|
|
*
|
|
* @param module Module to check.
|
|
* @return Whether it's a Nextcloud file.
|
|
*/
|
|
isNextcloudFile(module: CoreCourseAnyModuleData): boolean {
|
|
if ('contentsinfo' in module && module.contentsinfo) {
|
|
return module.contentsinfo.repositorytype == 'nextcloud';
|
|
}
|
|
|
|
return !!(module.contents && module.contents[0] && module.contents[0].repositorytype == 'nextcloud');
|
|
}
|
|
|
|
/**
|
|
* Opens a file of the resource activity.
|
|
*
|
|
* @param module Module where to get the contents.
|
|
* @param courseId Course Id, used for completion purposes.
|
|
* @param options Options to open the file.
|
|
* @return Resolved when done.
|
|
*/
|
|
async openModuleFile(module: CoreCourseModuleData, courseId: number, options: CoreUtilsOpenFileOptions = {}): Promise<void> {
|
|
const modal = await CoreDomUtils.showModalLoading();
|
|
|
|
try {
|
|
// Download and open the file from the resource contents.
|
|
await CoreCourseHelper.downloadModuleAndOpenFile(
|
|
module,
|
|
courseId,
|
|
AddonModResourceProvider.COMPONENT,
|
|
module.id,
|
|
module.contents,
|
|
undefined,
|
|
options,
|
|
);
|
|
|
|
try {
|
|
await AddonModResource.logView(module.instance, module.name);
|
|
CoreCourse.checkModuleCompletion(courseId, module.completiondata);
|
|
} catch {
|
|
// Ignore errors.
|
|
}
|
|
} catch (error) {
|
|
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_resource.errorwhileloadingthecontent', true);
|
|
} finally {
|
|
modal.dismiss();
|
|
}
|
|
}
|
|
|
|
}
|
|
export const AddonModResourceHelper = makeSingleton(AddonModResourceHelperProvider);
|