: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
Plugin Name: Custom Twitter Feeds
Plugin URI: https://smashballoon.com/custom-twitter-feeds
Description: Customizable X Feeds, formerly known as Twitter feeds, for your website
Author URI: https://smashballoon.com/
Text Domain: custom-twitter-feeds
Copyright 2024 Smash Balloon LLC (email : hey@smashballoon.com)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
if ( ! defined( 'CTF_URL' ) ) {
// Use smash Twitter if API V2 credentials are not defined.
$ctf_options = get_option('ctf_options', array());
define( 'CTF_DOING_SMASH_TWITTER', empty($ctf_options['consumer_key']) && empty($ctf_options['consumer_secret']));
define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
define( 'CTF_VERSION', '2.2.2' );
define( 'CTF_TITLE', 'Custom Twitter Feeds' );
define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
define( 'CTF_PRODUCT_NAME', 'Custom Twitter Feeds' );
if ( ! defined( 'CTF_SITE_ACCESS_TOKEN_KEY' ) ) {
define( 'CTF_SITE_ACCESS_TOKEN_KEY', 'site_access_token_free' );
if ( ! defined( 'SMASH_TWITTER_URL' ) ) {
define( 'SMASH_TWITTER_URL', 'https://ctf.smashballoon.com/api/' );
if ( ! defined( 'SMASH_TWITTER_TIMELINE_PATH' ) ) {
define( 'SMASH_TWITTER_TIMELINE_PATH', '1.1/timeline' );
if ( ! defined( 'SMASH_TWITTER_URL_EXTRA_GET_PARAMS' ) ) {
define( 'SMASH_TWITTER_URL_EXTRA_GET_PARAMS', '' );
if ( ! defined( 'OAUTH_PROCESSOR_URL' ) ) {
define( 'OAUTH_PROCESSOR_URL', 'https://connect.smashballoon.com/auth/tw/' );
if ( ! defined( 'CTF_PLUGIN_NAME' ) ) {
define( 'CTF_PLUGIN_NAME', 'Custom Twitter Feeds' );
if ( ! defined( 'CTF_PLUGIN_DIR' ) ) {
define( 'CTF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
if ( ! defined( 'CTF_PLUGIN_URL' ) ) {
define( 'CTF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
if ( ! defined( 'CTF_DBVERSION' ) ) {
define( 'CTF_DBVERSION', '1.5' );
if ( ! defined( 'CTF_FEED_LOCATOR' ) ) {
define( 'CTF_FEED_LOCATOR', 'ctf_feed_locator' );
if ( ! defined( 'CTF_BUILDER_DIR' ) ) {
define( 'CTF_BUILDER_DIR', CTF_PLUGIN_DIR . 'admin/builder/' );
if ( ! defined( 'CTF_BUILDER_URL' ) ) {
define( 'CTF_BUILDER_URL', CTF_PLUGIN_URL . 'admin/builder/' );
if ( ! defined( 'CTF_UPLOADS_NAME' ) ) {
define( 'CTF_UPLOADS_NAME', 'sb-twitter-feed-images' );
if ( ! defined( 'CTF_PLUGIN_NAME' ) ) {
define( 'CTF_PLUGIN_NAME', 'Custom Twitter Feed Free' );
if ( ! defined( 'CTF_FEEDS_POSTS_TABLE' ) ) {
define( 'CTF_FEEDS_POSTS_TABLE', 'ctf_feeds_posts' );
if ( ! defined( 'CTF_POSTS_TABLE' ) ) {
define( 'CTF_POSTS_TABLE', 'ctf_posts' );
if ( ! defined( 'CTF_LICENSE_URL' ) ) {
define( 'CTF_LICENSE_URL', 'https://smashballoon.com/' );
if ( ! defined( 'CTF_PLUGIN_BASENAME' ) ) {
define( 'CTF_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
if ( version_compare( phpversion(), '5.6', '<' ) ) {
if( !function_exists( 'ctf_check_php_notice' ) ){
function ctf_check_php_notice(){ ?>
<div class="notice notice-error">
<p><strong><?php echo esc_html__('Important:','custom-twitter-feeds') ?> </strong><?php echo esc_html__('Your website is using an outdated version of PHP. The Custom Twitter Feeds plugin requires PHP version 5.6 or higher and so has been temporarily deactivated.','custom-twitter-feeds') ?></p>
echo esc_html__('To continue using the plugin','custom-twitter-feeds') . ', ';
echo __('you can either manually reinstall the previous version of the plugin ','custom-twitter-feeds' );
echo esc_html__('or contact your host to request that they upgrade your PHP version to 5.6 or higher.','custom-twitter-feeds');
add_action( 'admin_notices', 'ctf_check_php_notice' );
return; //Stop until PHP version is fixed
require_once( CTF_URL . '/inc/widget.php' );
require_once( CTF_URL . '/inc/admin-hooks.php' );
function ctf_plugin_init() {
require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/blocks/class-ctf-blocks.php';
$ctf_blocks = new CTF_Blocks();
if ( $ctf_blocks->allow_load() ) {
require trailingslashit( CTF_PLUGIN_DIR ) . 'vendor/autoload.php';
include_once trailingslashit( CTF_PLUGIN_DIR ) . '/inc/ctf-functions.php';
if ( version_compare( PHP_VERSION, '5.3.0' ) >= 0
&& version_compare( get_bloginfo('version'), '4.6' , '>' ) ) {
$ctf_notifications = new TwitterFeed\Admin\CTF_Notifications();
$ctf_notifications->init();
$ctf_new_user = new TwitterFeed\Admin\CTF_New_User();
$ctf_global_settings = new TwitterFeed\Admin\CTF_Global_Settings();
$ctf_admin_notices = new TwitterFeed\Admin\CTF_Admin_Notices();
$ctf_tooltip_wizard = new TwitterFeed\Builder\CTF_Tooltip_Wizard();
$ctf_support = new TwitterFeed\Admin\CTF_Support();
$ctf_admin_notices = new TwitterFeed\Admin\CTF_Admin_Notices();
$ctf_about_us = new TwitterFeed\Admin\CTF_About_Us();
require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/Admin/addon-functions.php';
require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/Admin/CTF_Upgrader.php';
$ctf_upgrader = new TwitterFeed\Admin\CTF_Upgrader();
$cron_update_manager = new \TwitterFeed\SmashTwitter\CronUpdaterManager();
$cron_update_manager->hooks();
$error_reporter = new \TwitterFeed\SmashTwitter\Services\ErrorReporterService();
$error_reporter->init_hooks();
add_action( 'plugins_loaded', 'ctf_plugin_init' );
include_once trailingslashit( CTF_PLUGIN_DIR ) . '/inc/Builder/CTF_Feed_Builder.php';
function ctf_update_settings() {
$existing_deprecated_options = get_option( 'ctf_configure' );
$existing_options = get_option( 'ctf_options' );
update_option( 'ctf_version', CTF_VERSION );
if ( ! empty( $existing_deprecated_options ) && empty( $existing_options ) ) {
$merged_options = $existing_deprecated_options;
$merged_options = array_merge( $merged_options, get_option( 'ctf_customize', array() ) );
$merged_options = array_merge( $merged_options, get_option( 'ctf_style', array() ) );
update_option( 'ctf_options', $merged_options );
function ctf_check_for_db_updates() {
$db_ver = get_option( 'ctf_db_version', 0 );
if ( (float) $db_ver < 1.0 ) {
$wp_roles->add_cap( 'administrator', 'manage_twitter_feed_options' );
$ctf_statuses_option = get_option( 'ctf_statuses', array() );
if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
$options_set = get_option( 'ctf_options', false );
$ctf_statuses_option['first_install'] = 'from_update';
$ctf_statuses_option['first_install'] = time();
$ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
if ( $ctf_rating_notice_option === 'dismissed' ) {
$ctf_statuses_option['rating_notice_dismissed'] = time();
$ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
if ( $ctf_rating_notice_waiting === false
&& $ctf_rating_notice_option === false ) {
$time = 2 * WEEK_IN_SECONDS;
set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
update_option( 'ctf_rating_notice', 'pending', false );
update_option( 'ctf_statuses', $ctf_statuses_option, false );
update_option( 'ctf_db_version', CTF_DBVERSION );
if ( version_compare( $db_ver, '1.0.1', '<' ) ) {
TwitterFeed\CTF_Feed_Locator::create_table();
update_option( 'ctf_db_version', CTF_DBVERSION );
if ( version_compare( $db_ver, '1.4', '<' ) ) {
update_option( 'ctf_db_version', CTF_DBVERSION );
\TwitterFeed\Builder\CTF_Db::create_tables();
$ctf_statuses_option = get_option( 'ctf_statuses', array() );
$ctf_options = get_option( 'ctf_options', array() );
if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
$options_set = get_option( 'ctf_options', false );
$ctf_statuses_option['first_install'] = 'from_update';
$ctf_statuses_option['first_install'] = time();
$ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
if ( $ctf_rating_notice_option === 'dismissed' ) {
$ctf_statuses_option['rating_notice_dismissed'] = time();
$ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
if ( $ctf_rating_notice_waiting === false
&& $ctf_rating_notice_option === false ) {
$time = 2 * WEEK_IN_SECONDS;
set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
update_option( 'ctf_rating_notice', 'pending', false );
$options_support_legacy = false;
if ( isset( $ctf_options['access_token'] ) && ! empty( $ctf_options['access_token'] ) ) {
$options_support_legacy = true;
$base_settings = \TwitterFeed\CTF_Settings::legacy_shortcode_atts( array(), $ctf_options );
update_option( 'ctf_legacy_feed_settings', ctf_json_encode( $base_settings ), false );
// how many legacy feeds?
'html_location' => array( 'header', 'footer', 'sidebar', 'content', 'unknown' ),
'group_by' => 'shortcode_atts',
$feeds_data = \TwitterFeed\CTF_Feed_Locator::legacy_twitter_feed_locator_query( $args );
$num_legacy = count( $feeds_data );
$ctf_statuses_option['support_legacy_shortcode'] = $options_support_legacy;
$ctf_statuses_option['legacy_onboarding'] = array(
$ctf_statuses_option['support_legacy_shortcode'] = true;
$ctf_statuses_option['legacy_onboarding'] = array(
$shortcode_atts = ! empty( $feeds_data[0] ) && $feeds_data[0]['shortcode_atts'] != '[""]' ? json_decode( $feeds_data[0]['shortcode_atts'], true ) : array();
$shortcode_atts = is_array( $shortcode_atts ) ? $shortcode_atts : array();
$ctf_statuses_option['support_legacy_shortcode'] = $shortcode_atts;
$shortcode_atts['from_update'] = true;
$db = ctf_get_database_settings();
$base_settings = \TwitterFeed\CTF_Settings::legacy_shortcode_atts( $shortcode_atts, $db );
$feed_saver = new \TwitterFeed\Builder\CTF_Feed_Saver( false );
$feed_saver->set_data( $base_settings );
$feed_saver->set_feed_name( $feed_name );
$new_feed_id = $feed_saver->update_or_insert();
'new_feed_id' => $new_feed_id,
'legacy_feed_id' => $feeds_data[0]['feed_id'],
\TwitterFeed\CTF_Feed_Locator::update_legacy_to_builder( $args );
} elseif ( $num_legacy === 0 && $options_support_legacy ) {
$ctf_statuses_option['support_legacy_shortcode'] = true;
update_option( 'ctf_statuses', $ctf_statuses_option, false );
update_option( 'ctf_db_version', CTF_DBVERSION );
if ( ! wp_next_scheduled( 'ctf_feed_update' ) ) {
wp_schedule_event( time() + 60, 'twicedaily', 'ctf_feed_update' );
$wp_roles->add_cap( 'administrator', 'manage_twitter_feed_options' );
if ( version_compare( $db_ver, '1.5', '<' ) ) {
update_option( 'ctf_db_version', CTF_DBVERSION );
ctf_create_database_table();
$table_name = $wpdb->prefix . CTF_FEEDS_POSTS_TABLE;
$wpdb->query( "ALTER TABLE $table_name ADD COLUMN type VARCHAR(1000) DEFAULT '' NOT NULL" );
$wpdb->query( "ALTER TABLE $table_name ADD COLUMN term VARCHAR(1000) DEFAULT '' NOT NULL" );
$wpdb->query( "ALTER TABLE $table_name ADD INDEX type_term (term(140),type(51))" );
\TwitterFeed\SmashTwitter\CronUpdaterManager::schedule_cron_job();
$ctf_statuses_option = get_option( 'ctf_statuses', array() );
// to space out the API requests, we have the initial cron update scheduled a day + random number of hours.
$ctf_statuses_option['first_cron_update'] = mt_rand(0,23) * 3600 + time() + DAY_IN_SECONDS;
update_option( 'ctf_statuses', $ctf_statuses_option, false );
update_option( 'ctf_db_version', CTF_DBVERSION );
add_action( 'wp_loaded', 'ctf_check_for_db_updates' );
* include the admin files only if in the admin area
$ctf_version = get_option( 'ctf_version', false );
require_once( CTF_URL . '/inc/CtfAdmin.php' );
require_once( CTF_URL . '/inc/notices.php' );
* Generates the Twitter feed wherever the shortcode is placed
* @param $atts array shortcode arguments
function ctf_init( $atts, $preview_settings = false ) {
wp_enqueue_script( 'ctf_scripts' );
$twitter_feed = TwitterFeed\CtfFeed::init( $atts, null, 0, array(), 1, $preview_settings);
if ( isset( $twitter_feed->feed_options['feederror'] ) && ! empty( $twitter_feed->feed_options['feederror'] ) ) {
return "<span id='ctf-no-id'>" . sprintf( __( 'No feed found with the ID %1$s. Go to the %2$sAll Feeds page%3$s and select an ID from an existing feed.', 'custom-twitter-feeds' ), esc_html( $twitter_feed->feed_options['feed'] ), '<a href="' . esc_url( admin_url( 'admin.php?page=ctf-feed-builder' ) ) . '">', '</a>' ) . '</span><br /><br />';
// if there is an error, display the error html, otherwise the feed
if ( ! $twitter_feed->tweet_set || ($twitter_feed->missing_credentials && ! CTF_DOING_SMASH_TWITTER) || ! isset( $twitter_feed->tweet_set[0]['created_at'] ) ) {
if ( ! empty( $twitter_feed->tweet_set['errors'] ) ) {
$twitter_feed->maybeCacheTweets();
$twitter_feed->maybeCacheTweets(true);
$feed_html .= $twitter_feed->getTweetSetHtml();
if ( ! $twitter_feed->feed_options['persistentcache'] ) {
$twitter_feed->maybeCacheTweets();
$feed_html .= $twitter_feed->getTweetSetHtml();
add_shortcode( 'custom-twitter-feed', 'ctf_init' );
add_shortcode( 'custom-twitter-feeds', 'ctf_init' );
* Called via ajax to get more posts after the "load more" button is clicked
function ctf_get_more_posts() {
$shortcode_data = json_decode( str_replace( '\"', '"', sanitize_text_field( $_POST['shortcode_data'] ) ), true ); // necessary to unescape quotes
$last_id_data = isset( $_POST['last_id_data'] ) ? sanitize_text_field( $_POST['last_id_data'] ) : '';
$num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
$ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
$feed = isset( $_POST['v2feed'] ) ? sanitize_key( $_POST['v2feed'] ) : '';
if ( empty( $shortcode_data ) ) {
$shortcode_data = array();
$shortcode_data['feed'] = $feed;
$is_pagination = empty( $last_id_data ) ? 0 : 1;
$persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
$twitter_feed = TwitterFeed\CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
if ( ! CTF_DOING_SMASH_TWITTER && ! $twitter_feed->feed_options['persistentcache'] ) {
$twitter_feed->maybeCacheTweets();
$feed_id = isset( $_POST['feed_id'] ) ? sanitize_text_field( $_POST['feed_id'] ) : 'unknown';
$location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
$post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
ctf_do_background_tasks( $feed_details );
echo $twitter_feed->getItemSetHtml( $is_pagination );
add_action( 'wp_ajax_nopriv_ctf_get_more_posts', 'ctf_get_more_posts' );
add_action( 'wp_ajax_ctf_get_more_posts', 'ctf_get_more_posts' );
function ctf_do_locator() {
if ( ! isset( $_POST['feed_id'] ) || strpos( $_POST['feed_id'], 'ctf' ) === false ) {
$feed_id = sanitize_text_field( $_POST['feed_id'] );
$atts_raw = isset( $_POST['atts'] ) ? json_decode( stripslashes( $_POST['atts'] ), true ) : array();
if ( is_array( $atts_raw ) ) {
array_map( 'sanitize_text_field', $atts_raw );
$atts = $atts_raw; // now sanitized
$location = isset( $_POST['location'] ) && in_array( $_POST['location'], array( 'header', 'footer', 'sidebar', 'content' ), true ) ? sanitize_text_field( $_POST['location'] ) : 'unknown';
$post_id = isset( $_POST['post_id'] ) && $_POST['post_id'] !== 'unknown' ? (int)$_POST['post_id'] : 'unknown';
ctf_do_background_tasks( $feed_details );