Skip to content

Instantly share code, notes, and snippets.

@thomasplevy
Created June 2, 2017 19:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thomasplevy/696146095ee96c3a89991433be21afc3 to your computer and use it in GitHub Desktop.
Save thomasplevy/696146095ee96c3a89991433be21afc3 to your computer and use it in GitHub Desktop.
<?php
if ( ! defined( 'ABSPATH' ) ) { exit; }
/**
* Engagments Class
*
* Finds and triggers the appropriate engagement
*/
class LLMS_Engagements {
/**
* Enable debug logging
* @var boolean
* @since 2.7.9
*/
private $debug = false;
/**
* protected instance of class
* @var null
*/
protected static $_instance = null;
/**
* Create instance of class
* @return object [Instance of engagements class]
*/
public static function instance() {
if ( is_null( self::$_instance ) ) {
self::$_instance = new self();
}
return self::$_instance;
}
/**
* Constructor
* Adds actions to events that trigger engagements
*/
private function __construct() {
if ( defined( 'LLMS_ENGAGEMENT_DEBUG' ) && LLMS_ENGAGEMENT_DEBUG ) {
$this->debug = true;
}
$this->add_actions();
$this->init();
}
/**
* Register all actions that trigger engagements
*
* @return void
*
* @since 2.3.0
* @version 3.3.1
*/
private function add_actions() {
$actions = apply_filters( 'lifterlms_engagement_actions', array(
'lifterlms_course_completed',
'lifterlms_course_track_completed',
'lifterlms_created_person',
'lifterlms_lesson_completed',
'lifterlms_product_purchased',
'lifterlms_quiz_completed',
'lifterlms_quiz_passed',
'lifterlms_quiz_failed',
'lifterlms_section_completed',
'llms_user_enrolled_in_course',
'llms_user_added_to_membership_level',
) );
foreach ( $actions as $action ) {
add_action( $action, array( $this, 'maybe_trigger_engagement' ), 777, 3 );
}
add_action( 'lifterlms_engagement_send_email', array( $this, 'handle_email' ), 10, 1 );
add_action( 'lifterlms_engagement_award_achievement', array( $this, 'handle_achievement' ), 10, 1 );
add_action( 'lifterlms_engagement_award_certificate', array( $this, 'handle_certificate' ), 10, 1 );
}
/**
* Include engagement types (excluding email)
* @return void
*/
public function init() {
include( 'class.llms.certificates.php' );
include( 'class.llms.achievements.php' );
}
/**
* Award an achievement
*
* This is called via do_action() by the 'maybe_trigger_engagement' function in this class
*
* @param array $args indexed array of args
* 0 => WP User ID
* 1 => WP Post ID of the email post
* 2 => WP Post ID of the related post that triggered the award
*
* @return void
*
* @since 2.3.0
*/
public function handle_achievement( $args ) {
$this->log( '======== handle_achievement() =======' );
$this->log( $args );
$a = LLMS()->achievements();
$a->trigger_engagement( $args[0], $args[1], $args[2] );
}
/**
* Award a certificate
*
* This is called via do_action() by the 'maybe_trigger_engagement' function in this class
*
* @param array $args indexed array of args
* 0 => WP User ID
* 1 => WP Post ID of the email post
* 2 => WP Post ID of the related post that triggered the award
*
* @return void
*
* @since 2.3.0
*/
public function handle_certificate( $args ) {
$this->log( '======== handle_certificate() =======' );
$this->log( $args );
$c = LLMS()->certificates();
$c->trigger_engagement( $args[0], $args[1], $args[2] );
}
/**
* Send an email engagement
*
* This is called via do_action() by the 'maybe_trigger_engagement' function in this class
*
* @param array $args indexed array of args
* 0 => WP User ID
* 1 => WP Post ID of the email post
* 2 => WP Post ID of the triggering post
* @return void
*
* @since 2.3.0
* @version 3.8.0
*/
public function handle_email( $args ) {
$this->log( '======== handle_email() =======' );
$this->log( $args );
$person_id = $args[0];
$email_id = $args[1];
$related_id = $args[2];
// dupcheck
global $wpdb;
$res = (int) $wpdb->get_var( $wpdb->prepare( "
SELECT count( meta_id )
FROM {$wpdb->prefix}lifterlms_user_postmeta
WHERE post_id = %d
AND user_id = %d
AND meta_value = %d
LIMIT 1
;",
array(
$related_id,
$person_id,
$email_id,
)
) );
if ( $res >= 1 ) {
llms_log( sprintf( 'Email `#%d` was not sent because of dupcheck', $email_id ) );
return;
}
// setup the email
$email = LLMS()->mailer()->get_email( 'engagement', array(
'person_id' => $args[0],
'email_id' => $args[1],
'related_id' => $args[2],
) );
if ( $email ) {
if ( $email->send() ) {
$wpdb->insert( $wpdb->prefix . 'lifterlms_user_postmeta',
array(
'user_id' => $person_id,
'post_id' => $related_id,
'meta_key' => '_email_sent',
'meta_value' => $email_id,
'updated_date' => current_time( 'mysql' ),
),
array( '%d', '%d', '%s', '%d', '%s' )
);
llms_log( sprintf( 'Email `#%d` sent sucessfully', $email_id ) );
} else {
llms_log( sprintf( 'Error: email `#%d` was not sent', $email_id ) );
}
}
}
/**
* Log debug data to the WordPress debug.log file
* @param mixed $log data to write to the log
* @return void
* @since 2.7.9
* @version 2.7.9
*/
public function log( $log ) {
if ( $this->debug ) {
llms_log( $log );
}
}
/**
* Handles all actions that could potentially trigger an engagement
*
* It will fire or schedule the actions after gathering all necessary data
*
* @return void
*
* @since 2.3.0
* @version 3.4.1
*/
public function maybe_trigger_engagement() {
$action = current_filter();
$args = func_get_args();
$this->log( '======= start maybe_trigger_engagement ========' );
$this->log( '$action: ' . $action );
$this->log( '$args: ' . json_encode( $args ) );
// setup variables used in queries and triggers based on the action
switch ( $action ) {
case 'lifterlms_created_person' :
$user_id = intval( $args[0] );
$trigger_type = 'user_registration';
$related_post_id = '';
break;
case 'lifterlms_course_completed' :
case 'lifterlms_course_track_completed' :
case 'lifterlms_lesson_completed' :
case 'lifterlms_section_completed' :
$user_id = intval( $args[0] );
$related_post_id = intval( $args[1] );
$trigger_type = str_replace( 'lifterlms_', '', $action );
break;
case 'lifterlms_quiz_completed':
case 'lifterlms_quiz_passed':
case 'lifterlms_quiz_failed':
$user_id = absint( $args[0] );
$related_post_id = absint( $args[1] );
$trigger_type = str_replace( 'lifterlms_', '', $action );
break;
case 'llms_user_added_to_membership_level':
case 'llms_user_enrolled_in_course':
$user_id = intval( $args[0] );
$related_post_id = intval( $args[1] );
$trigger_type = str_replace( 'llms_', '', get_post_type( $related_post_id ) ) . '_enrollment';
break;
case 'lifterlms_product_purchased' :
$user_id = intval( $args[0] );
$related_post_id = intval( $args[1] );
$trigger_type = str_replace( 'llms_', '', get_post_type( $related_post_id ) ) . '_purchased';
break;
// allow extensions to hook into our engagments
default :
extract( apply_filters( 'lifterlms_external_engagement_query_arguments' , array(
'related_post_id' => null,
'trigger_type' => null,
'user_id' => null,
), $action, $args ) );
}// End switch().
// we need a user and a trigger to proceed, related_post is optional though
if ( ! $user_id || ! $trigger_type ) {
return;
}
// gather triggerable engagements matching the supplied criteria
$engagements = apply_filters( 'lifterlms_get_engagements' , $this->get_engagements( $trigger_type, $related_post_id ), $trigger_type, $related_post_id );
$this->log( '$engagements: ' . json_encode( $engagements ) );
// only trigger engagements if there are engagements
if ( $engagements ) {
// loop through the engagements
foreach ( $engagements as $e ) {
$handler_action = null;
$handler_args = null;
// do actions based on the event type
switch ( $e->event_type ) {
case 'achievement' :
$handler_action = 'lifterlms_engagement_award_achievement';
$handler_args = array( $user_id, $e->engagement_id, $related_post_id );
break;
case 'certificate' :
/**
* @todo fix this
* if there's no related post id we have to send one anyway for certs to work
* this would only be for registration events @ version 2.3.0
* we'll just send the engagement_id twice until we find a better solution
*/
$related_post_id = ( ! $related_post_id ) ? $e->engagement_id : $related_post_id;
$handler_action = 'lifterlms_engagement_award_certificate';
$handler_args = array( $user_id, $e->engagement_id, $related_post_id );
break;
case 'email' :
$handler_action = 'lifterlms_engagement_send_email';
$handler_args = array( $user_id, $e->engagement_id, $related_post_id );
break;
// allow extensions to hook into our engagments
default :
extract( apply_filters( 'lifterlms_external_engagement_handler_arguments' , array(
'handler_action' => $handler_action,
'handler_args' => $handler_args,
), $e, $user_id, $related_post_id, $trigger_type ) );
}// End switch().
// can't proceed without an action and a handler
if ( ! $handler_action && ! $handler_args ) {
continue;
}
// if we have a delay, schedule the engagement handler
$delay = intval( $e->delay );
$this->log( '$delay: ' . $delay );
$this->log( '$handler_action: ' . $handler_action );
$this->log( '$handler_args: ' . json_encode( $handler_args ) );
if ( $delay ) {
wp_schedule_single_event( time() + ( DAY_IN_SECONDS * $delay ), $handler_action, array( $handler_args ) );
} // End if().
else {
do_action( $handler_action, $handler_args );
}
}// End foreach().
}// End if().
$this->log( '======= end maybe_trigger_engagement ========' );
}
/**
* Retreive engagements based on the trigger type
*
* Joins rather than nested loops and sub queries ftw
*
* @param string $trigger_type name of the trigger to look for
* @return array of objects
* Array(
* [0] => stdClass Object (
* [engagement_id] => 123, // WordPress Post ID of the event post (email, certificate, achievement, etc...)
* [trigger_id] => 123, // this is the Post ID of the llms_engagement post
* [trigger_event] => 'user_registration', // triggering action
* [event_type] => 'certificate', // engagment event action
* [delay] => 0, // time in days to delay the engagment
* )
* )
*
* @since 2.3.0
*/
private function get_engagements( $trigger_type, $related_post_id = '' ) {
global $wpdb;
if ( $related_post_id ) {
$related_select = ', relation_meta.meta_value AS related_post_id';
$related_join = "LEFT JOIN $wpdb->postmeta AS relation_meta ON triggers.ID = relation_meta.post_id";
$related_where = "AND relation_meta.meta_key = '_llms_engagement_trigger_post' AND relation_meta.meta_value = %d";
} else {
$related_select = '';
$related_join = '';
$related_where = '';
}
$r = $wpdb->get_results( $wpdb->prepare(
// the query
"SELECT
DISTINCT triggers.ID AS trigger_id
, triggers_meta.meta_value AS engagement_id
, engagements_meta.meta_value AS trigger_event
, event_meta.meta_value AS event_type
, delay.meta_value AS delay
$related_select
FROM $wpdb->postmeta AS engagements_meta
LEFT JOIN $wpdb->posts AS triggers ON triggers.ID = engagements_meta.post_id
LEFT JOIN $wpdb->postmeta AS triggers_meta ON triggers.ID = triggers_meta.post_id
LEFT JOIN $wpdb->posts AS engagements ON engagements.ID = triggers_meta.meta_value
LEFT JOIN $wpdb->postmeta AS event_meta ON triggers.ID = event_meta.post_id
LEFT JOIN $wpdb->postmeta AS delay ON triggers.ID = delay.post_id
$related_join
WHERE
triggers.post_type = 'llms_engagement'
AND triggers.post_status = 'publish'
AND triggers_meta.meta_key = '_llms_engagement'
AND engagements_meta.meta_key = '_llms_trigger_type'
AND engagements_meta.meta_value = %s
AND engagements.post_status = 'publish'
AND event_meta.meta_key = '_llms_engagement_type'
AND delay.meta_key = '_llms_engagement_delay'
$related_where
",
// prepare variables
$trigger_type, $related_post_id
), OBJECT );
$this->log( '$wpdb->last_query' . $wpdb->last_query );
return $r;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment