gocodebox/lifterlms

View on GitHub
includes/abstracts/llms-abstract-processor-user-engagement-sync.php

Summary

Maintainability
A
3 hrs
Test Coverage
A
92%
<?php
/**
 * LLMS_Abstract_Processor_User_Engagement_Sync class
 *
 * @package LifterLMS/Abstracts/Classes
 *
 * @since 6.0.0
 * @version 6.0.0
 */

defined( 'ABSPATH' ) || exit;

/**
 * Base processor class for syncing awarded engagements (certificates or achievements) to their engagement template.
 *
 * @since 6.0.0
 */
abstract class LLMS_Abstract_Processor_User_Engagement_Sync extends LLMS_Abstract_Processor {

    use LLMS_Trait_User_Engagement_Type;

    /**
     * A text type for an admin notice that the sync of awarded engagements to an engagement template is already scheduled.
     *
     * @since 6.0.0
     *
     * @var int
     */
    protected const TEXT_SYNC_NOTICE_ALREADY_SCHEDULED = 0;

    /**
     * A text type for an admin notice that the sync of awarded engagements to an engagement template is complete.
     *
     * @since 6.0.0
     *
     * @var int
     */
    protected const TEXT_SYNC_NOTICE_AWARDED_ENGAGEMENTS_COMPLETE = 1;

    /**
     * A text type for an admin notice that there are no awarded engagements to sync the template with.
     *
     * @since 6.0.0
     *
     * @var int
     */
    protected const TEXT_SYNC_NOTICE_NO_AWARDED_ENGAGEMENTS = 2;

    /**
     * A text type for an admin notice that the sync of awarded engagements to an engagement template is scheduled.
     *
     * @since 6.0.0
     *
     * @var int
     */
    protected const TEXT_SYNC_NOTICE_SCHEDULED = 3;

    /**
     * Clear notices.
     *
     * @since 6.0.0
     *
     * @param int $engagement_template_id WP Post ID of the user engagement template.
     * @return void
     */
    private function clear_notices( $engagement_template_id ) {
        $notices = array(
            'awarded-%1$ss-sync-%2$d-scheduled',
            'awarded-%1$ss-sync-%2$d-already-scheduled',
            'awarded-%1$ss-sync-%2$d-done',
        );

        foreach ( $notices as $notice ) {
            LLMS_Admin_Notices::delete_notice(
                sprintf( $notice, $this->engagement_type, $engagement_template_id )
            );
        }
    }

    /**
     * Action triggered to sync all the awarded engagements that need to be updated.
     *
     * @since 6.0.0
     *
     * @param int $engagement_template_id WP Post ID of the engagement template.
     * @return void
     */
    public function dispatch_sync( $engagement_template_id ) {

        $this->log(
            sprintf(
                'awarded %1$ss bulk sync dispatched for the %1$s template %2$s (#%3$d)',
                $this->engagement_type,
                get_the_title( $engagement_template_id ),
                $engagement_template_id
            )
        );

        /**
         * Filter the query arguments used when retrieving the awarded engagements to sync.
         *
         * The dynamic portion of the hook name,
         * {@see LLMS_Abstract_Processor_User_Engagement_Sync::$engagement_type `$this->engagement_type`},
         * refers to the engagement type, either 'achievement' or 'certificate'.
         *
         * @since 6.0.0
         *
         * @param array $args Query arguments passed to LLMS_Awards_Query.
         */
        $args = apply_filters(
            "llms_processor_sync_awarded_{$this->engagement_type}s_query_args",
            array(
                'templates' => $engagement_template_id,
                'per_page'  => 20,
                'page'      => 1,
                'status'    => array(
                    'publish',
                    'future',
                ),
                'type'      => $this->engagement_type,
            )
        );

        $query = new LLMS_Awards_Query( $args );

        if ( ! $query->get_found_results() ) {
            return;
        }

        while ( $args['page'] <= $query->get_max_pages() ) {
            $this->push_to_queue(
                array(
                    'query_args' => $args,
                )
            );

            $args['page'] ++;
        }

        // Save queue and dispatch the process.
        $this->save()->dispatch();
    }

    /**
     * Returns a translated text of the given type.
     *
     * @since 6.0.0
     *
     * @param int   $text_type One of the LLMS_Abstract_Processor_User_Engagement_Sync::TEXT_ constants.
     * @param array $variables Optional variables that are used in sprintf().
     * @return string
     */
    protected function get_text( $text_type, $variables = array() ) {

        return __( 'Invalid text type.', 'lifterlms' );
    }

    /**
     * Initializer.
     *
     * @since 6.0.0
     *
     * @return void
     */
    protected function init() {

        // For the cron.
        add_action( $this->schedule_hook, array( $this, 'dispatch_sync' ), 10, 1 );

        // For LifterLMS actions which trigger bulk enrollment.
        $this->actions = array(
            "llms_do_awarded_{$this->engagement_type}s_bulk_sync" => array(
                'arguments' => 1,
                /** @see LLMS_Abstract_Processor_User_Engagement_Sync::schedule_sync() */
                'callback'  => 'schedule_sync',
                'priority'  => 10,
            ),
        );
    }

    /**
     * Perform actions when the process is completed.
     *
     * @since 6.0.0
     *
     * @param array $args Array of processing data.
     * @return void
     */
    private function process_completed( $args ) {

        $this->log(
            sprintf(
                'awarded %1$s bulk sync completed for the %1$s template %2$s (#%3$d)',
                $this->engagement_type,
                get_the_title( $args['query_args']['templates'] ),
                $args['query_args']['templates']
            )
        );

        $this->clear_notices( $args['query_args']['templates'] );

        LLMS_Admin_Notices::add_notice(
            sprintf( 'awarded-%1$ss-sync-%2$d-done', $this->engagement_type, $args['query_args']['templates'] ),
            $this->get_text(
                self::TEXT_SYNC_NOTICE_AWARDED_ENGAGEMENTS_COMPLETE,
                array( 'engagement_template_id' => $args['query_args']['templates'] )
            ),
            array(
                'dismissible'      => true,
                'dismiss_for_days' => 0,
                'type'             => 'success',
            )
        );
    }

    /**
     * Schedule sync.
     *
     * This will schedule an event that will setup the queue of items for the background process.
     *
     * @since 6.0.0
     *
     * @param int $engagement_template_id WP Post ID of the user engagement template.
     * @return void
     */
    public function schedule_sync( $engagement_template_id ) {

        $this->log(
            sprintf(
                'awarded %1$ss bulk sync for the %1$s template %2$s (#%3$d)',
                $this->engagement_type,
                get_the_title( $engagement_template_id ),
                $engagement_template_id
            )
        );

        $this->clear_notices( $engagement_template_id );

        $args          = array( $engagement_template_id );
        $awarded_count = $this->count_awarded_engagements( $engagement_template_id );

        if ( 0 === $awarded_count ) {

            $log_message    = 'no awarded %1$ss to bulk sync with the %1$s template %2$s (#%3$d)';
            $notice_message = $this->get_text( self::TEXT_SYNC_NOTICE_NO_AWARDED_ENGAGEMENTS, compact( 'engagement_template_id' ) );
            $notice_id      = 'awarded-%1$ss-sync-%2$d-no-awarded';
            $notice_type    = 'error';

        } elseif ( wp_next_scheduled( $this->schedule_hook, $args ) ) {

            $log_message    = 'awarded %1$ss bulk sync already scheduled for the %1$s template %2$s (#%3$d)';
            $notice_message = $this->get_text( self::TEXT_SYNC_NOTICE_ALREADY_SCHEDULED, compact( 'engagement_template_id' ) );
            $notice_id      = 'awarded-%1$ss-sync-%2$d-already-scheduled';
            $notice_type    = 'warning';
        } else {

            wp_schedule_single_event( time(), $this->schedule_hook, $args );
            $log_message    = 'awarded %1$ss bulk sync scheduled for the %1$s template %2$s (#%3$d)';
            $notice_message = $this->get_text( self::TEXT_SYNC_NOTICE_SCHEDULED, compact( 'engagement_template_id' ) );
            $notice_id      = 'awarded-%1$ss-sync-%2$d-scheduled';
            $notice_type    = 'info';
        }

        $this->log(
            sprintf(
                $log_message,
                $this->engagement_type,
                get_the_title( $engagement_template_id ),
                $engagement_template_id
            )
        );

        LLMS_Admin_Notices::add_notice(
            sprintf( $notice_id, $this->engagement_type, $engagement_template_id ),
            $notice_message,
            array(
                'dismissible'      => true,
                'dismiss_for_days' => 0,
                'type'             => $notice_type,
            )
        );
    }

    /**
     * Sync awarded engagements.
     *
     * @since 6.0.0
     *
     * @param LLMS_Abstract_User_Engagement[] $engagements Array of awarded engagements to sync.
     * @param int                             $template_id Engagement template ID.
     * @return void
     */
    private function sync_awarded_engagements( $engagements, $template_id ) {

        $success_log_message = 'awarded %1$s %2$s (#%3$d) successfully synced with template %4$s (#%5$d)';
        $error_log_message   = 'an error occurred while trying to sync awarded %1$s %2$s (#%3$d) from template %4$s (#%5$d)';

        foreach ( $engagements as $awarded_engagement ) {
            $this->log(
                sprintf(
                    $awarded_engagement->sync() ? $success_log_message : $error_log_message,
                    $this->engagement_type,
                    $awarded_engagement->get( 'title', true ),
                    $awarded_engagement->get( 'id' ),
                    get_the_title( $template_id ),
                    $template_id
                )
            );
        }
    }

    /**
     * Execute sync for each item in the queue until all awarded engagements are synced.
     *
     * @since 6.0.0
     *
     * @param array $args Array of processing data.
     * @return boolean `true` to keep the item in the queue and process again.
     *                 `false` to remove the item from the queue.
     */
    public function task( $args ) {

        $this->log(
            sprintf(
                'awarded %1$ss bulk sync task started for the %1$s template %2$s (#%3$d) - chunk %4$d',
                $this->engagement_type,
                get_the_title( $args['query_args']['templates'] ),
                $args['query_args']['templates'],
                $args['query_args']['page']
            )
        );

        // Ensure the item has all the data we need to process it.
        if ( empty( $args['query_args']['templates'] ) ) {
            return false;
        }

        $query = new LLMS_Awards_Query( $args['query_args'] );

        if ( $query->has_results() ) {
            $this->sync_awarded_engagements( $query->get_awards(), $args['query_args']['templates'] );
        }

        if ( $query->is_last_page() ) {
            $this->process_completed( $args );
        }

        return false;
    }
}