commit
01726139d3
|
@ -51,8 +51,8 @@ jobs:
|
||||||
echo "Found $found missing langkeys"
|
echo "Found $found missing langkeys"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Run Linter
|
- name: Run Linter (ignore warnings)
|
||||||
run: npm run lint
|
run: npm run lint -- --quiet
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: npm run test:ci
|
run: npm run test:ci
|
||||||
- name: Production builds
|
- name: Production builds
|
||||||
|
|
|
@ -99,9 +99,9 @@ export class AddonBlockActivityModulesComponent extends CoreBlockBaseComponent i
|
||||||
let icon: string;
|
let icon: string;
|
||||||
|
|
||||||
if (modName === 'resources') {
|
if (modName === 'resources') {
|
||||||
icon = CoreCourse.getModuleIconSrc('page', modIcons['page']);
|
icon = await CoreCourse.getModuleIconSrc('page', modIcons['page']);
|
||||||
} else {
|
} else {
|
||||||
icon = CoreCourseModuleDelegate.getModuleIconSrc(modName, modIcons[modName]) || '';
|
icon = await CoreCourseModuleDelegate.getModuleIconSrc(modName, modIcons[modName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.entries.push({
|
this.entries.push({
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
<core-loading [hideUntil]="loaded" [fullscreen]="false" class="margin">
|
<core-loading [hideUntil]="loaded" [fullscreen]="false" class="margin">
|
||||||
<ion-item class="ion-text-wrap item-media" *ngFor="let entry of entries" detail="true" button
|
<ion-item class="ion-text-wrap item-media" *ngFor="let entry of entries" detail="true" button
|
||||||
(click)="gotoCoureListModType(entry)">
|
(click)="gotoCoureListModType(entry)">
|
||||||
<img slot="start" [src]="entry.icon" alt="" role="presentation" class="core-module-icon">
|
<core-mod-icon slot="start" [modicon]="entry.icon" [modname]="entry.modName" [showAlt]="false">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label>{{ entry.name }}</ion-label>
|
<ion-label>{{ entry.name }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</core-loading>
|
</core-loading>
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
<ion-card>
|
<ion-card>
|
||||||
<ion-item class="core-course-module-handler item-media ion-text-wrap" detail="false" (click)="action($event, item)"
|
<ion-item class="core-course-module-handler item-media ion-text-wrap" detail="false" (click)="action($event, item)"
|
||||||
button>
|
button>
|
||||||
<img slot="start" [src]="item.iconUrl" alt="" role="presentation" *ngIf="item.iconUrl" class="core-module-icon">
|
<core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl"
|
||||||
|
[modname]="item.modname" [componentId]="item.cmid" [showAlt]="false">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<!-- Add the icon title so accessibility tools read it. -->
|
<!-- Add the icon title so accessibility tools read it. -->
|
||||||
<span class="sr-only" *ngIf="item.iconTitle">{{ item.iconTitle }}</span>
|
<span class="sr-only" *ngIf="item.iconTitle">{{ item.iconTitle }}</span>
|
||||||
|
|
|
@ -52,14 +52,14 @@ export class AddonBlockRecentlyAccessedItemsProvider {
|
||||||
const items: AddonBlockRecentlyAccessedItemsItem[] =
|
const items: AddonBlockRecentlyAccessedItemsItem[] =
|
||||||
await site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets);
|
await site.read('block_recentlyaccesseditems_get_recent_items', undefined, preSets);
|
||||||
|
|
||||||
return items.map((item) => {
|
return await Promise.all(items.map(async (item) => {
|
||||||
const modicon = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'src');
|
const modicon = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'src');
|
||||||
|
|
||||||
item.iconUrl = CoreCourse.getModuleIconSrc(item.modname, modicon || undefined);
|
item.iconUrl = await CoreCourse.getModuleIconSrc(item.modname, modicon || undefined);
|
||||||
item.iconTitle = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'title');
|
item.iconTitle = item.icon && CoreDomUtils.getHTMLElementAttribute(item.icon, 'title');
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<ng-container *ngFor="let event of dayEvents.events">
|
<ng-container *ngFor="let event of dayEvents.events">
|
||||||
<ion-item class="ion-text-wrap core-course-module-handler item-media" detail="false" (click)="action($event, event.url)"
|
<ion-item class="ion-text-wrap core-course-module-handler item-media" detail="false" (click)="action($event, event.url)"
|
||||||
[attr.aria-label]="event.name" button>
|
[attr.aria-label]="event.name" button>
|
||||||
<img slot="start" [src]="event.iconUrl" alt="" role="presentation" *ngIf="event.iconUrl" class="core-module-icon">
|
<core-mod-icon *ngIf="event.iconUrl" slot="start" [modicon]="event.iconUrl" [componentId]="event.instance"
|
||||||
|
[modname]="event.modulename">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<!-- Add the icon title so accessibility tools read it. -->
|
|
||||||
<span class="sr-only" *ngIf="event.iconTitle">{{ event.iconTitle }}</span>
|
|
||||||
<p class="item-heading">
|
<p class="item-heading">
|
||||||
<core-format-text [text]="event.name" contextLevel="module" [contextInstanceId]="event.id"
|
<core-format-text [text]="event.name" contextLevel="module" [contextInstanceId]="event.id"
|
||||||
[courseId]="event.course && event.course.id">
|
[courseId]="event.course && event.course.id">
|
||||||
|
|
|
@ -51,12 +51,12 @@ export class AddonBlockTimelineEventsComponent implements OnChanges {
|
||||||
/**
|
/**
|
||||||
* Detect changes on input properties.
|
* Detect changes on input properties.
|
||||||
*/
|
*/
|
||||||
ngOnChanges(changes: {[name: string]: SimpleChange}): void {
|
async ngOnChanges(changes: {[name: string]: SimpleChange}): Promise<void> {
|
||||||
this.showCourse = CoreUtils.isTrueOrOne(this.showCourse);
|
this.showCourse = CoreUtils.isTrueOrOne(this.showCourse);
|
||||||
|
|
||||||
if (changes.events || changes.from || changes.to) {
|
if (changes.events || changes.from || changes.to) {
|
||||||
if (this.events && this.events.length > 0) {
|
if (this.events && this.events.length > 0) {
|
||||||
const filteredEvents = this.filterEventsByTime(this.from, this.to);
|
const filteredEvents = await this.filterEventsByTime(this.from, this.to);
|
||||||
this.empty = !filteredEvents || filteredEvents.length <= 0;
|
this.empty = !filteredEvents || filteredEvents.length <= 0;
|
||||||
|
|
||||||
const eventsByDay: Record<number, AddonCalendarEvent[]> = {};
|
const eventsByDay: Record<number, AddonCalendarEvent[]> = {};
|
||||||
|
@ -92,22 +92,23 @@ export class AddonBlockTimelineEventsComponent implements OnChanges {
|
||||||
* @param end Number of days after the start.
|
* @param end Number of days after the start.
|
||||||
* @return Filtered events.
|
* @return Filtered events.
|
||||||
*/
|
*/
|
||||||
protected filterEventsByTime(start: number, end?: number): AddonBlockTimelineEvent[] {
|
protected async filterEventsByTime(start: number, end?: number): Promise<AddonBlockTimelineEvent[]> {
|
||||||
start = moment().add(start, 'days').startOf('day').unix();
|
start = moment().add(start, 'days').startOf('day').unix();
|
||||||
end = typeof end != 'undefined' ? moment().add(end, 'days').startOf('day').unix() : end;
|
end = typeof end != 'undefined' ? moment().add(end, 'days').startOf('day').unix() : end;
|
||||||
|
|
||||||
return this.events.filter((event) => {
|
return await Promise.all(this.events.filter((event) => {
|
||||||
if (end) {
|
if (end) {
|
||||||
return start <= event.timesort && event.timesort < end;
|
return start <= event.timesort && event.timesort < end;
|
||||||
}
|
}
|
||||||
|
|
||||||
return start <= event.timesort;
|
return start <= event.timesort;
|
||||||
}).map((event) => {
|
}).map(async (event) => {
|
||||||
event.iconUrl = CoreCourse.getModuleIconSrc(event.icon.component);
|
event.iconUrl = await CoreCourse.getModuleIconSrc(event.icon.component);
|
||||||
event.iconTitle = event.modulename && CoreCourse.translateModuleName(event.modulename);
|
event.modulename = event.modulename || event.icon.component;
|
||||||
|
event.iconTitle = CoreCourse.translateModuleName(event.modulename);
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> core-mod-icon {
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
&.addon-calendar-eventtype-category > ion-icon {
|
&.addon-calendar-eventtype-category > ion-icon {
|
||||||
background-color: var(--addon-calendar-event-category-color);
|
background-color: var(--addon-calendar-event-category-color);
|
||||||
}
|
}
|
||||||
|
@ -25,4 +29,5 @@
|
||||||
background-color: var(--addon-calendar-event-site-color);
|
background-color: var(--addon-calendar-event-site-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -88,8 +88,9 @@
|
||||||
<span class="addon-calendar-event-time">
|
<span class="addon-calendar-event-time">
|
||||||
{{ event.timestart * 1000 | coreFormatDate: timeFormat }}
|
{{ event.timestart * 1000 | coreFormatDate: timeFormat }}
|
||||||
</span>
|
</span>
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation"
|
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false"
|
||||||
class="core-module-icon">
|
[modname]="event.modulename" [componentId]="event.instance">
|
||||||
|
</core-mod-icon>
|
||||||
<!-- Add the icon title so accessibility tools read it. -->
|
<!-- Add the icon title so accessibility tools read it. -->
|
||||||
<span class="sr-only">
|
<span class="sr-only">
|
||||||
{{ 'addon.calendar.type' + event.formattedType | translate }}
|
{{ 'addon.calendar.type' + event.formattedType | translate }}
|
||||||
|
|
|
@ -144,15 +144,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-module-icon {
|
core-mod-icon {
|
||||||
margin-right: 1px;
|
margin-right: 1px;
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
--size: 16px;
|
--size: 16px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
|
::ng-deep img {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
.core-module-icon[slot="start"] {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,8 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
isCurrentMonth = false;
|
isCurrentMonth = false;
|
||||||
isPastMonth = false;
|
isPastMonth = false;
|
||||||
|
|
||||||
protected year?: number;
|
protected year: number;
|
||||||
protected month?: number;
|
protected month: number;
|
||||||
protected categoriesRetrieved = false;
|
protected categoriesRetrieved = false;
|
||||||
protected categories: { [id: number]: CoreCategoryData } = {};
|
protected categories: { [id: number]: CoreCategoryData } = {};
|
||||||
protected currentSiteId: string;
|
protected currentSiteId: string;
|
||||||
|
@ -121,16 +121,19 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
);
|
);
|
||||||
|
|
||||||
this.differ = differs.find([]).create();
|
this.differ = differs.find([]).create();
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
this.year = now.getFullYear();
|
||||||
|
this.month = now.getMonth() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component loaded.
|
* Component loaded.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const now = new Date();
|
this.year = this.initialYear ? this.initialYear : this.year;
|
||||||
|
this.month = this.initialMonth ? this.initialMonth : this.month;
|
||||||
this.year = this.initialYear ? this.initialYear : now.getFullYear();
|
|
||||||
this.month = this.initialMonth ? this.initialMonth : now.getMonth() + 1;
|
|
||||||
|
|
||||||
this.calculateIsCurrentMonth();
|
this.calculateIsCurrentMonth();
|
||||||
|
|
||||||
|
@ -211,11 +214,11 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
let result: { daynames: Partial<AddonCalendarDayName>[]; weeks: Partial<AddonCalendarWeek>[] };
|
let result: { daynames: Partial<AddonCalendarDayName>[]; weeks: Partial<AddonCalendarWeek>[] };
|
||||||
try {
|
try {
|
||||||
result = await AddonCalendar.getMonthlyEvents(this.year!, this.month!);
|
result = await AddonCalendar.getMonthlyEvents(this.year, this.month);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!CoreApp.isOnline()) {
|
if (!CoreApp.isOnline()) {
|
||||||
// Allow navigating to non-cached months in offline (behave as if using emergency cache).
|
// Allow navigating to non-cached months in offline (behave as if using emergency cache).
|
||||||
result = await AddonCalendarHelper.getOfflineMonthWeeks(this.year!, this.month!);
|
result = await AddonCalendarHelper.getOfflineMonthWeeks(this.year, this.month);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -223,27 +226,29 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
|
|
||||||
// Calculate the period name. We don't use the one in result because it's in server's language.
|
// Calculate the period name. We don't use the one in result because it's in server's language.
|
||||||
this.periodName = CoreTimeUtils.userDate(
|
this.periodName = CoreTimeUtils.userDate(
|
||||||
new Date(this.year!, this.month! - 1).getTime(),
|
new Date(this.year, this.month - 1).getTime(),
|
||||||
'core.strftimemonthyear',
|
'core.strftimemonthyear',
|
||||||
);
|
);
|
||||||
this.weekDays = AddonCalendar.getWeekDays(result.daynames[0].dayno);
|
this.weekDays = AddonCalendar.getWeekDays(result.daynames[0].dayno);
|
||||||
this.weeks = result.weeks as AddonCalendarWeek[];
|
this.weeks = result.weeks as AddonCalendarWeek[];
|
||||||
this.calculateIsCurrentMonth();
|
this.calculateIsCurrentMonth();
|
||||||
|
|
||||||
this.weeks.forEach((week) => {
|
await Promise.all(this.weeks.map(async (week) => {
|
||||||
week.days.forEach((day) => {
|
await Promise.all(week.days.map(async (day) => {
|
||||||
day.periodName = CoreTimeUtils.userDate(
|
day.periodName = CoreTimeUtils.userDate(
|
||||||
new Date(this.year!, this.month! - 1, day.mday).getTime(),
|
new Date(this.year, this.month - 1, day.mday).getTime(),
|
||||||
'core.strftimedaydate',
|
'core.strftimedaydate',
|
||||||
);
|
);
|
||||||
day.eventsFormated = day.eventsFormated || [];
|
day.eventsFormated = day.eventsFormated || [];
|
||||||
day.filteredEvents = day.filteredEvents || [];
|
day.filteredEvents = day.filteredEvents || [];
|
||||||
day.events.forEach((event) => {
|
// Format online events.
|
||||||
/// Format online events.
|
const onlineEventsFormatted = await Promise.all(
|
||||||
day.eventsFormated!.push(AddonCalendarHelper.formatEventData(event));
|
day.events.map(async (event) => AddonCalendarHelper.formatEventData(event)),
|
||||||
});
|
);
|
||||||
});
|
|
||||||
});
|
day.eventsFormated = day.eventsFormated.concat(onlineEventsFormatted);
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
|
||||||
if (this.isCurrentMonth) {
|
if (this.isCurrentMonth) {
|
||||||
const currentDay = new Date().getDate();
|
const currentDay = new Date().getDate();
|
||||||
|
@ -323,7 +328,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
|
|
||||||
// Don't invalidate monthly events after a change, it has already been handled.
|
// Don't invalidate monthly events after a change, it has already been handled.
|
||||||
if (!afterChange) {
|
if (!afterChange) {
|
||||||
promises.push(AddonCalendar.invalidateMonthlyEvents(this.year!, this.month!));
|
promises.push(AddonCalendar.invalidateMonthlyEvents(this.year, this.month));
|
||||||
}
|
}
|
||||||
promises.push(CoreCourses.invalidateCategories(0, true));
|
promises.push(CoreCourses.invalidateCategories(0, true));
|
||||||
promises.push(AddonCalendar.invalidateTimeFormat());
|
promises.push(AddonCalendar.invalidateTimeFormat());
|
||||||
|
@ -386,7 +391,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
* @param day Day.
|
* @param day Day.
|
||||||
*/
|
*/
|
||||||
dayClicked(day: number): void {
|
dayClicked(day: number): void {
|
||||||
this.onDayClicked.emit({ day: day, month: this.month!, year: this.year! });
|
this.onDayClicked.emit({ day: day, month: this.month, year: this.year });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -398,7 +403,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
this.currentTime = CoreTimeUtils.timestamp();
|
this.currentTime = CoreTimeUtils.timestamp();
|
||||||
|
|
||||||
this.isCurrentMonth = this.year == now.getFullYear() && this.month == now.getMonth() + 1;
|
this.isCurrentMonth = this.year == now.getFullYear() && this.month == now.getMonth() + 1;
|
||||||
this.isPastMonth = this.year! < now.getFullYear() || (this.year == now.getFullYear() && this.month! < now.getMonth() + 1);
|
this.isPastMonth = this.year < now.getFullYear() || (this.year == now.getFullYear() && this.month < now.getMonth() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -432,9 +437,9 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
protected decreaseMonth(): void {
|
protected decreaseMonth(): void {
|
||||||
if (this.month === 1) {
|
if (this.month === 1) {
|
||||||
this.month = 12;
|
this.month = 12;
|
||||||
this.year!--;
|
this.year--;
|
||||||
} else {
|
} else {
|
||||||
this.month!--;
|
this.month--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,9 +449,9 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
protected increaseMonth(): void {
|
protected increaseMonth(): void {
|
||||||
if (this.month === 12) {
|
if (this.month === 12) {
|
||||||
this.month = 1;
|
this.month = 1;
|
||||||
this.year!++;
|
this.year++;
|
||||||
} else {
|
} else {
|
||||||
this.month!++;
|
this.month++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,7 +460,7 @@ export class AddonCalendarCalendarComponent implements OnInit, DoCheck, OnDestro
|
||||||
*/
|
*/
|
||||||
protected mergeEvents(): void {
|
protected mergeEvents(): void {
|
||||||
const monthOfflineEvents: { [day: number]: AddonCalendarEventToDisplay[] } =
|
const monthOfflineEvents: { [day: number]: AddonCalendarEventToDisplay[] } =
|
||||||
this.offlineEvents[AddonCalendarHelper.getMonthId(this.year!, this.month!)];
|
this.offlineEvents[AddonCalendarHelper.getMonthId(this.year, this.month)];
|
||||||
|
|
||||||
this.weeks.forEach((week) => {
|
this.weeks.forEach((week) => {
|
||||||
week.days.forEach((day) => {
|
week.days.forEach((day) => {
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<ng-container *ngFor="let event of filteredEvents">
|
<ng-container *ngFor="let event of filteredEvents">
|
||||||
<ion-item class="ion-text-wrap addon-calendar-event" [attr.aria-label]="event.name" (click)="eventClicked(event)" button
|
<ion-item class="ion-text-wrap addon-calendar-event" [attr.aria-label]="event.name" (click)="eventClicked(event)" button
|
||||||
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" detail="true">
|
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" detail="true">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" slot="start" class="core-module-icon" alt=""
|
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [modname]="event.modulename"
|
||||||
role="presentation">
|
[componentId]="event.instance" [showAlt]="false"></core-mod-icon>
|
||||||
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
|
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
|
|
@ -173,7 +173,7 @@ export class AddonCalendarUpcomingEventsComponent implements OnInit, DoCheck, On
|
||||||
async fetchEvents(): Promise<void> {
|
async fetchEvents(): Promise<void> {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
const result = await AddonCalendar.getUpcomingEvents();
|
const result = await AddonCalendar.getUpcomingEvents();
|
||||||
this.onlineEvents = result.events.map((event) => AddonCalendarHelper.formatEventData(event));
|
this.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event)));
|
||||||
// Schedule notifications for the events retrieved.
|
// Schedule notifications for the events retrieved.
|
||||||
AddonCalendar.scheduleEventsNotifications(this.onlineEvents);
|
AddonCalendar.scheduleEventsNotifications(this.onlineEvents);
|
||||||
// Merge the online events with offline data.
|
// Merge the online events with offline data.
|
||||||
|
|
|
@ -61,8 +61,9 @@
|
||||||
<ng-container *ngFor="let event of filteredEvents">
|
<ng-container *ngFor="let event of filteredEvents">
|
||||||
<ion-item class="addon-calendar-event ion-text-wrap" [attr.aria-label]="event.name" (click)="gotoEvent(event.id)"
|
<ion-item class="addon-calendar-event ion-text-wrap" [attr.aria-label]="event.name" (click)="gotoEvent(event.id)"
|
||||||
[class.item-dimmed]="event.ispast" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button detail="true">
|
[class.item-dimmed]="event.ispast" [ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button detail="true">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" slot="start" class="core-module-icon" alt=""
|
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [showAlt]="false"
|
||||||
role="presentation">
|
[modname]="event.modname" [componentId]="event.instance">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
|
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
|
|
@ -333,7 +333,7 @@ export class AddonCalendarDayPage implements OnInit, OnDestroy {
|
||||||
try {
|
try {
|
||||||
// Don't pass courseId and categoryId, we'll filter them locally.
|
// Don't pass courseId and categoryId, we'll filter them locally.
|
||||||
result = await AddonCalendar.getDayEvents(this.year, this.month, this.day);
|
result = await AddonCalendar.getDayEvents(this.year, this.month, this.day);
|
||||||
this.onlineEvents = result.events.map((event) => AddonCalendarHelper.formatEventData(event));
|
this.onlineEvents = await Promise.all(result.events.map((event) => AddonCalendarHelper.formatEventData(event)));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (CoreApp.isOnline()) {
|
if (CoreApp.isOnline()) {
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
||||||
</ion-buttons>
|
</ion-buttons>
|
||||||
<h1 *ngIf="event">
|
<h1 *ngIf="event">
|
||||||
<img *ngIf="event.moduleIcon" src="{{event.moduleIcon}}" alt="" role="presentation" class="core-module-icon">
|
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false"
|
||||||
|
[modname]="event.modulename" [componentId]="event.instance"></core-mod-icon>
|
||||||
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true"></ion-icon>
|
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true"></ion-icon>
|
||||||
<!-- Add the icon title so accessibility tools read it. -->
|
<!-- Add the icon title so accessibility tools read it. -->
|
||||||
<span class="sr-only">
|
<span class="sr-only">
|
||||||
|
|
|
@ -207,7 +207,7 @@ export class AddonCalendarEventPage implements OnInit, OnDestroy {
|
||||||
try {
|
try {
|
||||||
// Get the event data.
|
// Get the event data.
|
||||||
const event = await AddonCalendar.getEventById(this.eventId);
|
const event = await AddonCalendar.getEventById(this.eventId);
|
||||||
this.event = AddonCalendarHelper.formatEventData(event);
|
this.event = await AddonCalendarHelper.formatEventData(event);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const offlineEvent = AddonCalendarHelper.formatOfflineEventData(
|
const offlineEvent = AddonCalendarHelper.formatOfflineEventData(
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
ion-card ion-note {
|
ion-card ion-note {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
}
|
}
|
||||||
h1 ion-icon, h1 img {
|
h1 ion-icon, h1 img, h1 core-mod-icon {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,7 +161,9 @@ export class AddonCalendarHelperProvider {
|
||||||
*
|
*
|
||||||
* @param event Event to format.
|
* @param event Event to format.
|
||||||
*/
|
*/
|
||||||
formatEventData(event: AddonCalendarEvent | AddonCalendarEventBase | AddonCalendarGetEventsEvent): AddonCalendarEventToDisplay {
|
async formatEventData(
|
||||||
|
event: AddonCalendarEvent | AddonCalendarEventBase | AddonCalendarGetEventsEvent,
|
||||||
|
): Promise<AddonCalendarEventToDisplay> {
|
||||||
|
|
||||||
const eventFormatted: AddonCalendarEventToDisplay = {
|
const eventFormatted: AddonCalendarEventToDisplay = {
|
||||||
...event,
|
...event,
|
||||||
|
@ -176,7 +178,7 @@ export class AddonCalendarHelperProvider {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (event.modulename) {
|
if (event.modulename) {
|
||||||
eventFormatted.eventIcon = CoreCourse.getModuleIconSrc(event.modulename);
|
eventFormatted.eventIcon = await CoreCourse.getModuleIconSrc(event.modulename);
|
||||||
eventFormatted.moduleIcon = eventFormatted.eventIcon;
|
eventFormatted.moduleIcon = eventFormatted.eventIcon;
|
||||||
eventFormatted.iconTitle = CoreCourse.translateModuleName(event.modulename);
|
eventFormatted.iconTitle = CoreCourse.translateModuleName(event.modulename);
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,7 @@
|
||||||
</p>
|
</p>
|
||||||
<ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url"
|
<ion-item class="ion-text-wrap" *ngFor="let activity of coursemodules" [href]="activity.url"
|
||||||
[attr.aria-label]="activity.name" core-link capture="true">
|
[attr.aria-label]="activity.name" core-link capture="true">
|
||||||
<img slot="start" core-external-content [src]="activity.iconurl" alt="" *ngIf="activity.iconurl"
|
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl"></core-mod-icon>
|
||||||
class="core-module-icon">
|
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
||||||
[courseId]="courseId">
|
[courseId]="courseId">
|
||||||
|
|
|
@ -114,8 +114,8 @@
|
||||||
</p>
|
</p>
|
||||||
<ion-item class="ion-text-wrap core-course-module-handler item-media" [attr.aria-label]="activity.name"
|
<ion-item class="ion-text-wrap core-course-module-handler item-media" [attr.aria-label]="activity.name"
|
||||||
core-link *ngFor="let activity of competency.coursemodules" [href]="activity.url" capture="true">
|
core-link *ngFor="let activity of competency.coursemodules" [href]="activity.url" capture="true">
|
||||||
<img slot="start" [src]="activity.iconurl" core-external-content alt=""
|
<core-mod-icon slot="start" [modicon]="activity.iconurl" [showAlt]="false" *ngIf="activity.iconurl">
|
||||||
*ngIf="activity.iconurl" class="core-module-icon">
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
<core-format-text [text]="activity.name" contextLevel="module" [contextInstanceId]="activity.id"
|
||||||
[courseId]="courseId">
|
[courseId]="courseId">
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
|
|
||||||
import { Injectable, Type } from '@angular/core';
|
import { Injectable, Type } from '@angular/core';
|
||||||
import { AddonModForum, AddonModForumProvider } from '../forum';
|
import { AddonModForum, AddonModForumProvider } from '../forum';
|
||||||
import { CoreCourseAnyModuleData } from '@features/course/services/course';
|
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
|
@ -23,6 +22,7 @@ import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { AddonModForumIndexComponent } from '../../components/index';
|
import { AddonModForumIndexComponent } from '../../components/index';
|
||||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||||
|
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler to support forum modules.
|
* Handler to support forum modules.
|
||||||
|
@ -53,8 +53,8 @@ export class AddonModForumModuleHandlerService extends CoreModuleHandlerBase imp
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(module: CoreCourseAnyModuleData, courseId: number): CoreCourseModuleHandlerData {
|
async getData(module: CoreCourseModule, courseId: number): Promise<CoreCourseModuleHandlerData> {
|
||||||
const data = super.getData(module, courseId);
|
const data = await super.getData(module, courseId);
|
||||||
|
|
||||||
if ('afterlink' in module && !!module.afterlink) {
|
if ('afterlink' in module && !!module.afterlink) {
|
||||||
data.extraBadgeColor = '';
|
data.extraBadgeColor = '';
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||||
import { CoreCourseWSModule } from '@features/course/services/course';
|
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ export class AddonModLabelModuleHandlerService extends CoreModuleHandlerBase imp
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(module: CoreCourseWSModule): CoreCourseModuleHandlerData {
|
async getData(module: CoreCourseModule): Promise<CoreCourseModuleHandlerData> {
|
||||||
// Remove the description from the module so it isn't rendered twice.
|
// Remove the description from the module so it isn't rendered twice.
|
||||||
const title = module.description || '';
|
const title = module.description || '';
|
||||||
module.description = '';
|
module.description = '';
|
||||||
|
|
|
@ -16,15 +16,9 @@ import { Injectable, Type } from '@angular/core';
|
||||||
|
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||||
import { CoreCourseAnyModuleData } from '@features/course/services/course';
|
|
||||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||||
import { CoreApp } from '@services/app';
|
import { makeSingleton } from '@singletons';
|
||||||
import { CoreFilepool } from '@services/filepool';
|
|
||||||
import { CoreSites } from '@services/sites';
|
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
|
||||||
import { DomSanitizer, makeSingleton } from '@singletons';
|
|
||||||
import { AddonModLtiHelper } from '../lti-helper';
|
import { AddonModLtiHelper } from '../lti-helper';
|
||||||
import { AddonModLti, AddonModLtiProvider } from '../lti';
|
|
||||||
import { AddonModLtiIndexComponent } from '../../components/index';
|
import { AddonModLtiIndexComponent } from '../../components/index';
|
||||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||||
|
|
||||||
|
@ -54,15 +48,18 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(
|
async getData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number,
|
courseId: number,
|
||||||
sectionId?: number,
|
sectionId?: number,
|
||||||
forCoursePage?: boolean,
|
forCoursePage?: boolean,
|
||||||
): CoreCourseModuleHandlerData {
|
): Promise<CoreCourseModuleHandlerData> {
|
||||||
const data = super.getData(module, courseId, sectionId, forCoursePage);
|
const data = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||||
data.showDownloadButton = false;
|
data.showDownloadButton = false;
|
||||||
|
|
||||||
|
// Handle custom icons.
|
||||||
|
data.icon = module.modicon;
|
||||||
|
|
||||||
data.buttons = [{
|
data.buttons = [{
|
||||||
icon: 'fas-external-link-alt',
|
icon: 'fas-external-link-alt',
|
||||||
label: 'addon.mod_lti.launchactivity',
|
label: 'addon.mod_lti.launchactivity',
|
||||||
|
@ -72,49 +69,9 @@ export class AddonModLtiModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
},
|
},
|
||||||
}];
|
}];
|
||||||
|
|
||||||
// Handle custom icons.
|
|
||||||
CoreUtils.ignoreErrors(this.loadCustomIcon(module, courseId, data));
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the custom icon.
|
|
||||||
*
|
|
||||||
* @param module Module.
|
|
||||||
* @param courseId Course ID.
|
|
||||||
* @param data Handler data.
|
|
||||||
* @return Promise resolved when done.
|
|
||||||
*/
|
|
||||||
protected async loadCustomIcon(
|
|
||||||
module: CoreCourseAnyModuleData,
|
|
||||||
courseId: number,
|
|
||||||
handlerData: CoreCourseModuleHandlerData,
|
|
||||||
): Promise<void> {
|
|
||||||
const lti = await AddonModLti.getLti(courseId, module.id);
|
|
||||||
|
|
||||||
const icon = lti.secureicon || lti.icon;
|
|
||||||
if (!icon) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const siteId = CoreSites.getCurrentSiteId();
|
|
||||||
|
|
||||||
try {
|
|
||||||
await CoreFilepool.downloadUrl(siteId, icon, false, AddonModLtiProvider.COMPONENT, module.id);
|
|
||||||
|
|
||||||
// Get the internal URL.
|
|
||||||
const url = await CoreFilepool.getSrcByUrl(siteId, icon, AddonModLtiProvider.COMPONENT, module.id);
|
|
||||||
|
|
||||||
handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(url);
|
|
||||||
} catch {
|
|
||||||
// Error downloading. If we're online we'll set the online url.
|
|
||||||
if (CoreApp.isOnline()) {
|
|
||||||
handlerData.icon = DomSanitizer.bypassSecurityTrustUrl(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { CoreConstants } from '@/core/constants';
|
import { CoreConstants } from '@/core/constants';
|
||||||
import { Injectable, Type } from '@angular/core';
|
import { Injectable, Type } from '@angular/core';
|
||||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||||
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
|
||||||
|
@ -23,7 +23,6 @@ import { CoreFileHelper } from '@services/file-helper';
|
||||||
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
import { CoreMimetypeUtils } from '@services/utils/mimetype';
|
||||||
import { CoreTextUtils } from '@services/utils/text';
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
import { CoreTimeUtils } from '@services/utils/time';
|
import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { CoreWSFile } from '@services/ws';
|
|
||||||
import { makeSingleton, Translate } from '@singletons';
|
import { makeSingleton, Translate } from '@singletons';
|
||||||
import { AddonModResourceIndexComponent } from '../../components/index';
|
import { AddonModResourceIndexComponent } from '../../components/index';
|
||||||
import { AddonModResource, AddonModResourceCustomData } from '../resource';
|
import { AddonModResource, AddonModResourceCustomData } from '../resource';
|
||||||
|
@ -63,12 +62,12 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(
|
async getData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number,
|
courseId: number,
|
||||||
sectionId?: number,
|
sectionId?: number,
|
||||||
forCoursePage?: boolean,
|
forCoursePage?: boolean,
|
||||||
): CoreCourseModuleHandlerData {
|
): Promise<CoreCourseModuleHandlerData> {
|
||||||
const updateStatus = (status: string): void => {
|
const updateStatus = (status: string): void => {
|
||||||
if (!handlerData.buttons) {
|
if (!handlerData.buttons) {
|
||||||
return;
|
return;
|
||||||
|
@ -79,7 +78,7 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
};
|
};
|
||||||
const openWithPicker = CoreFileHelper.defaultIsOpenWithPicker();
|
const openWithPicker = CoreFileHelper.defaultIsOpenWithPicker();
|
||||||
|
|
||||||
const handlerData = super.getData(module, courseId, sectionId, forCoursePage);
|
const handlerData = await super.getData(module, courseId, sectionId, forCoursePage);
|
||||||
handlerData.updateStatus = updateStatus.bind(this);
|
handlerData.updateStatus = updateStatus.bind(this);
|
||||||
handlerData.buttons = [{
|
handlerData.buttons = [{
|
||||||
hidden: true,
|
hidden: true,
|
||||||
|
@ -113,7 +112,7 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
* @param courseId The course ID.
|
* @param courseId The course ID.
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async hideOpenButton(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
|
protected async hideOpenButton(module: CoreCourseModule, courseId: number): Promise<boolean> {
|
||||||
if (!('contentsinfo' in module) || !module.contentsinfo) {
|
if (!('contentsinfo' in module) || !module.contentsinfo) {
|
||||||
await CoreCourse.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName);
|
await CoreCourse.loadModuleContents(module, courseId, undefined, false, false, undefined, this.modName);
|
||||||
}
|
}
|
||||||
|
@ -131,12 +130,11 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
* @return Resource data.
|
* @return Resource data.
|
||||||
*/
|
*/
|
||||||
protected async getResourceData(
|
protected async getResourceData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number,
|
courseId: number,
|
||||||
handlerData: CoreCourseModuleHandlerData,
|
handlerData: CoreCourseModuleHandlerData,
|
||||||
): Promise<AddonResourceHandlerData> {
|
): Promise<AddonResourceHandlerData> {
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
let infoFiles: CoreWSFile[] = [];
|
|
||||||
let options: AddonModResourceCustomData = {};
|
let options: AddonModResourceCustomData = {};
|
||||||
|
|
||||||
// Check if the button needs to be shown or not.
|
// Check if the button needs to be shown or not.
|
||||||
|
@ -150,12 +148,11 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
return;
|
return;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if ('customdata' in module && typeof module.customdata != 'undefined') {
|
if ('customdata' in module && module.customdata !== undefined) {
|
||||||
options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata));
|
options = CoreTextUtils.unserialize(CoreTextUtils.parseJSON(module.customdata));
|
||||||
} else {
|
} else {
|
||||||
// Get the resource data.
|
// Get the resource data.
|
||||||
promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => {
|
promises.push(AddonModResource.getResourceData(courseId, module.id).then((info) => {
|
||||||
infoFiles = info.contentfiles;
|
|
||||||
options = CoreTextUtils.unserialize(info.displayoptions);
|
options = CoreTextUtils.unserialize(info.displayoptions);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -164,28 +161,22 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
const files: (CoreCourseModuleContentFile | CoreWSFile)[] = module.contents && module.contents.length
|
let mimetypeIcon = '';
|
||||||
? module.contents
|
|
||||||
: infoFiles;
|
|
||||||
|
|
||||||
const resourceData: AddonResourceHandlerData = {
|
|
||||||
icon: '',
|
|
||||||
extra: '',
|
|
||||||
};
|
|
||||||
const extra: string[] = [];
|
const extra: string[] = [];
|
||||||
|
|
||||||
if ('contentsinfo' in module && module.contentsinfo) {
|
if ('contentsinfo' in module && module.contentsinfo) {
|
||||||
// No need to use the list of files.
|
// No need to use the list of files.
|
||||||
const mimetype = module.contentsinfo.mimetypes[0];
|
const mimetype = module.contentsinfo.mimetypes[0];
|
||||||
if (mimetype) {
|
if (mimetype) {
|
||||||
resourceData.icon = CoreMimetypeUtils.getMimetypeIcon(mimetype);
|
mimetypeIcon = CoreMimetypeUtils.getMimetypeIcon(mimetype);
|
||||||
}
|
}
|
||||||
resourceData.extra = CoreTextUtils.cleanTags(module.afterlink);
|
extra.push(CoreTextUtils.cleanTags(module.afterlink));
|
||||||
|
|
||||||
} else if (files && files.length) {
|
} else if (module.contents && module.contents[0]) {
|
||||||
|
const files = module.contents;
|
||||||
const file = files[0];
|
const file = files[0];
|
||||||
|
|
||||||
resourceData.icon = CoreMimetypeUtils.getFileIcon(file.filename || '');
|
mimetypeIcon = CoreMimetypeUtils.getFileIcon(file.filename || '');
|
||||||
|
|
||||||
if (options.showsize) {
|
if (options.showsize) {
|
||||||
const size = options.filedetails
|
const size = options.filedetails
|
||||||
|
@ -227,16 +218,12 @@ export class AddonModResourceModuleHandlerService extends CoreModuleHandlerBase
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceData.extra += extra.join(' ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No previously set, just set the icon.
|
return {
|
||||||
if (resourceData.icon == '') {
|
icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon, mimetypeIcon),
|
||||||
resourceData.icon = CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined);
|
extra: extra.join(' '),
|
||||||
}
|
};
|
||||||
|
|
||||||
return resourceData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { CoreConstants } from '@/core/constants';
|
||||||
import { Injectable, Type } from '@angular/core';
|
import { Injectable, Type } from '@angular/core';
|
||||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
||||||
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
|
||||||
import { CoreCourse, CoreCourseAnyModuleData } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
import { CoreCourseModule } from '@features/course/services/course-helper';
|
import { CoreCourseModule } from '@features/course/services/course-helper';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
|
||||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||||
|
@ -54,7 +54,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(module: CoreCourseAnyModuleData, courseId: number): CoreCourseModuleHandlerData {
|
async getData(module: CoreCourseModule, courseId: number): Promise<CoreCourseModuleHandlerData> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the URL.
|
* Open the URL.
|
||||||
|
@ -77,7 +77,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlerData: CoreCourseModuleHandlerData = {
|
const handlerData: CoreCourseModuleHandlerData = {
|
||||||
icon: CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined),
|
icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon),
|
||||||
title: module.name,
|
title: module.name,
|
||||||
class: 'addon-mod_url-handler',
|
class: 'addon-mod_url-handler',
|
||||||
showDownloadButton: false,
|
showDownloadButton: false,
|
||||||
|
@ -111,7 +111,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
|
||||||
this.hideLinkButton(module, courseId).then((hideButton) => {
|
this.hideLinkButton(module, courseId).then(async (hideButton) => {
|
||||||
if (!handlerData.buttons) {
|
if (!handlerData.buttons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -119,9 +119,10 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
handlerData.buttons[0].hidden = hideButton;
|
handlerData.buttons[0].hidden = hideButton;
|
||||||
|
|
||||||
if (module.contents && module.contents[0]) {
|
if (module.contents && module.contents[0]) {
|
||||||
|
const icon = AddonModUrl.guessIcon(module.contents[0].fileurl);
|
||||||
|
|
||||||
// Calculate the icon to use.
|
// Calculate the icon to use.
|
||||||
handlerData.icon = AddonModUrl.guessIcon(module.contents[0].fileurl) ||
|
handlerData.icon = await CoreCourse.getModuleIconSrc(module.modname, module.modicon, icon);
|
||||||
CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -139,7 +140,7 @@ export class AddonModUrlModuleHandlerService extends CoreModuleHandlerBase imple
|
||||||
* @param courseId The course ID.
|
* @param courseId The course ID.
|
||||||
* @return Resolved when done.
|
* @return Resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async hideLinkButton(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
|
protected async hideLinkButton(module: CoreCourseModule, courseId: number): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const contents = await CoreCourse.getModuleContents(module, courseId, undefined, false, false, undefined, this.modName);
|
const contents = await CoreCourse.getModuleContents(module, courseId, undefined, false, false, undefined, this.modName);
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,9 @@
|
||||||
<ion-card-content>
|
<ion-card-content>
|
||||||
<ng-container *ngFor="let module of section.modules">
|
<ng-container *ngFor="let module of section.modules">
|
||||||
<ion-item class="ion-no-padding" *ngIf="module.totalSize! > 0">
|
<ion-item class="ion-no-padding" *ngIf="module.totalSize! > 0">
|
||||||
<img *ngIf="module.handlerData!.icon" [src]="module.handlerData!.icon" [alt]="module.modNameTranslated"
|
<core-mod-icon slot="start" *ngIf="module.handlerData!.icon"
|
||||||
class="core-module-icon" slot="start">
|
[modicon]="module.handlerData!.icon" [modname]="module.modname" [componentId]="module.instance">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label class="ion-text-wrap">
|
<ion-label class="ion-text-wrap">
|
||||||
<h3 class="{{module.handlerData!.class}} addon-storagemanager-module-size">
|
<h3 class="{{module.handlerData!.class}} addon-storagemanager-module-size">
|
||||||
{{ module.name }}
|
{{ module.name }}
|
||||||
|
|
|
@ -63,7 +63,6 @@ export class AddonStorageManagerCourseStoragePage implements OnInit {
|
||||||
section.modules.forEach((module) => {
|
section.modules.forEach((module) => {
|
||||||
module.parentSection = section;
|
module.parentSection = section;
|
||||||
module.totalSize = 0;
|
module.totalSize = 0;
|
||||||
module.modNameTranslated = CoreCourse.translateModuleName(module.modname) || '';
|
|
||||||
|
|
||||||
// Note: This function only gets the size for modules which are downloadable.
|
// Note: This function only gets the size for modules which are downloadable.
|
||||||
// For other modules it always returns 0, even if they have downloaded some files.
|
// For other modules it always returns 0, even if they have downloaded some files.
|
||||||
|
@ -235,5 +234,4 @@ type AddonStorageManagerCourseSection = Omit<CoreCourseSection, 'modules'> & {
|
||||||
type AddonStorageManagerModule = CoreCourseModule & {
|
type AddonStorageManagerModule = CoreCourseModule & {
|
||||||
parentSection?: AddonStorageManagerCourseSection;
|
parentSection?: AddonStorageManagerCourseSection;
|
||||||
totalSize?: number;
|
totalSize?: number;
|
||||||
modNameTranslated?: string;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,6 +40,7 @@ import { CoreInputErrorsComponent } from './input-errors/input-errors';
|
||||||
import { CoreLoadingComponent } from './loading/loading';
|
import { CoreLoadingComponent } from './loading/loading';
|
||||||
import { CoreLocalFileComponent } from './local-file/local-file';
|
import { CoreLocalFileComponent } from './local-file/local-file';
|
||||||
import { CoreMarkRequiredComponent } from './mark-required/mark-required';
|
import { CoreMarkRequiredComponent } from './mark-required/mark-required';
|
||||||
|
import { CoreModIconComponent } from './mod-icon/mod-icon';
|
||||||
import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons';
|
import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons';
|
||||||
import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar';
|
import { CoreNavigationBarComponent } from './navigation-bar/navigation-bar';
|
||||||
import { CoreProgressBarComponent } from './progress-bar/progress-bar';
|
import { CoreProgressBarComponent } from './progress-bar/progress-bar';
|
||||||
|
@ -81,6 +82,7 @@ import { CoreButtonWithSpinnerComponent } from './button-with-spinner/button-wit
|
||||||
CoreLoadingComponent,
|
CoreLoadingComponent,
|
||||||
CoreLocalFileComponent,
|
CoreLocalFileComponent,
|
||||||
CoreMarkRequiredComponent,
|
CoreMarkRequiredComponent,
|
||||||
|
CoreModIconComponent,
|
||||||
CoreNavBarButtonsComponent,
|
CoreNavBarButtonsComponent,
|
||||||
CoreNavigationBarComponent,
|
CoreNavigationBarComponent,
|
||||||
CoreProgressBarComponent,
|
CoreProgressBarComponent,
|
||||||
|
@ -128,6 +130,7 @@ import { CoreButtonWithSpinnerComponent } from './button-with-spinner/button-wit
|
||||||
CoreLoadingComponent,
|
CoreLoadingComponent,
|
||||||
CoreLocalFileComponent,
|
CoreLocalFileComponent,
|
||||||
CoreMarkRequiredComponent,
|
CoreMarkRequiredComponent,
|
||||||
|
CoreModIconComponent,
|
||||||
CoreNavBarButtonsComponent,
|
CoreNavBarButtonsComponent,
|
||||||
CoreNavigationBarComponent,
|
CoreNavigationBarComponent,
|
||||||
CoreProgressBarComponent,
|
CoreProgressBarComponent,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<img
|
||||||
|
*ngIf="!isLocalUrl"
|
||||||
|
[src]="icon"
|
||||||
|
[alt]="showAlt ? modNameTranslated : ''"
|
||||||
|
[attr.role]="!showAlt ? 'presentation' : null"
|
||||||
|
class="core-module-icon"
|
||||||
|
core-external-content
|
||||||
|
[component]="linkIconWithComponent ? modname : null"
|
||||||
|
[componentId]="linkIconWithComponent ? componentId : null"
|
||||||
|
(error)="loadFallbackIcon()"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
*ngIf="isLocalUrl"
|
||||||
|
[src]="icon"
|
||||||
|
[alt]="showAlt ? modNameTranslated : ''"
|
||||||
|
[attr.role]="!showAlt ? 'presentation' : null"
|
||||||
|
class="core-module-icon"
|
||||||
|
(error)="loadFallbackIcon()"
|
||||||
|
>
|
|
@ -0,0 +1,37 @@
|
||||||
|
:host {
|
||||||
|
--size: var(--module-icon-size);
|
||||||
|
--margin-end: 0px;
|
||||||
|
--margin-vertical: 0px;
|
||||||
|
|
||||||
|
margin-top: var(--margin-vertical);
|
||||||
|
margin-bottom: var(--margin-vertical);
|
||||||
|
margin-right: var(--margin-end);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: var(--size);
|
||||||
|
height: var(--size);
|
||||||
|
max-width: var(--size);
|
||||||
|
max-height: var(--size);
|
||||||
|
|
||||||
|
&[alt] {
|
||||||
|
text-indent: -999999px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(ion-item) {
|
||||||
|
--margin-vertical: 12px;
|
||||||
|
--margin-end: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context(ion-card ion-item) {
|
||||||
|
--margin-vertical: 12px;
|
||||||
|
--margin-end: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host-context([dir=rtl]) {
|
||||||
|
margin-right: unset;
|
||||||
|
margin-left: var(--margin-end);
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// (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 { Component, Input, OnChanges, OnInit, SimpleChange } from '@angular/core';
|
||||||
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
|
|
||||||
|
const assetsPath = 'assets/img/mod/';
|
||||||
|
const fallbackModName = 'external-tool';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to handle a module icon.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-mod-icon',
|
||||||
|
templateUrl: 'mod-icon.html',
|
||||||
|
styleUrls: ['mod-icon.scss'],
|
||||||
|
})
|
||||||
|
export class CoreModIconComponent implements OnInit, OnChanges {
|
||||||
|
|
||||||
|
@Input() modname?; // The module name. Used also as component if set.
|
||||||
|
@Input() componentId?; // Component Id for external icons.
|
||||||
|
@Input() modicon?: string; // Module icon url or local url.
|
||||||
|
@Input() showAlt = true; // Show alt otherwise it's only presentation icon.
|
||||||
|
|
||||||
|
icon = '';
|
||||||
|
modNameTranslated = '';
|
||||||
|
isLocalUrl = true;
|
||||||
|
linkIconWithComponent = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async ngOnInit(): Promise<void> {
|
||||||
|
this.modNameTranslated = this.modname ? CoreCourse.translateModuleName(this.modname) || '' : '';
|
||||||
|
|
||||||
|
this.setIcon();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
||||||
|
if (changes && changes.modicon && changes.modicon.previousValue) {
|
||||||
|
this.setIcon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set icon.
|
||||||
|
*/
|
||||||
|
setIcon(): void {
|
||||||
|
this.icon = this.modicon || this.icon;
|
||||||
|
this.isLocalUrl = this.icon.startsWith(assetsPath);
|
||||||
|
|
||||||
|
// Cache icon if the url is not the theme generic one.
|
||||||
|
// If modname is not set icon won't be cached.
|
||||||
|
// Also if the url matches the regexp (the theme will manage the image so it's not cached).
|
||||||
|
this.linkIconWithComponent =
|
||||||
|
this.modname &&
|
||||||
|
this.componentId &&
|
||||||
|
!this.isLocalUrl &&
|
||||||
|
!this.icon.match('/theme/image.php/[^/]+/' + this.modname + '/[-0-9]*/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon to load on error.
|
||||||
|
*/
|
||||||
|
loadFallbackIcon(): void {
|
||||||
|
this.isLocalUrl = true;
|
||||||
|
const moduleName = !this.modname || CoreCourse.CORE_MODULES.indexOf(this.modname) < 0
|
||||||
|
? fallbackModName
|
||||||
|
: this.modname;
|
||||||
|
|
||||||
|
this.icon = assetsPath + moduleName + '.svg';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -79,3 +79,8 @@
|
||||||
left: 0;
|
left: 0;
|
||||||
right: unset;
|
right: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host-context(ion-item) {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
|
@ -113,8 +113,7 @@ export class CoreExternalContentDirective implements AfterViewInit, OnChanges {
|
||||||
* Get the URL that should be handled and, if valid, handle it.
|
* Get the URL that should be handled and, if valid, handle it.
|
||||||
*/
|
*/
|
||||||
protected async checkAndHandleExternalContent(): Promise<void> {
|
protected async checkAndHandleExternalContent(): Promise<void> {
|
||||||
const currentSite = CoreSites.getCurrentSite();
|
const siteId = this.siteId || CoreSites.getRequiredCurrentSite().getId();
|
||||||
const siteId = this.siteId || currentSite?.getId();
|
|
||||||
const tagName = this.element.tagName.toUpperCase();
|
const tagName = this.element.tagName.toUpperCase();
|
||||||
let targetAttr;
|
let targetAttr;
|
||||||
let url;
|
let url;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||||
import { CoreCourse, CoreCourseAnyModuleData } from '../services/course';
|
import { CoreCourse } from '../services/course';
|
||||||
import { CoreCourseModule } from '../services/course-helper';
|
import { CoreCourseModule } from '../services/course-helper';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../services/module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../services/module-delegate';
|
||||||
|
|
||||||
|
@ -34,14 +34,14 @@ export class CoreModuleHandlerBase implements Partial<CoreCourseModuleHandler> {
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(
|
async getData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number, // eslint-disable-line @typescript-eslint/no-unused-vars
|
courseId: number, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
sectionId?: number, // eslint-disable-line @typescript-eslint/no-unused-vars
|
sectionId?: number, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars
|
forCoursePage?: boolean, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
): CoreCourseModuleHandlerData {
|
): Promise<CoreCourseModuleHandlerData> {
|
||||||
return {
|
return {
|
||||||
icon: CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined),
|
icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon),
|
||||||
title: module.name,
|
title: module.name,
|
||||||
class: 'addon-mod_' + module.modname + '-handler',
|
class: 'addon-mod_' + module.modname + '-handler',
|
||||||
showDownloadButton: true,
|
showDownloadButton: true,
|
||||||
|
|
|
@ -13,8 +13,9 @@
|
||||||
[button]="module.handlerData.action && module.uservisible"
|
[button]="module.handlerData.action && module.uservisible"
|
||||||
detail="false">
|
detail="false">
|
||||||
|
|
||||||
<img slot="start" *ngIf="module.handlerData.icon" [src]="module.handlerData.icon" [alt]="modNameTranslated"
|
<core-mod-icon slot="start" *ngIf="module.handlerData.icon" [modicon]="module.handlerData.icon" [modname]="module.modname"
|
||||||
class="core-module-icon">
|
[componentId]="module.instance">
|
||||||
|
</core-mod-icon>
|
||||||
|
|
||||||
<ion-label class="core-module-title">
|
<ion-label class="core-module-title">
|
||||||
<p class="item-heading">
|
<p class="item-heading">
|
||||||
|
|
|
@ -89,16 +89,16 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
const course = CoreNavigator.getRouteParam<CoreCourseAnyCourseData>('course');
|
|
||||||
|
|
||||||
if (!course) {
|
try {
|
||||||
CoreDomUtils.showErrorModal('Missing required course parameter.');
|
this.course = CoreNavigator.getRequiredRouteParam<CoreCourseAnyCourseData>('course');
|
||||||
|
} catch (error) {
|
||||||
|
CoreDomUtils.showErrorModal(error);
|
||||||
CoreNavigator.back();
|
CoreNavigator.back();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.course = course;
|
|
||||||
this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
|
this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
|
||||||
this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
|
this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
|
||||||
this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
|
this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
|
||||||
|
|
|
@ -190,8 +190,8 @@ export class CoreCourseHelperProvider {
|
||||||
|
|
||||||
hasContent = true;
|
hasContent = true;
|
||||||
|
|
||||||
section.modules.forEach((module) => {
|
section.modules.forEach(async (module) => {
|
||||||
module.handlerData = CoreCourseModuleDelegate.getModuleDataFor(
|
module.handlerData = await CoreCourseModuleDelegate.getModuleDataFor(
|
||||||
module.modname,
|
module.modname,
|
||||||
module,
|
module,
|
||||||
courseId,
|
courseId,
|
||||||
|
@ -1610,7 +1610,7 @@ export class CoreCourseHelperProvider {
|
||||||
|
|
||||||
if (CoreSites.getCurrentSiteId() == site.getId()) {
|
if (CoreSites.getCurrentSiteId() == site.getId()) {
|
||||||
// Try to use the module's handler to navigate cleanly.
|
// Try to use the module's handler to navigate cleanly.
|
||||||
module.handlerData = CoreCourseModuleDelegate.getModuleDataFor(
|
module.handlerData = await CoreCourseModuleDelegate.getModuleDataFor(
|
||||||
module.modname,
|
module.modname,
|
||||||
module,
|
module,
|
||||||
courseId,
|
courseId,
|
||||||
|
@ -1664,9 +1664,9 @@ export class CoreCourseHelperProvider {
|
||||||
* @param modParams Params to pass to the module
|
* @param modParams Params to pass to the module
|
||||||
* @param True if module can be opened, false otherwise.
|
* @param True if module can be opened, false otherwise.
|
||||||
*/
|
*/
|
||||||
openModule(module: CoreCourseModule, courseId: number, sectionId?: number, modParams?: Params): boolean {
|
async openModule(module: CoreCourseModule, courseId: number, sectionId?: number, modParams?: Params): Promise<boolean> {
|
||||||
if (!module.handlerData) {
|
if (!module.handlerData) {
|
||||||
module.handlerData = CoreCourseModuleDelegate.getModuleDataFor(
|
module.handlerData = await CoreCourseModuleDelegate.getModuleDataFor(
|
||||||
module.modname,
|
module.modname,
|
||||||
module,
|
module,
|
||||||
courseId,
|
courseId,
|
||||||
|
|
|
@ -83,7 +83,7 @@ export class CoreCourseProvider {
|
||||||
|
|
||||||
static readonly COMPONENT = 'CoreCourse';
|
static readonly COMPONENT = 'CoreCourse';
|
||||||
|
|
||||||
protected readonly CORE_MODULES = [
|
readonly CORE_MODULES = [
|
||||||
'assign', 'assignment', 'book', 'chat', 'choice', 'data', 'database', 'date', 'external-tool',
|
'assign', 'assignment', 'book', 'chat', 'choice', 'data', 'database', 'date', 'external-tool',
|
||||||
'feedback', 'file', 'folder', 'forum', 'glossary', 'ims', 'imscp', 'label', 'lesson', 'lti', 'page', 'quiz',
|
'feedback', 'file', 'folder', 'forum', 'glossary', 'ims', 'imscp', 'label', 'lesson', 'lti', 'page', 'quiz',
|
||||||
'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop', 'h5pactivity',
|
'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop', 'h5pactivity',
|
||||||
|
@ -402,15 +402,16 @@ export class CoreCourseProvider {
|
||||||
): Promise<CoreCourseWSSection[]> => {
|
): Promise<CoreCourseWSSection[]> => {
|
||||||
const params: CoreCourseGetContentsParams = {
|
const params: CoreCourseGetContentsParams = {
|
||||||
courseid: courseId!,
|
courseid: courseId!,
|
||||||
options: [],
|
|
||||||
};
|
};
|
||||||
|
params.options = [];
|
||||||
|
|
||||||
const preSets: CoreSiteWSPreSets = {
|
const preSets: CoreSiteWSPreSets = {
|
||||||
omitExpires: preferCache,
|
omitExpires: preferCache,
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeStealth) {
|
if (includeStealth) {
|
||||||
params.options!.push({
|
params.options.push({
|
||||||
name: 'includestealthmodules',
|
name: 'includestealthmodules',
|
||||||
value: true,
|
value: true,
|
||||||
});
|
});
|
||||||
|
@ -418,13 +419,13 @@ export class CoreCourseProvider {
|
||||||
|
|
||||||
// If modName is set, retrieve all modules of that type. Otherwise get only the module.
|
// If modName is set, retrieve all modules of that type. Otherwise get only the module.
|
||||||
if (modName) {
|
if (modName) {
|
||||||
params.options!.push({
|
params.options.push({
|
||||||
name: 'modname',
|
name: 'modname',
|
||||||
value: modName,
|
value: modName,
|
||||||
});
|
});
|
||||||
preSets.cacheKey = this.getModuleByModNameCacheKey(modName);
|
preSets.cacheKey = this.getModuleByModNameCacheKey(modName);
|
||||||
} else {
|
} else {
|
||||||
params.options!.push({
|
params.options.push({
|
||||||
name: 'cmid',
|
name: 'cmid',
|
||||||
value: moduleId,
|
value: moduleId,
|
||||||
});
|
});
|
||||||
|
@ -520,7 +521,7 @@ export class CoreCourseProvider {
|
||||||
const params: CoreCourseGetCourseModuleWSParams = {
|
const params: CoreCourseGetCourseModuleWSParams = {
|
||||||
cmid: moduleId,
|
cmid: moduleId,
|
||||||
};
|
};
|
||||||
const preSets = {
|
const preSets: CoreSiteWSPreSets = {
|
||||||
cacheKey: this.getModuleCacheKey(moduleId),
|
cacheKey: this.getModuleCacheKey(moduleId),
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
};
|
};
|
||||||
|
@ -528,11 +529,9 @@ export class CoreCourseProvider {
|
||||||
|
|
||||||
if (response.warnings && response.warnings.length) {
|
if (response.warnings && response.warnings.length) {
|
||||||
throw new CoreWSError(response.warnings[0]);
|
throw new CoreWSError(response.warnings[0]);
|
||||||
} else if (response.cm) {
|
|
||||||
return response.cm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Error('WS core_course_get_course_module failed.');
|
return response.cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -632,7 +631,11 @@ export class CoreCourseProvider {
|
||||||
* @param modicon The mod icon string to use in case we are not using a core activity.
|
* @param modicon The mod icon string to use in case we are not using a core activity.
|
||||||
* @return The IMG src.
|
* @return The IMG src.
|
||||||
*/
|
*/
|
||||||
getModuleIconSrc(moduleName: string, modicon?: string): string {
|
async getModuleIconSrc(moduleName: string, modicon?: string, mimetypeIcon = ''): Promise<string> {
|
||||||
|
if (mimetypeIcon) {
|
||||||
|
return mimetypeIcon;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.CORE_MODULES.indexOf(moduleName) < 0) {
|
if (this.CORE_MODULES.indexOf(moduleName) < 0) {
|
||||||
if (modicon) {
|
if (modicon) {
|
||||||
return modicon;
|
return modicon;
|
||||||
|
@ -1491,7 +1494,7 @@ export type CoreCourseWSModule = {
|
||||||
label: string;
|
label: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}[]; // @since 3.11. Activity dates.
|
}[]; // @since 3.11. Activity dates.
|
||||||
contentsinfo?: { // Contents summary information.
|
contentsinfo?: { // @since v3.7.6 Contents summary information.
|
||||||
filescount: number; // Total number of files.
|
filescount: number; // Total number of files.
|
||||||
filessize: number; // Total files size.
|
filessize: number; // Total files size.
|
||||||
lastmodified: number; // Last time files were modified.
|
lastmodified: number; // Last time files were modified.
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { Injectable, Type } from '@angular/core';
|
||||||
|
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../module-delegate';
|
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '../module-delegate';
|
||||||
import { CoreCourse, CoreCourseAnyModuleData } from '../course';
|
import { CoreCourse } from '../course';
|
||||||
import { CoreCourseModule } from '../course-helper';
|
import { CoreCourseModule } from '../course-helper';
|
||||||
import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module';
|
import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module';
|
||||||
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
|
||||||
|
@ -41,12 +41,12 @@ export class CoreCourseModuleDefaultHandler implements CoreCourseModuleHandler {
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
getData(
|
async getData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
): CoreCourseModuleHandlerData {
|
): Promise<CoreCourseModuleHandlerData> {
|
||||||
// Return the default data.
|
// Return the default data.
|
||||||
const defaultData: CoreCourseModuleHandlerData = {
|
const defaultData: CoreCourseModuleHandlerData = {
|
||||||
icon: CoreCourse.getModuleIconSrc(module.modname, 'modicon' in module ? module.modicon : undefined),
|
icon: await CoreCourse.getModuleIconSrc(module.modname, module.modicon),
|
||||||
title: module.name,
|
title: module.name,
|
||||||
class: 'core-course-default-handler core-course-module-' + module.modname + '-handler',
|
class: 'core-course-default-handler core-course-module-' + module.modname + '-handler',
|
||||||
action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => {
|
action: (event: Event, module: CoreCourseModule, courseId: number, options?: CoreNavigationOptions) => {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import { CoreSite } from '@classes/site';
|
||||||
import { CoreCourseModuleDefaultHandler } from './handlers/default-module';
|
import { CoreCourseModuleDefaultHandler } from './handlers/default-module';
|
||||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
|
||||||
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseWSModule } from './course';
|
import { CoreCourse, CoreCourseWSModule } from './course';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
import { CoreCourseModule } from './course-helper';
|
import { CoreCourseModule } from './course-helper';
|
||||||
|
@ -52,11 +52,11 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
|
||||||
* @return Data to render the module.
|
* @return Data to render the module.
|
||||||
*/
|
*/
|
||||||
getData(
|
getData(
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number,
|
courseId: number,
|
||||||
sectionId?: number,
|
sectionId?: number,
|
||||||
forCoursePage?: boolean,
|
forCoursePage?: boolean,
|
||||||
): CoreCourseModuleHandlerData;
|
): Promise<CoreCourseModuleHandlerData> | CoreCourseModuleHandlerData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the component to render the module. This is needed to support singleactivity course format.
|
* Get the component to render the module. This is needed to support singleactivity course format.
|
||||||
|
@ -82,7 +82,7 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
|
||||||
*
|
*
|
||||||
* @return The icon src.
|
* @return The icon src.
|
||||||
*/
|
*/
|
||||||
getIconSrc?(): string | undefined;
|
getIconSrc?(module: CoreCourseWSModule): Promise<string> | string | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this type of module supports a certain feature.
|
* Check if this type of module supports a certain feature.
|
||||||
|
@ -277,14 +277,14 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu
|
||||||
* @param forCoursePage Whether the data will be used to render the course page.
|
* @param forCoursePage Whether the data will be used to render the course page.
|
||||||
* @return Data to render the module.
|
* @return Data to render the module.
|
||||||
*/
|
*/
|
||||||
getModuleDataFor(
|
async getModuleDataFor(
|
||||||
modname: string,
|
modname: string,
|
||||||
module: CoreCourseAnyModuleData,
|
module: CoreCourseModule,
|
||||||
courseId: number,
|
courseId: number,
|
||||||
sectionId?: number,
|
sectionId?: number,
|
||||||
forCoursePage?: boolean,
|
forCoursePage?: boolean,
|
||||||
): CoreCourseModuleHandlerData | undefined {
|
): Promise<CoreCourseModuleHandlerData | undefined> {
|
||||||
return this.executeFunctionOnEnabled<CoreCourseModuleHandlerData>(
|
return await this.executeFunctionOnEnabled<CoreCourseModuleHandlerData>(
|
||||||
modname,
|
modname,
|
||||||
'getData',
|
'getData',
|
||||||
[module, courseId, sectionId, forCoursePage],
|
[module, courseId, sectionId, forCoursePage],
|
||||||
|
@ -343,12 +343,12 @@ export class CoreCourseModuleDelegateService extends CoreDelegate<CoreCourseModu
|
||||||
*
|
*
|
||||||
* @param modname The name of the module type.
|
* @param modname The name of the module type.
|
||||||
* @param modicon The mod icon string.
|
* @param modicon The mod icon string.
|
||||||
* @return The icon src.
|
* @return Promise resolved with the icon src.
|
||||||
*/
|
*/
|
||||||
getModuleIconSrc(modname: string, modicon?: string): string {
|
async getModuleIconSrc(modname: string, modicon?: string): Promise<string> {
|
||||||
return this.executeFunctionOnEnabled<string>(modname, 'getIconSrc') ||
|
const icon = await this.executeFunctionOnEnabled<Promise<string>>(modname, 'getIconSrc');
|
||||||
CoreCourse.getModuleIconSrc(modname, modicon) ||
|
|
||||||
'';
|
return icon || await CoreCourse.getModuleIconSrc(modname, modicon) || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -52,8 +52,11 @@
|
||||||
>
|
>
|
||||||
<ion-icon *ngIf="row.icon" name="{{row.icon}}" slot="start" [attr.aria-label]="row.iconAlt">
|
<ion-icon *ngIf="row.icon" name="{{row.icon}}" slot="start" [attr.aria-label]="row.iconAlt">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
<img *ngIf="row.image" [src]="row.image" slot="start" class="core-module-icon"
|
<img *ngIf="row.image && !row.itemmodule" [src]="row.image" slot="start" class="core-module-icon"
|
||||||
[alt]="row.iconAlt"/>
|
[alt]="row.iconAlt"/>
|
||||||
|
<core-mod-icon *ngIf="row.image && row.itemmodule" [modicon]="row.image" slot="start"
|
||||||
|
[modname]="row.itemmodule">
|
||||||
|
</core-mod-icon>
|
||||||
<span [innerHTML]="row.gradeitem"></span>
|
<span [innerHTML]="row.gradeitem"></span>
|
||||||
</th>
|
</th>
|
||||||
<ng-container *ngFor="let column of grades.columns">
|
<ng-container *ngFor="let column of grades.columns">
|
||||||
|
|
|
@ -114,7 +114,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
|
||||||
* Update the table of grades.
|
* Update the table of grades.
|
||||||
*/
|
*/
|
||||||
private async fetchGrades(): Promise<void> {
|
private async fetchGrades(): Promise<void> {
|
||||||
const table = await CoreGrades.getCourseGradesTable(this.grades.courseId!, this.grades.userId);
|
const table = await CoreGrades.getCourseGradesTable(this.grades.courseId, this.grades.userId);
|
||||||
const formattedTable = await CoreGradesHelper.formatGradesTable(table);
|
const formattedTable = await CoreGradesHelper.formatGradesTable(table);
|
||||||
|
|
||||||
this.grades.setTable(formattedTable);
|
this.grades.setTable(formattedTable);
|
||||||
|
@ -192,7 +192,7 @@ class CoreGradesCourseManager extends CorePageItemsListManager<CoreGradesFormatt
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
protected async logActivity(): Promise<void> {
|
protected async logActivity(): Promise<void> {
|
||||||
await CoreGrades.logCourseGradesView(this.courseId!, this.userId!);
|
await CoreGrades.logCourseGradesView(this.courseId, this.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -82,6 +82,11 @@
|
||||||
height: 16px;
|
height: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
core-mod-icon {
|
||||||
|
--size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ion-icon {
|
ion-icon {
|
||||||
color: var(--icon-color);
|
color: var(--icon-color);
|
||||||
}
|
}
|
||||||
|
@ -119,6 +124,11 @@
|
||||||
background-color: var(--cell-hover);
|
background-color: var(--cell-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
height: var(--a11y-min-target-size);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
<ion-item *ngIf="grade.itemname && grade.link" class="ion-text-wrap" detail="true" [href]="grade.link" core-link
|
<ion-item *ngIf="grade.itemname && grade.link" class="ion-text-wrap" detail="true" [href]="grade.link" core-link
|
||||||
capture="true">
|
capture="true">
|
||||||
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon>
|
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon>
|
||||||
<img *ngIf="grade.image" [src]="grade.image" slot="start" class="core-module-icon"
|
<img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image && grade.itemmodule" slot="start" [alt]="grade.iconAlt"/>
|
||||||
[alt]="grade.iconAlt">
|
<core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
|
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
|
||||||
</core-format-text></h2>
|
</core-format-text></h2>
|
||||||
|
@ -27,7 +28,9 @@
|
||||||
|
|
||||||
<ion-item *ngIf="grade.itemname && !grade.link" class="ion-text-wrap" >
|
<ion-item *ngIf="grade.itemname && !grade.link" class="ion-text-wrap" >
|
||||||
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon>
|
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="start" [attr.aria-label]="grade.iconAlt"></ion-icon>
|
||||||
<img *ngIf="grade.image" [src]="grade.image" slot="start" class="core-module-icon" [alt]="grade.iconAlt"/>
|
<img *ngIf="grade.image && !grade.itemmodule" [src]="grade.image" slot="start" [alt]="grade.iconAlt"/>
|
||||||
|
<core-mod-icon *ngIf="grade.image && grade.itemmodule" [modicon]="grade.image" slot="start" [modname]="grade.itemmodule">
|
||||||
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
|
<h2><core-format-text [text]="grade.itemname" contextLevel="course" [contextInstanceId]="courseId">
|
||||||
</core-format-text></h2>
|
</core-format-text></h2>
|
||||||
|
|
|
@ -54,7 +54,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param tableRow JSON object representing row of grades table data.
|
* @param tableRow JSON object representing row of grades table data.
|
||||||
* @return Formatted row object.
|
* @return Formatted row object.
|
||||||
*/
|
*/
|
||||||
protected formatGradeRow(tableRow: CoreGradesTableRow): CoreGradesFormattedRow {
|
protected async formatGradeRow(tableRow: CoreGradesTableRow): Promise<CoreGradesFormattedRow> {
|
||||||
const row: CoreGradesFormattedRow = {
|
const row: CoreGradesFormattedRow = {
|
||||||
rowclass: '',
|
rowclass: '',
|
||||||
};
|
};
|
||||||
|
@ -68,7 +68,7 @@ export class CoreGradesHelperProvider {
|
||||||
let content = String(column.content);
|
let content = String(column.content);
|
||||||
|
|
||||||
if (name == 'itemname') {
|
if (name == 'itemname') {
|
||||||
this.setRowIcon(row, content);
|
await this.setRowIcon(row, content);
|
||||||
row.link = this.getModuleLink(content);
|
row.link = this.getModuleLink(content);
|
||||||
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||||
row.rowclass += column.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
row.rowclass += column.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
||||||
|
@ -95,7 +95,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param tableRow JSON object representing row of grades table data.
|
* @param tableRow JSON object representing row of grades table data.
|
||||||
* @return Formatted row object.
|
* @return Formatted row object.
|
||||||
*/
|
*/
|
||||||
protected formatGradeRowForTable(tableRow: CoreGradesTableRow): CoreGradesFormattedTableRow {
|
protected async formatGradeRowForTable(tableRow: CoreGradesTableRow): Promise<CoreGradesFormattedTableRow> {
|
||||||
const row: CoreGradesFormattedTableRow = {};
|
const row: CoreGradesFormattedTableRow = {};
|
||||||
for (let name in tableRow) {
|
for (let name in tableRow) {
|
||||||
const column: CoreGradesTableColumn = tableRow[name];
|
const column: CoreGradesTableColumn = tableRow[name];
|
||||||
|
@ -113,7 +113,7 @@ export class CoreGradesHelperProvider {
|
||||||
row.colspan = itemNameColumn.colspan;
|
row.colspan = itemNameColumn.colspan;
|
||||||
row.rowspan = tableRow.leader?.rowspan || 1;
|
row.rowspan = tableRow.leader?.rowspan || 1;
|
||||||
|
|
||||||
this.setRowIcon(row, content);
|
await this.setRowIcon(row, content);
|
||||||
row.rowclass = itemNameColumn.class.indexOf('leveleven') < 0 ? 'odd' : 'even';
|
row.rowclass = itemNameColumn.class.indexOf('leveleven') < 0 ? 'odd' : 'even';
|
||||||
row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : '';
|
||||||
row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
|
||||||
|
@ -158,7 +158,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param table JSON object representing a table with data.
|
* @param table JSON object representing a table with data.
|
||||||
* @return Formatted HTML table.
|
* @return Formatted HTML table.
|
||||||
*/
|
*/
|
||||||
formatGradesTable(table: CoreGradesTable): CoreGradesFormattedTable {
|
async formatGradesTable(table: CoreGradesTable): Promise<CoreGradesFormattedTable> {
|
||||||
const maxDepth = table.maxdepth;
|
const maxDepth = table.maxdepth;
|
||||||
const formatted: CoreGradesFormattedTable = {
|
const formatted: CoreGradesFormattedTable = {
|
||||||
columns: [],
|
columns: [],
|
||||||
|
@ -178,7 +178,7 @@ export class CoreGradesHelperProvider {
|
||||||
feedback: false,
|
feedback: false,
|
||||||
contributiontocoursetotal: false,
|
contributiontocoursetotal: false,
|
||||||
};
|
};
|
||||||
formatted.rows = table.tabledata.map(row => this.formatGradeRowForTable(row));
|
formatted.rows = await Promise.all(table.tabledata.map(row => this.formatGradeRowForTable(row)));
|
||||||
|
|
||||||
// Get a row with some info.
|
// Get a row with some info.
|
||||||
let normalRow = formatted.rows.find(
|
let normalRow = formatted.rows.find(
|
||||||
|
@ -382,7 +382,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param gradeId Grade Object identifier.
|
* @param gradeId Grade Object identifier.
|
||||||
* @return Formatted HTML table.
|
* @return Formatted HTML table.
|
||||||
*/
|
*/
|
||||||
getGradesTableRow(table: CoreGradesTable, gradeId: number): CoreGradesFormattedRow | null {
|
async getGradesTableRow(table: CoreGradesTable, gradeId: number): Promise<CoreGradesFormattedRow | null> {
|
||||||
if (table.tabledata) {
|
if (table.tabledata) {
|
||||||
const selectedRow = table.tabledata.find(
|
const selectedRow = table.tabledata.find(
|
||||||
(row) =>
|
(row) =>
|
||||||
|
@ -393,7 +393,7 @@ export class CoreGradesHelperProvider {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
return this.formatGradeRow(selectedRow);
|
return await this.formatGradeRow(selectedRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +408,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @return Formatted HTML table.
|
* @return Formatted HTML table.
|
||||||
* @deprecated since app 4.0
|
* @deprecated since app 4.0
|
||||||
*/
|
*/
|
||||||
getModuleGradesTableRows(table: CoreGradesTable, moduleId: number): CoreGradesFormattedRow[] {
|
async getModuleGradesTableRows(table: CoreGradesTable, moduleId: number): Promise<CoreGradesFormattedRow[]> {
|
||||||
if (!table.tabledata) {
|
if (!table.tabledata) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
@ -416,7 +416,7 @@ export class CoreGradesHelperProvider {
|
||||||
// Find href containing "/mod/xxx/xxx.php".
|
// Find href containing "/mod/xxx/xxx.php".
|
||||||
const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/;
|
const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/;
|
||||||
|
|
||||||
return table.tabledata.filter((row) => {
|
return await Promise.all(table.tabledata.filter((row) => {
|
||||||
if (row.itemname && row.itemname.content) {
|
if (row.itemname && row.itemname.content) {
|
||||||
const matches = row.itemname.content.match(regex);
|
const matches = row.itemname.content.match(regex);
|
||||||
|
|
||||||
|
@ -428,7 +428,7 @@ export class CoreGradesHelperProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}).map((row) => this.formatGradeRow(row));
|
}).map((row) => this.formatGradeRow(row)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -534,7 +534,7 @@ export class CoreGradesHelperProvider {
|
||||||
* @param text HTML where the image will be rendered.
|
* @param text HTML where the image will be rendered.
|
||||||
* @return Row object with the image.
|
* @return Row object with the image.
|
||||||
*/
|
*/
|
||||||
protected setRowIcon<T extends CoreGradesFormattedRowCommonData>(row: T, text: string): T {
|
protected async setRowIcon<T extends CoreGradesFormattedRowCommonData>(row: T, text: string): Promise<T> {
|
||||||
text = text.replace('%2F', '/').replace('%2f', '/');
|
text = text.replace('%2F', '/').replace('%2f', '/');
|
||||||
if (text.indexOf('/agg_mean') > -1) {
|
if (text.indexOf('/agg_mean') > -1) {
|
||||||
row.itemtype = 'agg_mean';
|
row.itemtype = 'agg_mean';
|
||||||
|
@ -566,7 +566,7 @@ export class CoreGradesHelperProvider {
|
||||||
row.itemtype = 'mod';
|
row.itemtype = 'mod';
|
||||||
row.itemmodule = module[1];
|
row.itemmodule = module[1];
|
||||||
row.iconAlt = CoreCourse.translateModuleName(row.itemmodule) || '';
|
row.iconAlt = CoreCourse.translateModuleName(row.itemmodule) || '';
|
||||||
row.image = CoreCourse.getModuleIconSrc(
|
row.image = await CoreCourse.getModuleIconSrc(
|
||||||
module[1],
|
module[1],
|
||||||
CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined,
|
CoreDomUtils.convertToElement(text).querySelector('img')?.getAttribute('src') ?? undefined,
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { IonRefresher } from '@ionic/angular';
|
||||||
import { Params } from '@angular/router';
|
import { Params } from '@angular/router';
|
||||||
|
|
||||||
import { CoreSite, CoreSiteConfig } from '@classes/site';
|
import { CoreSite, CoreSiteConfig } from '@classes/site';
|
||||||
import { CoreCourse, CoreCourseModuleBasicInfo, CoreCourseWSSection } from '@features/course/services/course';
|
import { CoreCourse, CoreCourseWSModule, CoreCourseWSSection } from '@features/course/services/course';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreSiteHome } from '@features/sitehome/services/sitehome';
|
import { CoreSiteHome } from '@features/sitehome/services/sitehome';
|
||||||
|
@ -48,7 +48,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
hasContent = false;
|
hasContent = false;
|
||||||
items: string[] = [];
|
items: string[] = [];
|
||||||
siteHomeId = 1;
|
siteHomeId = 1;
|
||||||
currentSite?: CoreSite;
|
currentSite!: CoreSite;
|
||||||
searchEnabled = false;
|
searchEnabled = false;
|
||||||
downloadEnabled = false;
|
downloadEnabled = false;
|
||||||
downloadCourseEnabled = false;
|
downloadCourseEnabled = false;
|
||||||
|
@ -75,7 +75,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
this.switchDownload(this.downloadEnabled && this.downloadCourseEnabled && this.downloadCoursesEnabled);
|
this.switchDownload(this.downloadEnabled && this.downloadCourseEnabled && this.downloadCoursesEnabled);
|
||||||
}, CoreSites.getCurrentSiteId());
|
}, CoreSites.getCurrentSiteId());
|
||||||
|
|
||||||
this.currentSite = CoreSites.getCurrentSite()!;
|
this.currentSite = CoreSites.getRequiredCurrentSite();
|
||||||
this.siteHomeId = CoreSites.getCurrentSiteHomeId();
|
this.siteHomeId = CoreSites.getCurrentSiteHomeId();
|
||||||
|
|
||||||
const module = CoreNavigator.getRouteParam<CoreCourseModule>('module');
|
const module = CoreNavigator.getRouteParam<CoreCourseModule>('module');
|
||||||
|
@ -97,7 +97,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
protected async loadContent(): Promise<void> {
|
protected async loadContent(): Promise<void> {
|
||||||
this.hasContent = false;
|
this.hasContent = false;
|
||||||
|
|
||||||
const config = this.currentSite!.getStoredConfig() || { numsections: 1, frontpageloggedin: undefined };
|
const config = this.currentSite.getStoredConfig() || { numsections: 1, frontpageloggedin: undefined };
|
||||||
|
|
||||||
this.items = await CoreSiteHome.getFrontPageItems(config.frontpageloggedin);
|
this.items = await CoreSiteHome.getFrontPageItems(config.frontpageloggedin);
|
||||||
this.hasContent = this.items.length > 0;
|
this.hasContent = this.items.length > 0;
|
||||||
|
@ -105,13 +105,13 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
if (this.items.some((item) => item == 'NEWS_ITEMS')) {
|
if (this.items.some((item) => item == 'NEWS_ITEMS')) {
|
||||||
// Get the news forum.
|
// Get the news forum.
|
||||||
try {
|
try {
|
||||||
const forum = await CoreSiteHome.getNewsForum();
|
const forum = await CoreSiteHome.getNewsForum(this.siteHomeId);
|
||||||
this.newsForumModule = await CoreCourse.getModuleBasicInfo(forum.cmid);
|
this.newsForumModule = await CoreCourse.getModule(forum.cmid, forum.course);
|
||||||
this.newsForumModule.handlerData = CoreCourseModuleDelegate.getModuleDataFor(
|
this.newsForumModule.handlerData = await CoreCourseModuleDelegate.getModuleDataFor(
|
||||||
this.newsForumModule.modname,
|
this.newsForumModule.modname,
|
||||||
this.newsForumModule,
|
this.newsForumModule,
|
||||||
this.siteHomeId,
|
this.siteHomeId,
|
||||||
this.newsForumModule.section,
|
undefined,
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -120,7 +120,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const sections = await CoreCourse.getSections(this.siteHomeId!, false, true);
|
const sections = await CoreCourse.getSections(this.siteHomeId, false, true);
|
||||||
|
|
||||||
// Check "Include a topic section" setting from numsections.
|
// Check "Include a topic section" setting from numsections.
|
||||||
this.section = config.numsections ? sections.find((section) => section.section == 1) : undefined;
|
this.section = config.numsections ? sections.find((section) => section.section == 1) : undefined;
|
||||||
|
@ -137,10 +137,10 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
// Add log in Moodle.
|
// Add log in Moodle.
|
||||||
CoreCourse.logView(
|
CoreCourse.logView(
|
||||||
this.siteHomeId!,
|
this.siteHomeId,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
this.currentSite!.getInfo()?.sitename,
|
this.currentSite.getInfo()?.sitename,
|
||||||
).catch(() => {
|
).catch(() => {
|
||||||
// Ignore errors.
|
// Ignore errors.
|
||||||
});
|
});
|
||||||
|
@ -157,11 +157,11 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
doRefresh(refresher?: IonRefresher): void {
|
doRefresh(refresher?: IonRefresher): void {
|
||||||
const promises: Promise<unknown>[] = [];
|
const promises: Promise<unknown>[] = [];
|
||||||
|
|
||||||
promises.push(CoreCourse.invalidateSections(this.siteHomeId!));
|
promises.push(CoreCourse.invalidateSections(this.siteHomeId));
|
||||||
promises.push(this.currentSite!.invalidateConfig().then(async () => {
|
promises.push(this.currentSite.invalidateConfig().then(async () => {
|
||||||
// Config invalidated, fetch it again.
|
// Config invalidated, fetch it again.
|
||||||
const config: CoreSiteConfig = await this.currentSite!.getConfig();
|
const config: CoreSiteConfig = await this.currentSite.getConfig();
|
||||||
this.currentSite!.setConfig(config);
|
this.currentSite.setConfig(config);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}));
|
}));
|
||||||
|
@ -251,6 +251,6 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NewsForum = CoreCourseModuleBasicInfo & {
|
type NewsForum = CoreCourseWSModule & {
|
||||||
handlerData?: CoreCourseModuleHandlerData;
|
handlerData?: CoreCourseModuleHandlerData;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { makeSingleton } from '@singletons';
|
||||||
import { CoreCourse } from '../../course/services/course';
|
import { CoreCourse } from '../../course/services/course';
|
||||||
import { CoreCourses } from '../../courses/services/courses';
|
import { CoreCourses } from '../../courses/services/courses';
|
||||||
import { AddonModForum, AddonModForumData } from '@addons/mod/forum/services/forum';
|
import { AddonModForum, AddonModForumData } from '@addons/mod/forum/services/forum';
|
||||||
|
import { CoreError } from '@classes/errors/error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items with index 1 and 3 were removed on 2.5 and not being supported in the app.
|
* Items with index 1 and 3 were removed on 2.5 and not being supported in the app.
|
||||||
|
@ -57,7 +58,7 @@ export class CoreSiteHomeProvider {
|
||||||
return forum;
|
return forum;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw null;
|
throw new CoreError('No news forum found');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
<img [src]="item.avatarUrl" core-external-content alt="" role="presentation"
|
<img [src]="item.avatarUrl" core-external-content alt="" role="presentation"
|
||||||
onError="this.src='assets/img/user-avatar.png'">
|
onError="this.src='assets/img/user-avatar.png'">
|
||||||
</ion-avatar>
|
</ion-avatar>
|
||||||
<img slot="start" *ngIf="item.iconUrl" [src]="item.iconUrl" core-external-content alt="" role="presentation"
|
<core-mod-icon *ngIf="item.iconUrl" [modicon]="item.iconUrl" slot="start" [showAlt]="false">
|
||||||
class="core-module-icon">
|
</core-mod-icon>
|
||||||
<ion-label>
|
<ion-label>
|
||||||
<h2>{{ item.heading }}</h2>
|
<h2>{{ item.heading }}</h2>
|
||||||
<p *ngFor="let text of item.details">{{ text }}</p>
|
<p *ngFor="let text of item.details">{{ text }}</p>
|
||||||
|
|
|
@ -527,36 +527,10 @@ img[core-external-content]:not([src]) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activity modules
|
|
||||||
.core-module-icon {
|
|
||||||
--size: var(--module-icon-size);
|
|
||||||
width: var(--size);
|
|
||||||
height: var(--size);
|
|
||||||
max-width: var(--size);
|
|
||||||
max-height: var(--size);
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-item img.core-module-icon[slot="start"] {
|
|
||||||
margin-top: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
margin-right: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card ion-item img.core-module-icon[slot="start"] {
|
|
||||||
margin-top: 12px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
margin-right: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ion-card ion-item:only-child {
|
ion-card ion-item:only-child {
|
||||||
--inner-border-width: 0;
|
--inner-border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[dir=rtl] ion-item img.core-module-icon[slot="start"] {
|
|
||||||
margin-right: unset;
|
|
||||||
margin-left: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-course-module-handler:not(.addon-mod-label-handler) .item-heading .filter_mathjaxloader_equation div {
|
.core-course-module-handler:not(.addon-mod-label-handler) .item-heading .filter_mathjaxloader_equation div {
|
||||||
display: inline !important;
|
display: inline !important;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue