Ejemplo n.º 1
0
 /**
  * @access public
  * @since  8.5.15
  *
  * @global wpdb $wpdb
  *
  * @param array  $atts
  *
  * @return string
  */
 public static function where($atts = array())
 {
     global $wpdb;
     $defaults = array('table' => CN_ENTRY_TABLE, 'field' => '', 'value' => '', 'format' => '%s', 'relation' => 'AND', 'delimiter' => '|');
     $atts = cnSanitize::args($atts, $defaults);
     $where = '';
     $atts['relation'] = in_array($atts['relation'], array('AND', 'OR')) ? strtoupper($atts['relation']) : 'AND';
     if (0 < strlen($atts['field']) && 0 < count($atts['value'])) {
         $value = cnFunction::parseStringList($atts['value'], $atts['delimiter']);
         $count = count($value);
         if (1 < $count) {
             $where = $atts['relation'] . ' ' . $atts['table'] . '.' . $atts['field'] . ' ' . self::in($value, $atts['format']);
         } elseif (1 == $count) {
             $where = $wpdb->prepare($atts['relation'] . ' ' . $atts['table'] . '.' . $atts['field'] . ' = ' . $atts['format'], $value);
         }
     }
     return $where;
 }
 /**
  * Display a message box above the search results with information
  * about the current query and the option (a button) to clear results.
  *
  * @access public
  * @since  0.8
  * @static
  * @param  array           $atts     The shortcode $atts array.
  * @param  array           $results  The cnRetrieve query results.
  * @param  cnTemplate|null $template An instance of the cnTemplate object.
  *
  * @return string
  */
 public static function searchingMessage($atts = array(), $results = array(), $template = NULL)
 {
     // Check whether or not the category description should be displayed or not.
     if (!cnSettingsAPI::get('connections', 'connections_display_results', 'search_message')) {
         return '';
     }
     $html = '';
     $messages = array();
     $defaults = array('header' => '', 'header_tag' => 'h3', 'container_tag' => 'ul', 'item_tag' => 'li', 'before' => '', 'before-item' => '', 'after-item' => '', 'after' => '', 'return' => FALSE);
     $atts = wp_parse_args($atts, apply_filters('cn_search_results_atts', $defaults));
     // Get the directory home page ID.
     $homeID = $atts['force_home'] ? cnSettingsAPI::get('connections', 'connections_home_page', 'page_id') : $atts['home_id'];
     //$addAction = cnSettingsAPI::get( 'connections', 'connections_home_page', 'page_id' ) != $atts['home_id'] ? TRUE : FALSE;
     // The base post permalink is required, do not filter the permalink thru cnSEO.
     if (!is_admin()) {
         cnSEO::doFilterPermalink(FALSE);
     }
     $permalink = get_permalink($homeID);
     $permalink = apply_filters('cn_permalink', $permalink, $atts);
     // Re-enable the filter.
     if (!is_admin()) {
         cnSEO::doFilterPermalink();
     }
     // Store the query vars
     $queryVars = array();
     $queryVars['cn-s'] = cnQuery::getVar('cn-s') ? esc_html(cnQuery::getVar('cn-s')) : FALSE;
     $queryVars['cn-char'] = cnQuery::getVar('cn-char') ? esc_html(urldecode(cnQuery::getVar('cn-char'))) : FALSE;
     $queryVars['cn-cat'] = cnQuery::getVar('cn-cat') ? cnQuery::getVar('cn-cat') : FALSE;
     $queryVars['cn-organization'] = cnQuery::getVar('cn-organization') ? esc_html(urldecode(cnQuery::getVar('cn-organization'))) : FALSE;
     $queryVars['cn-department'] = cnQuery::getVar('cn-department') ? esc_html(urldecode(cnQuery::getVar('cn-department'))) : FALSE;
     $queryVars['cn-locality'] = cnQuery::getVar('cn-locality') ? esc_html(urldecode(cnQuery::getVar('cn-locality'))) : FALSE;
     $queryVars['cn-region'] = cnQuery::getVar('cn-region') ? esc_html(urldecode(cnQuery::getVar('cn-region'))) : FALSE;
     $queryVars['cn-postal-code'] = cnQuery::getVar('cn-postal-code') ? esc_html(urldecode(cnQuery::getVar('cn-postal-code'))) : FALSE;
     $queryVars['cn-country'] = cnQuery::getVar('cn-country') ? esc_html(urldecode(cnQuery::getVar('cn-country'))) : FALSE;
     // if ( cnQuery::getVar('cn-near-coord') ) $queryVars['cn-near-coord']     = cnQuery::getVar('cn-near-coord');
     // if ( cnQuery::getVar('cn-radius') ) $queryVars['cn-radius']             = cnQuery::getVar('cn-radius');
     // if ( cnQuery::getVar('cn-unit') ) $queryVars['cn-unit']                 = cnQuery::getVar('cn-unit');
     $messages = apply_filters('cn_search_results_messages-before', $messages, $atts, $results, $template);
     if ($queryVars['cn-cat']) {
         $categoryID = $queryVars['cn-cat'];
         $terms = array();
         // 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)) {
             foreach ($categoryID as $id) {
                 $term = cnTerm::getBy('id', $id, 'category');
                 $terms[] = esc_html($term->name);
             }
         } else {
             $term = cnTerm::getBy('id', $categoryID, 'category');
             $terms[] = esc_html($term->name);
         }
         $messages['cn-cat'] = sprintf(__('You are searching within category(ies): %s', 'connections'), implode(', ', $terms));
     }
     if ($queryVars['cn-s']) {
         // If value is a string, string the white space and covert to an array.
         if (!is_array($queryVars['cn-s'])) {
             $originalString = array($queryVars['cn-s']);
             $queryVars['cn-s'] = cnFunction::parseStringList($queryVars['cn-s'], '\\s');
             $queryVars['cn-s'] = array_merge($originalString, $queryVars['cn-s']);
             $queryVars['cn-s'] = array_unique($queryVars['cn-s']);
         }
         // Trim any white space from around the terms in the array.
         array_walk($queryVars['cn-s'], 'trim');
         $messages['cn-s'] = sprintf(__('You are searching for the keyword(s): %s', 'connections'), esc_html(implode(', ', $queryVars['cn-s'])));
     }
     if ($queryVars['cn-char']) {
         $messages['cn-char'] = sprintf(__('The results are being filtered by the character: %s', 'connections'), $queryVars['cn-char']);
     }
     if ($queryVars['cn-organization']) {
         $messages['cn-organization'] = sprintf(__('The results are being filtered by the organization: %s', 'connections'), $queryVars['cn-organization']);
     }
     if ($queryVars['cn-department']) {
         $messages['cn-department'] = sprintf(__('The results are being filtered by the department: %s', 'connections'), $queryVars['cn-department']);
     }
     if ($queryVars['cn-locality']) {
         $messages['cn-locality'] = sprintf(__('The results are being filtered by the locality: %s', 'connections'), $queryVars['cn-locality']);
     }
     if ($queryVars['cn-region']) {
         $messages['cn-region'] = sprintf(__('The results are being filtered by the region: %s', 'connections'), $queryVars['cn-region']);
     }
     if ($queryVars['cn-postal-code']) {
         $messages['cn-postal-code'] = sprintf(__('The results are being filtered by the postal code: %s', 'connections'), $queryVars['cn-postal-code']);
     }
     if ($queryVars['cn-country']) {
         $messages['cn-country'] = sprintf(__('The results are being filtered by the country: %s', 'connections'), $queryVars['cn-country']);
     }
     $messages = apply_filters('cn_search_results_messages-after', $messages, $atts, $results, $template);
     $messages = apply_filters('cn_search_results_messages', $messages, $atts, $results, $template);
     if (!empty($messages)) {
         if (0 < strlen($atts['header'])) {
             $header = sprintf('<%1$s class="cn-search-message-header">%2$s</%1$s>', $atts['header_tag'], esc_html($atts['header']));
         } else {
             $header = '';
         }
         // Render the HTML <li> items.
         foreach ($messages as $key => $message) {
             $html .= sprintf(PHP_EOL . "\t" . '<%2$s class="cn-search-message-list-item" id="cn-search-message-list-item-%3$s">%1$s%4$s%5$s</%2$s>', $atts['before-item'], $atts['item_tag'], esc_attr($key), wp_kses_post($message), $atts['after-item']);
         }
         // Wrap the <li> items in a <ul>.
         $html = sprintf('<%1$s class="cn-search-message-list">%2$s' . PHP_EOL . '</%1$s>', $atts['container_tag'], $html);
         // Add the clear search button.
         $html .= sprintf('<div id="cn-clear-search"><a class="button btn" id="cn-clear-search-button" href="%1$s">%2$s</a></div>' . PHP_EOL, esc_url($permalink), __('Clear Search', 'connections'));
         // Wrap it all in a <div>.
         $html = '<div id="cn-search-messages">' . $header . $html . '</div>' . PHP_EOL;
     }
     $html = $atts['before'] . $html . $atts['after'] . PHP_EOL;
     return self::echoOrReturn($atts['return'], $html);
 }
Ejemplo n.º 3
0
 /**
  * Returns an indexed array of objects the addresses per the defined options.
  *
  * @param $atts {
  *     @type string       $fields    The fields to return.
  *                                   Default: all
  *                                   Accepts: all, ids, locality, regions, postal-code, country
  *     @type int          $id        The entry ID in which to retrieve the addresses for.
  *     @type bool         $preferred Whether or not to return only the preferred address.
  *                                   Default: false
  *     @type array|string $type      The address types to return.
  *                                   Default: array() which will return all registered address types.
  *                                   Accepts: home, work, school, other and any other registered types.
  *     @type array|string $city      Return address in the defined cities.
  *     @type array|string $state     Return address in the defined states.
  *     @type array|string $country   Return address in the defined countries.
  *     @type array        $coordinates {
  *         Return the addresses at the specific coordinates.
  *         @type float $latitude
  *         @type float $longitude
  *     }
  * }
  *
  * @return array
  */
 public function addresses($atts = array())
 {
     /** @var wpdb $wpdb */
     global $wpdb;
     $where = array('WHERE 1=1');
     /*
      * // START -- Set the default attributes array. \\
      */
     $defaults = array('fields' => 'all', 'id' => NULL, 'preferred' => FALSE, 'type' => array(), 'city' => array(), 'state' => array(), 'zipcode' => array(), 'country' => array(), 'coordinates' => array(), 'limit' => NULL);
     $atts = cnSanitize::args($atts, $defaults);
     /*
      * // END -- Set the default attributes array if not supplied. \\
      */
     /**
      * @var int          $id
      * @var bool         $preferred
      * @var array|string $type
      * @var array|string $city
      * @var array|string $state
      * @var array|string $zipcode
      * @var array|string $country
      * @var array        $coordinates
      * @var null|int     $limit
      */
     extract($atts);
     /*
      * Convert these to values to an array if they were supplied as a comma delimited string
      */
     /** @var array $type */
     cnFunction::parseStringList($type);
     /** @var array $city */
     cnFunction::parseStringList($city);
     /** @var array $state */
     cnFunction::parseStringList($state);
     /** @var array $zipcode */
     cnFunction::parseStringList($zipcode);
     /** @var array $country */
     cnFunction::parseStringList($country);
     switch ($atts['fields']) {
         case 'ids':
             $select = array('a.id', 'a.entry_id');
             break;
         case 'locality':
             $select = array('a.city');
             break;
         case 'region':
             $select = array('a.state');
             break;
         case 'postal-code':
             $select = array('a.zipcode');
             break;
         case 'country':
             $select = array('a.country');
             break;
         default:
             $select = array('a.*');
     }
     if (!empty($id)) {
         $where[] = $wpdb->prepare('AND `entry_id` = %d', $id);
     }
     if (!empty($preferred)) {
         $where[] = $wpdb->prepare('AND `preferred` = %d', (bool) $preferred);
     }
     if (!empty($type)) {
         $where[] = $wpdb->prepare('AND `type` IN (' . cnFormatting::prepareINPlaceholders($type) . ')', $type);
     }
     if (!empty($city)) {
         $where[] = $wpdb->prepare('AND `city` IN (' . cnFormatting::prepareINPlaceholders($city) . ')', $city);
     }
     if (!empty($state)) {
         $where[] = $wpdb->prepare('AND `state` IN (' . cnFormatting::prepareINPlaceholders($state) . ')', $state);
     }
     if (!empty($zipcode)) {
         $where[] = $wpdb->prepare('AND `zipcode` IN (' . cnFormatting::prepareINPlaceholders($zipcode) . ')', $zipcode);
     }
     if (!empty($country)) {
         $where[] = $wpdb->prepare('AND `country` IN (' . cnFormatting::prepareINPlaceholders($country) . ')', $country);
     }
     if (!empty($coordinates)) {
         if (!empty($coordinates['latitude']) && !empty($coordinates['longitude'])) {
             $where[] = $wpdb->prepare('AND `latitude` = %f', $coordinates['latitude']);
             $where[] = $wpdb->prepare('AND `longitude` = %f', $coordinates['longitude']);
         }
     }
     // Limit the characters that are queried based on if the current user can view public, private or unlisted entries.
     $where = self::setQueryVisibility($where, array('table' => 'a'));
     $limit = is_null($atts['limit']) ? '' : sprintf(' LIMIT %d', $atts['limit']);
     $sql = sprintf('SELECT %1$s FROM %2$s AS a %3$s ORDER BY `order`%4$s', implode(', ', $select), CN_ENTRY_ADDRESS_TABLE, implode(' ', $where), $limit);
     $results = $wpdb->get_results($sql);
     return $results;
 }
Ejemplo n.º 4
0
 /**
  * Renders a HTML tag attribute.
  *
  * @access public
  * @since  0.8
  *
  * @uses   sanitize_html_class()
  * @uses   esc_attr()
  *
  * @param  string       $type  The attribute name.
  * @param  array|string $value The attribute value.
  *
  * @return string        The rendered attribute.
  */
 public static function attribute($type, $value)
 {
     switch ($type) {
         case 'class':
             if (is_array($value) && !empty($value)) {
                 array_walk($value, 'sanitize_html_class');
                 return $value ? ' class="' . implode($value, ' ') . '" ' : '';
             } elseif (!empty($value)) {
                 return ' class="' . sanitize_html_class((string) $value) . '" ';
             } else {
                 return '';
             }
             break;
         case 'id':
             if (!empty($value)) {
                 return ' id="' . esc_attr((string) $value) . '" ';
             } else {
                 return '';
             }
             break;
         case 'style':
             if (is_array($value) && !empty($value)) {
                 array_walk($value, create_function('&$i, $property', '$i = "$property: $i";'));
                 return $value ? ' style="' . implode($value, '; ') . '"' : '';
             }
             return '';
             break;
         case 'value':
             return ' value="' . esc_attr((string) $value) . '" ';
             break;
         case 'data':
             $data = array();
             /**
              * Create valid HTML5 data attributes.
              *
              * @link http://stackoverflow.com/a/22753630/5351316
              * @link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
              */
             if (cnFunction::isDimensionalArray($value)) {
                 foreach ($value as $_value) {
                     if (isset($_value['name']) && 0 < strlen($_value['name'])) {
                         $name = 'data-' . cnFormatting::toCamelCase($_value['name']);
                         $data[$name] = $_value['value'];
                     }
                 }
             } else {
                 if (isset($value['name']) && 0 < strlen($value['name'])) {
                     $name = 'data-' . cnFormatting::toCamelCase($value['name']);
                     $data[$name] = $value['value'];
                 }
             }
             if (!empty($data)) {
                 array_walk($data, create_function('&$i, $name', '$i = $name . \'="\' . esc_attr( $i ) . \'"\';'));
                 return ' ' . implode($data, ' ');
             }
             return '';
             break;
         default:
             if (is_array($value) && !empty($value)) {
                 array_walk($value, 'esc_attr');
                 return $value ? ' ' . esc_attr($type) . '="' . implode($value, ' ') . '" ' : '';
             } elseif (!empty($value)) {
                 return ' ' . esc_attr($type) . '="' . esc_attr((string) $value) . '" ';
             } else {
                 return '';
             }
     }
 }
 /**
  * Start the element output.
  *
  * @see   Walker::start_el()
  *
  * @since 8.1.6
  *
  * @uses   esc_attr()
  * @uses   number_format_i18n()
  * @uses   cnTerm::get()
  *
  * @param string $output Passed by reference. Used to append additional content.
  * @param object $term   Term object.
  * @param int    $depth  Depth of category in reference to parents. Default 0.
  * @param array  $args   An array of arguments. @see CN_Walker_Term_List::render()
  * @param int    $id     ID of the current term.
  */
 public function start_el(&$output, $term, $depth = 0, $args = array(), $id = 0)
 {
     $indent = str_repeat("\t", $depth);
     $count = $args['show_count'] ? '<span class="cn-cat-count">&nbsp;(' . esc_html(number_format_i18n($term->count)) . ')</span>' : '';
     $url = cnTerm::permalink($term, 'category', $args);
     $html = sprintf('<a href="%1$s" title="%2$s">%3$s</a>', $url, esc_attr($term->name), esc_html($term->name) . $count);
     /**
      * Allows extensions to alter the HTML of term list item.
      *
      * @since 8.5.18
      *
      * @param string        $html  The HTML.
      * @param cnTerm_Object $term  The current term.
      * @param int           $depth Depth of category. Used for tab indentation.
      * @param array         $args  The method attributes.
      */
     $html = apply_filters('cn_term_list_item', $html, $term, $depth, $args);
     $class = array('cat-item', 'cat-item-' . $term->term_id, 'cn-cat-parent');
     $termChildren = cnTerm::getTaxonomyTerms($term->taxonomy, array('parent' => $term->term_id, 'hide_empty' => FALSE, 'fields' => 'count'));
     if (!empty($termChildren)) {
         $class[] = 'cn-cat-has-children';
     }
     if (!empty($args['current_category'])) {
         if (is_numeric($args['current_category'])) {
             $_current_category = cnTerm::get($args['current_category'], $term->taxonomy);
             // cnTerm::get() can return NULL || an instance of WP_Error, so, lets check for that.
             if (is_null($_current_category) || is_wp_error($_current_category)) {
                 $_current_category = new stdClass();
                 $_current_category->parent = 0;
             }
         } else {
             $_current_category = new stdClass();
             $_current_category->parent = 0;
         }
         if ($term->slug == $args['current_category']) {
             $class[] = ' current-cat';
         } elseif ($term->term_id == $args['current_category']) {
             $class[] = ' current-cat';
         } elseif ($term->term_id == $_current_category->parent) {
             $class[] = ' current-cat-parent';
         }
     }
     /**
      * Allows extensions to add/remove class names to the current term list item.
      *
      * @since 8.5.18
      *
      * @param array         $class The array of class names.
      * @param cnTerm_Object $term  The current term.
      * @param int           $depth Depth of category. Used for tab indentation.
      * @param array         $args  The method attributes.
      */
     $class = apply_filters('cn_term_list_item_class', $class, $term, $depth, $args);
     $class = cnSanitize::htmlClass($class);
     $output .= "{$indent}<li" . ' class="' . cnFunction::escAttributeDeep($class) . '"' . ">{$html}";
     // Do not add EOL here, it'll add unwanted whitespace if terms are inline.
 }
 /**
  * Returns as an array of objects containing the dates per the defined options for the current entry.
  *
  * Accepted options for the $atts property are:
  * preferred (bool) Retrieve the preferred entry date.
  *  type (array) || (string) Retrieve specific date types.
  *   Permitted Types:
  *    baptism
  *    certification
  *    employment
  *    membership
  *    graduate_high_school
  *    graduate_college
  *    ordination
  *
  * Filters:
  *  cn_date_atts => (array) Set the method attributes.
  *  cn_date_cached => (bool) Define if the returned dates should be from the object cache or queried from the db.
  *  cn_date => (object) Individual date as it is processed thru the loop.
  *  cn_dates => (array) All dates before they are returned.
  *
  * @access public
  * @since 0.7.3
  * @version 1.0
  * @param array   $atts         Accepted values as noted above.
  * @param bool    $cached       Returns the cached date data rather than querying the db.
  * @param bool    $saving       Whether or no the data is being saved to the db.
  *
  * @return array
  */
 public function getDates($atts = array(), $cached = TRUE, $saving = FALSE)
 {
     // Grab an instance of the Connections object.
     $instance = Connections_Directory();
     $results = array();
     $atts = apply_filters('cn_date_atts', $atts);
     $cached = apply_filters('cn_date_cached', $cached);
     /*
      * // START -- Set the default attributes array. \\
      */
     $defaults = array('preferred' => FALSE, 'type' => NULL);
     $atts = cnSanitize::args($atts, $defaults);
     $atts['id'] = $this->getId();
     /*
      * // END -- Set the default attributes array if not supplied. \\
      */
     /*
      * Load back into the results the data from the legacy fields, anniversary and birthday,
      * for backward compatibility with versions 0.7.2.6 and older.
      */
     if (!empty($this->anniversary)) {
         $anniversary = new stdClass();
         $anniversary->id = 0;
         $anniversary->order = 0;
         $anniversary->preferred = FALSE;
         $anniversary->type = 'anniversary';
         $anniversary->name = __('Anniversary', 'connections');
         $anniversary->date = $this->getAnniversary('Y-m-d');
         $anniversary->day = $this->getAnniversary('m-d');
         $anniversary->visibility = 'public';
         $results['anniversary'] = $anniversary;
     }
     if (!empty($this->birthday)) {
         $birthday = new stdClass();
         $birthday->id = 0;
         $birthday->order = 0;
         $birthday->preferred = FALSE;
         $birthday->type = 'birthday';
         $birthday->name = __('Birthday', 'connections');
         $birthday->date = $this->getBirthday('Y-m-d');
         $birthday->day = $this->getBirthday('m-d');
         $birthday->visibility = 'public';
         $results['birthday'] = $birthday;
     }
     if ($cached) {
         if (!empty($this->dates)) {
             $dates = unserialize($this->dates);
             if (empty($dates)) {
                 return $results;
             }
             /**
              * @var bool   $preferred
              * @var string $type
              */
             extract($atts);
             /*
              * Covert to an array if it was supplied as a comma delimited string
              */
             cnFunction::parseStringList($type);
             foreach ((array) $dates as $key => $date) {
                 /**
                  * Allow plugins to filter raw data before object is setup.
                  *
                  * @since 8.5.19
                  *
                  * @param array $date
                  */
                 $date = apply_filters('cn_date-pre_setup', $date);
                 $row = new stdClass();
                 isset($date['id']) ? $row->id = (int) $date['id'] : ($row->id = 0);
                 isset($date['order']) ? $row->order = (int) $date['order'] : ($row->order = 0);
                 isset($date['preferred']) ? $row->preferred = (bool) $date['preferred'] : ($row->preferred = FALSE);
                 isset($date['type']) ? $row->type = $this->format->sanitizeString($date['type']) : ($row->type = '');
                 isset($date['date']) ? $row->date = $this->format->sanitizeString($date['date']) : ($row->date = '');
                 isset($date['visibility']) ? $row->visibility = $this->format->sanitizeString($date['visibility']) : ($row->visibility = '');
                 /*
                  * Set the date name based on the type.
                  */
                 $dateTypes = $instance->options->getDateOptions();
                 $row->name = $dateTypes[$row->type];
                 /*
                  * If the date type is anniversary or birthday and the date is equal to the date
                  * saved in the legacy fields, unset the data imported from the legacy field.
                  * This is for compatibility with versions 0.7.2.6 and older.
                  *
                  * NOTE: Only the month and day will be compared because the legacy getAnniversary() and getBirthday() methods
                  * will return the year of the next anniversary or birthday. IE: if that date in the current year has already
                  * passed the year would be the next year.
                  */
                 if ('anniversary' == $row->type && isset($results['anniversary']) && substr($row->date, 5, 5) == $results['anniversary']->day) {
                     unset($results['anniversary']);
                 }
                 if ('birthday' == $row->type && isset($results['birthday']) && substr($row->date, 5, 5) == $results['birthday']->day) {
                     unset($results['birthday']);
                 }
                 /*
                  * // START -- Do not return dates that do not match the supplied $atts.
                  */
                 if ($preferred && !$row->preferred) {
                     continue;
                 }
                 if (!empty($type) && !in_array($row->type, $type)) {
                     continue;
                 }
                 /*
                  * // END -- Do not return dates that do not match the supplied $atts.
                  */
                 /*
                  * // START -- Compatibility for previous versions.
                  */
                 // Versions prior to 8.1.5 may not have visibility set, so we'll assume it was 'public' since it wasn't the option before.
                 if (!isset($date['visibility']) || empty($date['visibility'])) {
                     $row->visibility = 'public';
                 }
                 /*
                  * // END -- Compatibility for previous versions.
                  */
                 // If the user does not have permission to view the address, do not return it.
                 if (!$this->validate->userPermitted($row->visibility) && !$saving) {
                     continue;
                 }
                 $results[] = apply_filters('cn_date', $row);
             }
         }
     } else {
         // Exit right away and return an emtpy array if the entry ID has not been set otherwise all dates will be returned by the query.
         if (!isset($this->id) || empty($this->id)) {
             return array();
         }
         $dates = $instance->retrieve->dates($atts, $saving);
         if (empty($dates)) {
             return $results;
         }
         foreach ($dates as $date) {
             /** This filter is documented in ../includes/entry/class.entry-data.php */
             $date = apply_filters('cn_date-pre_setup', $date);
             $date->id = (int) $date->id;
             $date->order = (int) $date->order;
             $date->preferred = (bool) $date->preferred;
             $date->type = $this->format->sanitizeString($date->type);
             $date->date = $this->format->sanitizeString($date->date);
             $date->visibility = $this->format->sanitizeString($date->visibility);
             /*
              * Set the date name based on the date type.
              */
             $dateTypes = $instance->options->getDateOptions();
             $date->name = $dateTypes[$date->type];
             /*
              * If the date type is anniversary or birthday and the date is equal to the date
              * saved in the legacy fields are the same, unset the data imported from the legacy field.
              * This is for compatibility with versions 0.7.2.6 and older.
              */
             if ('anniversary' == $date->type && isset($results['anniversary']) && $date->date == $results['anniversary']->date) {
                 unset($results['anniversary']);
             }
             if ('birthday' == $date->type && isset($results['birthday']) && $date->date == $results['birthday']->date) {
                 unset($results['birthday']);
             }
             /*
              * If the date type is anniversary or birthday and the date is equal to the date
              * saved in the legacy fields, unset the data imported from the legacy field.
              * This is for compatibility with versions 0.7.2.6 and older.
              *
              * NOTE: Only the month and day will be compared because the legacy getAnniversary() and getBirthday() methods
              * will return the year of the next anniversary or birthday. IE: if that date in the current year has already
              * passed the year would be the next year.
              */
             if ('anniversary' == $date->type && isset($results['anniversary']) && substr($date->date, 5, 5) == $results['anniversary']->day) {
                 unset($results['anniversary']);
             }
             if ('birthday' == $date->type && isset($results['birthday']) && substr($date->date, 5, 5) == $results['birthday']->day) {
                 unset($results['birthday']);
             }
             $results[] = apply_filters('cn_date', $date);
         }
     }
     return apply_filters('cn_dates', $results);
 }
    /**
     * Return an array of entry ID/s found with the supplied search terms.
     *
     * @todo Allow the fields for each table to be defined as a comma delimited list, convert an array and validate against of list of valid table fields.
     * @todo Add a filter to allow the search fields to be changed.
     *
     * Resources used:
     *  http://devzone.zend.com/26/using-mysql-full-text-searching/
     *  http://onlamp.com/onlamp/2003/06/26/fulltext.html
     *
     * @since  0.7.2.0
     * @param  array   $atts [optional]
     *
     * @return array
     */
    public function search($atts = array())
    {
        /** @var wpdb $wpdb */
        global $wpdb;
        $results = array();
        $scored = array();
        $fields = cnSettingsAPI::get('connections', 'search', 'fields');
        $fields = apply_filters('cn_search_fields', $fields);
        // If no search search fields are set, return an empty array.
        if (empty($fields)) {
            return array();
        }
        /*
         * // START -- Set the default attributes array. \\
         */
        $defaults['terms'] = array();
        if (in_array('family_name', $fields)) {
            $defaults['fields']['entry'][] = 'family_name';
        }
        if (in_array('first_name', $fields)) {
            $defaults['fields']['entry'][] = 'first_name';
        }
        if (in_array('middle_name', $fields)) {
            $defaults['fields']['entry'][] = 'middle_name';
        }
        if (in_array('last_name', $fields)) {
            $defaults['fields']['entry'][] = 'last_name';
        }
        if (in_array('title', $fields)) {
            $defaults['fields']['entry'][] = 'title';
        }
        if (in_array('organization', $fields)) {
            $defaults['fields']['entry'][] = 'organization';
        }
        if (in_array('department', $fields)) {
            $defaults['fields']['entry'][] = 'department';
        }
        if (in_array('contact_first_name', $fields)) {
            $defaults['fields']['entry'][] = 'contact_first_name';
        }
        if (in_array('contact_last_name', $fields)) {
            $defaults['fields']['entry'][] = 'contact_last_name';
        }
        if (in_array('bio', $fields)) {
            $defaults['fields']['entry'][] = 'bio';
        }
        if (in_array('notes', $fields)) {
            $defaults['fields']['entry'][] = 'notes';
        }
        if (in_array('address_line_1', $fields)) {
            $defaults['fields']['address'][] = 'line_1';
        }
        if (in_array('address_line_2', $fields)) {
            $defaults['fields']['address'][] = 'line_2';
        }
        if (in_array('address_line_3', $fields)) {
            $defaults['fields']['address'][] = 'line_3';
        }
        if (in_array('address_line_4', $fields)) {
            $defaults['fields']['address'][] = 'line_4';
        }
        if (in_array('address_district', $fields)) {
            $defaults['fields']['address'][] = 'district';
        }
        if (in_array('address_county', $fields)) {
            $defaults['fields']['address'][] = 'county';
        }
        if (in_array('address_city', $fields)) {
            $defaults['fields']['address'][] = 'city';
        }
        if (in_array('address_state', $fields)) {
            $defaults['fields']['address'][] = 'state';
        }
        if (in_array('address_zipcode', $fields)) {
            $defaults['fields']['address'][] = 'zipcode';
        }
        if (in_array('address_country', $fields)) {
            $defaults['fields']['address'][] = 'country';
        }
        if (in_array('phone_number', $fields)) {
            $defaults['fields']['phone'][] = 'number';
        }
        $atts = wp_parse_args($atts, apply_filters('cn_search_atts', $defaults));
        // @todo Validate each fields array to ensure only permitted fields will be used.
        /*
         * // END -- Set the default attributes array if not supplied. \\
         */
        // If no search terms were entered, return an empty array.
        if (empty($atts['terms'])) {
            return array();
        }
        // If value is a string, stripe the white space and covert to an array.
        //if ( ! is_array( $atts['terms'] ) ) $atts['terms'] = explode( ' ', trim( $atts['terms'] ) );
        // Trim any white space from around the terms in the array.
        //array_walk( $atts['terms'] , 'trim' );
        $original = $atts['terms'];
        $atts['terms'] = cnFunction::parseStringList($atts['terms'], '\\s');
        array_unshift($atts['terms'], $original);
        $atts['terms'] = array_unique($atts['terms']);
        $atts['terms'] = apply_filters('cn_search_terms', $atts['terms']);
        // Remove any single characters and stop words from terms.
        $atts['terms'] = $this->parse_search_terms($atts['terms']);
        // If no search terms are left after removing stop words, return an empty array.
        if (empty($atts['terms'])) {
            return array();
        }
        /*
         * Perform search using FULLTEXT if enabled.
         *
         * Perform the search on each table individually because joining the tables doesn't scale when
         * there are a large number of entries.
         *
         * NOTES:
         * 	The following is the error reported by MySQL when DB does not support FULLTEXT:  'The used table type doesn't support FULLTEXT indexes'
         * 	If DB does not support FULLTEXT the query will fail and the $results will be an empty array.
         *
         * 	FULLTEXT Restrictions as noted here: http://onlamp.com/onlamp/2003/06/26/fulltext.html
         *
         * 		Some of the default behaviors of these restrictions can be changed in your my.cnf or using the SET command
         *
         * 		FULLTEXT indices are NOT supported in InnoDB tables.
         * 		MySQL requires that you have at least three rows of data in your result set before it will return any results.
         * 		By default, if a search term appears in more than 50% of the rows then MySQL will not return any results.
         * 		By default, your search query must be at least four characters long and may not exceed 254 characters.
         * 		MySQL has a default stopwords file that has a list of common words (i.e., the, that, has) which are not returned in your search. In other words, searching for the will return zero rows.
         * 		According to MySQL's manual, the argument to AGAINST() must be a constant string. In other words, you cannot search for values returned within the query.
         */
        if (cnSettingsAPI::get('connections', 'search', 'fulltext_enabled')) {
            $terms = array();
            $shortwords = array();
            /*
             * Remove any shortwords from the FULLTEXT query since the db will drop them anyway.
             * Add the shortwords to a separate array to be used to do a LIKE query.
             */
            foreach ($atts['terms'] as $key => $term) {
                if (strlen($term) >= 2 && strlen($term) <= 3) {
                    unset($atts['terms'][$key]);
                    $shortwords[] = $term;
                }
            }
            if (!empty($atts['terms'])) {
                // Make each term required, functional AND query.
                $terms = apply_filters('cn_search_fulltext_terms', '+' . implode(' +', $atts['terms']), $atts['terms']);
            }
            /*
             * Only search the primary records if at least one fields is selected to be searched.
             */
            if (!empty($atts['fields']['entry'])) {
                $select = array();
                $from = array();
                $where = array();
                $like = array();
                $select[] = 'SELECT ' . CN_ENTRY_TABLE . '.id';
                /*
                 * Set up the SELECT to return the results scored by relevance.
                 */
                if (!empty($terms)) {
                    $select[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['entry']) . ') AGAINST (%s) AS score', $terms);
                }
                $from[] = CN_ENTRY_TABLE;
                /*
                 * If there are long word terms, perform a FULLTEXT query.
                 */
                if (!empty($terms)) {
                    $where[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['entry']) . ') AGAINST (%s IN BOOLEAN MODE)', $terms);
                }
                /*
                 * If there are no long words and there are short words, perform a LIKE query for the short words.
                 */
                if (empty($terms) && !empty($shortwords)) {
                    foreach ($shortwords as $word) {
                        /**
                         * Allow plugins to alter the shortword LIKE query.
                         *
                         * By default the shortword LIKE query will only return results with entries matching words that
                         * begin with the shortword. This is done to match the FULLTEXT search query which only returns
                         * results with terms that being with the search term.
                         *
                         * To alter the LIKE query to return results for shortword where entries contain the shortword,
                         * rather than begins with, use this example filter:
                         *
                         * <code>
                         * add_filter( 'cn_search_like_shortword', 'my_custom_search_like_shortword', 10, 2 );
                         * function my_custom_search_like_shortword( $esc_word, $word ) {
                         *
                         *     return '%' . $wpdb->esc_like( $word ) . '%';
                         * }
                         * </code>
                         *
                         * @since 8.5.27
                         *
                         * @param string $word The shortword where the $word is escaped for a LIKE query with a
                         *                     trailing `%` so the LIKE query will return results that begin with $word.
                         * @param string $word The shortword to perform the LIKE query with.
                         */
                        $word = apply_filters('cn_search_like_shortword', $wpdb->esc_like($word) . '%', $word);
                        $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['entry']) . ' LIKE %s ', array_fill(0, count($atts['fields']['entry']), $word));
                    }
                    $where[] = '( ' . implode(') OR (', $like) . ')';
                }
                /*
                 * Return the query results ordered by the relevance score.
                 */
                $orderBy = empty($terms) ? '' : ' ORDER BY score';
                $sql = implode(', ', $select) . ' FROM ' . implode(',', $from) . ' WHERE ' . implode(' AND ', $where) . $orderBy;
                $scored = $wpdb->get_results($sql, ARRAY_A);
            }
            /*
             * Only search the address records if at least one fields is selected to be searched.
             */
            if (!empty($atts['fields']['address'])) {
                $select = array();
                $from = array();
                $where = array();
                $like = array();
                $select[] = 'SELECT ' . CN_ENTRY_ADDRESS_TABLE . '.entry_id';
                /*
                 * Set up the SELECT to return the results scored by relevance.
                 */
                if (!empty($terms)) {
                    $select[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['address']) . ') AGAINST (%s) AS score', $terms);
                }
                $from[] = CN_ENTRY_ADDRESS_TABLE;
                /*
                 * If there are long word terms, perform a FULLTEXT query.
                 */
                if (!empty($terms)) {
                    $where[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['address']) . ') AGAINST (%s IN BOOLEAN MODE)', $terms);
                }
                /*
                 * If there are no long words and there are short words, perform a LIKE query for the short words.
                 */
                if (empty($terms) && !empty($shortwords)) {
                    foreach ($shortwords as $word) {
                        $word = apply_filters('cn_search_like_shortword', $wpdb->esc_like($word) . '%', $word);
                        $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['address']) . ' LIKE %s ', array_fill(0, count($atts['fields']['address']), $word));
                    }
                    $where[] = '( ' . implode(') OR (', $like) . ')';
                }
                /*
                 * Return the query results ordered by the relevance score.
                 */
                $orderBy = empty($terms) ? '' : ' ORDER BY score';
                $sql = implode(', ', $select) . ' FROM ' . implode(',', $from) . ' WHERE ' . implode(' AND ', $where) . $orderBy;
                $ids = $wpdb->get_results($sql, ARRAY_A);
                /*
                 * If any results are returned merge them in to the $scored results.
                 */
                if (!empty($ids)) {
                    $scored = array_merge($scored, $ids);
                }
            }
            /*
             * Only search the phone records if the field is selected to be search.
             */
            if (!empty($atts['fields']['phone'])) {
                $select = array();
                $from = array();
                $where = array();
                $like = array();
                $select[] = 'SELECT ' . CN_ENTRY_PHONE_TABLE . '.entry_id';
                /*
                 * Set up the SELECT to return the results scored by relevance.
                 */
                if (!empty($terms)) {
                    $select[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['phone']) . ') AGAINST (%s) AS score', $terms);
                }
                $from[] = CN_ENTRY_PHONE_TABLE;
                /*
                 * If there are long word terms, perform a FULLTEXT query.
                 */
                if (!empty($terms)) {
                    $where[] = $wpdb->prepare('MATCH (' . implode(', ', $atts['fields']['phone']) . ') AGAINST (%s IN BOOLEAN MODE)', $terms);
                }
                /*
                 * If there are no long words and there are short words, perform a LIKE query for the short words.
                 */
                if (empty($terms) && !empty($shortwords)) {
                    foreach ($shortwords as $word) {
                        $word = apply_filters('cn_search_like_shortword', $wpdb->esc_like($word) . '%', $word);
                        $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['phone']) . ' LIKE %s ', array_fill(0, count($atts['fields']['phone']), $word));
                    }
                    $where[] = '( ' . implode(') OR (', $like) . ')';
                }
                /*
                 * Return the query results ordered by the relevance score.
                 */
                $orderBy = empty($terms) ? '' : ' ORDER BY score';
                $sql = implode(', ', $select) . ' FROM ' . implode(',', $from) . ' WHERE ' . implode(' AND ', $where) . $orderBy;
                $ids = $wpdb->get_results($sql, ARRAY_A);
                /*
                 * If any results are returned merge them in to the $scored results.
                 */
                if (!empty($ids)) {
                    $scored = array_merge($scored, $ids);
                }
            }
            $scored = apply_filters('cn_search_scored_results', $scored, $terms);
            /*
             * The query results are stored in the $scored array ordered by relevance.
             * Only the entry ID/s are needed to be returned. Setup the $results array
             * with only the entry ID/s in the same order as returned by the relevance score.
             */
            foreach ($scored as $entry) {
                $results[] = isset($entry['id']) ? $entry['id'] : $entry['entry_id'];
            }
        }
        /*
         * If no results are found, perhaps to the way MySQL performs FULLTEXT queries, FULLTEXT search being disabled
         * or the DB not supporting FULLTEXT, run a LIKE query.
         *
         * Perform the search on each table individually because joining the tables doesn't scale when
         * there are a large number of entries.
         */
        if ((cnSettingsAPI::get('connections', 'search', 'keyword_enabled') && empty($results) || cnSettingsAPI::get('connections', 'search', 'fulltext_enabled') && empty($results)) && !empty($atts['terms'])) {
            /*
             * Only search the primary records if at least one fields is selected to be searched.
             */
            if (!empty($atts['fields']['entry'])) {
                $like = array();
                // Reset the like array.
                foreach ($atts['terms'] as $term) {
                    /*
                     * Attempt to secure the query using $wpdb->prepare() and like_escape()
                     *
                     * Since $wpdb->prepare() required var for each directive in the query string we'll use array_fill
                     * where the count based on the number of columns that will be searched.
                     */
                    $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['entry']) . ' LIKE %s ', array_fill(0, count($atts['fields']['entry']), '%' . $wpdb->esc_like($term) . '%'));
                }
                $sql = 'SELECT ' . CN_ENTRY_TABLE . '.id
									FROM ' . CN_ENTRY_TABLE . '
									WHERE (' . implode(') OR (', $like) . ')';
                //print_r($sql);
                $results = array_merge($results, $wpdb->get_col($sql));
                //print_r($results);die;
            }
            /*
             * Only search the address records if at least one fields is selected to be searched.
             */
            if (!empty($atts['fields']['address'])) {
                $like = array();
                // Reset the like array.
                foreach ($atts['terms'] as $term) {
                    /*
                     * Attempt to secure the query using $wpdb->prepare() and like_escape()
                     *
                     * Since $wpdb->prepare() required var for each directive in the query string we'll use array_fill
                     * where the count based on the number of columns that will be searched.
                     */
                    $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['address']) . ' LIKE %s ', array_fill(0, count($atts['fields']['address']), '%' . $wpdb->esc_like($term) . '%'));
                }
                $sql = 'SELECT ' . CN_ENTRY_ADDRESS_TABLE . '.entry_id
									FROM ' . CN_ENTRY_ADDRESS_TABLE . '
									WHERE (' . implode(') OR (', $like) . ')';
                //print_r($sql);
                $results = array_merge($results, $wpdb->get_col($sql));
                //print_r($results);
            }
            /*
             * Only search the phone records if the field is selected to be search.
             */
            if (!empty($atts['fields']['phone'])) {
                $like = array();
                // Reset the like array.
                foreach ($atts['terms'] as $term) {
                    /*
                     * Attempt to secure the query using $wpdb->prepare() and like_escape()
                     *
                     * Since $wpdb->prepare() required var for each directive in the query string we'll use array_fill
                     * where the count based on the number of columns that will be searched.
                     */
                    $like[] = $wpdb->prepare(implode(' LIKE %s OR ', $atts['fields']['phone']) . ' LIKE %s ', array_fill(0, count($atts['fields']['phone']), '%' . $wpdb->esc_like($term) . '%'));
                }
                $sql = 'SELECT ' . CN_ENTRY_PHONE_TABLE . '.entry_id
									FROM ' . CN_ENTRY_PHONE_TABLE . '
									WHERE (' . implode(') OR (', $like) . ')';
                //print_r($sql);
                $results = array_merge($results, $wpdb->get_col($sql));
                //print_r($results);
            }
        }
        return apply_filters('cn_search_results', $results, $atts);
    }
 /**
  * Import settings from a JSON encoded string.
  *
  * @access private
  * @since  8.3
  * @static
  *
  * @param string $json
  *
  * @return bool|string
  */
 public static function import($json)
 {
     $result = cnFunction::decodeJSON($json, TRUE);
     if (is_wp_error($result)) {
         return $result->get_error_message('json_decode_error');
     }
     foreach ($result as $pluginID => $options) {
         foreach ($options as $id => $value) {
             update_option($id, $value);
         }
     }
     return TRUE;
 }
 /**
  * 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);
 }