diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html
index bf4835c89..3919f3926 100644
--- a/src/core/features/grades/pages/course/course.html
+++ b/src/core/features/grades/pages/course/course.html
@@ -27,13 +27,18 @@
+
+ |
+
+ [class.core-grades-grade-clickable]="row.expandable && showSummary" [id]="'grade-'+row.id"
+ *ngIf="useLegacyLayout || row.itemtype !== 'leader'">
-
+ |
|
|
-
-
+
+
- |
-
-
-
- |
-
-
-
-
- |
+ [class.ion-hide-md-down]="column.hiddenPhone" [innerHTML]="row[column.name]">
+ |
+
+
+
+ |
+
+
+
+
+ |
+
diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts
index f3d57d541..2f85483de 100644
--- a/src/core/features/grades/pages/course/course.page.ts
+++ b/src/core/features/grades/pages/course/course.page.ts
@@ -54,6 +54,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
rows?: CoreGradesFormattedTableRow[];
totalColumnsSpan?: number;
withinSplitView?: boolean;
+ useLegacyLayout?: boolean; // Whether to use the layout before 4.1.
protected fetchSuccess = false;
@@ -68,6 +69,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
this.expandLabel = Translate.instant('core.expand');
this.collapseLabel = Translate.instant('core.collapse');
+ this.useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1');
if (route.snapshot.data.swipeEnabled ?? true) {
const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(CoreGradesCoursesSource, []);
@@ -133,11 +135,22 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy {
row.expanded = expand ?? !row.expanded;
- let colspan: number = this.columns.length + (row.colspan ?? 0) - 1;
+ let colspan: number = this.columns.length + (row.colspan ?? 0);
+
+ if (this.useLegacyLayout) {
+ colspan--;
+ }
+
for (let i = this.rows.indexOf(row) - 1; i >= 0; i--) {
const previousRow = this.rows[i];
- if (previousRow.expandable || !previousRow.colspan || !previousRow.rowspan || previousRow.colspan !== colspan) {
+ if (
+ !previousRow.rowspan ||
+ !previousRow.colspan ||
+ previousRow.colspan !== colspan ||
+ (!this.useLegacyLayout && previousRow.itemtype !== 'leader') ||
+ (this.useLegacyLayout && previousRow.expandable)
+ ) {
continue;
}
diff --git a/src/core/features/grades/pages/course/course.scss b/src/core/features/grades/pages/course/course.scss
index 5358730a6..096eb096d 100644
--- a/src/core/features/grades/pages/course/course.scss
+++ b/src/core/features/grades/pages/course/course.scss
@@ -124,6 +124,14 @@
}
}
+ .core-grades-table-leader {
+ width: 0;
+ }
+
+ .ion-no-border {
+ border: 0 !important;
+ }
+
.dimmed_text,
.hidden {
opacity: .7;
diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts
index f417a20a3..720ae5c45 100644
--- a/src/core/features/grades/services/grades-helper.ts
+++ b/src/core/features/grades/services/grades-helper.ts
@@ -25,6 +25,7 @@ import {
CoreGradesTable,
CoreGradesTableColumn,
CoreGradesTableItemNameColumn,
+ CoreGradesTableLeaderColumn,
CoreGradesTableRow,
} from '@features/grades/services/grades';
import { CoreTextUtils } from '@services/utils/text';
@@ -71,7 +72,8 @@ export class CoreGradesHelperProvider {
let content = String(column.content);
if (name == 'itemname') {
- await this.setRowIcon(row, content);
+ this.setRowIconAndType(row, content);
+
row.link = this.getModuleLink(content);
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
row.rowclass += column.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
@@ -96,10 +98,23 @@ export class CoreGradesHelperProvider {
* Formats a row from the grades table to be rendered in one table.
*
* @param tableRow JSON object representing row of grades table data.
+ * @param useLegacyLayout Whether to use the layout before 4.1.
* @return Formatted row object.
*/
- protected formatGradeRowForTable(tableRow: CoreGradesTableRow): CoreGradesFormattedTableRow {
+ protected formatGradeRowForTable(tableRow: CoreGradesTableRow, useLegacyLayout: boolean): CoreGradesFormattedTableRow {
const row: CoreGradesFormattedTableRow = {};
+
+ if (!useLegacyLayout && 'leader' in tableRow) {
+ const row = {
+ itemtype: 'leader',
+ rowspan: tableRow.leader?.rowspan,
+ };
+
+ this.setRowEvenOddClass(row, (tableRow.leader as CoreGradesTableLeaderColumn).class);
+
+ return row;
+ }
+
for (let name in tableRow) {
const column: CoreGradesTableColumn = tableRow[name];
@@ -116,13 +131,13 @@ export class CoreGradesHelperProvider {
row.colspan = itemNameColumn.colspan;
row.rowspan = tableRow.leader?.rowspan || 1;
- this.setRowIcon(row, content);
- row.rowclass = itemNameColumn.class.indexOf('leveleven') < 0 ? 'odd' : 'even';
+ this.setRowIconAndType(row, content);
+ this.setRowEvenOddClass(row, itemNameColumn.class);
row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : '';
row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
content = content.replace(/<\/span>/gi, '\n');
- content = CoreTextUtils.cleanTags(content);
+ content = CoreTextUtils.cleanTags(content, true);
name = 'gradeitem';
} else if (name === 'grade') {
// Add the pass/fail class if present.
@@ -202,7 +217,7 @@ export class CoreGradesHelperProvider {
feedback: false,
contributiontocoursetotal: false,
};
- formatted.rows = table.tabledata.map(row => this.formatGradeRowForTable(row));
+ formatted.rows = this.formatGradesTableRows(table.tabledata);
// Get a row with some info.
let normalRow = formatted.rows.find(
@@ -234,6 +249,33 @@ export class CoreGradesHelperProvider {
return formatted;
}
+ /**
+ * Format table rows.
+ *
+ * @param rows Unformatted rows.
+ * @returns Formatted rows.
+ */
+ protected formatGradesTableRows(rows: CoreGradesTableRow[]): CoreGradesFormattedTableRow[] {
+ const useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1');
+ const formattedRows = rows.map(row => this.formatGradeRowForTable(row, useLegacyLayout));
+
+ if (!useLegacyLayout) {
+ for (let index = 0; index < formattedRows.length - 1; index++) {
+ const row = formattedRows[index];
+ const previousRow = formattedRows[index - 1] ?? null;
+
+ if (row.itemtype !== 'leader') {
+ continue;
+ }
+
+ row.colspan = previousRow.colspan;
+ previousRow.rowclass = `${previousRow.rowclass ?? ''} ion-no-border`.trim();
+ }
+ }
+
+ return formattedRows;
+ }
+
/**
* Get course data for grades since they only have courseid.
*
@@ -474,7 +516,7 @@ export class CoreGradesHelperProvider {
// Find href containing "/mod/xxx/xxx.php".
const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/;
- return table.tabledata.filter((row) => {
+ return this.formatGradesTableRows(table.tabledata.filter((row) => {
if (row.itemname && row.itemname.content) {
const matches = row.itemname.content.match(regex);
@@ -486,7 +528,7 @@ export class CoreGradesHelperProvider {
}
return false;
- }).map((row) => this.formatGradeRowForTable(row));
+ }));
}
/**
@@ -582,14 +624,25 @@ export class CoreGradesHelperProvider {
return CoreGrades.invalidateCourseGradesItemsData(courseId, userId, groupId, siteId);
}
+ /**
+ * Set 'odd' or 'even' classes into a row.
+ *
+ * @param row Row.
+ * @param classes Existing row classes.
+ */
+ protected setRowEvenOddClass(row: CoreGradesFormattedTableRow, classes: string): void {
+ const level = parseInt(classes.match(/(?:^|\s)level(\d+)(?:$|\s)/)?.[1] ?? '0');
+
+ row.rowclass = `${row.rowclass ?? ''} ${level % 2 === 0 ? 'even' : 'odd'}`.trim();
+ }
+
/**
* Parses the image and sets it to the row.
*
- * @param row Formatted grade row object.
- * @param text HTML where the image will be rendered.
- * @return Row object with the image.
+ * @param row Row.
+ * @param text Row content.
*/
- protected setRowIcon(row: T, text: string): T {
+ protected setRowIconAndType(row: CoreGradesFormattedRowCommonData, text: string): void {
text = text.replace('%2F', '/').replace('%2f', '/');
if (text.indexOf('/agg_mean') > -1) {
row.itemtype = 'agg_mean';
@@ -603,7 +656,7 @@ export class CoreGradesHelperProvider {
row.itemtype = 'outcome';
row.icon = 'fas-tasks';
row.iconAlt = Translate.instant('core.grades.outcome');
- } else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1) {
+ } else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1 || text.indexOf('category-content') > -1) {
row.itemtype = 'category';
row.icon = 'fas-folder';
row.iconAlt = Translate.instant('core.grades.category');
@@ -643,8 +696,6 @@ export class CoreGradesHelperProvider {
row.iconAlt = Translate.instant('core.unknown');
}
}
-
- return row;
}
/**