Headline
CVE-2023-0558: contentstudio-plugin.php in contentstudio/trunk – WordPress Plugin Repository
The ContentStudio plugin for WordPress is vulnerable to authorization bypass due to an unsecure token check that is susceptible to type juggling in versions up to, and including, 1.2.5. This makes it possible for unauthenticated attackers to execute functions intended for use by users with proper API keys.
1<?php2/*3Plugin Name: ContentStudio4Description: ContentStudio provides you with powerful blogging & social media tools to keep your audience hooked by streamlining the process for you to discover and share engaging content on multiple blogging & social media networks5Version: 1.2.76Author: ContentStudio7Author URI: http://contentstudio.io/8Plugin URI: http://contentstudio.io/9*/1011/**12 * add the meta SEO title for the web page if the post has SEO title available13 */14// include_once(ABSPATH . ‘wp-includes/pluggable.php’);15function cstu_add_wpseo_title()16{1718 global $post;19 if ($post) {20 if (isset($post->ID)) {21 if ($post->post_type == ‘post’) {22 $meta_title = get_post_meta($post->ID, ‘contentstudio_wpseo_title’);23 if(isset($meta_title[0]) && $meta_title[0]){24 return $meta_title[0];25 }26 }27 }28 }29}3031add_filter('pre_get_document_title’, ‘cstu_add_wpseo_title’);3233// Check for existing class34if (! class_exists(‘contentstudio’)) {3536 class ContentStudio37 {38 protected $api_url = 'https://api-prod.contentstudio.io/’;3940 protected $assets = 'https://contentstudio.io/img’;4142 private $version = “1.2.7";4344 protected $contentstudio_id = '’;4546 protected $blog_id = '’;4748 public $cstu_plugin;4950 protected $cstu_plugin_assets_dir;5152 const INVALID_MESSAGE = 'Invalid API Key, please make sure you have correct API key added.’;5354 const INVALID_MESSAGE_POST_API = 'Invalid API Key, please make sure you have correct API key added.’;5556 const UNKNOWN_ERROR_MESSAGE = ‘An Unknown error occurred while uploading media file.’;5758 /**59 * Add ContentStudio and settings link to the admin menu60 */61 public function __construct()62 {6364 $this->cstu_plugin = plugin_basename(__FILE__);6566 $this->cstu_plugin_assets_dir = plugin_dir_url(__FILE__) . "assets/";6768 $this->hooks();69 register_activation_hook(__FILE__, [$this, ‘activation’]);70 if (is_admin()) {71 $this->register_admin_hooks();72 }7374 // create db table and register webhooks75 $this->create_cstu_database_table();76 $this->register_global_hooks();7778 // register style7980 }81828384 /**85 * Creaate a database table for the SEO settings86 */87 public function create_cstu_database_table()88 {89 global $wpdb;90 $table_name = $wpdb->prefix."seo";91 $sql = "CREATE TABLE $table_name (92 id mediumint(9) NOT NULL AUTO_INCREMENT,93 post_id mediumint(9) NOT NULL default 0,94 title varchar (100) not NULL,95 description varchar (100) default null,96 slug varchar (100) ,PRIMARY KEY (id));";9798 require_once(ABSPATH.’wp-admin/includes/upgrade.php’);99 maybe_create_table($table_name, $sql);100 }101102 /**103 * Add a ContentStudio icon to the menu104 */105 public function add_menu()106 {107 if(current_user_can(‘editor’) || current_user_can(‘administrator’)){108 add_menu_page('ContentStudio Publisher’, 'ContentStudio’, 'edit_posts’, 'contentstudio_settings’, [109 $this,110 'connection_page’,111 ], $this->cstu_plugin_assets_dir . “menu-logo.png", ‘50.505’);112 // Settings link to the plugin listing page.113 }114 }115116 /**117 * Register global webhooks118 */119 public function register_global_hooks()120 {121122 add_action('init’, [$this, ‘cstu_verfiy_wp_user’]);123 add_action('init’, [$this, ‘cstu_check_token’]);124 add_action('init’, [$this, ‘cstu_get_blog_authors’]);125 add_action('init’, [$this, ‘cstu_get_blog_categories’]);126 add_action('init’, [$this, ‘cstu_create_new_post’]);127 add_action('init’, [$this, ‘cstu_update_post’]);128 add_action('init’, [$this, ‘cstu_is_installed’]);129 add_action('init’, [$this, ‘cstu_unset_token’]);130 add_action('init’, [$this, ‘cstu_get_metadata’]);131 add_action('init’, [$this, ‘cstu_create_nonce_for_post’]);132 add_action('init’, [$this, ‘cstu_is_upload_dir_exists’]);133 add_action('wp_head’, [$this, ‘add_cstu_custom_stylesheet’]);134 add_action('wp_head’, [$this, ‘add_cstu_meta_data’]);135 add_action('init’, [$this, ‘cstu_change_post_status’]);136 if (! function_exists(‘get_plugins’)) {137 require_once ABSPATH.’wp-admin/includes/plugin.php’;138 }139 }140141 /**142 * Add meta SEO title for the users’s shared blog posts.143 */144 function add_cstu_meta_data()145 {146 global $post;147 if ($post) {148 if (isset($post->ID)) {149 $meta_description = get_post_meta($post->ID, ‘contentstudio_wpseo_description’);150 if ($meta_description) {151 echo '<meta name="description” content="’.esc_attr($meta_description[0]).’” />’."\n";152 }153154 //$meta_title = get_post_meta($post->ID, ‘contentstudio_wpseo_title’);155 //echo ‘<title>’.$meta_title[0].’</title>’ . "\n";156157 //return $meta_title[0];158 }159 }160 }161162 /**163 * Adding a custom stylesheet to the WordPress blog, added due to the drag and drop snippet to be shown on the WordPress.164 */165166 function add_cstu_custom_stylesheet()167 {168 wp_register_style('contentstudio-curation’, // handle name169 plugin_dir_url(__FILE__).’_inc/contentstudio_curation.css’, // the URL of the stylesheet170 [], // an array of dependent styles171 ‘1.0’, // version number172 ‘screen’);173 }174175 /**176 * Registers admin-only hooks.177 */178 public function register_admin_hooks()179 {180181 add_action(‘admin_menu’, [$this, ‘add_menu’]);182183 add_filter("plugin_action_links_$this->cstu_plugin", [$this, ‘plugin_settings_link’], 2, 2);184185 // ajax requests186 add_action(‘wp_ajax_add_cstu_api_key’, [$this, ‘add_cstu_api_key’]);187 // Add check for activation redirection188 add_action(‘admin_init’, [$this, ‘activation_redirect’]);189 // load resources190191 }192193 public function hooks()194 {195196 register_activation_hook(__FILE__, [$this, ‘activation’]);197 register_deactivation_hook(__FILE__, [$this, ‘deactivation’]);198 }199200 /**201 * plugin activation, deactivation and uninstall hooks202 */203 public function activation()204 {205 register_uninstall_hook(__FILE__, [‘contentstudio’, ‘uninstall’]);206 // Set redirection to true207 add_option(‘contentstudio_redirect’, true);208 }209210 /**211 * on plugin deactivation212 */213 public function deactivation()214 {215 delete_option(‘contentstudio_redirect’);216 delete_option(‘contentstudio_token’);217 }218219 /**220 * Find all of the image urls that are in the content description221 *222 * @param $content mixed The data of the post223 * @return array|null result whether it contains images or not224 */225 public function cstu_find_all_images_urls($content)226 {227 $pattern = ‘/<img[^>]*src=["\’]([^"\’]*)[^"\’]*["\’][^>]*>/i’; // find img tags and retrieve src228 preg_match_all($pattern, $content, $urls, PREG_SET_ORDER);229 if (empty($urls)) {230 return null;231 }232 foreach ($urls as $index => &$url) {233 $images[$index][‘alt’] = preg_match(‘/<img[^>]*alt=["\’]([^"\’]*)[^"\’]*["\’][^>]*>/i’, $url[0], $alt) ? $alt[1] : null;234 $images[$index][‘url’] = $url = $url[1];235 }236 foreach (array_unique($urls) as $index => $url) {237 $unique_array[] = $images[$index];238 }239240 return $unique_array;241 }242 243 /**244 *245 * Check whether the plugin yoast is active246 *247 * @return bool status of the plugin true if active/false if inactive248 */249250 function is_yoast_active()251 {252 $active_plugins = apply_filters('active_plugins’, get_option(‘active_plugins’));253 foreach ($active_plugins as $plugin) {254 if (strpos($plugin, ‘wp-seo’)) {255 return true;256 }257 }258259 return false;260 }261262 /**263 *264 * Check whether the All in one SEO plugin is active265 *266 * @return bool status of the plugin true if active/false if inactive267 */268269 function is_all_in_one_seo_active()270 {271272 $active_plugins = apply_filters('active_plugins’, get_option(‘active_plugins’));273 foreach ($active_plugins as $plugin) {274 if (strpos($plugin, ‘all_in_one_seo_pack’)) {275 return true;276 }277 }278279 return false;280 }281282 // on plugin removal283 public function uninstall()284 {285 delete_option(‘contentstudio_redirect’);286 delete_option(‘contentstudio_token’);287 }288289 /**290 * Checks to see if the plugin was just activated to redirect them to settings291 */292 public function activation_redirect()293 {294295296 if (get_option('contentstudio_redirect’, false)) {297 // Redirect to settings page298 if (delete_option(‘contentstudio_redirect’)) {299 // If the plugin is being network activated on a multisite install300 if (is_multisite() && is_network_admin()) {301 $redirect_url = network_admin_url(‘plugins.php’);302 } else {303 $redirect_url = 'admin.php?page=contentstudio_settings’;304 }305306 if (wp_safe_redirect($redirect_url)) {307 // NOTE: call to exit after wp_redirect is per WP Codex doc:308 // http://codex.wordpress.org/Function_Reference/wp_redirect#Usage309 exit;310 }311 }312 }313 }314315 // filters plugins section316317 public function plugin_settings_link($actions, $file)318 {319 if (false !== strpos($file, ‘plugin’)) {320 $url = "admin.php?page=contentstudio_settings";321 $actions[‘settings’] = '<a href="’.esc_url($url).’">Settings</a>’;322 }323324 return $actions;325 }326327 // ajax section328329 public function add_cstu_api_key()330 {331 if (isset($_POST[‘data’])) {332333 if ($_POST[‘data’][‘security’]) {334 $nonce = sanitize_text_field($_POST[‘data’][‘security’]);335 if (! wp_verify_nonce($nonce, ‘ajax-nonce’)) {336 echo json_encode([‘status’ => false, ‘message’ => ‘Invalid security token provided.’]);337 die();338 }339 }340341 if (isset($_POST[‘data’][‘key’])) {342 if (strlen($_POST[‘data’][‘key’]) == 0) {343 echo json_encode([‘status’ => false, ‘message’ => ‘Please enter your API key’]);344 die();345 }346347 $api_key = sanitize_text_field($_POST[‘data’][‘key’]);348349 $response = json_decode($this->is_cstu_connected($api_key), true);350 if ($response[‘status’] == false) {351 echo json_encode($response);352 die();353 }354 if ($response[‘status’] == true) {355 // if successfully verified.356357 if (add_option('contentstudio_token’, $api_key) == false) {358 update_option('contentstudio_token’, $api_key);359 }360361 echo json_encode([362 ‘status’ => true,363 ‘message’ => 'Your blog has been successfully connected with ContentStudio.’,364 ]);365 die();366 } else {367 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);368 die();369 }370 } else {371 echo json_encode([‘status’ => false, ‘message’ => ‘Please enter your API key’]);372 die();373 }374 } else {375 echo json_encode([‘status’ => false, ‘message’ => ‘Please enter your API key’]);376 die();377 }378 }379380 public function cstu_is_installed()381 {382 if (isset($_REQUEST[‘cstu_is_installed’]) && ($_REQUEST[‘cstu_is_installed’])) {383 $plugin_data = get_plugin_data(__FILE__);384385 echo json_encode([386 ‘status’ => true,387 ‘message’ => 'ContentStudio plugin installed’,388 ‘version’ => $plugin_data[‘Version’],389 ]);390 die();391 }392 }393394 // check token direct ajax request.395 public function cstu_check_token()396 {397 if (isset($_REQUEST[‘token_validity’]) && isset($_REQUEST[‘token’])) {398 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);399400 // server side token validation required.401402 if ($valid) {403 echo json_encode([‘status’ => true, ‘message’ => ‘Token validated successfully.’]);404 die();405 } else {406 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);407 die();408 }409 }410 }411412 // validate token from the server to local.413 public function do_validate_cstu_token($token)414 {415 $token = sanitize_text_field($token);416 if (get_option(‘contentstudio_token’) === $token) {417 return true;418 }419420 return false;421 }422423 /**424 * validate username and password.425 * 426 */427 public function do_validate_wp_user($user_info)428 {429 $user_info = explode(":", base64_decode($user_info));430 $user = get_user_by('login’, $user_info[0]);431 if ($user && $user->ID != 0) {432 if (wp_check_password($user_info[1], $user->data->user_pass, $user->ID)) { // validate password433 if ($user->has_cap(‘publish_posts’) && $user->has_cap(‘edit_posts’)) {434 return [‘status’ => true, ‘message’ => ‘User validated successfully.’];435 } else {436 $error = “You don’t have permission to publish posts.";437 }438 } else {439 $error = “Invalid password.";440 }441 } else {442 $error = “Invalid username.";443 }444 return [‘status’ => false, ‘message’ => $error];445 }446447448 /**449 * verify wordpress user and set token450 */451 public function cstu_verfiy_wp_user()452 {453 if (isset($_REQUEST[‘cstu_verfiy_wp_user’]) && $_REQUEST[‘cstu_verfiy_wp_user’]) {454 try {455 if (isset($_REQUEST[‘username’], $_REQUEST[‘password’], $_REQUEST[‘token’]) && $_REQUEST[‘username’] && $_REQUEST[‘password’] && $_REQUEST[‘token’]) {456 $user = get_user_by('login’, $_REQUEST[‘username’]); // validate username457 if ($user && $user->ID != 0) {458 if (wp_check_password($_REQUEST[‘password’], $user->data->user_pass, $user->ID)) { // validate password459 if ($user->has_cap(‘publish_posts’) && $user->has_cap(‘edit_posts’)) {460 // set token for later requests validation.461 $token = sanitize_text_field($_REQUEST[‘token’]);462 update_option('contentstudio_token’, $token);463 464 echo json_encode([‘status’ => true, ‘message’ => ‘User verification completed successfully!’]);465 die();466 } else {467 echo json_encode([‘status’ => false, ‘message’ => “You don’t have permissions or the capabilities to publish or edit posts.”]);468 die();469 }470 } else {471 echo json_encode([‘status’ => false, ‘message’ => ‘The password that you entered is incorrect.’]);472 die();473 }474 } else {475 echo json_encode([‘status’ => false, ‘message’ => ‘No user exists with your provided username.’]);476 die();477 }478 } else {479 echo json_encode([‘status’ => false, ‘message’ => ‘Invalid request parameters.’]);480 die();481 }482 } catch (Exception $e) {483 echo json_encode([484 ‘status’ => false, ‘message’ => self::UNKNOWN_ERROR_MESSAGE,485 ‘line’ => $e->getLine(), ‘error_message’ => $e->getMessage()486 ]);487 }488 }489 }490491 /**492 * unset token ajax request493 */494 public function cstu_unset_token()495 {496 if (isset($_REQUEST[‘cstu_unset_token’]) && isset($_REQUEST[‘token’])) {497498 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);499500 if ($valid) {501 delete_option(‘contentstudio_token’);502 echo json_encode([‘status’ => true, ‘message’ => ‘Your API key has been removed successfully!’]);503 die();504 } else {505 // TODO: need to brainstorm here.506 echo json_encode([507 ‘status’ => false,508 ‘message’ => 'API key mismatch, please enter the valid API key.’,509 ]);510 die();511 }512 }513 }514515 /**516 * Check if the user blog is connected or not, send a remote request to the ContentStudio.517 *518 * @param $token string - token to send for the request519 * @return mixed520 */521 public function is_cstu_connected($token)522 {523 $plugin_data = get_plugin_data(__FILE__);524525 $payload = [526 ‘body’ => [527 ‘token’ => $token,528 “name” => get_bloginfo(“name”),529 “description” => get_bloginfo(“description”),530 “wpurl” => get_bloginfo(“wpurl”),531 “url” => get_bloginfo(“url”),532 ‘version’ => $plugin_data[‘Version’],533 ],534 ];535536 return wp_remote_post($this->api_url.’blog/wordpress_plugin’, $payload)[‘body’];537 }538539 /**540 * Gets blog meta data541 */542543 public function cstu_get_metadata()544 {545 if (isset($_REQUEST[‘cstu_get_metadata’], $_REQUEST[‘token’])) {546547 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);548549 if (!$valid) {550 echo json_encode([551 ‘status’ => false,552 ‘message’ => self::INVALID_MESSAGE_POST_API,553 ]);554 die();555 }556557558 $varsbloginfo = array(559 “name” => get_bloginfo( “name” ),560 “description” => get_bloginfo( “description” ),561 “wpurl” => get_bloginfo( “wpurl” ),562 “url” => get_bloginfo( “url” ),563 “language” => get_bloginfo( “language” ),564 “charset” => get_bloginfo( ‘charset’ ),565 “version” => get_bloginfo( “version” ),566 “timezone_string” => get_option( “timezone_string” ),567 “gmt_offset” => get_option( “gmt_offset” ),568 “server_time” => time(),569 “server_date” => date( ‘c’ ),570 “token” => get_option(‘contentstudio_token’),571 //"is_connected” => $this->is_cstu_connected($token),572 “plugin_version” => $this->version,573 “php_version” => PHP_VERSION,574 “php_disabled_fn” => ini_get( ‘disable_functions’ ),575 “php_disabled_cl” => ini_get( ‘disable_classes’ ),576 //"use_wp_json_encode” => $this->use_wp_json_encode,577 //"first_transport” => $http->_get_first_available_transport( $this->api ),578 // misc blog //579 “site_url” => get_option( ‘siteurl’ ),580 “pingback_url” => get_bloginfo( “pingback_url” ),581 “rss2_url” => get_bloginfo( “rss2_url” ),582 );583584 $varsbloginfo[“debug”] = array();585586 $theme = wp_get_theme();587 $varsbloginfo[“debug”][“theme”] = array();588 $varsbloginfo[“debug”][“theme”][“Name”] = $theme->get( ‘Name’ );589 $varsbloginfo[“debug”][“theme”][“ThemeURI”] = $theme->get( ‘ThemeURI’ );590 $varsbloginfo[“debug”][“theme”][“Description”] = $theme->get( ‘Description’ );591 $varsbloginfo[“debug”][“theme”][“Author”] = $theme->get( ‘Author’ );592 $varsbloginfo[“debug”][“theme”][“AuthorURI”] = $theme->get( ‘AuthorURI’ );593 $varsbloginfo[“debug”][“theme”][“Version”] = $theme->get( ‘Version’ );594 $varsbloginfo[“debug”][“theme”][“Template”] = $theme->get( ‘Template’ );595 $varsbloginfo[“debug”][“theme”][“Status”] = $theme->get( ‘Status’ );596 $varsbloginfo[“debug”][“theme”][“Tags”] = $theme->get( ‘Tags’ );597 $varsbloginfo[“debug”][“theme”][“TextDomain”] = $theme->get( ‘TextDomain’ );598 $varsbloginfo[“debug”][“theme”][“DomainPath”] = $theme->get( ‘DomainPath’ );599600 echo json_encode([601 ‘status’ => true,602 ‘message’ => 'Meta Data Of Blog’,603 ‘usermetadeata’ => $varsbloginfo,604 ]);605 die();606 }607 }608609 /**610 * Get a list of blog authors611 */612 public function cstu_get_blog_authors()613 {614 if (isset($_REQUEST[‘authors’], $_REQUEST[‘token’])) {615 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);616 if ($valid) {617618 if (!isset($_REQUEST[‘user_info’])) {619 echo json_encode([‘status’ => false, ‘message’ => ‘user_info is required’]);620 die();621 }622 // validate user info623 $result = $this->do_validate_wp_user($_REQUEST[‘user_info’]);624 if ($result[‘status’] == false) {625 echo json_encode($result);626 die();627 }628629630 $authors = get_users();631 $return_authors = [];632 foreach ($authors as $author) {633 if (!$author->has_cap(‘publish_posts’) || !$author->has_cap(‘edit_posts’)) {634 continue;635 }636 $return_authors[] = [637 “display_name” => $author->data->display_name,638 “user_id” => $author->ID,639 ];640 }641 echo json_encode($return_authors);642 die();643 } else {644 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);645 die();646 }647 }648 }649650 /**651 * Get a list of blog categories652 */653 public function cstu_get_blog_categories()654 {655 if (isset($_REQUEST[‘categories’], $_REQUEST[‘token’])) {656 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);657 if ($valid) {658659 if (!isset($_REQUEST[‘user_info’])) {660 echo json_encode([‘status’ => false, ‘message’ => ‘user_info is required’]);661 die();662 }663 // validate user info664 $result = $this->do_validate_wp_user($_REQUEST[‘user_info’]);665 if ($result[‘status’] == false) {666 echo json_encode($result);667 die();668 }669670 $args = [671 “hide_empty” => 0,672 “type” => "post",673 “orderby” => "name",674 “order” => "ASC",675 ];676 $categories = get_categories($args);677 $return_categories = [];678679 foreach ($categories as $category) {680 $return_categories[] = [681 “name” => $category->cat_name,682 “term_id” => $category->term_id,683 ];684 }685 echo json_encode($return_categories);686 die();687 } else {688 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);689 die();690 }691 }692 }693694 /**695 * Check if the wp-content/upload directory exists for the user blog.696 *697 * It is called from the ContentStudio Remote Server.698 */699 public function cstu_is_upload_dir_exists()700 {701 if (isset($_REQUEST) && isset($_REQUEST[‘cstu_is_upload_dir_exists’]) && isset($_REQUEST[‘token’])) {702 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);703 if ($valid) {704 $base_dir = wp_upload_dir()[‘basedir’];705 if (! is_dir($base_dir)) {706 echo json_encode([707 ‘status’ => true,708 ‘message’ => 'Your WordPress wp-content/uploads/ directory does not exist. Please create a directory first to enable featured images/media uploads.’,709 ]);710 } else {711 echo json_encode([‘status’ => false, ‘message’ => ‘Directory already exists.’]);712 }713 die();714 } else {715 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);716 die();717 }718 }719 }720721 /**722 * @param $post_id mixed This will check whether the seo data already exists in database723 *724 * @return bool true if exists/false if empty725 */726 public function cstu_seo_exists($post_id)727 {728 global $wpdb;729 $sql = $wpdb->prepare("select id from ".$wpdb->prefix."seo where post_id=’%d’", (int) $post_id);730 $get_post = $wpdb->get_results($sql);731 if (count($get_post)) {732 return true;733 }734735 return false;736 }737738 /**739 * Create a nonce for create and update post.740 * This nonce will used for create and update post.741 * 742 */743 public function cstu_create_nonce_for_post()744 {745 if (isset($_REQUEST) && isset($_REQUEST[‘cstu_create_nonce_for_post’], $_REQUEST[‘token’])) {746 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);747 if ($valid) {748749 if (!isset($_REQUEST[‘user_info’])) {750 echo json_encode([‘status’ => false, ‘message’ => ‘user_info is required’]);751 die();752 }753 // validate user info754 $result = $this->do_validate_wp_user($_REQUEST[‘user_info’]);755 if ($result[‘status’] == false) {756 echo json_encode($result);757 die();758 }759760 $nonce = wp_create_nonce(‘cstu_nonce_for_post’);761 echo json_encode([‘status’ => true, ‘message’ => 'Nonce created successfully’, ‘nonce’ => $nonce]);762 die();763 } else {764 echo json_encode([‘status’ => false, ‘message’ => self::INVALID_MESSAGE]);765 die();766 }767 }768 }769770 /**771 * Create a new WordPress post, action is called from the REMOTE ContentStudio Server.772 * This action is called from the ContentStudio Remote Server.773 * Post data is sent from the ContentStudio Remote Server.774 * Request will be validated using the token (contentstudio_token) and wordpress nonce.775 *776 */777 public function cstu_create_new_post()778 {779 if (isset($_REQUEST) && isset($_REQUEST[‘cstu_create_new_post’], $_REQUEST[‘token’], $_REQUEST[‘nonce’])) {780781 // validate the token782783 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);784 if ($valid) {785786 // check if the nonce is valid787 if (!wp_verify_nonce($_REQUEST[‘nonce’], ‘cstu_nonce_for_post’)) {788 echo json_encode([‘status’ => false, ‘message’ => ‘Invalid wordpress nonce’, ‘invalid_nonce’ => true]);789 die();790 }791792 if (!isset($_REQUEST[‘user_info’])) {793 echo json_encode([‘status’ => false, ‘message’ => ‘user_info is required’]);794 die();795 }796797 $result = $this->do_validate_wp_user($_REQUEST[‘user_info’]);798 if ($result[‘status’] == false) {799 echo json_encode($result);800 die();801 }802803 // request post title is available804805 if (isset($_REQUEST[‘post’])) {806 $post_title = sanitize_text_field($_REQUEST[‘post’][‘post_title’]);807 // check for the post title and make sure it does not exists.808809 if (isset($post_title) && $post_title) {810 global $wpdb;811 $post_title = wp_strip_all_tags($post_title);812 $sql = $wpdb->prepare("select ID from “.$wpdb->posts.” where post_title=’%s’ AND post_status = 'publish’", $post_title);813 $get_posts_list = $wpdb->get_results($sql);814 if (count($get_posts_list)) {815 $cstu_post_update = get_page_by_title( $post_title, '’, ‘post’ );816 $getid = $cstu_post_update->ID;817 echo json_encode([818 ‘status’ => false,819 ‘message’ => "Post already exists on your blog with title '$getid’.",820 ]);821 die();822 }823 }824 }825826 // get list of categories827828 $categories = explode(',’, sanitize_text_field($_REQUEST[‘post’][‘post_category’]));829830 $this->kses_remove_filters();831 $post_id = 0;832 if (isset($_REQUEST[‘post’][‘post_id’]) && $_REQUEST[‘post’][‘post_id’]) {833 $post_id = (int) sanitize_text_field($_REQUEST[‘post’][‘post_id’]);834 }835836 $tags = [];837 if (isset($_REQUEST[‘post’][‘tags’]))838 $tags = explode(',’, sanitize_text_field($_REQUEST[‘post’][‘tags’]));839840 // insert the post841 $post_author = (int) sanitize_text_field($_REQUEST[‘post’][‘post_author’]);842 $post_content = sanitize_meta('post_content’, $_REQUEST[‘post’][‘post_content’], ‘post’);843 $post_status = sanitize_text_field($_REQUEST[‘post’][‘post_status’]);844 $post_title = sanitize_text_field($_REQUEST[‘post’][‘post_title’]);845846 $post = wp_insert_post([847 ‘ID’ => $post_id,848 ‘post_title’ => $post_title,849 ‘post_author’ => $post_author,850 ‘post_content’ => $post_content,851 ‘post_status’ => $post_status,852 ‘post_category’ => $categories,853 ‘tags_input’ => $tags854 ]);855856 if (! $post or $post == 0) {857 $post = wp_insert_post([858 ‘post_author’ => $post_author,859 ‘post_content’ => $post_content,860 ‘post_status’ => $post_status,861 ‘post_category’ => $categories,862 ‘tags_input’ => $tags863 ]);864 global $wpdb;865 $wpdb->update($wpdb->posts, [‘post_title’ => wp_strip_all_tags((string) $post_title)], [‘ID’ => $post]);866 // slug scenario867 }868869 // get post870 $get_post = get_post($post);871872 // set the tags873 if (isset($_REQUEST[‘post’][‘terms’])) $this->cstu_set_tags($get_post);874875 // seo settings876 $this->set_cstu_metadata_post($get_post);877 $this->set_cstu_yoast_settinsg($get_post);878 $this->set_cstu_all_in_one_seo($get_post);879880881 $post_response = [882 ‘post_id’ => $get_post->ID,883 ‘link’ => get_permalink($get_post->ID),884 ‘status’ => true,885 ‘allow_url_fopen’ => ini_get(‘allow_url_fopen’)886 ];887888 $this->uploadFeatureImages($post_response, $_REQUEST[‘post’][‘featured_image’], $post, $post_title);889 echo json_encode($post_response);890 die();891892 } else {893 echo json_encode([894 ‘status’ => false,895 ‘message’ => self::INVALID_MESSAGE_POST_API,896 ]);897 die();898 }899 }900 }901902 private function uploadFeatureImages(&$response,$image,$post,$post_title) {903 try {904 // reload the post again to get the latest url.905 if (isset($image) && $image) {906 $status_code = wp_remote_get($image)[‘response’][‘code’];907 // if the status is valid process for upload.908 if ($status_code == 301 || $status_code == 200) {909 $img = $this->cstu_generate_image($image, $post,$post_title);910911 if (!$img[‘status’]) {912 $response[‘status’] = false;913 $response[‘warning_message’] = $img[‘message’];914 $response[‘resp’] = $img;915 }916 } else {917 $response[‘status’] = false;918 $response[‘warning_message’] = 'Post featured image seems to be down. Image HTTP status code is '.$status_code;919 }920 }921 }922 catch (\Exception $e){923 $response[‘status’] = false;924 $response[‘message’] = self::UNKNOWN_ERROR_MESSAGE;925 $response[‘line’] = $e->getLine();926 $response[‘error_message’] = $e->getMessage();927 }928929 }930931 /**932 * Updates an existing WordPress post, action is called from the REMOTE ContentStudio Server.933 * This action is called from the ContentStudio Remote Server.934 * Post data is sent from the ContentStudio Remote Server.935 * Request will be validated using the token (contentstudio_token) and wordpress nonce.936 *937 */938 public function cstu_update_post()939 {940 if (isset($_REQUEST) && isset($_REQUEST[‘cstu_update_post’], $_REQUEST[‘token’], $_REQUEST[‘nonce’])) {941942 // validate the token943944 $valid = $this->do_validate_cstu_token($_REQUEST[‘token’]);945 if ($valid) {946947 // check if the nonce is valid948 if (!wp_verify_nonce($_REQUEST[‘nonce’], ‘cstu_nonce_for_post’)) {949 echo json_encode([‘status’ => false, ‘message’ => 'Invalid wordpress nonce’, ‘invalid_nonce’ => true]);950 die();951 }952953 if (!isset($_REQUEST[‘user_info’])) {954 echo json_encode([‘status’ => false, ‘message’ => ‘user_info is required’]);955 die();956 }957958 // validate the username and password959 $result = $this->do_validate_wp_user($_REQUEST[‘user_info’]);960 if ($result[‘status’] == false) {961 echo json_encode($result);962 die();963 }964965966 $categories = explode(',’, sanitize_text_field($_REQUEST[‘post’][‘post_category’]));967968 $this->kses_remove_filters();969 $post_id = 0;970 if (isset($_REQUEST[‘post’][‘post_id’]) && $_REQUEST[‘post’][‘post_id’]) {971 $post_id = (int) sanitize_text_field($_REQUEST[‘post’][‘post_id’]);972 }973974 // update the post975 $post_author = (int) sanitize_text_field($_REQUEST[‘post’][‘post_author’]);976 $post_content = sanitize_meta('post_content’, $_REQUEST[‘post’][‘post_content’], ‘post’);977 $post_status = sanitize_text_field($_REQUEST[‘post’][‘post_status’]);978 $post_title = sanitize_text_field($_REQUEST[‘post’][‘post_title’]);979980 $post = wp_update_post([981 ‘ID’ => $post_id,982 ‘post_title’ => $post_title,983 ‘post_author’ => $post_author,984 ‘post_content’ => $post_content,985 ‘post_status’ => $post_status,986 ‘post_category’ => $categories,987 ]);988989 if (! $post or $post == 0) {990 $post = wp_update_post([991 ‘post_title’ => $post_title,992 ‘post_author’ => $post_author,993 ‘post_content’ => $post_content,994 ‘post_status’ => $post_status,995 ‘post_category’ => $categories,996 ]);997 global $wpdb;998 $wpdb->update($wpdb->posts, [‘post_title’ => wp_strip_all_tags((string) $post_title)], [‘ID’ => $post]);999 // slug scenario1000 }10011002 // get post10031004 $get_post = get_post($post);10051006 // set the tags10071008 if (isset($_REQUEST[‘post’][‘terms’])) $this->cstu_set_tags($get_post);10091010 // seo settings1011 $this->set_cstu_metadata_post($get_post);1012 $this->set_cstu_yoast_settinsg($get_post);1013 $this->set_cstu_all_in_one_seo($get_post);10141015 // reload the post again to get the latest url.10161017 if (isset($_REQUEST[‘post’][‘featured_image’]) && $_REQUEST[‘post’][‘featured_image’]) {1018 // perform http request to see the status code of the image.1019 $status_code = wp_remote_get($_REQUEST[‘post’][‘featured_image’])[‘response’][‘code’];10201021 // if the status is valid process for upload.10221023 if ($status_code == 301 || $status_code == 200) {1024 $img = $this->cstu_generate_image($_REQUEST[‘post’][‘featured_image’], $post,$_REQUEST[‘post’][‘post_title’]);1025 if ($img[‘status’]) {1026 echo json_encode([1027 ‘status’ => true,1028 ‘post_id’ => $get_post->ID,1029 ‘link’ => get_permalink($get_post->ID),1030 ]);1031 die();1032 } else {1033 echo json_encode([1034 ‘status’ => false,1035 ‘warning_message’ => $img[‘message’],1036 ‘post_id’ => $get_post->ID,1037 ‘link’ => get_permalink($get_post->ID),1038 ]);1039 die();1040 }1041 } else {1042 echo json_encode([1043 ‘status’ => false,1044 ‘warning_message’ => 'Post featured image seems to be down. Image HTTP status code is '.$status_code,1045 ‘post_id’ => $get_post->ID,1046 ‘link’ => get_permalink($get_post->ID)//get_post_permalink($get_post->ID),1047 ]);1048 die();1049 }1050 } else {1051 echo json_encode([1052 ‘status’ => true,1053 ‘post_id’ => $get_post->ID,1054 ‘link’ => get_permalink($get_post->ID),1055 ]); // get_post_permalink($get_post->ID)1056 die();1057 }1058 } else {1059 echo json_encode([1060 ‘status’ => false,1061 ‘message’ => self::INVALID_MESSAGE_POST_API,1062 ]);1063 die();1064 }1065 }1066 /*else {1067 echo json_encode([‘status’ => false, ‘message’ => “error”]);1068 die();1069 }*/1070 }10711072 /**1073 * Set the meta description so that when we publish our content, we show that to the end-user instead of our personal one.1074 *1075 * @param $get_post object WordPress post that we retrieved.1076 */1077 public function set_cstu_metadata_post($get_post)1078 {1079 try {1080 // setting up meta description1081 $meta_description = null;1082 if (isset($_REQUEST[‘post’][‘post_meta_description’])) {1083 $meta_description = sanitize_text_field($_REQUEST[‘post’][‘post_meta_description’]);1084 }1085 if ($meta_description) {1086 if (! get_post_meta($get_post->ID, ‘contentstudio_wpseo_description’)) {1087 add_post_meta($get_post->ID, 'contentstudio_wpseo_description’, $meta_description, true);1088 } else {1089 update_post_meta($get_post->ID, 'contentstudio_wpseo_description’, $meta_description);1090 }1091 }1092 $meta_title = null;1093 if (isset($_REQUEST[‘post’][‘post_meta_title’])) {1094 $meta_title = sanitize_text_field($_REQUEST[‘post’][‘post_meta_title’]);1095 }10961097 if ($meta_title) {1098 if (! get_post_meta($get_post->ID, ‘contentstudio_wpseo_title’)) {1099 add_post_meta($get_post->ID, 'contentstudio_wpseo_title’, $meta_title, true);1100 } else {1101 update_post_meta($get_post->ID, 'contentstudio_wpseo_title’, $meta_title);1102 }1103 }11041105 $slug = null;1106 if (isset($_REQUEST[‘post’][‘post_meta_url’])) {1107 global $wpdb;11081109 $slug = sanitize_text_field($_REQUEST[‘post’][‘post_meta_url’]);1110 $value = wp_unique_post_slug($slug, $get_post->ID, $get_post->post_status, $get_post->post_type, $get_post->post_parent);1111 $slug = $value;11121113 wp_update_post([1114 ‘post_name’ => (string) $slug,1115 ‘ID’ => $get_post->ID,1116 ]);1117 //$wpdb->update($wpdb->posts, [‘post_name’ => (string) $slug], [‘ID’ => $get_post->ID]);1118 }1119 }1120 catch (\Exception $e){11211122 }11231124 }11251126 /**1127 * Configure the SEO settings for the YOAST SEO plugin.1128 *1129 * @param $post object - Post object so that we can get the ID of a POST.1130 */1131 public function set_cstu_yoast_settinsg($post)1132 {1133 try {1134 if ($this->is_yoast_active()) {1135 global $wpdb;1136 $sql = $wpdb->prepare("select object_id from ".$wpdb->prefix."yoast_seo_meta where object_id=’%d’", $post->ID);1137 $get_object = $wpdb->get_results($sql);1138 if (! count($get_object)) {1139 $wpdb->insert($wpdb->prefix."yoast_seo_meta", [1140 “object_id” => $post->ID,1141 “internal_link_count” => 0,1142 “incoming_link_count” => 0,1143 ]);1144 }1145 $wpdb->insert($wpdb->postmeta, [1146 “post_id” => $post->ID,1147 “meta_key” => "_yoast_wpseo_title",1148 “meta_value” => sanitize_text_field($_REQUEST[‘post’][‘post_meta_title’]),1149 ]);1150 $wpdb->insert($wpdb->postmeta, [1151 “post_id” => $post->ID,1152 “meta_key” => "_yoast_wpseo_metadesc",1153 “meta_value” => sanitize_text_field($_REQUEST[‘post’][‘post_meta_description’]),1154 ]);1155 }1156 }1157 catch (\Exception $e){11581159 }11601161 }11621163 /**1164 * Configure the SEO settings for the All-in-one SEO plugin.1165 *1166 * @param $post object - Post object so that we can get the ID of a POST.1167 */1168 public function set_cstu_all_in_one_seo($post)1169 {1170 try {1171 if ($this->is_all_in_one_seo_active()) {1172 global $wpdb;1173 $wpdb->insert($wpdb->postmeta, [1174 “post_id” => $post->ID,1175 “meta_key” => "_aioseop_description",1176 “meta_value” => sanitize_text_field($_REQUEST[‘post’][‘post_meta_description’]),1177 ]);1178 $wpdb->insert($wpdb->postmeta, [1179 “post_id” => $post->ID,1180 “meta_key” => "_aioseop_title",1181 “meta_value” => sanitize_text_field($_REQUEST[‘post’][‘post_meta_title’]),1182 ]);1183 $slug = sanitize_text_field($_REQUEST[‘post’][‘post_meta_url’]);1184 if ($slug) {1185 $wpdb->insert($wpdb->postmeta, [1186 “post_id” => $post->ID,1187 “meta_key” => "_wp_old_slug",1188 “meta_value” => $slug,1189 ]);1190 }1191 }1192 }1193 catch (\Exception $e){11941195 }11961197 }11981199 /**1200 * Download a featured image and store the web server of the user.1201 *1202 * @param $image_url - target url to download1203 * @param $post_id - post id for which it will be attached/1204 * @return array - return of a status true or false with a message.1205 */1206 public function cstu_generate_image($image_url, $post_id, $post_title)1207 {12081209 try {1210 //get the upload dir of a website1211 $upload_dir = wp_upload_dir();12121213 // if there is no upload dir folder made for the user website1214 if (isset($upload_dir[‘error’]) && $upload_dir[‘error’]) return [‘status’ => false, ‘message’ => $upload_dir[‘error’]];12151216 // check allow_url_fopen is disable or enable1217 if ( !ini_get(‘allow_url_fopen’) ) return [‘status’ => false, ‘message’ => ‘allow_url_fopen is disable from PHP Configuration.’];12181219 // check if the url contains query params or arguments, remove those.1220 if(strpos($image_url, ‘?’) !== false) $image_url = substr($image_url, 0, strpos($image_url, ‘?’));1221 if(strpos($image_url, ‘#’) !== false) $image_url = substr($image_url, 0, strpos($image_url, ‘#’));1222 $image_data = file_get_contents($image_url);12231224 // if the url contains the amazon url. download the image and get its mimetype1225 if (strpos($image_url, ‘contentstudioio.s3.amazonaws.com’) !== false) {12261227 $filename = basename($image_url);1228 $img_headers = wp_remote_get($image_url);1229 // check content type and assign a type of image to the filename.1230 switch ($img_headers[‘headers’][‘content-type’]){1231 case 'image/png’:1232 $filename .= '.png’;1233 break;1234 case 'image/jpg’:1235 case 'image/jpeg’:1236 $filename .= '.jpg’;1237 break;1238 case 'image/gif’:1239 $filename .= '.gif’;1240 break;1241 }1242 }1243 // if it is ytimg link, get the correct id by splitting it.1244 elseif (strpos($image_url, ‘ytimg.com’) !== false) $filename = explode('/’, $image_url)[4].’_’.basename($image_url);1245 else $filename = basename($image_url);12461247 $modified_filename = sanitize_file_name($post_title);1248 if(strpos($filename, ‘.’) === false){1249 $filename = $modified_filename . '.png’;1250 } else{1251 $filename = $modified_filename . substr($filename, strrpos($filename, ‘.’));1252 }12531254 // create a file with its name1255 if (wp_mkdir_p($upload_dir[‘path’])) $file = $upload_dir[‘path’].’/’.$filename;1256 else $file = $upload_dir[‘basedir’].’/’.$filename;125712581259 // put the content1260 $resp = file_put_contents($file, $image_data);1261 $wp_filetype = wp_check_filetype($filename, null);12621263 // prepare attachment payload1264 $attachment = [1265 ‘post_mime_type’ => $wp_filetype[‘type’],1266 ‘post_title’ => $filename,1267 ‘post_content’ => '’,1268 ‘post_status’ => ‘inherit’,1269 ];1270 $attach_id = wp_insert_attachment($attachment, $file, $post_id);12711272 // store the image and set it for the post12731274 require_once(ABSPATH.’wp-admin/includes/image.php’);1275 $attach_data = wp_generate_attachment_metadata($attach_id, $file);1276 $res1 = wp_update_attachment_metadata($attach_id, $attach_data);1277 $res2 = set_post_thumbnail($post_id, $attach_id);1278 update_post_meta($attach_id, '_wp_attachment_image_alt’, $post_title);1279 if ($res2) {1280 return [‘status’ => true];1281 } else {1282 return [‘status’ => false, ‘message’ => self::UNKNOWN_ERROR_MESSAGE];1283 }1284 }1285 catch (\Exception $e){1286 return [‘status’ => false, ‘message’ => self::UNKNOWN_ERROR_MESSAGE,1287 'line’=>$e->getLine(), ‘error_message’ => $e->getMessage()];1288 }12891290 }12911292 /**1293 * Render a ContentStudio plugin page.1294 */1295 public function connection_page()1296 {1297 if (! current_user_can(‘edit_posts’)) {1298 wp_die(__(‘You do not have sufficient permissions to access this page.’));1299 }13001301 $token = get_option(‘contentstudio_token’);1302 //$response = ['status’=>true]; //NOTE: for locally testing…1303 $response = json_decode($this->is_cstu_connected($token), true);13041305 $response[‘reconnect’] = false;1306 if (isset($_GET[‘reconnect’]) && $_GET[‘reconnect’] == ‘true’) {1307 $response[‘reconnect’] = true;1308 }13091310 $response[‘security_plugins’] = $this->cstu_check_installed_security_plugins();1311 // Save the data to the error log so you can see what the array format is like.13121313 $this->load_resources();13141315 include(sprintf("%s/page.php", dirname(__FILE__)));1316 }13171318 /**1319 * Analyzing the security plugins that the user may have installed, and giving them a headsup to1320 * whitelist our server’s IP address so that there are no problems while authentication being done1321 * with ContentStudio.1322 *1323 * NOTE: we are not modifying anything to these plugins, just checking their status wether they have been1324 * activated, if activated, show a notification to the user.1325 *1326 * @return array - returns list of an array that is used for displaying the name of the plugins.1327 */1328 function cstu_check_installed_security_plugins()1329 {1330 $activated_plugins = get_option(‘active_plugins’);1331 $response = [1332 ‘wordfence’ => $this->is_plugin_activated($activated_plugins, ‘wordfence/wordfence.php’),1333 ‘jetpack’ => $this->is_plugin_activated($activated_plugins, ‘jetpack/jetpack.php’),1334 ‘6scan’ => $this->is_plugin_activated($activated_plugins, ‘6scan-protection/6scan.php’),1335 ‘wp_security_scan’ => $this->is_plugin_activated($activated_plugins, ‘wp-security-scan/index.php’),1336 ‘wp_all_in_one_wp_security’ => $this->is_plugin_activated($activated_plugins, ‘all-in-one-wp-security-and-firewall/wp-security.php’),1337 ‘bulletproof_security’ => $this->is_plugin_activated($activated_plugins, ‘bulletproof-security/bulletproof-security.php’),1338 ‘better_wp_security’ => $this->is_plugin_activated($activated_plugins, ‘better-wp-security/better-wp-security.php’),1339 ‘limit_login_attempts_reloaded’ => $this->is_plugin_activated($activated_plugins, ‘limit-login-attempts-reloaded/limit-login-attempts-reloaded.php’),1340 ‘limit_login_attempts’ => $this->is_plugin_activated($activated_plugins, ‘limit-login-attempts/limit-login-attempts.php’),1341 ‘lockdown_wp_admin’ => $this->is_plugin_activated($activated_plugins, ‘lockdown-wp-admin/lockdown-wp-admin.php’),1342 ‘miniorange_limit_login_attempts’ => $this->is_plugin_activated($activated_plugins, ‘miniorange-limit-login-attempts/mo_limit_login_widget.php’),1343 ‘wp_cerber’ => $this->is_plugin_activated($activated_plugins, ‘wp-cerber/wp-cerber.php’),1344 ‘wp_limit_login_attempts’ => $this->is_plugin_activated($activated_plugins, ‘wp-limit-login-attempts/wp-limit-login-attempts.php’),1345 ‘sucuri_scanner’ => $this->is_plugin_activated($activated_plugins, ‘sucuri-scanner/sucuri.php’),1346 // ‘limit_login_attempts_reloaded’ => $this->is_plugin_activated($all_plugins, ‘limit-login-attempts-reloaded/limit-login-attempts-reloaded.php’),1347 ];13481349 return $response;1350 }13511352 /**1353 * Check if the value of the plugin name is found in the list of plugins that have been activated by the user.1354 *1355 * @param $plugins_list - list of activated plugins by the user1356 * @param $file_name - name of the file for the plugin1357 * @return bool1358 */1359 function is_plugin_activated($plugins_list, $file_name)1360 {1361 if (in_array($file_name, $plugins_list)) {1362 return true;1363 }13641365 return false;1366 }13671368 /**1369 * Load the style1370 */1371 function load_resources()1372 {1373 wp_enqueue_style('contentstudio.css’, plugin_dir_url(__FILE__).’_inc/contentstudio.css’, [], 0.01, false);1374 wp_enqueue_style('contentstudio_curation.css’, plugin_dir_url(__FILE__).’_inc/contentstudio_curation.css’, [], 0.01, false);1375 wp_enqueue_script('notify.min.js’, plugin_dir_url(__FILE__).’_inc/notify.min.js’, [‘jquery’], 0.01, false);1376 wp_enqueue_script('helper.js’, plugin_dir_url(__FILE__).’_inc/helper.js’, [‘jquery’], 0.01, false);1377 wp_localize_script('helper.js’, 'ajax_object’, [1378 ‘ajax_url’ => admin_url(‘admin-ajax.php’),1379 ‘security’ => wp_create_nonce(‘add_cstu_api_key’),1380 ]);1381 }13821383 /**1384 * Prepare a payload for the request.1385 *1386 * @param $url - fully qualified URL to target1387 * @param $body - payload to send to the request.1388 * @return mixed1389 */1390 public function prepare_request($url, $body)1391 {1392 $params = [1393 ‘method’ => 'POST’,1394 ‘body’ => $this->array_decode_entities($body),1395 ];13961397 return $this->perform_request($this->api_url.$url, $params);1398 }13991400 /**1401 * Provide a layer of compatibility by detecting and retrying after an initial error state. All attempts to1402 * access external resources should use this function.1403 *1404 * @param $url - fully qualified URL to target1405 * @param null $params - optional used in cases where caller wishes to POST1406 *1407 * @return mixed - result of $http->request(…) call or WP_Error instance1408 */1409 public function perform_request($url, $params = null)1410 {1411 $http = new WP_Http;14121413 $out = $this->perform_http_request($http, $url, false, $params);14141415 if (is_wp_error($out)) {1416 $out = $this->perform_http_request($http, $url, true, $params);1417 }14181419 return $out;1420 }14211422 /**1423 * @param $http - instance of an HTTP client, providing a `request` function1424 * @param $url - fully qualified URL to target1425 * @param bool|false $skip_ssl_verify - if true, will install filters that should prevent SSL cert validation1426 * for next request1427 * @param null $params - optional used in cases where caller wishes to POST1428 *1429 * @return mixed - result of $http->request(…) call or WP_Error instance1430 */1431 public function perform_http_request($http, $url, $skip_ssl_verify = false, $params = null)1432 {14331434 if (isset($skip_ssl_verify) && (true === $skip_ssl_verify)) {1435 // For the CURL SSL verifying, some websites does not have the valid SSL certificates.1436 add_filter('https_ssl_verify’, ‘__return_false’);1437 add_filter('https_local_ssl_verify’, ‘__return_false’);1438 }14391440 if (isset($params)) {1441 /** @noinspection PhpUndefinedMethodInspection */1442 return $http->request($url, $params);1443 } else {1444 /** @noinspection PhpUndefinedMethodInspection */1445 return $http->request($url);1446 }1447 }14481449 /**1450 * Decode entities from the array1451 *1452 * @param $array1453 * @return array1454 */14551456 public function array_decode_entities($array)1457 {1458 $new_array = [];14591460 foreach ($array as $key => $string) {1461 if (is_string($string)) {1462 $new_array[$key] = html_entity_decode($string, ENT_QUOTES);1463 } else {1464 $new_array[$key] = $string;1465 }1466 }14671468 return $new_array;1469 }14701471 /**1472 * @param string $param1473 */1474 public function sanitize(&$param = ‘’)1475 {1476 if (is_string($param)) {1477 $param = esc_sql($param);1478 $param = esc_html($param);1479 }1480 }14811482 /**1483 * Remove the filters before altering the post.1484 */1485 function kses_remove_filters()1486 {1487 // Post filtering1488 remove_filter('content_save_pre’, ‘wp_filter_post_kses’);1489 remove_filter('excerpt_save_pre’, ‘wp_filter_post_kses’);1490 remove_filter('content_filtered_save_pre’, ‘wp_filter_post_kses’);1491 }14921493 /**1494 * change status of the post from the draft to publish or publish to draft.1495 */14961497 public function cstu_change_post_status()1498 {1499 if (isset($_REQUEST) && isset($_REQUEST[‘cstu_change_post_status’])) {1500 $post_id = (int) sanitize_text_field($_REQUEST[‘post’][‘id’]);1501 $status = sanitize_text_field($_REQUEST[‘post’][‘status’]);1502 global $wpdb;1503 $sql = $wpdb->prepare("select post_status from “.$wpdb->posts.” where ID = '%d’", $post_id);1504 $post_status = $wpdb->get_results($sql)[0]->post_status;1505 if ($post_status == $status) {1506 echo json_encode([‘status’ => false, ‘message’ => “Your post status is already $post_status”]);1507 die();1508 }1509 $result = $wpdb->update($wpdb->posts, [“post_status” => $status], [“ID” => $post_id]);1510 if ($result) {1511 echo json_encode([‘status’ => true, ‘message’ => ‘Your post status has been updated’]);1512 die();1513 }1514 }1515 }15161517 /**1518 *1519 * Assign a post with the tags that the user may have assigned.1520 *1521 * @param $get_post mixed The post object to which the tags are to be assigned1522 */15231524 public function cstu_set_tags($get_post)1525 {1526 try {1527 global $wpdb;1528 $post_tags = sanitize_text_field($_REQUEST[‘post’][‘terms’]);1529 if (! is_array($post_tags)) {1530 $post_tags = explode(",", $post_tags);1531 }1532 $terms = [];1533 foreach ($post_tags as $tag) {1534 $term = term_exists($tag);1535 if ($term) {15361537 $sql = $wpdb->prepare("select term_taxonomy_id from “.$wpdb->term_taxonomy.” where term_id=’%s’", $term);1538 $result = $wpdb->get_results($sql);1539 $term_taxonomy_id = $result[0]->term_taxonomy_id;1540 $wpdb->query("UPDATE “.$wpdb->term_taxonomy.” SET count = count + 1 where term_id=".$term);1541 $terms[] = $term_taxonomy_id;1542 } else {1543 if ($_REQUEST[‘post’][‘post_status’] == ‘publish’) {1544 $new_term = wp_insert_term($tag, “category”);1545 $wpdb->query("UPDATE “.$wpdb->term_taxonomy.” SET count = count + 1 where term_id=".$new_term[“term_id”]);1546 $terms[] = $new_term[“term_taxonomy_id”];1547 } else {1548 $new_term = wp_insert_term($tag, “post_tag”);1549 $wpdb->query("UPDATE “.$wpdb->term_taxonomy.” SET count = count + 1 where term_id=".$new_term[“term_id”]);1550 $terms[] = $new_term[“term_taxonomy_id”];1551 }1552 }1553 }1554 foreach ($terms as $term) {1555 $data = [$get_post->ID, $term];1556 global $wpdb;1557 $post_query = $wpdb->prepare("insert into “.$wpdb->term_relationships.” (object_id,term_taxonomy_id) values (‘%s’,’%s’)", $data);1558 $wpdb->get_results($post_query);1559 }1560 }1561 catch (\Exception $e){15621563 }15641565 }1566 }15671568 function my_cstu_scripts() {1569 wp_register_style('contentstudio-dashboard’, plugin_dir_url(__FILE__).(“_inc/main.css”), [], ‘1.0.0’);15701571 wp_enqueue_style(‘contentstudio-dashboard’);15721573 }15741575 add_action( 'wp_enqueue_scripts’, ‘my_cstu_scripts’ );15761577 return new ContentStudio();1578}