diff --git a/scripts/langindex.json b/scripts/langindex.json
index f4057dcb9..1ef97ed16 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -1582,6 +1582,8 @@
"core.course.previousactivity": "local_moodlemobileapp",
"core.course.previousactivitynotfound": "local_moodlemobileapp",
"core.course.refreshcourse": "local_moodlemobileapp",
+ "core.course.relativedatessubmissionduedateafter": "course",
+ "core.course.relativedatessubmissionduedatebefore": "course",
"core.course.section": "moodle",
"core.course.startdate": "moodle",
"core.course.thisweek": "format_weeks/currentsection",
diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html
index f01531b83..b338fc7d1 100644
--- a/src/core/features/course/components/module-info/core-course-module-info.html
+++ b/src/core/features/course/components/module-info/core-course-module-info.html
@@ -27,9 +27,8 @@
- {{ date.label }} {{ date.timestamp
- *
- 1000 | coreFormatDate:'strftimedatetime' }}
+ {{ date.label }}
+ {{ date.readableTime }}
diff --git a/src/core/features/course/components/module/core-course-module.html b/src/core/features/course/components/module/core-course-module.html
index 3a6c3e003..8a099383d 100644
--- a/src/core/features/course/components/module/core-course-module.html
+++ b/src/core/features/course/components/module/core-course-module.html
@@ -82,9 +82,8 @@
- {{ date.label }} {{ date.timestamp
- *
- 1000 | coreFormatDate:'strftimedatetime' }}
+ {{ date.label }}
+ {{ date.readableTime }}
diff --git a/src/core/features/course/lang.json b/src/core/features/course/lang.json
index 8195622c0..33b03e34d 100644
--- a/src/core/features/course/lang.json
+++ b/src/core/features/course/lang.json
@@ -50,6 +50,8 @@
"previousactivity": "Previous activity",
"previousactivitynotfound": "Previous activity not found. It's possible that it has been hidden or deleted.",
"refreshcourse": "Refresh course",
+ "relativedatessubmissionduedateafter": "{{$a.datediffstr}} after course start",
+ "relativedatessubmissionduedatebefore": "{{$a.datediffstr}} before course start",
"section": "Section",
"startdate": "Course start date",
"thisweek": "This week",
diff --git a/src/core/features/course/services/course-helper.ts b/src/core/features/course/services/course-helper.ts
index e42f3bd71..5529e62db 100644
--- a/src/core/features/course/services/course-helper.ts
+++ b/src/core/features/course/services/course-helper.ts
@@ -27,6 +27,7 @@ import {
CoreCourseModuleCompletionTracking,
CoreCourseModuleCompletionStatus,
CoreCourseGetContentsWSModule,
+ CoreCourseGetContentsWSModuleDate,
} from './course';
import { CoreConstants } from '@/core/constants';
import { CoreLogger } from '@singletons/logger';
@@ -2059,12 +2060,20 @@ export type CoreCourseSectionWithStatus = CoreCourseSection & {
/**
* Module with calculated data.
*/
-export type CoreCourseModuleData = Omit & {
+export type CoreCourseModuleData = Omit & {
course: number; // The course id.
isStealth?: boolean;
handlerData?: CoreCourseModuleHandlerData;
completiondata?: CoreCourseModuleCompletionData;
section: number;
+ dates?: CoreCourseModuleDate[];
+};
+
+/**
+ * Module date with calculated data.
+ */
+export type CoreCourseModuleDate = CoreCourseGetContentsWSModuleDate & {
+ readableTime: string;
};
/**
diff --git a/src/core/features/course/services/course.ts b/src/core/features/course/services/course.ts
index d2d518b94..f5a283831 100644
--- a/src/core/features/course/services/course.ts
+++ b/src/core/features/course/services/course.ts
@@ -38,7 +38,7 @@ import {
import { CoreDomUtils } from '@services/utils/dom';
import { CoreWSError } from '@classes/errors/wserror';
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
-import { CoreCourseHelper, CoreCourseModuleData, CoreCourseModuleCompletionData } from './course-helper';
+import { CoreCourseHelper, CoreCourseModuleData, CoreCourseModuleCompletionData, CoreCourseModuleDate } from './course-helper';
import { CoreCourseFormatDelegate } from './format-delegate';
import { CoreCronDelegate } from '@services/cron';
import { CoreCourseLogCronHandler } from './handlers/log-cron';
@@ -53,6 +53,7 @@ import { CoreDatabaseTable } from '@classes/database/database-table';
import { CoreDatabaseCachingStrategy } from '@classes/database/database-table-proxy';
import { SQLiteDB } from '@classes/sqlitedb';
import { CorePlatform } from '@services/platform';
+import { CoreTime } from '@singletons/time';
const ROOT_CACHE_KEY = 'mmCourse:';
@@ -658,11 +659,39 @@ export class CoreCourseProvider {
};
}
+ let formattedDates: CoreCourseModuleDate[] | undefined;
+
+ if (module.dates) {
+ formattedDates = module.dates.map(date => {
+ let readableTime = '';
+ if (!date.relativeto) {
+ readableTime = CoreTimeUtils.userDate(date.timestamp * 1000, 'core.strftimedatetime', true);
+ } else {
+ readableTime = Translate.instant(
+ 'core.course.relativedatessubmissionduedate' + (date.timestamp > date.relativeto ? 'after' : 'before'),
+ {
+ $a: {
+ datediffstr: date.relativeto === date.timestamp ?
+ '0 ' + Translate.instant('core.secs') :
+ CoreTime.formatTime(date.relativeto - date.timestamp, 3),
+ },
+ },
+ );
+ }
+
+ return {
+ ...date,
+ readableTime,
+ };
+ });
+ }
+
return {
...module,
course: courseId,
section: sectionId,
completiondata: completionData,
+ dates: formattedDates,
};
}
@@ -1707,10 +1736,7 @@ export type CoreCourseGetContentsWSModule = {
completiondata?: CoreCourseModuleWSCompletionData; // Module completion data.
contents?: CoreCourseModuleContentFile[];
downloadcontent?: number; // @since 4.0 The download content value.
- dates?: {
- label: string;
- timestamp: number;
- }[]; // @since 3.11. Activity dates.
+ dates?: CoreCourseGetContentsWSModuleDate[]; // @since 3.11. Activity dates.
contentsinfo?: { // @since v3.7.6 Contents summary information.
filescount: number; // Total number of files.
filessize: number; // Total files size.
@@ -1720,6 +1746,16 @@ export type CoreCourseGetContentsWSModule = {
};
};
+/**
+ * Activity date.
+ */
+export type CoreCourseGetContentsWSModuleDate = {
+ label: string;
+ timestamp: number;
+ relativeto?: number; // @since 4.1. Relative date timestamp.
+ dataid?: string; // @since 4.1. ID to identify the text.
+};
+
/**
* Data returned by core_course_get_contents WS.
*/
diff --git a/src/core/features/course/tests/behat/relative_dates.feature b/src/core/features/course/tests/behat/relative_dates.feature
new file mode 100644
index 000000000..5ed164db7
--- /dev/null
+++ b/src/core/features/course/tests/behat/relative_dates.feature
@@ -0,0 +1,45 @@
+@core @core_course @app @javascript
+Feature: Check relative dates feature.
+
+ Background:
+ Given the following config values are set as admin:
+ | enablecourserelativedates | 1 |
+ Given the following "users" exist:
+ | username | firstname | lastname | email |
+ | student1 | Student | 1 | student1@example.com |
+ | teacher1 | Student | 1 | student1@example.com |
+ And the following "courses" exist:
+ | fullname | shortname | category | startdate | enddate | relativedatesmode | showactivitydates |
+ | Course 1 | C1 | 0 | ## 1 January 2022 ## | ## 1 January 2023 ## | 1 | 1 |
+ And the following "course enrolments" exist:
+ | user | course | role |
+ | student1 | C1 | student |
+ | teacher1 | C1 | editingteacher |
+ And the following "activities" exist:
+ | activity | course | idnumber | name | allowsubmissionsfromdate | duedate | section |
+ | assign | C1 | assign1 | Assignment 1 | ## 20 January 2022 ## | ## 31 July 2022 ## | 1 |
+ | assign | C1 | assign2 | Assignment 2 | ## 1 December 2021 ## | ## 31 January 2023 10:00 AM ## | 2 |
+
+ Scenario: Relative dates (student)
+ Given I entered the course "Course 1" as "student1" in the app
+ When I press "Course index" in the app
+ And I press "Topic 1" in the app
+ Then I should find "20 January 2022, 12:00 AM" near "Opened:" in the app
+ And I should find "31 July 2022, 12:00 AM" near "Due:" in the app
+
+ When I press "Course index" in the app
+ And I press "Topic 2" in the app
+ And I should find "1 December 2021, 12:00 AM" near "Opened:" in the app
+ And I should find "31 January 2023, 10:00 AM" near "Due:" in the app
+
+ Scenario: Relative dates (teacher)
+ Given I entered the course "Course 1" as "teacher1" in the app
+ When I press "Course index" in the app
+ And I press "Topic 1" in the app
+ Then I should find "19 days after course start" near "Opened:" in the app
+ And I should find "211 days after course start" near "Due:" in the app
+
+ When I press "Course index" in the app
+ And I press "Topic 2" in the app
+ And I should find "31 days before course start" near "Opened:" in the app
+ And I should find "1 year 30 days 10 hours after course start" near "Due:" in the app