How to link to a page that has a shortcode?

I am building a calendar plugin that can be added within a page via shortcode. I am trying to find how I can build a link that links to the page the shortcode for the calendar is in. So another words if a user adds [calendar-shortcode] to a page I want to ability to link to that page from anywhere within the site.
So far this is what I have come up with, but I am wondering if there is another way.

/**
* Get page shortcode is on.
*
* @param $shortcode string. The shortcode tag
* @param $transient string. Name of transient
* @return array|mixed
*/
function get_shortcode_page($shortcode, $transient){

if(false === ($shortcode_page = get_transient($transient))){

    //Search query for shortcode tag
    $the_query = new WP_Query(array('s' => $shortcode));

    //Build simple array of page info to pass into transient
    if(isset($the_query) && $the_query->post_count != 0){
        $shortcode_page = array('ID' => $the_query->posts[0]->ID, 'permalink' => get_permalink($the_query->posts[0]->ID));

        set_transient($transient, $shortcode_page);
    }
    return null;

   }
  return $calendar_page;
}

//Use function to find shortcode page info
$page = get_shortcode_page('class-schedule', 'shortcode_calendar_page');

 /**
 * Delete shortcode transients
 *
 * If page permalink changes then delete old transients.
 *
 *
 * @param $post_id
 */
function delete_calendar_shortcode($post_id){

//get shortcode page.
$calendar_page = get_shortcode_page('class-schedule', 'shortcode_calendar_page');

    if($post_id != $calendar_page['ID'])
        return $post_id;

    $current_permalink = get_permalink( $post_id );

    if ($current_permalink != $calendar_page['permalink']){
        delete_transient( 'shortcode_calendar_page' );
   }

   return $post_id;
}

add_action('save_post',  'delete_calendar_shortcode');

This has not been tested much but it does work. I want to know if there is a more efficient way of doing this?

Thanks

UPDATE:

Here is version 2 of above: This will remove the need for the get_shortcode_page() function above. Everything will be handled on page save.

/**
 * Delete shortcode transients vs 2
 *
 * If page permalink changes then delete old transients.
 *
 *
 * @param $post_id
 */
function delete_calendar_shortcode_vs2($post_id){

// If this is an autosave, our form has not been submitted,
if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
    return $post_id;
}

$content_post = get_post($post_id);
$content = $content_post->post_content;

//only run if current page being saved has the shortcode
if(has_shortcode($content, 'class-schedule')){

    //Build simple array of page info to pass into transient
    $page = array('ID' => $post_id, 'permalink' => get_permalink($post_id));

    if(false !== ($calendar_page = get_transient('shortcode_calendar_page'))){

        if($page['ID'] != $calendar_page['ID'] || $page['permalink'] !=   $calendar_page['permalink']){
            delete_transient('shortcode_calendar_page');
        }
    }

    set_transient('shortcode_calendar_page', $page);
  }

return $post_id;
}

 add_action('save_post', 'delete_calendar_shortcode_vs2');

//get shortcode page from transient
$page = get_transient('shortcode_calendar_page');

Again…I am looking to see if there is another way that makes more sense. I also need to add rewrite rules based off the page the shortcode is on. I figured one step at a time.
thanks

//////// UPDATE V3 with the aid of @true ////////////

    /**
     * Shortcode page meta builder
     *
     * Checks on save if shortcode for page exist and saves info to meta.
     *
     * @param $post_id
     */


     function has_shortcode_meta_builder($post_id){

        // If this is an autosave, our form has not been submitted,
        if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE){
            return $post_id;
        }

        $content_post = get_post($post_id);
        $content = $content_post->post_content;

        if(has_shortcode($content, 'class-schedule')){
            update_post_meta($post_id, 'has_shortcode', '1');
            delete_transient('shortcode_pages');

        }else{
            //check if meta exist in the event shortcode was removed from page.
            $meta = get_post_meta($post_id, 'has_shortcode');
            if(!empty($meta)){
                delete_post_meta($post_id, 'has_shortcode');
                delete_transient('shortcode_pages');
            }
        }

        return $post_id;
    }

    add_action('save_post', 'has_shortcode_meta_builder');

This function will then be used to find all the pages with the shortcode.
Either return the transient results or makes a sql call for new results to rebuild the transient.

    /**
     * Get all posts and their permalinks that have shortcode.
     * Find pages with shortcode and store them into transient
     *
     * @global wpdb $wpdb
     * @return array. 
     */
    public function get_pages_with_shortocde(){
        global $wpdb;

        if(false === ($post_data = get_transient('shortcode_pages'))){

            $post_data = $wpdb->get_results($wpdb->prepare(
                "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s", 'has_shortcode'));

            if(!empty($post_data)){
                foreach($post_data as $row){
                    $post = get_post($row->post_id);
                    $row->slug = $post->post_name;
                    $row->post_permalink = get_permalink($row->post_id);
                }
            }
            set_transient('shortcode_pages', $post_data);
        }

        return $post_data;
    }

Finally add an action so when post are deleted it will check if page does or does not have the shortcode within it. If it did then delete the shortcode transient (which will refresh next time called)

    /**
     * Delete actions when post is deleted.
     *
     * @param $post_id
     */
     function delete_post_data($post_id){
        global $wpdb;

        //Delete shortcode transient so that it can be rebuilt excluding this page
        $content_post = get_post($post_id);
        $content = $content_post->post_content;
        if(has_shortcode($content, 'class-calendar)){
            delete_transient('shortcode_pages');
        }

    }
 add_action('before_delete_post', 'delete_post_data');

1 Answer
1

Transients are probably not the way to go here.

From what I understand:

  1. You want to know if a post has a shortcode by doing some form of look up

  2. You want to be able to get a list of permalinks / posts that have this shortcode.

Here’s what I suggest.

/**
 * Checks for calender shortcode and flags the post with post meta.
 * @param integer $post_id
 * @return integer
 */
function save_calender_postmeta($post_id)
{
    // Make sure this isn't an autosave.
    if(!defined('DOING_AUTOSAVE') || !DOING_AUTOSAVE){
        // Get Post Content
        $content = get_post_field('post_content', $post_id);
        // Check for shortcode
        if(has_shortcode($content, 'class-schedule')) {
            // Update (or it will insert) the 'has_calender' post meta.
            update_post_meta($post_id, 'has_calender', '1');
        }
        else {
            // Delete the has calender post meta if it exists.
            delete_post_meta($post_id, 'has_calender');
        }
    }
    return $post_id;
}
add_action('save_post', 'save_calender_postmeta');

And then, if you want to get all the posts that have calenders you could use:

/**
 * Get all posts and their permalinks that have calenders.
 * @global wpdb $wpdb
 */
function get_calender_posts_links()
{
    global $wpdb;
    $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s",
            'has_calender'
    ));
    if(!empty($rows)) {
        // Let's include the post permalinks with each row.
        foreach($rows as $row) {
            $row->post_permalink = get_permalink($row->post_id);
        }
    }
    return $rows;
}

Or if you wanted to check if a single post had a calender:

/**
 * Check if a post has a calender.
 * @param int $post_id
 * @return boolean
 */
function post_has_calender($post_id)
{
    return (get_post_meta($post_id, 'has_calender'));
}

Let me know if this is what you were looking for.

Leave a Comment