The Problem
I need to modify and add functions and CSS classes to the site. However, I don’t want to touch the Astra theme files directly: for one, I don’t want to break anything without a fallback, and two, I want to avoid headaches whenever there is an update. Today’s particular problem is that the cart widget on the home page has white text on a white background whenever there isn’t anything in the cart.
Previous developers of the site had created an “Astra Child” theme that I can see in the WordPress Theme manager page, but whenever I selected the site reports a critical error. My guess is they left it there because they couldn’t figure out what the problem was:
sandbox@MyCompany:~/www/wp-content/themes/astra-child$ cat functions.php
<?php
/**
* Load Astra child template.
*/
function astra_child_enqueue_styles() {
// if ( is_multisite() ) {
// wp_enqueue_style( 'astra-child-style', get_stylesheet_uri() );
// } else {
// }
// wp_enqueue_style( 'astra-parent-style', get_template_directory_uri() . '/style.css' );
wp_enqueue_style( 'astra-parent-style', get_stylesheet_uri() );
}
add_action( 'wp_enqueue_scripts', 'astra_child_enqueue_styles', 11 );
/**
* Change forgot password url.
*
* @param string $lostpassword_url .
* @param string $redirect .
* @return string
*/
function change_forgot_url( $lostpassword_url, $redirect ) {
$lostpassword_url = 'https://staging4.website.com/login/?action=lostpassword';
return false;
}
// add_filter( 'lostpassword_url', 'change_forgot_url', 10, 2 );sandbox@MyCompany:~/www/wp-content/themes/astra-child$ cat style.css
/*
* Theme Name: astra Child
* Theme URI: https://example.com/twenty-fifteen-child/
* description: astra child theme
* Author: saffiretech
* Author URI: https://example.com
* Template: astra
* Version: 1.0.0
* License: GNU General Public License v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: astra-child
*/
/* Remove learndash default forgot password option */
div.ld-login-modal-login a.ld-forgot-password-link {
display: none !important;
}Experimentation
After simply trying to activate the theme and seeing the “critical error” I started to experiment. Is it something in the code that prevents child themes? Or is it something with this particular configuration?
The first step was to copy everything from the main Astra theme into the child theme folder. I checked that I could just select a different source directory and everything would continue to run. Sure enough, the site showed no problems.
The next step was to create the scaffolding for a true child theme, rather than just a copy. I figured that the simplest version of the child theme is one that is defined as a child theme, inherits everything from the parent theme, and modifies absolutely nothing:
/* functions.php */
<?php
/**
* Astra Test Theme functions and definitions
*
* @link https://developer.wordpress.org/themes/basics/theme-functions/
*
* @package Astra Test
* @since 1.0.0
*/
/**
* Define Constants
*/
define( 'CHILD_THEME_ASTRA_TEST_VERSION', '1.0.0' );
/**
* Enqueue styles
*/
function child_enqueue_styles() {
wp_enqueue_style( 'astra-test-theme-css', get_stylesheet_uri() . '/style.css', array('astra-theme-css'), CHILD_THEME_ASTRA_TEST_VERSION, 'all' );
}
add_action( 'wp_enqueue_scripts', 'child_enqueue_styles', 15 );/* style.css */
/**
Theme Name: Astra Test
Author: Adnan Valdes
Author URI: http://wpastra.com/about/
Description: A test Astra Child theme
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: astra-test
Template: astra
*/Now, this version did work, but adding a couple of custom functions to the functions.php file caused it to fail. The two functions below are being used a add a conditional button in Stackable, they worked just fine inside the parent theme functions.php file, as well as the full copy:
function is_course_expiring_soon( $course_id, $user_id ) {
$expiration_date = ld_course_access_expires_on( $course_id, $user_id );
if ( ! $expiration_date || $expiration_date <= 0 ) {
return false;
}
$current_date = time();
$days_remaining = ($expiration_date - $current_date) / DAY_IN_SECONDS;
return ( $days_remaining <= 10 && $days_remaining > 0 );
}
function is_course_expiring_soon_for_current_user() {
$user_id = get_current_user_id();
$post_id = get_the_ID();
$course_id = learndash_get_course_id( $post_id );
if ( ! $course_id ) {
return false;
}
return is_course_expiring_soon( $course_id, $user_id );It took some digging and (a significant amount of) frustration to figure this one out, but I suspect the problem is the use of ld_course_access_expires_on and learndash_get_course_id.
Those two functions are part of the LearnDash LMS plugin we use; my guess is that on the child theme the functions.php file is being called before the LearnDash plugin actually loads (the functions are used as part of the LearnDash course page to allow easy course extensions, but the page is built with Stackable).
The solution was to create a hook runs after the plugins are loaded before using the functions:
add_action( 'plugins_loaded', 'astra_test_setup_course_expiration_functions' );
function astra_test_setup_course_expiration_functions() {
if ( ! function_exists( 'ld_course_access_expires_on' ) || ! function_exists( 'learndash_get_course_id' ) ) {
return;
}
function is_course_expiring_soon( $course_id, $user_id ) {
$expiration_date = ld_course_access_expires_on( $course_id, $user_id );
if ( ! $expiration_date || $expiration_date <= 0 ) {
return false;
}
$current_date = time();
$days_remaining = ($expiration_date - $current_date) / DAY_IN_SECONDS;
return ( $days_remaining <= 10 && $days_remaining > 0 );
}
function is_course_expiring_soon_for_current_user() {
$user_id = get_current_user_id();
$post_id = get_the_ID();
$course_id = learndash_get_course_id( $post_id );
if ( ! $course_id ) {
return false;
}
return is_course_expiring_soon( $course_id, $user_id );
}
}
Here I wrapped my functions in a meta function that checks whether the functions exist. I also added an action that calls that meta function after all the plugins are loaded.
The Solution
In the end, this is what I used as a function.php and style.php file, which seems to work:
<?php
/**
* MyCompany Theme functions and definitions
*
* @package MyCompany
* @since 1.0.0
*/
/**
* Define Constants
*/
define( 'CHILD_THEME_MyCompany_VERSION', '1.0.0' );
/**
* Enqueue styles
*/
function child_enqueue_styles() {
wp_enqueue_style( 'astra-theme-css', get_template_directory_uri() . '/style.css' ); // Load parent theme styles
wp_enqueue_style( 'MyCompany-theme-css', get_stylesheet_uri(), array('astra-theme-css'), CHILD_THEME_MyCompany_VERSION, 'all' );
}
add_action( 'wp_enqueue_scripts', 'child_enqueue_styles', 15 );
add_action( 'plugins_loaded', 'astra_test_setup_course_expiration_functions' );
function astra_test_setup_course_expiration_functions() {
if ( ! function_exists( 'ld_course_access_expires_on' ) || ! function_exists( 'learndash_get_course_id' ) ) {
return;
}
// Now declare your functions here.
function is_course_expiring_soon( $course_id, $user_id ) {
$expiration_date = ld_course_access_expires_on( $course_id, $user_id );
if ( ! $expiration_date || $expiration_date <= 0 ) {
return false;
}
$current_date = time();
$days_remaining = ($expiration_date - $current_date) / DAY_IN_SECONDS;
return ( $days_remaining <= 10 && $days_remaining > 0 );
}
function is_course_expiring_soon_for_current_user() {
$user_id = get_current_user_id();
$post_id = get_the_ID();
$course_id = learndash_get_course_id( $post_id );
if ( ! $course_id ) {
return false;
}
return is_course_expiring_soon( $course_id, $user_id );
}
}/**
Theme Name: MyCompany
Author: Adnan Valdes
Author URI: http://theMyCompany.com
Description: A child theme of Astra; used for The Wise Pilot.
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: MyCompany
Template: astra
*/
.woocommerce-mini-cart__empty-message {
color:black;
}