Skip to content

Commit b7126d0

Browse files
authored
Merge pull request #2316 from themeum/dev
3.9.5 merged to master
2 parents b31b432 + cf45ae9 commit b7126d0

File tree

14 files changed

+76
-29
lines changed

14 files changed

+76
-29
lines changed

assets/json/countries.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18156,6 +18156,10 @@
1815618156
"id": 5106,
1815718157
"name": "Cuenca"
1815818158
},
18159+
{
18160+
"id": 5117,
18161+
"name": "Ceuta"
18162+
},
1815918163
{
1816018164
"id": 1191,
1816118165
"name": "Gipuzkoa"
@@ -18204,6 +18208,10 @@
1820418208
"id": 5090,
1820518209
"name": "Lugo"
1820618210
},
18211+
{
18212+
"id": 5118,
18213+
"name": "Melilla"
18214+
},
1820718215
{
1820818216
"id": 1158,
1820918217
"name": "Madrid"

assets/react/front/course/_archive.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,6 @@ window.jQuery(document).ready($ => {
136136
});
137137
filter_criteria['tutor-course-filter-category'] = [...new Set(category_ids)];
138138
}
139-
else {
140-
filter_criteria['tutor-course-filter-category'] = JSON.parse($("#course_filter_categories").val());
141-
}
142139

143140
}
144141

assets/react/v3/entries/course-builder/components/course-basic/CourseBasicSidebar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { useState } from 'react';
66
import { Controller, useFormContext, useWatch } from 'react-hook-form';
77

88
import SVGIcon from '@TutorShared/atoms/SVGIcon';
9-
10-
import ScheduleOptions from '@CourseBuilderComponents/course-basic/ScheduleOptions';
119
import FormCategoriesInput from '@TutorShared/components/fields/FormCategoriesInput';
1210
import FormImageInput from '@TutorShared/components/fields/FormImageInput';
1311
import FormInput from '@TutorShared/components/fields/FormInput';
@@ -16,6 +14,8 @@ import FormSelectUser, { type UserOption } from '@TutorShared/components/fields/
1614
import FormTagsInput from '@TutorShared/components/fields/FormTagsInput';
1715
import FormVideoInput from '@TutorShared/components/fields/FormVideoInput';
1816

17+
import ScheduleOptions from '@CourseBuilderComponents/course-basic/ScheduleOptions';
18+
1919
import type { CourseDetailsResponse, CourseFormData } from '@CourseBuilderServices/course';
2020
import { getCourseId } from '@CourseBuilderUtils/utils';
2121
import { tutorConfig } from '@TutorShared/config/config';
@@ -291,6 +291,7 @@ const CourseBasicSidebar = () => {
291291
emptyStateText={__('No instructors added.', 'tutor')}
292292
isInstructorMode
293293
visibilityKey={VisibilityControlKeys.COURSE_BUILDER.BASICS.INSTRUCTORS}
294+
postAuthor={courseDetails?.post_author}
294295
/>
295296
)}
296297
/>

assets/react/v3/shared/components/fields/FormSelectUser.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { AnimationType } from '@TutorShared/hooks/useAnimation';
1414
import { useDebounce } from '@TutorShared/hooks/useDebounce';
1515
import { useSelectKeyboardNavigation } from '@TutorShared/hooks/useSelectKeyboardNavigation';
1616

17+
import { type CourseDetailsResponse } from '@CourseBuilderServices/course';
1718
import Show from '@TutorShared/controls/Show';
1819
import { withVisibilityControl } from '@TutorShared/hoc/withVisibilityControl';
1920
import type { User } from '@TutorShared/services/users';
@@ -45,6 +46,7 @@ type FormSelectUserProps = {
4546
helpText?: string;
4647
emptyStateText?: string;
4748
isInstructorMode?: boolean;
49+
postAuthor?: CourseDetailsResponse['post_author'];
4850
} & FormControllerProps<UserOption | UserOption[] | null>;
4951

5052
const userPlaceholderData: UserOption = {
@@ -54,6 +56,8 @@ const userPlaceholderData: UserOption = {
5456
avatar_url: 'https://gravatar.com/avatar',
5557
};
5658

59+
const currentUser = tutorConfig.current_user;
60+
5761
const FormSelectUser = ({
5862
field,
5963
fieldState,
@@ -70,10 +74,12 @@ const FormSelectUser = ({
7074
helpText,
7175
emptyStateText = __('No user selected', __TUTOR_TEXT_DOMAIN__),
7276
isInstructorMode = false,
77+
postAuthor,
7378
}: FormSelectUserProps) => {
7479
const inputValue = field.value ?? (isMultiSelect ? [] : userPlaceholderData);
7580
const selectedIds = Array.isArray(inputValue) ? inputValue.map((item) => String(item.id)) : [String(inputValue.id)];
76-
const isCurrentUserAdmin = tutorConfig.current_user.roles?.includes(TutorRoles.ADMINISTRATOR);
81+
const isCurrentUserAdmin = currentUser.roles?.includes(TutorRoles.ADMINISTRATOR);
82+
const isCurrentUserAuthor = String(currentUser.data.id) === String(postAuthor?.ID || '');
7783

7884
const triggerRef = useRef<HTMLDivElement>(null);
7985
const [isOpen, setIsOpen] = useState(false);
@@ -257,7 +263,7 @@ const FormSelectUser = ({
257263
</button>
258264
}
259265
>
260-
<Show when={isCurrentUserAdmin || instructor.isRemoveAble}>
266+
<Show when={isCurrentUserAdmin || isCurrentUserAuthor || instructor.isRemoveAble}>
261267
<button
262268
type="button"
263269
onClick={() => handleDeleteSelection(instructor.id)}

assets/react/v3/shared/config/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const defaultTutorConfig = {
5858
filter: null,
5959
},
6060
settings: {
61-
monetize_by: __TUTOR_TEXT_DOMAIN__,
61+
monetize_by: 'tutor',
6262
enable_course_marketplace: 'off',
6363
course_permalink_base: '',
6464
supported_video_sources: '',

classes/Quiz.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ public function tutor_instructor_feedback() {
283283
$attempt_details = self::attempt_details( Input::post( 'attempt_id', 0, Input::TYPE_INT ) );
284284
$feedback = Input::post( 'feedback', '', Input::TYPE_KSES_POST );
285285
$attempt_info = isset( $attempt_details->attempt_info ) ? $attempt_details->attempt_info : false;
286-
$course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_info, 0 );
287-
288-
if ( ! tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id ) ) {
286+
$course_id = tutor_utils()->avalue_dot( 'course_id', $attempt_details, 0 );
287+
$is_instructor = tutor_utils()->is_instructor_of_this_course( get_current_user_id(), $course_id );
288+
if ( ! current_user_can( 'manage_options' ) && ! $is_instructor ) {
289289
wp_send_json_error( tutor_utils()->error_message() );
290290
}
291291

classes/User.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,9 @@ public function edit_user_profile( $user ) {
245245
private function delete_existing_user_photo( $user_id, $type ) {
246246
$meta_key = 'cover_photo' == $type ? '_tutor_cover_photo' : '_tutor_profile_photo';
247247
$photo_id = get_user_meta( $user_id, $meta_key, true );
248-
is_numeric( $photo_id ) ? wp_delete_attachment( $photo_id, true ) : 0;
248+
if ( is_numeric( $photo_id ) ) {
249+
wp_delete_attachment( $photo_id, true );
250+
}
249251
delete_user_meta( $user_id, $meta_key );
250252
}
251253

@@ -281,7 +283,7 @@ public function update_user_photo() {
281283
/**
282284
* Photo Update from profile
283285
*/
284-
$photo = tutor_utils()->array_get( 'photo_file', $_FILES );
286+
$photo = tutor_utils()->array_get( 'photo_file', $_FILES ); //phpcs:ignore -- already sanitized.
285287
$photo_size = tutor_utils()->array_get( 'size', $photo );
286288
$photo_type = tutor_utils()->array_get( 'type', $photo );
287289

@@ -373,6 +375,14 @@ public function profile_update( $user_id ) {
373375
$_tutor_profile_bio = Input::post( self::PROFILE_BIO_META, '', Input::TYPE_KSES_POST );
374376
$_tutor_profile_image = Input::post( self::PROFILE_PHOTO_META, '', Input::TYPE_KSES_POST );
375377

378+
if ( is_numeric( $_tutor_profile_image ) ) {
379+
$attachment = get_post( $_tutor_profile_image );
380+
381+
if ( 'attachment' === $attachment->post_type && $user_id !== $attachment->post_author ) {
382+
return;
383+
}
384+
}
385+
376386
update_user_meta( $user_id, self::PROFILE_JOB_TITLE_META, $_tutor_profile_job_title );
377387
update_user_meta( $user_id, self::PROFILE_BIO_META, $_tutor_profile_bio );
378388
update_user_meta( $user_id, self::PROFILE_PHOTO_META, $_tutor_profile_image );

classes/Utils.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4066,11 +4066,13 @@ public function get_course_rating( $course_id = 0 ) {
40664066
WHERE comments.comment_post_ID = %d
40674067
AND comments.comment_type = %s
40684068
AND commentmeta.meta_key = %s
4069+
AND comments.comment_approved = %s
40694070
GROUP BY CAST(commentmeta.meta_value AS SIGNED);
40704071
",
40714072
$course_id,
40724073
'tutor_course_rating',
4073-
'tutor_rating'
4074+
'tutor_rating',
4075+
'approved'
40744076
)
40754077
);
40764078

models/CartModel.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,19 @@ public function add_course_to_cart( $user_id, $course_id, $item_type = '', $item
7373
);
7474
}
7575

76+
if ( 'gift' === $item_type ) {
77+
$item_details = wp_json_encode( $item_details, JSON_UNESCAPED_UNICODE );
78+
} else {
79+
$item_details = wp_json_encode( $item_details );
80+
}
81+
7682
return QueryHelper::insert(
7783
"{$wpdb->prefix}tutor_cart_items",
7884
array(
7985
'cart_id' => $user_cart_id,
8086
'course_id' => $course_id,
8187
'item_type' => $item_type,
82-
'item_details' => $item_details ? wp_json_encode( $item_details ) : null,
88+
'item_details' => $item_details,
8389
)
8490
);
8591
}
@@ -261,5 +267,4 @@ public function clear_user_cart( $user_id ) {
261267
)
262268
);
263269
}
264-
265270
}

readme.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Tags: lms, course, elearning, education, learning management system
55
Requires at least: 5.3
66
Tested up to: 6.8
77
Requires PHP: 7.4
8-
Stable tag: 3.9.4
8+
Stable tag: 3.9.5
99
License: GPLv3
1010
License URI: https://www.gnu.org/licenses/gpl-3.0.html
1111

@@ -319,6 +319,20 @@ Tutor LMS allows you to offer certificates to your students upon course completi
319319

320320
== Changelog ==
321321

322+
= 3.9.5 - Jan 08, 2025
323+
324+
New: Certificate download button added for Admins and Instructors
325+
Update: Added support for PHP 8.4
326+
Update: Instructors can remove co-instructors from their own courses
327+
Update: Multiple improvements and fixes to gift course functionality
328+
Fix: Fixed security issues
329+
Fix: Users can now retry payment after a failed transaction
330+
Fix: Filters no longer stop working when the category filter is disabled on the course list page
331+
Fix: Fixed datepicker style conflict with WooCommerce coupon date picker UI
332+
Fix: Unpublished rating count no longer appears on the course details page
333+
Fix: Added some missing cities of Spain to the checkout page dropdown
334+
Fix: Gift course messages now support special characters
335+
322336
= 3.9.4 - Dec 18, 2025
323337

324338
Update: Compatibility with WordPress 6.9

0 commit comments

Comments
 (0)