added additional plugins

This commit is contained in:
Rodolfo Martinez 2025-12-12 19:05:48 -05:00
parent c85895d306
commit 00e60ec1b7
132 changed files with 27514 additions and 0 deletions

View file

@ -0,0 +1 @@
.DS_Store

View file

@ -0,0 +1,135 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software,
and (2) offer you this license which gives you legal permission to
copy, distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on,
we want its recipients to know that what they have is not the
original, so that any problems introduced by others will not reflect
on the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at
all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
[... full license continues unchanged until end ...]
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View file

@ -0,0 +1,7 @@
# learndash-start-button
This is a plugin to add simple Start buttons to improve the learndash experience for the end user. This adds shortcodes and Gutenberg blocks to make placement easy. This is intended for Wordpress Gutenberg/FSE sites.
Intended usage: Add a "Start First Course" to group pages, and add a "Start Course Now" to course pages.
Why I made this: because users opened their learndash pages and were lost. I literally had users sitting staring at a screen unsure as to how to proceed. I have requested this functionality from Learndash time and again, so I decided to make it myself.

View file

@ -0,0 +1,84 @@
/**
* LearnDash Start Course/Group Buttons - Block Editor Styles
*/
/* Block wrapper in editor */
.wp-block-ldsb-start-button {
padding: 10px;
border: 1px dashed #ddd;
background: #f9f9f9;
border-radius: 4px;
margin: 20px 0;
}
/* Prevent button interaction in editor */
.wp-block-ldsb-start-button .ldsb-start-button {
pointer-events: none;
}
/* Selected block styling */
.wp-block-ldsb-start-button.is-selected {
border-color: #007cba;
background: #f0f8ff;
}
/* Button preview in editor - import main styles */
.wp-block-ldsb-start-button .ldsb-button-wrapper {
margin: 10px 0;
display: block;
}
.wp-block-ldsb-start-button .ldsb-start-button {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 16px 32px;
background-color: #2d7a2d;
color: #f6f6f6;
text-decoration: none;
border-radius: 8px;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.5px;
box-shadow: 0 4px 12px rgba(45, 122, 45, 0.3);
border: none;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, sans-serif;
}
.wp-block-ldsb-start-button .ldsb-button-text {
font-size: 18px;
font-weight: 700;
text-transform: uppercase;
line-height: 1.2;
}
.wp-block-ldsb-start-button .ldsb-button-arrow {
width: 24px;
height: 24px;
flex-shrink: 0;
}
/* Block placeholder when no course selected */
.wp-block-ldsb-start-button.no-course::before {
content: "LearnDash Start Course Button: Configure course ID in block settings";
display: block;
color: #757575;
font-size: 13px;
margin-bottom: 10px;
font-style: italic;
}
/* Alignment in editor preview */
.wp-block-ldsb-start-button.align-left .ldsb-button-wrapper {
text-align: left;
}
.wp-block-ldsb-start-button.align-center .ldsb-button-wrapper {
text-align: center;
}
.wp-block-ldsb-start-button.align-right .ldsb-button-wrapper {
text-align: right;
}

View file

@ -0,0 +1,2 @@
<?php
// Silence is golden.

View file

@ -0,0 +1,173 @@
/**
* LearnDash Start Course/Group Buttons - Frontend Styles
*
* WCAG AAA Compliant Colors:
* Background: #2D7A2D (Brightest green that passes AAA with #f6f6f6)
* Text: #f6f6f6
* Contrast Ratio: 7:1
*/
.ldsb-button-wrapper {
margin: 20px 0;
display: block;
width: 100%;
}
.ldsb-start-button {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 16px 32px;
background-color: #2d7a2d; /* Brightest green that passes WCAG AAA with #f6f6f6 */
color: #f6f6f6;
text-decoration: none;
border-radius: 8px;
font-size: 18px;
font-weight: 700;
letter-spacing: 0.5px;
box-shadow: 0 4px 12px rgba(45, 122, 45, 0.3);
transition: all 0.3s ease;
border: none;
cursor: pointer;
font-family:
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
Cantarell, sans-serif;
}
.ldsb-start-button:hover {
background-color: #245f24;
color: #ffffff;
box-shadow: 0 6px 20px rgba(45, 122, 45, 0.4);
transform: translateY(-2px);
text-decoration: none;
}
.ldsb-start-button:active {
transform: translateY(0);
box-shadow: 0 2px 8px rgba(45, 122, 45, 0.3);
}
.ldsb-start-button:focus {
outline: 3px solid #5ea85e;
outline-offset: 2px;
}
.ldsb-button-text {
font-size: 18px;
font-weight: 700;
text-transform: uppercase;
line-height: 1.2;
}
.ldsb-button-arrow {
width: 24px;
height: 24px;
transition: transform 0.3s ease;
flex-shrink: 0;
}
.ldsb-start-button:hover .ldsb-button-arrow {
transform: translateX(4px);
}
/* Ensure proper visibility in different contexts */
.entry-content .ldsb-start-button,
.learndash-wrapper .ldsb-start-button {
display: inline-flex;
}
/* Alignment options - using both text-align and flex for better compatibility */
.ldsb-button-wrapper.align-left {
text-align: left;
}
.ldsb-button-wrapper.align-center {
text-align: center;
}
.ldsb-button-wrapper.align-center .ldsb-start-button {
margin-left: auto;
margin-right: auto;
}
.ldsb-button-wrapper.align-right {
text-align: right;
}
.ldsb-button-wrapper.align-right .ldsb-start-button {
margin-left: auto;
}
/* Full width option */
.ldsb-button-wrapper.align-full .ldsb-start-button,
.ldsb-button-wrapper.full-width .ldsb-start-button {
width: 100%;
justify-content: center;
}
/* Wide alignment */
.ldsb-button-wrapper.align-wide {
max-width: 100%;
width: 100%;
}
.ldsb-button-wrapper.align-wide .ldsb-start-button {
width: 100%;
max-width: 600px;
margin: 0 auto;
justify-content: center;
}
/* Mobile responsive styles */
@media (max-width: 480px) {
.ldsb-start-button {
padding: 14px 24px;
font-size: 16px;
width: 100%;
justify-content: center;
}
.ldsb-button-text {
font-size: 16px;
}
.ldsb-button-arrow {
width: 20px;
height: 20px;
}
}
/* Tablet styles */
@media (min-width: 481px) and (max-width: 768px) {
.ldsb-start-button {
padding: 15px 28px;
font-size: 17px;
}
.ldsb-button-text {
font-size: 17px;
}
}
/* High contrast mode support */
@media (prefers-contrast: high) {
.ldsb-start-button {
border: 2px solid currentColor;
}
}
/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
.ldsb-start-button,
.ldsb-button-arrow {
transition: none;
}
.ldsb-start-button:hover {
transform: none;
}
.ldsb-start-button:hover .ldsb-button-arrow {
transform: none;
}
}

View file

@ -0,0 +1,2 @@
<?php
// Silence is golden.

View file

@ -0,0 +1,566 @@
/**
* LearnDash Start Course Button - Gutenberg Blocks
*
* Registers custom Gutenberg blocks for Start Course and Start Group buttons
*/
(function (wp) {
"use strict";
// Destructure required WordPress packages
const { __ } = wp.i18n;
const { createElement: el } = wp.element;
const { registerBlockType } = wp.blocks;
const {
TextControl,
ToggleControl,
PanelBody,
PanelRow,
SelectControl,
Notice,
} = wp.components;
const { InspectorControls, BlockControls, AlignmentToolbar, useBlockProps } =
wp.blockEditor;
const { Fragment } = wp.element;
// SVG Arrow Icon
const arrowIcon = el(
"svg",
{
className: "ldsb-button-arrow",
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
xmlns: "http://www.w3.org/2000/svg",
},
el("path", {
d: "M5 12H19M19 12L12 5M19 12L12 19",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
}),
);
// Course block icon
const courseBlockIcon = el(
"svg",
{
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
},
el("path", {
d: "M13 7L18 12M18 12L13 17M18 12H6",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
}),
el("rect", {
x: "3",
y: "3",
width: "18",
height: "18",
rx: "2",
stroke: "currentColor",
strokeWidth: "2",
}),
);
// Group block icon
const groupBlockIcon = el(
"svg",
{
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
},
el("path", {
d: "M17 21V19C17 17.9391 16.5786 16.9217 15.8284 16.1716C15.0783 15.4214 14.0609 15 13 15H5C3.93913 15 2.92172 15.4214 2.17157 16.1716C1.42143 16.9217 1 17.9391 1 19V21",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
}),
el("circle", {
cx: "9",
cy: "7",
r: "4",
stroke: "currentColor",
strokeWidth: "2",
}),
el("path", {
d: "M23 21V19C22.9993 18.1137 22.7044 17.2528 22.1614 16.5523C21.6184 15.8519 20.8581 15.3516 20 15.13",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
}),
el("path", {
d: "M16 3.13C16.8604 3.35031 17.623 3.85071 18.1676 4.55232C18.7122 5.25392 19.0078 6.11683 19.0078 7.005C19.0078 7.89318 18.7122 8.75608 18.1676 9.45769C17.623 10.1593 16.8604 10.6597 16 10.88",
stroke: "currentColor",
strokeWidth: "2",
strokeLinecap: "round",
strokeLinejoin: "round",
}),
);
// Register the Course Start button block
registerBlockType("ldsb/start-button", {
title: __("LearnDash Start Course Button", "learndash-start-button"),
description: __(
"A prominent button to start LearnDash courses",
"learndash-start-button",
),
icon: courseBlockIcon,
category: "common",
keywords: [
__("learndash", "learndash-start-button"),
__("start", "learndash-start-button"),
__("course", "learndash-start-button"),
__("button", "learndash-start-button"),
],
attributes: {
courseId: {
type: "number",
default: 0,
},
buttonText: {
type: "string",
default: "Start Course",
},
newTab: {
type: "boolean",
default: false,
},
alignment: {
type: "string",
default: "left",
enum: ["left", "center", "right"],
},
},
supports: {
align: false, // We handle alignment internally
className: true,
html: false,
},
// Edit function - what you see in the editor
edit: function (props) {
const {
attributes: { courseId, buttonText, newTab, alignment },
setAttributes,
className,
} = props;
const blockProps = useBlockProps({
className: `align-${alignment} ${courseId === 0 ? "no-course" : ""}`,
});
// Update alignment
function onChangeAlignment(newAlignment) {
setAttributes({ alignment: newAlignment || "left" });
}
return el(
Fragment,
{},
// Block Controls (toolbar)
el(
BlockControls,
{},
el(AlignmentToolbar, {
value: alignment,
onChange: onChangeAlignment,
alignmentControls: [
{
icon: "editor-alignleft",
title: __("Align left", "learndash-start-button"),
align: "left",
},
{
icon: "editor-aligncenter",
title: __("Align center", "learndash-start-button"),
align: "center",
},
{
icon: "editor-alignright",
title: __("Align right", "learndash-start-button"),
align: "right",
},
],
}),
),
// Inspector Controls (sidebar)
el(
InspectorControls,
{},
el(
PanelBody,
{
title: __("Button Settings", "learndash-start-button"),
initialOpen: true,
},
el(
PanelRow,
{},
el(TextControl, {
label: __("Course ID", "learndash-start-button"),
help: __(
"Enter the LearnDash course ID. Leave empty to use current course.",
"learndash-start-button",
),
value: courseId || "",
onChange: function (value) {
var parsed = parseInt(value, 10);
setAttributes({ courseId: isNaN(parsed) || parsed < 0 ? 0 : parsed });
},
type: "number",
min: 0,
}),
),
el(
PanelRow,
{},
el(TextControl, {
label: __("Button Text", "learndash-start-button"),
help: __(
"The text to display on the button",
"learndash-start-button",
),
value: buttonText,
onChange: function (value) {
setAttributes({ buttonText: value || "Start Course" });
},
}),
),
el(
PanelRow,
{},
el(ToggleControl, {
label: __("Open in New Tab", "learndash-start-button"),
help: __(
"Open the course in a new browser tab",
"learndash-start-button",
),
checked: newTab,
onChange: function (value) {
setAttributes({ newTab: value });
},
}),
),
),
// Usage instructions panel
el(
PanelBody,
{
title: __("Usage Instructions", "learndash-start-button"),
initialOpen: false,
},
el(
"div",
{ className: "ldsb-help-text" },
el(
"p",
{},
__("This button will automatically:", "learndash-start-button"),
),
el(
"ul",
{},
el(
"li",
{},
__(
"• Link to the first lesson if available",
"learndash-start-button",
),
),
el(
"li",
{},
__(
'• Show "Login to Start" for logged-out users',
"learndash-start-button",
),
),
el(
"li",
{},
__(
'• Show "Enroll to Start" for non-enrolled users',
"learndash-start-button",
),
),
),
),
),
),
// Block Preview
el(
"div",
blockProps,
el(
"div",
{
className: `ldsb-button-wrapper align-${alignment}`,
style: { textAlign: alignment },
},
el(
"a",
{
className: "ldsb-start-button",
href: "#",
onClick: function (e) {
e.preventDefault();
},
},
el("span", { className: "ldsb-button-text" }, buttonText),
arrowIcon,
),
),
),
);
},
// Save function - null because this is a dynamic block
save: function () {
return null; // Dynamic block rendered by PHP
},
});
// Register the Group Start button block
registerBlockType("ldsb/start-group", {
title: __("LearnDash Start Group Work", "learndash-start-button"),
description: __(
"A button to start the first course in a LearnDash group",
"learndash-start-button",
),
icon: groupBlockIcon,
category: "common",
keywords: [
__("learndash", "learndash-start-button"),
__("group", "learndash-start-button"),
__("start", "learndash-start-button"),
__("button", "learndash-start-button"),
],
attributes: {
groupId: {
type: "number",
default: 0,
},
buttonText: {
type: "string",
default: "Start First Course",
},
newTab: {
type: "boolean",
default: false,
},
alignment: {
type: "string",
default: "left",
enum: ["left", "center", "right"],
},
},
supports: {
align: false,
className: true,
html: false,
},
// Edit function
edit: function (props) {
const {
attributes: { groupId, buttonText, newTab, alignment },
setAttributes,
className,
} = props;
const blockProps = useBlockProps({
className: `align-${alignment} ${groupId === 0 ? "no-group" : ""}`,
});
function onChangeAlignment(newAlignment) {
setAttributes({ alignment: newAlignment || "left" });
}
return el(
Fragment,
{},
// Block Controls
el(
BlockControls,
{},
el(AlignmentToolbar, {
value: alignment,
onChange: onChangeAlignment,
alignmentControls: [
{
icon: "editor-alignleft",
title: __("Align left", "learndash-start-button"),
align: "left",
},
{
icon: "editor-aligncenter",
title: __("Align center", "learndash-start-button"),
align: "center",
},
{
icon: "editor-alignright",
title: __("Align right", "learndash-start-button"),
align: "right",
},
],
}),
),
// Inspector Controls
el(
InspectorControls,
{},
el(
PanelBody,
{
title: __("Group Button Settings", "learndash-start-button"),
initialOpen: true,
},
el(
PanelRow,
{},
el(TextControl, {
label: __("Group ID", "learndash-start-button"),
help: __(
"Enter the LearnDash group ID. Leave empty to use current group.",
"learndash-start-button",
),
value: groupId || "",
onChange: function (value) {
var parsed = parseInt(value, 10);
setAttributes({ groupId: isNaN(parsed) || parsed < 0 ? 0 : parsed });
},
type: "number",
min: 0,
}),
),
el(
PanelRow,
{},
el(TextControl, {
label: __("Button Text", "learndash-start-button"),
help: __(
"The text to display on the button",
"learndash-start-button",
),
value: buttonText,
onChange: function (value) {
setAttributes({ buttonText: value || "Start First Course" });
},
}),
),
el(
PanelRow,
{},
el(ToggleControl, {
label: __("Open in New Tab", "learndash-start-button"),
help: __(
"Open the course in a new browser tab",
"learndash-start-button",
),
checked: newTab,
onChange: function (value) {
setAttributes({ newTab: value });
},
}),
),
),
el(
PanelBody,
{
title: __("How It Works", "learndash-start-button"),
initialOpen: false,
},
el(
"div",
{ className: "ldsb-help-text" },
el("p", {}, __("This button will:", "learndash-start-button")),
el(
"ul",
{},
el(
"li",
{},
__(
"• Link to the first course in the group",
"learndash-start-button",
),
),
el(
"li",
{},
__(
"• Go to the first lesson if available",
"learndash-start-button",
),
),
el(
"li",
{},
__(
'• Show "Login to Start" for logged-out users',
"learndash-start-button",
),
),
el(
"li",
{},
__(
'• Show "Join Group to Start" for non-members',
"learndash-start-button",
),
),
),
),
),
),
// Block Preview
el(
"div",
blockProps,
el(
"div",
{
className: `ldsb-button-wrapper align-${alignment}`,
style: { textAlign: alignment },
},
el(
"a",
{
className: "ldsb-start-button ldsb-group-button",
href: "#",
onClick: function (e) {
e.preventDefault();
},
},
el("span", { className: "ldsb-button-text" }, buttonText),
arrowIcon,
),
),
),
);
},
save: function () {
return null; // Dynamic block
},
});
})(window.wp);

View file

@ -0,0 +1,2 @@
<?php
// Silence is golden.

View file

@ -0,0 +1,2 @@
<?php
// Silence is golden.

View file

@ -0,0 +1,677 @@
<?php
/**
* Plugin Name: LearnDash Start Course Button
* Plugin URI: https://sspmedia.ca/wordpress/
* Description: Adds prominent "Start Course" and "Start Group Work" buttons for LearnDash with shortcode and Gutenberg block support
* Version: 1.0.0
* Author: SSP Media
* License: GPL v2 or later
* Text Domain: learndash-start-button
* Domain Path: /languages
* Requires at least: 5.0
* Requires PHP: 7.2
* WC requires at least: 7.0
* WC tested up to: 9.0
*
* @package LearnDashStartCourseButton
*/
// Prevent direct access - Wordfence compatible
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
// Define plugin constants
if (!defined('LDSB_VERSION')) {
define('LDSB_VERSION', '1.0.0');
}
if (!defined('LDSB_PLUGIN_URL')) {
define('LDSB_PLUGIN_URL', plugin_dir_url(__FILE__));
}
if (!defined('LDSB_PLUGIN_PATH')) {
define('LDSB_PLUGIN_PATH', plugin_dir_path(__FILE__));
}
if (!defined('LDSB_PLUGIN_BASENAME')) {
define('LDSB_PLUGIN_BASENAME', plugin_basename(__FILE__));
}
/**
* Check plugin dependencies
*
* @since 1.0.0
* @return bool True if dependencies are met, false otherwise
*/
function ldsb_check_dependencies() {
$deps_met = true;
$notices = array();
// Check for LearnDash
if (!defined('LEARNDASH_VERSION')) {
$deps_met = false;
$notices[] = __('LearnDash Start Course Button requires LearnDash LMS to be installed and activated.', 'learndash-start-button');
} elseif (version_compare(LEARNDASH_VERSION, '3.0', '<')) {
$deps_met = false;
$notices[] = __('LearnDash Start Course Button requires LearnDash version 3.0 or higher.', 'learndash-start-button');
}
// Display admin notices if dependencies not met
if (!$deps_met && is_admin() && !empty($notices)) {
add_action('admin_notices', function() use ($notices) {
foreach ($notices as $notice) {
echo '<div class="notice notice-error"><p>' . esc_html($notice) . '</p></div>';
}
});
return false;
}
return true;
}
add_action('plugins_loaded', 'ldsb_check_dependencies');
/**
* Declare WooCommerce HPOS compatibility
*
* @since 1.0.0
* @return void
*/
add_action('before_woocommerce_init', function() {
if (class_exists('\Automattic\WooCommerce\Utilities\FeaturesUtil')) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true);
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', __FILE__, true);
}
});
/**
* Enqueue plugin styles - Only on LearnDash pages
*
* @since 1.0.0
* @return void
*/
function ldsb_enqueue_styles() {
// Only load on singular pages or LearnDash archives
if (!is_singular() && !is_post_type_archive('sfwd-courses') && !is_post_type_archive('groups') && !is_tax('ld_course_category') && !is_tax('ld_course_tag') && !is_tax('ld_group_category') && !is_tax('ld_group_tag')) {
return;
}
// Check if current post is LearnDash content or has our shortcode
if (is_singular()) {
global $post;
// Validate post object exists
if (!$post || !is_object($post) || !isset($post->post_content)) {
return;
}
// Check post type
$ld_post_types = array('sfwd-courses', 'sfwd-lessons', 'sfwd-topic', 'sfwd-quiz', 'groups');
$is_ld_content = in_array(get_post_type(), $ld_post_types);
// Check for shortcodes
$has_course_shortcode = has_shortcode($post->post_content, 'learndash_start_button');
$has_group_shortcode = has_shortcode($post->post_content, 'learndash_start_group');
// Check for blocks
$has_course_block = has_block('ldsb/start-button', $post);
$has_group_block = has_block('ldsb/start-group', $post);
if (!$is_ld_content && !$has_course_shortcode && !$has_group_shortcode && !$has_course_block && !$has_group_block) {
return;
}
}
wp_enqueue_style(
'ldsb-button-styles',
LDSB_PLUGIN_URL . 'assets/css/style.css',
array(),
LDSB_VERSION,
'all'
);
}
add_action('wp_enqueue_scripts', 'ldsb_enqueue_styles');
/**
* Get first lesson URL with compatibility checks
*
* @since 1.0.0
* @param int $course_id The course ID
* @return string|false The first lesson URL or course URL, false on failure
*/
function ldsb_get_first_lesson_url($course_id) {
$course_url = get_permalink($course_id);
// If we can't get the course URL, return false
if (!$course_url) {
return false;
}
// Try new LearnDash 4.0+ method first
if (function_exists('learndash_course_get_children_of_step')) {
$lessons = learndash_course_get_children_of_step($course_id, $course_id, 'sfwd-lessons');
if (!empty($lessons)) {
$lesson_url = get_permalink($lessons[0]);
return $lesson_url ? $lesson_url : $course_url;
}
}
// Fallback to older method
if (function_exists('learndash_get_course_lessons_list')) {
$lessons = learndash_get_course_lessons_list($course_id);
if (!empty($lessons) && is_array($lessons)) {
$first_lesson = reset($lessons);
if (isset($first_lesson['post']->ID)) {
$lesson_url = get_permalink($first_lesson['post']->ID);
return $lesson_url ? $lesson_url : $course_url;
}
}
}
return $course_url;
}
/**
* Sanitize multiple HTML classes
*
* @since 1.0.0
* @param string $classes Space-separated list of class names
* @return string Sanitized space-separated list of class names
*/
function ldsb_sanitize_html_classes($classes) {
if (empty($classes)) {
return '';
}
// Split by spaces and sanitize each class individually
$classes_array = explode(' ', $classes);
$sanitized_classes = array();
foreach ($classes_array as $class) {
$class = trim($class);
if (!empty($class)) {
$sanitized = sanitize_html_class($class);
if (!empty($sanitized)) {
$sanitized_classes[] = $sanitized;
}
}
}
return implode(' ', $sanitized_classes);
}
/**
* Check user access with compatibility
*
* @since 1.0.0
* @param int $course_id The course ID to check access for
* @param int|null $user_id The user ID to check, defaults to current user
* @return bool True if user has access, false otherwise
*/
function ldsb_check_user_access($course_id, $user_id = null) {
if (null === $user_id) {
$user_id = get_current_user_id();
}
// Try newer method first (LearnDash 3.0+)
if (function_exists('sfwd_lms_has_access')) {
return sfwd_lms_has_access($course_id, $user_id);
}
// Fallback for older versions
if (function_exists('ld_course_check_user_access')) {
return ld_course_check_user_access($course_id, $user_id);
}
// Last resort - check if user is logged in
return is_user_logged_in();
}
/**
* Get first course URL from a group
*
* @since 1.0.0
* @param int $group_id The group ID
* @return string|false The first course/lesson URL or group URL, false on failure
*/
function ldsb_get_first_group_course_url($group_id) {
// Get group courses
$group_courses = array();
// Try LearnDash 3.2+ method
if (function_exists('learndash_group_enrolled_courses')) {
$group_courses = learndash_group_enrolled_courses($group_id);
} elseif (function_exists('learndash_get_group_enrolled_courses')) {
$group_courses = learndash_get_group_enrolled_courses($group_id);
} else {
// Fallback to direct meta query
$group_courses = get_post_meta($group_id, 'learndash_group_enrolled_courses', true);
if (!is_array($group_courses)) {
$group_courses = array();
}
}
// If we have courses, get the first one
if (!empty($group_courses)) {
$first_course_id = is_array($group_courses) ? reset($group_courses) : $group_courses;
// Check if it's a valid course
if (get_post_type($first_course_id) === 'sfwd-courses') {
// Try to get first lesson of this course
$course_url = ldsb_get_first_lesson_url($first_course_id);
if ($course_url) {
return $course_url;
}
}
}
// Fallback to group URL
$group_url = get_permalink($group_id);
return $group_url ? $group_url : false;
}
/**
* Register shortcode for the Start Group Work button
*
* @since 1.0.0
* @param array $atts Shortcode attributes
* @return string Button HTML or empty comment
*/
function ldsb_start_group_shortcode($atts) {
// Verify LearnDash is active
if (!defined('LEARNDASH_VERSION')) {
return '<!-- LearnDash not active -->';
}
// Parse and sanitize attributes
$atts = shortcode_atts(array(
'group_id' => '',
'text' => __('Start First Course', 'learndash-start-button'),
'new_tab' => 'no',
'class' => '',
'alignment' => ''
), $atts, 'learndash_start_group');
// Sanitize inputs
$group_id = !empty($atts['group_id']) ? absint($atts['group_id']) : get_the_ID();
$button_text = sanitize_text_field($atts['text']);
$new_tab = in_array($atts['new_tab'], array('yes', 'true', '1'), true);
$custom_class = ldsb_sanitize_html_classes($atts['class']);
$alignment = !empty($atts['alignment']) ? sanitize_key($atts['alignment']) : '';
// Validate group exists and is correct post type
$group_post = get_post($group_id);
if (!$group_post || get_post_type($group_post) !== 'groups') {
return '<!-- Invalid group ID or not a group page -->';
}
// Get the first course URL from the group
$course_url = ldsb_get_first_group_course_url($group_id);
// If we can't get a valid URL, return empty
if (!$course_url) {
return '<!-- Could not generate valid URL for group -->';
}
// Determine button text - SIMPLIFIED VERSION
$access_text = esc_html($button_text); // Default text for everyone
// ONLY override for logged-out users
if (!is_user_logged_in()) {
$access_text = __('Login to Start', 'learndash-start-button');
$course_url = wp_login_url($course_url);
}
// Admins and all logged-in users see the default button text
// No "Join Group to Start" text anywhere!
// Build wrapper classes
$wrapper_classes = array('ldsb-button-wrapper');
if ($alignment) {
$wrapper_classes[] = 'align-' . $alignment;
}
if ($custom_class) {
$wrapper_classes[] = $custom_class;
}
$wrapper_class_string = implode(' ', $wrapper_classes);
// Build link attributes
$link_attrs = array(
'href' => esc_url($course_url),
'class' => 'ldsb-start-button ldsb-group-button',
'role' => 'button',
'data-group-id' => $group_id
);
if ($new_tab) {
$link_attrs['target'] = '_blank';
$link_attrs['rel'] = 'noopener noreferrer';
}
// Build attributes string
$attrs_string = '';
foreach ($link_attrs as $key => $value) {
$attrs_string .= sprintf(' %s="%s"', $key, esc_attr($value));
}
// Build the button HTML
$button_html = sprintf(
'<div class="%s">
<a%s>
<span class="ldsb-button-text">%s</span>
<svg class="ldsb-button-arrow" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
</div>',
esc_attr($wrapper_class_string),
$attrs_string,
$access_text
);
// Allow filtering of output
return apply_filters('ldsb_group_button_html', $button_html, $atts);
}
add_shortcode('learndash_start_group', 'ldsb_start_group_shortcode');
/**
* Register shortcode for the Start Now button
*
* @since 1.0.0
* @param array $atts Shortcode attributes
* @return string Button HTML or empty comment
*/
function ldsb_start_button_shortcode($atts) {
// Verify LearnDash is active
if (!defined('LEARNDASH_VERSION')) {
return '<!-- LearnDash not active -->';
}
// Parse and sanitize attributes
$atts = shortcode_atts(array(
'course_id' => '',
'text' => __('Start Course', 'learndash-start-button'),
'new_tab' => 'no',
'class' => '',
'alignment' => ''
), $atts, 'learndash_start_button');
// Sanitize inputs
$course_id = !empty($atts['course_id']) ? absint($atts['course_id']) : get_the_ID();
$button_text = sanitize_text_field($atts['text']);
$new_tab = in_array($atts['new_tab'], array('yes', 'true', '1'), true);
$custom_class = ldsb_sanitize_html_classes($atts['class']);
$alignment = !empty($atts['alignment']) ? sanitize_key($atts['alignment']) : '';
// Validate course exists and get post type
$course_post = get_post($course_id);
if (!$course_post) {
return '<!-- Invalid course ID -->';
}
// Get proper course ID if we're on a lesson/topic
$course_post_type = get_post_type($course_post);
if ($course_post_type !== 'sfwd-courses') {
if (function_exists('learndash_get_course_id')) {
$course_id = learndash_get_course_id($course_id);
if (!$course_id) {
return '<!-- No associated course found -->';
}
}
}
// Get the course URL
$course_url = ldsb_get_first_lesson_url($course_id);
// If we can't get a valid URL, return empty
if (!$course_url) {
return '<!-- Could not generate valid URL for course -->';
}
// Check user access
$user_id = get_current_user_id();
$has_access = ldsb_check_user_access($course_id, $user_id);
$access_text = esc_html($button_text);
if (!$has_access && !is_user_logged_in()) {
$access_text = __('Login to Start', 'learndash-start-button');
$course_url = wp_login_url($course_url);
} elseif (!$has_access) {
$access_text = __('Enroll to Start', 'learndash-start-button');
}
// Build wrapper classes
$wrapper_classes = array('ldsb-button-wrapper');
if ($alignment) {
$wrapper_classes[] = 'align-' . $alignment;
}
if ($custom_class) {
$wrapper_classes[] = $custom_class;
}
$wrapper_class_string = implode(' ', $wrapper_classes);
// Build link attributes
$link_attrs = array(
'href' => esc_url($course_url),
'class' => 'ldsb-start-button',
'role' => 'button',
'data-course-id' => $course_id
);
if ($new_tab) {
$link_attrs['target'] = '_blank';
$link_attrs['rel'] = 'noopener noreferrer';
}
// Build attributes string
$attrs_string = '';
foreach ($link_attrs as $key => $value) {
$attrs_string .= sprintf(' %s="%s"', $key, esc_attr($value));
}
// Build the button HTML
$button_html = sprintf(
'<div class="%s">
<a%s>
<span class="ldsb-button-text">%s</span>
<svg class="ldsb-button-arrow" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M5 12H19M19 12L12 5M19 12L12 19" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</a>
</div>',
esc_attr($wrapper_class_string),
$attrs_string,
$access_text
);
// Allow filtering of output
return apply_filters('ldsb_button_html', $button_html, $atts);
}
add_shortcode('learndash_start_button', 'ldsb_start_button_shortcode');
/**
* Register Gutenberg blocks
*
* @since 1.0.0
* @return void
*/
function ldsb_register_blocks() {
// Check if Gutenberg is available
if (!function_exists('register_block_type')) {
return;
}
// Register block editor script
wp_register_script(
'ldsb-block-editor',
LDSB_PLUGIN_URL . 'assets/js/block.js',
array('wp-blocks', 'wp-element', 'wp-editor', 'wp-components', 'wp-data', 'wp-i18n'),
LDSB_VERSION,
true
);
// Add translation support
wp_set_script_translations('ldsb-block-editor', 'learndash-start-button');
// Register block editor styles
wp_register_style(
'ldsb-block-editor-style',
LDSB_PLUGIN_URL . 'assets/css/editor.css',
array('wp-edit-blocks'),
LDSB_VERSION
);
// Register the Course Start button block
register_block_type('ldsb/start-button', array(
'editor_script' => 'ldsb-block-editor',
'editor_style' => 'ldsb-block-editor-style',
'render_callback' => 'ldsb_render_block',
'attributes' => array(
'courseId' => array(
'type' => 'number',
'default' => 0
),
'buttonText' => array(
'type' => 'string',
'default' => __('Start Course', 'learndash-start-button')
),
'newTab' => array(
'type' => 'boolean',
'default' => false
),
'alignment' => array(
'type' => 'string',
'default' => 'left',
'enum' => array('left', 'center', 'right')
)
),
'supports' => array(
'align' => false, // We handle alignment internally
'className' => true,
'html' => false
)
));
// Register the Group Start button block
register_block_type('ldsb/start-group', array(
'editor_script' => 'ldsb-block-editor',
'editor_style' => 'ldsb-block-editor-style',
'render_callback' => 'ldsb_render_group_block',
'attributes' => array(
'groupId' => array(
'type' => 'number',
'default' => 0
),
'buttonText' => array(
'type' => 'string',
'default' => __('Start First Course', 'learndash-start-button')
),
'newTab' => array(
'type' => 'boolean',
'default' => false
),
'alignment' => array(
'type' => 'string',
'default' => 'left',
'enum' => array('left', 'center', 'right')
)
),
'supports' => array(
'align' => false,
'className' => true,
'html' => false
)
));
}
add_action('init', 'ldsb_register_blocks');
/**
* Render block callback
*
* @since 1.0.0
* @param array $attributes Block attributes
* @return string Block HTML output
*/
function ldsb_render_block($attributes) {
// Sanitize attributes
$course_id = isset($attributes['courseId']) ? absint($attributes['courseId']) : 0;
$button_text = isset($attributes['buttonText']) ? sanitize_text_field($attributes['buttonText']) : __('Start Course', 'learndash-start-button');
$new_tab = isset($attributes['newTab']) && $attributes['newTab'] ? 'yes' : 'no';
$alignment = isset($attributes['alignment']) ? sanitize_key($attributes['alignment']) : 'left';
// Pass alignment to shortcode
return ldsb_start_button_shortcode(array(
'course_id' => $course_id,
'text' => $button_text,
'new_tab' => $new_tab,
'alignment' => $alignment
));
}
/**
* Render group block callback
*
* @since 1.0.0
* @param array $attributes Block attributes
* @return string Block HTML output
*/
function ldsb_render_group_block($attributes) {
// Sanitize attributes
$group_id = isset($attributes['groupId']) ? absint($attributes['groupId']) : 0;
$button_text = isset($attributes['buttonText']) ? sanitize_text_field($attributes['buttonText']) : __('Start First Course', 'learndash-start-button');
$new_tab = isset($attributes['newTab']) && $attributes['newTab'] ? 'yes' : 'no';
$alignment = isset($attributes['alignment']) ? sanitize_key($attributes['alignment']) : 'left';
// Pass to group shortcode
return ldsb_start_group_shortcode(array(
'group_id' => $group_id,
'text' => $button_text,
'new_tab' => $new_tab,
'alignment' => $alignment
));
}
/**
* Plugin activation hook
*
* @since 1.0.0
* @return void
*/
function ldsb_activate() {
// Check minimum requirements
if (version_compare(PHP_VERSION, '7.2', '<')) {
deactivate_plugins(LDSB_PLUGIN_BASENAME);
wp_die(__('This plugin requires PHP 7.2 or higher.', 'learndash-start-button'));
}
// Set default options
add_option('ldsb_version', LDSB_VERSION);
// Note: flush_rewrite_rules() removed - this plugin doesn't register custom post types or rewrite rules
}
register_activation_hook(__FILE__, 'ldsb_activate');
/**
* Plugin deactivation hook
*
* @since 1.0.0
* @return void
*/
function ldsb_deactivate() {
// Note: No cleanup needed on deactivation
// flush_rewrite_rules() should NOT be called on deactivation per WordPress best practices
}
register_deactivation_hook(__FILE__, 'ldsb_deactivate');
/**
* Load plugin textdomain
*
* @since 1.0.0
* @return void
*/
function ldsb_load_textdomain() {
load_plugin_textdomain(
'learndash-start-button',
false,
dirname(LDSB_PLUGIN_BASENAME) . '/languages'
);
}
add_action('plugins_loaded', 'ldsb_load_textdomain');
// End of file - no closing PHP tag to prevent whitespace issues

View file

@ -0,0 +1,32 @@
<?php
/**
* LearnDash Start Now Button - Uninstall
*
* Handles plugin cleanup when deleted through WordPress admin
*
* @package LearnDashStartButton
* @since 1.0.0
*/
// If uninstall not called from WordPress, exit
if (!defined('WP_UNINSTALL_PLUGIN')) {
exit;
}
// Clean up options
delete_option('ldsb_version');
// Clean up any transients (if you add any in the future)
delete_transient('ldsb_courses_cache');
// Clean up user meta if any (currently none)
// delete_metadata('user', 0, 'ldsb_user_preference', '', true);
// Clean up post meta if any (currently none)
// delete_post_meta_by_key('_ldsb_button_override');
// Flush rewrite rules
flush_rewrite_rules();
// Note: We don't delete the block from posts as that could break content
// The blocks will gracefully degrade to showing nothing if plugin is removed