LearnDash.php
401 lines
| 1 | <?php |
| 2 | /** |
| 3 | * LearnDash integration for Presto Player. |
| 4 | * |
| 5 | * @package PrestoPlayer |
| 6 | */ |
| 7 | |
| 8 | namespace PrestoPlayer\Integrations\LearnDash; |
| 9 | |
| 10 | use PrestoPlayer\Models\Post; |
| 11 | use PrestoPlayer\Support\Utility; |
| 12 | use PrestoPlayer\Contracts\Service; |
| 13 | |
| 14 | /** |
| 15 | * Integrates Presto Player video progression with LearnDash. |
| 16 | */ |
| 17 | class LearnDash implements Service { |
| 18 | |
| 19 | /** |
| 20 | * Register the integration hooks. |
| 21 | * |
| 22 | * @return void |
| 23 | */ |
| 24 | public function register() { |
| 25 | add_action( |
| 26 | 'plugins_loaded', |
| 27 | function () { |
| 28 | if ( ! self::isEnabled() ) { |
| 29 | return; |
| 30 | } |
| 31 | |
| 32 | add_filter( 'learndash_settings_fields', array( $this, 'settingsFields' ), 10, 2 ); |
| 33 | add_filter( 'ld_video_provider', array( $this, 'addProvider' ), 10, 2 ); |
| 34 | add_filter( 'presto-settings-block-js-options', array( $this, 'jsOptions' ) ); |
| 35 | add_filter( 'get_post_metadata', array( $this, 'filterVideoURL' ), 10, 3 ); |
| 36 | |
| 37 | add_filter( 'presto_player/block/default_attributes', array( $this, 'addVideoAttributes' ) ); |
| 38 | add_filter( 'presto_player/templates/player_tag', array( $this, 'addPlayerTags' ) ); |
| 39 | } |
| 40 | ); |
| 41 | } |
| 42 | |
| 43 | /** |
| 44 | * Add tags to player |
| 45 | * |
| 46 | * @param array $data Player data containing cookie key and video progress. |
| 47 | * @return void |
| 48 | */ |
| 49 | public function addPlayerTags( $data ) { |
| 50 | if ( empty( $data['cookieKey'] ) || empty( $data['videoProgress'] ) ) { |
| 51 | return; |
| 52 | } |
| 53 | |
| 54 | ob_start(); |
| 55 | ?> |
| 56 | data-video-cookie-key="<?php echo esc_attr( $data['cookieKey'] ); ?>" |
| 57 | data-video-progression="<?php echo esc_attr( $data['videoProgress'] ); ?>" |
| 58 | data-video-provider="presto" |
| 59 | <?php |
| 60 | echo ob_get_clean(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Output composed of the esc_attr() escaped data attributes above. |
| 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Add attributes to video for video progression |
| 65 | * |
| 66 | * @param array $attributes Block attributes. |
| 67 | * @return array |
| 68 | */ |
| 69 | public function addVideoAttributes( $attributes ) { |
| 70 | global $post; |
| 71 | |
| 72 | // Bail if not a learndash post type. |
| 73 | if ( ! self::isLearnDashPost( $post ) ) { |
| 74 | return $attributes; |
| 75 | } |
| 76 | |
| 77 | $logic_video = true; |
| 78 | $video_id = ! empty( $attributes['id'] ) ? '(presto-' . $attributes['id'] . ')' : '(presto)'; |
| 79 | $video_completed = $this->stepIsCompleted( $post->ID ); |
| 80 | |
| 81 | if ( $video_completed ) { |
| 82 | $logic_video = false; |
| 83 | } else { |
| 84 | // Get lesson settings. |
| 85 | $step_settings = learndash_get_setting( $post ); |
| 86 | |
| 87 | if ( 'BEFORE' === ( $step_settings['lesson_video_shown'] ?? '' ) ) { |
| 88 | $logic_video = true; |
| 89 | |
| 90 | $topics = learndash_get_topic_list( $post->ID ); |
| 91 | if ( ! empty( $topics ) ) { |
| 92 | $progress = learndash_get_course_progress( null, $topics[0]->ID ); |
| 93 | if ( ! empty( $progress ) ) { |
| 94 | $topics_completed = 0; |
| 95 | foreach ( $progress['posts'] as $topic ) { |
| 96 | if ( (int) 1 === (int) $topic->completed ) { |
| 97 | ++$topics_completed; |
| 98 | break; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | if ( ! empty( $topics_completed ) ) { |
| 103 | $logic_video = false; |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | if ( true === $logic_video ) { |
| 111 | $logic_video_str = 'true'; |
| 112 | } else { |
| 113 | $logic_video_str = 'false'; |
| 114 | } |
| 115 | |
| 116 | $attributes['cookieKey'] = $this->buildVideoCookieKey( $video_id ); |
| 117 | $attributes['videoProgress'] = $logic_video_str; |
| 118 | |
| 119 | return $attributes; |
| 120 | } |
| 121 | |
| 122 | /** |
| 123 | * Build unique video progress cookie key. This is used to track the video state |
| 124 | * in the user's browser. |
| 125 | * |
| 126 | * @param integer $attach_id Attachment ID of the video. |
| 127 | * |
| 128 | * @return string $cookie_key. |
| 129 | */ |
| 130 | public function buildVideoCookieKey( $attach_id = '' ) { |
| 131 | $cookie_key = ''; |
| 132 | $cookie_key = $this->getNonceSlug(); |
| 133 | |
| 134 | if ( ( isset( $attach_id ) ) && ( ! empty( $attach_id ) ) ) { |
| 135 | $lesson_video_url = trim( $attach_id ); |
| 136 | $lesson_video_url = html_entity_decode( $lesson_video_url ); |
| 137 | |
| 138 | $cookie_key .= '_' . $lesson_video_url; |
| 139 | } |
| 140 | $cookie_key = 'learndash-video-progress-' . md5( $cookie_key ); |
| 141 | |
| 142 | return $cookie_key; |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * Utility function to get the nonce slug. |
| 147 | * |
| 148 | * @since 3.2.3 |
| 149 | */ |
| 150 | protected function getNonceSlug() { |
| 151 | $post_id = get_the_ID(); |
| 152 | $step_id = $post_id; |
| 153 | $course_id = learndash_get_course_id( $post_id ); |
| 154 | $user_id = (int) get_current_user_id(); |
| 155 | |
| 156 | return 'learndash_video_' . $user_id . '_' . $course_id . '_' . $step_id; |
| 157 | } |
| 158 | |
| 159 | /** |
| 160 | * Dynamically replace (presto) with (presto-$video_id) |
| 161 | * |
| 162 | * @param mixed $value The meta value being filtered. |
| 163 | * @param integer $object_id The post ID the meta belongs to. |
| 164 | * @param string $meta_key The meta key being requested. |
| 165 | * |
| 166 | * @return mixed |
| 167 | */ |
| 168 | public function filterVideoURL( $value, $object_id, $meta_key ) { |
| 169 | // Prevent recursion. |
| 170 | remove_filter( current_filter(), __FUNCTION__ ); |
| 171 | |
| 172 | // Only learndash meta. |
| 173 | if ( ! in_array( $meta_key, array( '_sfwd-topic', '_sfwd-lessons' ) ) ) { |
| 174 | return $value; |
| 175 | } |
| 176 | |
| 177 | // Get meta. |
| 178 | $meta_cache = wp_cache_get( $object_id, 'post_meta' ); |
| 179 | if ( ! $meta_cache ) { |
| 180 | $meta_cache = update_meta_cache( 'post', array( $object_id ) ); |
| 181 | if ( isset( $meta_cache[ $object_id ] ) ) { |
| 182 | $meta_cache = $meta_cache[ $object_id ]; |
| 183 | } else { |
| 184 | $meta_cache = null; |
| 185 | } |
| 186 | } |
| 187 | if ( ! $meta_key ) { |
| 188 | return $meta_cache; |
| 189 | } |
| 190 | |
| 191 | if ( isset( $meta_cache[ $meta_key ] ) ) { |
| 192 | $saved = maybe_unserialize( $meta_cache[ $meta_key ][0] ); |
| 193 | |
| 194 | if ( is_array( $saved ) ) { |
| 195 | $key = array_key_exists( 'sfwd-lessons_lesson_video_url', $saved ) ? 'sfwd-lessons_lesson_video_url' : ''; |
| 196 | $key = array_key_exists( 'sfwd-topic_lesson_video_url', $saved ) ? 'sfwd-topic_lesson_video_url' : $key; |
| 197 | } |
| 198 | |
| 199 | if ( ! $key ) { |
| 200 | return $value; |
| 201 | } |
| 202 | |
| 203 | if ( strpos( $saved[ $key ], '(presto' ) === false ) { |
| 204 | return $value; |
| 205 | } |
| 206 | |
| 207 | $post_model = new Post( get_post( $object_id ) ); |
| 208 | $video_id = $post_model->findVideoId(); |
| 209 | $saved[ $key ] = "(presto-$video_id)"; |
| 210 | $meta_cache[ $meta_key ][0] = $saved; |
| 211 | return $meta_cache[ $meta_key ]; |
| 212 | } |
| 213 | |
| 214 | return $value; |
| 215 | } |
| 216 | |
| 217 | /** |
| 218 | * Pass javascript options to presto player |
| 219 | * |
| 220 | * @param array $options Javascript options passed to the player. |
| 221 | * @return array |
| 222 | */ |
| 223 | public function jsOptions( $options ) { |
| 224 | if ( self::isEnabled() ) { |
| 225 | global $post; |
| 226 | $settings = learndash_get_setting( $post ); |
| 227 | |
| 228 | if ( ! empty( $settings['lesson_video_auto_complete_delay'] ) ) { |
| 229 | $post_type_obj = get_post_type_object( $post->post_type ); |
| 230 | $settings['videos_auto_complete_delay_message'] = sprintf( |
| 231 | // translators: placeholders: 1. Lesson or Topic label, 2. span for counter. |
| 232 | wp_kses_post( _x( '<p class="ld-video-delay-message">%1$s will auto complete in %2$s seconds</p>', 'placeholders: 1. Lesson or Topic label, 2. span for counter', 'presto-player' ) ), |
| 233 | $post_type_obj->labels->singular_name, |
| 234 | '<span class="time-countdown">' . $settings['lesson_video_auto_complete_delay'] . '</span>' |
| 235 | ); |
| 236 | } |
| 237 | |
| 238 | $options['learndash'] = $settings; |
| 239 | } |
| 240 | |
| 241 | return $options; |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Is LearnDash enabled? |
| 246 | * |
| 247 | * @return boolean |
| 248 | */ |
| 249 | public static function isEnabled() { |
| 250 | return defined( 'LEARNDASH_VERSION' ); |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Should the video load on the learndash page |
| 255 | * |
| 256 | * @return boolean |
| 257 | */ |
| 258 | public static function shouldVideoLoad() { |
| 259 | global $post; |
| 260 | |
| 261 | // Bail if not a learndash post type. |
| 262 | if ( ! self::isLearnDashPost( $post ) ) { |
| 263 | return true; |
| 264 | } |
| 265 | |
| 266 | // Step is completed, load video. |
| 267 | if ( self::stepIsCompleted( $post ) ) { |
| 268 | return true; |
| 269 | } |
| 270 | |
| 271 | // Check if lesson steps are complete. |
| 272 | return self::areStepsComplete( $post ); |
| 273 | } |
| 274 | |
| 275 | /** |
| 276 | * Is this a learndash post? |
| 277 | * |
| 278 | * @param \WP_Post $post The post to check. |
| 279 | * @return boolean |
| 280 | */ |
| 281 | public static function isLearnDashPost( $post ) { |
| 282 | if ( ! $post ) { |
| 283 | return false; |
| 284 | } |
| 285 | |
| 286 | return in_array( $post->post_type, array( 'sfwd-lessons', 'sfwd-topic' ) ); |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * Is the learndash step completed |
| 291 | * |
| 292 | * @param \WP_Post $post The post to check. |
| 293 | * @return bool |
| 294 | */ |
| 295 | public static function stepIsCompleted( $post ) { |
| 296 | if ( ! function_exists( 'learndash_get_course_progress' ) ) { |
| 297 | return true; |
| 298 | } |
| 299 | |
| 300 | global $post; |
| 301 | $progress = learndash_get_course_progress( null, $post->ID ); |
| 302 | return ( ! empty( $progress['this'] ) ) && ( $progress['this'] instanceof \WP_Post ) && ( true === (bool) $progress['this']->completed ); |
| 303 | } |
| 304 | |
| 305 | /** |
| 306 | * Are lesson/topic steps complete? |
| 307 | * |
| 308 | * @param \WP_Post $post The post to check. |
| 309 | * @return boolean |
| 310 | */ |
| 311 | public static function areStepsComplete( \WP_Post $post ) { |
| 312 | // Get lesson settings. |
| 313 | $lesson_settings = learndash_get_setting( $post ); |
| 314 | |
| 315 | // We're only concerned with "AFTER". |
| 316 | if ( 'AFTER' !== ( $lesson_settings['lesson_video_shown'] ?? '' ) ) { |
| 317 | return true; |
| 318 | } |
| 319 | |
| 320 | // If this is a lesson, check if topics are completed. |
| 321 | if ( 'sfwd-lessons' === $post->post_type ) { |
| 322 | if ( ! learndash_lesson_topics_completed( $post->ID ) ) { |
| 323 | return false; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | // Quizes must also be completed. |
| 328 | return self::areQuizzesCompleted( $post ); |
| 329 | } |
| 330 | |
| 331 | /** |
| 332 | * Are quizzes completed? |
| 333 | * |
| 334 | * @param \WP_Post $post The post to check. |
| 335 | * @return boolean |
| 336 | */ |
| 337 | public static function areQuizzesCompleted( \WP_Post $post ) { |
| 338 | // Quizes must also be completed. |
| 339 | $quizzes_completed = true; |
| 340 | $lesson_quizzes_list = learndash_get_lesson_quiz_list( $post->ID ); |
| 341 | if ( ! empty( $lesson_quizzes_list ) ) { |
| 342 | foreach ( $lesson_quizzes_list as $quiz ) { |
| 343 | if ( 'completed' !== $quiz['status'] ) { |
| 344 | $quizzes_completed = false; |
| 345 | break; |
| 346 | } |
| 347 | } |
| 348 | } |
| 349 | return (bool) $quizzes_completed; |
| 350 | } |
| 351 | |
| 352 | /** |
| 353 | * Add our video provider to learndash |
| 354 | * |
| 355 | * @param array $video_data The current video provider data. |
| 356 | * @param array $step_settings The LearnDash step settings. |
| 357 | * @return array |
| 358 | */ |
| 359 | public function addProvider( $video_data, $step_settings ) { |
| 360 | if ( strpos( $step_settings['lesson_video_url'], '(presto' ) !== false ) { |
| 361 | return 'presto'; |
| 362 | } |
| 363 | return $video_data; |
| 364 | } |
| 365 | |
| 366 | /** |
| 367 | * Adds our setting to the lesson settings page |
| 368 | * |
| 369 | * @param array $settings The current settings fields. |
| 370 | * @param string $meta_box_key The settings meta box key. |
| 371 | * @return array |
| 372 | */ |
| 373 | public function settingsFields( $settings, $meta_box_key ) { |
| 374 | // If it's not one of these settings pages, bail. |
| 375 | if ( ! in_array( $meta_box_key, array( 'learndash-lesson-display-content-settings', 'learndash-topic-display-content-settings' ) ) ) { |
| 376 | return $settings; |
| 377 | } |
| 378 | |
| 379 | $setting = array( |
| 380 | 'lesson_use_presto_video' => array( |
| 381 | 'name' => 'lesson_use_presto_video', |
| 382 | 'label' => esc_html__( 'Use Presto Video', 'presto-player' ), |
| 383 | 'type' => 'checkbox-switch', |
| 384 | 'value' => ! empty( $settings['use_presto_video'] ) ? $settings['use_presto_video'] : '', |
| 385 | 'help_text' => esc_html__( 'Use the Presto Player video in your post content for video progression.', 'presto-player' ), |
| 386 | 'default' => '', |
| 387 | 'options' => array( |
| 388 | 'on' => esc_html__( 'The presto video in this post will be used for video progression.', 'presto-player' ), |
| 389 | '' => '', |
| 390 | ), |
| 391 | 'parent_setting' => 'lesson_video_enabled', |
| 392 | ), |
| 393 | ); |
| 394 | |
| 395 | // Insert before video url. |
| 396 | $settings = Utility::arrayInsert( $settings, $setting, 'lesson_video_url', 'before' ); |
| 397 | |
| 398 | return $settings; |
| 399 | } |
| 400 | } |
| 401 |