/** * Ensure WPSEO header items are not added to internal Connections pages. * @todo Should add Connections related header items to mimic WPSEO. * * @access private * @since 8.1.1 * @return void */ function cn_remove_wpseo_head() { if (cnQuery::getVar('cn-entry-slug') || cnQuery::getVar('cn-cat-slug') || cnQuery::getVar('cn-cat')) { if (isset($GLOBALS['wpseo_front'])) { remove_action('wp_head', array($GLOBALS['wpseo_front'], 'head'), 1); } } }
/** * Initiate the template options using the option values. * * @access private * @since 3.0 * * @param array $atts The shortcode $atts array. * * @return array */ public static function initOptions($atts) { if (cnQuery::getVar('cn-entry-slug')) { /** * @var cnOutput $entry * @var array $option */ $options = cnSettingsAPI::get('connections_template', self::SLUG, 'single'); } else { /** * @var cnOutput $entry * @var array $option */ $options = cnSettingsAPI::get('connections_template', self::SLUG, 'card'); $style = array('background-color' => '#FFF', 'border' => $options['border_width'] . 'px solid ' . $options['border_color'], 'border-radius' => $options['border_radius'] . 'px', 'color' => '#000', 'margin' => '8px 0', 'padding' => '10px', 'position' => 'relative'); if (is_array($style)) { $atts = wp_parse_args($style, $atts); } } if (is_array($options)) { $atts = wp_parse_args($options, $atts); } return $atts; }
/** * Returns the current category being viewed. * * @access public * @since 8.5.18 * @static * * @return false|cnTerm_Object */ public static function getCurrent() { $current = FALSE; if (cnQuery::getVar('cn-cat-slug')) { $slug = explode('/', cnQuery::getVar('cn-cat-slug')); // If the category slug is a descendant, use the last slug from the URL for the query. $current = end($slug); } elseif ($catIDs = cnQuery::getVar('cn-cat')) { if (is_array($catIDs)) { // If value is a string, strip the white space and covert to an array. $catIDs = wp_parse_id_list($catIDs); // Use the first element $current = reset($catIDs); } else { $current = $catIDs; } } if (!empty($current)) { if (ctype_digit((string) $current)) { $field = 'id'; } else { $field = 'slug'; } $current = cnTerm::getBy($field, $current, 'category'); // cnTerm::getBy() can return NULL || an instance of WP_Error, so, lets check for that. if (!is_a($current, 'cnTerm_Object')) { $current = FALSE; } } return $current; }
/** * Add the the current Connections category description or entry bio excerpt as the page meta description. * * @access private * @since 0.7.8 * @static * * @uses cnQuery::getVar() * @uses esc_attr() * @uses strip_shortcodes() */ public static function metaDesc() { // Whether or not to filter the page title with the current directory location. if (!cnSettingsAPI::get('connections', 'seo_meta', 'page_desc')) { return; } $description = ''; if (cnQuery::getVar('cn-cat-slug')) { // If the category slug is a descendant, use the last slug from the URL for the query. $categorySlug = explode('/', cnQuery::getVar('cn-cat-slug')); if (isset($categorySlug[count($categorySlug) - 1])) { $categorySlug = $categorySlug[count($categorySlug) - 1]; } $term = cnTerm::getBy('slug', $categorySlug, 'category'); $category = new cnCategory($term); $description = $category->getExcerpt(array('length' => 160)); } if (cnQuery::getVar('cn-cat')) { if (is_array(cnQuery::getVar('cn-cat'))) { return; } $categoryID = cnQuery::getVar('cn-cat'); $term = cnTerm::getBy('id', $categoryID, 'category'); $category = new cnCategory($term); $description = $category->getExcerpt(array('length' => 160)); } if (cnQuery::getVar('cn-entry-slug')) { // Grab an instance of the Connections object. $instance = Connections_Directory(); $result = $instance->retrieve->entries(array('slug' => urldecode(cnQuery::getVar('cn-entry-slug')))); // Make sure an entry is returned and then echo the meta desc. if (!empty($result)) { $entry = new cnEntry($result[0]); $description = $entry->getExcerpt(array('length' => 160)); } } if (0 == strlen($description)) { return; } echo '<meta name="description" content="' . esc_attr(trim(strip_shortcodes(strip_tags(stripslashes($description))))) . '"/>' . "\n"; }
/** * The private recursive function to build the category link item. * * Accepted option for the $atts property are: * type (string) * show_empty (bool) Whether or not to display empty categories. * show_count (bool) Whether or not to display the category count. * * @param object $category A category object. * @param int $level The current category level. * @param int $depth The depth limit. * @param array $slug An array of the category slugs to be used to build the permalink. * @param array $atts * @return string */ private static function categoryLinkDescendant($category, $level, $depth, $slug, $atts) { /** * @var WP_Rewrite $wp_rewrite * @var connectionsLoad $connections */ global $wp_rewrite; $out = ''; $defaults = array('show_empty' => TRUE, 'show_count' => TRUE, 'exclude' => array(), 'force_home' => FALSE, 'home_id' => cnSettingsAPI::get('connections', 'connections_home_page', 'page_id')); $atts = wp_parse_args($atts, $defaults); // Do not show the excluded category as options. if (!empty($atts['exclude']) && in_array($category->term_id, $atts['exclude'])) { return $out; } if ($atts['show_empty'] || !empty($category->count) || !empty($category->children)) { $count = $atts['show_count'] ? ' (' . $category->count . ')' : ''; /* * Determine of pretty permalink is enabled. * If it is, add the category slug to the array which will be imploded to be used to build the URL. * If it is not, set the $slug to the category term ID. */ if ($wp_rewrite->using_permalinks()) { $slug[] = $category->slug; } else { $slug = array($category->slug); } /* * Get tge current category from the URL / query string. */ if (cnQuery::getVar('cn-cat-slug')) { // Category slug $queryCategorySlug = cnQuery::getVar('cn-cat-slug'); if (!empty($queryCategorySlug)) { // If the category slug is a descendant, use the last slug from the URL for the query. $queryCategorySlug = explode('/', $queryCategorySlug); if (isset($queryCategorySlug[count($queryCategorySlug) - 1])) { $currentCategory = $queryCategorySlug[count($queryCategorySlug) - 1]; } } } elseif (cnQuery::getVar('cn-cat')) { $currentCategory = cnQuery::getVar('cn-cat'); } else { $currentCategory = ''; } $out .= '<li class="cat-item cat-item-' . $category->term_id . ($currentCategory == $category->slug || $currentCategory == $category->term_id ? ' current-cat' : '') . ' cn-cat-parent">'; // Create the permalink anchor. $out .= cnURL::permalink(array('type' => 'category', 'slug' => implode('/', $slug), 'title' => $category->name, 'text' => $category->name . $count, 'home_id' => $atts['home_id'], 'force_home' => $atts['force_home'], 'return' => TRUE)); /* * Only show the descendants based on the following criteria: * - There are descendant categories. * - The descendant depth is < than the current $level * * When descendant depth is set to 0, show all descendants. * When descendant depth is set to < $level, call the recursive function. */ if (!empty($category->children) && ($depth <= 0 ? -1 : $level) < $depth) { $out .= '<ul class="children cn-cat-children">'; foreach ($category->children as $child) { $out .= self::categoryLinkDescendant($child, $level + 1, $depth, $slug, $atts); } $out .= '</ul>'; } $out .= '</li>'; } return $out; }
/** * Render the note informing the user that the category select drop down is for customization/display purposes only, * that is non-functional. * * @access private * @since 8.4 * * @uses cnTemplate_Customizer::supports() */ public function categoryMessage() { if ($this->supports('category-select') && !cnQuery::getVar('cn-entry-slug')) { ?> <div id="cn-customizer-messages"> <ul id="cn-customizer-message-list"> <li class="cn-customizer-message"><?php _e('<strong>NOTE:</strong> Category select is for customization purposes only. It will not filter the results.', 'connections'); ?> </li> </ul> </div> <?php } }
/** * Retrieve all connected logs. * * Used for retrieving logs related to particular items, such as a specific error. * * @access private * @since 8.2.10 * @static * * @uses wp_parse_args() * @uses get_posts() * @uses cnQuery::getVar() * @uses cnLog::valid() * * @param array $atts * * @return array|false An indexed array of logs or false if none were found. */ public static function getConnected($atts = array()) { $defaults = array('post_parent' => 0, 'post_type' => self::POST_TYPE, 'posts_per_page' => 20, 'post_status' => 'publish', 'paged' => cnQuery::getVar('paged'), 'type' => FALSE); $query = wp_parse_args($atts, $defaults); if ($query['type']) { if (is_array($query['type'])) { $types = array(); foreach ($query['type'] as $type) { if (self::valid($type)) { $types[] = $type; } } } else { $types = ''; if (self::valid($query['type'])) { $types = $query['type']; } } if (!empty($types)) { $query['tax_query'] = array(array('taxonomy' => self::TAXONOMY, 'field' => 'slug', 'terms' => $types)); } } $logs = get_posts($query); if ($logs) { return $logs; } // No logs found. return FALSE; }
/** * An indexed array of file names to be searched for. * * The file names is an index array of file names where the * highest priority is first and the lowest priority is last. * * @access public * @since 0.8 * @static * @uses apply_filters() * @uses cnQuery::getVar() * @param string $base The base file name. Typically `card` for a template file and the template slug for CSS and JS files. * @param string $name The template part name; such as `single` or `category`. * @param string $slug The template part slug; such as an entry slug or category slug. * @param string $ext [optional] The template file name extension. Defaults to `php`. * * @return array An indexed array of file names to search for. */ public static function fileNames($base, $name = NULL, $slug = NULL, $ext = 'php') { $files = array(); if (cnQuery::getVar('cn-cat')) { $categoryID = cnQuery::getVar('cn-cat'); // Since the `cn-cat` query var can be an array, we'll only add the category slug // template name when querying a single category. if (!is_array($categoryID)) { $term = cnTerm::getBy('id', $categoryID, 'category'); $files[] = self::fileName($base, 'category', $term->slug, $ext); } $files[] = self::fileName($base, 'category', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-cat-slug')) { $files[] = self::fileName($base, 'category', cnQuery::getVar('cn-cat-slug'), $ext); $files[] = self::fileName($base, 'category', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-country')) { $country = self::queryVarSlug(cnQuery::getVar('cn-country')); $files[] = self::fileName($base, 'country', $country, $ext); $files[] = self::fileName($base, 'country', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-region')) { $region = self::queryVarSlug(cnQuery::getVar('cn-region')); $files[] = self::fileName($base, 'region', $region, $ext); $files[] = self::fileName($base, 'region', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-postal-code')) { $zipcode = self::queryVarSlug(cnQuery::getVar('cn-postal-code')); $files[] = self::fileName($base, 'postal-code', $zipcode, $ext); $files[] = self::fileName($base, 'postal-code', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-locality')) { $locality = self::queryVarSlug(cnQuery::getVar('cn-locality')); $files[] = self::fileName($base, 'locality', $locality, $ext); $files[] = self::fileName($base, 'locality', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-organization')) { $organization = self::queryVarSlug(cnQuery::getVar('cn-organization')); $files[] = self::fileName($base, 'organization', $organization, $ext); $files[] = self::fileName($base, 'organization', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-department')) { $department = self::queryVarSlug(cnQuery::getVar('cn-department')); $files[] = self::fileName($base, 'department', $department, $ext); $files[] = self::fileName($base, 'department', NULL, $ext); // var_dump( $files ); } if (cnQuery::getVar('cn-entry-slug')) { $files[] = self::fileName($base, NULL, cnQuery::getVar('cn-entry-slug'), $ext); $files[] = self::fileName($base, 'single', NULL, $ext); // var_dump( $files ); } // If `$name` was supplied, add it to the files to search for. if (!is_null($name)) { $files[] = self::fileName($base, $name, NULL, $ext); } // Add the base as the least priority, since it is required. $files[] = self::fileName($base, NULL, NULL, $ext); /** * Allow template choices to be filtered. * * The resulting array should be in the order of most specific first, least specific last. * e.g. 0 => card-single.php, 1 => card.php */ $files = apply_filters('cn_locate_file_names', $files, $base, $name, $slug, $ext); // var_dump( $files ); // Sort the files based on priority ksort($files, SORT_NUMERIC); // var_dump( $files ); return array_filter($files); }
/** * Render an unordered list of categories. * * This is the Connections equivalent of @see wp_list_categories() in WordPress core ../wp-includes/category-template.php * * @access public * @since 8.1.6 * @static * * @uses wp_parse_args() * @uses cnTerm::getTaxonomyTerms() * @uses cnURL::permalink() * @uses Walker::walk() * * @param array $atts { * Optional. An array of arguments. * NOTE: Additionally, all valid options as supported in @see cnTerm::getTaxonomyTerms(). * * @type string $show_option_all A non-blank value causes the display of a link to the directory home page. * Default: ''. The default is not to display a link. * Accepts: Any valid string. * @type string $show_option_none Set the text to show when no categories are listed. * Default: 'No Categories' * Accepts: Any valid string. * @type bool $show_count Whether or not to display the category count. * Default: FALSE * @type int $depth Controls how many levels in the hierarchy of categories are to be included in the list. * Default: 0 * Accepts: 0 - All categories and child categories. * -1 - All Categories displayed flat, not showing the parent/child relationships. * 1 - Show only top level/root parent categories. * n - Value of n (int) specifies the depth (or level) to descend in displaying the categories. * @type string $taxonomy The taxonomy tree to display. * Default: 'category' * Accepts: Any registered taxonomy. * @type bool $return Whether or not to return or echo the resulting HTML. * Default: FALSE * } * * @return string */ public static function render($atts = array()) { $out = ''; $defaults = array('show_option_all' => '', 'show_option_none' => __('No categories', 'connections'), 'orderby' => 'name', 'order' => 'ASC', 'show_count' => FALSE, 'hide_empty' => FALSE, 'child_of' => 0, 'exclude' => array(), 'hierarchical' => TRUE, 'depth' => 0, 'parent_id' => array(), 'taxonomy' => 'category', 'force_home' => FALSE, 'home_id' => cnSettingsAPI::get('connections', 'connections_home_page', 'page_id'), 'return' => FALSE); $atts = wp_parse_args($atts, $defaults); $atts['parent_id'] = wp_parse_id_list($atts['parent_id']); $walker = new self(); if (empty($atts['parent_id'])) { $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('name' => ''))); } else { $terms = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('include' => $atts['parent_id'], 'child_of' => 0, 'name' => ''))); // If any of the `parent_id` is not a root parent (where $term->parent = 0) set it parent ID to `0` // so the term tree will be properly constructed. foreach ($terms as $term) { if (0 !== $term->parent) { $term->parent = 0; } } foreach ($atts['parent_id'] as $termID) { $children = cnTerm::getTaxonomyTerms($atts['taxonomy'], array_merge($atts, array('child_of' => $termID, 'name' => ''))); if (!is_wp_error($children)) { $terms = array_merge($terms, $children); } } } /** * Allows extensions to add/remove class names to the term tree list. * * @since 8.5.18 * * @param array $class The array of class names. * @param array $terms The array of terms. * @param array $atts The method attributes. */ $class = apply_filters('cn_term_list_class', array('cn-cat-tree'), $terms, $atts); $class = cnSanitize::htmlClass($class); $out .= '<ul class="' . cnFunction::escAttributeDeep($class) . '">' . PHP_EOL; if (empty($terms)) { $out .= '<li class="cat-item-none">' . $atts['show_option_none'] . '</li>'; } else { if (cnQuery::getVar('cn-cat-slug')) { $slug = explode('/', cnQuery::getVar('cn-cat-slug')); // If the category slug is a descendant, use the last slug from the URL for the query. $atts['current_category'] = end($slug); } elseif ($catIDs = cnQuery::getVar('cn-cat')) { if (is_array($catIDs)) { // If value is a string, strip the white space and covert to an array. $catIDs = wp_parse_id_list($catIDs); // Use the first element $atts['current_category'] = reset($catIDs); } else { $atts['current_category'] = $catIDs; } } else { $atts['current_category'] = 0; } if (!empty($atts['show_option_all'])) { $out .= '<li class="cat-item-all"><a href="' . cnURL::permalink(array('type' => 'home', 'data' => 'url', 'return' => TRUE)) . '">' . $atts['show_option_all'] . '</a></li>'; } $out .= $walker->walk($terms, $atts['depth'], $atts); } $out .= '</ul>' . PHP_EOL; if ($atts['return']) { return $out; } echo $out; }
/** * Checks the requested URL for Connections' query vars and if found rewrites the URL * and passes the new URL back to finish being processed by the redirect_canonical() function. * * Hooks into the redirect_canonical filter. * * @access private * @since 0.7.3.2 * @uses cnQuery::getVar() * @uses remove_query_arg() * @uses user_trailingslashit() * @param string $redirectURL * @param string $requestedURL * @return string */ public function canonicalRedirectFilter($redirectURL, $requestedURL) { $originalURL = $redirectURL; $parsedURL = @parse_url($requestedURL); $redirectURL = explode('?', $redirectURL); $redirectURL = $redirectURL[0]; // Ensure array index is set, prevent PHP error notice. if (!isset($parsedURL['query'])) { $parsedURL['query'] = ''; } // If paged, append pagination if (cnQuery::getVar('cn-pg')) { $page = (int) cnQuery::getVar('cn-pg'); $parsedURL['query'] = remove_query_arg('cn-pg', $parsedURL['query']); if ($page > 1 && !stripos($redirectURL, "pg/{$page}")) { $redirectURL .= user_trailingslashit("pg/{$page}", 'page'); } // var_dump( $redirectURL ); // exit(); } // Add back on to the URL any remaining query string values. $parsedURL['query'] = preg_replace('#^\\??&*?#', '', $parsedURL['query']); if ($redirectURL && !empty($parsedURL['query'])) { parse_str($parsedURL['query'], $_parsed_query); $_parsed_query = array_map('rawurlencode', $_parsed_query); $redirectURL = add_query_arg($_parsed_query, $redirectURL); } return $redirectURL; }
/** * Add the entry actions to the admin bar * * @access private * @static * @since 8.2 * @uses cnURL::permalink() * @uses current_user_can() * @param $admin_bar object * * @return void */ public static function adminBarMenuItems($admin_bar) { if (cnQuery::getVar('cn-entry-slug')) { // Grab an instance of the Connections object. $instance = Connections_Directory(); $entry = $instance->retrieve->entries(array('slug' => rawurldecode(cnQuery::getVar('cn-entry-slug')), 'status' => 'approved,pending')); // Make sure an entry is returned and if not, return $title unaltered. if (empty($entry)) { return; } // preg_match( '/href="(.*?)"/', cnURL::permalink( array( 'slug' => $entry->slug, 'return' => TRUE ) ), $matches ); // $permalink = $matches[1]; if (current_user_can('connections_manage') && current_user_can('connections_view_menu') && (current_user_can('connections_edit_entry_moderated') || current_user_can('connections_edit_entry'))) { $admin_bar->add_node(array('parent' => FALSE, 'id' => 'cn-edit-entry', 'title' => __('Edit Entry', 'connections'), 'href' => admin_url(wp_nonce_url('admin.php?page=connections_manage&cn-action=edit_entry&id=' . $entry[0]->id, 'entry_edit_' . $entry[0]->id)), 'meta' => array('title' => __('Edit Entry', 'connections')))); } } }
/** * Parses a TimThumb compatible URL via the CN_IMAGE_ENDPOINT root rewrite endpoint * and stream the image to the browser with the correct headers for the image type being served. * * Streams an image resource to the browser or a error message. * * @access private * @since 8.1 * @static * * @uses cnQuery::getVar() * @uses path_is_absolute() * @uses cnColor::rgb2hex2rgb() * @uses self::get() * @uses is_wp_error() */ public static function query() { /** @var wpdb $wpdb */ global $wpdb; if (cnQuery::getVar(CN_IMAGE_ENDPOINT)) { if (path_is_absolute(cnQuery::getVar('src'))) { header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); echo '<h1>ERROR/s:</h1><ul><li>Source is file path. Source must be a local file URL.</li></ul>'; exit; } $atts = array(); if (cnQuery::getVar('cn-entry-slug')) { $sql = $wpdb->prepare('SELECT slug FROM ' . CN_ENTRY_TABLE . ' WHERE slug=%s', cnQuery::getVar('cn-entry-slug')); $result = $wpdb->get_var($sql); if (is_null($result)) { header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); echo '<h1>ERROR/s:</h1><ul><li>Cheating?</li></ul>'; exit; } else { $atts['sub_dir'] = $result; } } if (cnQuery::getVar('w')) { $atts['width'] = cnQuery::getVar('w'); } if (cnQuery::getVar('h')) { $atts['height'] = cnQuery::getVar('h'); } if (cnQuery::getVar('zc') || cnQuery::getVar('zc') === '0') { $atts['crop_mode'] = cnQuery::getVar('zc'); } if (cnQuery::getVar('a')) { $atts['crop_focus'] = array('center', 'center'); if (strpos(cnQuery::getVar('a'), 't') !== FALSE) { $atts['crop_focus'][1] = 'top'; } if (strpos(cnQuery::getVar('a'), 'r') !== FALSE) { $atts['crop_focus'][0] = 'right'; } if (strpos(cnQuery::getVar('a'), 'b') !== FALSE) { $atts['crop_focus'][1] = 'bottom'; } if (strpos(cnQuery::getVar('a'), 'l') !== FALSE) { $atts['crop_focus'][0] = 'left'; } $atts['crop_focus'] = implode(',', $atts['crop_focus']); } if (cnQuery::getVar('f')) { $filters = explode('|', cnQuery::getVar('f')); foreach ($filters as $filter) { $param = explode(',', $filter); for ($i = 0; $i < 4; $i++) { if (!isset($param[$i])) { $param[$i] = NULL; } else { $param[$i] = $param[$i]; } } switch ($param[0]) { case '1': $atts['negate'] = TRUE; break; case '2': $atts['grayscale'] = TRUE; break; case '3': $atts['brightness'] = $param[1]; break; case '4': $atts['contrast'] = $param[1]; break; case '5': $atts['colorize'] = cnColor::rgb2hex2rgb($param[1] . ',' . $param[2] . ',' . $param[3]); break; case '6': $atts['detect_edges'] = TRUE; break; case '7': $atts['emboss'] = TRUE; break; case '8': $atts['gaussian_blur'] = TRUE; break; case '9': $atts['blur'] = TRUE; break; case '10': $atts['sketchy'] = TRUE; break; case '11': $atts['smooth'] = $param[1]; } } } if (cnQuery::getVar('s') && cnQuery::getVar('s') === '1') { $atts['sharpen'] = TRUE; } if (cnQuery::getVar('o')) { $atts['opacity'] = cnQuery::getVar('o'); } if (cnQuery::getVar('q')) { $atts['quality'] = cnQuery::getVar('q'); } if (cnQuery::getVar('cc')) { $atts['canvas_color'] = cnQuery::getVar('cc'); } // This needs to be set after the `cc` query var because it should override any value set using the `cc` query var, just like TimThumb. if (cnQuery::getVar('ct') && cnQuery::getVar('ct') === '1') { $atts['canvas_color'] = 'transparent'; } // Process the image. $image = self::get(cnQuery::getVar('src'), $atts, 'editor'); // If there been an error if (is_wp_error($image)) { $errors = implode('</li><li>', $image->get_error_messages()); // Display the error messages. header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); echo '<h1>ERROR/s:</h1><ul><li>' . wp_kses_post($errors) . '</li></ul>'; exit; } // Ensure a stream quality is set otherwise we get a block mess as an image when serving a cached image. $quality = cnQuery::getVar('q') ? cnQuery::getVar('q') : 90; // Ensure valid value for $quality. If invalid valid is supplied reset to the default of 90, matching WP core. if (filter_var((int) $quality, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 100))) === FALSE) { $quality = 90; } $image->set_quality($quality); $image->stream(); exit; } }
/** * * The $atts['meta_query'] can have two different structures when passed to * @see cnMeta_Query::parse_query_vars(), they are: * * array( * 'meta_key' => (string), * 'meta_value => (string|array), * 'meta_type => (string), * 'meta_compare' => (string) * ) * * OR * * array( * 'meta_query' => * array( * 'key' => (string), * 'value' => (string|array), * 'compare' => (string), * 'type' => (string) * ), * [array(...),] * ) * * The later, 'meta_query', can have multiple arrays. * * @access public * @since unknown * @version 1.0 * @param array * @return array */ public function entries($atts = array()) { /** @var $wpdb wpdb */ global $wpdb; // Grab an instance of the Connections object. $instance = Connections_Directory(); $select[] = CN_ENTRY_TABLE . '.*'; $from[] = CN_ENTRY_TABLE; $join = array(); $where[] = 'WHERE 1=1'; $having = array(); $orderBy = array(); $random = FALSE; $visibility = array(); /* * // START -- Set the default attributes array. \\ */ $defaults['list_type'] = NULL; $defaults['category'] = NULL; $defaults['category_in'] = NULL; $defaults['exclude_category'] = NULL; $defaults['category_name'] = NULL; $defaults['category_slug'] = NULL; $defaults['wp_current_category'] = FALSE; $defaults['char'] = ''; $defaults['id'] = NULL; $defaults['slug'] = NULL; $defaults['family_name'] = NULL; $defaults['last_name'] = NULL; $defaults['title'] = NULL; $defaults['organization'] = NULL; $defaults['department'] = NULL; $defaults['district'] = NULL; $defaults['county'] = NULL; $defaults['city'] = NULL; $defaults['state'] = NULL; $defaults['zip_code'] = NULL; $defaults['country'] = NULL; $defaults['visibility'] = NULL; $defaults['status'] = array('approved'); $defaults['order_by'] = array('sort_column', 'last_name', 'first_name'); $defaults['limit'] = NULL; $defaults['offset'] = 0; $defaults['meta_query'] = array(); $defaults['allow_public_override'] = FALSE; $defaults['private_override'] = FALSE; $defaults['search_terms'] = NULL; // $atts vars to support showing entries within a specified radius. $defaults['near_addr'] = NULL; $defaults['latitude'] = NULL; $defaults['longitude'] = NULL; $defaults['radius'] = 10; $defaults['unit'] = 'mi'; $defaults['lock'] = FALSE; $atts = cnSanitize::args($atts, $defaults); /* * // END -- Set the default attributes array if not supplied. \\ */ /* * // START -- Process the query vars. \\ * NOTE: these will override any values supplied via $atts, which include via the shortcode. */ if ((defined('DOING_AJAX') && DOING_AJAX || !is_admin()) && !$atts['lock']) { // Category slug $queryCategorySlug = cnQuery::getVar('cn-cat-slug'); if (!empty($queryCategorySlug)) { // If the category slug is a descendant, use the last slug from the URL for the query. $queryCategorySlug = explode('/', $queryCategorySlug); if (isset($queryCategorySlug[count($queryCategorySlug) - 1])) { $atts['category_slug'] = $queryCategorySlug[count($queryCategorySlug) - 1]; } } // Category ID $queryCategoryID = cnQuery::getVar('cn-cat'); if (!empty($queryCategoryID)) { $atts['category'] = $queryCategoryID; } // Category in $queryCategoryIn = cnQuery::getVar('cn-cat-in'); if (!empty($queryCategoryIn)) { $atts['category_in'] = cnQuery::getVar('cn-cat-in'); } // Country $queryCountry = cnQuery::getVar('cn-country'); if (!empty($queryCountry)) { $atts['country'] = urldecode($queryCountry); } // Postal Code $queryPostalCode = cnQuery::getVar('cn-postal-code'); if (!empty($queryPostalCode)) { $atts['zip_code'] = urldecode($queryPostalCode); } // Region [State] $queryRegion = cnQuery::getVar('cn-region'); if (!empty($queryRegion)) { $atts['state'] = urldecode($queryRegion); } // Locality [City] $queryLocality = cnQuery::getVar('cn-locality'); if (!empty($queryLocality)) { $atts['city'] = urldecode($queryLocality); } // County $queryCounty = cnQuery::getVar('cn-county'); if (!empty($queryCounty)) { $atts['county'] = urldecode($queryCounty); } // District $queryDistrict = cnQuery::getVar('cn-district'); if (!empty($queryDistrict)) { $atts['district'] = urldecode($queryDistrict); } // Organization $queryOrganization = cnQuery::getVar('cn-organization'); if (!empty($queryOrganization)) { $atts['organization'] = urldecode($queryOrganization); } // Department $queryDeparment = cnQuery::getVar('cn-department'); if (!empty($queryDeparment)) { $atts['department'] = urldecode($queryDeparment); } // Entry slug // NOTE: The entry slug is saved in the DB URL encoded, so there's no need to urldecode(). $queryEntrySlug = cnQuery::getVar('cn-entry-slug'); if (!empty($queryEntrySlug)) { $atts['slug'] = $queryEntrySlug; } // Initial character. $queryInitialChar = cnQuery::getVar('cn-char'); if (!empty($queryInitialChar)) { $atts['char'] = urldecode($queryInitialChar); } // Pagination $queryPage = cnQuery::getVar('cn-pg'); $atts['offset'] = !empty($queryPage) ? ($queryPage - 1) * $atts['limit'] : $atts['offset']; $atts['offset'] = $atts['offset'] > 0 ? $atts['offset'] : NULL; // Search terms $searchTerms = cnQuery::getVar('cn-s'); if (!empty($searchTerms)) { $atts['search_terms'] = $searchTerms; } // Geo-location $queryCoord = cnQuery::getVar('cn-near-coord'); if (!empty($queryCoord)) { $queryCoord = explode(',', $queryCoord); $atts['latitude'] = $wpdb->prepare('%f', $queryCoord[0]); $atts['longitude'] = $wpdb->prepare('%f', $queryCoord[1]); // Get the radius, otherwise the default of 10. if (cnQuery::getVar('cn-radius')) { $atts['radius'] = $wpdb->prepare('%d', cnQuery::getVar('cn-radius')); } // Sanitize and set the unit. $atts['unit'] = cnQuery::getVar('cn-unit') ? sanitize_key(cnQuery::getVar('cn-unit')) : sanitize_key($atts['unit']); } } /* * // END -- Process the query vars. \\ */ /* * // START -- Reset some of the $atts based if category_slug or entry slug * is being used. This is done to prevent query conflicts. This should be safe because * if a specific entry or category is being queried the other $atts can be discarded. * This has to be done in order to reconcile values passed via the shortcode and the * query string values. * * @TODO I know there are more scenarios to consider ... but this is all I can think of at the moment. * Thought --- maybe resetting to the default $atts should be done --- something to consider. */ if (!empty($atts['slug']) || !empty($atts['category_slug'])) { $atts['list_type'] = NULL; $atts['category'] = NULL; $atts['category_in'] = NULL; $atts['category_exclude'] = NULL; $atts['wp_current_category'] = NULL; } if (!empty($atts['slug'])) { $atts['near_addr'] = NULL; $atts['latitude'] = NULL; $atts['longitude'] = NULL; $atts['radius'] = 10; $atts['unit'] = 'mi'; } /* * // END -- Reset. */ /* * If in a post get the category names assigned to the post. */ if ($atts['wp_current_category'] && !is_page()) { // Get the current post categories. $wpCategories = get_the_category(); // Build an array of the post categories. foreach ($wpCategories as $wpCategory) { $categoryNames[] = $wpdb->prepare('%s', $wpCategory->cat_name); } } /* * Build and array of the supplied category names and their children. */ if (!empty($atts['category_name'])) { cnFunction::parseStringList($atts['category_name'], ','); foreach ($atts['category_name'] as $categoryName) { // Add the parent category to the array and remove any whitespace from the beginning/end of the name just in case the user added it when using the shortcode. $categoryNames[] = $wpdb->prepare('%s', htmlspecialchars($categoryName)); // Retrieve the children categories $results = $this->categoryChildren('name', $categoryName); foreach ((array) $results as $term) { // Only add the name if it doesn't already exist. If it doesn't sanitize and add to the array. if (!in_array($term->name, $categoryNames)) { $categoryNames[] = $wpdb->prepare('%s', $term->name); } } } } /* * Build and array of the supplied category slugs and their children. */ if (!empty($atts['category_slug'])) { $categorySlugs = array(); cnFunction::parseStringList($atts['category_slug'], ','); foreach ($atts['category_slug'] as $categorySlug) { // Add the parent category to the array and remove any whitespace from the beginning/end of the name in case the user added it when using the shortcode. $categorySlugs[] = sanitize_title($categorySlug); // Retrieve the children categories. $results = $this->categoryChildren('slug', $categorySlug); foreach ((array) $results as $term) { // Only add the slug if it doesn't already exist. If it doesn't sanitize and add to the array. if (!in_array($term->name, $categorySlugs)) { $categorySlugs[] = sanitize_title($term->slug); } } } } /* * Build an array of all the categories and their children based on the supplied category IDs. */ if (!empty($atts['category'])) { $categoryIDs = array(); $atts['category'] = wp_parse_id_list($atts['category']); foreach ($atts['category'] as $categoryID) { // Add the parent category ID to the array. $categoryIDs[] = absint($categoryID); // Retrieve the children categories $termChildren = cnTerm::children($categoryID, 'category'); if (!is_a($termChildren, 'WP_Error') && !empty($termChildren)) { $categoryIDs = array_merge($categoryIDs, $termChildren); } } } /* * Exclude the specified categories by ID. */ if (!empty($atts['exclude_category'])) { if (!isset($categoryIDs)) { $categoryIDs = array(); } $categoryExcludeIDs = array(); $atts['exclude_category'] = wp_parse_id_list($atts['exclude_category']); $categoryIDs = array_diff($categoryIDs, $atts['exclude_category']); foreach ($atts['exclude_category'] as $categoryID) { // Add the parent category ID to the array. $categoryExcludeIDs[] = absint($categoryID); // Retrieve the children categories $termChildren = cnTerm::children($categoryID, 'category'); if (!is_a($termChildren, 'WP_Error') && !empty($termChildren)) { $categoryExcludeIDs = array_merge($categoryExcludeIDs, $termChildren); } } $atts['exclude_category'] = array_unique($categoryExcludeIDs); $sql = 'SELECT tr.entry_id FROM ' . CN_TERM_RELATIONSHIP_TABLE . ' AS tr INNER JOIN ' . CN_TERM_TAXONOMY_TABLE . ' AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE 1=1 AND tt.term_id IN (' . implode(", ", $atts['exclude_category']) . ')'; // Store the entryIDs that are to be excluded. $results = $wpdb->get_col($sql); //print_r($results); if (!empty($results)) { $where[] = 'AND ' . CN_ENTRY_TABLE . '.id NOT IN (' . implode(", ", $results) . ')'; } } // Convert the supplied category IDs $atts['category_in'] to an array. if (!empty($atts['category_in'])) { $atts['category_in'] = wp_parse_id_list($atts['category_in']); // Remove empty values from the array. $atts['category_in'] = array_filter($atts['category_in']); // Ensure there is something to query after filtering the array. if (!empty($atts['category_in'])) { // Exclude any category IDs that may have been set. $atts['category_in'] = array_diff($atts['category_in'], (array) $atts['exclude_category']); // Build the query to retrieve entry IDs that are assigned to all the supplied category IDs; operational AND. $sql = 'SELECT DISTINCT tr.entry_id FROM ' . CN_TERM_RELATIONSHIP_TABLE . ' AS tr INNER JOIN ' . CN_TERM_TAXONOMY_TABLE . ' AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE 1=1 AND tt.term_id IN (' . implode(", ", $atts['category_in']) . ') GROUP BY tr.entry_id HAVING COUNT(*) = ' . count($atts['category_in']) . ' ORDER BY tr.entry_id'; // Store the entryIDs that exist on all of the supplied category IDs $results = $wpdb->get_col($sql); //print_r($results); if (!empty($results)) { $where[] = 'AND ' . CN_ENTRY_TABLE . '.id IN (' . implode(", ", $results) . ')'; } else { $where[] = 'AND 1=2'; } } /* * This is the query to use to return entry IDs that are in the same categories. The COUNT value * should equal the number of category IDs in the IN() statement. SELECT DISTINCT tr.entry_id FROM `wp_connections_term_relationships` AS tr INNER JOIN `wp_connections_term_taxonomy` AS tt ON (tr.term_taxonomy_id = tt.term_taxonomy_id) WHERE 1=1 AND tt.term_id IN ('51','73','76') GROUP BY tr.entry_id HAVING COUNT(*) = 3 ORDER BY tr.entry_id */ } if (!empty($categoryIDs) || !empty($categoryExcludeIDs) || !empty($categoryNames) || !empty($categorySlugs)) { // Set the query string to INNER JOIN the term relationship and taxonomy tables. $join[] = 'INNER JOIN ' . CN_TERM_RELATIONSHIP_TABLE . ' ON ( ' . CN_ENTRY_TABLE . '.id = ' . CN_TERM_RELATIONSHIP_TABLE . '.entry_id )'; $join[] = 'INNER JOIN ' . CN_TERM_TAXONOMY_TABLE . ' ON ( ' . CN_TERM_RELATIONSHIP_TABLE . '.term_taxonomy_id = ' . CN_TERM_TAXONOMY_TABLE . '.term_taxonomy_id )'; $join[] = 'INNER JOIN ' . CN_TERMS_TABLE . ' ON ( ' . CN_TERMS_TABLE . '.term_id = ' . CN_TERM_TAXONOMY_TABLE . '.term_id )'; // Set the query string to return entries within the category taxonomy. $where[] = 'AND ' . CN_TERM_TAXONOMY_TABLE . '.taxonomy = \'category\''; if (!empty($categoryIDs)) { $where[] = 'AND ' . CN_TERM_TAXONOMY_TABLE . '.term_id IN (' . implode(", ", $categoryIDs) . ')'; unset($categoryIDs); } if (!empty($categoryExcludeIDs)) { $where[] = 'AND ' . CN_TERM_TAXONOMY_TABLE . '.term_id NOT IN (' . implode(", ", $categoryExcludeIDs) . ')'; unset($categoryExcludeIDs); } if (!empty($categoryNames)) { $where[] = 'AND ' . CN_TERMS_TABLE . '.name IN (' . implode(", ", (array) $categoryNames) . ')'; unset($categoryNames); } if (!empty($categorySlugs)) { $where[] = 'AND ' . CN_TERMS_TABLE . '.slug IN (\'' . implode("', '", (array) $categorySlugs) . '\')'; unset($categorySlugs); } } /* * // START --> Set up the query to only return the entries that match the supplied IDs. * NOTE: This includes the entry IDs returned for category_in. */ if (!empty($atts['id'])) { $atts['id'] = wp_parse_id_list($atts['id']); // Set query string to return specific entries. $where[] = 'AND ' . CN_ENTRY_TABLE . '.id IN (\'' . implode("', '", $atts['id']) . '\')'; } /* * // END --> Set up the query to only return the entries that match the supplied IDs. */ /* * // START --> Set up the query to only return the entries that match the supplied search terms. */ if (!empty($atts['search_terms'])) { $searchResults = $this->search(array('terms' => $atts['search_terms'])); //print_r($searchResults); // If there were no results, add a WHERE clause that will not return results when performing the whole query. if (empty($searchResults)) { $where[] = 'AND 1=2'; } else { // Set $atts['order_by'] to the order the entry IDs were returned. // This is to support the relevancy order results being returned by self::search(). $atts['order_by'] = 'id|SPECIFIED'; $atts['id'] = $searchResults; // Set the entry IDs to be the search results. $where[] = 'AND ' . CN_ENTRY_TABLE . '.id IN (\'' . implode("', '", $searchResults) . '\')'; } } /* * // END --> Set up the query to only return the entries that match the supplied search terms. */ /* * // START --> Set up the query to only return the entry that matches the supplied slug. */ if (!empty($atts['slug'])) { // Trim the white space from the ends. $atts['slug'] = trim($atts['slug']); $where[] = $wpdb->prepare('AND ' . CN_ENTRY_TABLE . '.slug = %s', $atts['slug']); } /* * // END --> Set up the query to only return the entry that matches the supplied slug. */ /* * // START --> Set up the query to only return the entries that match the supplied entry type. */ if (!empty($atts['list_type'])) { cnFunction::parseStringList($atts['list_type'], ','); $permittedEntryTypes = array('individual', 'organization', 'family', 'connection_group'); // Set query string for entry type. if ((bool) array_intersect($atts['list_type'], $permittedEntryTypes)) { // Compatibility code to make sure any occurrences of the deprecated entry type connections_group // is changed to entry type family $atts['list_type'] = str_replace('connection_group', 'family', $atts['list_type']); $where[] = cnQuery::where(array('field' => 'entry_type', 'value' => $atts['list_type'])); } } /* * // END --> Set up the query to only return the entries that match the supplied entry type. */ /* * // START --> Set up the query to only return the entries that match the supplied filters. */ $where[] = cnQuery::where(array('field' => 'family_name', 'value' => $atts['family_name'])); $where[] = cnQuery::where(array('field' => 'last_name', 'value' => $atts['last_name'])); $where[] = cnQuery::where(array('field' => 'title', 'value' => $atts['title'])); $where[] = cnQuery::where(array('field' => 'organization', 'value' => $atts['organization'])); $where[] = cnQuery::where(array('field' => 'department', 'value' => $atts['department'])); if (!empty($atts['district']) || !empty($atts['county']) || !empty($atts['city']) || !empty($atts['state']) || !empty($atts['zip_code']) || !empty($atts['country'])) { if (!isset($join['address'])) { $join['address'] = 'INNER JOIN ' . CN_ENTRY_ADDRESS_TABLE . ' ON ( ' . CN_ENTRY_TABLE . '.id = ' . CN_ENTRY_ADDRESS_TABLE . '.entry_id )'; } $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'district', 'value' => $atts['district'])); $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'county', 'value' => $atts['county'])); $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'city', 'value' => $atts['city'])); $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'state', 'value' => $atts['state'])); $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'zipcode', 'value' => $atts['zip_code'])); $where[] = cnQuery::where(array('table' => CN_ENTRY_ADDRESS_TABLE, 'field' => 'country', 'value' => $atts['country'])); } if (0 < strlen($atts['char'])) { $having[] = $wpdb->prepare('HAVING sort_column LIKE %s', $wpdb->esc_like($atts['char']) . '%'); } /* * // END --> Set up the query to only return the entries that match the supplied filters. */ /* * // START --> Set up the query to only return the entries based on user permissions. */ $where = self::setQueryVisibility($where, $atts); /* * // END --> Set up the query to only return the entries based on user permissions. */ /* * // START --> Set up the query to only return the entries based on status. */ $where = self::setQueryStatus($where, $atts); /* * // END --> Set up the query to only return the entries based on status. */ /* * // START --> Geo-limit the query. */ //$atts['latitude'] = 40.3663671; //$atts['longitude'] = -75.8876941; if (!empty($atts['latitude']) && !empty($atts['longitude'])) { $earthRadius = 6371; // Earth's radius in (SI) km. // Convert the supplied radius from the supplied unit to (SI) km. $atts['radius'] = cnGeo::convert(array('value' => $atts['radius'], 'from' => $atts['unit'], 'to' => 'km', 'format' => FALSE, 'return' => TRUE)); // Limiting bounding box (in degrees). $minLat = $atts['latitude'] - rad2deg($atts['radius'] / $earthRadius); $maxLat = $atts['latitude'] + rad2deg($atts['radius'] / $earthRadius); $minLng = $atts['longitude'] - rad2deg($atts['radius'] / $earthRadius / cos(deg2rad($atts['latitude']))); $maxLng = $atts['longitude'] + rad2deg($atts['radius'] / $earthRadius / cos(deg2rad($atts['latitude']))); // Convert origin of geographic circle to radians. $atts['latitude'] = deg2rad($atts['latitude']); $atts['longitude'] = deg2rad($atts['longitude']); // Add the SELECT statement that adds the `radius` column. $select[] = $wpdb->prepare('acos(sin(%f)*sin(radians(latitude)) + cos(%f)*cos(radians(latitude))*cos(radians(longitude)-%f))*6371 AS distance', $atts['latitude'], $atts['latitude'], $atts['longitude']); // Create a subquery that will limit the rows that have the cosine law applied to within the bounding box. $geoSubselect = $wpdb->prepare('(SELECT entry_id FROM ' . CN_ENTRY_ADDRESS_TABLE . ' WHERE latitude>%f AND latitude<%f AND longitude>%f AND longitude<%f) AS geo_bound', $minLat, $maxLat, $minLng, $maxLng); // The subquery needs to be added to the beginning of the array so the inner joins on the other tables are joined to the CN_ENTRY_TABLE array_unshift($from, $geoSubselect); // Add the JOIN for the address table. NOTE: This JOIN is also set in the ORDER BY section. The 'address' index is to make sure it doea not get added to the query twice. if (!isset($join['address'])) { $join['address'] = 'INNER JOIN ' . CN_ENTRY_ADDRESS_TABLE . ' ON ( ' . CN_ENTRY_TABLE . '.id = ' . CN_ENTRY_ADDRESS_TABLE . '.entry_id )'; } // Add the WHERE statement to limit the query to a geographic circle per the defined radius. $where[] = $wpdb->prepare('AND acos(sin(%f)*sin(radians(latitude)) + cos(%f)*cos(radians(latitude))*cos(radians(longitude)-%f))*6371 < %f', $atts['latitude'], $atts['latitude'], $atts['longitude'], $atts['radius']); // This is required otherwise addresses the user may not have permissions to view will be included in the query // which could be confusing since entries could appear to be outside of the search radius when in fact the entry // is within the search radius, it is just the address used to determine that is not viewable to the user. //$where[] = 'AND ' . CN_ENTRY_ADDRESS_TABLE . '.visibility IN (\'' . implode( "', '", (array) $visibility ) . '\')'; $where = self::setQueryVisibility($where, array('table' => CN_ENTRY_ADDRESS_TABLE)); // Temporarily set the sort order to 'radius' for testing. //$atts['order_by'] = array('radius'); } /* * // END --> Geo-limit the query. */ /* * // START --> Build the ORDER BY query segment. */ //if ( empty($atts['order_by']) ) //{ // Setup the default sort order if none were supplied. //$orderBy = array('sort_column', 'last_name', 'first_name'); //} //else //{ $orderFields = array('id', 'date_added', 'date_modified', 'first_name', 'last_name', 'title', 'organization', 'department', 'city', 'state', 'zipcode', 'country', 'birthday', 'anniversary', 'sort_column'); $orderFlags = array('SPECIFIED' => 'SPECIFIED', 'RANDOM' => 'RANDOM', 'ASC' => 'ASC', 'SORT_ASC' => 'ASC', 'DESC' => 'DESC', 'SORT_DESC' => 'DESC', 'NUMERIC' => '+0', 'SORT_NUMERIC' => '+0', 'SORT_NUMERIC_ASC' => '+0', 'NUMERIC_DESC' => '+0 DESC', 'SORT_NUMERIC_DESC' => '+0 DESC'); // If a geo-bound query is being performed the `radius` order field can be used. if (!empty($atts['latitude']) && !empty($atts['longitude'])) { array_push($orderFields, 'distance'); } // Get registered date types. $dateTypes = array_keys($instance->options->getDateOptions()); // Convert to an array if (!is_array($atts['order_by'])) { // Trim the space characters if present. $atts['order_by'] = str_replace(' ', '', $atts['order_by']); // Build an array of each field to order by and its sort order. $atts['order_by'] = explode(',', $atts['order_by']); } // For each field the sort order can be defined. foreach ($atts['order_by'] as $orderByField) { $orderByAtts[] = explode('|', $orderByField); } // Build the ORDER BY query segment foreach ($orderByAtts as $field) { // Trim any spaces the user may have supplied and set it to be lowercase. $field[0] = strtolower(trim($field[0])); // Check to make sure the supplied field is one of the valid fields to order by. if (in_array($field[0], $orderFields) || cnString::startsWith('meta_key:', $field[0])) { // The date_modified actually maps to the `ts` column in the db. if ($field[0] == 'date_modified') { $field[0] = 'ts'; } // If one of the order fields is an address region add the INNER JOIN to the CN_ENTRY_ADDRESS_TABLE if ($field[0] == 'city' || $field[0] == 'state' || $field[0] == 'zipcode' || $field[0] == 'country') { if (!isset($join['address'])) { $join['address'] = 'INNER JOIN ' . CN_ENTRY_ADDRESS_TABLE . ' ON ( ' . CN_ENTRY_TABLE . '.id = ' . CN_ENTRY_ADDRESS_TABLE . '.entry_id )'; } } if (cnString::startsWith('meta_key:', $field[0])) { // Extract the meta key name from $field[0]. $meta = explode(':', $field[0]); // Ensure the meta key does exist and is not empty before altering the query. if (isset($meta[1]) && !empty($meta[1])) { $atts['meta_query']['meta_query'][] = array('key' => $meta[1]); if (1 < count($atts['meta_query']['meta_query'])) { $field[0] = 'mt' . (count($atts['meta_query']['meta_query']) - 1) . '.meta_value'; } else { $field[0] = CN_ENTRY_TABLE_META . '.meta_value'; } } } // If we're ordering by anniversary or birthday, we need to convert the string to a UNIX timestamp so it is properly ordered. // Otherwise, it is sorted as a string which can give some very odd results compared to what is expected. //if ( $field[0] == 'anniversary' || $field[0] == 'birthday' ) { // // $field[0] = 'FROM_UNIXTIME( ' . $field[0] . ' )'; //} if (in_array($field[0], $dateTypes)) { if (!isset($join['date'])) { $join['date'] = 'INNER JOIN ' . CN_ENTRY_DATE_TABLE . ' ON ( ' . CN_ENTRY_TABLE . '.id = ' . CN_ENTRY_DATE_TABLE . '.entry_id )'; } $where[] = $wpdb->prepare('AND ' . CN_ENTRY_DATE_TABLE . '.type = %s', $field[0]); $field[0] = 'date'; } // Check to see if an order flag was set and is a valid order flag. if (isset($field[1])) { // Trim any spaces the user might have added and change the string to uppercase.. $field[1] = strtoupper(trim($field[1])); // If a user included a sort flag that is invalid/mis-spelled it is skipped since it can not be used. if (array_key_exists($field[1], $orderFlags)) { /* * The SPECIFIED and RANDOM order flags are special use and should only be used with the id sort field. * Set the default sort flag if it was use on any other sort field than id. */ if (($orderFlags[$field[1]] == 'SPECIFIED' || $orderFlags[$field[1]] == 'RANDOM') && $field[0] != 'id') { $field[1] = 'SORT_ASC'; } switch ($orderFlags[$field[1]]) { /* * Order the results based on the order of the supplied entry IDs */ case 'SPECIFIED': if (!empty($atts['id'])) { $orderBy = array('FIELD( ' . CN_ENTRY_TABLE . '.id, ' . implode(', ', (array) $atts['id']) . ' )'); } break; /* * Randomize the order of the results. */ /* * Randomize the order of the results. */ case 'RANDOM': $random = TRUE; break; /* * Return the results in ASC or DESC order. */ /* * Return the results in ASC or DESC order. */ default: $orderBy[] = $field[0] . ' ' . $orderFlags[$field[1]]; break; } } else { $orderBy[] = $field[0]; } } else { $orderBy[] = $field[0]; } } } //} if (!empty($atts['meta_query'])) { $metaQuery = new cnMeta_Query(); $metaQuery->parse_query_vars($atts['meta_query']); $metaClause = $metaQuery->get_sql('entry', CN_ENTRY_TABLE, 'id'); $join['meta'] = $metaClause['join']; $where['meta'] = $metaClause['where']; } $orderBy = empty($orderBy) ? 'ORDER BY sort_column, last_name, first_name' : 'ORDER BY ' . implode(', ', $orderBy); /* * // END --> Build the ORDER BY query segment. */ /* * // START --> Set up the query LIMIT and OFFSET. */ $limit = empty($atts['limit']) ? '' : $wpdb->prepare(' LIMIT %d ', $atts['limit']); $offset = empty($atts['offset']) ? '' : $wpdb->prepare(' OFFSET %d ', $atts['offset']); /* * // END --> Set up the query LIMIT and OFFSET. */ /* * // START --> Build the SELECT query segment. */ $select[] = 'CASE `entry_type` WHEN \'individual\' THEN `last_name` WHEN \'organization\' THEN `organization` WHEN \'connection_group\' THEN `family_name` WHEN \'family\' THEN `family_name` END AS `sort_column`'; /* * // END --> Build the SELECT query segment. */ $pieces = array('select', 'from', 'join', 'where', 'having', 'orderBy', 'limit', 'offset'); /** * Filter the query SQL clauses. * * @since 8.5.14 * * @param array $pieces Terms query SQL clauses. */ $clauses = apply_filters('cn_entry_query_clauses', compact($pieces)); foreach ($pieces as $piece) { ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : ''; } /** * NOTES: * * Many queries can produce multiple results per entry ID when we really only want it once. * For example an entry maybe return once for each category it is assigned or once for each * address an entry has that is within the search radius. * * Simply adding `GROUP BY CN_ENTRY_TABLE.id seems to fix this, but may be incorrect and might fail * on db/s other than MySQL such as Oracle. * * Very useful links that provide more details that require further study: * * @link http://www.psce.com/blog/2012/05/15/mysql-mistakes-do-you-use-group-by-correctly/ * @link http://rpbouman.blogspot.com/2007/05/debunking-group-by-myths.html */ if ($random) { $seed = cnFormatting::stripNonNumeric(cnUtility::getIP()) . date('Hdm', current_time('timestamp', 1)); $sql = 'SELECT SQL_CALC_FOUND_ROWS *, RAND(' . $seed . ') AS random FROM ( SELECT DISTINCT ' . implode(', ', $select) . ' FROM ' . implode(', ', $from) . ' ' . implode(' ', $join) . ' ' . implode(' ', $where) . ' GROUP BY ' . CN_ENTRY_TABLE . '.id ' . implode(' ', $having) . ') AS T ORDER BY random' . $limit . $offset; // print_r($sql); } else { $sql = 'SELECT SQL_CALC_FOUND_ROWS DISTINCT ' . implode(', ', $select) . ' FROM ' . implode(', ', $from) . ' ' . implode(' ', $join) . ' ' . implode(' ', $where) . ' GROUP BY ' . CN_ENTRY_TABLE . '.id ' . implode(' ', $having) . ' ' . $orderBy . ' ' . $limit . $offset; // print_r($sql); } if (!($results = $this->results($sql))) { $results = $wpdb->get_results($sql); $this->cache($sql, $results); // The most recent query to have been executed by cnRetrieve::entries $instance->lastQuery = $wpdb->last_query; // The most recent query error to have been generated by cnRetrieve::entries $instance->lastQueryError = $wpdb->last_error; // ID generated for an AUTO_INCREMENT column by the most recent INSERT query. $instance->lastInsertID = $wpdb->insert_id; // The number of rows returned by the last query. $instance->resultCount = $wpdb->num_rows; // The number of rows returned by the last query without the limit clause set $foundRows = $wpdb->get_results('SELECT FOUND_ROWS()'); $instance->resultCountNoLimit = $foundRows[0]->{'FOUND_ROWS()'}; $this->resultCountNoLimit = $foundRows[0]->{'FOUND_ROWS()'}; } // The total number of entries based on user permissions. // $instance->recordCount = self::recordCount( array( 'public_override' => $atts['allow_public_override'], 'private_override' => $atts['private_override'] ) ); // The total number of entries based on user permissions with the status set to 'pending' // $instance->recordCountPending = self::recordCount( array( 'public_override' => $atts['allow_public_override'], 'private_override' => $atts['private_override'], 'status' => array( 'pending' ) ) ); // The total number of entries based on user permissions with the status set to 'approved' // $instance->recordCountApproved = self::recordCount( array( 'public_override' => $atts['allow_public_override'], 'private_override' => $atts['private_override'], 'status' => array( 'approved' ) ) ); /* * ONLY in the admin. * * Reset the pagination filter for the current user, remove the offset from the query and re-run the * query if the offset for the query is greater than the record count with no limit set in the query. * */ if (is_admin() && $atts['offset'] > $instance->resultCountNoLimit) { $instance->currentUser->resetFilterPage('manage'); unset($atts['offset']); $results = $this->entries($atts); } elseif ($atts['offset'] > $instance->resultCountNoLimit) { /* * This is for the front end, reset the page and offset and re-run the query if the offset * is greater than the record count with no limit. * * @TODO this should somehow be precessed in the parse_request action hook so the URL * permalink and query vars can be properly updated. */ set_query_var('cn-pg', 0); $atts['offset'] = 0; $results = $this->entries($atts); } return $results; }
public static function download() { // Grab an instance of the Connections object. $instance = Connections_Directory(); $process = cnQuery::getVar('cn-process'); $token = cnQuery::getVar('cn-token'); $id = absint(cnQuery::getVar('cn-id')); if ('vcard' === $process) { $slug = cnQuery::getVar('cn-entry-slug'); //var_dump($slug); /* * If the token and id values were set, the link was likely from the admin. * Check for those values and validate the token. The primary reason for this * to be able to download vCards of entries that are set to "Unlisted". */ if (!empty($id) && !empty($token)) { if (!wp_verify_nonce($token, 'download_vcard_' . $id)) { wp_die('Invalid vCard Token'); } $entry = $instance->retrieve->entry($id); // Die if no entry was found. if (empty($entry)) { wp_die(__('vCard not available for download.', 'connections')); } $vCard = new cnvCard($entry); //var_dump($vCard);die; } else { $entry = $instance->retrieve->entries(array('slug' => $slug)); //var_dump($entry);die; // Die if no entry was found. if (empty($entry)) { wp_die(__('vCard not available for download.', 'connections')); } $vCard = new cnvCard($entry[0]); //var_dump($vCard);die; } $filename = sanitize_file_name($vCard->getName()); //var_dump($filename); $data = $vCard->getvCard(); //var_dump($data);die; header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . $filename . '.vcf'); header('Content-Length: ' . strlen($data)); header('Pragma: public'); header("Pragma: no-cache"); //header( "Expires: 0" ); header('Expires: Wed, 11 Jan 1984 05:00:00 GMT'); header('Cache-Control: private'); // header( 'Connection: close' ); //ob_clean(); flush(); echo $data; exit; } }
/** * Run the actions registered to custom content blocks. * * Render any custom content blocks registered to the `cn_entry_output_content-{id}` action hook. * * This will also run any actions registered for a custom metaboxes and its fields. * The actions should hook into `cn_output_meta_field-{key}` to be rendered. * * Accepted option for the $atts property are: * id (string) The custom block ID to render. * order (mixed) array | string An indexed array of custom content block IDs that should be rendered in the order in the array. * If a string is provided, it should be a comma delimited string containing the content block IDs. It will be converted to an array. * exclude (array) An indexed array of custom content block IDs that should be excluded from being rendered. * include (array) An indexed array of custom content block IDs that should be rendered. * NOTE: Custom content block IDs in `exclude` outweigh custom content block IDs in include. Meaning if the * same custom content block ID exists in both, the custom content block will be excluded. * * @access public * @since 0.8 * @uses do_action() * @uses wp_parse_args() * @uses apply_filters() * @uses has_action() * @param mixed $atts array | string [optional] The custom content block(s) to render. * @param array $shortcode_atts [optional] If this is used within the shortcode template loop, the shortcode atts * should be passed so the shortcode atts can be passed by do_action() to allow access to the action callback. * @param cnTemplate|null $template [optional] If this is used within the shortcode template loop, the template object * should be passed so the template object can be passed by do_action() to allow access to the action callback. * * @return string The HTML output of the custom content blocks. */ public function getContentBlock($atts = array(), $shortcode_atts = array(), $template = NULL) { $blockContainerContent = ''; if (cnQuery::getVar('cn-entry-slug')) { $settings = cnSettingsAPI::get('connections', 'connections_display_single', 'content_block'); } else { $settings = cnSettingsAPI::get('connections', 'connections_display_list', 'content_block'); } $order = isset($settings['order']) ? $settings['order'] : array(); $include = isset($settings['active']) ? $settings['active'] : array(); $exclude = empty($include) ? $order : array(); $titles = array(); $defaults = array('id' => '', 'order' => is_string($atts) && !empty($atts) ? $atts : $order, 'exclude' => is_string($atts) && !empty($atts) ? '' : $exclude, 'include' => is_string($atts) && !empty($atts) ? '' : $include, 'layout' => 'list', 'container_tag' => 'div', 'block_tag' => 'div', 'header_tag' => 'h3', 'before' => '', 'after' => '', 'return' => FALSE); $atts = wp_parse_args(apply_filters('cn_output_content_block_atts', $atts), $defaults); if (!empty($atts['id'])) { $blocks = array($atts['id']); } elseif (!empty($atts['order'])) { $blocks = cnFunction::parseStringList($atts['order'], ','); } // Nothing to render, exit. if (empty($blocks)) { return ''; } // Cleanup user input. Trim whitespace and convert to lowercase. $blocks = array_map('strtolower', array_map('trim', $blocks)); // Output the registered action in the order supplied by the user. foreach ($blocks as $key) { isset($blockNumber) ? $blockNumber++ : ($blockNumber = 1); // Exclude/Include the metaboxes that have been requested to exclude/include. if (!empty($atts['exclude'])) { if (in_array($key, $atts['exclude'])) { continue; } } else { if (!empty($atts['include'])) { if (!in_array($key, $atts['include'])) { continue; } } } ob_start(); // If the hook has a registered meta data output callback registered, lets run it. if (has_action('cn_output_meta_field-' . $key)) { // Grab the meta. $results = $this->getMeta(array('key' => $key, 'single' => TRUE)); if (!empty($results)) { do_action("cn_output_meta_field-{$key}", $key, $results, $this, $shortcode_atts, $template); } } // Render the "Custom Fields" meta block content. if ('meta' == $key) { $this->getMetaBlock(array(), $shortcode_atts, $template); } $hook = "cn_entry_output_content-{$key}"; if (has_action($hook)) { do_action($hook, $this, $shortcode_atts, $template); } $blockContent = ob_get_clean(); if (empty($blockContent)) { continue; } $blockID = $this->getSlug() . '-' . $blockNumber; // Store the title in an array that can be accessed/passed from outside the content block loop. // And if there is no title for some reason, create one from the key. if ($name = cnOptions::getContentBlocks($key)) { $titles[$blockID] = $name; } elseif ($name = cnOptions::getContentBlocks($key, 'single')) { $titles[$blockID] = $name; } else { $titles[$blockID] = ucwords(str_replace(array('-', '_'), ' ', $key)); } //$titles[ $blockID ] = cnOptions::getContentBlocks( $key ) ? cnOptions::getContentBlocks( $key ) : ucwords( str_replace( array( '-', '_' ), ' ', $key ) ); $blockContainerContent .= apply_filters('cn_entry_output_content_block', sprintf('<%2$s class="cn-entry-content-block cn-entry-content-block-%3$s" id="cn-entry-content-block-%4$s">%1$s%5$s</%2$s>' . PHP_EOL, sprintf('<%1$s>%2$s</%1$s>', $atts['header_tag'], $titles[$blockID]), $atts['block_tag'], $key, $blockID, $blockContent), $this, $key, $blockID, $titles[$blockID], $blockContent, $atts, $shortcode_atts); } if (empty($blockContainerContent)) { return ''; } $out = apply_filters('cn_entry_output_content_block_container', sprintf('<%1$s class="cn-entry-content-block-%2$s">%3$s</%1$s>' . PHP_EOL, $atts['container_tag'], $atts['layout'], $blockContainerContent), $this, $blockContainerContent, $titles, $atts, $shortcode_atts); $out = $atts['before'] . $out . $atts['after'] . PHP_EOL; return $this->echoOrReturn($atts['return'], $out); }
/** * Display results based on query var `cn-view`. * * @access public * @since 0.7.3 * @static * * @uses cnQuery::getVar() * * @param array $atts * @param string $content [optional] * @param string $tag * * @return string */ public static function view($atts, $content = '', $tag = 'connections') { // Grab an instance of the Connections object. $instance = Connections_Directory(); /*$getAllowPublic = $instance->options->getAllowPublic(); var_dump($getAllowPublic); $getAllowPublicOverride = $instance->options->getAllowPublicOverride(); var_dump($getAllowPublicOverride); $getAllowPrivateOverride = $instance->options->getAllowPrivateOverride(); var_dump($getAllowPrivateOverride);*/ /* * Only show this message under the following condition: * - ( The user is not logged in AND the 'Login Required' is checked ) AND ( neither of the shortcode visibility overrides are enabled ). */ if (!is_user_logged_in() && !$instance->options->getAllowPublic() && !($instance->options->getAllowPublicOverride() || $instance->options->getAllowPrivateOverride())) { $message = $instance->settings->get('connections', 'connections_login', 'message'); // Format and texturize the message. $message = wptexturize(wpautop($message)); // Make any links and such clickable. $message = make_clickable($message); // Apply the shortcodes. $message = do_shortcode($message); return $message; } $view = cnQuery::getVar('cn-view'); switch ($view) { case 'submit': if (has_action('cn_submit_entry_form')) { ob_start(); /** * @todo There s/b capability checks just like when editing an entry so users can only submit when they have the permissions. */ do_action('cn_submit_entry_form', $atts, $content, $tag); return ob_get_clean(); } else { return '<p>' . __('Future home of front end submissions.', 'connections') . '</p>'; } break; case 'landing': return '<p>' . __('Future home of the landing pages, such a list of categories.', 'connections') . '</p>'; break; case 'search': if (has_action('cn_submit_search_form')) { ob_start(); do_action('cn_submit_search_form', $atts, $content, $tag); return ob_get_clean(); } else { return '<p>' . __('Future home of the search page.', 'connections') . '</p>'; } break; case 'results': if (has_action('cn_submit_search_results')) { ob_start(); do_action('cn_submit_search_results', $atts, $content, $tag); return ob_get_clean(); } else { return '<p>' . __('Future home of the search results landing page.', 'connections') . '</p>'; } break; // Show the standard result list. // Show the standard result list. case 'card': return cnShortcode_Connections::shortcode($atts, $content); break; // Show the "View All" result list using the "Names" template. // Show the "View All" result list using the "Names" template. case 'all': // Disable the output of the repeat character index. $atts['repeat_alphaindex'] = FALSE; // Force the use of the Names template. $atts['template'] = 'names'; return cnShortcode_Connections::shortcode($atts, $content); break; // Show the entry detail using a template based on the entry type. // Show the entry detail using a template based on the entry type. case 'detail': switch (cnQuery::getVar('cn-process')) { case 'edit': if (has_action('cn_edit_entry_form')) { // Check to see if the entry has been linked to a user ID. $entryID = get_user_meta(get_current_user_id(), 'connections_entry_id', TRUE); // var_dump( $entryID ); $results = $instance->retrieve->entries(array('status' => 'approved,pending')); // var_dump( $results ); /* * The `cn_edit_entry_form` action should only be executed if the user is * logged in and they have the `connections_manage` capability and either the * `connections_edit_entry` or `connections_edit_entry_moderated` capability. */ if (is_user_logged_in() && (current_user_can('connections_manage') || (int) $entryID == (int) $results[0]->id) && (current_user_can('connections_edit_entry') || current_user_can('connections_edit_entry_moderated'))) { ob_start(); if (!current_user_can('connections_edit_entry') && $results[0]->status == 'pending') { echo '<p>' . __('Your entry submission is currently under review, however, you can continue to make edits to your entry submission while your submission is under review.', 'connections') . '</p>'; } do_action('cn_edit_entry_form', $atts, $content, $tag); return ob_get_clean(); } else { return __('You are not authorized to edit entries. Please contact the admin if you received this message in error.', 'connections'); } } break; default: // Ensure an array is passed the the cnRetrieve::entries method. if (!is_array($atts)) { $atts = (array) $atts; } $results = $instance->retrieve->entries($atts); //var_dump($results); $atts['list_type'] = $instance->settings->get('connections', 'connections_display_single', 'template') ? $results[0]->entry_type : NULL; // Disable the output of the following because they do no make sense to display for a single entry. $atts['show_alphaindex'] = FALSE; $atts['repeat_alphaindex'] = FALSE; $atts['show_alphahead'] = FALSE; return cnShortcode_Connections::shortcode($atts, $content); break; } break; // Show the standard result list. // Show the standard result list. default: //return cnShortcode_Connections::shortcode( $atts, $content ); if (has_action("cn_view_{$view}")) { ob_start(); do_action("cn_view_{$view}", $atts, $content, $tag); return ob_get_clean(); } break; } return cnShortcode_Connections::shortcode($atts, $content); }