-
Kevin Cristiano authored
Signed-off-by:
Kevin Cristiano <kcristiano@tadpole.cc>
Kevin Cristiano authoredSigned-off-by:
Kevin Cristiano <kcristiano@tadpole.cc>
civicrm.shortcodes.php 22.33 KiB
<?php
/*
+--------------------------------------------------------------------+
| CiviCRM version 4.7 |
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC (c) 2004-2018 |
+--------------------------------------------------------------------+
| This file is a part of CiviCRM. |
| |
| CiviCRM is free software; you can copy, modify, and distribute it |
| under the terms of the GNU Affero General Public License |
| Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
| |
| CiviCRM is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
| See the GNU Affero General Public License for more details. |
| |
| You should have received a copy of the GNU Affero General Public |
| License and the CiviCRM Licensing Exception along |
| with this program; if not, contact CiviCRM LLC |
| at info[AT]civicrm[DOT]org. If you have questions about the |
| GNU Affero General Public License or the licensing of CiviCRM, |
| see the CiviCRM license FAQ at http://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/
/**
*
* @package CRM
* @copyright CiviCRM LLC (c) 2004-2018
*
*/
// this file must not accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
/**
* Define CiviCRM_For_WordPress_Shortcodes Class
*/
class CiviCRM_For_WordPress_Shortcodes {
/**
* Declare our properties
*/
// init property to store reference to Civi
public $civi;
// init property to store shortcodes
public $shortcodes = array();
// init property to store shortcode markup
public $shortcode_markup = array();
// count multiple passes of do_shortcode in a post
public $shortcode_in_post = array();
/**
* Instance constructor
*
* @return object $this The object instance
*/
function __construct() {
// store reference to Civi object
$this->civi = civi_wp();
}
/**
* Register hooks to handle the presence of shortcodes in content
*
* @return void
*/
public function register_hooks() {
// register the CiviCRM shortcode
add_shortcode( 'civicrm', array( $this, 'render_single' ) );
// add CiviCRM core resources when a shortcode is detected in the post content
add_action( 'wp', array( $this, 'prerender' ), 10, 1 );
}
/**
* Determine if a CiviCRM shortcode is present in any of the posts about to be displayed
* Callback method for 'wp' hook, always called from WP front-end
*
* @param object $wp The WP object, present but not used
* @return void
*/
public function prerender( $wp ) {
/**
* At this point, all conditional tags are available
* @see http://codex.wordpress.org/Conditional_Tags
*/
// bail if this is a 404
if ( is_404() ) return;
// a counter's useful
$shortcodes_present = 0;
// let's loop through the results
// this also has the effect of bypassing the logic in
// https://github.com/civicrm/civicrm-wordpress/pull/36
if ( have_posts() ) {
while ( have_posts() ) : the_post();
global $post;
// check for existence of shortcode in content
if ( has_shortcode( $post->post_content, 'civicrm' ) ) {
// get CiviCRM shortcodes in this post
$shortcodes_array = $this->get_for_post( $post->post_content );
// sanity check
if ( !empty( $shortcodes_array ) ) {
// add it to our property
$this->shortcodes[$post->ID] = $shortcodes_array;
// bump shortcode counter
$shortcodes_present += count( $this->shortcodes[$post->ID] );
}
}
endwhile;
}
// reset loop
rewind_posts();
// did we get any?
if ( $shortcodes_present ) {
// we need CiviCRM initialised prior to parsing shortcodes
if (!$this->civi->initialize()) {
return;
}
// how should we handle multiple shortcodes?
if ( $shortcodes_present > 1 ) {
// add CSS resources for front end
add_action( 'wp_enqueue_scripts', array( $this->civi, 'front_end_css_load' ), 100 );
// let's add dummy markup
foreach( $this->shortcodes AS $post_id => $shortcode_array ) {
// set flag if there are multple shortcodes in this post
$multiple = ( count( $shortcode_array ) > 1 ) ? 1 : 0;
foreach( $shortcode_array AS $shortcode ) {
// mimic invoke in multiple shortcode context
$this->shortcode_markup[$post_id][] = $this->render_multiple( $post_id, $shortcode, $multiple );
}
}
} else {
// add core resources for front end
add_action( 'wp', array( $this->civi, 'front_end_page_load' ), 100 );
// since we have only one shortcode, run the_loop again
// the DB query has already been done, so this has no significant impact
if ( have_posts() ) {
while ( have_posts() ) : the_post();
global $post;
// is this the post?
if ( ! array_key_exists( $post->ID, $this->shortcodes ) ) {
continue;
}
// the shortcode must be the first item in the shortcodes array
$shortcode = $this->shortcodes[$post->ID][0];
// check to see if a shortcode component has been repeated?
$atts = $this->get_atts( $shortcode );
// test for hijacking
if ( isset( $atts['hijack'] ) AND $atts['hijack'] == '1' ) {
add_filter( 'civicrm_context', array( $this, 'get_context' ) );
}
// store corresponding markup
$this->shortcode_markup[$post->ID][] = do_shortcode( $shortcode );
// test for hijacking
if ( isset( $atts['hijack'] ) AND $atts['hijack'] == '1' ) {
// ditch the filter
remove_filter( 'civicrm_context', array( $this, 'get_context' ) );
// set title
global $civicrm_wp_title;
$post->post_title = $civicrm_wp_title;
// override page title
add_filter( 'single_post_title', array( $this->civi, 'single_page_title' ), 50, 2 );
// overwrite content
add_filter( 'the_content', array( $this, 'get_content' ) );
}
endwhile;
}
// reset loop
rewind_posts();
}
}
// flag that we have parsed shortcodes
$this->shortcodes_parsed = TRUE;
// broadcast this as well
do_action( 'civicrm_shortcodes_parsed' );
}
/**
* Handles CiviCRM-defined shortcodes
*
* @param array Shortcode attributes array
* @return string HTML for output
*/
public function render_single( $atts ) {
// Do not parse shortcodes in REST context for PUT, POST and DELETE methods
if(defined('REST_REQUEST') && REST_REQUEST && (isset($_PUT) || isset($_POST) || isset($_DELETE)) ){
// Return the original shortcode
$shortcode = '[civicrm';
foreach($atts as $att=>$val){
$shortcode.=' '.$att.'="'.$val.'"';
}
$shortcode.=']';
return $shortcode;
}
// check if we've already parsed this shortcode
global $post;
if ( is_object($post) ) {
if ( !empty( $this->shortcode_markup ) ) {
if ( isset( $this->shortcode_markup[$post->ID] ) ) {
// set counter flag
if ( ! isset( $this->shortcode_in_post[$post->ID] ) ) {
$this->shortcode_in_post[$post->ID] = 0;
} else {
$this->shortcode_in_post[$post->ID]++;
}
// this shortcode must have been rendered
return $this->shortcode_markup[$post->ID][$this->shortcode_in_post[$post->ID]];
}
}
}
// preprocess shortcode attributes
$args = $this->preprocess_atts( $atts );
// invoke() requires environment variables to be set
foreach ( $args as $key => $value ) {
if ( $value !== NULL ) {
$_REQUEST[$key] = $_GET[$key] = $value;
}
}
// kick out if not CiviCRM
if (!$this->civi->initialize()) {
return '';
}
// check permission
$argdata = $this->civi->get_request_args();
if ( ! $this->civi->users->check_permission( $argdata['args'] ) ) {
return $this->civi->users->get_permission_denied();;
}
// CMW: why do we need this? Nothing that follows uses it...
require_once ABSPATH . WPINC . '/pluggable.php';
ob_start(); // start buffering
$this->civi->invoke(); // now, instead of echoing, shortcode output ends up in buffer
$content = ob_get_clean(); // save the output and flush the buffer
return $content;
}
/**
* Return a generic display for a shortcode instead of a CiviCRM invocation
*
* @param int $post_id The containing WordPress post ID
* @param string $shortcode The shortcode being parsed
* @param bool $multiple Boolean flag, TRUE if post has multiple shortcodes, FALSE otherwise
* @return string $markup Generic markup for multiple instances
*/
private function render_multiple( $post_id = FALSE, $shortcode = FALSE, $multiple = 0 ) {
// get attributes
$atts = $this->get_atts( $shortcode );
// pre-process shortcode and retrieve args
$args = $this->preprocess_atts( $atts );
// get data for this shortcode
$data = $this->get_data( $atts, $args );
// sanity check
if ( $data === FALSE ) return '';
// did we get a title?
$title = __( 'Content via CiviCRM', 'civicrm' );
if ( ! empty( $data['title'] ) ) $title = $data['title'];
// init title flag
$show_title = TRUE;
// default link
$link = get_permalink( $post_id );
// default to no class
$class = '';
// access CIvi config object
$config = CRM_Core_Config::singleton();
// do we have multiple shortcodes?
if ( $multiple != 0 ) {
$links = array();
foreach( $args AS $var => $arg ) {
if ( ! empty( $arg ) AND $var != 'q' ) {
$links[] = $var . '=' . $arg;
}
}
$query = implode( '&', $links );
// $absolute, $frontend, $forceBackend
$base_url = $this->civi->get_base_url(TRUE, FALSE, FALSE);
// construct query parts
$queryParts = array();
$queryParts[] = 'page=CiviCRM';
if (isset($args['q'])) {
$queryParts[] = 'q=' . $args['q'];
}
if (isset($query)) {
$queryParts[] = $query;
}
// construct link
$link = trailingslashit( $base_url ) . '?' . implode('&', $queryParts);
// add a class for styling purposes
$class = ' civicrm-shortcode-multiple';
}
// test for hijacking
if ( !$multiple ) {
if ( isset( $atts['hijack'] ) AND $atts['hijack'] == '1' ) {
// add title to array
$this->post_titles[$post_id] = $data['title'];
// override title
add_filter( 'the_title', array( $this, 'get_title' ), 100, 2 );
// overwrite content
add_filter( 'the_content', array( $this, 'get_content' ) );
// don't show title
$show_title = FALSE;
// add a class for styling purposes
$class = ' civicrm-shortcode-single';
}
}
// set some template variables
// description
$description = FALSE;
if ( isset( $data['text'] ) AND ! empty( $data['text'] ) ) {
$description = $data['text'];
}
// provide an enticing link
$more_link = sprintf(
'<a href="%s">%s</a>',
$link,
apply_filters( 'civicrm_shortcode_more_link', __( 'Find out more...', 'civicrm' ) )
);
// assume CiviCRM footer is not enabled
$empowered_enabled = FALSE;
$footer = '';
// test config object for setting
if ( $config->empoweredBy == 1 ) {
// footer enabled - define it
$civi = __( 'CiviCRM.org - Growing and Sustaining Relationships', 'civicrm' );
$logo = '<div class="empowered-by-logo"><span>' . __( 'CiviCRM', 'civicrm' ) . '</span></div>';
$civi_link = '<a href="http://civicrm.org/" title="' . $civi . '" target="_blank" class="empowered-by-link">' . $logo . '</a>';
$empowered = sprintf( __( 'Empowered by %s', 'civicrm' ), $civi_link );
$footer = apply_filters( 'civicrm_shortcode_footer', $empowered );
$empowered_enabled = TRUE;
}
// start buffering
ob_start();
// include template
include( CIVICRM_PLUGIN_DIR . 'assets/templates/civicrm.shortcode.php' );
// save the output and flush the buffer
$markup = ob_get_clean();
// allow plugins to override
return apply_filters( 'civicrm_shortcode_render_multiple', $markup, $post_id, $shortcode );
}
/**
* In order to hijack the page, we need to override the context
*
* @return string Overridden context code
*/
public function get_context() {
return 'nonpage';
}
/**
* In order to hijack the page, we need to override the content
*
* @return string Overridden content
*/
public function get_content( $content ) {
global $post;
// is this the post?
if ( ! array_key_exists( $post->ID, $this->shortcode_markup ) ) {
return $content;
}
// bail if it has multiple shortcodes
if ( count( $this->shortcode_markup[$post->ID] ) > 1 ) {
return $content;
}
return $this->shortcode_markup[$post->ID][0];
}
/**
* In order to hijack the page, we need to override the title
*
* @return string Overridden title
*/
public function get_title( $title, $post_id ) {
// is this the post?
if ( ! array_key_exists( $post_id, $this->shortcode_markup ) ) {
return $title;
}
// bail if it has multiple shortcodes
if ( count( $this->shortcode_markup[$post_id] ) > 1 ) {
return $title;
}
// shortcodes may or may not override title
if ( array_key_exists( $post_id, $this->post_titles ) ) {
$title = $this->post_titles[$post_id];
}
return $title;
}
/**
* Detect and return CiviCRM shortcodes in post content
*
* @param $content The content to parse
* @return array $shortcodes Array of shortcodes
*/
private function get_for_post( $content ) {
// init return array
$shortcodes = array();
// attempt to discover all instances of the shortcode
$pattern = get_shortcode_regex();
if (
preg_match_all( '/' . $pattern . '/s', $content, $matches )
&& array_key_exists( 2, $matches )
&& in_array( 'civicrm', $matches[2] ) )
{
// get keys for our shortcode
$keys = array_keys( $matches[2], 'civicrm' );
foreach( $keys AS $key ) {
$shortcodes[] = $matches[0][$key];
}
}
return $shortcodes;
}
/**
* Return attributes for a given CiviCRM shortcode
*
* @param $shortcode The shortcode to parse
* @return array $shortcode_atts Array of shortcode attributes
*/
private function get_atts( $shortcode ) {
// strip all but attributes definitions
$text = str_replace( '[civicrm ', '', $shortcode );
$text = str_replace( ']', '', $text );
// extract attributes
$shortcode_atts = shortcode_parse_atts( $text );
return $shortcode_atts;
}
/**
* Preprocess CiviCRM-defined shortcodes
*
* @param array $atts Shortcode attributes array
* @return array $args Shortcode arguments array
*/
public function preprocess_atts( $atts ) {
$shortcode_atts = shortcode_atts( array(
'component' => 'contribution',
'action' => NULL,
'mode' => NULL,
'id' => NULL,
'cid' => NULL,
'gid' => NULL,
'cs' => NULL,
'force' => NULL,
),
$atts,
'civicrm'
);
extract( $shortcode_atts );
$args = array(
'reset' => 1,
'id' => $id,
'force' => $force,
);
switch ( $component ) {
case 'contribution':
if ( $mode == 'preview' || $mode == 'test' ) {
$args['action'] = 'preview';
}
$args['q'] = 'civicrm/contribute/transact';
break;
case 'event':
switch ( $action ) {
case 'register':
$args['q'] = 'civicrm/event/register';
if ( $mode == 'preview' || $mode == 'test' ) {
$args['action'] = 'preview';
}
break;
case 'info':
$args['q'] = 'civicrm/event/info';
$_REQUEST['page'] = $_GET['page'] = 'CiviCRM';
break;
default:
echo '<p>' . __( 'Do not know how to handle this shortcode', 'civicrm' ) . '</p>';
return;
}
break;
case 'user-dashboard':
$args['q'] = 'civicrm/user';
unset( $args['id'] );
break;
case 'profile':
if ($mode == 'edit') {
$args['q'] = 'civicrm/profile/edit';
}
elseif ($mode == 'view') {
$args['q'] = 'civicrm/profile/view';
}
elseif ($mode == 'search') {
$args['q'] = 'civicrm/profile';
}
else {
$args['q'] = 'civicrm/profile/create';
}
$args['gid'] = $gid;
break;
case 'petition':
$args['q'] = 'civicrm/petition/sign';
$args['sid'] = $args['id'];
unset($args['id']);
break;
default:
echo '<p>' . __( 'Do not know how to handle this shortcode', 'civicrm' ) . '</p>';
return;
}
/**
* Filter the CiviCRM shortcode arguments.
*
* This filter allows plugins or CiviExtensions to modify the attributes
* that the 'civicrm' shortcode allows. Injected attributes and their values
* will also become available in the $_REQUEST and $_GET arrays.
*
* @param array $args Existing shortcode arguments
* @param array $shortcode_atts Shortcode attributes
* @return array $args Modified shortcode arguments
*/
return apply_filters( 'civicrm_shortcode_preprocess_atts', $args, $shortcode_atts );
}
/**
* Post-process CiviCRM-defined shortcodes
*
* @param array $atts Shortcode attributes array
* @param array $args Shortcode arguments array
* @return array|bool $data The array data used to build the shortcode markup (or false on failure)
*/
public function get_data( $atts, $args ) {
// init return array
$data = array();
if (!$this->civi->initialize()) {
return FALSE;
}
/**
* Filter the base CiviCRM API parameters.
*
* This filter allows plugins or CiviExtensions to modify the API call when
* there are multiple shortcodes being rendered.
*
* @param array $params Existing API params
* @param array $atts Shortcode attributes array
* @param array $args Shortcode arguments array
* @return array $params Modified API params
*/
$params = apply_filters( 'civicrm_shortcode_api_params', array(
'version' => 3,
'page' => 'CiviCRM',
'q' => 'civicrm/ajax/rest',
'sequential' => '1',
), $atts, $args );
// get the Civi entity via the API
switch ( $atts['component'] ) {
case 'contribution':
// add event ID
$params['id'] = $args['id'];
// call API
$civi_entity = civicrm_api( 'contribution_page', 'getsingle', $params );
// set title
$data['title'] = $civi_entity['title'];
// set text, if present
$data['text'] = '';
if ( isset( $civi_entity['intro_text'] ) ) {
$data['text'] = $civi_entity['intro_text'];
}
break;
case 'event':
// add event ID
$params['id'] = $args['id'];
// call API
$civi_entity = civicrm_api( 'event', 'getsingle', $params );
// set title
switch ( $atts['action'] ) {
case 'register':
$data['title'] = sprintf(
__( 'Register for %s', 'civicrm' ),
$civi_entity['title']
);
break;
case 'info':
default:
$data['title'] = $civi_entity['title'];
break;
}
// set text, if present
$data['text'] = '';
if ( isset( $civi_entity['summary'] ) ) {
$data['text'] = $civi_entity['summary'];
}
if (
// summary is not present or is empty
( !isset($civi_entity['summary']) OR empty($civi_entity['summary']) )
AND
// we do have a description
isset( $civi_entity['description'] ) AND !empty( $civi_entity['description'] )
) {
// override with description
$data['text'] = $civi_entity['description'];
}
break;
case 'user-dashboard':
// set title
$data['title'] = __( 'Dashboard', 'civicrm' );
break;
case 'profile':
// add event ID
$params['id'] = $args['gid'];
// call API
$civi_entity = civicrm_api( 'uf_group', 'getsingle', $params );
// set title
$data['title'] = $civi_entity['title'];
// set text to empty
$data['text'] = '';
break;
case 'petition':
// add petition ID
$params['id'] = $atts['id'];
// call API
$civi_entity = civicrm_api( 'survey', 'getsingle', $params );
// set title
$data['title'] = $civi_entity['title'];
// set text, if present
$data['text'] = '';
if ( isset( $civi_entity['instructions'] ) ) {
$data['text'] = $civi_entity['instructions'];
}
break;
default:
// do we need to protect against malformed shortcodes?
break;
}
/**
* Filter the CiviCRM shortcode data array.
*
* This filter allows plugins or CiviExtensions to modify the data used to
* display the shortcode when there are multiple shortcodes being rendered.
*
* @param array $data Existing shortcode data
* @param array $atts Shortcode attributes array
* @param array $args Shortcode arguments array
* @return array $data Modified shortcode data
*/
return apply_filters( 'civicrm_shortcode_get_data', $data, $atts, $args );
}
} // class CiviCRM_For_WordPress_Shortcodes ends