* * @return string The string with all EOL characters removed. */ public static function removeEOL($string) { if (cnSettingsAPI::get('connections', 'compatibility', 'strip_rnt')) { $search = array("\r\n", "\r", "\n", "\t", PHP_EOL); $replace = array(' '); $string = str_replace($search, $replace, $string); } return trim($string); } /** * Attemps to intelligently remove <p> and <br> tags added around * the shortcodes by wpautop(). * * @access public * @since 0.8 * @param string $content The content captured by the cn_template shortcode. * * @return string */ public static function removePBR($content) { $content = strtr($content, array('<p><!--' => '<!--', '--></p>' => '-->', '<p>[' => '[', ']</p>' => ']', '/]</p>' => ']', ']<br />' => ']', '/]<br />' => ']')); return $content; } } // Init the class. cnShortcode::init();
/** * @param array $atts * @param string $content * @param string $tag * * @return string */ public static function shortcode($atts, $content = '', $tag = 'connections') { // Grab an instance of the Connections object. $instance = Connections_Directory(); $html = ''; if (is_customize_preview()) { /** * Hook to allow the active template to be overridden and set to the current template being customized. * * @since 8.4 * * @param array $atts { * @type string $template The template slug of the template being customized. * } */ $atts = apply_filters('cn_template_customizer_template', $atts); } /** @var cnTemplate $template */ $template = cnTemplateFactory::loadTemplate($atts); if ($template === FALSE) { return cnTemplatePart::loadTemplateError($atts); } /* * This filter adds the current template paths to cnLocate so when template * part file overrides are being searched for, it'll also search in template * specific paths. This filter is then removed at the end of the shortcode. */ add_filter('cn_locate_file_paths', array($template, 'templatePaths')); self::addFilterRegistry('cn_locate_file_paths'); do_action('cn_template_include_once-' . $template->getSlug()); do_action('cn_template_enqueue_js-' . $template->getSlug()); /* * Now that the template has been loaded, Validate the user supplied shortcode atts. */ $defaults = array('id' => NULL, 'slug' => NULL, 'category' => NULL, 'category_in' => NULL, 'exclude_category' => NULL, 'category_name' => NULL, 'category_slug' => NULL, 'wp_current_category' => FALSE, 'allow_public_override' => FALSE, 'private_override' => FALSE, 'show_alphaindex' => cnSettingsAPI::get('connections', 'display_results', 'index'), 'repeat_alphaindex' => cnSettingsAPI::get('connections', 'display_results', 'index_repeat'), 'show_alphahead' => cnSettingsAPI::get('connections', 'display_results', 'show_current_character'), 'list_type' => NULL, 'order_by' => NULL, 'limit' => NULL, 'offset' => NULL, 'family_name' => NULL, 'last_name' => NULL, 'title' => NULL, 'organization' => NULL, 'department' => NULL, 'district' => NULL, 'county' => NULL, 'city' => NULL, 'state' => NULL, 'zip_code' => NULL, 'country' => NULL, 'meta_query' => '', 'content' => '', 'near_addr' => NULL, 'latitude' => NULL, 'longitude' => NULL, 'radius' => 10, 'unit' => 'mi', 'template' => NULL, 'width' => NULL, 'lock' => FALSE, 'force_home' => FALSE, 'home_id' => in_the_loop() && is_page() ? get_the_ID() : cnSettingsAPI::get('connections', 'home_page', 'page_id')); $defaults = apply_filters('cn_list_atts_permitted', $defaults); $defaults = apply_filters('cn_list_atts_permitted-' . $template->getSlug(), $defaults); $atts = shortcode_atts($defaults, $atts, $tag); $atts = apply_filters('cn_list_atts', $atts); $atts = apply_filters('cn_list_atts-' . $template->getSlug(), $atts); /* * Convert some of the $atts values in the array to boolean. */ cnFormatting::toBoolean($atts['allow_public_override']); cnFormatting::toBoolean($atts['private_override']); cnFormatting::toBoolean($atts['show_alphaindex']); cnFormatting::toBoolean($atts['repeat_alphaindex']); cnFormatting::toBoolean($atts['show_alphahead']); cnFormatting::toBoolean($atts['wp_current_category']); cnFormatting::toBoolean($atts['lock']); cnFormatting::toBoolean($atts['force_home']); // var_dump( $atts ); /* * The post editor entity encodes the post text we have to decode it * so a match can be made when the query is run. */ $atts['family_name'] = html_entity_decode($atts['family_name']); $atts['last_name'] = html_entity_decode($atts['last_name']); $atts['title'] = html_entity_decode($atts['title']); $atts['organization'] = html_entity_decode($atts['organization']); $atts['department'] = html_entity_decode($atts['department']); $atts['city'] = html_entity_decode($atts['city']); $atts['state'] = html_entity_decode($atts['state']); $atts['zip_code'] = html_entity_decode($atts['zip_code']); $atts['country'] = html_entity_decode($atts['country']); $atts['category_name'] = html_entity_decode($atts['category_name']); if (0 < strlen($atts['meta_query'])) { // The meta query syntax follows the JSON standard, except, the WordPress Shortcode API does not allow // brackets within shortcode options, so parenthesis have to be used instead, so, lets swap them // that was json_decode can be ran and the resulting array used in cnRetrieve::entries(). $atts['meta_query'] = str_replace(array('(', ')'), array('[', ']'), $atts['meta_query']); $metaQuery = cnFormatting::maybeJSONdecode($atts['meta_query']); $atts['meta_query'] = is_array($metaQuery) ? $metaQuery : array(); } $atts = apply_filters('cn_list_retrieve_atts', $atts); $atts = apply_filters('cn_list_retrieve_atts-' . $template->getSlug(), $atts); $results = $instance->retrieve->entries($atts); // $html .= print_r( $instance->lastQuery, TRUE ); // Apply any registered filters to the results. if (!empty($results)) { $results = apply_filters('cn_list_results', $results); $results = apply_filters('cn_list_results-' . $template->getSlug(), $results); self::addFilterRegistry('cn_list_results-' . $template->getSlug()); } ob_start(); // Prints the template's CSS file. // NOTE: This is primarily to support legacy templates which included a CSS // file which was not enqueued in the page header. do_action('cn_template_inline_css-' . $template->getSlug(), $atts); // The return to top anchor do_action('cn_list_return_to_target', $atts); $html .= ob_get_clean(); $html .= sprintf('<div class="cn-list" id="cn-list" data-connections-version="%1$s-%2$s"%3$s>', $instance->options->getVersion(), $instance->options->getDBVersion(), empty($atts['width']) ? '' : ' style="width: ' . $atts['width'] . 'px;"'); $html .= sprintf('<div class="cn-template cn-%1$s" id="cn-%1$s" data-template-version="%2$s">', $template->getSlug(), $template->getVersion()); // The filter should check $content that content is not empty before processing $content. // And if it is empty the filter should return (bool) FALSE, so the core template parts can be executed. $content = apply_filters("cn_shortcode_content-{$tag}", FALSE, $content, $atts, $results, $template); if ($content === FALSE) { ob_start(); // Render the core result list header. cnTemplatePart::header($atts, $results, $template); // Render the core result list body. cnTemplatePart::body($atts, $results, $template); // Render the core result list footer. cnTemplatePart::footer($atts, $results, $template); $html .= ob_get_clean(); } else { $html .= $content; } $html .= PHP_EOL . '</div>' . (WP_DEBUG ? '<!-- END #cn-' . $template->getSlug() . ' -->' : '') . PHP_EOL; $html .= PHP_EOL . '</div>' . (WP_DEBUG ? '<!-- END #cn-list -->' : '') . PHP_EOL; // Clear any filters that have been added. // This allows support using the shortcode multiple times on the same page. cnShortcode::clearFilterRegistry(); // @todo This should be run via a filter. return self::removeEOL($html); }
/** * The result list foot. * * @access public * @since 0.8 * @static * @param array $atts The shortcode $atts array. * @param array $results The cnRetrieve query results. * @param object $template An instance of the cnTemplate object. * * @return string */ public static function footer($atts, $results, $template) { $defaults = array('return' => FALSE); $atts = wp_parse_args($atts, $defaults); $out = '<div class="cn-clear" id="cn-list-foot">' . PHP_EOL; ob_start(); do_action('cn_action_list_both-' . $template->getSlug(), $atts, $results); cnShortcode::addFilterRegistry('cn_action_list_both-' . $template->getSlug()); do_action('cn_action_list_after-' . $template->getSlug(), $atts, $results); cnShortcode::addFilterRegistry('cn_action_list_after-' . $template->getSlug()); do_action('cn_action_list_both', $atts, $results); do_action('cn_action_list_after', $atts, $results); $out .= ob_get_clean(); $out .= PHP_EOL . '</div>' . (WP_DEBUG ? '<!-- END #cn-list-foot -->' : '') . PHP_EOL; return self::echoOrReturn($atts['return'], $out); }
/** * Display the upcoming list. * * @access public * @since unknown * * @param array $atts * @param string $content [optional] * @param string $tag [optional] When called as the callback for add_shortcode, the shortcode tag is passed * automatically. Manually setting the shortcode tag so the function can be called * independently. * * @return string */ function _upcoming_list($atts, $content = NULL, $tag = 'upcoming_list') { global $connections, $wpdb; // $template =& $connections->template; $out = ''; $alternate = ''; $templateTypeDefaults = array('list_type' => 'birthday', 'template' => NULL); $templateType = cnSanitize::args($atts, $templateTypeDefaults); /* * If a list type was specified in the shortcode, load the template based on that type. * However, if a specific template was specified, that should preempt the template to be loaded based on the list type if it was specified.. */ if (!empty($atts['template'])) { $template = cnTemplateFactory::getTemplate($templateType['template']); } else { $templateSlug = $connections->options->getActiveTemplate($templateType['list_type']); $template = cnTemplateFactory::getTemplate($templateSlug); } // No template found return error message. if ($template === FALSE) { return cnTemplatePart::loadTemplateError($templateType); } $defaults = array('list_type' => 'birthday', 'days' => '30', 'include_today' => TRUE, 'private_override' => FALSE, 'name_format' => '', 'date_format' => 'F jS', 'show_lastname' => FALSE, 'show_title' => TRUE, 'list_title' => NULL, 'template' => NULL, 'content' => '', 'force_home' => TRUE, 'home_id' => cnSettingsAPI::get('connections', 'home_page', 'page_id')); $defaults = apply_filters('cn_list_atts_permitted', $defaults); $defaults = apply_filters('cn_list_atts_permitted-' . $template->getSlug(), $defaults); $atts = shortcode_atts($defaults, $atts, $tag); $atts = apply_filters('cn_list_atts', $atts); $atts = apply_filters('cn_list_atts-' . $template->getSlug(), $atts); /* * Convert some of the $atts values in the array to boolean. */ cnFormatting::toBoolean($atts['include_today']); cnFormatting::toBoolean($atts['private_override']); cnFormatting::toBoolean($atts['show_lastname']); cnFormatting::toBoolean($atts['repeat_alphaindex']); cnFormatting::toBoolean($atts['show_title']); if (0 == strlen($atts['name_format'])) { $atts['name_format'] = $atts['show_lastname'] ? '%first% %last%' : '%first%'; } /** @var cnTemplate $template */ do_action('cn_register_legacy_template_parts'); do_action('cn_action_include_once-' . $template->getSlug()); do_action('cn_action_js-' . $template->getSlug()); /* * This filter adds the current template paths to cnLocate so when template * part file overrides are being searched for, it'll also search in template * specific paths. This filter is then removed at the end of the shortcode. */ add_filter('cn_locate_file_paths', array($template, 'templatePaths')); cnShortcode::addFilterRegistry('cn_locate_file_paths'); do_action('cn_template_include_once-' . $template->getSlug()); do_action('cn_template_enqueue_js-' . $template->getSlug()); /* * Set the query vars and run query. */ // Show only public or private [if permitted] entries. if (is_user_logged_in() || $atts['private_override'] != FALSE) { $visibilityfilter = " AND (visibility='private' OR visibility='public') AND (" . $atts['list_type'] . " != '')"; } else { $visibilityfilter = " AND (visibility='public') AND (`" . $atts['list_type'] . "` != '')"; } // Get the current date from WP which should have the current time zone offset. $wpCurrentDate = date('Y-m-d', $connections->options->wpCurrentTime); // Whether or not to include the event occurring today or not. $atts['include_today'] ? $includeToday = '<=' : ($includeToday = '<'); $newSQL = "SELECT * FROM " . CN_ENTRY_TABLE . " WHERE" . " (YEAR(DATE_ADD('{$wpCurrentDate}', INTERVAL " . $atts['days'] . " DAY))" . " - YEAR(DATE_ADD(FROM_UNIXTIME(`" . $atts['list_type'] . "`), INTERVAL " . $connections->options->sqlTimeOffset . " SECOND)) )" . " - ( MID(DATE_ADD('{$wpCurrentDate}', INTERVAL " . $atts['days'] . " DAY),5,6)" . " < MID(DATE_ADD(FROM_UNIXTIME(`" . $atts['list_type'] . "`), INTERVAL " . $connections->options->sqlTimeOffset . " SECOND),5,6) )" . " > ( YEAR('{$wpCurrentDate}')" . " - YEAR(DATE_ADD(FROM_UNIXTIME(`" . $atts['list_type'] . "`), INTERVAL " . $connections->options->sqlTimeOffset . " SECOND)) )" . " - ( MID('{$wpCurrentDate}',5,6)" . " " . $includeToday . " MID(DATE_ADD(FROM_UNIXTIME(`" . $atts['list_type'] . "`), INTERVAL " . $connections->options->sqlTimeOffset . " SECOND),5,6) )" . $visibilityfilter; //$out .= print_r($newSQL , TRUE); $results = $wpdb->get_results($newSQL); //$out .= print_r($results , TRUE); // If there are no results no need to proceed and output message. if (empty($results)) { $noResultMessage = __('No results.', 'connections'); $noResultMessage = apply_filters('cn_upcoming_no_result_message', $noResultMessage); $out .= '<p class="cn-upcoming-no-results">' . $noResultMessage . '</p>'; } else { /*The SQL returns an array sorted by the birthday and/or anniversary date. However the year end wrap needs to be accounted for. Otherwise earlier months of the year show before the later months in the year. Example Jan before Dec. The desired output is to show Dec then Jan dates. This function checks to see if the month is a month earlier than the current month. If it is the year is changed to the following year rather than the current. After a new list is built, it is resorted based on the date.*/ foreach ($results as $key => $row) { if (gmmktime(23, 59, 59, gmdate('m', $row->{$atts['list_type']}), gmdate('d', $row->{$atts['list_type']}), gmdate('Y', $connections->options->wpCurrentTime)) < $connections->options->wpCurrentTime) { $dateSort[] = $row->{$atts['list_type']} = gmmktime(0, 0, 0, gmdate('m', $row->{$atts['list_type']}), gmdate('d', $row->{$atts['list_type']}), gmdate('Y', $connections->options->wpCurrentTime) + 1); } else { $dateSort[] = $row->{$atts['list_type']} = gmmktime(0, 0, 0, gmdate('m', $row->{$atts['list_type']}), gmdate('d', $row->{$atts['list_type']}), gmdate('Y', $connections->options->wpCurrentTime)); } } array_multisort($dateSort, SORT_ASC, $results); if (empty($atts['list_title'])) { switch ($atts['list_type']) { case 'birthday': if ($atts['days'] >= 1) { $list_title = 'Upcoming Birthdays the next ' . $atts['days'] . ' days'; } else { $list_title = 'Today\'s Birthdays'; } break; case 'anniversary': if ($atts['days'] >= 1) { $list_title = 'Upcoming Anniversaries the next ' . $atts['days'] . ' days'; } else { $list_title = 'Today\'s Anniversaries'; } break; } } else { $list_title = $atts['list_title']; } ob_start(); // Prints the template's CSS file. do_action('cn_template_inline_css-' . $template->getSlug(), $atts); $out .= ob_get_contents(); ob_end_clean(); $out .= '<div class="connections-list cn-upcoming cn-' . $atts['list_type'] . '" id="cn-list" data-connections-version="' . $connections->options->getVersion() . '-' . $connections->options->getDBVersion() . '">' . "\n"; $out .= "\n" . '<div class="cn-template cn-' . $template->getSlug() . '" id="cn-' . $template->getSlug() . '">' . "\n"; $out .= "\n" . '<div class="cn-clear" id="cn-list-head">' . "\n"; if ($atts['show_title']) { $out .= '<div class="cn-upcoming-title">' . $list_title . '</div>'; } $out .= "\n" . '</div>' . "\n"; $out .= '<div class="cn-clear" id="cn-list-body">' . "\n"; foreach ($results as $row) { $entry = new cnvCard($row); $vCard =& $entry; // Configure the page where the entry link to. $entry->directoryHome(array('page_id' => $atts['home_id'], 'force_home' => $atts['force_home'])); if (!$atts['show_lastname']) { $entry->setLastName(''); } $entry->name = $entry->getName(array('format' => $atts['name_format'])); $alternate == '' ? $alternate = '-alternate' : ($alternate = ''); $out .= '<div class="cn-upcoming-row' . $alternate . ' vcard ' . '">' . "\n"; ob_start(); do_action('cn_action_card-' . $template->getSlug(), $entry, $template, $atts); $out .= ob_get_contents(); ob_end_clean(); $out .= '</div>' . "\n"; } $out .= "\n" . '</div>' . "\n"; $out .= "\n" . '<div class="cn-clear" id="cn-list-foot">' . "\n"; $out .= "\n" . '</div>' . "\n"; $out .= "\n" . '</div>' . "\n"; $out .= "\n" . '</div>' . "\n"; } if (cnSettingsAPI::get('connections', 'connections_compatibility', 'strip_rnt')) { $search = array("\r\n", "\r", "\n", "\t"); $replace = array('', '', '', ''); $out = str_replace($search, $replace, $out); } // Clear any filters that have been added. // This allows support using the shortcode multiple times on the same page. cnShortcode::clearFilterRegistry(); return $out; }
/** * Set's up the core shortcode and starts the shortcode * replacement process using the WordPress shortcode API. * * @access public * @since 0.8 * @static * @param array $atts The shortcode $atts array. * @param string $content The content of an enclosing shortcode tag. * @param array $results The cnRetrieve query results. * @param object $template An instance of the cnTemplate object. * * @return string */ private function __construct($atts, $content, $results, $template) { // Store the entry query array $results and the cnTemplate object $template // so they can be easily passed to the template part shortcode callbacks. $this->atts = $atts; $this->template = $template; $this->results = $results; $this->html = $this->do_shortcode(cnShortcode::removePBR($content)); }
/** * Callback for `the_content` filter. * * Checks for the `cn-entry-slug` query var and if it is set. Replace the post content with a shortcode to query * only the queried entry. * * NOTE: The Divi theme has a visual page layout builder which uses shortcodes to generate the layout. * So if Divi is the child or root theme, replace just the shortcode instance rather than the post contents. * BUG: If multiple instances of the shortcode are on the page, only the first instance will be replaced, * defeating the purpose of this code -- to only display the first instance on the shortcode. * Possible solution is to check for multiple matches and replace all but the initial match with an empty string. * * @access private * @since unknown * @since 8.5.21 Refactor to remove theme specific exclusion by remove all but the initial shortcode in the content * when viewing a single entry profile page. * @static * * @param string $content Post content. * * @return string */ public static function single($content) { //error_log( 'PRE-SINGLE: ' . $content ); $slug = cnQuery::getVar('cn-entry-slug'); $matches = self::find('connections', $content, 'matches'); if ($slug && $matches) { //$atts = shortcode_parse_atts( $matches[0][3] ); //$atts['slug'] = sanitize_title( $slug ); //$shortcode = self::write( 'connections', $atts ); // //$theme = wp_get_theme(); //$parent = $theme->parent(); // //if ( FALSE === $parent ) { // // $replace = in_array( $theme->get( 'Name' ), array( 'Divi', 'Enfold', 'Kleo' ), TRUE ) ? TRUE : FALSE; // //} elseif ( $parent instanceof WP_Theme ) { // // $replace = in_array( $parent->get( 'Name' ), array( 'Divi', 'Enfold', 'Kleo' ), TRUE ) ? TRUE : FALSE; // //} else { // // $replace = FALSE; //} // //if ( $replace ) { // // $content = str_replace( $matches[0][0], $shortcode, $content ); // //} else { // // $content = $shortcode; //} foreach ($matches as $key => $match) { // Remove all but the first shortcode from the post content. if (0 < $key) { $content = str_replace($match[0], '', $content); // Rewrite the shortcode, adding the entry slug to the shortcode. } else { $atts = shortcode_parse_atts($match[3]); $atts['slug'] = sanitize_title($slug); $shortcode = cnShortcode::write('connections', $atts); $content = str_replace($match[0], $shortcode, $content); } } } //error_log( 'POST-SINGLE: ' . $content ); return $content; }