wordpress: loading javascript only where shortcode appears - php

have an interesting conundrum. I need to load about 8 javascript files and the same number of styles for my plugin. These are only needed where ever my shortcode is ran.
I've tried to load them with print_styles and print_scripts but they aren't rendering properly, plus to do so breaks xhtml validation. So at the moment they load on every page and due to the number of files needed its not feasible to leave it like this.
On another project I wrote a function into my plugin's index.php file that would take the current page, search it for my shortcode and if found only then would it print the scripts, but this is an ugly hack.
Has anybody got any suggestions or solutions?
any help would be appreciated,
regards,
Daithi

to answer my own question... I had it write the first time. You have to search each page to check that your shortcode is being used. This has to be done when page data is loaded and before page is displayed. To me it is complete overkill on the system, but unfortunately it is the way it is. I got this information from:
get_shortcode_regex
and
old nabble
So first:
add_action('template_redirect','wp_my_shortcode_head');
then:
function wp_my_shortcode_head(){
global $posts;
$pattern = get_shortcode_regex();
preg_match('/'.$pattern.'/s', $posts[0]->post_content, $matches);
if (is_array($matches) && $matches[2] == 'YOURSHORTCODE') {
//shortcode is being used
}
}
replace 'YOURSHORTCODE' with the name of your shortcode and add your wp_enqueue_scripts into where it says //shortcode is being used.

I read a solution in here: http://scribu.net/wordpress/conditional-script-loading-revisited.html
Basically if using wordpress 3.3 you can enqueue your scripts in your short code function.
function my_shortcode($atts){
wp_enqueue_script( 'my-script', plugins_url( 'plugin_name/js/script.js' ), array('jquery'), NULL, true);
// if you add a css it will be added to the footer
//wp_enqueue_style( 'my-css', plugins_url( 'plugin_name/css/style.css' ) );
//the rest of shortcode functionality
}

Loading Scripts and Styles Dynamically Per Page Using a Shortcode
Advantages
Does not search through all the posts everytime the shortcode is called.
Able to add styles as well as scripts dynamically only when shortcode is on the page.
Does not use regexes since they tend to be slower than strstr() or strpos(). If you need to pickup args then you should use the shortcode regex mentioned above.
Reduces file calls
Explanation of Code
Finds the shortcodes on page using the save_post hook only when the post is not a revision and matches the specified post_type.
Saves the found post ids as an array using add_option() with autoload set to yes unless the entry is already present. Then it will use update_option().
Uses hook wp_enqueue_scripts to call our add_scripts_and_styles() function.
That function then calls get_option() to retrieve our array of page ids. If the current $page_id is in the $option_id_array then it adds the scripts and styles.
Please note: I converted the code from OOP Namespaced classes so I may have missed something. Let me know in the comments if I did.
Code Example: Finding Shortcode Occurences
function find_shortcode_occurences($shortcode, $post_type = 'page')
{
$found_ids = array();
$args = array(
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => -1,
);
$query_result = new WP_Query($args);
foreach ($query_result->posts as $post) {
if (false !== strpos($post->post_content, $shortcode)) {
$found_ids[] = $post->ID;
}
}
return $found_ids;
}
function save_option_shortcode_post_id_array( $post_id )
{
if ( wp_is_post_revision( $post_id ) OR 'page' != get_post_type( $post_id )) {
return;
}
$option_name = 'yourprefix-yourshortcode';
$id_array = find_shortcode_occurences($option_name);
$autoload = 'yes';
if (false == add_option($option_name, $id_array, '', $autoload)) update_option($option_name, $id_array);
}
add_action('save_post', 'save_option_shortcode_id_array' );
Code Example: Shortcode Dynamically Include Scripts and Styles
function yourshortcode_add_scripts_and_styles() {
$page_id = get_the_ID();
$option_id_array = get_option('yourprefix-yourshortcode');
if (in_array($page_id, $option_id_array)) {
wp_enqueue_script( $handle, $src, $deps, $ver, $footer = true );
wp_enqueue_style( $handle, $src , $deps);
}
}
add_action('wp_enqueue_scripts', 'yourshortcode_add_scripts_and_styles');

Just read this tutorial over here: http://scribu.net/wordpress/optimal-script-loading.html
Seems to be the best way.
add_action('init', 'register_my_script');
add_action('wp_footer', 'print_my_script');
function register_my_script() {
wp_register_script('my-script', plugins_url('my-script.js', __FILE__), array('jquery'), '1.0', true);
}
function print_my_script() {
global $add_my_script;
if ( ! $add_my_script )
return;
wp_print_scripts('my-script');
}
In this case, the script will be enqueued only if the $add_my_script
global was set at some point during the rendering of the page.
add_shortcode('myshortcode', 'my_shortcode_handler');
function my_shortcode_handler($atts) {
global $add_my_script;
$add_my_script = true;
// actual shortcode handling here
}
So, the script will be added if [myshortcode ...] was found in any of
the posts on the current page.

Load Scripts and Styles if Post/Page has Short Code
The best solution is to load the files into the page header if, and only if, the current post or page has the short code inside its content. And that’s exactly what the following function does:
function flip_register_frontend_assets()
{
//register your scripts and styles here
wp_register_style('pp_font','plugin_styles.css', null, null, 'all');
global $post;
//check whether your content has shortcode
if(isset($post->post_content) && has_shortcode( $post->post_content, 'your-
shortcode')){
//Enqueue your scripts and styles here
wp_enqueue_style( 'pp_font');
}
}
Simply place this function inside of one of your plugin files and you’re good to go.
You will need to replace [your-shortcode] with the short code you want to search for, and you will also need to replace plugin_styles.css with your stylesheet name.

You can just use this code to check if the shortcode is implemented in page content or in sidebar widgets.
<?php
if ( shortcode_exists( 'gallery' ) ) {
// The [gallery] short code exists.
}
?>

I use WordPress Version 5.4 with OOP style of code i dont know if this affect why none of the above solutions didn't work for me so i come up with this solution:
public function enqueue_scripts() {
global $post;
//error_log( print_r( $post, true ) );
//error_log( print_r( $post->post_content, true ) );
//error_log( print_r( strpos($post->post_content, '[YOUR_SHORTCODE]'),true));
if ( is_a( $post, 'WP_Post' ) && strpos($post->post_content, '[YOUR_SHORTCODE]') )
{
wp_register_style('my-css', $_css_url);
wp_register_script('my-js', $_js_url);
}
}
Hope this help someone.

How many pages are these scripts going to be loaded on? Would it be feasible to maintain an array of pages, and only load the scripts/stylesheets when the current page is in the array?
Otherwise, without scanning the code there is no way to do this, as WP doesn't even know the shortcode exists until well into the page load.

BraedenP is right, I'm pretty sure there is no way to detect shortcode usage at the execution time of wp_enqueue_scripts / when the stylesheets load.
Is there any reason you must do this in 8 files? One would just be more efficient, then it may not be a problem to load it on every page.
You could consider a PHP stylesheet solution that only executes certain styles if needed. A css.php file may resemble:
<?php
header("content-type: text/css");
/* You can require the blog header to refer to WP variables and make queries */
//require '../../../wp-blog-header.php';
$css = '';
$css .= file_get_contents('style.css');
/* Consider using GET variables or querying a variable in the WP database to determine which stylesheets should be loaded. You could add an option to the backend that allows a stylesheet to be turned on or off. */
if($condition1 == TRUE) $css .= file_get_contents('condition1.css');
if($condition2 == TRUE) $css .= file_get_contents('condition2.css');
?>
Less scripts and less stylesheets means less http requests and a faster load time.

Related

Visual Composer not showing specific page styles

When I view regular page from visual composer, it works fine like this:
http://vrshealth.com/qc2
Margins, backgrounds, etc are all working.
I needed to make a custom post type "quality-check" and am using archive-quality-check.php to display this and the vc-custom-xxxx styles are not loading for some reason:
http://dev-vrshealth.pantheonsite.io/quality-check/
I did some research and the only thing I could find is that page-specific VC styles don't work with Ajax-loaded pages. But it is not loaded through ajax.
Here is the relevant code from archive-quality-check.php which displays if you haven't already chosen a product lot # to display:
<?php if ($_SERVER['REQUEST_METHOD'] != 'POST'): ?>
<div class="col-xs-12 col-md-12" id="page-content">
<?php
$post_id4098 = get_post(4098);
$content = $post_id4098->post_content;
$content = apply_filters('the_content', $content);
$content = str_replace(']]>', ']]>', $content);
WPBMap::addAllMappedShortcodes();
echo do_shortcode($content);
?>
</div>
I feel like I must be missing something here, like a function to output metadata or some type of custom css, but I can't find any documentation which explains how.
Had the same problem.
Just insert this before echoing content.
get_post_meta( $id, '_wpb_shortcodes_custom_css', true )
Worked for me on latest WP and VC versions.
The answer that Laurent gave worked great for me! However I would suggest creating a function for it in your functions.php file. Maybe something like this:
function vc_custom_css($id) {
$shortcodes_custom_css = get_post_meta( $id, '_wpb_shortcodes_custom_css', true );
if ( ! empty( $shortcodes_custom_css ) ) {
echo '<style type="text/css">';
echo $shortcodes_custom_css;
echo '</style>';
}
}
Then you can just use vc_custom_css($yourPostID); whenever it is required.
Hi I had the same issues, so I have searched in the pluggin, and finaly this works for me :
$vcM = Vc_Manager::getInstance();
$vc = $vcM->vc();
$vc->addShortcodesCustomCss($pop_up_id);
The issue with vc-custom-xxxx styles described in this topic happening to me quite frequently, with various WordPress Themes.
I have tried an approach with function from the second answer. However there is still the issue, when loading page preview via "Preview changes" button. The style' meta data does not updated unless you manually re-save the page before viewing the preview.
The most correct answer was given by Pierre Mar, but I'd like to put a small note on it. You need to replace $pop_up_id with your current post id.
The final function would look like:
function vc_echo_custom_css($post_id) {
$vcM = Vc_Manager::getInstance();
$vc = $vcM->vc();
$vc->addShortcodesCustomCss($post_id);
}
Include it in your Child's theme functions.php file, and then you can call it from any template like:
vc_echo_custom_css(4098);

Add login form shortcode programatically to every published product page

I am running a wholesale shop on Woocommerce. Login is required to see the prices. This is set up and working properly. Now I wish to add a logon form on every product page to only show to visitors (not logged on users).
I am using the WooCommerce Catalog Visibility plugin. This plugin offers the functionality I described above, but my theme is somehow messing it up. The plugin author says to talk to the theme developer and the theme developer says to talk to the plugin author. So now I am trying to find a workaround.
First issue: The plugin comes with a shortcode [woocommerce_logon_form] that will display a logon form. I don't want to manually add this to every existing product since I have thousands of products on my site. I am looking for a way to get it in through the code for the product page layout.
I found this code (to be added to the functions.php) to work well:
// adds notice at single product page above add to cart
add_action( 'woocommerce_single_product_summary', 'return_policy', 20 );
function return_policy() {
echo '<p id="rtrn">30-day return policy offered. See Terms and Conditions for details.</p>';
}
However, it will only show text. The short code won't work when added instead of the sample text.
Second issue: The short code shows the form even when the customer is already logged in.
I am currently using this nice code that shows or hides content depending on whether the user is logged in or not:
add_shortcode( 'access', 'access_check_shortcode' );
function access_check_shortcode( $attr, $content = null ) {
extract( shortcode_atts( array( 'capability' => 'read' ), $attr ) );
if ( current_user_can( $capability ) && !is_null( $content ) && !is_feed() )
return $content;
return '';
}
add_shortcode( 'visitor', 'visitor_check_shortcode' );
function visitor_check_shortcode( $atts, $content = null ) {
if ( ( !is_user_logged_in() && !is_null( $content ) ) || is_feed() )
return $content;
return '';
}
That shortcode works perfectly for text, but not with other shortcodes.
So the combination of these short codes: [visitor][woocommerce_logon_form][/visitor] will not show the logon form to visitors. Instead it will only show them this as text [woocommerce_logon_form].
Please help! I am sure this is probably easily fixed by someone with coding skills.
I appreciate your effort to answer to this question. Keep in mind that my understanding of code is very limited and it would be great if you can also point out in which file to add or modify code.
To make your shortcode working in php code or in php/html code you need to use a native WordPress function do_shortcode() … You can use it with your shortcode for example in your 1st function this way:
add_action( 'woocommerce_single_product_summary', 'return_policy', 20 );
function return_policy() {
echo do_shortcode('[woocommerce_logon_form]');
}
And this will work…
To see all the different hooks you can use instead of woocommerce_single_product_summary, please see this 2 templates code to chose in case a more convenient hook:
WooCommerce single-product.php template
WooCommerce content-single-product.php template
You can also add it the same way in one of your existing short codes, this way:
add_shortcode( 'visitor', 'visitor_check_shortcode' );
function visitor_check_shortcode( $atts, $content = null ) {
if ( ( !is_user_logged_in() && !is_null( $content ) ) || is_feed() )
return do_shortcode('[woocommerce_logon_form]');
return '';
}
And this will work too.
See as reference this answer: Change markup in WooCommerce shortcode output
So as you can see your problem is solved on both issues

Accessing post data before functions.php in Wordpress

I am updating my custom Wordpress theme, but as it is rather time consuming process I would like to launch one section at a time.
In other words there would be several versions of a theme for different parts of the website.
To keep things tidy I would like to keep them in separate folders, with all the assets such as js, images, css.
I managed to rewrite template hierarchy using conditional tags but got stuck on functions.php
I was trying to use custom filed (post meta) to switch between several functions.php files but unfortunately $post is not available there so I am unable to use get_post_meta().
I could only find a trace of a solution with custom db queries, $wpdb etc. but can't really figure it out.
Is there any fairly simple solution to hook-up into post data (wp_query) before functions.php is loaded? Or to somehow differently modify where the functions are loaded from?
To illustrate what I'm writing about I pasted my main index.php
<?
get_header();
/*
* Get theme version according to the custom field 'section'
*/
if( function_exists ( 'theme_version' ) ){
$theme = theme_version( #get_the_ID() );
} else {
$theme = 'v2';
}
include_once( 'theme/'. $theme .'/TEMPLATE_BUILDER.php' );
get_footer();
?>
Thanks!
Hopefully found a right answer (after couple of hours of research, trial and error)
I placed the below code in main (wp-native) functions.php
in attempt to keep the code and file structure tidy, works as a charm.
add_action('after_setup_theme', function(){
// parse_url tidies-up the uri
$section = get_post_meta( url_to_postid( parse_url( $_SERVER['REQUEST_URI'], PHP_URL_PATH ) ),'section', true);
if ( !empty( $section )){
// assign sections to the theme versions below
$theme_version = array(
'v3' => array(
'Sixth Form',
[ ... ]
),
'v3.1' => array(
'Contact',
[ ... ]
)
);
foreach($theme_version as $key => $value) { if(in_array( $section, $value )) $theme = $key; }
}
if( empty($theme) ) $theme = 'v2'; // default theme version
require_once( 'theme/' . $theme . '/functions.php' );
$GLOBALS['theme-ver'] = $theme; // set the global to use in index.php (and somewhere else perhaps)
});
The code is not complete yet – needs some conditional clauses as the functions.php is sometimes called multiple times within the loop (especially with custom wp_query)
Perhaps someone will find the above useful. By the way it's quite surprising that WP doesn't natively support some kind of 'theme version control' – I can see strong benefits of not having to upgrade the whole website at once for instance to e. g. resp.

How to modify page title in shortcode?

How do I modify a page title for specific pages in shortcode?
The following will change the title but it executes for every page. I need more control over where it executes.
function assignPageTitle(){
return "Title goes here";
}
add_filter('wp_title', 'assignPageTitle');
Is there a way to call the above in a shortcode function? I know how to use do_shortcode() but the above is a filter.
My goal is to modify the page title based on a URL parameter. This only happens for specific pages.
Although WordPress shortcodes was not designed to do this, it can be done. The problem is shortcodes are processed AFTER the head section is sent so the solution is to process the shortcode BEFORE the head section is sent.
add_filter( 'pre_get_document_title', function( $title ) {
global $post;
if ( ! $post || ! $post->post_content ) {
return $title;
}
if ( preg_match( '#\[mc_set_title.*\]#', $post->post_content, $matches ) !== 1 ) {
return '';
}
return do_shortcode( $matches[0] );
} );
add_shortcode( 'mc_set_title', function( $atts ) {
if ( ! doing_filter( 'pre_get_document_title' ) ) {
# just remove the shortcode from post content in normal shortcode processing
return '';
}
# in filter 'pre_get_document_title' - you can use $atts and global $post to compute the title
return 'MC TITLE';
} );
The critical point is when the filter 'pre_get_document_title' is done the global $post object is set and $post->post_content is available. So, you can find the shortcodes for this post at this time.
When the shortcode is normally called it replaces itself with the empty string so it has no effect on the post_content. However, when called from the filter 'pre_get_document_title' it can compute the title from its arguments $atts and the global $post.
Taken from the WordPress Codex
Introduced in WordPress 2.5 is the Shortcode API, a simple set of
functions for creating macro codes for use in post content.
This would suggest that you can't control page titles using shortcodes as the shortcode is run inside the post content at which point the title tag has already been rendered and is then too late.
What is it exactly that you want to do? Using the Yoast SEO Plugin you can set Post and Page titles within each post if this is what you want to do?
You could create your custom plugin based on your URL parameters as so:
function assignPageTitle(){
if( $_GET['query'] == 'something' ) { return 'something'; }
elseif( $_GET['query'] == 'something-else' ) { return 'something-else'; }
else { return "Default Title"; }
}
add_filter('wp_title', 'assignPageTitle');

Calling WordPress get_template_part from inside a shortcode function renders template first

I have a page where I need to allow the user to enter a paragraph of text. Then after that text, insert a shortcode that will render a list of posts, then add more free-form text after that. My thought was that they should be able to insert a shortcode which will output the posts. This way they can simply add the shortcode where they wish the posts to appear.
I currently have the logic which retrieve the posts separated in its own file. Currently I include it in a page by simply using the get_template_part() function:
get_template_part('donation', 'posts');
I looked into how to create a shortcode and included the following code into my functions.php file in order to create the shortcode:
add_shortcode('donation-posts', 'fnDonatePosts');
function fnDonatePosts($attr, $content)
{
get_template_part('donation', 'posts');
}
The donation-posts.php is being properly executed and the posts are appearing, however, they are always positioned BEFORE the content and not in the location that the shortcode was placed.
I have tried removing the get_template_part() function and just output some text and that works fine. So I understand that the get_template_part() may not be the right way to do this, however, I have not been able to uncover a way to do what I am attempting to do (I am sure that there is a way... I just haven't found it).
I have tried:
include(get_template_directory(). '/donation-posts.php');
include_once(get_template_directory(). '/donation-posts.php') :
But these stopped processing once they hit the PHP code in the included file.
I have also tried:
$file = file_get_contents(get_template_directory(). '/donation-posts.php');
return $file;
But this only returns the contents of the file (as the function name indicates) which means it doesn't execute the PHP script to return the posts.
Anybody out there done this before?
You may try this, it may solve your problem because get_template_part basically reacts like PHP's require, it doesn't return but echos the content immediately where it's been called.
add_shortcode('donation-posts', 'fnDonatePosts');
function fnDonatePosts($attr, $content)
{
ob_start();
get_template_part('donation', 'posts');
$ret = ob_get_contents();
ob_end_clean();
return $ret;
}
Here is a more dynamic version where you can pass the path to the template.
function template_part( $atts, $content = null ){
$tp_atts = shortcode_atts(array(
'path' => null,
), $atts);
ob_start();
get_template_part($tp_atts['path']);
$ret = ob_get_contents();
ob_end_clean();
return $ret;
}
add_shortcode('template_part', 'template_part');
And the shortcode:
[template_part path="includes/social-sharing"]
Minimal version of the accepted answer:
function my_template_part_shortcode() {
ob_start();
get_template_part( 'my_template' );
return ob_get_clean();
}
add_shortcode( 'my_template_part', 'my_template_part_shortcode' );
where my-template.php is the file you'd like to include.
get_template_part() didn't work for me when using it in functions.php. I used locate_template() inside the ob_start and clean instead. For example:
function full_petition_shortcode( $attr ) {
ob_start();
locate_template( 'petition.php', TRUE, TRUE );
return ob_get_clean();
}
add_shortcode( 'full-petition', 'full_petition_shortcode' );

Categories