) {} /** * Starts the cleaning procedure to fix escaped, corrupted data. * * @since 4.1.2 * * @return void */ public function scheduleUnescapeData() { aioseo()->core->cache->update( 'unslash_escaped_data_posts', time(), WEEK_IN_SECONDS ); aioseo()->actionScheduler->scheduleSingle( 'aioseo_unslash_escaped_data_posts', 120 ); } /** * Unlashes corrupted escaped data in posts. * * @since 4.1.2 * * @return void */ public function unslashEscapedDataPosts() { $postsToUnslash = apply_filters( 'aioseo_debug_unslash_escaped_posts', 200 ); $timeStarted = gmdate( 'Y-m-d H:i:s', aioseo()->core->cache->get( 'unslash_escaped_data_posts' ) ); $posts = aioseo()->core->db->start( 'aioseo_posts' ) ->select( '*' ) ->whereRaw( "updated < '$timeStarted'" ) ->orderBy( 'updated ASC' ) ->limit( $postsToUnslash ) ->run() ->result(); if ( empty( $posts ) ) { aioseo()->core->cache->delete( 'unslash_escaped_data_posts' ); return; } aioseo()->actionScheduler->scheduleSingle( 'aioseo_unslash_escaped_data_posts', 120, [], true ); foreach ( $posts as $post ) { $aioseoPost = Models\Post::getPost( $post->post_id ); foreach ( $this->getColumnsToUnslash() as $columnName ) { // Remove backslashes but preserve encoded unicode characters in JSON data. $aioseoPost->$columnName = aioseo()->helpers->pregReplace( '/\\\(?![uU][+]?[a-zA-Z0-9]{4})/', '', $post->$columnName ); } $aioseoPost->images = null; $aioseoPost->image_scan_date = null; $aioseoPost->videos = null; $aioseoPost->video_scan_date = null; $aioseoPost->save(); } } /** * Returns a list of names of database columns that should be unslashed when cleaning the corrupted data. * * @since 4.1.2 * * @return array The list of column names. */ protected function getColumnsToUnslash() { return [ 'title', 'description', 'keywords', 'keyphrases', 'page_analysis', 'canonical_url', 'og_title', 'og_description', 'og_image_custom_url', 'og_image_custom_fields', 'og_video', 'og_custom_url', 'og_article_section', 'og_article_tags', 'twitter_title', 'twitter_description', 'twitter_image_custom_url', 'twitter_image_custom_fields', 'schema_type_options', 'local_seo', 'options' ]; } /** * Get the first available page item for the current user. * * @since 4.1.3 * * @return bool|string The page slug. */ public function getFirstAvailablePageSlug() { foreach ( $this->pages as $slug => $page ) { // Ignore other pages. if ( $this->pageSlug !== $page['parent'] ) { continue; } if ( current_user_can( $this->getPageRequiredCapability( $slug ) ) ) { return $slug; } } return false; } /** * Appends a message to the default WordPress "trashed" message. * * @since 4.1.2 * * @param array $messages The original messages. * @return array The modified messages. */ public function appendTrashedMessage( $messages ) { // Let advanced users override this. if ( apply_filters( 'aioseo_redirects_disable_trashed_posts_suggestions', false ) ) { return $messages; } if ( function_exists( 'aioseoRedirects' ) && aioseoRedirects()->options->monitor->trash ) { return $messages; } if ( empty( $_GET['ids'] ) ) { // phpcs:ignore HM.Security.NonceVerification.Recommended return $messages; } $ids = array_map( 'intval', explode( ',', wp_unslash( $_GET['ids'] ) ) ); // phpcs:ignore HM.Security.NonceVerification.Recommended, HM.Security.ValidatedSanitizedInput.InputNotSanitized $posts = []; foreach ( $ids as $id ) { // We need to clone the post here so we can get a real permalink for the post even if it is not published already. $post = aioseo()->helpers->getPost( $id ); if ( ! is_a( $post, 'WP_Post' ) ) { continue; } $post->post_status = 'publish'; $post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID ); $posts[] = [ 'url' => str_replace( '__trashed', '', get_permalink( $post ) ), 'target' => '/', 'type' => 301 ]; } if ( empty( $posts ) ) { return $messages; } $url = aioseo()->slugMonitor->manualRedirectUrl( $posts ); $addRedirect = _n( 'Add Redirect to improve SEO', 'Add Redirects to improve SEO', count( $posts ), 'all-in-one-seo-pack' ); $messages['post']['trashed'] = $messages['post']['trashed'] . ' ' . $addRedirect . ' |'; $messages['page']['trashed'] = $messages['page']['trashed'] . ' ' . $addRedirect . ' |'; return $messages; } /** * Get the class name for the Score button. * Depending on the score the button should have different color. * * @since 4.0.0 * * @param int $score The content to retrieve from the remote URL. * @return string The class name for Score button. */ private function getScoreClass( $score ) { $scoreClass = 50 < $score ? 'score-orange' : 'score-red'; if ( 0 === $score ) { $scoreClass = 'score-none'; } if ( $score >= 80 ) { $scoreClass = 'score-green'; } return $scoreClass; } /** * Loads the plugin text domain. * * @since 4.1.4 * * @return void */ public function loadTextDomain() { aioseo()->helpers->loadTextDomain( 'all-in-one-seo-pack' ); } /** * Add the div for the modal portal. * * @since 4.2.5 * * @return void */ public function addAioseoModalPortal() { echo '
'; } /** * Outputs the element we can mount our footer promotion standalone Vue app on. * Also enqueues the assets. * * @since 4.3.6 * @version 4.4.3 * * @return void */ public function addFooterPromotion() { echo wp_kses_post( '' ); aioseo()->core->assets->load( 'src/vue/standalone/footer-links/main.js' ); } }