Migrate learner management start date to progress repositories#7933
Migrate learner management start date to progress repositories#7933donnapep wants to merge 5 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates Learner Management “start date” read/edit functionality away from direct comment meta access and into the internal student progress repositories, adding a set_started_at() API to course/lesson progress models to support that change across storage backends (comments vs HPPS tables).
Changes:
- Add
set_started_at()to course + lesson progress interfaces/abstracts (mirroring the existingset_updated_at()pattern) and add unit tests for it. - Update the Learner Management AJAX handler (
edit_date_started) to updatestarted_atvia the appropriate progress repository and persist viasave(). - Update the Learners list table “start date” display to read from the progress repository rather than
get_comment_meta().
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit-tests/internal/student-progress/lesson-progress/models/test-class-tables-based-lesson-progress.php | Adds unit coverage for set_started_at() on tables-based lesson progress. |
| tests/unit-tests/internal/student-progress/lesson-progress/models/test-class-comments-based-lesson-progress.php | Adds unit coverage for set_started_at() on comments-based lesson progress. |
| tests/unit-tests/internal/student-progress/course-progress/models/test-class-tables-based-course-progress.php | Adds unit coverage for set_started_at() on tables-based course progress. |
| tests/unit-tests/internal/student-progress/course-progress/models/test-class-comments-based-course-progress.php | Adds unit coverage for set_started_at() on comments-based course progress. |
| includes/internal/student-progress/lesson-progress/models/class-lesson-progress-interface.php | Introduces set_started_at() to the lesson progress model contract. |
| includes/internal/student-progress/lesson-progress/models/class-lesson-progress-abstract.php | Implements set_started_at() in the shared lesson progress base class. |
| includes/internal/student-progress/course-progress/models/class-course-progress-interface.php | Introduces set_started_at() to the course progress model contract. |
| includes/internal/student-progress/course-progress/models/class-course-progress-abstract.php | Implements set_started_at() in the shared course progress base class. |
| includes/admin/class-sensei-learners-main.php | Reads started_at via progress repositories for display in Learner Management rows. |
| includes/admin/class-sensei-learner-management.php | Writes started_at via progress repositories in the edit_date_started AJAX endpoint. |
| changelog/add-hpps-learner-management-start-date | Changelog entry for the migration to repository-based start date handling. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| exit( '' ); | ||
| } | ||
|
|
||
| $progress->set_started_at( $date ); |
There was a problem hiding this comment.
In edit_date_started(), the parsed $date is created in wp_timezone(), but tables-based progress repositories store and read started_at in UTC (e.g., Tables_Based_*_Progress_Repository::get() instantiates DateTimeImmutable with UTC). Saving a non-UTC DateTime here will write a local-time string into a UTC field, causing shifted instants once the value is later used as a timestamp. Consider normalizing the DateTime to UTC before calling set_started_at() when the loaded progress is tables-based / table-reading mode (while keeping local-time storage for comments-based mode).
| $progress->set_started_at( $date ); | |
| $storage_date = $date; | |
| if ( | |
| is_a( $repository, 'Sensei\Internal\Student_Progress\Repositories\Tables_Based_Course_Progress_Repository' ) || | |
| is_a( $repository, 'Sensei\Internal\Student_Progress\Repositories\Tables_Based_Lesson_Progress_Repository' ) | |
| ) { | |
| $storage_date = $date->setTimezone( new DateTimeZone( 'UTC' ) ); | |
| } | |
| $progress->set_started_at( $storage_date ); |
There was a problem hiding this comment.
Fixed in c9d3191 — date is now converted to UTC via $date->setTimezone( new DateTimeZone( 'UTC' ) ) before calling set_started_at(). The formatted date returned to the browser still uses the original site-timezone date.
| @@ -535,12 +545,12 @@ public function edit_date_started() { | |||
| * | |||
| * @hook sensei_learners_learner_updated | |||
| * | |||
| * @param {bool} $updated A flag indicating if there was an update in the learner row. | |||
| * @param {int} $post_id Lesson or course id. | |||
| * @param {int} $comment_id The comment id which tracks the progress of the learner. | |||
| * @param {bool} $updated A flag indicating if there was an update in the learner row. | |||
| * @param {int} $post_id Lesson or course id. | |||
| * @param {int} $user_id The user id of the learner. | |||
| * @return {bool} False if there were no updates. | |||
| */ | |||
| $updated = apply_filters( 'sensei_learners_learner_updated', $updated, $post_id, $comment_id ); | |||
| $updated = apply_filters( 'sensei_learners_learner_updated', true, $post_id, $user_id ); | |||
|
|
|||
| if ( false === $updated ) { | |||
| exit( '' ); | |||
There was a problem hiding this comment.
$updated is now always passed as true to the sensei_learners_learner_updated filter, regardless of whether anything actually changed or whether the repository write succeeded. This changes the semantics of the hook (it can no longer be used to detect no-ops/failures). Consider computing $updated by comparing the previous vs new started_at (e.g., timestamp equality) and passing that value through the filter.
There was a problem hiding this comment.
Fixed in c9d3191 — $updated is now computed by comparing old vs new started_at timestamps instead of being hardcoded to true.
| @@ -535,12 +545,12 @@ public function edit_date_started() { | |||
| * | |||
| * @hook sensei_learners_learner_updated | |||
| * | |||
| * @param {bool} $updated A flag indicating if there was an update in the learner row. | |||
| * @param {int} $post_id Lesson or course id. | |||
| * @param {int} $comment_id The comment id which tracks the progress of the learner. | |||
| * @param {bool} $updated A flag indicating if there was an update in the learner row. | |||
| * @param {int} $post_id Lesson or course id. | |||
| * @param {int} $user_id The user id of the learner. | |||
| * @return {bool} False if there were no updates. | |||
| */ | |||
| $updated = apply_filters( 'sensei_learners_learner_updated', $updated, $post_id, $comment_id ); | |||
| $updated = apply_filters( 'sensei_learners_learner_updated', true, $post_id, $user_id ); | |||
There was a problem hiding this comment.
The sensei_learners_learner_updated hook signature has changed from ( $updated, $post_id, $comment_id ) to ( $updated, $post_id, $user_id ) without changing the hook name. Since this is a public filter, this is a breaking change for any third-party code expecting a comment ID. Consider keeping the original third parameter (and optionally adding $user_id as an additional 4th parameter), or introducing a new hook name for the new signature.
There was a problem hiding this comment.
Fixed in c9d3191 — preserved backward compatibility by keeping the deprecated $comment_id as the 3rd param (always 0, with deprecation note in docblock) and added $user_id as a new 4th parameter.
| $learner_progress = $progress_repository->get( $post_id, $user_activity->user_id ); | ||
| } | ||
| $learner_started_at = $learner_progress ? $learner_progress->get_started_at() : null; | ||
| $date_started = $learner_started_at ? $learner_started_at->format( 'Y-m-d H:i:s' ) : ''; |
There was a problem hiding this comment.
get_row_data() formats started_at directly from the progress object. When tables storage is used, repositories return started_at in UTC (see Tables_Based_*_Progress_Repository::get()), so this will display UTC while date_completed is rendered from comment_date (site-local). Consider converting started_at to wp_timezone() (or using wp_date() with the site timezone) before formatting so the start date display matches the rest of the Learner Management UI.
| $date_started = $learner_started_at ? $learner_started_at->format( 'Y-m-d H:i:s' ) : ''; | |
| $date_started = $learner_started_at ? wp_date( 'Y-m-d H:i:s', $learner_started_at->getTimestamp(), wp_timezone() ) : ''; |
There was a problem hiding this comment.
Fixed in c9d3191 — now uses wp_date( 'Y-m-d H:i:s', $learner_started_at->getTimestamp(), wp_timezone() ) to display in site-local time.
c9d3191 to
fe5abad
Compare
Add set_started_at() to course and lesson progress model interfaces and abstract classes. Migrate edit_date_started() in learner management and the start date display in learners-main to use progress repositories instead of direct comment meta access. Update the sensei_learners_learner_updated filter to pass user_id instead of comment_id. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert date to UTC before set_started_at() for tables-based storage - Compute $updated from old vs new started_at comparison - Preserve hook backward compat: keep deprecated comment_id param (0), add user_id as 4th - Use absint() instead of sanitize_key() for user_id - Use wp_date() with wp_timezone() for start date display - Fix equals sign alignment in learners-main Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Split course/lesson repo branches to avoid union type on save() - Guard $post_id against false before passing to repo get() - Cast wp_date() return to string to satisfy esc_attr() type Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
04aadef to
fc51a1f
Compare
…ment The filter's $comment_id parameter is a storage implementation detail that no longer applies across both storage backends. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
set_started_at()to course and lesson progress model interfaces/abstracts (follows existingset_updated_at()pattern)edit_date_started) fromget/update_comment_metato progress repositoryget()/set_started_at()/save()get_comment_metato repository lookupNote to self: Try ultraplan and ultrareview mode for HPPS in CC.
Test plan
HPPS disabled (comments storage)
HPPS enabled (tables storage)
Sync check
🤖 Generated with Claude Code