Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelogs/3099-elementor-enrollment-visibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
significance: minor
type: added
entry: "Added an Enrollment Visibility control to LifterLMS Elementor widgets, allowing widgets to be shown or hidden based on the visitor's enrollment status, login state, or enrollment in specific courses or memberships."
255 changes: 252 additions & 3 deletions includes/elementor/class-llms-elementor-widget-base.php
Original file line number Diff line number Diff line change
@@ -1,19 +1,124 @@
<?php
/**
* Abstract base class for LifterLMS Elementor widgets.
*
* @package LifterLMS/Classes/Elementor
*
* @since 7.7.0
*/

defined( 'ABSPATH' ) || exit;

/**
* LLMS_Elementor_Widget_Base abstract class.
*
* @since 7.7.0
* @since [version] Added enrollment visibility controls and server-side render gate.
*/
abstract class LLMS_Elementor_Widget_Base extends \Elementor\Widget_Base {

/**
* Constructor.
*
* @since 7.7.0
*
* @param array $data Widget data.
* @param array|null $args Widget arguments.
* @return void
*/
public function __construct( $data = array(), $args = null ) {
parent::__construct( $data, $args );
}

/**
* Get widget icon.
*
* @since 7.7.0
*
* @return string
*/
public function get_icon() {
return 'dashicons-before dashicons-welcome-learn-more';
}

/**
* Get widget categories.
*
* @since 7.7.0
*
* @return string[]
*/
public function get_categories() {
return array( 'lifterlms' );
}

/**
* Register the enrollment visibility controls section.
*
* Adds an "Enrollment Visibility" section under the Advanced tab of every
* LifterLMS Elementor widget, mirroring the visibility option available on
* standard Gutenberg blocks.
*
* Subclasses call this near the end of their `_register_controls()` method.
*
* @since [version]
*
* @return void
*/
protected function add_visibility_controls() {

$this->start_controls_section(
'llms_visibility_section',
array(
'label' => esc_html__( 'Enrollment Visibility', 'lifterlms' ),
'tab' => \Elementor\Controls_Manager::TAB_ADVANCED,
)
);

$this->add_control(
'llms_visibility',
array(
'label' => esc_html__( 'Display to', 'lifterlms' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'all',
'options' => array(
'all' => esc_html__( 'everyone', 'lifterlms' ),
'enrolled' => esc_html__( 'enrolled users', 'lifterlms' ),
'not_enrolled' => esc_html__( 'non-enrolled users or visitors', 'lifterlms' ),
'logged_in' => esc_html__( 'logged in users', 'lifterlms' ),
'logged_out' => esc_html__( 'logged out users', 'lifterlms' ),
),
)
);

$this->add_control(
'llms_visibility_in',
array(
'label' => esc_html__( 'Enrolled In', 'lifterlms' ),
'type' => \Elementor\Controls_Manager::SELECT,
'default' => 'any',
'options' => array(
'any' => esc_html__( 'in any course or membership', 'lifterlms' ),
'any_course' => esc_html__( 'in any course', 'lifterlms' ),
'any_membership' => esc_html__( 'in any membership', 'lifterlms' ),
'this' => esc_html__( 'in this course or membership', 'lifterlms' ),
),
'condition' => array(
'llms_visibility' => array( 'enrolled', 'not_enrolled' ),
),
)
);

$this->end_controls_section();
}

/**
* Add a footer promo control linking to Elementor documentation.
*
* @since 7.7.0
*
* @return void
*/
protected function add_footer_promo_control() {

$this->add_control(
Expand All @@ -30,13 +135,157 @@ protected function add_footer_promo_control() {
);
}

/**
* Render the widget output.
*
* Checks enrollment visibility before delegating to `render_widget()`.
* When the current visitor does not meet the configured condition the
* widget outputs nothing. Inside the Elementor editor the widget always
* renders so authors can see and configure it regardless of their
* own enrollment status.
*
* @since [version]
*
* @return void
*/
protected function render() {
$settings = $this->get_settings_for_display();

echo do_shortcode( '[lifterlms_course_continue_button]' );
// Always render inside the Elementor editor.
if ( ! \Elementor\Plugin::$instance->editor->is_edit_mode() ) {
$settings = $this->get_settings_for_display();
$attrs = array(
'llms_visibility' => isset( $settings['llms_visibility'] ) ? $settings['llms_visibility'] : 'all',
'llms_visibility_in' => isset( $settings['llms_visibility_in'] ) ? $settings['llms_visibility_in'] : '',
);

if ( ! $this->is_visible( $attrs ) ) {
return;
}
}

$this->render_widget();
}

/**
* Determine whether the widget should be visible to the current visitor.
*
* Evaluates the enrollment visibility attributes saved on the widget
* against the current user's login state and enrollment records. Returns
* `true` when the widget should be shown, `false` when it should be hidden.
*
* Supported `llms_visibility` values:
* - `all` — always visible (default).
* - `logged_in` — visible to logged-in users only.
* - `logged_out` — visible to logged-out visitors only.
* - `enrolled` — visible when the user is enrolled per `llms_visibility_in`.
* - `not_enrolled` — visible when the user is NOT enrolled per `llms_visibility_in`.
*
* Supported `llms_visibility_in` values (for enrolled / not_enrolled):
* - `any` — enrolled in any course or membership.
* - `any_course` — enrolled in any course.
* - `any_membership` — enrolled in any membership.
* - `this` — enrolled in the current post (resolved via `get_the_ID()`).
*
* @since [version]
*
* @param array $attrs {
* Visibility attribute map.
*
* @type string $llms_visibility Primary visibility mode. Default 'all'.
* @type string $llms_visibility_in Sub-mode for enrolled / not_enrolled. Default 'any'.
* }
* @return bool `true` if the widget should be shown to the current visitor.
*/
protected function is_visible( $attrs ) {

$visibility = ! empty( $attrs['llms_visibility'] ) ? $attrs['llms_visibility'] : 'all';
$uid = get_current_user_id();
$visible = true;

if ( 'all' === $visibility ) {
$visible = true;
} elseif ( 'logged_in' === $visibility ) {
$visible = (bool) $uid;
} elseif ( 'logged_out' === $visibility ) {
$visible = ! $uid;
} elseif ( 'enrolled' === $visibility ) {
$visibility_in = ! empty( $attrs['llms_visibility_in'] ) ? $attrs['llms_visibility_in'] : 'any';
$visible = $uid && $this->check_enrollment( $uid, $visibility_in );
} elseif ( 'not_enrolled' === $visibility ) {
$visibility_in = ! empty( $attrs['llms_visibility_in'] ) ? $attrs['llms_visibility_in'] : 'any';
$visible = ! $uid || ! $this->check_enrollment( $uid, $visibility_in );
}

/**
* Filter whether an Elementor widget should be visible.
*
* @since [version]
*
* @param bool $visible Whether the widget should be shown.
* @param array $attrs Visibility attribute map from widget settings.
* @param int $uid Current user ID (0 for logged-out visitors).
*/
return apply_filters( 'llms_elementor_widget_is_visible', $visible, $attrs, $uid );
}

/**
* Check whether a user meets the enrollment condition.
*
* @since [version]
*
* @param int $uid WP_User ID.
* @param string $visibility_in Enrollment sub-option: 'any', 'any_course', 'any_membership', or 'this'.
* @return bool
*/
private function check_enrollment( $uid, $visibility_in ) {

if ( 'this' === $visibility_in ) {
return (bool) llms_is_user_enrolled( $uid, get_the_ID() );
}

$student = llms_get_student( $uid );
if ( ! $student ) {
return false;
}

if ( in_array( $visibility_in, array( 'any', 'any_course' ), true ) ) {
$result = $student->get_enrollments( 'course', array( 'limit' => 1 ) );
if ( ! empty( $result['found'] ) ) {
return true;
}
}

if ( in_array( $visibility_in, array( 'any', 'any_membership' ), true ) ) {
$result = $student->get_enrollments( 'membership', array( 'limit' => 1 ) );
if ( ! empty( $result['found'] ) ) {
return true;
}
}

return false;
}

/**
* Render the widget-specific front-end output.
*
* Subclasses should override this method to output their content.
* A no-op default is provided to avoid fatal errors for any
* subclass not yet migrated to the new rendering contract.
*
* @since [version]
*
* @return void
*/
protected function render_widget() {}

/**
* Elementor live preview template.
*
* @since 7.7.0
*
* @return void
*/
protected function _content_template() {
// Define your template variables here
// Intentionally left empty — widgets use server-side rendering only.
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,54 @@
<?php
/**
* Course Continue Button Elementor widget.
*
* @package LifterLMS/Classes/Elementor
*
* @since 7.7.0
*/

defined( 'ABSPATH' ) || exit;

/**
* LLMS_Elementor_Widget_Course_Continue_Button class.
*
* @since 7.7.0
* @since [version] Added enrollment visibility controls.
*/
class LLMS_Elementor_Widget_Course_Continue_Button extends LLMS_Elementor_Widget_Base {

/**
* Get widget name.
*
* @since 7.7.0
*
* @return string
*/
public function get_name() {
return 'llms_course_continue_button_widget';
}

/**
* Get widget title.
*
* @since 7.7.0
*
* @return string
*/
public function get_title() {
return __( 'Course Continue Button', 'lifterlms' );
}

/**
* Register widget controls.
*
* @since 7.7.0
* @since [version] Added enrollment visibility section.
*
* @return void
*/
protected function _register_controls() {

$this->start_controls_section(
'content_section',
array(
Expand All @@ -31,11 +69,18 @@ protected function _register_controls() {
$this->add_footer_promo_control();

$this->end_controls_section();
}

protected function render() {
$settings = $this->get_settings_for_display();
$this->add_visibility_controls();
}

/**
* Render widget output on the frontend.
*
* @since 7.7.0
*
* @return void
*/
protected function render_widget() {
echo do_shortcode( '[lifterlms_course_continue_button]' );
}
}
Loading
Loading