: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Creates the settings for the feed and outputs the html
use TwitterFeed\CTF_Parse;
use TwitterFeed\CtfCache;
use TwitterFeed\CTF_Feed;
use TwitterFeed\CTF_Settings;
use TwitterFeed\V2\CtfOauthConnect;
use TwitterFeed\CTF_Feed_Locator;
use TwitterFeed\CTF_GDPR_Integrations;
use TwitterFeed\Builder\CTF_Feed_Builder;
use TwitterFeed\SmashTwitter\ErrorReport;
use TwitterFeed\SmashTwitter\SettingsFilter;
use TwitterFeed\SmashTwitter\TweetFilterer;
use TwitterFeed\SmashTwitter\TweetSetModifier;
if ( ! defined( 'ABSPATH' ) ) {
public $errors = array();
private $num_needed_input;
public $feed_options = array();
public $missing_credentials;
protected $transient_data = false;
protected $raw_shortcode_atts;
private $num_tweets_needed;
private $check_for_duplicates = false;
private $persistent_index;
public $unique_legacy_id;
* retrieves and sets options that apply to the feed
* @param array $atts data from the shortcode
* @param string $last_id_data the last visible tweet on the feed, empty string if first set
* @param int $num_needed_input this number represents the number left to retrieve after the first set
public function __construct($atts, $last_id_data, $num_needed_input, $preview_settings = false) {
$this->settings_filter = new SettingsFilter();
$this->error_report = new ErrorReport();
$this->atts = CTF_Settings::filter_atts_for_legacy($atts);
$this->raw_shortcode_atts = CTF_Settings::filter_atts_for_legacy($atts);
if( isset($this->atts['feed']) ){
$atts['feed'] = $this->atts['feed'];
$this->last_id_data = $last_id_data;
$this->num_needed_input = $num_needed_input;
$this->db_options = get_option( 'ctf_options', array() );
$this->is_legacy = false;
if ( ! empty( $atts['feed'] ) && $atts['feed'] !== 'legacy' ) {
$this->feed_id = $this->atts['feed'];
$this->feed_options = CTF_Settings::get_settings_by_feed_id( $atts['feed'], $preview_settings );
if($this->feed_options == false){
$this->feed_options['feederror'] = true;
$this->feed_options['feed'] = $this->feed_id;
$this->atts = $this->feed_options;
add_action( 'wp_footer', [ $this, 'get_feed_style' ] );
$ctf_statuses = get_option( 'ctf_statuses', array() );
if ( ! empty( $ctf_statuses['support_legacy_shortcode'] ) ) {
if($preview_settings !== false && is_array($preview_settings)){
$this->feed_options = wp_parse_args( $this->atts, $preview_settings );
$legacy_settings_option = self::get_legacy_feed_settings();
if ( empty( $legacy_settings_option ) ) {
$this->feed_options = CTF_Settings::legacy_shortcode_atts( $this->atts, $this->db_options );
$this->feed_options = wp_parse_args( $this->atts, $legacy_settings_option );
$this->atts = wp_parse_args( $this->atts, \TwitterFeed\Builder\CTF_Feed_Saver::settings_defaults() );
$this->feed_options = wp_parse_args( $this->feed_options, $this->atts );
$this->feed_options['persistentcache'] = $this->atts['persistentcache'];
add_action( 'wp_footer', [ $this, 'get_feed_style' ] );
$this->unique_legacy_id = rand( 1, time() );
if ( ! empty( $this->feed_options ) ) {
$this->feed_options['customizer'] = isset($atts['customizer']) && $atts['customizer'] == true ? true : false;
if ( ! empty( $atts['feed'] ) ) {
$this->feed_options['feed'] = $atts['feed'];
$this->feed_options['selfreplies'] = false;
$this->feed_options['includereplies'] = false;
$this->feed_options['is_legacy'] = $this->is_legacy;
* creates and returns all of the data needed to generate the output for the feed
* @param array $atts data from the shortcode
* @param string $last_id_data the last visible tweet on the feed, empty string if first set
* @param int $num_needed_input this number represents the number left to retrieve after the first set
* @return CtfFeed the complete object for the feed
public static function init( $atts, $last_id_data = '', $num_needed_input = 0, $ids_to_remove = array(), $persistent_index = 1, $preview_settings = false ){
if ( empty( $atts['feed'] ) ) {
$ctf_statuses = get_option( 'ctf_statuses', array() );
if ( empty( $ctf_statuses['support_legacy_shortcode'] ) ) {
$feed = new CtfFeed( $atts, $last_id_data, $num_needed_input, $preview_settings );
$feed->setCacheTypeOption();
if (CTF_DOING_SMASH_TWITTER || $feed->feed_options['persistentcache']) {
$feed->persistent_index = $persistent_index;
* creates all of the feed options with shortcode settings having the highest priority
public function setFeedOptions(){
$this->feed_options['num'] = isset($this->feed_options['num']) && !empty($this->feed_options['num']) ? $this->feed_options['num'] : 1;
$this->setFeedTypeAndTermOptions();
$this->setAccessTokenAndSecretOptions();
$this->setConsumerKeyAndSecretOptions();
$this->setDatabaseOnlyOptions( $db_only );
$this->setCacheTimeOptions();
$this->setIncludeExcludeOptions();
if ( CTF_GDPR_Integrations::doing_gdpr( $this->feed_options ) ) {
CTF_GDPR_Integrations::init();
$this->feed_options['usertimeline_includeretweets'] = true;
if ( CTF_DOING_SMASH_TWITTER ) {
$this->settings_filter->set_settings( $this->feed_options );
if ( empty( $this->feed_options['feed_types_and_terms'] ) ) {
$this->feed_options['feed_types_and_terms'] = array();
$this->settings_filter->set_feed_type_and_terms( $this->feed_options['feed_types_and_terms'] );
$this->settings_filter->filter_feed_type_and_terms();
$this->feed_options = $this->settings_filter->get_settings();
$this->feed_options['feed_types_and_terms'] = $this->settings_filter->get_feed_type_and_terms();
* uses the feed options to set the the tweets in the feed by using
* an existing set in a cache or by retrieving them from Twitter
protected function setTweetSet(){
$this->setTransientName();
if ( ! empty( $this->feed_options['feed'] ) ) {
$feed_id = $this->feed_options['feed'];
if ( ! empty( $this->last_id_data ) ) {
$page = $this->last_id_data;
$this->cache = new CtfCache( $feed_id, $this->feed_options['cache_time'], $page );
if (! empty($this->raw_shortcode_atts['doingcronupdate'])) {
$this->maybeSetTweetsFromTwitter();
$success = $this->maybeSetTweetsFromCache();
$is_persistent = $this->feed_options['persistentcache'] && ($this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag');
if ( ! $success && ! $is_persistent) {
$this->maybeSetTweetsFromTwitter();
} elseif ( ! $success && $is_persistent ) {
$this->errors['error_message'] = 'No Tweets returned';
if (empty($this->api_obj)) {
$this->api_obj = new \stdClass();
->api_obj->api_error_no = '';
->api_obj->api_error_message = 'No Tweets returned';
$this->tweet_set = false;
$this->num_tweets_needed = $this->numTweetsNeeded();
* the access token and secret must be set in order for the feed to work
* this function processes the user input and sets a flag if none are entered
private function setAccessTokenAndSecretOptions()
if ( CTF_DOING_SMASH_TWITTER ) {
$this->feed_options['access_token'] = 'missing';
$this->feed_options['access_token_secret'] = 'missing';
$this->feed_options['access_token'] = isset( $this->db_options['access_token'] ) && strlen( $this->db_options['access_token'] ) > 30 ? $this->db_options['access_token'] : 'missing';
$this->feed_options['access_token_secret'] = isset( $this->db_options['access_token_secret'] ) && strlen( $this->db_options['access_token_secret'] ) > 30 ? $this->db_options['access_token_secret'] : 'missing';
// verify that access token and secret have been entered
$this->setMissingCredentials();
* generates the flag if there are missing access tokens
private function setMissingCredentials() {
if ( $this->feed_options['access_token'] == 'missing' || $this->feed_options['access_token_secret'] == 'missing' ) {
$this->missing_credentials = true;
$this->missing_credentials = false;
* processes the consumer key and secret options
protected function setConsumerKeyAndSecretOptions(){
if (! empty( $this->db_options['consumer_key'] ) && ! empty($this->db_options['consumer_secret'] )) {
$this->feed_options['consumer_key'] = isset($this->db_options['consumer_key']) && strlen($this->db_options['consumer_key']) > 15 ? $this->db_options['consumer_key'] : 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
$this->feed_options['consumer_secret'] = isset($this->db_options['consumer_secret']) && strlen($this->db_options['consumer_secret']) > 30 ? $this->db_options['consumer_secret'] : 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
$this->feed_options['consumer_key'] = 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
$this->feed_options['consumer_secret'] = 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
* determines what value to use and saves it for the appropriate key in the feed_options array
* @param $options mixed the key or array of keys to be set
* @param $options_page string options page this setting is set on
* @param string $default default value to use if there is no user input
public function setDatabaseOnlyOptions( $options, $default = '' )
if ( is_array( $options ) ) {
foreach ( $options as $option ) {
$this->feed_options[$option] = isset( $this->db_options[$option] ) && ! empty( $this->db_options[$option] ) ? $this->db_options[$option] : $default;
$this->feed_options[$options] = isset( $this->db_options[$options] ) && ! empty( $this->db_options[$options] ) ? $this->db_options[$options] : $default;
* determines what value to use and saves it for the appropriate key in the feed_options array
* @param $options mixed the key or array of keys to be set
* @param $options_page string options page this setting is set on
* @param string $default default value to use if there is no user input
public function setStandardTextOptions( $options, $default = '' )
if ( is_array( $options ) ) {
foreach ( $options as $option ) {
$this->feed_options[$option] = isset( $this->atts[$option] ) ? esc_attr( __( $this->atts[$option], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$option] ) ? esc_attr( $this->db_options[$option] ) : $default );
$this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( __( $this->atts[$options], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
* creates the appropriate style attribute string for the text size setting
* @param $value mixed pixel size or other that the user has selected
* @return string string for the style attribute
public static function processTextSizeStyle( $value )
$processed_value = $value == 'inherit' ? '' : 'font-size: ' . $value . 'px;';
* determines what value to use and saves it for the appropriate key in the feed_options array
* @param $options mixed the key or array of keys to be set
* @param string $default default value to use if there is no user input
public function setTextSizeOptions( $options, $default = '' )
if ( is_array( $options ) ) {
foreach ( $options as $option ) {
$this->feed_options[$option] = isset( $this->atts[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$option] ) ) : ( isset( $this->db_options[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$option] ) ) : $default );
$this->feed_options[$options] = isset( $this->atts[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$options] ) ) : ( isset( $this->db_options[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$options] ) ) : $default );
* determines what value to use and saves it for the appropriate key in the feed_options array
* @param $options mixed the key or array of keys to be set
* @param $property string name of the property to be set
* @param string $default default value to use if there is no user input
public function setStandardStyleProperty( $options, $property, $default = '' )
if ( is_array( $options ) ) {
foreach ( $options as $option ) {
$this->feed_options[$option] = isset( $this->atts[$option] ) && $this->atts[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$option] ) . ';' : ( isset( $this->db_options[$option] ) && $this->db_options[$option] != '#' && $this->db_options[$option] != '' && $this->db_options[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$option] ) . ';' : $default );
$this->feed_options[$options] = isset( $this->atts[$options] ) && $this->atts[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$options] ) . ';' : ( isset( $this->db_options[$options] ) && $this->db_options[$options] != '#' && $this->db_options[$options] != '' && $this->db_options[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$options] ) . ';' : $default );
* determines what value to use and saves it for the appropriate key in the feed_options array
* @param $options mixed the key or array of keys to be set
* @param bool|true $default default value to use if there is no user input
public function setStandardBoolOptions( $options, $default = true )
if ( is_array( $options ) ) {
foreach ( $options as $option ) {
$this->feed_options[$option] = isset( $this->atts[$option] ) ? ( $this->atts[$option] === 'true' ) : ( isset( $this->db_options[$option] ) ? (bool) $this->db_options[$option] : (bool) $default );
$this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( $this->atts[$options] ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
* sets the width and height of the feed based on user input
public function setDimensionOptions(){
$this->feed_options['width'] = isset( $this->atts['width'] ) ? 'width: '. esc_attr( $this->atts['width'] ) .';' : ( ( isset( $this->db_options['width'] ) && $this->db_options['width'] != '' ) ? 'width: '. esc_attr( $this->db_options['width'] ) . ( isset( $this->db_options['width_unit'] ) ? esc_attr( $this->db_options['width_unit'] ) : '%' ) . ';' : '' );
$this->feed_options['height'] = isset( $this->atts['height'] ) ? 'height: '. esc_attr( $this->atts['height'] ) .';' : ( ( isset( $this->db_options['height'] ) && $this->db_options['height'] != '' ) ? 'height: '. esc_attr( $this->db_options['height'] ) . ( isset( $this->db_options['height_unit'] ) ? esc_attr( $this->db_options['height_unit'] ) : 'px' ) . ';' : '' );
* sets the cache time based on user input
public function setCacheTimeOptions(){
if ( ! empty( $this->raw_shortcode_atts ) && ! empty( $this->raw_shortcode_atts['doingcronupdate'] ) ) {
$this->feed_options['cache_time'] = 60;
$user_cache = isset($this->db_options['cache_time']) ? ((int)$this->db_options['cache_time'] * (int)$this->db_options['cache_time_unit']) : HOUR_IN_SECONDS;
$caching_type = ! empty( $this->db_options['ctf_caching_type'] ) ? $this->db_options['ctf_caching_type'] : 'page';
if ( empty( $this->raw_shortcode_atts['feed'] ) || $caching_type === 'page' ) {
$this->feed_options['cache_time'] = max($user_cache, 60);
$this->feed_options['cache_time'] = DAY_IN_SECONDS + HOUR_IN_SECONDS;
* sets the number of tweets to retrieve
public function setTweetsToRetrieve(){
$min_tweets_to_retrieve = 10;
if ($this->num_needed_input < 1) {
if ($this->feed_options['num'] < 10) {
$count = max(round((int)$this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.6) , $min_tweets_to_retrieve);
elseif ($this->feed_options['num'] < 30) {
$count = round((int)$this->feed_options['num'] * (float)$this->feed_options['multiplier'] * 1.2);
$count = round((int)$this->feed_options['num'] * (float)$this->feed_options['multiplier']);
$count = max($this->num_needed_input, 50);
$this->feed_options['num'] = $this->num_needed_input;
$this->feed_options['count'] = min($count, 200);