Skip to content

Commit

Permalink
Merge pull request #3811 from crazyserver/MOBILE-4348
Browse files Browse the repository at this point in the history
Mobile 4348
  • Loading branch information
dpalou authored Oct 9, 2023
2 parents e171f9b + 60dad4c commit 63bd215
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
<!-- Activity info. -->
<core-course-module-info [module]="module" [courseId]="courseId" [description]="displayDescription && description"
[component]="component" [componentId]="componentId" (completionChanged)="onCompletionChange()">
<div class="addon-mod_resource-afterlink ion-text-wrap" *ngIf="module.afterlink" description>
<core-format-text [text]="module.afterlink" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId">
</core-format-text>
</div>
</core-course-module-info>

<ion-card class="core-warning-card" *ngIf="warning">
Expand Down
4 changes: 4 additions & 0 deletions src/addons/mod/resource/components/index/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@
font-size: 24px;
}
}

.addon-mod_resource-afterlink {
font-size: var(--text-size);
}
}
2 changes: 2 additions & 0 deletions src/addons/mod/resource/components/index/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ export class AddonModResourceIndexComponent extends CoreCourseModuleMainResource
throw new CoreError(Translate.instant('core.filenotfound'));
}

this.module.afterlink = await AddonModResourceHelper.getAfterLinkDetails(this.module, this.courseId);

// Get the resource instance to get the latest name/description and to know if it's embedded.
const resource = await AddonModResource.getResourceData(this.courseId, this.module.id);
this.description = resource.intro || '';
Expand Down
124 changes: 14 additions & 110 deletions src/addons/mod/resource/services/handlers/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
import { CoreFileHelper } from '@services/file-helper';
import { CoreMimetypeUtils } from '@services/utils/mimetype';
import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time';
import { makeSingleton, Translate } from '@singletons';
import { AddonModResourceIndexComponent } from '../../components/index';
import { AddonModResource, AddonModResourceCustomData } from '../resource';
import { AddonModResource } from '../resource';
import { AddonModResourceHelper } from '../resource-helper';
import { CoreUtils } from '@services/utils/utils';

/**
* Handler to support resource modules.
Expand Down Expand Up @@ -94,19 +93,20 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
},
};

this.getResourceData(module, courseId, handlerData).then((extra) => {
handlerData.extraBadge = extra;
const [hideButton, extraBadge] = await Promise.all([
CoreUtils.ignoreErrors(this.hideOpenButton(module)),
CoreUtils.ignoreErrors(AddonModResourceHelper.getAfterLinkDetails(module, courseId)),
]);

return;
}).catch(() => {
// Ignore errors.
});

try {
handlerData.icon = this.getIconSrc(module);
} catch {
// Ignore errors.
// Check if the button needs to be shown or not.
if (hideButton !== undefined) {
handlerData.button.hidden = hideButton;
}
if (extraBadge !== undefined) {
handlerData.extraBadge = extraBadge;
}

handlerData.icon = this.getIconSrc(module);

return handlerData;
}
Expand All @@ -127,102 +127,6 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
return status !== CoreConstants.DOWNLOADED || AddonModResourceHelper.isDisplayedInIframe(module);
}

/**
* Returns the activity icon and data.
*
* @param module The module object.
* @param courseId The course ID.
* @returns Resource data.
*/
protected async getResourceData(
module: CoreCourseModuleData,
courseId: number,
handlerData: CoreCourseModuleHandlerData,
): Promise<string> {
const promises: Promise<void>[] = [];
let options: AddonModResourceCustomData = {};

// Check if the button needs to be shown or not.
promises.push(this.hideOpenButton(module).then((hideOpenButton) => {
if (!handlerData.button) {
return;
}

handlerData.button.hidden = hideOpenButton;

return;
}));

if (module.customdata !== undefined) {
options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata));
} else {
// Get the resource data.
promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => {
options = CoreTextUtils.unserialize(info.displayoptions);

return;
}));
}

await Promise.all(promises);

if (module.contentsinfo) {
// No need to use the list of files.
return CoreTextUtils.cleanTags(module.afterlink);
}

if (!module.contents || !module.contents[0]) {
return '';
}

const extra: string[] = [];
const files = module.contents;
const mainFile = files[0];

if (options.showsize) {
const size = options.filedetails
? options.filedetails.size
: files.reduce((result, file) => result + (file.filesize || 0), 0);

extra.push(CoreTextUtils.bytesToSize(size, 1));
}

if (options.showtype) {
// We should take it from options.filedetails.size if available but it's already translated.
extra.push(CoreMimetypeUtils.getMimetypeDescription(mainFile));
}

if (options.showdate) {
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;

if (options.filedetails && options.filedetails.modifieddate) {
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.modifieddate * 1000, 'core.strftimedatetimeshort') },
));
} else if (options.filedetails && options.filedetails.uploadeddate) {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(options.filedetails.uploadeddate * 1000, 'core.strftimedatetimeshort') },
));
} else if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
/* Modified date may be up to several minutes later than uploaded date just because
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate((mainFile.timemodified || 0) * 1000, 'core.strftimedatetimeshort') },
));
} else {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(timecreated * 1000, 'core.strftimedatetimeshort') },
));
}
}

return extra.join(' · ');
}

/**
* @inheritdoc
*/
Expand Down
115 changes: 114 additions & 1 deletion src/addons/mod/resource/services/resource-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype';
import { CoreUtilsOpenFileOptions } from '@services/utils/utils';
import { makeSingleton, Translate } from '@singletons';
import { CorePath } from '@singletons/path';
import { AddonModResource, AddonModResourceProvider } from './resource';
import { AddonModResource, AddonModResourceCustomData, AddonModResourceProvider } from './resource';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreTextUtils } from '@services/utils/text';
import { CoreTimeUtils } from '@services/utils/time';

/**
* Service that provides helper functions for resources.
Expand Down Expand Up @@ -222,5 +224,116 @@ export class AddonModResourceHelperProvider {
}
}

/**
* Get resource show options.
*
* @param module The module object.
* @param courseId The course ID.
* @returns Resource options.
*/
protected async getModuleOptions(module: CoreCourseModuleData, courseId: number): Promise<AddonModResourceCustomData> {
if (module.customdata !== undefined) {
const customData: { displayoptions: string } | string = CoreTextUtils.parseJSON(module.customdata);
const displayOptions = typeof customData === 'object' ? customData.displayoptions : customData;

return CoreTextUtils.unserialize(displayOptions);
}

// Get the resource data. Legacy version (from 3.5 to 3.6.6)
const info = await AddonModResource.getResourceData(courseId, module.id);
const options: AddonModResourceCustomData = CoreTextUtils.unserialize(info.displayoptions);

if (!module.contents?.[0] || options.filedetails !== undefined) {
// Contents attribute should be loaded at this point and it's needed to get mainFile.
// Filedetails won't be usually loaded, but if it's there's no need to check mainFile.

return options;
}

// Fill filedetails checking files in contents.
options.filedetails = {};

const files = module.contents;
const mainFile = files[0];

if (options.showsize) {
options.filedetails.size = files.reduce((result, file) => result + (file.filesize || 0), 0);
}

if (options.showtype) {
options.filedetails.type = CoreMimetypeUtils.getMimetypeDescription(mainFile);
}

if (options.showdate) {
const timecreated = 'timecreated' in mainFile ? mainFile.timecreated : 0;

if ((mainFile.timemodified || 0) > timecreated + CoreConstants.SECONDS_MINUTE * 5) {
/* Modified date may be up to several minutes later than uploaded date just because
teacher did not submit the form promptly. Give teacher up to 5 minutes to do it. */
options.filedetails.modifieddate = mainFile.timemodified || 0;
} else {
options.filedetails.uploadeddate = timecreated;
}
}

return options;
}

/**
* Get afterlink details to be shown on the activity card.
*
* @param module The module object.
* @param courseId The course ID.
* @returns Description string to be shown on the activity card.
*/
async getAfterLinkDetails(
module: CoreCourseModuleData,
courseId: number,
): Promise<string> {
const options = await this.getModuleOptions(module, courseId);

if (!options.filedetails) {
return '';
}

const details = options.filedetails;

const extra: string[] = [];

if (options.showsize && details.size) {
extra.push(CoreTextUtils.bytesToSize(details.size, 1));
}

if (options.showtype) {
// The order of this if conditions should not be changed.
if (details.extension) {
// From LMS 4.3 onwards only extension is shown.
extra.push(details.extension);
} else if (details.mimetype) {
// Mostly used from 3.7 to 4.2.
extra.push(CoreMimetypeUtils.getMimetypeDescription(details.mimetype));
} else if (details.type) {
// Used on 3.5 and 3.6 where mimetype populated on getModuleOptions using main file.
extra.push(details.type); // Already translated.
}
}

if (options.showdate) {
if (details.modifieddate) {
extra.push(Translate.instant(
'addon.mod_resource.modifieddate',
{ $a: CoreTimeUtils.userDate(details.modifieddate * 1000, 'core.strftimedatetimeshort') },
));
} else if (details.uploadeddate) {
extra.push(Translate.instant(
'addon.mod_resource.uploadeddate',
{ $a: CoreTimeUtils.userDate(details.uploadeddate * 1000, 'core.strftimedatetimeshort') },
));
}
}

return extra.join(' · ');
}

}
export const AddonModResourceHelper = makeSingleton(AddonModResourceHelperProvider);
16 changes: 12 additions & 4 deletions src/addons/mod/resource/services/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,20 @@ export type AddonModResourceResource = {
};

export type AddonModResourceCustomData = {
showsize?: boolean;
filedetails?: {
size: number;
modifieddate: number;
uploadeddate: number;
isref?: boolean; // If file is a reference the 'size' or 'date' attribute can not be cached.
// If showsize is true.
size?: number; // Size in bytes.
// If showtype is true.
type?: string; // Mimetype description (already translated).
mimetype?: string; // @since LMS 3.7
extension?: string; // @since LMS 4.3
// If showdate is true.
modifieddate?: number; // Only if file has been modified.
uploadeddate?: number; // Only if file has NOT been modified.

};
showsize?: boolean;
showtype?: boolean;
showdate?: boolean;
printintro?: boolean;
Expand Down
3 changes: 3 additions & 0 deletions src/theme/theme.base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,9 @@ body.core-iframe-fullscreen ion-router-outlet {
.item-dimmed {
opacity: 0.7;
--background: var(--light);
ion-item {
--background: var(--light);
}
}

// Extra text colors.
Expand Down

0 comments on commit 63bd215

Please sign in to comment.