diff --git a/.config/travis/add-on.yml b/.config/travis/add-on.yml
deleted file mode 100644
index 74e475dd46..0000000000
--- a/.config/travis/add-on.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# This travis config file is intended to be used by LifterLMS Add-ons.
-#
-# Example usage in .travis.yml:
-#
-# import:
-# - gocodebox/lifterlms:.config/travis/add-on.yml
-#
-
-# Import main configs.
-import:
- - gocodebox/lifterlms:.config/travis/main.yml
-
-# If $LLMS_BRANCH is specified, install the plugin from git.
-install:
- - |
- if [ ! -z "$LLMS_BRANCH" ]; then
- ./vendor/bin/llms-tests plugin https://github.com/gocodebox/lifterlms.git@${LLMS_BRANCH}
- fi
-
-# Test against the "nightly" dev branch of the the LifterLMS core.
-jobs:
- include:
- - php: "8.0"
- env: LLMS_BRANCH=dev WP_VERSION=latest
diff --git a/.config/travis/e2e.yml b/.config/travis/e2e.yml
deleted file mode 100644
index 39e269b8cb..0000000000
--- a/.config/travis/e2e.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-addons:
- artifacts:
- paths:
- - ./tmp/e2e-screenshots
-
-services:
- - xvfb
- - docker
-
-jobs:
- allow_failures:
- - php: "8.0"
- env: WP_VERSION=nightly LLMS_TRAVIS_TESTS=E2E
-
- include:
- - php: "8.0"
- env: WP_VERSION=latest LLMS_TRAVIS_TESTS=E2E
- - php: "8.0"
- env: WP_VERSION=nightly LLMS_TRAVIS_TESTS=E2E
-
diff --git a/.config/travis/eslint.yml b/.config/travis/eslint.yml
deleted file mode 100644
index 1fbeaf66da..0000000000
--- a/.config/travis/eslint.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# TravisCI config file partial for running an eslint job
-#
-# This partial is intended to be used alongside the main.yml config found within this same directory.
-#
-# Example usage in .travis.yml:
-#
-# import:
-# - gocodebox/lifterlms:.config/travis/main.yml
-# - gocodebox/lifterlms:.config/travis/eslint.yml
-#
-
-jobs:
- include:
- - env: ESLINT=1
- language: node_js
- node_js: lts/*
- before_install:
- install:
- - npm ci
- script:
- - npm run lint:js
- after_script:
diff --git a/.config/travis/main.yml b/.config/travis/main.yml
deleted file mode 100644
index 55acdeae12..0000000000
--- a/.config/travis/main.yml
+++ /dev/null
@@ -1,129 +0,0 @@
-os: linux
-dist: bionic
-language: php
-
-services:
- - mysql
-
-cache:
- directories:
- - node_modules
- - vendor
- - $HOME/.composer/cache
-
-env:
- global:
- - TESTS_DB_HOST=localhost
- - TESTS_DB_NAME=llms_tests
- - TESTS_DB_PASS=""
- jobs:
- - WP_VERSION=latest # 5.8
- - WP_VERSION="5.7"
- - WP_VERSION="5.6"
- - WP_VERSION="5.5"
- - WP_VERSION="5.4"
-
-php:
- - "8.0"
- - "7.4"
- - "7.3"
-
-jobs:
- fast_finish: true
-
- allow_failures:
- - env: WP_VERSION=nightly
- - env: WP_VERSION=latest RUN_CODE_COVERAGE=1
- - php: nightly
-
- exclude:
- # These WP Versions don't work on PHP 8.0
- - php: "8.0"
- env: WP_VERSION="5.5"
- - php: "8.0"
- env: WP_VERSION="5.4"
-
- include:
- - php: "8.0"
- env: PHPCS=1
- - php: nightly
- env: WP_VERSION=latest
- - php: "8.0"
- env: WP_VERSION=nightly
- - php: "7.4"
- env: WP_VERSION=latest RUN_CODE_COVERAGE=1
- before_script:
- # Download CodeClimate Test Reporter
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- - chmod +x ./cc-test-reporter
- script:
- - ./cc-test-reporter before-build
- - composer run-script tests-run -- --coverage-clover clover.xml
- after_script:
- - ./cc-test-reporter after-build --coverage-input-type clover --exit-code $TRAVIS_TEST_RESULT
-
-before_install:
- # Disable xDebug for faster builds
- - |
- if [ "1" != $RUN_CODE_COVERAGE ] && [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
- phpenv config-rm xdebug.ini
- fi
- # Raise PHP memory limit to 2048MB
- - echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
- # Install composer deps.
- - |
- if [ "8" != $( php -r "echo PHP_MAJOR_VERSION;" ) ]; then
- composer install
- else
- composer run install-php8
- fi
-
-install:
- - |
- if [ "E2E" = "$LLMS_TRAVIS_TESTS" ]; then
- sudo rm /usr/local/bin/docker-compose
- curl -L https://github.com/docker/compose/releases/download/1.25.0/docker-compose-`uname -s`-`uname -m` > docker-compose
- chmod +x docker-compose
- sudo mv docker-compose /usr/local/bin
- nvm install --lts
- npm ci
- [[ -n $DOCKER_USERNAME ]] && [[ -n $DOCKER_PASSWORD ]] && echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- composer run env up
- composer run env:setup
- if [ "latest" != $WP_VERSION ]; then
- ./vendor/bin/llms-env version $WP_VERSION
- fi;
- WP_VERSION_REAL=$( ./vendor/bin/llms-env wp core version )
- echo $WP_VERSION_REAL
- elif [ "1" = "$PHPCS" ]; then
- echo "Nothing to install"
- else
- composer run tests-install
- fi
-
-script:
- - |
- if [ "E2E" = "$LLMS_TRAVIS_TESTS" ]; then
- WP_VERSION=$WP_VERSION_REAL npm run test
- elif [ "1" = "$PHPCS" ]; then
- if [ "trunk" = "$TRAVIS_BRANCH" ]; then
- composer run-script check-cs-errors
- else
- composer run-script check-cs-errors -- $( git diff --name-only --diff-filter=ACMR $TRAVIS_COMMIT_RANGE )
- fi
- else
- composer run-script tests-run
- fi
-
-after_script:
- - |
- if [ "E2E" = "$LLMS_TRAVIS_TESTS" ]; then
- ./vendor/bin/llms-env down
- fi
-
-notifications:
- slack:
- on_success: change
- on_failure: always
- rooms:
- - secure: VzwXDPjuNCrKed9ACY7dwzyIjcnt6G1iC1LnKAOIx9fyPZ7TARLIf5bSa9M7P5w4uQHK7kpm5yFNtPHKGwaazZnCZxH8jcDMc4M8y3w6j9uNlbidOgfrCpp07lY6kpd8ViR7ANZ4V5Noz+ts8/gSA0yUib6vGP87s6RKHTyVTfNuFmHui7t6vF3S1VCXm4JmOrqmZbY9DlN+8JcyE0Ao3KOk/UDSCZICqo7cYnMci2oHGfb+2VRu49B61tASnV0r/dRu7gjEQTtqwElIJfuP0hGeAYc6bee5vFLA4EIdz2TMgr/Fm1El5eIg+1ZB4bOVEHzUlonLLGaUlqcYfKtmmYiV8BBnte1xBlEflLxYj92ethTUtTvkicVmtK50IlyL8kpb4WBwhXMEjSoKGLmdfaeNGKZ0vS/BnyDA0eWmt4EQ5ZVQL50ukhvmOAXhMB5T+K6Bg6T3yJzXIxej0MrSSNVygpeIwl5RqleXOKJJtJe3TsrsQfdqidXVrKAGSrwlwDRSMLC7JN3l99+5PEXzgb106TE0TBgrMOEClTVyH4gAjplqQ70diw9SAp0rnU518dTDj9HMvZ7KcGQgnAzKI82iB1LaWsWrMjqHtPbn/h+2vRDQNRnx8umnCmC8ezRr4l+xZ8Cb9KgrhvJW+bed3pQFmD/LerSuW6ZgHFsN/KI=
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a584730e0..6b641e349d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,52 @@
LifterLMS Changelog
===================
+v8.0.0 - 2025-01-20
+-------------------
+
+##### New Features
+
++ New popup when adding an Access Plan with templates to help set options quickly.
++ Adds support for Beaver Builder for Courses, Memberships and Lessons. [#2761](https://github.com/gocodebox/lifterlms/issues/2761)
++ Ability to show typed password for verification on the Student Dashboard login form.
++ Adds new "Featured Pricing Information" setting for Courses and Memberships.
++ Showing 'Has Quiz' or 'Has Assignment' in the Course Syllabus.
+
+##### Updates and Enhancements
+
++ Improved accessibility of various front-end and back-end UIs including the Access Plans metaboxes to use proper labels and screen reader text where needed.
++ Allow lesson access based on enrollment drip setting with Course Start Date set. [#2843](https://github.com/gocodebox/lifterlms/issues/2843)
++ Course Information header is now h2 instead of h3 by default, for accessibility in heading order.
++ Adding purchase checkout link to the access plan header as an icon. [#2793](https://github.com/gocodebox/lifterlms/issues/2793)
++ Accessibility updates for lesson favorite and write a review forms. [#2852](https://github.com/gocodebox/lifterlms/issues/2852)
+
+##### Bug Fixes
+
++ Allow additional shortcodes like [audio] within the description (content) of a quiz question.
++ Can now deactivate the Confirmation field for blocks like E-mail address and Password in forms. [#2646](https://github.com/gocodebox/lifterlms/issues/2646)
++ Fix SendWP connect button. [#2792](https://github.com/gocodebox/lifterlms/issues/2792)
++ Avoid unloading the textdomain for core translations in case the lifterlms textdomain is used before init (WP 6.7). [#2807](https://github.com/gocodebox/lifterlms/issues/2807)
++ Avoid wrapping of the text of buttons. [#2820](https://github.com/gocodebox/lifterlms/issues/2820)
+
+##### Developer Notes
+
++ Improved llmsPostsSelect2 method when called on multiple elements at once, each with different options. [#2805](https://github.com/gocodebox/lifterlms/issues/2805)
+
+##### Updated Templates
+
++ [templates/course/favorite.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/course/favorite.php)
++ [templates/course/lesson-preview.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/course/lesson-preview.php)
++ [templates/global/form-login.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/global/form-login.php)
++ [templates/global/form-registration.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/global/form-registration.php)
++ [templates/loop/featured-pricing.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/loop/featured-pricing.php)
++ [templates/myaccount/form-edit-account.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/myaccount/form-edit-account.php)
++ [templates/myaccount/form-redeem-voucher.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/myaccount/form-redeem-voucher.php)
++ [templates/notifications/basic.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/notifications/basic.php)
++ [templates/product/access-plan-button.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/product/access-plan-button.php)
++ [templates/product/free-enroll-form.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/product/free-enroll-form.php)
++ [templates/quiz/questions/description.php](https://github.com/gocodebox/lifterlms/blob/8.0.0/templates/quiz/questions/description.php)
+
+
v7.8.7 - 2024-12-17
-------------------
diff --git a/assets/js/app/llms-lesson-preview.js b/assets/js/app/llms-lesson-preview.js
index c872dc7eb4..c9c1179a9e 100644
--- a/assets/js/app/llms-lesson-preview.js
+++ b/assets/js/app/llms-lesson-preview.js
@@ -25,7 +25,7 @@ LLMS.LessonPreview = {
var self = this;
- this.$locked = $( 'a[href="#llms-lesson-locked"]' );
+ this.$locked = $( '.llms-lesson-locked' );
if ( this.$locked.length ) {
@@ -56,10 +56,6 @@ LLMS.LessonPreview = {
var self = this;
- this.$locked.on( 'click', function() {
- return false;
- } );
-
this.$locked.on( 'mouseenter', function() {
var $tip = $( this ).find( '.llms-tooltip' );
diff --git a/assets/js/app/llms-visibility-toggle.js b/assets/js/app/llms-visibility-toggle.js
new file mode 100644
index 0000000000..35674710b5
--- /dev/null
+++ b/assets/js/app/llms-visibility-toggle.js
@@ -0,0 +1,76 @@
+/**
+ * Handle Password Visibility Toggle for LifterLMS Forms
+ *
+ * @package LifterLMS/Scripts
+ *
+ * @since TBD
+ */
+
+LLMS.PasswordVisibility = {
+
+ /**
+ * Initialize references and setup event binding
+ *
+ * @since TBD
+ * @return void
+ */
+ init: function() {
+ this.$toggleButtons = $( '.llms-visibility-toggle button' );
+
+ if ( this.$toggleButtons.length ) {
+ this.$toggleButtons.removeClass( 'hide-if-no-js' );
+ this.bind();
+ }
+ },
+
+ /**
+ * Bind DOM events for toggle buttons
+ *
+ * @since TBD
+ * @return void
+ */
+ bind: function() {
+ var self = this;
+
+ // Remove any previous click events and bind the new click event
+ this.$toggleButtons.off('click').on('click', function(event) {
+ self.toggleVisibility( $(this) );
+ });
+ },
+
+ /**
+ * Toggle visibility of password fields
+ *
+ * @since TBD
+ * @param {Object} $button The jQuery object of the clicked button
+ * @return void
+ */
+ toggleVisibility: function( $button ) {
+ var isVisible = parseInt( $button.attr('data-toggle'), 10 );
+ var $form = $button.closest( '.llms-form-fields' );
+ var $passwordFields = $form.find( 'input.llms-field-input' );
+ var $icon = $button.find( 'i' );
+ var $stateText = $button.find( '.llms-visibility-toggle-state' );
+
+ // Toggle the visibility state
+ if ( isVisible === 1 ) {
+ // Show password
+ $passwordFields.filter('[type="password"]').attr('type', 'text');
+ $button.attr('data-toggle', 0);
+ $icon.removeClass('fa-eye').addClass('fa-eye-slash');
+ $stateText.text(LLMS.l10n.translate('Hide Password'));
+ } else {
+ // Hide password
+ $passwordFields.filter('[type="text"]').attr('type', 'password');
+ $button.attr('data-toggle', 1);
+ $icon.removeClass('fa-eye-slash').addClass('fa-eye');
+ $stateText.text(LLMS.l10n.translate('Show Password'));
+ }
+ }
+
+};
+
+// Initialize the Password Visibility module on document ready
+jQuery(document).ready(function($) {
+ LLMS.PasswordVisibility.init();
+});
diff --git a/assets/js/llms-admin.js b/assets/js/llms-admin.js
index 43d7edd4c1..b7e1333216 100644
--- a/assets/js/llms-admin.js
+++ b/assets/js/llms-admin.js
@@ -51,96 +51,98 @@
* @return void
*/
$.fn.llmsPostsSelect2 = function( options ) {
+ var localOptions = options;
+
+ this.each( function() {
+ var self = $( this ),
+ options = localOptions || {},
+ defaults = {
+ multiple: false,
+ placeholder: self.attr( 'data-placeholder' ) || ( undefined !== LLMS.l10n ? LLMS.l10n.translate( 'Select a Course/Membership' ) : 'Select a Course/Membership' ),
+ post_type: self.attr( 'data-post-type' ) || 'post',
+ post_statuses: self.attr( 'data-post-statuses' ) || 'publish',
+ instructor_id: null,
+ allow_clear: self.attr( 'data-post-type' ) || false,
+ width: null,
+ };
+
+ $.each( defaults, function( setting ) {
+ if ( self.attr( 'data-' + setting ) ) {
+ options[ setting ] = self.attr( 'data-' + setting );
+ }
+ } );
- var self = this,
- options = options || {},
- defaults = {
- multiple: false,
- placeholder: undefined !== LLMS.l10n ? LLMS.l10n.translate( 'Select a Course/Membership' ) : 'Select a Course/Membership',
- post_type: self.attr( 'data-post-type' ) || 'post',
- post_statuses: self.attr( 'data-post-statuses' ) || 'publish',
- instructor_id: null,
- allow_clear: self.attr( 'data-post-type' ) || false,
- width: null,
- };
-
- $.each( defaults, function( setting ) {
- if ( self.attr( 'data-' + setting ) ) {
- options[ setting ] = self.attr( 'data-' + setting );
+ if ( 'multiple' === self.attr( 'multiple' ) ) {
+ options.multiple = true;
}
- } );
- if ( 'multiple' === self.attr( 'multiple' ) ) {
- options.multiple = true;
- }
+ options = $.extend( defaults, options );
- options = $.extend( defaults, options );
-
- this.llmsSelect2( {
- allowClear: options.allow_clear,
- ajax: {
- dataType: 'JSON',
- delay: 250,
- method: 'POST',
- url: window.ajaxurl,
- data: function( params ) {
- return {
- action: 'select2_query_posts',
- page: ( params.page ) ? params.page - 1 : 0, // 0 index the pages to make it simpler for the database query
- post_type: options.post_type,
- instructor_id : options.instructor_id,
- post_statuses: options.post_statuses,
- term: params.term,
- _ajax_nonce: window.llms.ajax_nonce,
- };
- },
- processResults: function( data, params ) {
+ $( this ).llmsSelect2( {
+ allowClear: options.allow_clear,
+ ajax: {
+ dataType: 'JSON',
+ delay: 250,
+ method: 'POST',
+ url: window.ajaxurl,
+ data: function( params ) {
+ return {
+ action: 'select2_query_posts',
+ page: ( params.page ) ? params.page - 1 : 0, // 0 index the pages to make it simpler for the database query
+ post_type: options.post_type,
+ instructor_id : options.instructor_id,
+ post_statuses: options.post_statuses,
+ term: params.term,
+ _ajax_nonce: window.llms.ajax_nonce,
+ };
+ },
+ processResults: function( data, params ) {
+
+ // recursive function for creating
+ function map_data( items ) {
+
+ // this is a flat array of results
+ // used when only one post type is selected
+ // and to format children when using optgroups with multiple post types
+ if ( Array.isArray( items ) ) {
+ return $.map( items, function( item ) {
+ return format_item( item );
+ } );
+
+ // this sets up the top level optgroups when using multiple post types
+ } else {
+ return $.map( items, function( item ) {
+ return {
+ text: item.label,
+ children: map_data( item.items ),
+ }
+ } );
+ }
+ }
- // recursive function for creating
- function map_data( items ) {
-
- // this is a flat array of results
- // used when only one post type is selected
- // and to format children when using optgroups with multiple post types
- if ( Array.isArray( items ) ) {
- return $.map( items, function( item ) {
- return format_item( item );
- } );
-
- // this sets up the top level optgroups when using multiple post types
- } else {
- return $.map( items, function( item ) {
- return {
- text: item.label,
- children: map_data( item.items ),
- }
- } );
+ // format a single result (option)
+ function format_item( item ) {
+ return {
+ text: item.name,
+ id: item.id,
+ };
}
- }
- // format a single result (option)
- function format_item( item ) {
return {
- text: item.name,
- id: item.id,
+ results: map_data( data.items ),
+ pagination: {
+ more: data.more
+ }
};
- }
-
- return {
- results: map_data( data.items ),
- pagination: {
- more: data.more
- }
- };
+ },
},
- },
- cache: true,
- placeholder: options.placeholder,
- multiple: options.multiple,
- width: options.width,
+ cache: true,
+ placeholder: options.placeholder,
+ multiple: options.multiple,
+ width: options.width,
+ } );
} );
-
};
// automatically setup any select with the `llms-posts-select2` class
@@ -290,16 +292,28 @@
*/
remoteInstall: function( $btn, data, callback ) {
+ const self = this;
+
$btn.parent().find( '.llms-error' ).remove();
- $.post( ajaxurl, data, callback ).fail( function( jqxhr ) {
- LLMS.Spinner.stop( $btn );
- var msg = jqxhr.responseJSON && jqxhr.responseJSON.message ? jqxhr.responseJSON.message : jqxhr.responseText;
- if ( msg ) {
- $( '
' + LLMS.l10n.replace( 'Error: %s', { '%s': msg } ) + '
' ).insertAfter( $btn );
- }
- } );
+ $.post( ajaxurl, data, function() {
+ // Do a second call to get the redirect URL, since the plugin isn't in memory initially.
+ data.action = data.action + '_verify';
+ $.post( ajaxurl, data, callback ).fail( function( jqxhr ) {
+ self.handleRemoteInstallFailure( $btn, jqxhr );
+ } );
+ } )
+ .fail( function (jqxhr ) {
+ self.handleRemoteInstallFailure( $btn, jqxhr );
+ } );
+ },
- }
+ handleRemoteInstallFailure: function( $btn, jqxhr ) {
+ LLMS.Spinner.stop( $btn );
+ var msg = jqxhr.responseJSON && jqxhr.responseJSON.message ? jqxhr.responseJSON.message : jqxhr.responseText;
+ if ( msg ) {
+ $( '' + LLMS.l10n.replace( 'Error: %s', { '%s': msg } ) + '
' ).insertAfter( $btn );
+ }
+ },
};
diff --git a/assets/js/llms-favorites.js b/assets/js/llms-favorites.js
index d5d892168a..d50037ddb5 100644
--- a/assets/js/llms-favorites.js
+++ b/assets/js/llms-favorites.js
@@ -25,10 +25,9 @@
var self = this;
// Favorite clicked.
- $( '.llms-favorite-wrapper' ).on( 'click', function( e ) {
+ $( '.llms-favorite-wrapper button' ).on( 'click', function( e ) {
e.preventDefault();
- var $btn = $( this ).find( '.llms-heart-btn' );
- $btn && self.favorite( $btn );
+ self.favorite( $( this ) );
} );
// Adding class in Favorite's parent.
@@ -69,11 +68,13 @@
$fav_btns.each(
function() {
if( 'favorite' === user_action ) {
- $(this).removeClass( 'fa-heart-o' ).addClass( 'fa-heart' );
+ $(this).find( '.llms-heart-btn' ).removeClass( 'fa-heart-o' ).addClass( 'fa-heart' );
$(this).attr( 'data-action', 'unfavorite' );
+ $(this).attr( 'aria-pressed', true );
} else if ( 'unfavorite' === user_action ) {
- $(this).removeClass( 'fa-heart' ).addClass( 'fa-heart-o' );
+ $(this).find( '.llms-heart-btn' ).removeClass( 'fa-heart' ).addClass( 'fa-heart-o' );
$(this).attr( 'data-action', 'favorite' );
+ $(this).attr( 'aria-pressed', false );
}
// Updating count.
$(this).closest( '.llms-favorite-wrapper' ).find( '.llms-favorites-count' ).text( r.total_favorites );
diff --git a/assets/js/llms-favorites.min.js b/assets/js/llms-favorites.min.js
deleted file mode 100644
index c351341cd6..0000000000
--- a/assets/js/llms-favorites.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-!function(r){var a={bind:function(){var t=this;r(".llms-favorite-wrapper").on("click",function(a){a.preventDefault();a=r(this).find(".llms-heart-btn");a&&t.favorite(a)}),r(".llms-favorite-wrapper").parent().addClass("llms-has-favorite")},favorite:function(a){var e=a.attr("data-id"),i=a.attr("data-type"),o=a.attr("data-action");LLMS.Ajax.call({data:{action:"favorite_object",object_id:e,object_type:i,user_action:o},beforeSend:function(){},success:function(a){var t=r("[data-id="+e+"][data-type="+i+"][data-action="+o+"]");a.success&&t.each(function(){"favorite"===o?(r(this).removeClass("fa-heart-o").addClass("fa-heart"),r(this).attr("data-action","unfavorite")):"unfavorite"===o&&(r(this).removeClass("fa-heart").addClass("fa-heart-o"),r(this).attr("data-action","favorite")),r(this).closest(".llms-favorite-wrapper").find(".llms-favorites-count").text(a.total_favorites)})}})}};a.bind(),window.llms=window.llms||{},window.llms.favorites=a}(jQuery);
-//# sourceMappingURL=../maps/js/llms-favorites.min.js.map
diff --git a/assets/js/llms-metabox-options.js b/assets/js/llms-metabox-options.js
new file mode 100644
index 0000000000..c2fc58a6ec
--- /dev/null
+++ b/assets/js/llms-metabox-options.js
@@ -0,0 +1,46 @@
+/**
+ * JS for the Course and Membership metabox options
+ *
+ * @since 8.0.0
+ */
+
+( function( $ ){
+
+ $( '.llms-mb-container .llms-basic-editor' ).each( function() {
+
+ const name = $( this ).attr( 'data-name' );
+
+ const ed = new Quill( this, {
+ modules: {
+ toolbar: ['bold', 'italic', 'underline', 'strike', { 'script': 'sub'}, { 'script': 'super' }],
+ keyboard: {
+ bindings: {
+ tab: {
+ key: 9,
+ handler: function( range, context ) {
+ return true;
+ },
+ },
+ 13: {
+ key: 13,
+ handler: function( range, context ) {
+ ed.root.blur();
+ return false;
+ },
+ },
+ },
+ },
+ },
+ placeholder: $( this ).attr( 'data-placeholder' ),
+ theme: 'bubble',
+ } );
+
+ const keyboard = ed.getModule('keyboard');
+ keyboard.bindings['Enter'] = null;
+
+ ed.on( 'text-change', function() {
+ $( 'input[name="' + name + '"]' ).val( ed.getSemanticHTML() );
+ });
+
+ } );
+} )( jQuery );
diff --git a/assets/js/llms-metabox-product.js b/assets/js/llms-metabox-product.js
index 75c17930ae..1a4c2c735d 100644
--- a/assets/js/llms-metabox-product.js
+++ b/assets/js/llms-metabox-product.js
@@ -28,6 +28,8 @@
*/
this.$save = null;
+ this.$plan_dialog = null;
+
/**
* A randomly generated temporary ID used for the tinyMCE editor's id
* when a new plan is added
@@ -178,9 +180,27 @@
// trigger changes on load for all existing plans
$( '#llms-access-plans [data-controller-id]' ).trigger( 'change' );
+ var dialogEl = document.getElementById( 'llms-access-plan-dialog' );
+ if ( dialogEl ) {
+ self.$plan_dialog = new A11yDialog( dialogEl );
+ }
+
+ // If PMPro or others are hiding the Restrictions tab, or there's no time period option, we want to hide the Presell option.
+ if ( ! $( 'span.llms-nav-link' ).filter(function() {
+ return $( this ).text().trim() === LLMS.l10n.translate( 'Restrictions' );
+ }).length ||
+ ! $( '#_llms_time_period' ).length ) {
+ $( '.llms-access-plan-templates button[data-template="presell"]' ).hide();
+ }
+
// add a new empty plan interface on new plan button click.
$( '#llms-new-access-plan' ).on( 'click', function() {
self.init_plan();
+
+ if ( self.$plan_dialog ) {
+ self.$plan_dialog.show();
+ }
+
self.toggle_create_button( 'disable' );
self.toggle_save_button( 'enable' );
setTimeout( function() {
@@ -190,6 +210,108 @@
}, 500 );
} );
+ $( '#llms-access-plan-dialog .llms-access-plan-templates button.template').on( 'click', function( e ) {
+ e.preventDefault();
+ if ( self.$plan_dialog ) {
+ self.$plan_dialog.hide();
+ }
+
+ var $last_access_plan = self.$plans.find( '.llms-access-plan' ).last();
+
+ switch ( $( this ).attr( 'data-template' ) ) {
+ case 'free':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Free' ) ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[is_free]"]').val( 'yes' ).change();
+ break;
+ case 'monthly':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Monthly' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '100' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[period]"]').val( 'month' ).change();
+ break;
+ case 'annual':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Annual' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '1000' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[period]"]').val( 'year' ).change();
+ break;
+ case 'one-time':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'One Time' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '1000' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '0' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[access_expiration]"]').val( 'limited-period' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[access_length]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[access_period]"]').val( 'year' ).change();
+ break;
+ case 'lifetime':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Lifetime' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '1000' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '0' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[access_expiration]"]').val( 'lifetime' ).change();
+ break;
+ case 'paid-trial':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Paid Trial' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '100' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[period]"]').val( 'month' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[trial_offer]"]').val( 'yes' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[trial_price]"]').val( '1' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[trial_length]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[trial_period]"]').val( 'week' ).change();
+ break;
+ case 'free-trial':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Free Trial' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '100' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[frequency]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[period]"]').val( 'month' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[trial_offer]"]').val( 'yes' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[trial_price]"]').val( '0' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[trial_length]"]').val( '1' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[trial_period]"]').val( 'week' ).change();
+ break;
+ case 'hidden-access':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Hidden Access' ) ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[visibility]"]').val( 'hidden' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[is_free]"]').val( 'yes' ).change();
+ break;
+ case 'sale':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Sale' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '1000' ).change();
+ $last_access_plan.find('select[name^="_llms_plans["][name$="[on_sale]"]').val( 'yes' ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[sale_price]"]').val( '500' ).change();
+ break;
+ case 'presell':
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[title]"]').val( LLMS.l10n.translate( 'Pre-sale' ) ).change();
+ $last_access_plan.find('input[name^="_llms_plans["][name$="[price]"]').val( '1000' ).change();
+ var $restrictions_tab = $('span.llms-nav-link').filter(function() {
+ return $(this).text().trim() === 'Restrictions';
+ });
+ if ( $restrictions_tab.length ) {
+ $restrictions_tab.click();
+ if ( ! $('#_llms_time_period').is(':checked') ) {
+ $('#_llms_time_period').click();
+ }
+ var today = new Date();
+ today.setMonth(today.getMonth() + 1);
+
+ var month = (today.getMonth() + 1).toString().padStart(2, '0');
+ var day = today.getDate().toString().padStart(2, '0');
+ var year = today.getFullYear().toString();
+
+ var formattedDate = `${month}/${day}/${year}`;
+ $('#_llms_start_date').val( formattedDate ).change();
+ // TODO: Add some kind of a notice or something to let the user know that the start date has been set.
+ }
+
+ break;
+ case 'advanced':
+ break;
+ default:
+ self.$plans.trigger( 'llms-access-plan-template-' + $( this ).attr( 'data-template' ) );
+ break;
+ }
+ } );
+
self.$plans.sortable( {
handle: '.llms-drag-handle',
items: '.llms-access-plan',
diff --git a/assets/scss/_includes/_buttons.scss b/assets/scss/_includes/_buttons.scss
index 41911310d0..19aae7e1fb 100644
--- a/assets/scss/_includes/_buttons.scss
+++ b/assets/scss/_includes/_buttons.scss
@@ -17,6 +17,7 @@
padding: 12px 24px;
position: relative;
transition: all .5s ease;
+ white-space: nowrap;
&:disabled {
opacity: 0.5;
@@ -164,6 +165,31 @@ a.llms-button-secondary {
}
+.llms-button-plain {
+ background: transparent;
+ border: none;
+ border-radius: 3px;
+ color: #1D2327;
+ cursor: pointer;
+ font-size: 16px;
+ font-weight: 700;
+ text-decoration: none;
+ text-shadow: none;
+ line-height: 1;
+ margin: 0;
+ max-width: 100%;
+ padding: 1px 3px;
+ position: relative;
+
+ &:hover, &:active {
+ color: #1D2327;
+ }
+ &:focus {
+ color: #1D2327;
+ }
+
+}
+
.llms-course-continue-button {
display: inline-block;
}
diff --git a/assets/scss/_includes/_llms-form-field.scss b/assets/scss/_includes/_llms-form-field.scss
index d6fb817cd6..eb063e10b4 100644
--- a/assets/scss/_includes/_llms-form-field.scss
+++ b/assets/scss/_includes/_llms-form-field.scss
@@ -215,6 +215,28 @@
height: 100%;
}
+ &:has(.llms-visibility-toggle) {
+ align-items: center;
+ display: grid;
+ grid-template-areas:
+ "label toggle"
+ "input input";
+ grid-template-columns: 1fr auto;
+
+ label {
+ grid-area: label;
+ }
+
+ input {
+ grid-area: input;
+ }
+
+ .llms-visibility-toggle {
+ grid-area: toggle;
+ justify-self: end;
+ }
+ }
+
}
diff --git a/assets/scss/admin/_main.scss b/assets/scss/admin/_main.scss
index 79588aa040..566f0a86d7 100644
--- a/assets/scss/admin/_main.scss
+++ b/assets/scss/admin/_main.scss
@@ -338,3 +338,14 @@ a.llms-view-as {
}
}
}
+
+.llms-basic-editor.ql-container .ql-editor {
+ border-radius: 4px;
+ border: 1px solid #8c8f94;
+
+ &:focus {
+ border-color: var(--wp-admin-theme-color);
+ box-shadow: 0 0 0 .5px var(--wp-admin-theme-color);
+ outline: 2px solid #0000;
+ }
+}
diff --git a/assets/scss/admin/metaboxes/_metabox-product.scss b/assets/scss/admin/metaboxes/_metabox-product.scss
index 3dd5803819..8c6ce7b890 100644
--- a/assets/scss/admin/metaboxes/_metabox-product.scss
+++ b/assets/scss/admin/metaboxes/_metabox-product.scss
@@ -80,3 +80,104 @@
}
}
+
+.llms-dialog-container,
+.llms-dialog-overlay {
+ position: fixed;
+ inset: 0;
+}
+
+.llms-dialog-container {
+ z-index: 2;
+ display: flex;
+
+ .llms-dialog-overlay {
+ background-color: rgb(43 46 56 / 0.9);
+ }
+
+ &[aria-hidden='true'] {
+ display: none;
+ }
+
+ .llms-dialog-content {
+ margin: auto;
+ padding: 2em;
+ z-index: 2;
+ max-width: 90%;
+ width: 700px;
+ border-radius: 6px;
+ position: relative;
+ background-color: white;
+ overflow: auto;
+ max-height: 90vh;
+
+ .llms-dialog-close {
+ position: absolute;
+ top: 0.5em;
+ right: 0.5em;
+ border: 0;
+ padding: 0.25em;
+ background-color: transparent;
+ font-size: 1.5em;
+ width: 1.5em;
+ height: 1.5em;
+ text-align: center;
+ cursor: pointer;
+ transition: 0.15s;
+ border-radius: 50%;
+ }
+
+ .llms-access-plan-templates {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ grid-gap: 15px;
+
+ button, a {
+ background: #e1e1e1;
+ border: 1px solid $color-cinder;
+ color: $color-cinder;
+ padding: 25px;
+ font-size: 14px;
+ text-align: center;
+ vertical-align: baseline;
+
+ strong {
+ font-size: 18px;
+ display: block;
+ margin-bottom: 10px;
+ }
+ span {
+ display: block;
+ }
+
+ position: relative;
+ text-decoration: none;
+ transition: transform .2s;
+ cursor: pointer;
+ }
+
+ a {
+ border: 1px solid $color-brand-blue;
+ background: #efefef;
+ }
+
+ span.add-on {
+ background-color: #999;
+ border-bottom-left-radius: 5px;
+ border-bottom-right-radius: 5px;
+ color: #FFF;
+ font-size: 11px;
+ font-weight: bold;
+ left: 10px;
+ text-align: center;
+ padding: 2px 6px;
+ position: absolute;
+ text-transform: uppercase;
+ top: 0;
+ width: auto;
+ }
+ }
+ }
+}
+
+
diff --git a/assets/scss/frontend/_loop.scss b/assets/scss/frontend/_loop.scss
index ed27dcc482..a680450808 100644
--- a/assets/scss/frontend/_loop.scss
+++ b/assets/scss/frontend/_loop.scss
@@ -59,12 +59,16 @@
.llms-meta,
.llms-author,
- .llms-loop-title {
+ .llms-loop-title,
+ .llms-featured-pricing
+ {
padding: 0 15px;
}
.llms-meta,
- .llms-author {
+ .llms-author,
+ .llms-featured-pricing
+ {
color: $color-cinder;
display: block;
font-size: 14px;
diff --git a/assets/scss/frontend/_main.scss b/assets/scss/frontend/_main.scss
index c6d7b8cac2..08beec0747 100644
--- a/assets/scss/frontend/_main.scss
+++ b/assets/scss/frontend/_main.scss
@@ -495,6 +495,18 @@ svg .llms-animated-circle {
.fa-heart {
color: #EF476F;
}
+
+ button {
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ }
+
+ button:focus {
+ outline: 2px dashed #333;
+ outline-offset: 2px;
+ }
}
.llms-has-favorite .llms-parent-course-link {
@@ -513,4 +525,5 @@ svg .llms-animated-circle {
.llms-favorite-wrapper {
display: inline-block;
}
+
}
diff --git a/assets/scss/frontend/_reviews.scss b/assets/scss/frontend/_reviews.scss
index 6df33a0307..44ac701578 100644
--- a/assets/scss/frontend/_reviews.scss
+++ b/assets/scss/frontend/_reviews.scss
@@ -22,7 +22,7 @@
margin: 10px 0px
}
- h5 {
+ .review_error {
color: red;
display: none;
}
diff --git a/assets/scss/frontend/_student-dashboard.scss b/assets/scss/frontend/_student-dashboard.scss
index 7781be0c8e..11061852f0 100644
--- a/assets/scss/frontend/_student-dashboard.scss
+++ b/assets/scss/frontend/_student-dashboard.scss
@@ -217,6 +217,10 @@
}
}
+ .llms-featured-pricing {
+ display: none;
+ }
+
/**
* Dashboard Home
*/
diff --git a/assets/scss/frontend/_syllabus.scss b/assets/scss/frontend/_syllabus.scss
index 7810dd3946..3ef73fd27f 100644
--- a/assets/scss/frontend/_syllabus.scss
+++ b/assets/scss/frontend/_syllabus.scss
@@ -22,6 +22,15 @@
max-width: 100%;
position: relative;
+ section {
+ background: $color-white;
+ transition: background-color 0.3s ease;
+
+ &:hover {
+ background: $el-background-hover;
+ }
+ }
+
&.current-lesson {
.llms-lesson-title {
font-weight: 400;
@@ -29,34 +38,31 @@
}
.llms-lesson-link {
- background: $color-white;
color: $color-black;
display: block;
padding: 15px;
text-decoration: none;
- transition: background-color 0.3s ease;
-
- &:hover {
- background: $el-background-hover;
- }
&:visited {
color: #212121;
}
- &.llms-lesson-link-locked {
- background: #EFEFEF;
- color: $color-darkgrey;
+ .llms-lesson-preview-row {
+ display: flex;
+ flex-direction: row-reverse;
+ }
+ }
- &:hover {
- background: $color-border;
- }
+ .llms-lesson-locked {
+ background: #EFEFEF;
+ color: $color-darkgrey;
+ .llms-lesson-link {
+ color: $color-darkgrey;
}
- .llms-lesson-preview-row {
- display: flex;
- flex-direction: row-reverse;
+ &:hover {
+ background: $color-border;
}
}
@@ -89,6 +95,24 @@
}
}
+ .llms-lesson-meta {
+ display: flex;
+ padding: 5px 15px;
+ font-size: 14px;
+
+ div.llms-favorite-wrapper + span.llms-lesson-has-quiz {
+ margin-left: 10px;
+ }
+
+ div.llms-favorite-wrapper + span.llms-lesson-has-assignment {
+ margin-left: 10px;
+ }
+
+ span.llms-lesson-has-quiz + span.llms-lesson-has-assignment {
+ margin-left: 10px;
+ }
+ }
+
.llms-main {
flex-grow: 1;
}
diff --git a/assets/vendor/a11y-dialog/LICENSE b/assets/vendor/a11y-dialog/LICENSE
new file mode 100644
index 0000000000..b87de71449
--- /dev/null
+++ b/assets/vendor/a11y-dialog/LICENSE
@@ -0,0 +1,9 @@
+The MIT License (MIT)
+
+Copyright (c) 2024 Kitty Giraudel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/assets/vendor/a11y-dialog/a11y-dialog.min.js b/assets/vendor/a11y-dialog/a11y-dialog.min.js
new file mode 100644
index 0000000000..a07603bf48
--- /dev/null
+++ b/assets/vendor/a11y-dialog/a11y-dialog.min.js
@@ -0,0 +1,2 @@
+/*! a11y-dialog 8.1.1 — © Kitty Giraudel */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).A11yDialog=t()}(this,(function(){"use strict";const e=":not([inert]):not([inert] *)",t=':not([tabindex^="-"])',i=":not(:disabled)";var s=[`a[href]${e}${t}`,`area[href]${e}${t}`,`input:not([type="hidden"]):not([type="radio"])${e}${t}${i}`,`input[type="radio"]${e}${t}${i}`,`select${e}${t}${i}`,`textarea${e}${t}${i}`,`button${e}${t}${i}`,`details${e} > summary:first-of-type${t}`,`iframe${e}${t}`,`audio[controls]${e}${t}`,`video[controls]${e}${t}`,`[contenteditable]${e}${t}`,`[tabindex]${e}${t}`];function n(e){(e.querySelector("[autofocus]")||e).focus()}function o(e,t){if(t&&h(e))return e;if(function(e){return(!e.shadowRoot||"-1"!==e.getAttribute("tabindex"))&&!e.matches(":disabled,[hidden],[inert]")}(e))if(e.shadowRoot){let i=r(e.shadowRoot,t);for(;i;){const e=o(i,t);if(e)return e;i=d(i,t)}}else if("slot"===e.localName){const i=e.assignedElements({flatten:!0});t||i.reverse();for(const e of i){const i=o(e,t);if(i)return i}}else{let i=r(e,t);for(;i;){const e=o(i,t);if(e)return e;i=d(i,t)}}return!t&&h(e)?e:null}function r(e,t){return t?e.firstElementChild:e.lastElementChild}function d(e,t){return t?e.nextElementSibling:e.previousElementSibling}const h=e=>!e.shadowRoot?.delegatesFocus&&(e.matches(s.join(","))&&!(e=>!(!e.matches("details:not([open]) *")||e.matches("details>summary:first-of-type"))||!(e.offsetWidth||e.offsetHeight||e.getClientRects().length))(e));function l(e=document){const t=e.activeElement;return t?t.shadowRoot?l(t.shadowRoot)||document.activeElement:t:null}function a(e,t){const[i,s]=function(e){const t=o(e,!0);return[t,t?o(e,!1)||t:null]}(e);if(!i)return t.preventDefault();const n=l();t.shiftKey&&n===i?(s.focus(),t.preventDefault()):t.shiftKey||n!==s||(i.focus(),t.preventDefault())}function u(e,t){return function t(i){return i&&i!==document&&i!==window?(i.assignedSlot&&(i=i.assignedSlot),i.closest(e)||t(i.getRootNode().host)):null}(t)}const c="data-a11y-dialog";class f{$el;id;previouslyFocused;shown;constructor(e){this.$el=e,this.id=this.$el.getAttribute(c)||this.$el.id,this.previouslyFocused=null,this.shown=!1,this.maintainFocus=this.maintainFocus.bind(this),this.bindKeypress=this.bindKeypress.bind(this),this.handleTriggerClicks=this.handleTriggerClicks.bind(this),this.show=this.show.bind(this),this.hide=this.hide.bind(this),this.$el.setAttribute("aria-hidden","true"),this.$el.setAttribute("aria-modal","true"),this.$el.setAttribute("tabindex","-1"),this.$el.hasAttribute("role")||this.$el.setAttribute("role","dialog"),document.addEventListener("click",this.handleTriggerClicks,!0)}destroy(){return this.fire("destroy").defaultPrevented||(this.hide(),document.removeEventListener("click",this.handleTriggerClicks,!0),this.$el.replaceWith(this.$el.cloneNode(!0))),this}show(e){if(this.shown)return this;return this.fire("show",e).defaultPrevented||(this.shown=!0,this.$el.removeAttribute("aria-hidden"),this.previouslyFocused=l(),"BODY"===this.previouslyFocused?.tagName&&e?.target&&(this.previouslyFocused=e.target),"focus"===e?.type?this.maintainFocus(e):n(this.$el),document.body.addEventListener("focus",this.maintainFocus,!0),this.$el.addEventListener("keydown",this.bindKeypress,!0)),this}hide(e){if(!this.shown)return this;return this.fire("hide",e).defaultPrevented||(this.shown=!1,this.$el.setAttribute("aria-hidden","true"),this.previouslyFocused?.focus?.(),document.body.removeEventListener("focus",this.maintainFocus,!0),this.$el.removeEventListener("keydown",this.bindKeypress,!0)),this}on(e,t,i){return this.$el.addEventListener(e,t,i),this}off(e,t,i){return this.$el.removeEventListener(e,t,i),this}fire(e,t){const i=new CustomEvent(e,{detail:t,cancelable:!0});return this.$el.dispatchEvent(i),i}handleTriggerClicks(e){const t=e.composedPath()[0],i=u(`[${c}-show="${this.id}"]`,t),s=u(`[${c}-hide="${this.id}"]`,t),n=u(`[${c}-hide]`,t)&&u('[aria-modal="true"]',t)===this.$el;i&&this.show(e),(s||n)&&this.hide(e)}bindKeypress(e){if(u('[aria-modal="true"]',l())!==this.$el)return;let t=!1;try{t=!!this.$el.querySelector('[popover]:not([popover="manual"]):popover-open')}catch{}"Escape"!==e.key||"alertdialog"===this.$el.getAttribute("role")||t||(e.preventDefault(),this.hide(e)),"Tab"===e.key&&a(this.$el,e)}maintainFocus(e){e.target.closest(`[aria-modal="true"], [${c}-ignore-focus-trap]`)||n(this.$el)}}function $(){for(const e of document.querySelectorAll("[data-a11y-dialog]"))new f(e)}return"undefined"!=typeof document&&("loading"===document.readyState?document.addEventListener("DOMContentLoaded",$):$()),f}));
diff --git a/class-lifterlms.php b/class-lifterlms.php
index 13d0ca3493..fc50dea656 100644
--- a/class-lifterlms.php
+++ b/class-lifterlms.php
@@ -34,7 +34,7 @@ final class LifterLMS {
*
* @var string
*/
- public $version = '7.8.7';
+ public $version = '8.0.0';
/**
* LLMS_Assets instance
@@ -178,6 +178,7 @@ private function define_constants() {
'width' => true,
'height' => true,
'data-*' => true,
+ 'aria-label' => true,
'aria-live' => true,
'aria-hidden' => true,
'aria-*' => true,
@@ -260,76 +261,42 @@ private function define_constants() {
'span' => $allowed_atts,
'strong' => $allowed_atts,
'sup' => $allowed_atts,
+ 'sub' => $allowed_atts,
'del' => $allowed_atts,
'ins' => $allowed_atts,
'em' => $allowed_atts,
'bdi' => $allowed_atts,
+ 's' => $allowed_atts,
+ 'u' => $allowed_atts,
)
);
+
+ // Start with the wp_kses_post allowed fields and ensure all attributes are permitted.
+ $allowed_post_fields = wp_kses_allowed_html( 'post' );
+ foreach ( $allowed_post_fields as $field => $attributes ) {
+ $allowed_post_fields[ $field ] = array_merge( $attributes, $allowed_atts );
+ }
+
llms_maybe_define_constant(
'LLMS_ALLOWED_HTML_FORM_FIELDS',
- array(
- 'a' => $allowed_atts,
- 'abbr' => $allowed_atts,
- 'acronym' => $allowed_atts,
- 'article' => $allowed_atts,
- 'b' => $allowed_atts,
- 'bdo' => $allowed_atts,
- 'bdi' => $allowed_atts,
- 'blockquote' => $allowed_atts,
- 'br' => $allowed_atts,
- 'cite' => $allowed_atts,
- 'code' => $allowed_atts,
- 'del' => $allowed_atts,
- 'dfn' => $allowed_atts,
- 'em' => $allowed_atts,
- 'hr' => $allowed_atts,
- 'ins' => $allowed_atts,
- 'kbd' => $allowed_atts,
- 'q' => $allowed_atts,
- 's' => $allowed_atts,
- 'iframe' => $allowed_atts,
- 'header' => $allowed_atts,
- 'footer' => $allowed_atts,
- 'strike' => $allowed_atts,
- 'strong' => $allowed_atts,
- 'sub' => $allowed_atts,
- 'sup' => $allowed_atts,
- 'ul' => $allowed_atts,
- 'ol' => $allowed_atts,
- 'li' => $allowed_atts,
- 'p' => $allowed_atts,
- 'pre' => $allowed_atts,
- 'address' => $allowed_atts,
- 'aside' => $allowed_atts,
- 'nav' => $allowed_atts,
- 'form' => $allowed_atts,
- 'input' => $allowed_atts,
- 'textarea' => $allowed_atts,
- 'button' => $allowed_atts,
- 'select' => $allowed_atts,
- 'option' => $allowed_atts,
- 'checkbox' => $allowed_atts,
- 'radio' => $allowed_atts,
- 'optgroup' => $allowed_atts,
- 'div' => $allowed_atts,
- 'label' => $allowed_atts,
- 'span' => $allowed_atts,
- 'img' => $allowed_atts,
- 'i' => $allowed_atts,
- 'h1' => $allowed_atts,
- 'h2' => $allowed_atts,
- 'h3' => $allowed_atts,
- 'h4' => $allowed_atts,
- 'h5' => $allowed_atts,
- 'h6' => $allowed_atts,
- 'section' => $allowed_atts,
- 'fieldset' => $allowed_atts,
- 'legend' => $allowed_atts,
- 'datalist' => $allowed_atts,
- 'output' => $allowed_atts,
- 'progress' => $allowed_atts,
- 'meter' => $allowed_atts,
+ array_merge(
+ $allowed_post_fields,
+ array(
+ 'bdi' => $allowed_atts,
+ 'iframe' => $allowed_atts,
+ 'form' => $allowed_atts,
+ 'input' => $allowed_atts,
+ 'select' => $allowed_atts,
+ 'option' => $allowed_atts,
+ 'checkbox' => $allowed_atts,
+ 'radio' => $allowed_atts,
+ 'optgroup' => $allowed_atts,
+ 'datalist' => $allowed_atts,
+ 'output' => $allowed_atts,
+ 'progress' => $allowed_atts,
+ 'meter' => $allowed_atts,
+ 'source' => $allowed_atts,
+ )
)
);
llms_maybe_define_constant( 'LLMS_CONFIRMATION_FIELDS', array( 'email_address_confirm', 'password_confirm' ) );
@@ -369,6 +336,8 @@ public function init() {
( new LLMS_Media_Protector() )->register_callbacks();
include_once 'includes/class-llms-elementor-migrate.php';
+ include_once 'includes/class-llms-beaver-builder.php';
+ include_once 'includes/class-llms-beaver-builder-migrate.php';
do_action( 'lifterlms_init' );
}
diff --git a/composer.json b/composer.json
index 78ec050bbb..972f568c09 100644
--- a/composer.json
+++ b/composer.json
@@ -29,7 +29,7 @@
"php": ">=7.4",
"composer/installers": "~1.9.0",
"deliciousbrains/wp-background-processing": "1.0.2",
- "lifterlms/lifterlms-blocks": "2.5.8",
+ "lifterlms/lifterlms-blocks": "2.5.9",
"lifterlms/lifterlms-cli": "0.0.4",
"lifterlms/lifterlms-helper": "3.5.4",
"lifterlms/lifterlms-rest": "1.0.2",
diff --git a/docs/releases.md b/docs/releases.md
deleted file mode 100644
index 120ada8d3b..0000000000
--- a/docs/releases.md
+++ /dev/null
@@ -1,163 +0,0 @@
-Releasing LifterLMS Builds
-==========================
-
-This document outlines the workflow used by LifterLMS core maintainers to build and publish LifterLMS releases.
-
-This document assumes you have already installed LifterLMS for development following the [Installing for Development guide](./installing.md).
-
-## 0. Get ready
-
-Make sure you have your local repository up to date. If your origin is set to the gocodebox/lifterlms repo, these commands will get you up to date.
-
-1. `git checkout dev` and `git pull`
-2. `git checkout trunk` and `git pull`
-
-Make sure your @lifterlms/dev package in package.json is on the latest version. If it needs to be updated, update it, commit to the dev branch, and then run `npm install`.
-
-Make sure you are back on the dev branch.
-
-1. `git checkout dev`
-
-Make sure you have installed composer requirements via `composer install`.
-
-Make sure you have the latest `@lifterlms` JS packages. Note that this will update node_modules using the latest published/stable version of the packages, and won't include any updates made to those packages by this release itself.
-
-1. `npm install`
-
-Make sure that the dev version (or trunk since it will merge automatically) are tested up to the latest version of WordPress.
-
-For Add-ons, also confirm that the plugin headers include appropriate values for LLMS minimum version and LLMS tested up to as follows:
-
-1. Adjust these lines in the header of the main plugin .php file.
-
-* ` * Tested up to: 6.4.1` (this is the WordPress tested up to value)
-* ` * LLMS requires at least: 6.0.0` (only update this value if you are sure that the update breaks backwards compatibility)
-* ` * LLMS tested up to: 7.5.0 ` (this should be updated to the latest LifterLMS stable version)
-
-## 1. Build the Release
-
-Prepare the release: `npm run dev release prepare`:
-
-When running this command, the following happens:
-
-1. Determines the version number based on the significance values found in `.changelogs/` files. Unless `-F` is passed to the command to force a specific version number.
-2. Write the changelog entries to `CHANGELOG.md`.
-3. Updates version numbers of placeholder `[version]` tags, `package.json`, etc...
-4. Runs the release build command, `npm run build`.
-
-## 2. Run tests and coding standards checks
-
-0. Ensure phpunit tests are installed: `composer run tests-install`.
-1. Ensure phpunit tests pass: `composer run tests-run`.
-2. Ensure phpcs checks pass: `composer run check-cs-errors`.
-3. Ensure e2e tests pass: `npm run test`.
-4. Ensure eslint checks pass: `npm run lint:js`.
-
-## 3. Commit and push
-
-After building and testing the built release, all changes should be committed and pushed to GitHub.
-
-1. `git commit -a`
-2. Enter something like "build version 7.1.1" for the commit message.
-3. `git push`
-
-## 4. Generate the Distribution Archive
-
-Run `npm run dev release archive -- -i`.
-
-This is a more pedantic version of `npm run dev release archive` that will allow to easily inspect
-the archive: once created, the archive will be unpacked into the `dist` directory so that its content
-could be easily inspected. E.g. make sure it doesn't contain undesired files such as unwanted dependencies
-into the `vendor` directory, and so on.
-
-## 5. Run pre-release tests on the archived
-
-Install and activate the zip file on a temporary sandbox site.
-
-Note: If you are reusing a testing site that already has LifterLMS installed, you can add this line to your wp-config.php and then uninstall and delete LifterLMS from the plugins screen and it will delete all of the LifterLMS data.
-
-`define( 'LLMS_REMOVE_ALL_DATA', true );`
-
- 1. Run the setup wizard.
- 2. Import sample course
- 3. Enroll a student into the course.
- 4. Complete a lesson.
-
-_This manual testing ensures no errors occurred in the build steps above._
-
-## 6. Publish the Release
-
-Run `npm run dev release create`.
-
-The following steps are performed automatically by the above task:
-
-1. Publish to GitHub
- 1. The contents of the distribution archive is force-pushed to the `release` branch.
- 1. A new release tag draft is created for the current version number using `release` as the commit target.
- 1. The distribution archive is uploaded to the release.
- 1. The release is published.
- 1. A webhook ping notifies the `llms-releaser` server which performs the remaining steps of the release:
-1. Publish to WordPress plugin repository
- 1. Create a new SVN tag using the release asset (distribution archive) as the base.
- 1. Update the `trunk` branch to match the new tag.
-1. A changelog blog post is published to make.lifterlms.com.
-1. The number is updated at LifterLMS.com
-1. The distribution archive is synced to the release asset bucket in AWS S3 as a backup.
-
-## 7. Update Trunk
-
-After everything is complete, the final version of should be committed and pushed to GitHub trunk branch. It is possible this can also be done on GitHub.com directly by create a Pull Request from `dev` to `trunk`
-
-1. `git checkout trunk`
-2. `git merge dev`
-3. `git push`
-
-## 8. Push to WordPress.org (If Needed)
-
-As of this writing, only the core LifterLMS plugin, the LifterLMS Labs plugin, and the Lite LMS Prgress Tracker are hosted on wordpress.org.
-
-Note: The feature of the llms-releaser server that pushes updates to wordpress.org has been disabled due to some bugs there. The steps below can be used to "manually" push a release to wordpress.org.
-
-1. If you don't have a lifterlms-svn folder, create it. (The first time you create this, it will take many minutes to download.)
- 1. Navigate to your plugins folder.
- 1. `mkdir lifterlms-svn`
- 1. `cd lifterlms-svn`
- 1. `svn co http://plugins.svn.wordpress.org/lifterlms .`
-1. Make sure your svn repo is up to date with the remote repo by running `svn update`.
-1. Make room for the update by clearing out trunk: `rm -f -f trunk/*`
-1. Copy the new dist files into trunk. `cp -r -f ../lifterlms/dist/lifterlms/* trunk/`
-1. Check what has changed: `svn status`
-1. svn add any new files
- 1. If there are a lot of files to add, you can use `svn add --force trunk/*`
-1. svn rm any deleted files
- 1. If there are a lot of files to remove, you can use `svn st | grep ^! | awk '{print " --force "$2}' | xargs svn rm`
-1. Run `svn status` one more time to review changes and make sure all files are being properly modified, added, or removed from the repo.
-
-These next step is optional for point releases, but should be done for major and minor releases and whenever the deployment process is updated enough to warrant a double check.
-
-1. Update stable version in trunk readme to point to the last stable version.
- 1. `nano trunk/readme.txt`
- 1. Change stable to previous version. (not this version)
-
-1. Commit to SVN
- 1. `svn commit -m "7.6.2 - bug fixes and enhancements"`
-1. Run svn status again to make sure there are no files that still need to be added.
- 1. `svn status`
-1. Create a tag for the new version
- 1. `svn cp trunk/ tags/7.6.2`
- 1. `svn commit -m "tag for new version"`
-1. Wait (about 15min) for each commit to go out to WP repo.
-
-If you updated the stable version to point to the previous version, test then update to the latest version.
-
-1. Test trunk by visiting [the Advanced Tab of the plugin page](https://wordpress.org/plugins/lifterlms/advanced/)
- 1. scroll to the bottom of the page
- 1. Choose "Development Version" from the dropdown.
- 1. Click download.
- 1. Install the zip on a fresh dev site and run the standard set up and enroll test or any other tests you want.
-1. If the test goes well, update the stable tag to the latest version.
- 1. `nano trunk/readme.txt`
- 1. `nano tags/7.6.2/readme.txt`
- 1. `commit -m "updating stable version"`
-
-
diff --git a/includes/abstracts/abstract.llms.admin.metabox.php b/includes/abstracts/abstract.llms.admin.metabox.php
index 7b2f6388f0..684a5196fe 100644
--- a/includes/abstracts/abstract.llms.admin.metabox.php
+++ b/includes/abstracts/abstract.llms.admin.metabox.php
@@ -516,21 +516,25 @@ protected function save_field( $post_id, $field ) {
$val = '';
- // Get the posted value & sanitize it.
- if ( isset( $_POST[ $field['id'] ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified in `$this->save()` which calls this method.
-
- $flags = array();
+ if ( ! isset( $_POST[ $field['id'] ] ) ) {
+ return $this->save_field_db( $post_id, $field['id'], $val );
+ }
- if ( isset( $field['sanitize'] ) && in_array( $field['sanitize'], array( 'shortcode', 'no_encode_quotes' ), true ) ) {
- $flags[] = FILTER_FLAG_NO_ENCODE_QUOTES;
- } elseif ( ! empty( $field['multi'] ) ) {
- $flags[] = FILTER_REQUIRE_ARRAY;
- }
+ if ( 'basic-editor' === $field['type'] ) {
+ $val = wp_kses( $_POST[ $field['id'] ], LLMS_ALLOWED_HTML_PRICES );
+ return $this->save_field_db( $post_id, $field['id'], $val );
+ }
- $val = llms_filter_input_sanitize_string( INPUT_POST, $field['id'], $flags );
+ $flags = array();
+ if ( isset( $field['sanitize'] ) && in_array( $field['sanitize'], array( 'shortcode', 'no_encode_quotes' ), true ) ) {
+ $flags[] = FILTER_FLAG_NO_ENCODE_QUOTES;
+ } elseif ( ! empty( $field['multi'] ) ) {
+ $flags[] = FILTER_REQUIRE_ARRAY;
}
+ $val = llms_filter_input_sanitize_string( INPUT_POST, $field['id'], $flags );
+
return $this->save_field_db( $post_id, $field['id'], $val );
}
diff --git a/includes/abstracts/llms-abstract-email-provider.php b/includes/abstracts/llms-abstract-email-provider.php
index ae82888436..54ff8aa66c 100644
--- a/includes/abstracts/llms-abstract-email-provider.php
+++ b/includes/abstracts/llms-abstract-email-provider.php
@@ -31,7 +31,6 @@ abstract class LLMS_Abstract_Email_Provider {
* @var array
*/
protected $providers = array(
- 'mailhawk',
'sendwp',
);
@@ -119,7 +118,6 @@ public function __construct() {
*/
$priority = $this->is_connected() ? 5 : 10;
add_action( 'admin_init', array( $this, 'init' ), $priority );
-
}
/**
@@ -152,8 +150,9 @@ public function init() {
add_filter( 'llms_email_delivery_services', array( $this, 'add_settings' ) );
add_action( 'wp_ajax_llms_' . $this->id . '_remote_install', array( $this, 'ajax_callback_remote_install' ) );
- add_action( 'admin_print_footer_scripts', array( $this, 'output_js' ) );
+ add_action( 'wp_ajax_llms_' . $this->id . '_remote_install_verify', array( $this, 'ajax_callback_remote_install_verify' ) );
+ add_action( 'admin_print_footer_scripts', array( $this, 'output_js' ) );
}
/**
@@ -182,7 +181,6 @@ protected function activate_already_installed_plugin() {
}
return $is_plugin_installed;
-
}
/**
@@ -215,7 +213,6 @@ public function add_settings( $settings ) {
);
return array_merge( $settings, $new_settings );
-
}
/**
@@ -230,7 +227,20 @@ public function ajax_callback_remote_install() {
$ret = $this->do_remote_install();
ob_clean();
wp_send_json( $ret, ! empty( $ret['status'] ) ? $ret['status'] : 200 );
+ }
+ /**
+ * Ajax callback called after doing the initial install, so the plugin is loaded and available.
+ *
+ * @since 8.0.0
+ *
+ * @return void
+ */
+ public function ajax_callback_remote_install_verify() {
+
+ $ret = $this->do_remote_install_verify();
+ ob_clean();
+ wp_send_json( $ret, ! empty( $ret['status'] ) ? $ret['status'] : 200 );
}
/**
@@ -257,7 +267,6 @@ protected function can_remote_install() {
}
return true;
-
}
/**
@@ -273,7 +282,6 @@ protected function disable_other_providers() {
foreach ( $disable as $id ) {
add_filter( 'llms_disable_' . $id, '__return_true' );
}
-
}
/**
@@ -300,8 +308,32 @@ protected function do_remote_install() {
);
}
- return $this->do_remote_install_success();
+ return array( 'success' => true );
+ }
+ /**
+ * Verify the remote install, and then perform the post-install response. Otherwise the plugin isn't available yet.
+ *
+ * @return array
+ */
+ protected function do_remote_install_verify() {
+
+ $ret = $this->do_remote_install();
+
+ if ( is_wp_error( $ret ) ) {
+ return $ret;
+ }
+
+ if ( ! $this->is_installed() ) {
+ // Translators: %s = title of the email delivery plugin.
+ return array(
+ 'code' => 'llms_' . $this->id . '_not_found',
+ 'message' => sprintf( __( '%s plugin not found. Please try again.', 'lifterlms' ), $this->get_title() ),
+ 'status' => 400,
+ );
+ }
+
+ return $this->do_remote_install_success();
}
/**
@@ -321,14 +353,7 @@ protected function install() {
$ret = $this->install_plugin();
}
- // Final check to ensure the connector is installed and activated.
- if ( true === $ret && ! $this->is_installed() ) {
- // Translators: %s = title of the email delivery plugin.
- return new WP_Error( 'llms_' . $this->id . '_not_found', sprtinf( __( '%s plugin not found. Please try again.', 'lifterlms' ), $this->get_title() ), $install );
- }
-
return $ret;
-
}
/**
@@ -368,7 +393,6 @@ protected function install_plugin() {
}
return true;
-
}
/**
@@ -388,7 +412,5 @@ protected function should_output_inline() {
$screen = get_current_screen();
return ( 'lifterlms_page_llms-settings' === $screen->id && 'engagements' === llms_filter_input( INPUT_GET, 'tab' ) && ! $this->is_connected() );
-
}
-
}
diff --git a/includes/admin/class-llms-mailhawk.php b/includes/admin/class-llms-mailhawk.php
index 13dfbf7803..d8cdf647cd 100644
--- a/includes/admin/class-llms-mailhawk.php
+++ b/includes/admin/class-llms-mailhawk.php
@@ -43,7 +43,7 @@ protected function do_remote_install_success() {
'partner_id' => self::PARTNER_ID,
'register_url' => esc_url( trailingslashit( MAILHAWK_LICENSE_SERVER_URL ) ),
'client_state' => esc_html( \MailHawk\Keys::instance()->state() ),
- 'redirect_uri' => esc_url( \MailHawk\get_admin_mailhawk_uri() ),
+ 'redirect_uri' => esc_url( \MailHawk\mailhawk_admin_page() ),
);
}
@@ -134,7 +134,7 @@ protected function is_connected() {
* @return boolean
*/
protected function is_installed() {
- return defined( 'MAILHAWK_VERSION' );
+ return function_exists( '\MailHawk\mailhawk_admin_page' );
}
/**
diff --git a/includes/admin/class.llms.admin.assets.php b/includes/admin/class.llms.admin.assets.php
index dc7036f0a8..5a15471047 100644
--- a/includes/admin/class.llms.admin.assets.php
+++ b/includes/admin/class.llms.admin.assets.php
@@ -287,10 +287,15 @@ public function admin_scripts() {
if ( 'course' === $post_type || 'llms_membership' === $post_type ) {
+ self::register_quill();
+ self::register_a11y_dialog();
+
wp_enqueue_script( 'llms-metabox-students', LLMS_PLUGIN_URL . 'assets/js/llms-metabox-students' . LLMS_ASSETS_SUFFIX . '.js', array( 'jquery', 'llms-select2' ), LLMS_ASSETS_VERSION, true );
- wp_enqueue_script( 'llms-metabox-product', LLMS_PLUGIN_URL . 'assets/js/llms-metabox-product' . LLMS_ASSETS_SUFFIX . '.js', array( 'jquery', 'llms' ), LLMS_ASSETS_VERSION, true );
+ wp_enqueue_script( 'llms-metabox-product', LLMS_PLUGIN_URL . 'assets/js/llms-metabox-product' . LLMS_ASSETS_SUFFIX . '.js', array( 'jquery', 'llms', 'llms-a11y-dialog' ), LLMS_ASSETS_VERSION, true );
wp_enqueue_script( 'llms-metabox-instructors', LLMS_PLUGIN_URL . 'assets/js/llms-metabox-instructors' . LLMS_ASSETS_SUFFIX . '.js', array( 'jquery', 'llms' ), LLMS_ASSETS_VERSION, true );
+ wp_enqueue_script( 'llms-metabox-options', LLMS_PLUGIN_URL . 'assets/js/llms-metabox-options' . LLMS_ASSETS_SUFFIX . '.js', array( 'jquery', 'llms', 'llms-quill' ), LLMS_ASSETS_VERSION, true );
+ wp_enqueue_style( 'llms-quill-bubble' );
}
if ( 'lesson' === $post_type ) {
@@ -518,6 +523,20 @@ public static function register_quill( $modules = array() ) {
llms()->assets->register_script( "llms-quill-{$module}" );
}
}
+
+ /**
+ * Register the accessible dialog JS
+ *
+ * @since 8.0.0
+ *
+ * @return void
+ */
+ public static function register_a11y_dialog() {
+ if ( ! wp_script_is( 'llms-a11y-dialog', 'registered' ) ) {
+ wp_register_script( 'llms-a11y-dialog', LLMS_PLUGIN_URL . 'assets/vendor/a11y-dialog/a11y-dialog.min.js', array(), '8.1.1', true );
+ }
+ }
}
+
return new LLMS_Admin_Assets();
diff --git a/includes/admin/class.llms.admin.notices.core.php b/includes/admin/class.llms.admin.notices.core.php
index 7c4760e0ec..e56cce4d72 100644
--- a/includes/admin/class.llms.admin.notices.core.php
+++ b/includes/admin/class.llms.admin.notices.core.php
@@ -60,6 +60,31 @@ public static function add_init_actions() {
add_action( $action, array( __CLASS__, 'gateways' ), $priority );
add_action( $action, array( __CLASS__, 'media_protection' ), $priority );
+ add_action( $action, array( __CLASS__, 'beaver_builder' ), $priority );
+ }
+
+ public static function beaver_builder() {
+ $id = 'beaver-builder-lab';
+
+ if ( class_exists( 'LifterLMS_Labs' ) && llms_parse_bool( get_option( 'llms_lab_beaver-builder_enabled' ) ) && current_user_can( 'manage_lifterlms' ) ) {
+ $html = sprintf(
+ __( 'Improved Beaver Builder support is now included in core! To use it, %1$sdisable the Beaver Builder lab%2$s.', 'lifterlms' ),
+ '',
+ ' ',
+ );
+
+ LLMS_Admin_Notices::add_notice(
+ $id,
+ $html,
+ array(
+ 'type' => 'warning',
+ 'dismiss_for_days' => 730, // @TODO: there should be a "forever" setting here.
+ 'remindable' => false,
+ )
+ );
+ } elseif ( LLMS_Admin_Notices::has_notice( $id ) ) {
+ LLMS_Admin_Notices::delete_notice( $id );
+ }
}
/**
diff --git a/includes/admin/post-types/meta-boxes/class.llms.meta.box.course.options.php b/includes/admin/post-types/meta-boxes/class.llms.meta.box.course.options.php
index 2470cb3f9f..0b1bae01e9 100644
--- a/includes/admin/post-types/meta-boxes/class.llms.meta.box.course.options.php
+++ b/includes/admin/post-types/meta-boxes/class.llms.meta.box.course.options.php
@@ -33,7 +33,6 @@ public function configure() {
$this->title = __( 'Course Options', 'lifterlms' );
$this->screens = 'course';
$this->priority = 'high';
-
}
/**
@@ -175,6 +174,14 @@ public function get_fields() {
'id' => $this->prefix . 'audio_embed',
'class' => 'code input-full',
),
+ array(
+ 'type' => 'basic-editor',
+ 'label' => __( 'Featured Pricing Information', 'lifterlms' ),
+ 'desc' => __( 'Enter information on pricing for this course, to be displayed on the catalog page.', 'lifterlms' ),
+ 'id' => $this->prefix . 'featured_pricing',
+ 'class' => 'code input-full',
+ 'value' => 'test',
+ ),
),
),
array(
@@ -348,7 +355,7 @@ public function get_fields() {
'label' => __( 'Drip Method', 'lifterlms' ),
'value' => array(
array(
- 'key' => 'start',
+ 'key' => 'start',
'title' => __( 'After course start or enrollment', 'lifterlms' ),
),
),
@@ -424,7 +431,6 @@ function_exists( 'llms_blocks_is_post_migrated' ) && llms_blocks_is_post_migrate
}
return $fields;
-
}
/**
@@ -450,7 +456,5 @@ protected function save_before( $post_id ) {
unset( $_POST['_llms_post_course_difficulty'] ); // Don't save this to the postmeta table.
}
-
}
-
}
diff --git a/includes/admin/post-types/meta-boxes/class.llms.meta.box.membership.php b/includes/admin/post-types/meta-boxes/class.llms.meta.box.membership.php
index 96df0181a7..ecbef611fb 100644
--- a/includes/admin/post-types/meta-boxes/class.llms.meta.box.membership.php
+++ b/includes/admin/post-types/meta-boxes/class.llms.meta.box.membership.php
@@ -38,7 +38,6 @@ public function configure() {
'llms_membership',
);
$this->priority = 'high';
-
}
/**
@@ -78,7 +77,6 @@ private function get_content_table( $membership ) {
}
return apply_filters( 'llms_membership_get_content_table_data', $data, $membership );
-
}
/**
@@ -164,6 +162,20 @@ public function get_fields() {
),
),
+ array(
+ 'title' => __( 'General', 'lifterlms' ),
+ 'fields' => array(
+ array(
+ 'type' => 'basic-editor',
+ 'label' => __( 'Featured Pricing Information', 'lifterlms' ),
+ 'desc' => __( 'Enter information on pricing for this membership, to be displayed on the catalog page.', 'lifterlms' ),
+ 'id' => $this->prefix . 'featured_pricing',
+ 'class' => 'code input-full',
+ 'value' => 'test',
+ ),
+ ),
+ ),
+
array(
'title' => __( 'Restrictions', 'lifterlms' ),
'fields' => array(
@@ -316,6 +328,7 @@ public function save( $post_id ) {
$this->prefix . 'sales_page_content_page_id',
$this->prefix . 'sales_page_content_type',
$this->prefix . 'sales_page_content_url',
+ $this->prefix . 'featured_pricing',
);
if ( ! is_array( $fields ) ) {
@@ -340,6 +353,8 @@ public function save( $post_id ) {
$val = llms_filter_input_sanitize_string( INPUT_POST, $field['id'], array( FILTER_FLAG_NO_ENCODE_QUOTES ) );
} elseif ( isset( $field['multi'] ) && $field['multi'] ) {
$val = llms_filter_input_sanitize_string( INPUT_POST, $field['id'], array( FILTER_REQUIRE_ARRAY ) );
+ } elseif ( $field['type'] === 'basic-editor' ) {
+ $val = wp_kses( $_POST[ $field['id'] ], LLMS_ALLOWED_HTML_PRICES );
} else {
$val = llms_filter_input_sanitize_string( INPUT_POST, $field['id'] );
}
@@ -352,7 +367,5 @@ public function save( $post_id ) {
}
return $to_return;
-
}
-
}
diff --git a/includes/admin/post-types/meta-boxes/fields/llms.class.meta.box.basic.editor.php b/includes/admin/post-types/meta-boxes/fields/llms.class.meta.box.basic.editor.php
new file mode 100644
index 0000000000..c5bd2820ba
--- /dev/null
+++ b/includes/admin/post-types/meta-boxes/fields/llms.class.meta.box.basic.editor.php
@@ -0,0 +1,43 @@
+field = $_field;
+ }
+
+ /**
+ * outputs the Html for the given field
+ *
+ * @return void
+ */
+ public function output() {
+
+ parent::output(); ?>
+
+ field ) && $this->field['placeholder'] ) : ?>
+ data-placeholder="field['placeholder'] ); ?>"
+
+ >meta, LLMS_ALLOWED_HTML_PRICES ); ?>
+
+
+
+
+
+
+
+
+
+ ×
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/includes/admin/views/access-plans/access-plan.php b/includes/admin/views/access-plans/access-plan.php
index 7396d1b178..68856347d6 100644
--- a/includes/admin/views/access-plans/access-plan.php
+++ b/includes/admin/views/access-plans/access-plan.php
@@ -41,6 +41,10 @@
$on_sale = $plan->get( 'on_sale' );
$availability = $plan->get( 'availability' );
$checkout_redirect_url = $plan->get( 'checkout_redirect_url' );
+ $checkout_url = $plan->get_checkout_url( false );
+ if ( false === strpos( $checkout_url, home_url() ) ) {
+ $checkout_url = home_url( $checkout_url );
+ }
}
?>
@@ -65,6 +69,9 @@
+
+
+
@@ -88,7 +95,7 @@
- ' . esc_url( $plan->get_checkout_url( false ) ) . '' ); ?>
+ ' . esc_url( $checkout_url ) . '' ); ?>
@@ -554,7 +561,16 @@ class="llms-plan-price" name="_llms_plans[][pri
// Do we have any memberships to restrict this plan to?
$memberships_count = wp_count_posts( 'llms_membership' );
if ( $course && $memberships_count->publish > 0 ) :
- ?>
+
+ /**
+ * Filter to show/hide the Membership Settings for an Access Plan for a course.
+ *
+ * @param boolean $show_membership_settings Show membership settings for access plans.
+ * @param LLMS_Access_Plan $plan LLMS_Access_Plan.
+ * @param integer $id Access Plan ID.
+ */
+ if ( apply_filters( 'llms_show_membership_settings_for_access_plans', true, $plan, $id ) ) :
+ ?>
@@ -596,6 +612,7 @@ class="llms-plan-price" name="_llms_plans[][pri
diff --git a/includes/admin/views/access-plans/metabox.php b/includes/admin/views/access-plans/metabox.php
index 65871dac59..87d5bdd59f 100644
--- a/includes/admin/views/access-plans/metabox.php
+++ b/includes/admin/views/access-plans/metabox.php
@@ -26,7 +26,7 @@
);
// Translators: %1$s = Link to access plans documentation; %2$s = The singular label of the custom post type.
printf( wp_kses( __( 'Access plans define the payment options and access time-periods available for this %2$s.', 'lifterlms' ), $access_plan_allowed_html ), esc_url( 'https://lifterlms.com/docs/what-is-an-access-plan/' ), esc_html( strtolower( $product->get_post_type_label( 'singular_name' ) ) ) );
- ?>
+ ?>
@@ -60,4 +60,8 @@
require 'access-plan.php';
?>
+
+
+
+
diff --git a/includes/beaver-builder/index.php b/includes/beaver-builder/index.php
new file mode 100644
index 0000000000..d00b37e163
--- /dev/null
+++ b/includes/beaver-builder/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Author', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays the name, author, and bio for the author of a course.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-author/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-author/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module(
+ 'LLMS_Lab_Course_Author_Module',
+ array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'sections' => array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'fields' => array(
+ 'llms_course_id' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'course',
+ 'limit' => 1,
+ 'label' => esc_html__( 'Course', 'lifterlms' ),
+ 'help' => esc_html__( 'Select the course to display the author from. Leave blank for the current course.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_avatar_size' => array(
+ 'default' => 48,
+ 'type' => 'unit',
+ 'label' => esc_html__( 'Avatar Size', 'lifterlms' ),
+ 'description' => 'px',
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_show_bio' => array(
+ 'type' => 'select',
+ 'label' => esc_html__( 'Display Author Bio', 'lifterlms' ),
+ 'options' => array(
+ 'no' => esc_html__( 'No', 'lifterlms' ),
+ 'yes' => esc_html__( 'Yes', 'lifterlms' ),
+ ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+);
diff --git a/includes/beaver-builder/modules/course-author/includes/frontend.php b/includes/beaver-builder/modules/course-author/includes/frontend.php
new file mode 100644
index 0000000000..045386bd6b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-author/includes/frontend.php
@@ -0,0 +1,21 @@
+llms_course_id ) ? $settings->llms_course_id : get_the_ID();
+?>
+
+
+ [lifterlms_course_author avatar_size="llms_avatar_size ); ?>" bio="llms_show_bio ); ?>" course_id=""]
+
diff --git a/includes/beaver-builder/modules/course-author/includes/index.php b/includes/beaver-builder/modules/course-author/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-author/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Continue Button', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays a course progress bar for the current course.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-continue-button/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-continue-button/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+
+ add_filter( 'llms_course_continue_button_next_lesson', array( $this, 'force_display' ) );
+ }
+
+ /**
+ * Force display.
+ *
+ * @since 1.7.0
+ *
+ * @param int $lesson_id WP_Post ID of the lesson.
+ * @return int
+ */
+ public function force_display( $lesson_id ) {
+
+ if ( ! $lesson_id && FLBuilderModel::is_builder_active() ) {
+ return get_the_ID();
+ }
+
+ return $lesson_id;
+ }
+}
+
+FLBuilder::register_module(
+ 'LLMS_Lab_Course_Continue_Button_Module',
+ array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'sections' => array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'fields' => array(
+ 'llms_course_id' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'course',
+ 'limit' => 1,
+ 'label' => esc_html__( 'Course', 'lifterlms' ),
+ 'help' => esc_html__( 'Select the course to display a continue button for. Leave blank for the current course.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+);
diff --git a/includes/beaver-builder/modules/course-continue-button/includes/frontend.php b/includes/beaver-builder/modules/course-continue-button/includes/frontend.php
new file mode 100644
index 0000000000..f5dc6bd4ef
--- /dev/null
+++ b/includes/beaver-builder/modules/course-continue-button/includes/frontend.php
@@ -0,0 +1,21 @@
+llms_course_id ) ? $settings->llms_course_id : get_the_ID();
+?>
+
+
+ [lifterlms_course_continue_button course_id=""]
+
diff --git a/includes/beaver-builder/modules/course-continue-button/includes/index.php b/includes/beaver-builder/modules/course-continue-button/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-continue-button/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Instructors', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays instructors for the current course.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-instructors/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-instructors/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module( 'LLMS_Lab_Course_Instructors_Module', array() );
diff --git a/includes/beaver-builder/modules/course-instructors/includes/frontend.php b/includes/beaver-builder/modules/course-instructors/includes/frontend.php
new file mode 100644
index 0000000000..3f98c5d86c
--- /dev/null
+++ b/includes/beaver-builder/modules/course-instructors/includes/frontend.php
@@ -0,0 +1,20 @@
+
+
+
+ [lifterlms_course_instructors]
+
diff --git a/includes/beaver-builder/modules/course-instructors/includes/index.php b/includes/beaver-builder/modules/course-instructors/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-instructors/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Information', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays course information: length, difficulty, tracks, categories, and tags.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-meta-info/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-meta-info/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module(
+ 'LLMS_Lab_Course_Meta_Info_Module',
+ array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'sections' => array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'fields' => array(
+ 'llms_course_id' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'course',
+ 'limit' => 1,
+ 'label' => esc_html__( 'Course', 'lifterlms' ),
+ 'help' => esc_html__( 'Select the course to display the course information from. Leave blank for the current course.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+);
diff --git a/includes/beaver-builder/modules/course-meta-info/includes/frontend.php b/includes/beaver-builder/modules/course-meta-info/includes/frontend.php
new file mode 100644
index 0000000000..010357b5ad
--- /dev/null
+++ b/includes/beaver-builder/modules/course-meta-info/includes/frontend.php
@@ -0,0 +1,21 @@
+llms_course_id ) ? $settings->llms_course_id : get_the_ID();
+?>
+
+
+ [lifterlms_course_meta_info course_id=""]
+
diff --git a/includes/beaver-builder/modules/course-meta-info/includes/index.php b/includes/beaver-builder/modules/course-meta-info/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-meta-info/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Progress Bar', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays a course progress bar for the current course.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-progress-bar/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-progress-bar/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module( 'LLMS_Lab_Course_Progress_Bar_Module', array() );
diff --git a/includes/beaver-builder/modules/course-progress-bar/includes/frontend.php b/includes/beaver-builder/modules/course-progress-bar/includes/frontend.php
new file mode 100644
index 0000000000..47c22c3dc0
--- /dev/null
+++ b/includes/beaver-builder/modules/course-progress-bar/includes/frontend.php
@@ -0,0 +1,19 @@
+
+
+
+ [lifterlms_course_progress]
+
diff --git a/includes/beaver-builder/modules/course-progress-bar/includes/index.php b/includes/beaver-builder/modules/course-progress-bar/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-progress-bar/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Course Syllabus', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays a course syllabus current course.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'course-syllabus/',
+ 'url' => LLMS_BB_MODULES_URL . 'course-syllabus/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module( 'LLMS_Lab_Course_Syllabus_Module', array() );
diff --git a/includes/beaver-builder/modules/course-syllabus/includes/frontend.php b/includes/beaver-builder/modules/course-syllabus/includes/frontend.php
new file mode 100644
index 0000000000..29cab0441d
--- /dev/null
+++ b/includes/beaver-builder/modules/course-syllabus/includes/frontend.php
@@ -0,0 +1,19 @@
+
+
+
+ [lifterlms_course_syllabus]
+
diff --git a/includes/beaver-builder/modules/course-syllabus/includes/index.php b/includes/beaver-builder/modules/course-syllabus/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/course-syllabus/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Lesson Mark Complete Button', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays the mark complete / incomplete button(s) for a lesson', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'lesson-mark-complete/',
+ 'url' => LLMS_BB_MODULES_URL . 'lesson-mark-complete/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module( 'LLMS_Lab_Lesson_Mark_Complete_Module', array() );
diff --git a/includes/beaver-builder/modules/lesson-mark-complete/includes/frontend.php b/includes/beaver-builder/modules/lesson-mark-complete/includes/frontend.php
new file mode 100644
index 0000000000..d5d3d16582
--- /dev/null
+++ b/includes/beaver-builder/modules/lesson-mark-complete/includes/frontend.php
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ 12,
+ 'classes' => 'llms-button-primary auto button',
+ 'id' => 'llms_mark_complete',
+ 'value' => apply_filters( 'lifterlms_mark_lesson_complete_button_text', esc_html__( 'Mark Complete', 'lifterlms' ), llms_get_post( get_the_ID() ) ),
+ 'last_column' => true,
+ 'name' => 'mark_complete',
+ 'required' => false,
+ 'type' => 'submit',
+ )
+ );
+ ?>
+
+
+ [lifterlms_lesson_mark_complete]
+
+
diff --git a/includes/beaver-builder/modules/lesson-mark-complete/includes/index.php b/includes/beaver-builder/modules/lesson-mark-complete/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/lesson-mark-complete/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Membership Instructors', 'lifterlms' ),
+ 'description' => esc_html__( 'Displays instructors for the current membership.', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'membership-instructors/',
+ 'url' => LLMS_BB_MODULES_URL . 'membership-instructors/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+ }
+}
+
+FLBuilder::register_module( 'LLMS_Lab_Membership_Instructors_Module', array() );
diff --git a/includes/beaver-builder/modules/membership-instructors/includes/frontend.php b/includes/beaver-builder/modules/membership-instructors/includes/frontend.php
new file mode 100644
index 0000000000..d284168d6f
--- /dev/null
+++ b/includes/beaver-builder/modules/membership-instructors/includes/frontend.php
@@ -0,0 +1,18 @@
+
+
+
+ [lifterlms_membership_instructors]
+
diff --git a/includes/beaver-builder/modules/membership-instructors/includes/index.php b/includes/beaver-builder/modules/membership-instructors/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/membership-instructors/includes/index.php
@@ -0,0 +1 @@
+ esc_html__( 'Pricing Table', 'lifterlms' ),
+ 'description' => esc_html__( 'LifterLMS Course / Membership Pricing Table', 'lifterlms' ),
+ 'category' => esc_html__( 'LifterLMS Modules', 'lifterlms' ),
+ 'dir' => LLMS_BB_MODULES_DIR . 'pricing-table/',
+ 'url' => LLMS_BB_MODULES_URL . 'pricing-table/',
+ 'editor_export' => false,
+ 'enabled' => true,
+ )
+ );
+
+ // Ensure pricing tables always display when used within a BB module.
+ add_action( 'llms_lab_bb_before_pricing_table', array( $this, 'add_force_show_table_filter' ) );
+ add_action( 'lifterlms_after_access_plans', array( $this, 'remove_force_show_table_filter' ) );
+
+ // Ensure pricing tables always display when the frontend builder is active.
+ add_filter( 'llms_product_pricing_table_enrollment_status', array( $this, 'show_table' ) );
+ }
+
+ /**
+ * Force display of pricing tables within BB modules.
+ *
+ * @since 1.3.0
+ *
+ * @return void
+ */
+ public function add_force_show_table_filter() {
+ add_filter( 'llms_product_pricing_table_enrollment_status', '__return_false' );
+ }
+
+ /**
+ * Remove force display after pricing tables within BB modules.
+ *
+ * @since 1.3.0
+ *
+ * @return void
+ */
+ public function remove_force_show_table_filter() {
+ remove_filter( 'llms_product_pricing_table_enrollment_status', '__return_false' );
+ }
+
+ /**
+ * Get the product ID to be used based of BB module settings.
+ *
+ * @since 1.3.0
+ * @since 1.3.0 Use strict comparison for `in_array`.
+ *
+ * @param obj $settings BB node settings object.
+ * @return int|false
+ */
+ public function get_product_id( $settings ) {
+
+ $type = $settings->llms_product_type;
+ if ( ! $type ) {
+ $id = get_the_ID();
+ } elseif ( 'course' === $type || 'membership' === $type ) {
+ $key = sprintf( 'llms_%s_id', $type );
+ $id = $settings->$key;
+ }
+
+ if ( in_array( get_post_type( $id ), array( 'lesson', 'llms_quiz' ), true ) ) {
+ $course = llms_get_post_parent_course( $id );
+ $id = $course->get( 'id' );
+ }
+
+ // If the current id isn't a course or membership don't proceed.
+ if ( ! in_array( get_post_type( $id ), array( 'course', 'llms_membership' ), true ) ) {
+ return false;
+ }
+
+ return $id;
+ }
+
+ /**
+ * Always show the pricing table when the builder is active.
+ *
+ * @since 1.3.0
+ *
+ * @param bool $enrollment Enrollment status of the current user.
+ * @return bool
+ */
+ public function show_table( $enrollment ) {
+
+ if ( FLBuilderModel::is_builder_active() ) {
+ return false;
+ }
+
+ return $enrollment;
+ }
+}
+
+FLBuilder::register_module(
+ 'LLMS_Lab_Pricing_Table_Module',
+ array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'sections' => array(
+ 'general' => array(
+ 'title' => esc_html__( 'General', 'lifterlms' ),
+ 'fields' => array(
+ 'llms_product_type' => array(
+ 'type' => 'select',
+ 'label' => esc_html__( 'Product Type', 'lifterlms' ),
+ 'options' => array(
+ '' => esc_html__( 'Current Course or Membership', 'lifterlms' ),
+ 'course' => esc_html__( 'Course', 'lifterlms' ),
+ 'membership' => esc_html__( 'Memebership', 'lifterlms' ),
+ ),
+ 'toggle' => array(
+ 'course' => array(
+ 'fields' => array( 'llms_course_id' ),
+ ),
+ 'membership' => array(
+ 'fields' => array( 'llms_membership_id' ),
+ ),
+ ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_course_id' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'course',
+ 'limit' => 1,
+ 'label' => esc_html__( 'Course', 'lifterlms' ),
+ 'help' => esc_html__( 'Choose which course to display a pricing table for.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_membership_id' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'llms_membership',
+ 'limit' => 1,
+ 'label' => esc_html__( 'Membership', 'lifterlms' ),
+ 'help' => esc_html__( 'Choose which membership to display a pricing table for.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+);
diff --git a/includes/beaver-builder/modules/pricing-table/includes/frontend.php b/includes/beaver-builder/modules/pricing-table/includes/frontend.php
new file mode 100644
index 0000000000..eb119d1702
--- /dev/null
+++ b/includes/beaver-builder/modules/pricing-table/includes/frontend.php
@@ -0,0 +1,26 @@
+get_product_id( $settings );
+if ( ! $product_id ) {
+ return;
+}
+?>
+
+
+
+ [lifterlms_pricing_table product=""]
+
+
diff --git a/includes/beaver-builder/modules/pricing-table/includes/index.php b/includes/beaver-builder/modules/pricing-table/includes/index.php
new file mode 100644
index 0000000000..a0f0951f4b
--- /dev/null
+++ b/includes/beaver-builder/modules/pricing-table/includes/index.php
@@ -0,0 +1 @@
+Visiting / Non-enrolled Description
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ratio quidem vestra sic cogit. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Terram, mihi crede, ea lanx et maria deprimet. Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere.
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ratio quidem vestra sic cogit. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Terram, mihi crede, ea lanx et maria deprimet. Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere.";s:18:"responsive_display";s:0:"";s:18:"visibility_display";s:17:"llms_not_enrolled";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:15:"animation_delay";s:3:"0.0";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"margin_top";s:0:"";s:17:"margin_top_medium";s:0:"";s:21:"margin_top_responsive";s:0:"";s:12:"margin_right";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:9:"rich-text";s:22:"llms_enrollment_status";s:12:"not-enrolled";}}s:13:"5a85f4631441e";O:8:"stdClass":5:{s:4:"node";s:13:"5a85f4631441e";s:4:"type";s:6:"module";s:6:"parent";s:13:"5a85f4631432a";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":27:{s:10:"margin_top";s:0:"";s:17:"margin_top_medium";s:0:"";s:21:"margin_top_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:12:"margin_right";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:18:"responsive_display";s:0:"";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:9:"animation";s:0:"";s:15:"animation_delay";s:3:"0.0";s:2:"id";s:0:"";s:5:"class";s:0:"";s:4:"type";s:6:"widget";s:22:"widget-course_syllabus";O:8:"stdClass":3:{s:5:"title";s:14:"Course Outline";s:8:"collapse";i:1;s:7:"toggles";i:0;}s:6:"widget";s:27:"LLMS_Widget_Course_Syllabus";s:22:"llms_enrollment_status";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";}}s:13:"5a85f46314472";O:8:"stdClass":5:{s:4:"node";s:13:"5a85f46314472";s:4:"type";s:6:"module";s:6:"parent";s:13:"5a85f46314306";s:8:"position";i:1;s:8:"settings";O:8:"stdClass":26:{s:4:"text";s:619:"Enrolled Student Description
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ratio quidem vestra sic cogit. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Terram, mihi crede, ea lanx et maria deprimet. Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere.
+
+Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ratio quidem vestra sic cogit. Expressa vero in iis aetatibus, quae iam confirmatae sunt. Terram, mihi crede, ea lanx et maria deprimet. Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere.";s:18:"responsive_display";s:0:"";s:18:"visibility_display";s:13:"llms_enrolled";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:15:"animation_delay";s:3:"0.0";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"margin_top";s:0:"";s:17:"margin_top_medium";s:0:"";s:21:"margin_top_responsive";s:0:"";s:12:"margin_right";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:9:"rich-text";s:22:"llms_enrollment_status";s:8:"enrolled";}}}s:8:"settings";O:8:"stdClass":2:{s:3:"css";s:0:"";s:2:"js";s:0:"";}s:4:"type";s:6:"layout";}}}
diff --git a/includes/beaver-builder/templates/default-course-template.dat b/includes/beaver-builder/templates/default-course-template.dat
new file mode 100644
index 0000000000..9f4ecaf128
--- /dev/null
+++ b/includes/beaver-builder/templates/default-course-template.dat
@@ -0,0 +1 @@
+a:1:{s:6:"layout";a:1:{i:0;O:8:"stdClass":9:{s:4:"name";s:28:"LifterLMS Course Template v2";s:4:"slug";s:28:"lifterlms-course-template-v2";s:5:"index";i:0;s:6:"global";b:0;s:5:"image";s:0:"";s:10:"categories";a:1:{s:13:"uncategorized";s:13:"Uncategorized";}s:5:"nodes";a:10:{s:12:"jliog2q601df";O:8:"stdClass":6:{s:4:"node";s:12:"jliog2q601df";s:4:"type";s:3:"row";s:6:"parent";N;s:8:"position";i:0;s:8:"settings";O:8:"stdClass":194:{s:5:"width";s:4:"full";s:13:"content_width";s:5:"fixed";s:17:"max_content_width";s:0:"";s:22:"max_content_width_unit";s:2:"px";s:11:"full_height";s:7:"default";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:17:"content_alignment";s:6:"center";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:4:"none";s:15:"bg_image_source";s:7:"library";s:12:"bg_image_url";s:0:"";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:9:"no-repeat";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_video_source";s:9:"wordpress";s:8:"bg_video";s:0:"";s:13:"bg_video_webm";s:0:"";s:16:"bg_video_url_mp4";s:0:"";s:17:"bg_video_url_webm";s:0:"";s:20:"bg_video_service_url";s:0:"";s:14:"bg_video_audio";s:2:"no";s:15:"bg_video_mobile";s:2:"no";s:17:"bg_video_fallback";s:0:"";s:21:"bg_video_fallback_src";s:0:"";s:9:"ss_source";s:9:"wordpress";s:9:"ss_photos";s:0:"";s:11:"ss_feed_url";s:0:"";s:8:"ss_speed";s:1:"3";s:13:"ss_transition";s:4:"fade";s:21:"ss_transitionDuration";s:1:"1";s:12:"ss_randomize";s:5:"false";s:17:"bg_parallax_image";s:0:"";s:21:"bg_parallax_image_src";s:0:"";s:23:"bg_parallax_image_large";s:0:"";s:27:"bg_parallax_image_large_src";s:0:"";s:24:"bg_parallax_image_medium";s:0:"";s:28:"bg_parallax_image_medium_src";s:0:"";s:28:"bg_parallax_image_responsive";s:0:"";s:32:"bg_parallax_image_responsive_src";s:0:"";s:17:"bg_parallax_speed";s:1:"2";s:18:"bg_parallax_offset";s:0:"";s:24:"bg_parallax_offset_large";s:0:"";s:25:"bg_parallax_offset_medium";s:0:"";s:29:"bg_parallax_offset_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:8:"bg_color";s:0:"";s:11:"bg_gradient";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:17:"bg_gradient_large";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:18:"bg_gradient_medium";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:22:"bg_gradient_responsive";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:13:"bg_embed_code";s:0:"";s:6:"border";a:5:{s:5:"style";s:0:"";s:5:"color";s:0:"";s:5:"width";a:4:{s:3:"top";s:0:"";s:5:"right";s:0:"";s:6:"bottom";s:0:"";s:4:"left";s:0:"";}s:6:"radius";a:4:{s:8:"top_left";s:0:"";s:9:"top_right";s:0:"";s:11:"bottom_left";s:0:"";s:12:"bottom_right";s:0:"";}s:6:"shadow";a:5:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";s:6:"spread";s:0:"";}}s:12:"border_large";a:5:{s:5:"style";s:0:"";s:5:"color";s:0:"";s:5:"width";a:4:{s:3:"top";s:0:"";s:5:"right";s:0:"";s:6:"bottom";s:0:"";s:4:"left";s:0:"";}s:6:"radius";a:4:{s:8:"top_left";s:0:"";s:9:"top_right";s:0:"";s:11:"bottom_left";s:0:"";s:12:"bottom_right";s:0:"";}s:6:"shadow";a:5:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";s:6:"spread";s:0:"";}}s:13:"border_medium";a:5:{s:5:"style";s:0:"";s:5:"color";s:0:"";s:5:"width";a:4:{s:3:"top";s:0:"";s:5:"right";s:0:"";s:6:"bottom";s:0:"";s:4:"left";s:0:"";}s:6:"radius";a:4:{s:8:"top_left";s:0:"";s:9:"top_right";s:0:"";s:11:"bottom_left";s:0:"";s:12:"bottom_right";s:0:"";}s:6:"shadow";a:5:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";s:6:"spread";s:0:"";}}s:17:"border_responsive";a:5:{s:5:"style";s:0:"";s:5:"color";s:0:"";s:5:"width";a:4:{s:3:"top";s:0:"";s:5:"right";s:0:"";s:6:"bottom";s:0:"";s:4:"left";s:0:"";}s:6:"radius";a:4:{s:8:"top_left";s:0:"";s:9:"top_right";s:0:"";s:11:"bottom_left";s:0:"";s:12:"bottom_right";s:0:"";}s:6:"shadow";a:5:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";s:6:"spread";s:0:"";}}s:14:"top_edge_shape";s:0:"";s:14:"top_edge_align";s:10:"top center";s:19:"top_edge_fill_style";s:5:"color";s:19:"top_edge_fill_color";s:3:"aaa";s:25:"top_edge_fill_color_large";s:0:"";s:26:"top_edge_fill_color_medium";s:0:"";s:30:"top_edge_fill_color_responsive";s:0:"";s:22:"top_edge_fill_gradient";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:18:"top_edge_transform";a:7:{s:10:"scaleXSign";s:0:"";s:10:"scaleYSign";s:0:"";s:5:"skewX";s:0:"";s:5:"skewY";s:0:"";s:6:"scaleX";s:1:"1";s:6:"rotate";s:0:"";s:6:"scaleY";s:1:"1";}s:17:"bottom_edge_shape";s:0:"";s:17:"bottom_edge_align";s:13:"bottom center";s:22:"bottom_edge_fill_style";s:5:"color";s:22:"bottom_edge_fill_color";s:3:"aaa";s:28:"bottom_edge_fill_color_large";s:0:"";s:29:"bottom_edge_fill_color_medium";s:0:"";s:33:"bottom_edge_fill_color_responsive";s:0:"";s:25:"bottom_edge_fill_gradient";a:5:{s:4:"type";s:6:"linear";s:5:"angle";s:2:"90";s:8:"position";s:13:"center center";s:6:"colors";a:2:{i:0;s:0:"";i:1;s:0:"";}s:5:"stops";a:2:{i:0;s:1:"0";i:1;s:3:"100";}}s:21:"bottom_edge_transform";a:7:{s:10:"scaleXSign";s:0:"";s:10:"scaleYSign";s:0:"";s:5:"skewX";s:0:"";s:5:"skewY";s:0:"";s:6:"scaleX";s:1:"1";s:6:"rotate";s:0:"";s:6:"scaleY";s:1:"1";}s:18:"container_overflow";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:19:"top_edge_size_width";s:0:"";s:18:"top_edge_size_unit";s:2:"px";s:20:"top_edge_size_height";s:0:"";s:17:"top_edge_size_top";s:0:"";s:22:"bottom_edge_size_width";s:0:"";s:21:"bottom_edge_size_unit";s:2:"px";s:23:"bottom_edge_size_height";s:0:"";s:20:"bottom_edge_size_top";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"2glhdmstr08x";O:8:"stdClass":6:{s:4:"node";s:12:"2glhdmstr08x";s:4:"type";s:12:"column-group";s:6:"parent";s:12:"jliog2q601df";s:8:"position";i:0;s:8:"settings";s:0:"";s:6:"global";b:0;}s:12:"fl5tw9x2zseh";O:8:"stdClass":6:{s:4:"node";s:12:"fl5tw9x2zseh";s:4:"type";s:6:"column";s:6:"parent";s:12:"2glhdmstr08x";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":133:{s:4:"size";i:100;s:10:"size_large";s:0:"";s:11:"size_medium";s:0:"";s:15:"size_responsive";s:0:"";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:12:"equal_height";s:2:"no";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:17:"content_alignment";s:3:"top";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:5:"color";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:4:"none";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";s:0:"";s:8:"bg_color";s:0:"";s:11:"bg_gradient";s:0:"";s:17:"bg_gradient_large";s:0:"";s:18:"bg_gradient_medium";s:0:"";s:22:"bg_gradient_responsive";s:0:"";s:6:"border";s:0:"";s:12:"border_large";s:0:"";s:13:"border_medium";s:0:"";s:17:"border_responsive";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:16:"responsive_order";s:0:"";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;}s:6:"global";b:0;}s:12:"pijxr03bodzq";O:8:"stdClass":6:{s:4:"node";s:12:"pijxr03bodzq";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:1;s:8:"settings";O:8:"stdClass":42:{s:14:"llms_course_id";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:38:"class.llms.lab.course.meta.info.module";s:27:"responsive_display_filtered";b:1;s:24:"as_values_llms_course_id";s:0:"";s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"8mzehjn6w3rk";O:8:"stdClass":6:{s:4:"node";s:12:"8mzehjn6w3rk";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:3;s:8:"settings";O:8:"stdClass":45:{s:17:"llms_product_type";s:0:"";s:14:"llms_course_id";s:0:"";s:18:"llms_membership_id";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:35:"class.llms.lab.pricing.table.module";s:27:"responsive_display_filtered";b:1;s:24:"as_values_llms_course_id";s:0:"";s:28:"as_values_llms_membership_id";s:0:"";s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"5a04vrwqb8e6";O:8:"stdClass":6:{s:4:"node";s:12:"5a04vrwqb8e6";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:4;s:8:"settings";O:8:"stdClass":40:{s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:13:"llms_enrolled";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:41:"class.llms.lab.course.progress.bar.module";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"rx4cp18qmuf2";O:8:"stdClass":6:{s:4:"node";s:12:"rx4cp18qmuf2";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:6;s:8:"settings";O:8:"stdClass":42:{s:14:"llms_course_id";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:44:"class.llms.lab.course.continue.button.module";s:27:"responsive_display_filtered";b:1;s:24:"as_values_llms_course_id";s:0:"";s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"y8txrf61koc4";O:8:"stdClass":6:{s:4:"node";s:12:"y8txrf61koc4";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:5;s:8:"settings";O:8:"stdClass":40:{s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:37:"class.llms.lab.course.syllabus.module";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"vpyexwnartcs";O:8:"stdClass":6:{s:4:"node";s:12:"vpyexwnartcs";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":52:{s:7:"heading";s:18:"Course Information";s:3:"tag";s:2:"h2";s:4:"link";s:0:"";s:11:"link_target";s:5:"_self";s:13:"link_nofollow";s:2:"no";s:5:"color";s:0:"";s:10:"typography";a:11:{s:11:"font_family";s:7:"Default";s:11:"font_weight";s:7:"default";s:9:"font_size";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:11:"line_height";a:2:{s:6:"length";s:0:"";s:4:"unit";s:0:"";}s:10:"text_align";s:0:"";s:14:"letter_spacing";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:14:"text_transform";s:0:"";s:15:"text_decoration";s:0:"";s:10:"font_style";s:0:"";s:12:"font_variant";s:0:"";s:11:"text_shadow";a:4:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";}}s:16:"typography_large";a:9:{s:9:"font_size";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:11:"line_height";a:2:{s:6:"length";s:0:"";s:4:"unit";s:0:"";}s:10:"text_align";s:0:"";s:14:"letter_spacing";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:14:"text_transform";s:0:"";s:15:"text_decoration";s:0:"";s:10:"font_style";s:0:"";s:12:"font_variant";s:0:"";s:11:"text_shadow";a:4:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";}}s:17:"typography_medium";a:9:{s:9:"font_size";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:11:"line_height";a:2:{s:6:"length";s:0:"";s:4:"unit";s:0:"";}s:10:"text_align";s:0:"";s:14:"letter_spacing";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:14:"text_transform";s:0:"";s:15:"text_decoration";s:0:"";s:10:"font_style";s:0:"";s:12:"font_variant";s:0:"";s:11:"text_shadow";a:4:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";}}s:21:"typography_responsive";a:9:{s:9:"font_size";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:11:"line_height";a:2:{s:6:"length";s:0:"";s:4:"unit";s:0:"";}s:10:"text_align";s:0:"";s:14:"letter_spacing";a:2:{s:6:"length";s:0:"";s:4:"unit";s:2:"px";}s:14:"text_transform";s:0:"";s:15:"text_decoration";s:0:"";s:10:"font_style";s:0:"";s:12:"font_variant";s:0:"";s:11:"text_shadow";a:4:{s:5:"color";s:0:"";s:10:"horizontal";s:0:"";s:8:"vertical";s:0:"";s:4:"blur";s:0:"";}}s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:7:"heading";s:27:"responsive_display_filtered";b:1;s:11:"link-search";s:0:"";s:21:"as_values_link-search";s:0:"";s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"192jb8ht7qnf";O:8:"stdClass":6:{s:4:"node";s:12:"192jb8ht7qnf";s:4:"type";s:6:"module";s:6:"parent";s:12:"fl5tw9x2zseh";s:8:"position";i:2;s:8:"settings";O:8:"stdClass":40:{s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:40:"class.llms.lab.course.instructors.module";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}}s:8:"settings";O:8:"stdClass":2:{s:3:"css";s:0:"";s:2:"js";s:0:"";}s:4:"type";s:6:"layout";}}}
diff --git a/includes/beaver-builder/templates/default-lesson-template.dat b/includes/beaver-builder/templates/default-lesson-template.dat
new file mode 100644
index 0000000000..af72691ce0
--- /dev/null
+++ b/includes/beaver-builder/templates/default-lesson-template.dat
@@ -0,0 +1 @@
+a:1:{s:6:"layout";a:1:{i:0;O:8:"stdClass":9:{s:4:"name";s:16:"LifterLMS Lesson";s:4:"slug";s:16:"lifterlms-lesson";s:5:"index";i:0;s:6:"global";b:0;s:5:"image";s:0:"";s:10:"categories";a:1:{s:13:"uncategorized";s:13:"Uncategorized";}s:5:"nodes";a:4:{s:12:"yl8pz6amh015";O:8:"stdClass":6:{s:4:"node";s:12:"yl8pz6amh015";s:4:"type";s:3:"row";s:6:"parent";N;s:8:"position";i:0;s:8:"settings";O:8:"stdClass":192:{s:5:"width";s:5:"fixed";s:13:"content_width";s:5:"fixed";s:17:"max_content_width";s:0:"";s:22:"max_content_width_unit";s:2:"px";s:11:"full_height";s:7:"default";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:17:"content_alignment";s:6:"center";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:4:"none";s:15:"bg_image_source";s:7:"library";s:12:"bg_image_url";s:0:"";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:4:"none";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_video_source";s:9:"wordpress";s:8:"bg_video";s:0:"";s:13:"bg_video_webm";s:0:"";s:16:"bg_video_url_mp4";s:0:"";s:17:"bg_video_url_webm";s:0:"";s:20:"bg_video_service_url";s:0:"";s:14:"bg_video_audio";s:2:"no";s:15:"bg_video_mobile";s:2:"no";s:17:"bg_video_fallback";s:0:"";s:21:"bg_video_fallback_src";s:0:"";s:9:"ss_source";s:9:"wordpress";s:9:"ss_photos";s:0:"";s:11:"ss_feed_url";s:0:"";s:8:"ss_speed";s:1:"3";s:13:"ss_transition";s:4:"fade";s:21:"ss_transitionDuration";s:1:"1";s:12:"ss_randomize";s:5:"false";s:17:"bg_parallax_image";s:0:"";s:21:"bg_parallax_image_src";s:0:"";s:23:"bg_parallax_image_large";s:0:"";s:27:"bg_parallax_image_large_src";s:0:"";s:24:"bg_parallax_image_medium";s:0:"";s:28:"bg_parallax_image_medium_src";s:0:"";s:28:"bg_parallax_image_responsive";s:0:"";s:32:"bg_parallax_image_responsive_src";s:0:"";s:17:"bg_parallax_speed";s:4:"fast";s:18:"bg_parallax_offset";i:0;s:24:"bg_parallax_offset_large";s:0:"";s:25:"bg_parallax_offset_medium";s:0:"";s:29:"bg_parallax_offset_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";s:0:"";s:8:"bg_color";s:0:"";s:11:"bg_gradient";s:0:"";s:17:"bg_gradient_large";s:0:"";s:18:"bg_gradient_medium";s:0:"";s:22:"bg_gradient_responsive";s:0:"";s:13:"bg_embed_code";s:0:"";s:6:"border";s:0:"";s:12:"border_large";s:0:"";s:13:"border_medium";s:0:"";s:17:"border_responsive";s:0:"";s:14:"top_edge_shape";s:0:"";s:14:"top_edge_align";s:10:"top center";s:19:"top_edge_fill_style";s:5:"color";s:19:"top_edge_fill_color";s:3:"aaa";s:25:"top_edge_fill_color_large";s:0:"";s:26:"top_edge_fill_color_medium";s:0:"";s:30:"top_edge_fill_color_responsive";s:0:"";s:22:"top_edge_fill_gradient";s:0:"";s:18:"top_edge_transform";s:0:"";s:17:"bottom_edge_shape";s:0:"";s:17:"bottom_edge_align";s:13:"bottom center";s:22:"bottom_edge_fill_style";s:5:"color";s:22:"bottom_edge_fill_color";s:3:"aaa";s:28:"bottom_edge_fill_color_large";s:0:"";s:29:"bottom_edge_fill_color_medium";s:0:"";s:33:"bottom_edge_fill_color_responsive";s:0:"";s:25:"bottom_edge_fill_gradient";s:0:"";s:21:"bottom_edge_transform";s:0:"";s:18:"container_overflow";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:19:"top_edge_size_width";s:0:"";s:18:"top_edge_size_unit";s:2:"px";s:20:"top_edge_size_height";s:0:"";s:17:"top_edge_size_top";s:0:"";s:22:"bottom_edge_size_width";s:0:"";s:21:"bottom_edge_size_unit";s:2:"px";s:23:"bottom_edge_size_height";s:0:"";s:20:"bottom_edge_size_top";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;}s:6:"global";b:0;}s:12:"2jktgfquavlm";O:8:"stdClass":6:{s:4:"node";s:12:"2jktgfquavlm";s:4:"type";s:12:"column-group";s:6:"parent";s:12:"yl8pz6amh015";s:8:"position";i:0;s:8:"settings";s:0:"";s:6:"global";b:0;}s:12:"kug4765p9cia";O:8:"stdClass":6:{s:4:"node";s:12:"kug4765p9cia";s:4:"type";s:6:"column";s:6:"parent";s:12:"2jktgfquavlm";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":133:{s:4:"size";i:100;s:10:"size_large";s:0:"";s:11:"size_medium";s:0:"";s:15:"size_responsive";s:0:"";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:12:"equal_height";s:2:"no";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:17:"content_alignment";s:3:"top";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:5:"color";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:4:"none";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";s:0:"";s:8:"bg_color";s:0:"";s:11:"bg_gradient";s:0:"";s:17:"bg_gradient_large";s:0:"";s:18:"bg_gradient_medium";s:0:"";s:22:"bg_gradient_responsive";s:0:"";s:6:"border";s:0:"";s:12:"border_large";s:0:"";s:13:"border_medium";s:0:"";s:17:"border_responsive";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:16:"responsive_order";s:0:"";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;}s:6:"global";b:0;}s:12:"xfjay5k8e3c9";O:8:"stdClass":6:{s:4:"node";s:12:"xfjay5k8e3c9";s:4:"type";s:6:"module";s:6:"parent";s:12:"kug4765p9cia";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":40:{s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:42:"class.llms.lab.lesson.mark.complete.module";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}}s:8:"settings";O:8:"stdClass":2:{s:3:"css";s:0:"";s:2:"js";s:0:"";}s:4:"type";s:6:"layout";}}}
\ No newline at end of file
diff --git a/includes/beaver-builder/templates/default-llms_membership-template.dat b/includes/beaver-builder/templates/default-llms_membership-template.dat
new file mode 100644
index 0000000000..66f0b6b764
--- /dev/null
+++ b/includes/beaver-builder/templates/default-llms_membership-template.dat
@@ -0,0 +1 @@
+a:1:{s:6:"layout";a:1:{i:0;O:8:"stdClass":9:{s:4:"name";s:20:"LifterLMS Membership";s:4:"slug";s:20:"lifterlms-membership";s:5:"index";i:0;s:6:"global";b:0;s:5:"image";s:0:"";s:10:"categories";a:1:{s:13:"uncategorized";s:13:"Uncategorized";}s:5:"nodes";a:5:{s:12:"k39jbpv1lsha";O:8:"stdClass":6:{s:4:"node";s:12:"k39jbpv1lsha";s:4:"type";s:3:"row";s:6:"parent";N;s:8:"position";i:0;s:8:"settings";O:8:"stdClass":192:{s:5:"width";s:5:"fixed";s:13:"content_width";s:5:"fixed";s:17:"max_content_width";s:0:"";s:22:"max_content_width_unit";s:2:"px";s:11:"full_height";s:7:"default";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:17:"content_alignment";s:6:"center";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:4:"none";s:15:"bg_image_source";s:7:"library";s:12:"bg_image_url";s:0:"";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:4:"none";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_video_source";s:9:"wordpress";s:8:"bg_video";s:0:"";s:13:"bg_video_webm";s:0:"";s:16:"bg_video_url_mp4";s:0:"";s:17:"bg_video_url_webm";s:0:"";s:20:"bg_video_service_url";s:0:"";s:14:"bg_video_audio";s:2:"no";s:15:"bg_video_mobile";s:2:"no";s:17:"bg_video_fallback";s:0:"";s:21:"bg_video_fallback_src";s:0:"";s:9:"ss_source";s:9:"wordpress";s:9:"ss_photos";s:0:"";s:11:"ss_feed_url";s:0:"";s:8:"ss_speed";s:1:"3";s:13:"ss_transition";s:4:"fade";s:21:"ss_transitionDuration";s:1:"1";s:12:"ss_randomize";s:5:"false";s:17:"bg_parallax_image";s:0:"";s:21:"bg_parallax_image_src";s:0:"";s:23:"bg_parallax_image_large";s:0:"";s:27:"bg_parallax_image_large_src";s:0:"";s:24:"bg_parallax_image_medium";s:0:"";s:28:"bg_parallax_image_medium_src";s:0:"";s:28:"bg_parallax_image_responsive";s:0:"";s:32:"bg_parallax_image_responsive_src";s:0:"";s:17:"bg_parallax_speed";s:4:"fast";s:18:"bg_parallax_offset";i:0;s:24:"bg_parallax_offset_large";s:0:"";s:25:"bg_parallax_offset_medium";s:0:"";s:29:"bg_parallax_offset_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";s:0:"";s:8:"bg_color";s:0:"";s:11:"bg_gradient";s:0:"";s:17:"bg_gradient_large";s:0:"";s:18:"bg_gradient_medium";s:0:"";s:22:"bg_gradient_responsive";s:0:"";s:13:"bg_embed_code";s:0:"";s:6:"border";s:0:"";s:12:"border_large";s:0:"";s:13:"border_medium";s:0:"";s:17:"border_responsive";s:0:"";s:14:"top_edge_shape";s:0:"";s:14:"top_edge_align";s:10:"top center";s:19:"top_edge_fill_style";s:5:"color";s:19:"top_edge_fill_color";s:3:"aaa";s:25:"top_edge_fill_color_large";s:0:"";s:26:"top_edge_fill_color_medium";s:0:"";s:30:"top_edge_fill_color_responsive";s:0:"";s:22:"top_edge_fill_gradient";s:0:"";s:18:"top_edge_transform";s:0:"";s:17:"bottom_edge_shape";s:0:"";s:17:"bottom_edge_align";s:13:"bottom center";s:22:"bottom_edge_fill_style";s:5:"color";s:22:"bottom_edge_fill_color";s:3:"aaa";s:28:"bottom_edge_fill_color_large";s:0:"";s:29:"bottom_edge_fill_color_medium";s:0:"";s:33:"bottom_edge_fill_color_responsive";s:0:"";s:25:"bottom_edge_fill_gradient";s:0:"";s:21:"bottom_edge_transform";s:0:"";s:18:"container_overflow";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:19:"top_edge_size_width";s:0:"";s:18:"top_edge_size_unit";s:2:"px";s:20:"top_edge_size_height";s:0:"";s:17:"top_edge_size_top";s:0:"";s:22:"bottom_edge_size_width";s:0:"";s:21:"bottom_edge_size_unit";s:2:"px";s:23:"bottom_edge_size_height";s:0:"";s:20:"bottom_edge_size_top";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;}s:6:"global";b:0;}s:12:"n1qzbtkc8pvh";O:8:"stdClass":6:{s:4:"node";s:12:"n1qzbtkc8pvh";s:4:"type";s:12:"column-group";s:6:"parent";s:12:"k39jbpv1lsha";s:8:"position";i:0;s:8:"settings";s:0:"";s:6:"global";b:0;}s:12:"98w4t7m1vukq";O:8:"stdClass":6:{s:4:"node";s:12:"98w4t7m1vukq";s:4:"type";s:6:"column";s:6:"parent";s:12:"n1qzbtkc8pvh";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":133:{s:4:"size";i:100;s:10:"size_large";s:0:"";s:11:"size_medium";s:0:"";s:15:"size_responsive";s:0:"";s:10:"min_height";s:0:"";s:15:"min_height_unit";s:2:"px";s:16:"min_height_large";s:0:"";s:21:"min_height_large_unit";s:2:"px";s:17:"min_height_medium";s:0:"";s:22:"min_height_medium_unit";s:2:"px";s:21:"min_height_responsive";s:0:"";s:26:"min_height_responsive_unit";s:2:"px";s:12:"equal_height";s:2:"no";s:12:"aspect_ratio";s:0:"";s:18:"aspect_ratio_large";s:0:"";s:19:"aspect_ratio_medium";s:0:"";s:23:"aspect_ratio_responsive";s:0:"";s:17:"content_alignment";s:3:"top";s:10:"text_color";s:0:"";s:10:"link_color";s:0:"";s:11:"hover_color";s:0:"";s:13:"heading_color";s:0:"";s:7:"bg_type";s:5:"color";s:8:"bg_image";s:0:"";s:12:"bg_image_src";s:0:"";s:14:"bg_image_large";s:0:"";s:18:"bg_image_large_src";s:0:"";s:15:"bg_image_medium";s:0:"";s:19:"bg_image_medium_src";s:0:"";s:19:"bg_image_responsive";s:0:"";s:23:"bg_image_responsive_src";s:0:"";s:9:"bg_repeat";s:4:"none";s:15:"bg_repeat_large";s:0:"";s:16:"bg_repeat_medium";s:0:"";s:20:"bg_repeat_responsive";s:0:"";s:11:"bg_position";s:13:"center center";s:17:"bg_position_large";s:0:"";s:18:"bg_position_medium";s:0:"";s:22:"bg_position_responsive";s:0:"";s:13:"bg_x_position";s:0:"";s:18:"bg_x_position_unit";s:1:"%";s:19:"bg_x_position_large";s:0:"";s:24:"bg_x_position_large_unit";s:1:"%";s:20:"bg_x_position_medium";s:0:"";s:25:"bg_x_position_medium_unit";s:1:"%";s:24:"bg_x_position_responsive";s:0:"";s:29:"bg_x_position_responsive_unit";s:1:"%";s:13:"bg_y_position";s:0:"";s:18:"bg_y_position_unit";s:1:"%";s:19:"bg_y_position_large";s:0:"";s:24:"bg_y_position_large_unit";s:1:"%";s:20:"bg_y_position_medium";s:0:"";s:25:"bg_y_position_medium_unit";s:1:"%";s:24:"bg_y_position_responsive";s:0:"";s:29:"bg_y_position_responsive_unit";s:1:"%";s:13:"bg_attachment";s:6:"scroll";s:19:"bg_attachment_large";s:0:"";s:20:"bg_attachment_medium";s:0:"";s:24:"bg_attachment_responsive";s:0:"";s:7:"bg_size";s:5:"cover";s:13:"bg_size_large";s:0:"";s:14:"bg_size_medium";s:0:"";s:18:"bg_size_responsive";s:0:"";s:15:"bg_overlay_type";s:5:"color";s:16:"bg_overlay_color";s:0:"";s:19:"bg_overlay_gradient";s:0:"";s:8:"bg_color";s:0:"";s:11:"bg_gradient";s:0:"";s:17:"bg_gradient_large";s:0:"";s:18:"bg_gradient_medium";s:0:"";s:22:"bg_gradient_responsive";s:0:"";s:6:"border";s:0:"";s:12:"border_large";s:0:"";s:13:"border_medium";s:0:"";s:17:"border_responsive";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:16:"responsive_order";s:0:"";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";s:0:"";s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:11:"padding_top";s:0:"";s:12:"padding_unit";s:2:"px";s:17:"padding_top_large";s:0:"";s:18:"padding_large_unit";s:2:"px";s:18:"padding_top_medium";s:0:"";s:19:"padding_medium_unit";s:2:"px";s:22:"padding_top_responsive";s:0:"";s:23:"padding_responsive_unit";s:2:"px";s:13:"padding_right";s:0:"";s:19:"padding_right_large";s:0:"";s:20:"padding_right_medium";s:0:"";s:24:"padding_right_responsive";s:0:"";s:14:"padding_bottom";s:0:"";s:20:"padding_bottom_large";s:0:"";s:21:"padding_bottom_medium";s:0:"";s:25:"padding_bottom_responsive";s:0:"";s:12:"padding_left";s:0:"";s:18:"padding_left_large";s:0:"";s:19:"padding_left_medium";s:0:"";s:23:"padding_left_responsive";s:0:"";s:27:"responsive_display_filtered";b:1;}s:6:"global";b:0;}s:12:"4f7pbu8hqjt3";O:8:"stdClass":6:{s:4:"node";s:12:"4f7pbu8hqjt3";s:4:"type";s:6:"module";s:6:"parent";s:12:"98w4t7m1vukq";s:8:"position";i:1;s:8:"settings";O:8:"stdClass":45:{s:17:"llms_product_type";s:0:"";s:14:"llms_course_id";s:0:"";s:18:"llms_membership_id";s:0:"";s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:35:"class.llms.lab.pricing.table.module";s:27:"responsive_display_filtered";b:1;s:24:"as_values_llms_course_id";s:0:"";s:28:"as_values_llms_membership_id";s:0:"";s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}s:12:"1b68kdan35e2";O:8:"stdClass":6:{s:4:"node";s:12:"1b68kdan35e2";s:4:"type";s:6:"module";s:6:"parent";s:12:"98w4t7m1vukq";s:8:"position";i:0;s:8:"settings";O:8:"stdClass":40:{s:18:"responsive_display";s:27:"desktop,large,medium,mobile";s:18:"visibility_display";s:0:"";s:26:"visibility_user_capability";s:0:"";s:20:"llms_enrollment_type";s:0:"";s:21:"llms_enrollment_match";s:0:"";s:15:"llms_course_ids";s:0:"";s:19:"llms_membership_ids";s:0:"";s:9:"animation";a:3:{s:5:"style";s:0:"";s:5:"delay";s:1:"0";s:8:"duration";s:1:"1";}s:17:"container_element";s:3:"div";s:2:"id";s:0:"";s:5:"class";s:0:"";s:10:"node_label";s:0:"";s:6:"export";s:0:"";s:6:"import";s:0:"";s:11:"bb_css_code";s:0:"";s:10:"bb_js_code";s:0:"";s:10:"margin_top";s:0:"";s:11:"margin_unit";s:2:"px";s:16:"margin_top_large";s:0:"";s:17:"margin_large_unit";s:2:"px";s:17:"margin_top_medium";s:0:"";s:18:"margin_medium_unit";s:2:"px";s:21:"margin_top_responsive";s:0:"";s:22:"margin_responsive_unit";s:2:"px";s:12:"margin_right";s:0:"";s:18:"margin_right_large";s:0:"";s:19:"margin_right_medium";s:0:"";s:23:"margin_right_responsive";s:0:"";s:13:"margin_bottom";s:0:"";s:19:"margin_bottom_large";s:0:"";s:20:"margin_bottom_medium";s:0:"";s:24:"margin_bottom_responsive";s:0:"";s:11:"margin_left";s:0:"";s:17:"margin_left_large";s:0:"";s:18:"margin_left_medium";s:0:"";s:22:"margin_left_responsive";s:0:"";s:4:"type";s:44:"class.llms.lab.membership.instructors.module";s:27:"responsive_display_filtered";b:1;s:25:"as_values_llms_course_ids";s:0:"";s:29:"as_values_llms_membership_ids";s:0:"";}s:6:"global";b:0;}}s:8:"settings";O:8:"stdClass":2:{s:3:"css";s:0:"";s:2:"js";s:0:"";}s:4:"type";s:6:"layout";}}}
\ No newline at end of file
diff --git a/includes/beaver-builder/templates/index.php b/includes/beaver-builder/templates/index.php
new file mode 100644
index 0000000000..22e2576755
--- /dev/null
+++ b/includes/beaver-builder/templates/index.php
@@ -0,0 +1 @@
+is_migratable_post_type( $post->ID ) ) {
+ return;
+ }
+
+ if ( ! $this->should_migrate_post( $post->ID ) ) {
+ return;
+ }
+
+ $this->add_template_to_post();
+ }
+
+ protected function is_migratable_post_type( $post_id ) {
+ return in_array( get_post_type( $post_id ), array( 'course', 'lesson', 'llms_membership' ) );
+ }
+
+ public function add_template_to_post() {
+ // Get the existing layout data.
+ $data = FLBuilderModel::get_layout_data();
+
+ if ( ! $data ) {
+ return;
+ }
+
+ $path = LLMS_PLUGIN_DIR . 'includes/beaver-builder/templates/default-' . get_post_type() . '-template.dat';
+
+ if ( ! file_exists( $path ) ) {
+ return;
+ }
+
+ $templates = maybe_unserialize( file_get_contents( $path ) );
+ $template = $templates['layout'][0];
+
+ // TODO : Check if the data already has the template inserted.
+
+ // Get the next top-level position.
+ $position = FLBuilderModel::next_node_position( 'row' );
+
+ // Adjust the position of template nodes.
+ foreach ( $template->nodes as $node_id => $node ) {
+ if ( ! $node->parent ) {
+ $template->nodes[ $node_id ]->position += $position;
+ }
+ }
+ // Merge the template nodes with the existing nodes.
+ $data = array_merge( $data, $template->nodes );
+
+ FLBuilderModel::update_layout_data( $data );
+ }
+
+ /**
+ * Removes core template action hooks from posts which have been migrated to beaver builder widgets.
+ *
+ * @since 8.0.0
+ *
+ * @return void
+ */
+ public function remove_template_hooks() {
+ if ( ! function_exists( 'llms_is_beaver_builder_post' ) ||
+ ! llms_is_beaver_builder_post() ||
+ ( get_the_ID() && ! llms_parse_bool( get_post_meta( get_the_ID(), '_llms_beaver_builder_migrated', true ) ) ) ) {
+
+ if ( ! $this->is_migratable_post_type( get_the_ID() ) ) {
+ return;
+ }
+
+ // Remove the bottom actions if the builder is currently active to avoid confusion with uneditable pieces.
+ if ( ! class_exists( 'FLBuilderModel' ) || ! method_exists( 'FLBuilderModel', 'is_builder_active' ) || ! FLBuilderModel::is_builder_active() ) {
+ return;
+ }
+ }
+
+ switch ( get_post_type() ) {
+ case 'course':
+ $this->remove_course_template_hooks();
+ break;
+ case 'lesson':
+ $this->remove_lesson_template_hooks();
+ break;
+ case 'llms_membership':
+ $this->remove_membership_template_hooks();
+ break;
+ }
+ }
+
+ /**
+ * Remove membership template hooks.
+ *
+ * @since 8.0.0
+ */
+ public function remove_membership_template_hooks() {
+ remove_action( 'lifterlms_single_membership_after_summary', 'lifterlms_template_pricing_table', 10 );
+ }
+
+ /**
+ * Remove lesson template hooks.
+ *
+ * @since 8.0.0
+ */
+ public function remove_lesson_template_hooks() {
+ remove_action( 'lifterlms_single_lesson_after_summary', 'lifterlms_template_complete_lesson_link', 10 );
+ }
+
+ /**
+ * Remove course template hooks.
+ *
+ * @since 8.0.0
+ *
+ * @return void
+ */
+ public function remove_course_template_hooks() {
+ // TODO: Refactor this so it's not duplicated between Elementor and Beaver Builder.
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_meta_wrapper_start', 5 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_length', 10 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_difficulty', 20 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_course_tracks', 25 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_course_categories', 30 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_course_tags', 35 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_meta_wrapper_end', 50 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_course_progress', 60 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_single_syllabus', 90 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_course_author', 40 );
+ remove_action( 'lifterlms_single_course_after_summary', 'lifterlms_template_pricing_table', 60 );
+ }
+
+ /**
+ * Determine if a post should be migrated.
+ *
+ * @since 8.0.0
+ *
+ * @param int $post_id WP_Post ID.
+ * @return bool
+ */
+ public function should_migrate_post( $post_id ) {
+
+ $ret = ! llms_parse_bool( get_post_meta( $post_id, '_llms_beaver_builder_migrated', true ) );
+
+ /**
+ * Filters whether or not a post should be migrated
+ *
+ * @since 8.0.0
+ *
+ * @param bool $migrate Whether or not a post should be migrated.
+ * @param int $post_id WP_Post ID.
+ */
+ return apply_filters( 'llms_beaver_builder_should_migrate_post', $ret, $post_id );
+ }
+
+ /**
+ * Update post meta data to signal status of the editor migration.
+ *
+ * @since 8.0.0
+ *
+ * @param int $post_id WP_Post ID.
+ * @param bool $publish Whether or not the post is being published.
+ * @return void
+ */
+ public function maybe_update_migration_status( $post_id, $publish ) {
+ if ( ! $publish ) {
+ return;
+ }
+
+ if ( ! $this->is_migratable_post_type( $post_id ) ) {
+ return;
+ }
+
+ if ( llms_parse_bool( get_post_meta( $post_id, '_llms_beaver_builder_migrated', true ) ) ) {
+ return;
+ }
+
+ update_post_meta( $post_id, '_llms_beaver_builder_migrated', 'yes' );
+ }
+}
+
+return new LLMS_Beaver_Builder_Migrate();
diff --git a/includes/class-llms-beaver-builder.php b/includes/class-llms-beaver-builder.php
new file mode 100644
index 0000000000..62bb1b3936
--- /dev/null
+++ b/includes/class-llms-beaver-builder.php
@@ -0,0 +1,525 @@
+init();
+ }
+
+ public function is_available() {
+ return class_exists( 'FLBuilder' );
+ }
+
+ protected function init() {
+
+ // Break early if the LifterLMS Labs is installed and enabled.
+ if ( class_exists( 'LifterLMS_Labs' ) && llms_parse_bool( get_option( 'llms_lab_beaver-builder_enabled' ) ) ) {
+ return;
+ }
+
+ if ( ! class_exists( 'FLBuilder' ) || ! class_exists( 'FLBuilderModel' ) || ! class_exists( 'FLBUilderModule' ) ) {
+ return;
+ }
+
+ // Prevent uneditable llms post types from being enabled for page building.
+ add_filter( 'fl_builder_admin_settings_post_types', array( $this, 'remove_uneditable_post_types' ) );
+
+ // Add migrateable post types to the builder by default.
+ add_filter( 'fl_builder_post_types', array( $this, 'enable_post_types_by_default' ) );
+
+ add_action( 'wp', array( $this, 'load_modules' ), 1 );
+ add_action( 'init', array( $this, 'load_templates' ) );
+
+ add_filter( 'fl_builder_register_module', array( $this, 'register_module' ), 10, 2 );
+
+ add_filter( 'llms_page_restricted', array( $this, 'mod_page_restrictions' ), 999, 2 );
+
+ add_filter( 'fl_builder_register_settings_form', array( $this, 'add_visibility_settings' ), 999, 2 );
+
+ add_filter( 'fl_builder_is_node_visible', array( $this, 'is_node_visible' ), 10, 2 );
+
+ // Hide editors when builder is enabled for a post.
+ add_filter( 'llms_metabox_fields_lifterlms_course_options', array( $this, 'mod_metabox_fields' ) );
+ add_filter( 'llms_metabox_fields_lifterlms_membership', array( $this, 'mod_metabox_fields' ) );
+
+ add_filter( 'fl_builder_upgrade_url', array( $this, 'upgrade_url' ) );
+
+ // LifterLMS Private Areas.
+ add_action( 'llms_pa_before_do_area_content', array( $this, 'llms_pa_before_content' ) );
+ add_action( 'llms_pa_after_do_area_content', array( $this, 'llms_pa_after_content' ) );
+ }
+
+ public function enable_post_types_by_default( $types ) {
+ $types[] = 'course';
+ $types[] = 'lesson';
+ $types[] = 'llms_membership';
+
+ return $types;
+ }
+
+ /**
+ * Add LLMS post types to the enabled builder post types.
+ *
+ * Stub function called during install.
+ *
+ * @since 8.0.0
+ *
+ * @return void
+ */
+ public function install() {
+ if ( ! $this->is_available() ) {
+ return;
+ }
+
+ $existing = get_option( '_fl_builder_post_types', array( 'page' ) );
+ $types = array_unique( array_merge( $existing, array( 'course', 'lesson', 'llms_membership' ) ) );
+ update_option( '_fl_builder_post_types', $types );
+ }
+
+ /**
+ * This function should return array of settings fields.
+ *
+ * @since 1.3.0
+ *
+ * @return array
+ */
+ protected function settings() {
+ return array();
+ }
+
+ /**
+ * Add custom visibility settings for enrollments to the BB "Visibility" section.
+ *
+ * @since 1.5.0
+ * @since 1.5.3 Fixed localization textdomain.
+ * @since 1.7.0 Escaped strings.
+ *
+ * @param array $form Settings form array.
+ * @param string $id ID of the row/module/col/etc.
+ * @return array
+ */
+ public function add_visibility_settings( $form, $id ) {
+
+ $options = array(
+ 'llms_enrolled' => esc_html__( 'Enrolled Students', 'lifterlms' ),
+ 'llms_not_enrolled' => esc_html__( 'Non-Enrolled Students and Visitors', 'lifterlms' ),
+ );
+
+ $toggle = array(
+ 'llms_enrolled' => array(
+ 'fields' => array( 'llms_enrollment_type' ),
+ ),
+ 'llms_not_enrolled' => array(
+ 'fields' => array( 'llms_enrollment_type' ),
+ ),
+ );
+
+ $fields = array(
+ 'llms_enrollment_type' => array(
+ 'type' => 'select',
+ 'label' => esc_html__( 'In', 'lifterlms' ),
+ 'options' => array(
+ '' => esc_html__( 'Current Course or Membership', 'lifterlms' ),
+ 'any' => esc_html__( 'Any Course(s) or Membership(s)', 'lifterlms' ),
+ 'specific' => esc_html__( 'Specific Course(s) and/or Membership(s)', 'lifterlms' ),
+ ),
+ 'toggle' => array(
+ 'specific' => array(
+ 'fields' => array( 'llms_enrollment_match', 'llms_course_ids', 'llms_membership_ids' ),
+ ),
+ ),
+ 'help' => esc_html__( 'Select how to check the enrollment status of the current student.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_enrollment_match' => array(
+ 'type' => 'select',
+ 'label' => esc_html__( 'Match', 'lifterlms' ),
+ 'options' => array(
+ '' => esc_html__( 'Any of the following', 'lifterlms' ),
+ 'all' => esc_html__( 'All of the following', 'lifterlms' ),
+ ),
+ 'help' => esc_html__( 'Select how to check the enrollment status of the current student.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_course_ids' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'course',
+ 'label' => esc_html__( 'Courses', 'lifterlms' ),
+ 'help' => esc_html__( 'Choose which course(s) the student must be enrolled (or not enrolled) in to view this element.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ 'llms_membership_ids' => array(
+ 'type' => 'suggest',
+ 'action' => 'fl_as_posts',
+ 'data' => 'llms_membership',
+ 'label' => esc_html__( 'Memberships', 'lifterlms' ),
+ 'help' => esc_html__( 'Choose which membership(s) the student must be enrolled (or not enrolled) in to view this element.', 'lifterlms' ),
+ 'preview' => array(
+ 'type' => 'none',
+ ),
+ ),
+ );
+
+ // Rows.
+ if (
+ isset( $form['tabs'] ) &&
+ isset( $form['tabs']['advanced'] ) &&
+ isset( $form['tabs']['advanced']['sections'] ) &&
+ isset( $form['tabs']['advanced']['sections']['visibility'] )
+ ) {
+
+ $form['tabs']['advanced']['sections']['visibility']['fields']['visibility_display']['options'] = array_merge( $form['tabs']['advanced']['sections']['visibility']['fields']['visibility_display']['options'], $options );
+ $form['tabs']['advanced']['sections']['visibility']['fields']['visibility_display']['toggle'] = array_merge( $form['tabs']['advanced']['sections']['visibility']['fields']['visibility_display']['toggle'], $toggle );
+ $form['tabs']['advanced']['sections']['visibility']['fields'] = array_merge( $form['tabs']['advanced']['sections']['visibility']['fields'], $fields );
+
+ // Modules.
+ } elseif (
+ isset( $form['sections'] ) &&
+ isset( $form['sections']['visibility'] )
+ ) {
+
+ $form['sections']['visibility']['fields']['visibility_display']['options'] = array_merge( $form['sections']['visibility']['fields']['visibility_display']['options'], $options );
+ $form['sections']['visibility']['fields']['visibility_display']['toggle'] = array_merge( $form['sections']['visibility']['fields']['visibility_display']['toggle'], $toggle );
+ $form['sections']['visibility']['fields'] = array_merge( $form['sections']['visibility']['fields'], $fields );
+
+ }
+
+ return $form;
+ }
+
+ /**
+ * Create a single array of course & membership IDs from a BB node settings object.
+ *
+ * @since 1.3.0
+ *
+ * @param obj $settings BB Node Settings.
+ * @return array
+ */
+ private function get_related_posts_from_settings( $settings ) {
+
+ $post_ids = array();
+
+ foreach ( array( 'llms_course_ids', 'llms_membership_ids' ) as $key ) {
+
+ if ( ! empty( $settings->$key ) ) {
+
+ $ids = explode( ',', $settings->$key );
+ $post_ids = array_merge( $post_ids, $ids );
+
+ }
+ }
+
+ return $post_ids;
+ }
+
+ /**
+ * Determine if a node is visible based on llms enrollments status visibility settings.
+ *
+ * @since 1.3.0
+ * @since 1.5.0 Unknown.
+ * @since 1.5.3 Fixed visibility conditional logic for `'specific'` enrollment type.
+ * @since 1.7.0 Use `in_array()` strict comparisons.
+ *
+ * @param bool $visible Default visibility.
+ * @param obj $node BB node object.
+ * @return boolean
+ */
+ public function is_node_visible( $visible, $node ) {
+
+ if ( isset( $node->settings ) && isset( $node->settings->visibility_display ) && false !== strpos( $node->settings->visibility_display, 'llms_' ) ) {
+
+ $status = $node->settings->visibility_display;
+
+ $uid = get_current_user_id();
+ $type = ! empty( $node->settings->llms_enrollment_type ) ? $node->settings->llms_enrollment_type : null;
+
+ llms_log( $type );
+
+ if ( ! $type || 'any' === $type ) {
+
+ // No type means current course/membership.
+ if ( ! $type ) {
+
+ $current_id = get_the_ID();
+ // Cascade up for lessons & quizzes.
+ if ( in_array( get_post_type( $current_id ), array( 'lesson', 'llms_quiz' ), true ) ) {
+ $course = llms_get_post_parent_course( $current_id );
+ $current_id = $course->get( 'id' );
+ }
+
+ // If the current id isn't a course or membership don't proceed.
+ if ( ! in_array( get_post_type( $current_id ), array( 'course', 'llms_membership' ), true ) ) {
+ return $visibility;
+ }
+
+ // Get the enrollment status.
+ $enrollment_status = llms_is_user_enrolled( $uid, $current_id );
+ } elseif ( 'any' === $type ) { // Check if they're enrolled/not enrolled in anything.
+ $enrollment_status = $this->is_student_enrolled_in_one_thing( $uid );
+ }
+
+ if ( 'llms_enrolled' === $status ) {
+ return $enrollment_status;
+ } elseif ( 'llms_not_enrolled' === $status ) {
+ return ( ! $enrollment_status );
+ }
+ } elseif ( 'specific' === $type ) { // Check if they're enrolled / not enrolled in the specific courses/memberships.
+
+ $match = $node->settings->llms_enrollment_match ? $node->settings->llms_enrollment_match : 'any';
+ $ids = $this->get_related_posts_from_settings( $node->settings );
+
+ if ( empty( $ids ) ) {
+ return true;
+ }
+
+ if ( 'llms_enrolled' === $status ) {
+
+ if ( ! $uid ) {
+ return false;
+ }
+
+ return llms_is_user_enrolled( $uid, $ids, $match );
+
+ } elseif ( 'llms_not_enrolled' === $status ) {
+
+ if ( ! $uid ) {
+ return true;
+ }
+
+ return ! llms_is_user_enrolled( $uid, $ids, $match );
+
+ }
+ }
+ }
+
+ return $visible;
+ }
+
+ /**
+ * Detemine if a student is enrolled in at least one course or membership.
+ *
+ * @since 1.3.0
+ *
+ * @param int $uid WP_User ID.
+ * @return boolean
+ */
+ private function is_student_enrolled_in_one_thing( $uid ) {
+
+ if ( ! $uid ) {
+ return false;
+ }
+
+ $student = llms_get_student( $uid );
+ if ( ! $student->exists() ) {
+ return false;
+ }
+
+ $courses = $student->get_courses(
+ array(
+ 'limit' => 1,
+ 'status' => 'enrolled',
+ )
+ );
+
+ if ( $courses['results'] ) {
+ return true;
+ }
+
+ $memberships = $student->get_membership_levels();
+ if ( $memberships ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Replace the BB filter after we've rendered our content.
+ *
+ * @since 1.3.1
+ *
+ * @return void
+ */
+ public function llms_pa_after_content() {
+ add_filter( 'the_content', 'FLBuilder::render_content' );
+ }
+
+ /**
+ * BB will replace PA Post content with course/membership pagebuilder content
+ * so remove the filter and replace when we're done with our output.
+ *
+ * @since 1.3.1
+ *
+ * @return void
+ */
+ public function llms_pa_before_content() {
+ remove_filter( 'the_content', 'FLBuilder::render_content' );
+ }
+
+ /**
+ * Loads LifterLMS modules.
+ *
+ * @since 1.3.0
+ *
+ * @return void
+ */
+ public function load_modules() {
+ if ( ! class_exists( 'FLBUilderModule' ) ) {
+ return;
+ }
+
+ if ( file_exists( LLMS_BB_MODULES_DIR ) ) {
+ foreach ( glob( LLMS_BB_MODULES_DIR . '**/*.php', GLOB_NOSORT ) as $file ) {
+ require_once $file;
+ }
+ }
+ }
+
+ /**
+ * Filter out LifterLMS modules if it's not the right post type.
+ *
+ * @param $enabled
+ * @param $instance
+ *
+ * @return bool
+ */
+ public function register_module( $enabled, $instance ) {
+ $post_type = get_post_type();
+
+ if ( 'course' !== $post_type && in_array( $instance->slug, array( 'class.llms.lab.course.instructors.module', 'class.llms.lab.course.syllabus.module', 'class.llms.lab.course.progress.bar.module' ) ) ) {
+ return false;
+ }
+
+ if ( 'llms_membership' !== $post_type && in_array( $instance->slug, array( 'class.llms.lab.membership.instructors.module' ) ) ) {
+ return false;
+ }
+
+ if ( 'lesson' !== $post_type && in_array( $instance->slug, array( 'class.llms.lab.lesson.mark.complete.module' ) ) ) {
+ return false;
+ }
+
+ return $enabled;
+ }
+
+
+ /**
+ * Load LifterLMS layout templates.
+ *
+ * @since 1.3.0
+ *
+ * @return void
+ */
+ public function load_templates() {
+
+ if ( ! class_exists( 'FLBuilderModel' ) ) {
+ return;
+ }
+
+ FLBuilderModel::register_templates( LLMS_PLUGIN_DIR . 'includes/beaver-builder/templates/course-template.dat' );
+ }
+
+ /**
+ * Modify LifterLMS metabox Fields to show the page builder is active.
+ *
+ * @since 1.3.0
+ * @since 1.5.2 Unknown.
+ * @since 1.7.0 Use strict comparison for `in_array`.
+ *
+ * @param array $fields Metabox fields.
+ * @return array
+ */
+ public function mod_metabox_fields( $fields ) {
+
+ global $post;
+
+ $post_types = array( 'course', 'lesson', 'llms_membership' );
+
+ if ( in_array( $post->post_type, $post_types, true ) && FLBuilderModel::is_builder_enabled() ) {
+
+ unset( $fields[0]['fields'][0]['value']['content'] );
+
+ }
+
+ return $fields;
+ }
+
+ /**
+ * Bypass restriction checks for courses and memberships when the builder is active.
+ *
+ * Allows the builder to use custom LifterLMS visibility settings when a student is not enrolled.
+ *
+ * @since 1.3.0
+ * @since 1.7.0 Use `in_array` with strict comparison.
+ *
+ * @param array $results Restriction results data.
+ * @param int $post_id Current post id.
+ * @return array
+ */
+ public function mod_page_restrictions( $results, $post_id ) {
+
+ if (
+ FLBuilderModel::is_builder_enabled() &&
+ $results['is_restricted'] &&
+ in_array( get_post_type( $post_id ), array( 'course', 'llms_membership' ), true )
+ ) {
+ $results['is_restricted'] = false;
+ $results['reason'] = 'bb-lab';
+ }
+
+ return $results;
+ }
+
+ /**
+ * Prevent page building of LifterLMS Post Types that can't actually be pagebuilt despite what the settings may assume.
+ *
+ * @since 1.5.0
+ * @since 1.7.0 Removed unset parameters for `llms_certificate` and `llms_my_certificate` from the filter.
+ *
+ * @param array $post_types Post type objects as an array.
+ * @return array
+ */
+ public function remove_uneditable_post_types( $post_types ) {
+
+ unset( $post_types['llms_quiz'] );
+ unset( $post_types['llms_question'] );
+
+ return $post_types;
+ }
+
+ /**
+ * Upgrade url.
+ *
+ * @since 1.3.0
+ *
+ * @param string $url Default upgrade url.
+ * @return string
+ */
+ public function upgrade_url( $url ) {
+ return 'https://www.wpbeaverbuilder.com/?fla=968';
+ }
+}
+
+return new LLMS_Beaver_Builder();
diff --git a/includes/class-llms-elementor-migrate.php b/includes/class-llms-elementor-migrate.php
index e1f007d960..1dcb1f7488 100644
--- a/includes/class-llms-elementor-migrate.php
+++ b/includes/class-llms-elementor-migrate.php
@@ -47,7 +47,7 @@ public function get_elementor_data_template() {
'elType' => 'widget',
'settings' => array(
'content_width' => 'full',
- 'html' => '' . esc_attr__( 'Course Information', 'lifterlms' ) . ' ',
+ 'html' => '' . esc_attr__( 'Course Information', 'lifterlms' ) . ' ',
),
'elements' => array(),
'widgetType' => 'html',
diff --git a/includes/class-llms-loader.php b/includes/class-llms-loader.php
index b31ea05a0d..bff12bc3e3 100644
--- a/includes/class-llms-loader.php
+++ b/includes/class-llms-loader.php
@@ -339,7 +339,6 @@ public function includes_admin() {
require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-admin-plugins.php';
require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-admin-review.php';
require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-admin-users-table.php';
- require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-mailhawk.php';
require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-sendwp.php';
require_once LLMS_PLUGIN_DIR . 'includes/forms/class-llms-forms-unsupported-versions.php';
require_once LLMS_PLUGIN_DIR . 'includes/admin/class-llms-admin-permalinks.php';
diff --git a/includes/class.llms.install.php b/includes/class.llms.install.php
index 12fe4fe8f8..55f8fb27d7 100644
--- a/includes/class.llms.install.php
+++ b/includes/class.llms.install.php
@@ -589,6 +589,8 @@ public static function install() {
LLMS_Forms::instance()->install();
+ LLMS_Beaver_Builder::instance()->install();
+
$version = get_option( 'lifterlms_current_version', null );
$db_version = get_option( 'lifterlms_db_version', $version );
diff --git a/includes/class.llms.person.handler.php b/includes/class.llms.person.handler.php
index 47efa3aa9d..05672881c8 100644
--- a/includes/class.llms.person.handler.php
+++ b/includes/class.llms.person.handler.php
@@ -166,12 +166,13 @@ public static function get_login_fields( $layout = 'columns' ) {
'type' => ! $usernames ? 'email' : 'text',
),
array(
- 'columns' => ( 'columns' == $layout ) ? 6 : 12,
- 'id' => 'llms_password',
- 'label' => __( 'Password', 'lifterlms' ),
- 'last_column' => ( 'columns' == $layout ) ? true : true,
- 'required' => true,
- 'type' => 'password',
+ 'columns' => ( 'columns' == $layout ) ? 6 : 12,
+ 'id' => 'llms_password',
+ 'label' => __( 'Password', 'lifterlms' ),
+ 'last_column' => ( 'columns' == $layout ) ? true : true,
+ 'required' => true,
+ 'type' => 'password',
+ 'visibility_toggle' => true,
),
array(
'columns' => ( 'columns' == $layout ) ? 3 : 12,
@@ -280,14 +281,15 @@ private static function get_password_fields() {
$fields = array();
$fields[] = array(
- 'columns' => 6,
- 'classes' => 'llms-password',
- 'id' => 'password',
- 'label' => __( 'Password', 'lifterlms' ),
- 'last_column' => false,
- 'match' => 'password_confirm',
- 'required' => true,
- 'type' => 'password',
+ 'columns' => 6,
+ 'classes' => 'llms-password',
+ 'id' => 'password',
+ 'label' => __( 'Password', 'lifterlms' ),
+ 'last_column' => false,
+ 'match' => 'password_confirm',
+ 'required' => true,
+ 'type' => 'password',
+ 'visibility_toggle' => true,
);
$fields[] = array(
'columns' => 6,
diff --git a/includes/class.llms.review.php b/includes/class.llms.review.php
index 0fdee38e48..59d3cf5b63 100644
--- a/includes/class.llms.review.php
+++ b/includes/class.llms.review.php
@@ -125,13 +125,13 @@ public static function output() {
foreach ( $posts_array as $post ) {
?>
-
ID ) ); ?>
-
+ ID ) ); ?>
+
ID ) ) ) );
?>
-
+
ID ) ); ?>
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/includes/class.llms.view.manager.php b/includes/class.llms.view.manager.php
index 2b5315e424..4fdbd450d5 100644
--- a/includes/class.llms.view.manager.php
+++ b/includes/class.llms.view.manager.php
@@ -159,7 +159,7 @@ private function get_menu_items_to_add() {
$nodes[] = array(
'id' => $top_id,
'parent' => 'top-secondary',
- 'title' => ' ' . $title,
+ 'title' => ' ' . $title,
);
// Add view as links.
diff --git a/includes/forms/class-llms-form-field.php b/includes/forms/class-llms-form-field.php
index bd62f48d60..b3896dbbaa 100644
--- a/includes/forms/class-llms-form-field.php
+++ b/includes/forms/class-llms-form-field.php
@@ -23,29 +23,30 @@ class LLMS_Form_Field {
* @var array {
* Array of field settings.
*
- * @type array $attributes Associative array of HTML attributes to add to the field element.
- * @type bool $checked Determines if radio and checkbox fields are checked.
- * @type int $columns Number of columns the field wrapper should occupy when rendered. Accepts integers >= 1 and <= 12.
- * @type string[]|string $classes Additional CSS classes to add to the field element. Accepts a string or an array of strings.
- * @type string $data_store Determines where to store field values. Accepts "users" or "usermeta" to store on the respective WP core tables.
- * @type string|false $data_store_key Determines the key name to use when storing the field value. Pass `false` to disable automatic storage. Defaults to the value of the `$name` property.
- * @type string $description A string to use as the field's description or helper text.
- * @type string $default The default value to use for the field.
- * @type bool $disabled Whether or not the field is enabled.
- * @type string $id The field's HTML "id" attribute. Must be unique. If not supplied, an ID is automatically generated.
- * @type string $label Text to use in the label element associated with the field.
- * @type bool $label_show_empty When true and no `$label` is supplied, will show an empty label element.
- * @type bool $last_column When true, outputs a clearfix element following the element's wrapper. Allows ending a "row" of fields.
- * @type bool $match Match this field to another field for validation purposes. Must be the `$id` of another field in the form.
- * @type string $name The field's HTML "name" attribute. Default's to the value of `$id` when not supplied.
- * @type array $options An associative array of options used for select, checkbox groups, and radio fields.
- * @type string $options_preset A string representing a pre-defined set of `$options`. Accepts "countries" or "states". Custom presets can be defined using the filter "llms_form_field_options_preset_{$preset_id}".
- * @type string $placeholder The field's HTML placeholder attribute.
- * @type bool $required Determines if the field is marked as required.
- * @type string $selected Alias of `$default`.
- * @type string $type Field type. Accepts any HTML5 input type (text, email, tel, etc...), radio, checkbox, select, textarea, button, reset, submit, and html.
- * @type string $value Value of the field.
- * @type string[]|string $wrapper_classes Additional CSS classes to add to the field's wrapper element. Accepts a string or an array of strings.
+ * @type array $attributes Associative array of HTML attributes to add to the field element.
+ * @type bool $checked Determines if radio and checkbox fields are checked.
+ * @type int $columns Number of columns the field wrapper should occupy when rendered. Accepts integers >= 1 and <= 12.
+ * @type string[]|string $classes Additional CSS classes to add to the field element. Accepts a string or an array of strings.
+ * @type string $data_store Determines where to store field values. Accepts "users" or "usermeta" to store on the respective WP core tables.
+ * @type string|false $data_store_key Determines the key name to use when storing the field value. Pass `false` to disable automatic storage. Defaults to the value of the `$name` property.
+ * @type string $description A string to use as the field's description or helper text.
+ * @type string $default The default value to use for the field.
+ * @type bool $disabled Whether or not the field is enabled.
+ * @type string $id The field's HTML "id" attribute. Must be unique. If not supplied, an ID is automatically generated.
+ * @type string $label Text to use in the label element associated with the field.
+ * @type bool $label_show_empty When true and no `$label` is supplied, will show an empty label element.
+ * @type bool $last_column When true, outputs a clearfix element following the element's wrapper. Allows ending a "row" of fields.
+ * @type bool $match Match this field to another field for validation purposes. Must be the `$id` of another field in the form.
+ * @type string $name The field's HTML "name" attribute. Default's to the value of `$id` when not supplied.
+ * @type array $options An associative array of options used for select, checkbox groups, and radio fields.
+ * @type string $options_preset A string representing a pre-defined set of `$options`. Accepts "countries" or "states". Custom presets can be defined using the filter "llms_form_field_options_preset_{$preset_id}".
+ * @type string $placeholder The field's HTML placeholder attribute.
+ * @type bool $required Determines if the field is marked as required.
+ * @type string $selected Alias of `$default`.
+ * @type string $type Field type. Accepts any HTML5 input type (text, email, tel, etc...), radio, checkbox, select, textarea, button, reset, submit, and html.
+ * @type string $value Value of the field.
+ * @type string $visibility_toggle Determines if the field should show a button to toggle field masking (for password fields).
+ * @type string[]|string $wrapper_classes Additional CSS classes to add to the field's wrapper element. Accepts a string or an array of strings.
* }
*/
protected $settings = array();
@@ -202,29 +203,30 @@ public function explode_options_to_fields( $is_hidden = false ) {
protected function get_defaults() {
return array(
- 'attributes' => array(),
- 'checked' => false,
- 'columns' => 12,
- 'classes' => array(), // Or string of space-separated classes.
- 'data_store' => 'usermeta', // Users or usermeta.
- 'data_store_key' => '', // Defaults to value passed for "name".
- 'description' => '',
- 'default' => '',
- 'disabled' => false,
- 'id' => '',
- 'label' => '',
- 'label_show_empty' => false,
- 'last_column' => true,
- 'match' => '', // Test.
- 'name' => '', // Defaults to value passed for "id".
- 'options' => array(),
- 'options_preset' => '',
- 'placeholder' => '',
- 'required' => false,
- 'selected' => '', // Alias of "default".
- 'type' => 'text',
- 'value' => '',
- 'wrapper_classes' => array(), // Or string of space-separated classes.
+ 'attributes' => array(),
+ 'checked' => false,
+ 'columns' => 12,
+ 'classes' => array(), // Or string of space-separated classes.
+ 'data_store' => 'usermeta', // Users or usermeta.
+ 'data_store_key' => '', // Defaults to value passed for "name".
+ 'description' => '',
+ 'default' => '',
+ 'disabled' => false,
+ 'id' => '',
+ 'label' => '',
+ 'label_show_empty' => false,
+ 'last_column' => true,
+ 'match' => '', // Test.
+ 'name' => '', // Defaults to value passed for "id".
+ 'options' => array(),
+ 'options_preset' => '',
+ 'placeholder' => '',
+ 'required' => false,
+ 'selected' => '', // Alias of "default".
+ 'type' => 'text',
+ 'value' => '',
+ 'visibility_toggle' => false,
+ 'wrapper_classes' => array(), // Or string of space-separated classes.
);
}
@@ -262,6 +264,18 @@ protected function get_description_html() {
return $this->settings['description'] ? sprintf( '%s ', $this->settings['description'] ) : '';
}
+ /**
+ * Retrieve HTML for the visibility toggle button
+ *
+ * @since TBD
+ *
+ * @return string
+ */
+ protected function get_visibility_toggle_html() {
+
+ return $this->settings['visibility_toggle'] ? ' ' . esc_html__( 'Show Password', 'lifterlms' ) . '
' : '';
+ }
+
/**
* Retrieve the full HTML for the field.
*
@@ -457,6 +471,8 @@ public function get_html() {
$after .= $this->get_description_html();
}
+ $after .= $this->get_visibility_toggle_html();
+
$after .= '';
if ( $this->settings['last_column'] ) {
diff --git a/includes/functions/llms-functions-l10n.php b/includes/functions/llms-functions-l10n.php
index e04a57d12e..9d39186d3f 100644
--- a/includes/functions/llms-functions-l10n.php
+++ b/includes/functions/llms-functions-l10n.php
@@ -33,7 +33,6 @@ function llms_get_locale( $domain = 'lifterlms' ) {
* @param string $domain The textdomain.
*/
return apply_filters( 'plugin_locale', $locale, $domain );
-
}
function llms_l10n_get_safe_directory() {
@@ -52,7 +51,6 @@ function llms_l10n_get_safe_directory() {
* @param string $path Full server path to the safe directory.
*/
return apply_filters( 'llms_l10n_safe_directory', WP_LANG_DIR . '/lifterlms' );
-
}
/**
@@ -91,8 +89,6 @@ function llms_load_textdomain( $domain, $plugin_dir = null, $language_dir = null
$plugin_dir = $plugin_dir ? $plugin_dir : LLMS_PLUGIN_DIR;
$language_dir = $language_dir ? $language_dir : 'languages';
- unload_textdomain( $domain );
-
/**
* Load from the custom LifterLMS "safe" directory (if it exists).
*
@@ -107,7 +103,6 @@ function llms_load_textdomain( $domain, $plugin_dir = null, $language_dir = null
* 2. wp-content/plugins/lifterlms/languages/lifterlms-en_US.mo
*/
load_plugin_textdomain( $domain, false, sprintf( '%1$s/%2$s', basename( $plugin_dir ), $language_dir ) );
-
}
/**
@@ -126,19 +121,19 @@ function llms_get_permalink_structure() {
// Remove false or empty entries so we can use the default values.
array_filter( $saved_permalinks ),
array(
- 'course_base' => _x( 'course', 'course url slug', 'lifterlms' ),
- 'courses_base' => _x( 'courses', 'course archive url slug', 'lifterlms' ),
- 'memberships_base' => _x( 'memberships', 'membership archive url slug', 'lifterlms' ),
- 'lesson_base' => _x( 'lesson', 'lesson url slug', 'lifterlms' ),
- 'quiz_base' => _x( 'quiz', 'quiz url slug', 'lifterlms' ),
+ 'course_base' => _x( 'course', 'course url slug', 'lifterlms' ),
+ 'courses_base' => _x( 'courses', 'course archive url slug', 'lifterlms' ),
+ 'memberships_base' => _x( 'memberships', 'membership archive url slug', 'lifterlms' ),
+ 'lesson_base' => _x( 'lesson', 'lesson url slug', 'lifterlms' ),
+ 'quiz_base' => _x( 'quiz', 'quiz url slug', 'lifterlms' ),
'certificate_template_base' => _x( 'certificate-template', 'slug', 'lifterlms' ),
- 'certificate_base' => _x( 'certificate', 'slug', 'lifterlms' ),
- 'course_category_base' => _x( 'course-category', 'slug', 'lifterlms' ),
- 'course_tag_base' => _x( 'course-tag', 'slug', 'lifterlms' ),
- 'course_track_base' => _x( 'course-track', 'slug', 'lifterlms' ),
- 'course_difficulty_base' => _x( 'course-difficulty', 'slug', 'lifterlms' ),
- 'membership_category_base' => _x( 'membership-category', 'slug', 'lifterlms' ),
- 'membership_tag_base' => _x( 'membership-tag', 'slug', 'lifterlms' ),
+ 'certificate_base' => _x( 'certificate', 'slug', 'lifterlms' ),
+ 'course_category_base' => _x( 'course-category', 'slug', 'lifterlms' ),
+ 'course_tag_base' => _x( 'course-tag', 'slug', 'lifterlms' ),
+ 'course_track_base' => _x( 'course-track', 'slug', 'lifterlms' ),
+ 'course_difficulty_base' => _x( 'course-difficulty', 'slug', 'lifterlms' ),
+ 'membership_category_base' => _x( 'membership-category', 'slug', 'lifterlms' ),
+ 'membership_tag_base' => _x( 'membership-tag', 'slug', 'lifterlms' ),
)
);
@@ -149,7 +144,7 @@ function llms_get_permalink_structure() {
}
return $permalinks;
-};
+}
/**
* Set the permalink structure and only allow keys we know about.
*
diff --git a/includes/functions/llms.functions.access.php b/includes/functions/llms.functions.access.php
index fe1381eb84..4d26ab2cae 100644
--- a/includes/functions/llms.functions.access.php
+++ b/includes/functions/llms.functions.access.php
@@ -138,16 +138,30 @@ function llms_page_restricted( $post_id, $user_id = null ) {
}
if ( 'lesson' === $post_type || 'llms_quiz' === $post_type ) {
-
$course_id = llms_is_post_restricted_by_time_period( $post_id, $user_id );
if ( $course_id ) {
+ if ( 'lesson' === $post_type ) {
+ $lesson = new LLMS_Lesson( $post_id );
+ }
+
+ // If the lesson is dripped based on enrollment, we don't want to restrict it based on course time period.
+ if ( 'lesson' !== $post_type || ( $lesson && 'enrollment' !== $lesson->get( 'drip_method' ) ) ) {
+ $results['is_restricted'] = true;
+ $results['reason'] = 'course_time_period';
+ $results['restriction_id'] = $course_id;
+ /* This filter is documented above. */
+ return apply_filters( 'llms_page_restricted', $results, $post_id );
+ }
+ }
+
+ $lesson_id = llms_is_post_restricted_by_drip_settings( $post_id, $user_id );
+ if ( $lesson_id ) {
$results['is_restricted'] = true;
- $results['reason'] = 'course_time_period';
- $results['restriction_id'] = $course_id;
+ $results['reason'] = 'lesson_drip';
+ $results['restriction_id'] = $lesson_id;
/* This filter is documented above. */
return apply_filters( 'llms_page_restricted', $results, $post_id );
-
}
$prereq_data = llms_is_post_restricted_by_prerequisite( $post_id, $user_id );
@@ -158,18 +172,6 @@ function llms_page_restricted( $post_id, $user_id = null ) {
$results['restriction_id'] = $prereq_data['id'];
/* This filter is documented above. */
return apply_filters( 'llms_page_restricted', $results, $post_id );
-
- }
-
- $lesson_id = llms_is_post_restricted_by_drip_settings( $post_id, $user_id );
- if ( $lesson_id ) {
-
- $results['is_restricted'] = true;
- $results['reason'] = 'lesson_drip';
- $results['restriction_id'] = $lesson_id;
- /* This filter is documented above. */
- return apply_filters( 'llms_page_restricted', $results, $post_id );
-
}
}
}
diff --git a/includes/functions/llms.functions.templates.loop.php b/includes/functions/llms.functions.templates.loop.php
index 699ee46463..b5c4272485 100644
--- a/includes/functions/llms.functions.templates.loop.php
+++ b/includes/functions/llms.functions.templates.loop.php
@@ -361,6 +361,14 @@ function lifterlms_template_loop_lesson_count() {
}
}
+if ( ! function_exists( 'lifterlms_template_loop_featured_pricing_information' ) ) {
+ function lifterlms_template_loop_featured_pricing_information() {
+ if ( in_array( get_post_type( get_the_ID() ), array( 'course', 'llms_membership' ) ) ) {
+ llms_get_template( 'loop/featured-pricing.php' );
+ }
+ }
+}
+
/**
* Show enrollment date meta
* used on Dashboard only
diff --git a/includes/functions/llms.functions.templates.privacy.php b/includes/functions/llms.functions.templates.privacy.php
index f559411319..3301634ed8 100644
--- a/includes/functions/llms.functions.templates.privacy.php
+++ b/includes/functions/llms.functions.templates.privacy.php
@@ -91,7 +91,7 @@ function llms_privacy_policy_form_field( $echo = true ) {
$ret = llms_form_field(
array(
'columns' => 12,
- 'value' => '' . $notice . ' ',
+ 'value' => '' . $notice . '
',
'last_column' => true,
'type' => 'html',
'id' => 'llms-privacy-policy',
diff --git a/includes/llms.template.functions.php b/includes/llms.template.functions.php
index 43f9978c02..411dbc2025 100644
--- a/includes/llms.template.functions.php
+++ b/includes/llms.template.functions.php
@@ -1183,3 +1183,18 @@ function llms_is_elementor_post( $post_id = false ) {
return $post_id && class_exists( 'Elementor\Plugin' ) && Elementor\Plugin::instance()->documents->get( $post_id )->is_built_with_elementor();
}
}
+
+
+/**
+ * Function to check if a post is built with Beaver Builder
+ *
+ * @since 8.0.0
+ */
+if ( ! function_exists( 'llms_is_beaver_builder_post' ) ) {
+ function llms_is_beaver_builder_post( $post_id = false ) {
+ if ( ! $post_id ) {
+ $post_id = get_the_ID();
+ }
+ return $post_id && class_exists( 'FLBuilderModel' ) && FLBuilderModel::is_builder_enabled( $post_id );
+ }
+}
diff --git a/includes/llms.template.hooks.php b/includes/llms.template.hooks.php
index 36af156703..ff9779b188 100644
--- a/includes/llms.template.hooks.php
+++ b/includes/llms.template.hooks.php
@@ -85,6 +85,7 @@
add_action( 'lifterlms_after_loop_item_title', 'lifterlms_template_loop_difficulty', 20 );
add_action( 'lifterlms_after_loop_item_title', 'lifterlms_template_loop_lesson_count', 22 );
+add_action( 'lifterlms_after_loop_item', 'lifterlms_template_loop_featured_pricing_information', 3 );
add_action( 'lifterlms_after_loop_item', 'lifterlms_loop_link_end', 5 );
/**
diff --git a/includes/models/model.llms.access.plan.php b/includes/models/model.llms.access.plan.php
index 8df1c68dde..b5a1701fe4 100644
--- a/includes/models/model.llms.access.plan.php
+++ b/includes/models/model.llms.access.plan.php
@@ -544,11 +544,13 @@ public function get_product_type() {
* Retrieve the text displayed on "Buy" buttons
* Uses optional user submitted text and falls back to LifterLMS defaults if none is supplied
*
+ * @param boolean $verbose If true, the text will be verbose and include the plan name for accessibility.
* @return string
* @since 3.0.0
- * @version 3.23.0
+ * @since 8.0.0 Added $verbose parameter.
+ * @version 8.0.0
*/
- public function get_enroll_text() {
+ public function get_enroll_text( $verbose = false ) {
// User custom text option.
$text = $this->get( 'enroll_text' );
@@ -568,7 +570,13 @@ public function get_enroll_text() {
}
}
- return apply_filters( 'llms_plan_get_enroll_text', $text, $this );
+ // Build the verbose enroll text, if requested.
+ if ( $verbose ) {
+ $plan_name = $this->get( 'title' );
+ $text = sprintf( _x( '%1$s: Select the %2$s plan.', 'Verbose enrollment text', 'lifterlms' ), $text, $plan_name );
+ }
+
+ return apply_filters( 'llms_plan_get_enroll_text', $text, $this, $verbose );
}
/**
diff --git a/includes/models/model.llms.course.php b/includes/models/model.llms.course.php
index 6ca974e4d0..5fcff98cb1 100644
--- a/includes/models/model.llms.course.php
+++ b/includes/models/model.llms.course.php
@@ -44,6 +44,7 @@
* @property int $prerequisite_track WP Tax ID of a the prerequisite track.
* @property string $start_date Date when a course is opens. Students may register before this date but can only view content and complete lessons or quizzes after this date..
* @property string $length User defined course length.
+ * @property string $featured_pricing User defined additional pricing information.
* @property int $sales_page_content_page_id WP Post ID of the WP page to redirect to when $sales_page_content_type is 'page'.
* @property string $sales_page_content_type Sales page behavior [none,content,page,url].
* @property string $sales_page_content_url Redirect URL for a sales page, when $sales_page_content_type is 'url'.
@@ -91,6 +92,7 @@ class LLMS_Course extends LLMS_Post_Model implements LLMS_Interface_Post_Instruc
'drip_method' => 'text',
'ignore_lessons' => 'absint',
'days_before_available' => 'absint',
+ 'featured_pricing' => 'html',
// Private.
'temp_calc_data' => 'array',
@@ -179,7 +181,7 @@ public function get_available_points() {
*
* @param string $type Optional. Type of prereq to retrieve id for [course|track]. Default is 'course'.
* @return int|false Post ID of a course, taxonomy ID of a track, or false if none found.
-. */
+. */
public function get_prerequisite_id( $type = 'course' ) {
if ( $this->has_prerequisite( $type ) ) {
@@ -202,7 +204,6 @@ public function get_prerequisite_id( $type = 'course' ) {
}
return false;
-
}
/**
@@ -239,7 +240,6 @@ public function get_difficulty( $field = 'name' ) {
$difficulties = wp_list_pluck( $terms, $field );
return implode( ', ', $difficulties );
-
}
/**
@@ -267,7 +267,6 @@ public function get_instructors( $exclude_hidden = false ) {
$this,
$exclude_hidden
);
-
}
/**
@@ -294,7 +293,6 @@ public function get_lessons( $return = 'lessons' ) {
$ret = array_map( 'llms_get_post', $lessons );
}
return $ret;
-
}
/**
@@ -325,7 +323,6 @@ public function get_lessons_count() {
);
return $query->post_count;
-
}
/**
@@ -345,7 +342,6 @@ public function get_quizzes() {
}
}
return $quizzes;
-
}
/**
@@ -387,7 +383,6 @@ public function get_sections( $return = 'sections' ) {
}
return $r;
-
}
/**
@@ -445,7 +440,6 @@ public function get_student_count( $skip_cache = false ) {
$count = apply_filters( 'llms_course_get_student_count', $count, $this );
return absint( $count );
-
}
/**
@@ -561,7 +555,6 @@ public function get_percent_complete( $user_id = '' ) {
return 0;
}
return $student->get_progress( $this->get( 'id' ), 'course' );
-
}
/**
@@ -599,7 +592,6 @@ public function has_date_passed( $date_key ) {
}
return $now > $date;
-
}
/**
@@ -625,7 +617,6 @@ public function has_capacity() {
// Compare results.
return ( $this->get_student_count() < $capacity );
-
}
/**
@@ -657,7 +648,6 @@ public function has_prerequisite( $type = 'any' ) {
}
return false;
-
}
/**
@@ -690,7 +680,6 @@ public function is_enrollment_open() {
* @param LLMS_Course $course Course object.
*/
return apply_filters( 'llms_is_course_enrollment_open', $is_open, $this );
-
}
/**
@@ -726,7 +715,6 @@ public function is_open() {
* @param LLMS_Course $course Course object.
*/
return apply_filters( 'llms_is_course_open', $is_open, $this );
-
}
/**
@@ -759,7 +747,6 @@ public function is_prerequisite_complete( $type = 'course', $student_id = null )
$student = new LLMS_Student( $student_id );
return $student->is_complete( $prereq_id, $type );
-
}
/**
@@ -772,7 +759,6 @@ public function is_prerequisite_complete( $type = 'course', $student_id = null )
public function set_instructors( $instructors = array() ) {
return $this->instructors()->set_instructors( $instructors );
-
}
/**
@@ -818,7 +804,5 @@ public function toArrayAfter( $arr ) {
$arr['difficulty'] = $this->get_difficulty();
return $arr;
-
}
-
}
diff --git a/includes/models/model.llms.membership.php b/includes/models/model.llms.membership.php
index 558877b37b..cb00cd41a9 100644
--- a/includes/models/model.llms.membership.php
+++ b/includes/models/model.llms.membership.php
@@ -29,6 +29,7 @@
* @property string $redirect_custom_url Arbitrary URL to redirect users to when $restriction_redirect_type is 'custom'.
* @property string $restriction_add_notice Whether or not to add an on screen message when content is restricted by this membership [yes|no].
* @property string $restriction_notice Notice to display when $restriction_add_notice is 'yes'.
+ * @property string $featured_pricing User defined additional pricing information.
* @property int $sales_page_content_page_id WP Post ID of the WP page to redirect to when $sales_page_content_type is 'page'.
* @property string $sales_page_content_type Sales page behavior [none,content,page,url].
* @property string $sales_page_content_url Redirect URL for a sales page, when $sales_page_content_type is 'url'.
@@ -50,6 +51,7 @@ class LLMS_Membership extends LLMS_Post_Model implements LLMS_Interface_Post_Ins
'restriction_notice' => 'html',
'restriction_redirect_type' => 'text',
'redirect_custom_url' => 'text',
+ 'featured_pricing' => 'html',
);
/**
@@ -103,7 +105,6 @@ public function add_auto_enroll_courses( $course_ids, $replace = false ) {
}
return $this->set( 'auto_enroll', array_unique( $course_ids ) );
-
}
/**
@@ -161,7 +162,6 @@ public function get_associated_posts( $post_type = null ) {
// Remove empty arrays and return the rest.
return array_filter( $posts );
-
}
/**
@@ -183,7 +183,7 @@ public function get_auto_enroll_courses() {
$courses = array_values(
array_filter(
$courses,
- function( $id ) {
+ function ( $id ) {
return 'publish' === get_post_status( $id );
}
)
@@ -228,7 +228,6 @@ public function get_instructors( $exclude_hidden = false ) {
$this,
$exclude_hidden
);
-
}
/**
@@ -260,7 +259,6 @@ public function get_student_count() {
);
return $query->get_found_results();
-
}
/**
@@ -327,7 +325,6 @@ protected function query_associated_courses() {
}
return array_unique( $courses );
-
}
/**
@@ -386,7 +383,6 @@ protected function query_associated_posts( $post_type, $enabled_key, $enabled_va
}
return $ids;
-
}
/**
@@ -412,7 +408,6 @@ public function remove_auto_enroll_course( $course_id ) {
public function set_instructors( $instructors = array() ) {
return $this->instructors()->set_instructors( $instructors );
-
}
/**
@@ -440,5 +435,4 @@ public function toArrayAfter( $arr ) {
return $arr;
}
-
}
diff --git a/includes/shortcodes/class.llms.shortcode.membership.instructors.php b/includes/shortcodes/class.llms.shortcode.membership.instructors.php
new file mode 100644
index 0000000000..a79476d785
--- /dev/null
+++ b/includes/shortcodes/class.llms.shortcode.membership.instructors.php
@@ -0,0 +1,64 @@
+get_output()
+ *
+ * @return array
+ * @since 8.0.0
+ */
+ protected function get_default_attributes() {
+ return array();
+ }
+}
+
+return LLMS_Shortcode_Membership_Instructors::instance();
diff --git a/includes/shortcodes/class.llms.shortcodes.php b/includes/shortcodes/class.llms.shortcodes.php
index be87d1f552..9e29732490 100644
--- a/includes/shortcodes/class.llms.shortcodes.php
+++ b/includes/shortcodes/class.llms.shortcodes.php
@@ -67,6 +67,7 @@ public static function init() {
'LLMS_Shortcode_Hide_Content',
'LLMS_Shortcode_Lesson_Mark_Complete',
'LLMS_Shortcode_Membership_Link',
+ 'LLMS_Shortcode_Membership_Instructors',
'LLMS_Shortcode_My_Achievements',
'LLMS_Shortcode_Registration',
'LLMS_Shortcode_User_Info',
@@ -238,7 +239,7 @@ public static function access_plan_button( $atts, $content = '' ) {
$text = empty( $content ) ? $plan->get_enroll_text() : $content;
- $ret = '' . $text . ' ';
+ $ret = '' . $text . ' ';
}
/**
diff --git a/lifterlms.php b/lifterlms.php
index d889b28513..d4593fc240 100644
--- a/lifterlms.php
+++ b/lifterlms.php
@@ -10,7 +10,7 @@
* Plugin Name: LifterLMS
* Plugin URI: https://lifterlms.com/
* Description: Complete e-learning platform to sell online courses, protect lessons, offer memberships, and quiz students. WP Learning Management System.
- * Version: 7.8.7
+ * Version: 8.0.0
* Author: LifterLMS
* Author URI: https://lifterlms.com/
* Text Domain: lifterlms
diff --git a/package-lock.json b/package-lock.json
index 4e2a3a87aa..471c3e0f9f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "lifterlms",
- "version": "7.8.7",
+ "version": "8.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "lifterlms",
- "version": "7.8.7",
+ "version": "8.0.0",
"license": "GPL-3.0",
"dependencies": {
"@babel/core": "^7.16.5",
diff --git a/package.json b/package.json
index a0f916c9c4..746bfea575 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "lifterlms",
- "version": "7.8.7",
+ "version": "8.0.0",
"description": "LifterLMS by codeBOX",
"repository": {
"type": "git",
diff --git a/packages/llms-e2e-test-utils/src/import-course.js b/packages/llms-e2e-test-utils/src/import-course.js
index b2455bc7fd..15297c526c 100644
--- a/packages/llms-e2e-test-utils/src/import-course.js
+++ b/packages/llms-e2e-test-utils/src/import-course.js
@@ -28,7 +28,7 @@ export async function importCourse(
await visitAdminPage( 'admin.php', 'page=llms-import' );
// Upload button
- await clickButton('Upload');
+ await clickButton( 'Upload' );
const inputSelector = 'input[name="llms_import"]';
await page.waitForSelector( inputSelector );
diff --git a/templates/course/favorite.php b/templates/course/favorite.php
index c69e4e5222..367b010483 100644
--- a/templates/course/favorite.php
+++ b/templates/course/favorite.php
@@ -50,11 +50,17 @@
-
+
+
+
+
-
+
+
+
+
diff --git a/templates/course/lesson-preview.php b/templates/course/lesson-preview.php
index 3aa2ca8c19..e00d34e270 100644
--- a/templates/course/lesson-preview.php
+++ b/templates/course/lesson-preview.php
@@ -19,70 +19,135 @@
defined( 'ABSPATH' ) || exit;
$restrictions = llms_page_restricted( $lesson->get( 'id' ), get_current_user_id() );
+$data_msg = $restrictions['is_restricted'] ? ' data-tooltip-msg="' . esc_html( strip_tags( llms_get_restriction_message( $restrictions ) ) ) . '"' : '';
+
+// Get the section name for this lesson.
+$section = $lesson->get_parent_section() ? llms_get_post( $lesson->get_parent_section() ) : false;
+$section_title = $section ? $section->post->post_title : '';
+if ( isset( $total_lessons ) && $total_lessons ) {
+ $lesson_screen_reader_msg = sprintf(
+ /* translators: 1: lesson order, 2: total lessons, 3: section title */
+ __( 'Lesson %1$d of %2$d within section %3$s.', 'lifterlms' ),
+ isset( $order ) ? $order : $lesson->get( 'order' ),
+ $total_lessons,
+ $section_title
+ );
+} else {
+ $lesson_screen_reader_msg = sprintf(
+ /* translators: 1: lesson order, 2: section title */
+ __( 'Lesson %1$d within section %2$s.', 'lifterlms' ),
+ isset( $order ) ? $order : $lesson->get( 'order' ),
+ $section_title
+ );
+}
?>