The difference between each is that the first one, gform_entries_field_value, intercepts the field values in the Entries list.
The second one, gform_entry_field_value, filters the field values in the Individual entry page.
Here is a sample snippet to protect the field value on the Entries list:
add_filter( 'gform_entries_field_value', 'protect_specific_field_from_entries_list', 10, 4 );functionprotect_specific_field_from_entries_list( $value, $form_id, $field_id, $entry ) {// Replace field_id with the ID of the field you want to hideif ( $field_id ==17 ) {return'PROTECTED'; }return $value;}
You can reference the field or fields by the field ID.
Then, on every match, return a custom value.
Here is a sample snippet to protect the field value on the individual entry post:
add_filter( 'gform_entry_field_value', 'protect_field_value_in_entry_detail', 10, 4 );functionprotect_field_value_in_entry_detail( $value, $field, $entry, $form ) {// Replace $field->id with the ID of the field you want to protect.if ( $field->id ==17 ) { $field->label ='PROTECTED';return'PROTECTED'; }return $value;}
The results should be as follows:
If you inspect the elements in the browser’s dev console, the rendered values correspond to your new custom values.
In Gravity Forms, you can add the File Upload field to allow users to upload documents when submitting your form.
It can be used for virtually any file type, from text documents to image files, etc.
Gravity Forms, like any form software, validates unfilled required fields. If a user tries to submit a form without completing the required fields, the form page will reload with an error.
If the user uploaded a file during that incorrect submission, the file will persist in the upload field through the page refresh with the error.
But, a percentage number will suffix the filename in the upload field.
Something like this:
If there were no problems with the upload during the form error evaluation, the percentage would be equal to 100%.
That means the file was completely uploaded with the upload field and is ready.
If you complete the form, the file will be submitted along with any other data entered by the user.
What if you want to remove that percentage from the upload field?
So, even if the form page is reloaded when errors are found, the percentage is not displayed.
This is possible with a filter called gform_file_upload_markup.
First, we must understand that the upload percentage is part of the File Upload field markup.
The upload percentage is an HTML element under a span tag with its own CSS class, gfield_fileupload_percent.
You could add a quick CSS snippet and hide the upload percentage with display: none, like so:
.gfield_fileupload_percent{display:none;}
And that works well. But it is only a cosmetic effect.
The percentage is still there in the DOM.
An alternative method is removing the upload percentage from the File Upload field markup before rendering it in the front end.
That’s where the gform_file_upload_markup filter comes into play.
The filter in question helps you intercept the File Upload field markup in Gravity Forms.
The challenge is that a long array of markup is associated with that filter.
If not handled carefully, you could mess up the File Upload field markup.
To solve this issue, you can leverage PHP’s string manipulation capabilities.
In this instance, you can check in the filter for the existence of the upload percentage field in the File Upload markup.
If it does not exist, abort further execution and return the File Upload field markup as is.
But if it is detected, then continue the execution: fiend the start and end of the span element with the CSS class gfield_fileupload_percent assigned to it.
Then, remove the selected element from the File Upload field markup.
Below is an example snippet that would achieve just that:
add_filter( 'gform_file_upload_markup', 'remove_gfield_fileupload_percent', 10, 4 );functionremove_gfield_fileupload_percent( $file_upload_markup, $file_info, $form_id, $field_id ) {// Check for the presence of 'gfield_fileupload_percent' in the markup. $percent_pos =strpos( $file_upload_markup, 'gfield_fileupload_percent' );if ( $percent_pos ===false ) {// If 'gfield_fileupload_percent' is not found, return the current markup as is.return $file_upload_markup; }// Find the start of the span containing 'gfield_fileupload_percent'. $start_pos =strrpos( substr( $file_upload_markup, 0, $percent_pos ), '<span' );// Find the end of the span. $end_pos =strpos( $file_upload_markup, '</span>', $percent_pos ) +7;// Remove the span containing 'gfield_fileupload_percent'. $file_upload_markup =substr_replace( $file_upload_markup, '', $start_pos, $end_pos - $start_pos );return $file_upload_markup;}
This code should go in your theme’s functions.php or your preferred code snippets plugin.
The code can be used as is without the need for modification.
The exception would be that you want to add any custom conditions, etc.
LearnDash, by default, has different enrollment types: Open, Free, Buy Now, Recurring, and Closed.
When you set a course access mode to Open, any user account registered on your website will be automatically enrolled in that course.
Non-logged-in visitors will simply see all the course content. Just LearnDash won’t track any progress as there is no user account to assign the course completion yet.
With the remaining access modes, your users must complete some action in order to enroll in the courses.
The advantage is that not everyone will be part of the course, allowing you to keep a cleaner record of the enrolled users.
The disadvantage is that it may create friction.
Going on a different route, let’s say your online academy is receiving deal offers from related brands that have the potential to have a significant and positive impact on your business.
And let’s say that part of the deal is to allow access to a set of courses ‘for free’ to employees of that brand.
But this set of courses is Buy Now or Closed, and there is no clear way to make a frictionless enrollment process for the specific group of users.
You could create a free group that allows automatic access to any user who enrolls in it for a specific set of courses.
The problem with that is that even as hard as you try, with the base LearnDash setup, any user on the website may run into the group and would have free access to the courses.
That wouldn’t be the ideal outcome.
You could also prevent access to the group with a membership plugin like Paid Memberships Pro, but again, you’d need to indicate what users are part of that membership.
Or you could even manually enroll the users in the group to control who has access to the group and who does not.
An alternative, automated, and frictionless method is to enroll the users in a course or group as they sign up to your website but filter by their email address domain if they are eligible for auto-enrollment.
Saying your deal is with Delta Airlines, whose domain is www.delta.com, check any email address from @delta.com for the account creation. If the email domain matches, then auto-enroll the newly created user in a specific group with ID 5.
The code to achieve that would go something like so:
functionadd_user_to_group_based_on_email($user_id){$user_info=get_userdata($user_id);$email=$user_info->user_email;// Get domain from the email$domain=array_pop(explode('@',$email));// Define your specific domain and group ID$specific_domain='example.com';// Change this to your specific domain$group_id=123;// Change this to your LearnDash group ID// Check if domain matchesif($domain===$specific_domain){// Use the ld_update_group_access function to add the user to the groupld_update_group_access($user_id,$group_id);}}add_action('user_register','add_user_to_group_based_on_email');
The first step is to hook into the user_register action.
That allows you to intercept all user registrations.
Get the user by ID and get the user’s email.
Use a combination of explode and array_pop first to make the email address an array separated by the @ symbol. Then only stay with the last element of the array, which is the actual domain name.
Indicate the domain you want to filter in a variable and the ID of the group you want to auto-enroll the user into.
Then, evaluate the domains. If there is a match, leverage the ld_update_group_access() function provided by LearnDash to update the access for the specific user.
That’s about it. Now over to you.
Let me know in the comments below if you have any questions or if you want to add something.
Depending on your business model, you may allow lifetime access to the course content after your students complete the course.
Maybe so they can revisit the lessons and boost their learning experience.
Or simply as a good will bonus, and allow them to see course updates in the long run.
On the other hand, you can also set an access expiration instead.
LearnDash provides a shortocde [ld_course_expire_status].
In short, it allows you to show your students their current access status. If the access has not expired yet, they’ll see the date until they have access to.
So far, so good.
But what if you want to show them a custom message like “You have 90 days left before your access expires”.
And each time they log into their course the number of days left is reduced accordingly until the expiration.
You’d need to create your own custom solution to show a custom expiration message like that.
So, the logic would work something like this:
Retrieve the access expiration timestamp.
Get the current time.
Calculate the difference between each timestamp in seconds.
Transform the resulting time difference to days.
Evalute if the returned difference is negative, then indicate the course has expired.
Build a default return value including the difference in days.
The code would be something as follows:
functionld_days_until_course_expiration($user_id,$course_id){// Retrieve the timestamp for when the course access will expire.$expires_on_timestamp=ld_course_access_expires_on($course_id,$user_id);// Return a message when the course does not have an expiration timestamp.if(empty($expires_on_timestamp)){return__('Course access does not expire.','learndash');}// Get the current time.$current_time=current_time('timestamp');// Calculate the difference in seconds.$time_difference=$expires_on_timestamp-$current_time;// Convert the time difference to days.$days_left=floor($time_difference/(24*60*60));// Indicate the course has expired if the difference is negative.if($days_left<=0){return__('Course access has expired.','learndash');}// Default message$message=sprintf( _n('There is %d day left before course access expires.','There are %d days left before course access expires.',$days_left,'learndash'),$days_left);/** * Filters the course expiration message. * * @paramstring $message Default expiration message. * @paramint $days_left Number of days left. * @paramint $user_id User ID. * @paramint $course_id Course ID. */returnapply_filters('ld_course_expiration_message',$message,$days_left,$user_id,$course_id);}
You can see in the return statement the returned value is wrapped in the apply_filters() WordPress function.
That allows you to filter the message later.
Let’s say you want to display a custom message when the user’s access is to expire in 10 days.
Maybe to create a scarcity sense and let them know they should renew their subscription.
You can do something like this:
functioncustom_ld_course_expiration_message($message,$days_left,$user_id,$course_id){if($days_left==10){return__('Only 10 days left! Consider renewing or upgrading your course.','learndash');}return$message;}add_filter('ld_course_expiration_message','custom_ld_course_expiration_message',10,4);
The most basic usage, whatever you choice is, is simply echoing the ld_days_until_course_expiration() function like this:
add_action('wp_head',function(){echo ld_days_until_course_expiration('2','7');// Display the access expiration for a user with ID 2 enrolled in a course with ID 7.});
Keep in mind the ld_days_until_course_expiration() function requires the user ID and the course ID you are checking the access expiration status for.
To make it happen dynamically, you can do the following:
Use the get_current_user_id() WordPress function to retrieve the current user ID.
Build a wrapper around get_the_ID(), to get the current post ID if it is a course.
That’s it. Now over to you.
Let me know in the comments if you have any ideas to add, or make any questions if something is not clear.
After a customer completes a purchase in your WooCommerce store, they get redirected to the Order Received page.
This Order Received page contains information about the purchase, like the Order Number, the customer’s full name, email address, and payment method used, and there is also a table featuring the list of purchased items called Order Details.
Each item in the Order Details table usually has the Product name, Amount, Price, and, at the very end, a calculation of the order Subtotal, Shipping, Taxes (if any), and if you offered a discount, the discount for the total purchase.
Let’s say you are running a special deal to get sales rolling. You want to show your customers how much they are saving per item after the purchase.
Nice idea.
The problem is that WooCommerce does not have an option to display the discount per product.
So, how can you add one?
You can leverage the woocommerce_order_item_meta_end action.
In short, this action hook allows you to add custom content at the end of each order item.
The woocommerce_order_item_meta_end action allows you to use three parameters: item ID, item, and order.
First, you want to get the original item price, dividing the item subtotal by the item quantity the customer purchased.
Now, get the actual price paid per item, dividing the item total by the item quantity.
In WooCommerce, you may create Coupons that only apply to specific products. You may want to run the code only for products that qualify for the discount. Non-discounted products would not need to display a discounted price.
So, if the price the customer paid per a specific item is less than the original price of the same item, then show the discounted amount for that product.
An item will be excluded from the function when it fails this condition.
The resulting code looks like this:
add_action('woocommerce_order_item_meta_end','display_product_discount',10,3);functiondisplay_product_discount($item_id,$item,$order){// Get the original product price from the order item data$original_price=(float)$item->get_subtotal()/$item->get_quantity();// Get the actual price the customer paid for the product in the order$order_price_per_item=(float)$item->get_total()/$item->get_quantity();// Check if the product was discountedif($order_price_per_item<$original_price){// Calculate the discount per product$discount_per_product=$original_price-$order_price_per_item;// Display discount per product and discounted price with inline CSSecho'<div class="product-discount">';echo'Discount: '.wc_price($discount_per_product).'<br>';echo'<span style="color: green; font-weight: bold;">Price after discount: '.wc_price($order_price_per_item).'</span>';echo'</div>';}}
Focus mode is the way LearnDash allows you to provide your users with a distraction-free interface in your courses.
The Focus Mode was introduced in LearnDash 3.0, and it has been available in all subsequent versions ever since.
In your WordPress admin, go to LearnDash LMS > Settings, and under the General tab, inside the Design & Content Elements meta box, scroll down to the ‘Focus Mode’ option.
There, you can enable or disable the Focus Mode.
LearnDash Focus Mode is a global feature by design. When you turn it on, you are enabling it for all courses without exceptions.
That brings the question: How can you partially enable the Focus Mode?
It would be interesting if you could say what courses should have Focus Mode and what courses shouldn’t have it, right?
In LearnDash, there is no option to enable the Focus Mode for a specific course, though.
There is no way to enable the Focus Mode and then select what courses should be excluded from it either.
A couple of weeks ago, a member on the LearnDash LMS Tips & Tricks group on Facebook made the following question:
Is there a way to enable/disable Focus Mode on a course-by-course basis?
The answer was very clear: No, unless it involves custom development.
That question stuck with me, and I decided to take it up for a challenge.
The short answer is yes; you can enable or disable the LearnDash Focus Mode on a course-by-course basis.
So, it is possible. YEAH!
Let’s dive into how you can enable or disable the LearnDash Focus mode on a course by course basis.
You can enable or disable the Focus Mode globally from the admin, as discussed before.
Behind the scenes, the Focus Mode on/off toggle button in the LearnDash settings page is connected to a value in the wp_options table in the WordPress database.
There is an option called learndash_settings_theme_ld30 that holds as value a serialized array full of options as properties.
One of those values is focus_mode_enabled.
But it actually is not there by default. At least not when you have not enabled the Focus Mode yet.
You may ask, how the heck would you find out about focus_mode_enabled value in the first place?
Scan the LearnDash code base for focus_mode_enabled, and you’ll find a file called class-ld-settings-section-theme-ld30.php inside sfwd-lms/themes/ld30/includes, which contains the focus_mode_enabled option with a default value of zero.
Now, with that information, you can run the following experiment:
Retrieve the serialized option with the WordPress get_option() function.
Place all three snippets in the functions.php file of your active theme, save the changes, and load any page.
In the WordPress admin, go to LearnDash LMS > Settings, scroll down to the Focus Mode option, and you’ll see it is turned off.
Go back to your theme’s functions.php file and set the focus_mode_enabled property to ‘yes’, and save the changes.
$ld30_settings['focus_mode_enabled'] ='yes';
Reload the tab with the LearnDash settings, scroll down to the Focus Mode option again, and you’ll see this time it is enabled.
Brilliant. You have discovered a way to programmatically enable or disable the Focus Mode.
With that knowledge, you can create a custom button in the wpadminbar that switches the Focus Mode on and off without having to go all the way down to the LearnDash settings page.
But that’s it. You still don’t have a way to conditionally enable or disable the LearnDash Focus Mode for a specific course.
However, what you now know is that the Focus Mode feature relies entirely on the value of the focus_mode_enabled option in the database.
LearnDash has some logic in place that looks for the focus_mode_enabled option value.
If it is set to yes, display Focus Mode.
If it is set to an empty string, disable Focus Mode from the view.
You’d need to somehow change the focus_mode_enable option value to yes or an empty string when a user visits a course to either enable or disable the Focus Mode for that specific course.
But then, if you use the method above of changing the option value with update_option(), you would bring a ton of conflicts to your website as with every page load, the option would be turning on and off.
The solution you need is to make LearnDash think the Focus Mode is enabled while it is not.
But how, you ask?
In WordPress, you have actions and filters, globally called hooks. There are regular hooks, and there are also dynamic hooks.
With the option_{$option} hook, you can filter the current value of an option.
For the option holding the focus_mode_enabled property, you’d reference it as follows.
option_learndash_settings_theme_ld30
And then you’d use it like this:
add_filter('option_learndash_settings_theme_ld30','modify_learndash_option_value');functionmodify_learndash_option_value($value){$value['focus_mode_enabled']='yes';return$value;// Always return the value.}
The focus_mode_enabled option is now set to yes globally. Similar to your previous experiment above, go to the LearnDash settings page again, and you’ll see the Focus Mode toggled on.
But is the Focus Mode enabled?
To LearnDash, Focus Mode is enabled. But if you retrieve the serialized option again and do a var_dump on it, you’ll see that the focus_mode_enabled option is not really part of the array:
What you really are doing is intercepting the focus_mode_enabled option value before LearnDash logic ‘sees’ it.
Then, when LearnDash sees the value, it is set to whatever you set it to.
See that the value in the database remains intact.
This is useful in case you want to enable or disable the Focus Mode globally and then virtually enable or disable it on a course-by-course basis.
The next step is to define the condition that allows you to enable the focus mode on a specific course.
If you dive a little deeper into the dynamic filters, you’ll find another one called pre_option_{$option}.
You can use any of the two option_learndash_settings_theme_ld30 or pre_option_learndash_settings_theme_ld30.
The advantage of using pre_option_{$option} is that it is called before WordPress retrieves the option from the database. This prevents a database call and allows you to set whatever value you want for the option in a virtual sense.
The experiment examples below use the pre_option_{$option} dynamic filter.
The next advantage of using the pre_option_{$option} dynamic filter is that it loads early in the WordPress load cycle.
If you see from the details discussed previously, you need to modify the value before LearnDash even know about it.
You can prove that by building a function and hooking it into the template_redirect action.
It may be enough to play around with the post object and retrieve the post ID and the post type, then check if it belongs to a course, filter the focus_mode_enabled option.
By the time all that happens, LearnDash already retrieved the focus_mode_enabled option value from the WordPress database and is loading the course layout based on that value, either with Focus Mode enabled or disabled.
In other words, you are too late in the WordPress load cycle.
You need to stick with the pre_option_learndash_settings_theme_ld30 dynamic filter.
Returning to the condition to check for the specific course, you need to be aware that the Focus Mode in a course applies only to the steps inside that course.
When you enable Focus Mode in the LearnDash settings and go to the Single Course Page, you will only see the Focus Mode when you open any step on that course, either a Lesson, Topic, Quiz, Assignment, etc.
That means there is no straightforward way to say “Hey Focus Mode, please do not appear in this course”.
You’d need to check in the lesson if it belongs to the specific course you want to enable or disable the Focus Mode for, and then run your custom logic.
Thankfully, LearnDash provides a function called learndash_get_primary_course_for_step(), which does just that.
The function requires the step ID to be passed as a parameter. It then will return the ID of the course it belongs to.
You’d simply store the course ID in a variable, then evaluate the output of learndash_get_primary_course_for_step() with the course ID.
Now, you’d need a way to retrieve the current step ID to run the conditional.
That is an easy feat with get_the_ID().
The problem is that the pre_option_learndash_settings_theme_ld30 filter is ‘too early’ in the load cycle.
What this means is, on that stage of the load cycle, you cannot access the post object in WordPress as it has not loaded yet.
So, how can you use the good things of “both worlds” and put together the dynamic filter but use your custom condition to enable or disable Focus Mode?
You can extract information from the requested URL, like this
$request_uri=$_SERVER['REQUEST_URI'];
Then, you can get the post ID like this:
$post_id=url_to_postid($request_uri);
Now, you are good to make your condition. Let’s say you want to enable the Focus Mode for a course with ID 728. Your code would look something like this:
If you are curious like me, you may have realized that this gives you the power to enable or disable the Focus Mode on a single specific step only, let’s say, a specific lesson, topic, quiz, etc.
You’d change the condition to check if the current $post_id matches a specific ID.
You can additionally check if the current post’s post type is a lesson.
Remember to safely test this on a staging environment before committing changes to a live site.
I tested this on LearnDash 4.9.0.1, the latest version available.
How can you test it out too?
Go to demo.learndash.com, and spin up a new LearnDash Demo.
Then, go to LearnDash LMS > Courses and retrieve some IDs from courses and lessons.
With these IDs, go to Appearance > Theme Editor, locate the functions.php file of the active theme (Kadence), and start hacking around with the snippets on this article.
If you visit that link, you’ll get all posts created and published by my user account here on my website.
By default, WordPress gives you the author slug composed of the ‘author’ keyword followed by the account’s nicename. In most cases, this nicename can lead to a very easy guess of the actual username associated with the account, leading to potential security threats.
As a security measure, it is convenient to change this nicename to something else.
The most ‘straightforward’ method would be to change the nicename value directly in the database record for the user account.
However, in an ideal scenario, you should really not mess with the WordPress database. Even as a developer, you should keep the WordPress database intact as much as possible.
So, what other option do you have to customize the WordPress author slug?
You can leverage the power of hooks to mask the author’s nicename, hence changing the author slug with a custom name.
The main benefit of this approach is that it will protect the username from any potential attackers from knowing your account’s username.
The reasoning behind this is that attackers can use the account’s username in an attempt to crack your account’s password and gain admin access to your website.
So, how do you change the author slug in WordPress?
Let’s draw a few logical steps you can take advantage of to achieve the proposed outcome:
You need to indicate what user you will change the author slug for and the custom name you’ll replace the author slug with. You can store these in variables; please see the example below:
Use the author_link filter to change how WordPress links to the author’s page.
The author_link filter by default uses three parameters. For this example you only need to leverage two of those, $link and $author_id.
The first one gets the current author link value, while the other literally gets the author ID.
You also need to pass your $custom_slug and $specific_user to use the assigned values in the function’s logic.
Retrieve the user data with the get_userdata() function. This will bring the user object for a specific account.
Check if the user_login property matches the $specific_username, then build a ‘new link’ by replacing the selected account’s nicename with the $custom_slug.
To make the code work, wrap it around a function and hook that function into the init WordPress hook.
/** Snippet: Customize the WordPress author slug without a plugin.* Author: Obi Juan.* Author URI: https://obijuan.dev* Explanation: https://obijuan.dev/customize-the-wordpress-author-slug-without-a-plugin/.* Version: 1.0.0* Publish Date: September 28th, 2023.*/functioncustom_author_slug(){// Set your custom slug and specific username here.$custom_slug='custom-slug';$specific_username='specific_username';// Customizing the author link.add_filter('author_link',function($link,$author_id)use($custom_slug,$specific_username){$user_info=get_userdata($author_id);if ($user_info->user_login==$specific_username) {$link=str_replace($user_info->user_nicename,$custom_slug,$link);}return$link;},10,2);// Adding a rewrite rule for the custom slug.add_rewrite_rule('author/'.$custom_slug.'/?$','index.php?author_name='.$specific_username,'top' );// Ensure WordPress recognizes the new query variable.add_filter('query_vars',function($vars){$vars[] ='author_name';return$vars;});}add_action('init','custom_author_slug');
Add the final version of the code to your theme’s functions.php file.
You can add it to your favorite code snippet plugin alternatively.
The third option is to create a small utility plugin to hold your code.
NOTE: The author slug will not change until you flush your Permalinks. To flush the Permalinks, in the WordPress admin go to SETTINGS > PERMALINKS, scroll down the page, and click the blue ‘Save’ button.
Over to you.
Let me know how it goes in the comments, whether you encounter any issues, if it works flawlessly on the first try (hopefully), or if you have any questions.
Sometimes when starting a new project, there is no time to go to the remote Git server to create the repository first there then clone it to your local machine.
Instead, it may be more convenient to start with git init and connect to the remote repository later.
When creating the remote repository to upload your work, it is possible you add some defaults as LICENSE and README. If your local repository does not have these changes, trying to PUSH your branch will end up in an error, with some general suggestion that you need a method to reconcile your branches.
Something you can do is fetch, then rebase origin <> master.
All you need to do is:
gitfetch
gitrebaseorigin/master
After that then you’ll see the files that were created in your remote repository are now merged with the master branch in your local repository.
Sometimes you’ll end up with larger files that, when you try to run a simple find / replace operation, will error out or not start because there is insufficient memory.
A solution I found in Ubuntu with the sed command is as follows:
sed -i "s|search|replace|g" yourFile.txt
I’m using | as separator to support URLs. This is useful when replacing URLs in SQL files when migrating from one domain to another or fixing an SSL mixed content issue.