Aquí puedes aprender como se hace paso a paso un plugin similar al que se usa en la academia de www.boluda.com .
El plugin de academia online consiste en mostrar un grid de cursos, de un custom post type que se llama cursos y ademas un listado de las lecciones del mismo tanto en la pagina del curso como en las paginas de las lecciones. Este plugin para funcionar necesita tener instalado el tema Generate Press gratis con su Child Theme.
Creación de un widget de ayuda para el plugin.
El plugin muestra en la zona de escritorio de wordpress un widget con ayuda del funcionamiento y uso del plugin de academia.
El código necesario para crear el widget en la zona de escritorio de wordpress es el siguiente:
// Inicio Widget de Ayuda
function mi_plugin_agregar_widget() {
wp_add_dashboard_widget(
'mi_plugin_widget',
'Ayuda - Plugin Cursos GeneratePress',
'mi_plugin_mostrar_widget'
);
}
add_action( 'wp_dashboard_setup', 'mi_plugin_agregar_widget' );
function mi_plugin_mostrar_widget( $arg ) {
echo "<p>Este plugin funciona con un tema child de GeneratePress.</p>";
echo "<p>Tiene su propio CSS externo para hacer los ajuste que necesitemos.</p>";
echo "<p>El plugin crea dos roles con permisos de solo lectura, para propósito general 'alumnoinactivo', 'alumnoactivo'.</p>";
echo "<p>Por ejemplo los roles los puedes usar en los bloques de GutemBerg para permitir o no el acceso.</p>";
echo "<p>También los puedes usar en los plugins de pago, si ha pagado es un role y si no es otro.</p>";
echo "<p>El plugin crea un custom post type que se llama cursos, con el slug de curso y hierarchical.</p>";
echo "<p>Los Post de los cursos padres son los Cursos y los hijos son las Lecciones.</p>";
echo "<p>El plugin tiene un shortcode [grid_de_cursos] para mostrar un grid de los cursos en CSSGRID</p>";
echo "<p>El shortcode muestra una foto un titulo y una descripción que se obtienen del CPT cursos, desde el thumbnail, el titulo y el execrpt.</p>";
echo "<p>El Plugin añade un listado en el hook generate_after_content, del theme GeneratePress </p>";
echo "<p>El listado sale debajo del contenido, y muestra los cursos hijo ósea las lecciones.</p>";
echo "<p>El listado esta numerado, y los títulos de la lista son un h3.</p>";
echo "<p>Todas las características visibles están enfocadas hacer un seo correcto.</p>";
echo "<p>Añadimos a las columnas de cursos de la zona de admin la foto destacada y el ordenado por menu order.</p>";
echo "<p>Todo esta muy bien comentado para que puedas hacer mejoras y cambios.</p>";
echo "<p>Por ultimo tambien incluye esta ayuda en este Widget.</p>";
}
Creación del archivo CSS para el plugin de academia online gratis.
El plugin tiene un fichero de CSS externo, y para poder usar un fichero CSS externo dentro de un plugin usamos el código siguiente:
// Inicio añadimos un CSS externo para usar con este plugin.
function jl_lista_de_cursos_generatepress_css() {
wp_enqueue_style( '0-cursos-generatepress', plugins_url( '/0-lista-cursos-generatepress.css', __FILE__ ) );
}
add_action( 'wp_enqueue_scripts', 'jl_lista_de_cursos_generatepress_css' );
// Fin añadimos un CSS para este plugin
El fichero css del plugin de academia online gratis esta separado en tres partes, en una parte esta el código css del grid de los cursos, en otra el código del listado de los cursos dentro de un curso y en la ultima parte esta el código del listado de los curso dentro de una lección y el código completo del CSS es el siguiente:
/* Código CSS para el plugin Lista de Cursos hermanos */
:root {
--curso-color-titulo: #1177cc;
--curso-color-excerpt: #222222;
--curso-color-border-sombra: #cccccc;
--curso-color-border: #f7f7f7;/* girs claro */
--curso-color-background: #f7f7f7;/* girs claro */
--curso-color-background-hover: #ffffff;/* blanco */
--curso-color-background-visited: #ffffcc;/* amarillo claro */
--curso-color-background-active: #ccff99;/* verde claro */
}
/* Código CSS listado 1 */
.jl-listado-curso {
border-radius: 50px;
padding-top: 10px;
padding-right: 20px;
padding-left: 40px;
padding-bottom: 10px;
margin-top: 20px;
margin-bottom: 20px;
border-style: solid;
border-width: 1px;
background-color: var(--curso-color-background);
border-color: var(--curso-color-border);
}
.jl-listado-curso:hover {
background-color: var(--curso-color-background-hover);
border-color: var(--curso-color-border-sombra);
}
.jl-listado-curso:visited {
background-color: var(--curso-color-background-visited);
border-color: var(--curso-color-border);
}
.jl-listado-curso:active {
background-color: var(--curso-color-background-active);
border-color: var(--curso-color-border);
}
h3.jl-h3-listado-curso {
font-size: clamp(15px, calc(0.9375rem + ((1vw - 3.6px) * 0.3704)), 19px);
min-height: 0vw;
display: inline;
}
.jl-ol-listado-cursos, ol {
margin: unset;
}
/* Código CSS listado 2 */
.jl-listado-curso-hijo {
border-radius: 50px;
padding-top: 10px;
padding-right: 20px;
padding-left: 40px;
padding-bottom: 10px;
margin-top: 20px;
margin-bottom: 20px;
border-style: solid;
border-width: 1px;
background-color: var(--curso-color-background);
border-color: var(--curso-color-border);
}
.jl-listado-curso-hijo:hover {
background-color: var(--curso-color-background-hover);
border-color: var(--curso-color-border-sombra);
}
.jl-listado-curso-hijo:visited {
background-color: var(--curso-color-background-visited);
border-color: var(--curso-color-border);
}
.jl-listado-curso-hijo:active {
background-color: var(--curso-color-background-active);
border-color: var(--curso-color-border);
}
h3.jl-h3-listado-hijo {
font-size: clamp(15px, calc(0.9375rem + ((1vw - 3.6px) * 0.3704)), 19px);
min-height: 0vw;
display: inline;
}
.jl-ol-listado-cursos-hijos, ol {
margin: unset;
}
/* Código CSS para el plugin 0 Grid de Cursos */
.grid-de-shortcode-cursos {
$primary-color: #2B2B2B;
$secondary-color: #7F7F7F;
$tertiary-color: #F2F2F2;
$highlight-color: #FFB500;
--curso-color-titulo: #1177cc;
--curso-color-excerpt: #222222;
--curso-color-border-sombra: #b2b2be;
--curso-color-border: #ececec;
}
.grid-de-shortcode-cursos {
padding: 1em;
}
.grid-de-cursos {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
grid-gap: 20px;
}
/* Luego, podemos aplicar estilos adicionales a cada celda del grid, que es donde se mostrarán los cursos: */
.grid-de-cursos .curso {
border: 1px solid var(--curso-color-border);
border-radius: 5px;
overflow: hidden;
}
.grid-de-cursos .curso:hover {
box-shadow: 5px 5px 10px 0 var(--curso-color-border);
transform: translate3d(0px,-15px,0);
transition: all 0.2s ease 0.2s;
}
.grid-de-cursos .curso .thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
}
.grid-de-cursos .curso .titulocurso {
font-size: min(max(18px, calc(1.125rem + ((1vw - 5px) * 0.7042))), 28px);
min-height: 0vw;
font-weight: normal;
margin: 10px 0;
text-align: center;
padding: 5px;
color: var(--curso-color-titulo);
}
.caja-curso-submain, a {
text-decoration: none!important;
}
.grid-de-cursos .curso .excerpt {
font-size: font-size: min(max(14px, calc(0.875rem + ((1vw - 5px) * 0.7042))), 24px);
min-height: 0vw;
margin: 0;
padding: 10px;
color: var(--curso-color-excerpt);
}
Creación del custom post type de los cursos.
El contenido de los cursos y las lecciones lo guardamos en un custom post type que he llamado «cursos» y cuyo slug es «curso». El custom post type es idéntico a un post de WordPress pero le he añadido la opción de poderse usar de forma hierarchical para poder tener post padres e hijos o lo que es igual cursos y lecciones.
Para crear el custom post type de los cursos he creado el siguiente código:
// Inicio creamos el custom post type cursos
function jl_register_cursos_custom_post_type() {
$labels = array(
'name' => 'Cursos',
'singular_name' => 'Curso',
'add_new' => 'Añadir nuevo',
'add_new_item' => 'Añadir nuevo curso',
'edit_item' => 'Editar curso',
'new_item' => 'Nuevo curso',
'view_item' => 'Ver curso',
'view_items' => 'Ver cursos',
'search_items' => 'Buscar cursos',
'not_found' => 'No se han encontrado cursos',
'not_found_in_trash' => 'No se han encontrado cursos en la papelera',
'parent_item_colon' => 'Curso padre:',
'all_items' => 'Todos los cursos',
'archives' => 'Archivos de cursos',
'attributes' => 'Atributos de curso',
'insert_into_item' => 'Insertar en el curso',
'uploaded_to_this_item' => 'Subido a este curso',
'featured_image' => 'Imagen destacada del curso',
'set_featured_image' => 'Establecer imagen destacada del curso',
'remove_featured_image' => 'Eliminar imagen destacada del curso',
'use_featured_image' => 'Usar como imagen destacada del curso',
'menu_name' => 'Cursos',
'filter_items_list' => 'Filtrar lista de cursos',
'items_list_navigation' => 'Navegación de lista de cursos',
'items_list' => 'Lista de cursos',
'name_admin_bar' => 'Curso',
);
$args = array(
'labels' => $labels,
'description' => 'Tipo de publicación para los cursos del sitio',
'public' => true,
'hierarchical' => true,
'menu_order' => true,
'menu_position' => 5,
'menu_icon' => 'dashicons-welcome-learn-more',
'supports' => array(
'title',
'editor',
'author',
'thumbnail',
'excerpt',
'trackbacks',
'custom-fields',
'comments',
'revisions',
'page-attributes',
'post-formats'
),
'taxonomies' => array( 'category', 'post_tag' ),
'has_archive' => true,
'rewrite' => array(
'slug' => 'curso',
'with_front' => false
),
'show_in_rest' => true,
'rest_base' => 'cursos',
'rest_controller_class' => 'WP_REST_Posts_Controller'
);
register_post_type( 'cursos', $args );
}
add_action( 'init', 'jl_register_cursos_custom_post_type' );
// Fin creamos el custom post type cursos
Creación de un Grid para mostrar los cursos de la academia en una cuadricula
Para mostrar los cursos de la academia donde nosotros creamos he creado un shortcode que se llama [grid_de_cursos] , lo que hace el shortcode es mostrar un grid de los cursos usando CSS grid donde nosotros queramos.
El grid de los cursos se muestra de forma proporcional y automática en cualquier dispositivo y posición de pantalla.
Vista de los cursos en grid en una pagina de ancho completo
Vista de los cursos en grid en una pagina de ancho integrado
Vista de los cursos en grid en una pagina de ancho tipo tablet
Vista de los cursos en grid en una pagina de ancho tipo móvil
Cada curso se muestra dentro de una caja con una foto, el titulo del curso y la descripción del mismo, estos datos se extraen del custom post type cursos, del titulo, la imagen destacada y el excerpt.
El tamaño de la foto es automático y el alt de la imagen es el titulo del curso, el tamaño del titulo y la descripción del curso usa fuente dinámica variable que se adapta a cualquier tamaño de pantalla. Todos estos detalles los puedes varian modificando el CSS del plugin.
Lista de lecciones dentro de las paginas de los cursos.
Cuando estes viendo el contenido de un curso en la parte inferior del contenido muestro un listado con todas las lecciones del curso.
Lista de lecciones dentro de las paginas de los cursos.
Cuando estas viendo una lección tambien en la parte inferior puedes ver un listado con todas las lecciones del curso, para así obtener una experiencia agradable y tambien tener enlaces internos con navegación por cluster mejorando su puntuación SEO.
Explicación del listado de las lecciones
La lista de las lecciones esta numerada, el titulo de cada lección es un H3, el link de la lección es toda la caja del botón de la lección para mejorar su navegación pero el nombre de la url es el titulo de la lección sin el numero.
Los listados salen de forma automática en el hook con el nombre «generate_after_content» , este hook pertenece al tema Generate Press por eso es fundamental tener instalado un child tema de Generate Press para que funcione el plugin, aunque puedes cambiar el nombre del hook por uno de tu elección modificando el código que te dejo aquí abajo.
// Inicio de los listados de los cursos
// Inicio creamos dos los listados de los cursos, uno se muestra en los cursos padres y otro en los hijos
// Añadimos contenido después del contenido del post en el hook de GeneratePress generate_after_content
// Luego mostramos el contenido si es un post de cursos
// Creamos una funcion para añadir contenido el hook generate_after_content
function mi_plugin_after_content() {
// Ahora vamos a ver con un if, si es un curso padre y mostramos su listado.
// Muestra un mensaje después del contenido del post
if ( wp_get_post_parent_id( get_the_ID() ) === 0 ){
// if ( ( wp_get_post_parent_id( get_the_ID() ) === 0 ) && ( is_singular() && get_post_type() === 'cursos' ) ) {
// Si El post actual es un post padre
$post_id = get_the_ID(); // ID del post actual
// Obtener los post hijos del post padre actual
$hijos = get_children( array(
'post_parent' => $post_id,
'post_type' => 'cursos',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC'
) );
if ( !empty( $hijos ) ) {
echo "<!-- Esta es la lista de cursos hijos dentro de un curso padre -->";
echo "<!-- Si hay cursos hijos se muestra abajo -->";
$output .= '<div class="jl-listado-cursos-hijos">';
$output .= '<ol class="jl-ol-listado-cursos-hijos">';
foreach ( $hijos as $hijo ) {
$output .= '<a class="jl-a-listado-curso-hijo" rel="bookmark noopener noreferrer" href="' . get_permalink( $hijo->ID ) . '">';
$output .= '<div class="jl-listado-curso-hijo">';
$output .= '<li class="jl-li-listado-hijo">';
$output .= '<h3 class="jl-h3-listado-hijo">' . $hijo->post_title . '</h3>';
$output .= '</li>';
$output .= '</div>';
$output .= '</a>';
}
$output .= '</ol>';
$output .= '</div>';
} else {
echo "<!-- Si NO hay cursos hijos se muestra vacio -->";
$output = ''; // Si no hay post hijos, devolver una cadena vacía
}
// si es singular y cursos muestro la salida de la lista de cursos
if ( is_singular( 'cursos' ) ) {
echo '<div class="jl-listado-titulo-leccion"><h3>Lecciones</h3></div>';
echo $output; // muestro el listado
}
} else {
// Si El post actual no es un post padre es una leccion
$post_id = get_the_ID(); // ID del post actual
$post_parent = wp_get_post_parent_id( $post_id ); // ID del post padre
// Obtener los post hermanos del post hijo actual
$hermanos = get_posts( array(
'post_parent' => $post_parent,
'post_type' => 'cursos',
'posts_per_page' => -1,
// 'exclude' => $post_id, // Excluir el post hijo actual
'orderby' => 'menu_order',
'order' => 'ASC'
) );
if ( !empty( $hermanos ) ) {
echo "<!-- Esta es la lista de cursos hermanos dentro de un curso hijo -->";
echo "<!-- Si hay cursos hijo se muestra abajo -->";
$output .= '<div class="jl-listado-cursos">';
$output .= '<ol class="jl-ol-listado-cursos">';
foreach ( $hermanos as $hermano ) {
$output .= '<a class="jl-a-listado-curso" rel="bookmark noopener noreferrer" href="' . get_permalink( $hermano->ID ) . '">';
$output .= '<div class="jl-listado-curso">';
$output .= '<li class="jl-li-listado-curso">';
$output .= '<h3 class="jl-h3-listado-curso">' . $hermano->post_title . '</h3>';
$output .= '</li>';
$output .= '</div>';
$output .= '</a>';
}
$output .= '</ol>';
$output .= '</div>';
} else {
echo "<!-- Si NO hay cursos hijos se muestra nada -->";
$output = ''; // Si no hay post hijos, devolver una cadena vacía
}
// si es singular y cursos muestro la salida de la lista de cursos
if ( is_singular( 'cursos' ) ) {
echo '<div class="jl-listado-titulo-leccion"><h3>Lecciones</h3></div>';
echo $output; // muestro el listado
}
}
}
add_action( 'generate_after_content', 'mi_plugin_after_content' ); // generate_after_content es el hook donde lo mostramos
// Fin de los listados de los cursos
Explicación de como se ordenan los cursos y las lecciones del plugin
Para poder ordenar los cursos he añadido dos campos nuevos en la tabla del post type cursos de la zona de admin donde se muestra una foto del curso o lección y tambien el campo de menu orden, que ademas es ordenable desde la parte superior del menu.
Para hacer eso escribí el siguiente código:
// Inicio de mejora del listado de los cursos en la zona de admin
// Añadimos la foto destacada al listado de los cursos
function cpt_columns_head($defaults) {
$defaults['featured_image'] = 'Imagen Destacada';
return $defaults;
}
add_filter('manage_cursos_posts_columns', 'cpt_columns_head');
function cpt_columns_content($column_name, $post_ID) {
if ($column_name == 'featured_image') {
$post_featured_image = get_the_post_thumbnail($post_ID, array(120,9999)); // ancho máximo de 120px
if ($post_featured_image) {
echo $post_featured_image;
}
}
}
add_action('manage_cursos_posts_custom_column', 'cpt_columns_content', 10, 2);
// Añadimos el campo de orden por menu y damos capacidad para que se pueda ordenar por menu
function cpt_columns_head_menu($defaults) {
$defaults['menu_order'] = 'Menú Orden';
return $defaults;
}
add_filter('manage_cursos_posts_columns', 'cpt_columns_head_menu');
function cpt_sortable_columns_menu($columns) {
$columns['menu_order'] = 'menu_order';
return $columns;
}
add_filter('manage_edit-cursos_sortable_columns', 'cpt_sortable_columns_menu');
// Fin de mejora del listado de los cursos en la zona de admin
Puedes ordenar los cursos cambiando el numero del menu desde la zona de administrador donde pone orden.
Y tambien puedes cambiar el orden de las lecciones o cursos desde la edición del propio post type curso en los Atributos del curso.
El post que es padre o esta en la raíz sin los cursos, y el post que cuelga de un curso son las lecciones de ese propio curso, como puedes ver es super fácil de usar.
Creación de dos roles para los alumnos de la academia
Aunque para el propio plugin no es necesario roles, he creado dos roles nuevos uno se llama «alumnoactivo» y el otro «alumnoinactivo», estos dos roles los puedes usar para identificar con tu plugin de pago que alumnos o de restricción los alumnos que tienen acceso o no al contenido según el tipo de rol.
Puedes usar el rol para permitir el acceso al contenido de un bloque, ósea puedes dar permiso de lectura de un bloque solo a los usuarios con determinado role, y si no lo tienen pues no, esa es la forma mas simple y poderosa de controlar el contenido publico y privado.
El código que hice para hacer los dos roles es el siguiente:
// Inicio de la creacion de los roles
// Evitar que se eliminen los roles al desactivar el plugin
register_deactivation_hook( __FILE__, 'wpa_remove_roles' );
// Si se elimina el plugin, eliminar los roles "alumnoinactivo" y "alumnoactivo" de los usuarios y luego eliminar los roles de la base de datos
register_uninstall_hook( __FILE__, 'wpa_uninstall' );
// Crear los roles de usuario "alumnoinactivo" y "alumnoactivo"
add_action( 'init', 'wpa_add_roles' );
function wpa_add_roles() {
// Crear el rol de usuario "alumnoinactivo"
add_role( 'alumnoinactivo', 'Alumno Inactivo', array(
'read' => true,
) );
// Crear el rol de usuario "alumnoactivo"
add_role( 'alumnoactivo', 'Alumno Activo', array(
'read' => true,
'edit_posts' => false,
) );
}
// Evitar que se eliminen los roles al desactivar el plugin
function wpa_remove_roles() {
// Obtener todos los usuarios que tienen el rol "alumnoinactivo" o "alumnoactivo"
$users = get_users( array(
'role__in' => array( 'alumnoinactivo', 'alumnoactivo' )
) );
// Recorrer todos los usuarios y eliminarles los roles "alumnoinactivo" y "alumnoactivo"
foreach ( $users as $user ) {
$user->remove_role( 'alumnoinactivo' );
$user->remove_role( 'alumnoactivo' );
}
}
// Si se elimina el plugin, eliminar los roles "alumnoinactivo" y "alumnoactivo" de los usuarios y luego eliminar los roles de la base de datos
function wpa_uninstall() {
// Obtener todos los usuarios que tienen el rol "alumnoinactivo" o "alumnoactivo"
$users = get_users( array(
'role__in' => array( 'alumnoinactivo', 'alumnoactivo' )
) );
// Recorrer todos los usuarios y eliminarles los roles "alumnoinactivo" y "alumnoactivo"
foreach ( $users as $user ) {
$user->remove_role( 'alumnoinactivo' );
$user->remove_role( 'alumnoactivo' );
}
// Eliminar los roles de la base de datos
remove_role( 'alumnoinactivo' );
remove_role( 'alumnoactivo' );
}
// Fin de la creacion de los roles
Como mostrar contenido solo a los alumnos con el rol alumnoactivo
Para mostrar un contenido solo a los usuarios con el rol de alumnoactivo o a los usuarios con el role alumnoinactivo únicamente tienes que instalar un plugin como : Block Visibility — Conditional Visibility Control for the Block Editor .
Block Visibility añade a cada bloque de Gutemberg unos ajustes de permisos de lectura y en el caso de la version gratis del plugin permite mostrar o ocultar un bloque según el nombre del role.
Descargar el plugin de Academia OnLine Gratis
Bueno con esto ya tienes todo lo que necesitas para hacer gratis una academia como la de www.boluda.com. El plugin lo he creado poco a poco y esta muy bien documentado, seguramente no es perfecto, pero a mi me soluciona todos los problemas en las academias que hago.
Puedes copiar todos los codigo y usarlos como los necesites en tu propio proyecto y hacer el plugin paso a paso o descargar ya el Plugin de Academia OnLine Gratis, todo completo, solo tienes que bajarlo e instalarlo en tu web, recuerda siempre bajo tu responsabilidad, no no me hago cargo de los errores, fallos, daños o lo que sea que pueda ocasionarte el plugin.
Recuerda instalar el tema GeneratePress gratis y luego instalar y activar el Child tema de Generate Press.