From b96f86fe59b112056ac02003c73a65e7327b20a9 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Tue, 3 Feb 2026 10:29:38 +0600 Subject: [PATCH 1/9] fix(dashboard): Ensure form state is properly reset after successful mutation by passing formId in payload. --- .../js/frontend/dashboard/pages/settings.ts | 21 +++++++++++++++---- .../frontend/dashboard/settings/_profile.scss | 8 +++---- .../account/settings/preferences.php | 3 +-- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/assets/src/js/frontend/dashboard/pages/settings.ts b/assets/src/js/frontend/dashboard/pages/settings.ts index d08863c31a..219863b3f9 100644 --- a/assets/src/js/frontend/dashboard/pages/settings.ts +++ b/assets/src/js/frontend/dashboard/pages/settings.ts @@ -63,6 +63,11 @@ interface PreferencesFormProps { auto_play_next: boolean; theme: string; font_scale: number; + formId?: string; +} + +interface UpdateNotificationProps { + [key: string]: boolean | string; } const settings = () => { @@ -100,7 +105,8 @@ const settings = () => { this.handleResetPassword = this.handleResetPassword.bind(this); this.handleUpdateNotification = this.query.useMutation(this.updateNotification, { - onSuccess: (data: TutorMutationResponse) => { + onSuccess: (data: TutorMutationResponse, payload: UpdateNotificationProps) => { + this.form.reset(payload?.formId as string, payload as unknown as Record); this.toast.success(data?.message ?? __('Notification settings updated', 'tutor')); }, onError: (error: Error) => { @@ -168,7 +174,8 @@ const settings = () => { }); this.savePreferencesMutation = this.query.useMutation(this.updatePreferences, { - onSuccess: (data: TutorMutationResponse) => { + onSuccess: (data: TutorMutationResponse, payload: PreferencesFormProps) => { + this.form.reset(payload?.formId || '', payload as unknown as Record); this.toast.success(data?.message ?? __('Preferences saved successfully', 'tutor')); }, onError: (error: Error) => { @@ -177,16 +184,22 @@ const settings = () => { }); }, - async updatePreferences(payload: Record) { + async updatePreferences(payload: PreferencesFormProps) { return wpAjaxInstance.post(endpoints.UPDATE_USER_PREFERENCES, payload) as unknown as Promise< TutorMutationResponse >; }, - async updateNotification(payload: Record) { + async updateNotification(payload: UpdateNotificationProps) { const transformedPayload = Object.keys(payload).reduce( (formattedPayload, key) => { const value = payload[key]; + + if (typeof value !== 'boolean') { + formattedPayload[`${key}`] = value; + return formattedPayload; + } + const stringValue = typeof value === 'boolean' ? (value ? 'on' : 'off') : value; if (!key.includes('__')) { diff --git a/assets/src/scss/frontend/dashboard/settings/_profile.scss b/assets/src/scss/frontend/dashboard/settings/_profile.scss index 7107a7b8d0..4ee1c89b7f 100644 --- a/assets/src/scss/frontend/dashboard/settings/_profile.scss +++ b/assets/src/scss/frontend/dashboard/settings/_profile.scss @@ -108,7 +108,9 @@ body:has(#wpadminbar) { } .tutor-profile-notification-content { - border-top: 1px solid $tutor-border-idle; + &.tutor-profile-notification-content-first { + border-top: 1px solid $tutor-border-idle; + } } .tutor-profile-notification-toggle { @@ -142,8 +144,4 @@ body:has(#wpadminbar) { .tutor-input-field { margin-bottom: 0px; } - - .tutor-profile-notification-content { - border-top: 1px solid $tutor-border-idle; - } } diff --git a/templates/dashboard/account/settings/preferences.php b/templates/dashboard/account/settings/preferences.php index aa3cb4dee0..fdc0c3f510 100644 --- a/templates/dashboard/account/settings/preferences.php +++ b/templates/dashboard/account/settings/preferences.php @@ -41,9 +41,8 @@ shouldFocusError: true, defaultValues: })' - x-bind="getFormBindings()" - @submit="handleSubmit((data) => { savePreferencesMutation?.mutate(data); })($event)" + @submit="handleSubmit((data) => { savePreferencesMutation?.mutate({...data, formId: ''}); })($event)" >
From 7046a033eaf1eec95c76260e1d8ebd8760ab9c52 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Tue, 3 Feb 2026 11:54:43 +0600 Subject: [PATCH 2/9] style: remove unused CSS for profile notification content border --- assets/src/scss/frontend/dashboard/settings/_profile.scss | 6 ------ 1 file changed, 6 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/settings/_profile.scss b/assets/src/scss/frontend/dashboard/settings/_profile.scss index 4ee1c89b7f..34bcd2f303 100644 --- a/assets/src/scss/frontend/dashboard/settings/_profile.scss +++ b/assets/src/scss/frontend/dashboard/settings/_profile.scss @@ -107,12 +107,6 @@ body:has(#wpadminbar) { } } - .tutor-profile-notification-content { - &.tutor-profile-notification-content-first { - border-top: 1px solid $tutor-border-idle; - } - } - .tutor-profile-notification-toggle { display: flex; align-items: center; From d97b7c540d5a32a3462301f34327f22435a20f5c Mon Sep 17 00:00:00 2001 From: nur-alam Date: Thu, 5 Feb 2026 12:38:15 +0600 Subject: [PATCH 3/9] feat(templates): add filter for course loop button classes and text unify dashboard card buttons - Introduce `tutor_course_loop_button_class` filter to allow customization of button classes across all course loop templates - Replace hardcoded button classes in WooCommerce, EDD, Tutor, and course embed templates with the new filter - Add `tutor_dashboard_course_card_buttons()` function to centralize button rendering for dashboard cards - Update dashboard card templates to use the new function, removing duplicate buttons and improving consistency - Adjust dashboard progress card CSS to properly display the unified button actions - Ensure backward compatibility by maintaining default class values when filter is not used --- .../frontend/dashboard/_progress-card.scss | 11 +- includes/tutor-template-functions.php | 66 +++++++++- templates/course-embed.php | 2 +- templates/dashboard/courses/course-card.php | 121 +++++++++--------- templates/dashboard/student-dashboard.php | 9 +- templates/loop/add-to-cart-tutor.php | 4 +- templates/loop/add-to-cart-woocommerce.php | 2 +- templates/loop/course-continue.php | 14 +- templates/loop/course-price-edd.php | 5 +- templates/loop/course-price-tutor.php | 2 +- templates/loop/course-price-woocommerce.php | 2 +- templates/loop/course-price.php | 4 +- 12 files changed, 153 insertions(+), 89 deletions(-) diff --git a/assets/src/scss/frontend/dashboard/_progress-card.scss b/assets/src/scss/frontend/dashboard/_progress-card.scss index 5fb6466037..faef24214c 100644 --- a/assets/src/scss/frontend/dashboard/_progress-card.scss +++ b/assets/src/scss/frontend/dashboard/_progress-card.scss @@ -160,12 +160,15 @@ } &-actions { - display: none; - } - - &-footer { + position: static; display: block; + visibility: visible; + opacity: 1; padding: $tutor-spacing-6 $tutor-spacing-4 0; + + .tutor-btn { + width: 100%; + } } } } \ No newline at end of file diff --git a/includes/tutor-template-functions.php b/includes/tutor-template-functions.php index a15ccf4f76..d8af450077 100644 --- a/includes/tutor-template-functions.php +++ b/includes/tutor-template-functions.php @@ -660,7 +660,70 @@ function tutor_course_loop_price() { tutor_load_template( 'loop.course-price' ); } } - echo apply_filters( 'tutor_course_loop_price', ob_get_clean(), $course_id ); //phpcs:ignore -- already escaped inside template file + echo apply_filters( 'tutor_course_loop_price', ob_get_clean(), $course_id ); //phpcs:ignore -- already escaped inside template file + } +} +if ( ! function_exists( 'tutor_dashboard_course_card_buttons' ) ) { + /** + * Get dashboard course card buttons. + * + * @since 1.0.0 + * + * @return mixed|void + */ + function tutor_dashboard_course_card_buttons() { + add_filter( + 'tutor_course_loop_button_class', + function () { + return 'tutor-btn tutor-btn-primary tutor-btn-small'; + }, + 20, + 2 + ); + add_filter( + 'tutor_course_loop_continue_button_text', + function () { + return 'Resume'; + }, + 20, + 2 + ); + add_filter( + 'tutor_course_loop_start_button_text', + function () { + return 'Start'; + }, + 20, + 2 + ); + add_filter( + 'tutor_course_loop_download_button_text', + function () { + return 'Download'; + }, + 20, + 2 + ); + add_filter( + 'tutor_course_loop_bundle_button_text', + function () { + return 'Bundle'; + }, + 20, + 2 + ); + ob_start(); + tutor_course_loop_price(); + $content = ob_get_clean(); + + // Remove all filters to avoid any side effects. + remove_all_filters( 'tutor_course_loop_button_class', 20 ); + remove_all_filters( 'tutor_course_loop_continue_button_text', 20 ); + remove_all_filters( 'tutor_course_loop_start_button_text', 20 ); + remove_all_filters( 'tutor_course_loop_download_button_text', 20 ); + remove_all_filters( 'tutor_course_loop_bundle_button_text', 20 ); + + return $content; } } @@ -1147,7 +1210,6 @@ function tutor_lesson_lead_info( $lesson_id = 0, $echo = true ) { } return $output; - } } diff --git a/templates/course-embed.php b/templates/course-embed.php index 0c740df05d..73cecee252 100644 --- a/templates/course-embed.php +++ b/templates/course-embed.php @@ -120,7 +120,7 @@ diff --git a/templates/dashboard/courses/course-card.php b/templates/dashboard/courses/course-card.php index dee6e7305d..387fd088d0 100644 --- a/templates/dashboard/courses/course-card.php +++ b/templates/dashboard/courses/course-card.php @@ -20,73 +20,78 @@ $category_names = is_array( $course_categories ) ? wp_list_pluck( $course_categories, 'name' ) : array(); $category = implode( ', ', $category_names ); +$course_learning_url = tutor_utils()->get_course_first_lesson(); +if ( get_post_type() !== tutor()->course_post_type ) { + $course_learning_url = get_permalink(); +} + ?> - -
- -
- - - <?php the_title(); ?> +
+
+ + + <?php the_title(); ?> + +
+ +
+ +
+ +
+ +
+

+ +

-
diff --git a/templates/dashboard/student-dashboard.php b/templates/dashboard/student-dashboard.php index e5176aea7e..9fcc9ebd78 100644 --- a/templates/dashboard/student-dashboard.php +++ b/templates/dashboard/student-dashboard.php @@ -250,14 +250,7 @@ class="tutor-btn tutor-btn-link tutor-btn-x-small tutor-text-brand tutor-p-none
- - - -
-
diff --git a/templates/loop/add-to-cart-tutor.php b/templates/loop/add-to-cart-tutor.php index ed5862538b..9962398d97 100644 --- a/templates/loop/add-to-cart-tutor.php +++ b/templates/loop/add-to-cart-tutor.php @@ -27,14 +27,14 @@ if ( $is_course_in_user_cart ) { ?> - +
- diff --git a/templates/loop/add-to-cart-woocommerce.php b/templates/loop/add-to-cart-woocommerce.php index 50dab33080..a97c03caed 100644 --- a/templates/loop/add-to-cart-woocommerce.php +++ b/templates/loop/add-to-cart-woocommerce.php @@ -43,7 +43,7 @@ ' ', array_filter( array( - 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block ', + apply_filters( 'tutor_course_loop_button_class', 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', $course_id ) . ' ', 'product_type_' . $product->get_type(), $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button ' : '', $ajax_add_to_cart_class, diff --git a/templates/loop/course-continue.php b/templates/loop/course-continue.php index 52349a2b7e..83b9838d0d 100644 --- a/templates/loop/course-continue.php +++ b/templates/loop/course-continue.php @@ -25,7 +25,7 @@ $completed_percent = tutor_utils()->get_course_completed_percent(); $is_completed_course = tutor_utils()->is_completed_course(); $retake_course = tutor_utils()->can_user_retake_course(); -$button_class = 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block '; +$button_class = apply_filters( 'tutor_course_loop_button_class', 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', $course_id ); $can_complete_course = CourseModel::can_complete_course( $course_id, $user_id ); $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); @@ -35,21 +35,21 @@ if ( $lesson_url && ! $is_completed_course ) { ob_start(); - $link_text = __( 'Continue Learning', 'tutor' ); + $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); if ( 0 === (int) $completed_percent ) { - $link_text = __( 'Start Learning', 'tutor' ); + $link_text = apply_filters( 'tutor_course_loop_start_button_text', __( 'Start Learning', 'tutor' ), $course_id ); } elseif ( $completed_percent > 0 && $completed_percent < 100 ) { - $link_text = __( 'Continue Learning', 'tutor' ); + $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); } elseif ( 100 === (int) $completed_percent && false === $can_complete_course ) { $lesson_url = CourseModel::get_review_progress_link( $course_id, $user_id ); - $link_text = __( 'Review Progress', 'tutor' ); + $link_text = apply_filters( 'tutor_course_loop_review_button_text', __( 'Review Progress', 'tutor' ), $course_id ); } else { - $link_text = __( 'Continue Learning', 'tutor' ); + $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); } ?> + data-course_id=""> ' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; + $enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; if ( tutor_utils()->is_course_purchasable() ) { $enroll_btn = tutor_course_loop_add_to_cart( false ); diff --git a/templates/loop/course-price-tutor.php b/templates/loop/course-price-tutor.php index 48c0f8d16e..79d5d018a8 100644 --- a/templates/loop/course-price-tutor.php +++ b/templates/loop/course-price-tutor.php @@ -17,7 +17,7 @@ $required_loggedin_class = apply_filters( 'tutor_enroll_required_login_class', 'tutor-open-login-modal' ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; diff --git a/templates/loop/course-price-woocommerce.php b/templates/loop/course-price-woocommerce.php index bfa5daa44e..1a13bc0bfe 100644 --- a/templates/loop/course-price-woocommerce.php +++ b/templates/loop/course-price-woocommerce.php @@ -20,7 +20,7 @@ $required_loggedin_class = apply_filters( 'tutor_enroll_required_login_class', 'tutor-open-login-modal' ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; // Show purchase button if purchaseable. diff --git a/templates/loop/course-price.php b/templates/loop/course-price.php index 55c31327b6..e5ad14ba69 100644 --- a/templates/loop/course-price.php +++ b/templates/loop/course-price.php @@ -35,7 +35,7 @@ $attrs_string .= sprintf( '%s="%s" ', esc_attr( $key ), esc_attr( $value ) ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; if ( tutor_utils()->is_course_purchasable() && 'wc' === $monetization ) { @@ -68,7 +68,7 @@
-
'; From 1edd841ef4535d1646cd454ab53ba9e948d3496d Mon Sep 17 00:00:00 2001 From: nur-alam Date: Thu, 5 Feb 2026 15:57:28 +0600 Subject: [PATCH 4/9] fix: replace tutor_dashboard_course_card_buttons with new action button function Replace the deprecated `tutor_dashboard_course_card_buttons` function with `tutor_course_action_button_backup` to provide consistent course action buttons. Remove the `tutor_course_loop_button_class` filter usage across templates and hardcode button classes for better performance and maintainability. The new function determines appropriate button text and URL based on course progress and completion state, including certificate download links for completed courses. --- includes/tutor-template-functions.php | 115 +++++++++++--------- templates/course-embed.php | 2 +- templates/dashboard/courses/course-card.php | 3 +- templates/dashboard/student-dashboard.php | 2 +- templates/loop/add-to-cart-tutor.php | 4 +- templates/loop/add-to-cart-woocommerce.php | 2 +- templates/loop/course-continue.php | 12 +- templates/loop/course-price-edd.php | 8 +- templates/loop/course-price-tutor.php | 3 +- templates/loop/course-price-woocommerce.php | 2 +- templates/loop/course-price.php | 4 +- 11 files changed, 82 insertions(+), 75 deletions(-) diff --git a/includes/tutor-template-functions.php b/includes/tutor-template-functions.php index d8af450077..beb4178c04 100644 --- a/includes/tutor-template-functions.php +++ b/includes/tutor-template-functions.php @@ -9,6 +9,7 @@ */ use Tutor\Models\CourseModel; +use TUTOR_CERT\Certificate; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -663,67 +664,73 @@ function tutor_course_loop_price() { echo apply_filters( 'tutor_course_loop_price', ob_get_clean(), $course_id ); //phpcs:ignore -- already escaped inside template file } } -if ( ! function_exists( 'tutor_dashboard_course_card_buttons' ) ) { + +if ( ! function_exists( 'tutor_course_action_button_backup' ) ) { /** - * Get dashboard course card buttons. + * Get course loop action button based on course state. * - * @since 1.0.0 + * @since 4.0.0 + * + * @param int $course_id Course ID. + * @param bool $should_echo Whether to echo or return. + * @param bool $return_raw Whether to return raw data (url, text, target). * - * @return mixed|void + * @return string|array|void */ - function tutor_dashboard_course_card_buttons() { - add_filter( - 'tutor_course_loop_button_class', - function () { - return 'tutor-btn tutor-btn-primary tutor-btn-small'; - }, - 20, - 2 - ); - add_filter( - 'tutor_course_loop_continue_button_text', - function () { - return 'Resume'; - }, - 20, - 2 - ); - add_filter( - 'tutor_course_loop_start_button_text', - function () { - return 'Start'; - }, - 20, - 2 - ); - add_filter( - 'tutor_course_loop_download_button_text', - function () { - return 'Download'; - }, - 20, - 2 - ); - add_filter( - 'tutor_course_loop_bundle_button_text', - function () { - return 'Bundle'; - }, - 20, - 2 - ); + function tutor_course_action_button_backup( $course_id = 0, $should_echo = false, $return_raw = false ) { + $course_id = $course_id ? $course_id : get_the_ID(); + + $button_text = __( 'Start', 'tutor' ); + $button_url = tutor_utils()->get_course_first_lesson( $course_id ); + $target_blank = false; + + if ( ! $button_url ) { + $button_url = get_the_permalink( $course_id ); + } + + $is_completed = tutor_utils()->is_completed_course( $course_id ); + $course_progress = tutor_utils()->get_course_completed_percent( $course_id, 0, true ); + + if ( $is_completed ) { + $button_text = __( 'Completed', 'tutor' ); + $button_url = get_the_permalink( $course_id ); + + if ( function_exists( 'TUTOR_CERT' ) && class_exists( '\TUTOR_CERT\Certificate' ) ) { + $cert_addon = new Certificate(); + if ( ! $cert_addon->disable_certificate_for_individual_courses( $course_id ) && $cert_addon->has_course_certificate_template( $course_id ) ) { + $cert_url = $cert_addon->get_certificate( $course_id ); + if ( $cert_url ) { + $button_text = __( 'Get Certificate', 'tutor' ); + $button_url = $cert_url; + $target_blank = true; + } + } + } + } elseif ( $course_progress['completed_percent'] > 0 ) { + $button_text = __( 'Resume', 'tutor' ); + } + + if ( $return_raw ) { + return array( + 'text' => $button_text, + 'url' => $button_url, + 'target' => $target_blank ? '_blank' : '_self', + ); + } + ob_start(); - tutor_course_loop_price(); - $content = ob_get_clean(); + ?> + > + + + diff --git a/templates/dashboard/courses/course-card.php b/templates/dashboard/courses/course-card.php index 387fd088d0..ea5558801c 100644 --- a/templates/dashboard/courses/course-card.php +++ b/templates/dashboard/courses/course-card.php @@ -90,8 +90,7 @@ -
- +
diff --git a/templates/dashboard/student-dashboard.php b/templates/dashboard/student-dashboard.php index 9fcc9ebd78..0fb8124b57 100644 --- a/templates/dashboard/student-dashboard.php +++ b/templates/dashboard/student-dashboard.php @@ -250,7 +250,7 @@ class="tutor-btn tutor-btn-link tutor-btn-x-small tutor-text-brand tutor-p-none
- +
diff --git a/templates/loop/add-to-cart-tutor.php b/templates/loop/add-to-cart-tutor.php index 9962398d97..64a48b1775 100644 --- a/templates/loop/add-to-cart-tutor.php +++ b/templates/loop/add-to-cart-tutor.php @@ -27,14 +27,14 @@ if ( $is_course_in_user_cart ) { ?> - +
- diff --git a/templates/loop/add-to-cart-woocommerce.php b/templates/loop/add-to-cart-woocommerce.php index a97c03caed..a7f146ecd3 100644 --- a/templates/loop/add-to-cart-woocommerce.php +++ b/templates/loop/add-to-cart-woocommerce.php @@ -43,7 +43,7 @@ ' ', array_filter( array( - apply_filters( 'tutor_course_loop_button_class', 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', $course_id ) . ' ', + 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', 'product_type_' . $product->get_type(), $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button ' : '', $ajax_add_to_cart_class, diff --git a/templates/loop/course-continue.php b/templates/loop/course-continue.php index 83b9838d0d..02895ccce2 100644 --- a/templates/loop/course-continue.php +++ b/templates/loop/course-continue.php @@ -25,7 +25,7 @@ $completed_percent = tutor_utils()->get_course_completed_percent(); $is_completed_course = tutor_utils()->is_completed_course(); $retake_course = tutor_utils()->can_user_retake_course(); -$button_class = apply_filters( 'tutor_course_loop_button_class', 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', $course_id ); +$button_class = 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block'; $can_complete_course = CourseModel::can_complete_course( $course_id, $user_id ); $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); @@ -35,16 +35,16 @@ if ( $lesson_url && ! $is_completed_course ) { ob_start(); - $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); + $link_text = __( 'Continue Learning', 'tutor' ); if ( 0 === (int) $completed_percent ) { - $link_text = apply_filters( 'tutor_course_loop_start_button_text', __( 'Start Learning', 'tutor' ), $course_id ); + $link_text = __( 'Start Learning', 'tutor' ); } elseif ( $completed_percent > 0 && $completed_percent < 100 ) { - $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); + $link_text = __( 'Continue Learning', 'tutor' ); } elseif ( 100 === (int) $completed_percent && false === $can_complete_course ) { $lesson_url = CourseModel::get_review_progress_link( $course_id, $user_id ); - $link_text = apply_filters( 'tutor_course_loop_review_button_text', __( 'Review Progress', 'tutor' ), $course_id ); + $link_text = __( 'Review Progress', 'tutor' ); } else { - $link_text = apply_filters( 'tutor_course_loop_continue_button_text', __( 'Continue Learning', 'tutor' ), $course_id ); + $link_text = __( 'Continue Learning', 'tutor' ); } ?> ' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; + $enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; if ( tutor_utils()->is_course_purchasable() ) { $enroll_btn = tutor_course_loop_add_to_cart( false ); diff --git a/templates/loop/course-price-tutor.php b/templates/loop/course-price-tutor.php index 79d5d018a8..da86525b39 100644 --- a/templates/loop/course-price-tutor.php +++ b/templates/loop/course-price-tutor.php @@ -17,7 +17,8 @@ $required_loggedin_class = apply_filters( 'tutor_enroll_required_login_class', 'tutor-open-login-modal' ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; + +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; diff --git a/templates/loop/course-price-woocommerce.php b/templates/loop/course-price-woocommerce.php index 1a13bc0bfe..bfa5daa44e 100644 --- a/templates/loop/course-price-woocommerce.php +++ b/templates/loop/course-price-woocommerce.php @@ -20,7 +20,7 @@ $required_loggedin_class = apply_filters( 'tutor_enroll_required_login_class', 'tutor-open-login-modal' ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; // Show purchase button if purchaseable. diff --git a/templates/loop/course-price.php b/templates/loop/course-price.php index e5ad14ba69..55c31327b6 100644 --- a/templates/loop/course-price.php +++ b/templates/loop/course-price.php @@ -35,7 +35,7 @@ $attrs_string .= sprintf( '%s="%s" ', esc_attr( $key ), esc_attr( $value ) ); } -$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; +$enroll_btn = '
' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; if ( tutor_utils()->is_course_purchasable() && 'wc' === $monetization ) { @@ -68,7 +68,7 @@
-
'; From 523dfdd17e69175e114827fe223c3c840514066b Mon Sep 17 00:00:00 2001 From: nur-alam Date: Thu, 5 Feb 2026 16:03:10 +0600 Subject: [PATCH 5/9] keep course loop template file as it was before --- templates/course-embed.php | 2 +- templates/loop/add-to-cart-tutor.php | 4 ++-- templates/loop/add-to-cart-woocommerce.php | 2 +- templates/loop/course-continue.php | 4 ++-- templates/loop/course-price-edd.php | 5 ++--- templates/loop/course-price-tutor.php | 1 - 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/templates/course-embed.php b/templates/course-embed.php index e5706ca9a8..0c740df05d 100644 --- a/templates/course-embed.php +++ b/templates/course-embed.php @@ -120,7 +120,7 @@ diff --git a/templates/loop/add-to-cart-tutor.php b/templates/loop/add-to-cart-tutor.php index 64a48b1775..ed5862538b 100644 --- a/templates/loop/add-to-cart-tutor.php +++ b/templates/loop/add-to-cart-tutor.php @@ -27,14 +27,14 @@ if ( $is_course_in_user_cart ) { ?> - +
- diff --git a/templates/loop/add-to-cart-woocommerce.php b/templates/loop/add-to-cart-woocommerce.php index a7f146ecd3..50dab33080 100644 --- a/templates/loop/add-to-cart-woocommerce.php +++ b/templates/loop/add-to-cart-woocommerce.php @@ -43,7 +43,7 @@ ' ', array_filter( array( - 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block', + 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block ', 'product_type_' . $product->get_type(), $product->is_purchasable() && $product->is_in_stock() ? 'add_to_cart_button ' : '', $ajax_add_to_cart_class, diff --git a/templates/loop/course-continue.php b/templates/loop/course-continue.php index 02895ccce2..52349a2b7e 100644 --- a/templates/loop/course-continue.php +++ b/templates/loop/course-continue.php @@ -25,7 +25,7 @@ $completed_percent = tutor_utils()->get_course_completed_percent(); $is_completed_course = tutor_utils()->is_completed_course(); $retake_course = tutor_utils()->can_user_retake_course(); -$button_class = 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block'; +$button_class = 'tutor-btn tutor-btn-outline-primary tutor-btn-md tutor-btn-block '; $can_complete_course = CourseModel::can_complete_course( $course_id, $user_id ); $completion_mode = tutor_utils()->get_option( 'course_completion_process' ); @@ -49,7 +49,7 @@ ?> + data-course_id=""> ' . apply_filters( 'tutor_course_restrict_new_entry', '' . __( 'Enroll Course', 'tutor' ) . '', $course_id ) . '
'; $free_html = $enroll_btn; From 2a1bcd12840fbec4d6952afd9f9e4344281c8125 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Fri, 6 Feb 2026 00:12:40 +0600 Subject: [PATCH 6/9] refactor: replace course action button with hook-based rendering Remove hardcoded tutor_course_action_button_backup function and replace direct calls with do_action('tutor_course_action_btn'). Add new render_course_action_btn method in Course class to handle button rendering via hook. --- classes/Course.php | 48 ++++++++++++-- includes/tutor-template-functions.php | 69 --------------------- templates/dashboard/courses/course-card.php | 4 +- templates/dashboard/student-dashboard.php | 2 +- 4 files changed, 46 insertions(+), 77 deletions(-) diff --git a/classes/Course.php b/classes/Course.php index 07daa1a258..ac7eddb86a 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -15,6 +15,7 @@ } use stdClass; +use Tutor\Components\Button; use TUTOR\Input; use Tutor\Ecommerce\Tax; use Tutor\Models\QuizModel; @@ -300,6 +301,8 @@ public function __construct( $register_hooks = true ) { add_filter( 'template_include', array( $this, 'handle_password_protected' ) ); add_action( 'login_form_postpass', array( $this, 'handle_password_submit' ) ); add_filter( 'the_preview', array( $this, 'handle_schedule_courses' ) ); + + add_action( 'tutor_course_action_btn', array( $this, 'render_course_action_btn' ) ); } /** @@ -1775,7 +1778,7 @@ private function save_course_content_order() { if ( is_array( $order ) && count( $order ) ) { $i = 0; foreach ( $order as $topic ) { - $i++; + ++$i; $wpdb->update( $wpdb->posts, array( 'menu_order' => $i ), @@ -2819,10 +2822,10 @@ public function tutor_lms_hide_course_complete_btn( $html ) { } } if ( ! $has_passed ) { - $required_assignment_pass++; + ++$required_assignment_pass; } } else { - $required_assignment_pass++; + ++$required_assignment_pass; } } @@ -2838,11 +2841,11 @@ public function tutor_lms_hide_course_complete_btn( $html ) { $earned_percentage = QuizModel::calculate_attempt_earned_percentage( $attempt ); if ( $earned_percentage < $passing_grade ) { - $required_quiz_pass++; + ++$required_quiz_pass; $is_quiz_pass = false; } } else { - $required_quiz_pass++; + ++$required_quiz_pass; $is_quiz_pass = false; } } @@ -3244,4 +3247,39 @@ private static function set_course_regular_and_sale_price( $post_ID, $regular_pr update_post_meta( $post_ID, self::COURSE_PRICE_META, $regular_price ); update_post_meta( $post_ID, self::COURSE_SALE_PRICE_META, $sale_price ); } + + /** + * Render start/resume button in enrolled course action btn. + * + * @since 4.0.0 + * + * @param int $course_id course id. + * + * @return void + */ + public function render_course_action_btn( int $course_id ) { + $is_completed = tutor_utils()->is_completed_course( $course_id ); + if ( $is_completed ) { + return; + } + + $course_progress = tutor_utils()->get_course_completed_percent( $course_id, 0, true ); + + $button_text = $course_progress['completed_percent'] > 0 ? __( 'Resume', 'tutor' ) : __( 'Start', 'tutor' ); + $button_url = tutor_utils()->get_course_first_lesson( $course_id ); + + if ( ! $button_url ) { + $button_url = get_the_permalink( $course_id ); + } + + ob_start(); + ?> + + + + get_course_first_lesson( $course_id ); - $target_blank = false; - - if ( ! $button_url ) { - $button_url = get_the_permalink( $course_id ); - } - - $is_completed = tutor_utils()->is_completed_course( $course_id ); - $course_progress = tutor_utils()->get_course_completed_percent( $course_id, 0, true ); - - if ( $is_completed ) { - $button_text = __( 'Completed', 'tutor' ); - $button_url = get_the_permalink( $course_id ); - - if ( function_exists( 'TUTOR_CERT' ) && class_exists( '\TUTOR_CERT\Certificate' ) ) { - $cert_addon = new Certificate(); - if ( ! $cert_addon->disable_certificate_for_individual_courses( $course_id ) && $cert_addon->has_course_certificate_template( $course_id ) ) { - $cert_url = $cert_addon->get_certificate( $course_id ); - if ( $cert_url ) { - $button_text = __( 'Get Certificate', 'tutor' ); - $button_url = $cert_url; - $target_blank = true; - } - } - } - } elseif ( $course_progress['completed_percent'] > 0 ) { - $button_text = __( 'Resume', 'tutor' ); - } - - if ( $return_raw ) { - return array( - 'text' => $button_text, - 'url' => $button_url, - 'target' => $target_blank ? '_blank' : '_self', - ); - } - - ob_start(); - ?> - > - - - -
+
@@ -91,6 +91,6 @@
- +
diff --git a/templates/dashboard/student-dashboard.php b/templates/dashboard/student-dashboard.php index 0fb8124b57..d56c2d083b 100644 --- a/templates/dashboard/student-dashboard.php +++ b/templates/dashboard/student-dashboard.php @@ -250,7 +250,7 @@ class="tutor-btn tutor-btn-link tutor-btn-x-small tutor-text-brand tutor-p-none
- +
From 849736505dd6bf5dd3e2791f7aeca8c92ef3c818 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Fri, 6 Feb 2026 00:18:48 +0600 Subject: [PATCH 7/9] fix indentation and remove unused import --- includes/tutor-template-functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/tutor-template-functions.php b/includes/tutor-template-functions.php index bd1b39888f..a15ccf4f76 100644 --- a/includes/tutor-template-functions.php +++ b/includes/tutor-template-functions.php @@ -9,7 +9,6 @@ */ use Tutor\Models\CourseModel; -use TUTOR_CERT\Certificate; if ( ! defined( 'ABSPATH' ) ) { exit; @@ -661,7 +660,7 @@ function tutor_course_loop_price() { tutor_load_template( 'loop.course-price' ); } } - echo apply_filters( 'tutor_course_loop_price', ob_get_clean(), $course_id ); //phpcs:ignore -- already escaped inside template file + echo apply_filters( 'tutor_course_loop_price', ob_get_clean(), $course_id ); //phpcs:ignore -- already escaped inside template file } } @@ -1148,6 +1147,7 @@ function tutor_lesson_lead_info( $lesson_id = 0, $echo = true ) { } return $output; + } } From f2f5e248f685a07429433f74445dbd39788a0d31 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Fri, 6 Feb 2026 11:16:37 +0600 Subject: [PATCH 8/9] change pre-increment to post-increment for consistency --- classes/Course.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/classes/Course.php b/classes/Course.php index ac7eddb86a..e4a78d153d 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -1778,7 +1778,7 @@ private function save_course_content_order() { if ( is_array( $order ) && count( $order ) ) { $i = 0; foreach ( $order as $topic ) { - ++$i; + $i++; $wpdb->update( $wpdb->posts, array( 'menu_order' => $i ), @@ -2822,10 +2822,10 @@ public function tutor_lms_hide_course_complete_btn( $html ) { } } if ( ! $has_passed ) { - ++$required_assignment_pass; + $required_assignment_pass++; } } else { - ++$required_assignment_pass; + $required_assignment_pass++; } } @@ -2841,11 +2841,11 @@ public function tutor_lms_hide_course_complete_btn( $html ) { $earned_percentage = QuizModel::calculate_attempt_earned_percentage( $attempt ); if ( $earned_percentage < $passing_grade ) { - ++$required_quiz_pass; + $required_quiz_pass++; $is_quiz_pass = false; } } else { - ++$required_quiz_pass; + $required_quiz_pass++; $is_quiz_pass = false; } } From d31beacb49e15ad4891698b08f31be49a164a8d5 Mon Sep 17 00:00:00 2001 From: nur-alam Date: Fri, 6 Feb 2026 11:27:25 +0600 Subject: [PATCH 9/9] refactor: remove unused imports from Course.php Unused imports were cluttering the file and potentially causing confusion. This cleanup improves code readability and maintainability by removing unnecessary dependencies. --- classes/Course.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/classes/Course.php b/classes/Course.php index e4a78d153d..4af8325d41 100644 --- a/classes/Course.php +++ b/classes/Course.php @@ -14,8 +14,6 @@ exit; } -use stdClass; -use Tutor\Components\Button; use TUTOR\Input; use Tutor\Ecommerce\Tax; use Tutor\Models\QuizModel; @@ -24,7 +22,6 @@ use Tutor\Ecommerce\Ecommerce; use Tutor\Traits\JsonResponse; use Tutor\Helpers\ValidationHelper; -use TutorPro\CourseBundle\Models\BundleModel; /** * Course Class