: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
], http_build_query(['action' => 'get_subscription', 'token' => $token]));
if ($response->code == 200 && !empty($response->raw_body) && is_object($response->body)) {
$subscription = json_encode($response->body);
if (JSON_ERROR_NONE == json_last_error()){
error_log($response->raw_body);
else if ($this->isExpiredToken($response)){
error_log($response->raw_body);
public function paymentSuccess(){
$email = $_REQUEST['email'];
$checkout_id = $_REQUEST['checkout_id'];
$this->setOption( 'boosts_email', $email);
$this->setOption( 'boosts_checkout_id', $checkout_id);
$this->deleteOption('boosts_token');
$this->deleteOption('boosts_subscription');
$domain = $_SERVER['HTTP_HOST'];
$url = FF_BOOST_SERVER . 'registration?shop=' . $domain;
'action' => 'domain_registration',
'checkout_id' => $checkout_id,
'options' => $this->getOption('options', false, false, true),
'fb_auth_options' => $this->getOption('fb_auth_options', false, false, true),
$data['wp_locale'] = json_encode($wp_locale);
$data['wp_timezone_string'] = get_option( 'timezone_string' );
$data['wp_date_format'] = get_option( 'date_format' );
$data['wp_time_format'] = get_option( 'time_format' );
if (false != ($la_facebook_access_token = get_option('la_facebook_access_token', false))){
$data['la_facebook_access_token'] = $la_facebook_access_token;
if (false != ($la_facebook_access_token_expires = get_option('la_facebook_access_token_expires', false))) {
$data['la_facebook_access_token_expires'] = $la_facebook_access_token_expires;
$response = Request::post($url, [
'Content-Type: application/x-www-form-urlencoded'
], http_build_query($data));
if ($response->code != 200) {
error_log($response->raw_body);
$this->deleteOption('boosts_email');
$this->deleteOption('boosts_checkout_id');
if ($response->code == 403 && isset($response->body['error']) && $response->body['error'] == 'Exceeded the limit on the number of domains') {
header('Location: ' . admin_url('admin.php?page=flow-flow-admin&subscription=exceeded_domains'), true, 301);
header('Location: ' . admin_url('admin.php?page=flow-flow-admin&subscription=1'), true, 301);
public function upgradeSubscription() {
if (null != ($token = $this->getToken())){
$plan_id = $_REQUEST['plan_id'];
$response = Request::post(FF_BOOST_SERVER . 'flow-flow/ff', [
'Content-Type: application/x-www-form-urlencoded'
], http_build_query(['action' => 'upgrade_subscription', 'token' => $token, 'plan_id' => $plan_id]));
if ($response->code == 200) {
$response->body = (array)$response->body;
if ($response->body['success']){
header('Location: ' . admin_url('admin.php?page=flow-flow-admin'), true, 301);
else if ($this->isExpiredToken($response)){
$this->upgradeSubscription();
error_log($response->raw_body);
error_log('FLOW-FLOW DEBUG: no subscription token' );
public function cancelSubscription() {
if (null != ($token = $this->getToken())){
$response = Request::post(FF_BOOST_SERVER . 'flow-flow/ff', [
'Content-Type: application/x-www-form-urlencoded'
], http_build_query(['action' => 'cancel_subscription', 'token' => $token]));
if ($response->code == 200) {
$response->body = (array)$response->body;
if ($response->body['success']){
$this->deleteOption('boosts_email');
$this->deleteOption('boosts_token');
$this->deleteOption('boosts_checkout_id');
$this->deleteOption('boosts_subscription');
$this->deleteBoostedFeeds();
header('Location: ' . admin_url('admin.php?page=flow-flow-admin'), true, 301);
else if ($this->isExpiredToken($response)){
$this->cancelSubscription();
error_log($response->raw_body);
error_log('FLOW-FLOW DEBUG: no subscription token' );
public function clearSubscriptionCache() {
if (isset($_REQUEST['email'])){
$email = $_REQUEST['email'];
$this->setOption( 'boosts_email', $email);
if (isset($_REQUEST['checkout_id'])){
$checkout_id = $_REQUEST['checkout_id'];
$this->setOption( 'boosts_checkout_id', $checkout_id);
$this->deleteOption('boosts_token');
$this->deleteOption('boosts_subscription');
public final function email_notification(){
$admin_email = get_option('admin_email');
if (!empty($admin_email)){
$disabled_feeds = $conn->getAll('SELECT * FROM ?n WHERE enabled = 1 AND system_enabled = 0 AND send_email = 0', $this->cache_table_name);
if (!empty($disabled_feeds)){
/** @noinspection PhpIncludeInspection */
include($this->context['root'] . 'views/email.php');
$message = ob_get_clean();
$headers[] = 'MIME-Version: 1.0';
$headers[] = 'Content-type: text/html; charset=iso-8859-1';
$headers[] = 'X-Mailer: PHP/' . phpversion();
// $headers[] = 'To: ' . $admin_email;
$headers[] = 'From: Social Stream Apps <' . $admin_email . '>';
$blog_name = htmlspecialchars_decode(get_bloginfo('name'));
$success = mail($admin_email, "[Flow-Flow] Broken feeds detected on " . $blog_name, $message, implode("\r\n", $headers));
$conn->beginTransaction();
foreach ( $disabled_feeds as $feed ) {
$success = $this->saveSource($feed['feed_id'], [ 'send_email' => 1 ] );
throw new Exception('Save source problem');
error_log('email_notification');
error_log($e->getMessage());
error_log($e->getTraceAsString());
$conn->rollbackAndClose();
$errorMessage = error_get_last();
error_log($errorMessage['message']);
public function modifySource($source, $changed_content = true, $with_errors = false){
$enabled = $source['enabled'];
$cache_lifetime = $source['cache_lifetime'];
$status = isset($source['status']) ? intval($source['status']) : 0;
$boosted = $source['boosted'];
unset($source['enabled']);
unset($source['last_update']);
unset($source['cache_lifetime']);
unset($source['boosted']);
if ($with_errors && isset($source['errors'])){
$errors = serialize($source['errors']);
if (isset($source['errors'])) unset($source['errors']);
if (isset($source['status'])) unset($source['status']);
if (isset($source['system_enabled'])) unset($source['system_enabled']);
'settings' => serialize((object)$source),
'enabled' => (int)LASettingsUtils::YepNope2ClassicStyle($enabled, true),
'system_enabled' => (int)LASettingsUtils::YepNope2ClassicStyle($enabled, true),
'changed_time' => time(),
'cache_lifetime' => $cache_lifetime,
'settings' => serialize((object)$source),
'enabled' => (int)LASettingsUtils::YepNope2ClassicStyle($enabled, true),
'system_enabled' => (int)LASettingsUtils::YepNope2ClassicStyle($enabled, true),
'cache_lifetime' => $cache_lifetime,
if ($changed_content) $up['last_update'] = '0';
if ($with_errors && !empty($errors)) $up['errors'] = $errors;
if ( false === $conn->query( 'INSERT INTO ?n SET `feed_id`=?s, ?u ON DUPLICATE KEY UPDATE ?u',
$this->cache_table_name, $id, $in, $up ) ) {
private function changedContent( $source, $old ) {
foreach ( $source as $key => $value ) {
if ($key == 'status' || $key == 'enabled' || $key == 'posts' || $key == 'errors' || $key == 'last_update' ||
$key == 'cache_lifetime' || $key == 'mod' || $key == 'posts') continue;
if ($old_value !== $value) {
public function getGeneralSettings(){
return new LAGeneralSettings($this->getOption('options', true), $this->getOption('fb_auth_options', true));
public function getOption( $optionName, $serialized = false, $lock_row = false, $without_cache = false ) {
$options = LADB::getOption($this->conn(), $this->option_table_name, $this->plugin_slug_down . '_' . $optionName, $serialized, $lock_row, $without_cache);
if ($optionName == 'options' && is_array($options)) {
$options['general-uninstall'] = get_option($this->plugin_slug_down . '_general_uninstall', LASettingsUtils::NOPE);
* @param false $serialized
public function setOption($optionName, $optionValue, $serialized = false, $cached = true){
LADB::setOption($this->conn(), $this->option_table_name, $this->plugin_slug_down . '_' . $optionName, $optionValue, $serialized, $cached);
public function deleteOption($optionName){
LADB::deleteOption($this->conn(), $this->option_table_name, $this->plugin_slug_down . '_' . $optionName);
public function streams(){
if ($this->init) return $this->streams;
throw new Exception('Don`t init data manager');
public function countFeeds(){
return LADB::countFeeds($this->conn(), $this->cache_table_name);
public function getStream($streamId){
return $this->streams[$streamId];
public function delete_stream(){
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$conn->beginTransaction();
$id = $_POST['stream-id'];
LADB::deleteStream($conn, $this->streams_table_name, $this->streams_sources_table_name, $id);
do_action('ff_after_delete_stream', $id);
$this->proxyRequest($_POST);
error_log($e->getMessage());
error_log($e->getTraceAsString());
$conn->rollbackAndClose();
public function canCreateCssFolder(){
$dir = WP_CONTENT_DIR . '/resources/' . $this->context['slug'] . '/css';
return mkdir($dir, 0777, true);
public function generateCss($stream){
$dir = WP_CONTENT_DIR . '/resources/' . $this->context['slug'] . '/css';
$filename = $dir . "/stream-id" . $stream->id . ".css";
$filename = $dir . '/stream-id' . $stream->id . '-'. get_current_blog_id() . '.css';
/** @noinspection PhpIncludeInspection */
include($this->context['root'] . 'views/stream-template-css.php');
$output = ob_get_clean();
$a = fopen($filename, 'w');
public function clone_stream(){
if (!current_user_can('manage_options') || !check_ajax_referer( 'flow_flow_nonce', 'security', false ) ) {
die( json_encode( [ 'error' => 'not_allowed' ] ) );
$stream = $_REQUEST['stream'];
// cleaning if error was saved in database stream model, can be removed in future, now it's needed for affected users
if ( isset( $stream['error'] ) ) unset( $stream['error'] );
$stream = (object)$stream;
$conn->beginTransaction();
if (false !== ($count = LADB::maxIdOfStreams( $conn, $this->streams_table_name))) {
$newId = (string) ($count + 1);
$stream->name = "{$stream->name} copy";
$stream->last_changes = time();
LADB::setStream($conn, $this->streams_table_name, $this->streams_sources_table_name, $newId, $stream);
$this->generateCss($stream);
$this->proxyRequest($_POST);
echo json_encode($stream);
throw new Exception('Can`t get a new id for the clone stream');
$conn->rollbackAndClose();
error_log('clone_stream error:');
error_log($e->getMessage());
error_log($e->getTraceAsString());
protected function saveGeneralSettings($settings){
if (isset($settings['flow_flow_options']['general-uninstall'])){
$general_uninstall_option_name = $this->plugin_slug_down . '_general_uninstall';
$value = ($settings['flow_flow_options']['general-uninstall'] === LASettingsUtils::YEP) ? LASettingsUtils::YEP : LASettingsUtils::NOPE;
if ( get_option( $general_uninstall_option_name) !== false ) {
update_option( $general_uninstall_option_name, $value );
add_option( $general_uninstall_option_name, $value, '', 'no' );
unset($settings['flow_flow_options']['general-uninstall']);
$this->setOption('options', $settings['flow_flow_options'], true);
protected abstract function customizeResponse(&$response);
protected abstract function clean_cache($options);
protected abstract function refreshCache($streamId, $force_load_cache = false);
protected function refreshCache4Source($id, $force_load_cache = false, $boosted = false){
$this->saveSource($id, ['status' => '2']);
$useIpv4 = $this->getGeneralSettings()->useIPv4();
$use = $this->getGeneralSettings()->useCurlFollowLocation();
$url = $this->getLoadCacheUrl( $id, $force_load_cache );
LASettingsUtils::get( $url, 1, false, false, $use, $useIpv4);
public function streamsWithStatus(){
if (false !== ($result = self::streams())){
public function sources(){
if ($this->init) return $this->sources;
throw new Exception('Don`t init data manager');
//TODO: refactor posts table does not have field with name stream_id
public function clean(array $streams = null){
$partOfSql = $streams == null ? '' : $conn->parse('WHERE `stream_id` IN (?a)', $streams);
if ($conn->beginTransaction()){
$conn->query('DELETE FROM ?n ?p', $this->posts_table_name, $partOfSql);
$conn->query('DELETE FROM ?n', $this->image_cache_table_name);
$conn->rollbackAndClose();
public function deleteFeed($feedId){
if ($conn->beginTransaction()){
$partOfSql = $conn->parse('WHERE `feed_id` = ?s', $feedId);
$conn->query('DELETE FROM ?n ?p', $this->posts_table_name, $partOfSql);
$conn->query('DELETE FROM ?n ?p', $this->post_media_table_name, $partOfSql);
$conn->query('DELETE FROM ?n ?p', $this->cache_table_name, $partOfSql);
$conn->query('DELETE FROM ?n ?p', $this->streams_sources_table_name, $partOfSql);