Logo; } if ( ! aioseo()->options->social->twitter->general->defaultImagePosts ) { aioseo()->options->social->twitter->general->defaultImagePosts = $siteLogo; } } } /** * Adds the image scan date column to our posts table. * * @since 4.0.5 * * @return void */ public function addImageScanDateColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'image_scan_date' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD image_scan_date datetime DEFAULT NULL AFTER images" ); // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; } } /** * Modifes the default value of the twitter_use_og column. * * @since 4.0.6 * * @return void */ protected function disableTwitterUseOgDefault() { if ( aioseo()->core->db->tableExists( 'aioseo_posts' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} MODIFY twitter_use_og tinyint(1) DEFAULT 0" ); } } /** * Modifes the default value of the robots_max_imagepreview column. * * @since 4.0.6 * * @return void */ protected function updateMaxImagePreviewDefault() { if ( aioseo()->core->db->tableExists( 'aioseo_posts' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} MODIFY robots_max_imagepreview varchar(20) DEFAULT 'large'" ); } } /** * Deletes duplicate records in our custom tables. * * @since 4.0.13 * * @return void */ public function removeDuplicateRecords() { $duplicates = aioseo()->core->db->start( 'aioseo_posts' ) ->select( 'post_id, min(id) as id' ) ->groupBy( 'post_id having count(post_id) > 1' ) ->orderBy( 'count(post_id) DESC' ) ->run() ->result(); if ( empty( $duplicates ) ) { return; } foreach ( $duplicates as $duplicate ) { $postId = $duplicate->post_id; $firstRecordId = $duplicate->id; aioseo()->core->db->delete( 'aioseo_posts' ) ->whereRaw( "( id > $firstRecordId AND post_id = $postId )" ) ->run(); } } /** * Removes the location column. * * @since 4.0.17 * * @return void */ public function removeLocationColumn() { if ( aioseo()->core->db->columnExists( 'aioseo_posts', 'location' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} DROP location" ); } } /** * Clears the image data for WooCommerce Products so that we scan them again and include product gallery images. * * @since 4.1.2 * * @return void */ public function clearProductImages() { if ( ! aioseo()->helpers->isWooCommerceActive() ) { return; } aioseo()->core->db->update( 'aioseo_posts as ap' ) ->join( 'posts as p', 'ap.post_id = p.ID' ) ->where( 'p.post_type', 'product' ) ->set( [ 'images' => null, 'image_scan_date' => null ] ) ->run(); } /** * Adds the new flag to the notifications table. * * @since 4.1.3 * * @return void */ public function addNotificationsNewColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_notifications', 'new' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_notifications'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD new tinyint(1) NOT NULL DEFAULT 1 AFTER dismissed" ); // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; aioseo()->core->db ->update( 'aioseo_notifications' ) ->where( 'new', 1 ) ->set( 'new', 0 ) ->run(); } } /** * Noindexes the WooCommerce cart, checkout and account pages. * * @since 4.1.3 * * @return void */ public function noindexWooCommercePages() { if ( ! aioseo()->helpers->isWooCommerceActive() ) { return; } $cartId = (int) get_option( 'woocommerce_cart_page_id' ); $checkoutId = (int) get_option( 'woocommerce_checkout_page_id' ); $accountId = (int) get_option( 'woocommerce_myaccount_page_id' ); $cartPage = Models\Post::getPost( $cartId ); $checkoutPage = Models\Post::getPost( $checkoutId ); $accountPage = Models\Post::getPost( $accountId ); $newMeta = [ 'robots_default' => false, 'robots_noindex' => true ]; if ( $cartPage->exists() ) { $cartPage->set( $newMeta ); $cartPage->save(); } if ( $checkoutPage->exists() ) { $checkoutPage->set( $newMeta ); $checkoutPage->save(); } if ( $accountPage->exists() ) { $accountPage->set( $newMeta ); $accountPage->save(); } } /** * Adds the new capabilities for all the roles. * * @since 4.1.3 * * @return void */ protected function accessControlNewCapabilities() { aioseo()->access->addCapabilities(); } /** * Migrate dynamic settings to a separate options structure. * * @since 4.1.4 * * @return void */ protected function migrateDynamicSettings() { $rawOptions = $this->getRawOptions(); $options = aioseo()->dynamicOptions->noConflict(); // Sitemap post type priorities/frequencies. if ( ! empty( $rawOptions['sitemap']['dynamic']['priority']['postTypes'] ) ) { foreach ( $rawOptions['sitemap']['dynamic']['priority']['postTypes'] as $postTypeName => $data ) { if ( $options->sitemap->priority->postTypes->has( $postTypeName ) ) { $options->sitemap->priority->postTypes->$postTypeName->priority = $data['priority']; $options->sitemap->priority->postTypes->$postTypeName->frequency = $data['frequency']; } } } // Sitemap taxonomy priorities/frequencies. if ( ! empty( $rawOptions['sitemap']['dynamic']['priority']['taxonomies'] ) ) { foreach ( $rawOptions['sitemap']['dynamic']['priority']['taxonomies'] as $taxonomyName => $data ) { if ( $options->sitemap->priority->taxonomies->has( $taxonomyName ) ) { $options->sitemap->priority->taxonomies->$taxonomyName->priority = $data['priority']; $options->sitemap->priority->taxonomies->$taxonomyName->frequency = $data['frequency']; } } } // Facebook post type object types. if ( ! empty( $rawOptions['social']['facebook']['general']['dynamic']['postTypes'] ) ) { foreach ( $rawOptions['social']['facebook']['general']['dynamic']['postTypes'] as $postTypeName => $data ) { if ( $options->social->facebook->general->postTypes->has( $postTypeName ) ) { $options->social->facebook->general->postTypes->$postTypeName->objectType = $data['objectType']; } } } // Search appearance post type data. if ( ! empty( $rawOptions['searchAppearance']['dynamic']['postTypes'] ) ) { foreach ( $rawOptions['searchAppearance']['dynamic']['postTypes'] as $postTypeName => $data ) { if ( $options->searchAppearance->postTypes->has( $postTypeName ) ) { $options->searchAppearance->postTypes->$postTypeName->show = $data['show']; $options->searchAppearance->postTypes->$postTypeName->title = $data['title']; $options->searchAppearance->postTypes->$postTypeName->metaDescription = $data['metaDescription']; $options->searchAppearance->postTypes->$postTypeName->schemaType = $data['schemaType']; $options->searchAppearance->postTypes->$postTypeName->webPageType = $data['webPageType']; $options->searchAppearance->postTypes->$postTypeName->articleType = $data['articleType']; $options->searchAppearance->postTypes->$postTypeName->customFields = $data['customFields']; // Advanced settings. $advanced = ! empty( $data['advanced']['robotsMeta'] ) ? $data['advanced']['robotsMeta'] : null; if ( ! empty( $advanced ) ) { $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->default = $data['advanced']['robotsMeta']['default']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->noindex = $data['advanced']['robotsMeta']['noindex']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->nofollow = $data['advanced']['robotsMeta']['nofollow']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->noarchive = $data['advanced']['robotsMeta']['noarchive']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->noimageindex = $data['advanced']['robotsMeta']['noimageindex']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->notranslate = $data['advanced']['robotsMeta']['notranslate']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->nosnippet = $data['advanced']['robotsMeta']['nosnippet']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->noodp = $data['advanced']['robotsMeta']['noodp']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->maxSnippet = $data['advanced']['robotsMeta']['maxSnippet']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->maxVideoPreview = $data['advanced']['robotsMeta']['maxVideoPreview']; $options->searchAppearance->postTypes->$postTypeName->advanced->robotsMeta->maxImagePreview = $data['advanced']['robotsMeta']['maxImagePreview']; $options->searchAppearance->postTypes->$postTypeName->advanced->showDateInGooglePreview = $data['advanced']['showDateInGooglePreview']; $options->searchAppearance->postTypes->$postTypeName->advanced->showPostThumbnailInSearch = $data['advanced']['showPostThumbnailInSearch']; $options->searchAppearance->postTypes->$postTypeName->advanced->showMetaBox = $data['advanced']['showMetaBox']; $options->searchAppearance->postTypes->$postTypeName->advanced->bulkEditing = $data['advanced']['bulkEditing']; } if ( 'attachment' === $postTypeName ) { $options->searchAppearance->postTypes->$postTypeName->redirectAttachmentUrls = $data['redirectAttachmentUrls']; } } } } // Search appearance taxonomy data. if ( ! empty( $rawOptions['searchAppearance']['dynamic']['taxonomies'] ) ) { foreach ( $rawOptions['searchAppearance']['dynamic']['taxonomies'] as $taxonomyName => $data ) { if ( $options->searchAppearance->taxonomies->has( $taxonomyName ) ) { $options->searchAppearance->taxonomies->$taxonomyName->show = $data['show']; $options->searchAppearance->taxonomies->$taxonomyName->title = $data['title']; $options->searchAppearance->taxonomies->$taxonomyName->metaDescription = $data['metaDescription']; // Advanced settings. $advanced = ! empty( $data['advanced']['robotsMeta'] ) ? $data['advanced']['robotsMeta'] : null; if ( ! empty( $advanced ) ) { $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->default = $data['advanced']['robotsMeta']['default']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->noindex = $data['advanced']['robotsMeta']['noindex']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->nofollow = $data['advanced']['robotsMeta']['nofollow']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->noarchive = $data['advanced']['robotsMeta']['noarchive']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->noimageindex = $data['advanced']['robotsMeta']['noimageindex']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->notranslate = $data['advanced']['robotsMeta']['notranslate']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->nosnippet = $data['advanced']['robotsMeta']['nosnippet']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->noodp = $data['advanced']['robotsMeta']['noodp']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->maxSnippet = $data['advanced']['robotsMeta']['maxSnippet']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->maxVideoPreview = $data['advanced']['robotsMeta']['maxVideoPreview']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->robotsMeta->maxImagePreview = $data['advanced']['robotsMeta']['maxImagePreview']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->showDateInGooglePreview = $data['advanced']['showDateInGooglePreview']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->showPostThumbnailInSearch = $data['advanced']['showPostThumbnailInSearch']; $options->searchAppearance->taxonomies->$taxonomyName->advanced->showMetaBox = $data['advanced']['showMetaBox']; } } } } } /** * Fixes the default value for the post schema type. * * @since 4.1.5 * * @return void */ private function fixSchemaTypeDefault() { if ( aioseo()->core->db->tableExists( 'aioseo_posts' ) && aioseo()->core->db->columnExists( 'aioseo_posts', 'schema_type' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} MODIFY schema_type varchar(20) DEFAULT 'default'" ); } } /** * Add in image with/height columns and image URL for caching. * * @since 4.1.6 * * @return void */ protected function migrateOgTwitterImageColumns() { if ( aioseo()->core->db->tableExists( 'aioseo_posts' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; // OG Columns. if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'og_image_url' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD og_image_url text DEFAULT NULL AFTER og_image_type" ); } if ( aioseo()->core->db->columnExists( 'aioseo_posts', 'og_custom_image_height' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} CHANGE COLUMN og_custom_image_height og_image_height int(11) DEFAULT NULL AFTER og_image_url" ); } elseif ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'og_image_height' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD og_image_height int(11) DEFAULT NULL AFTER og_image_url" ); } if ( aioseo()->core->db->columnExists( 'aioseo_posts', 'og_custom_image_width' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} CHANGE COLUMN og_custom_image_width og_image_width int(11) DEFAULT NULL AFTER og_image_url" ); } elseif ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'og_image_width' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD og_image_width int(11) DEFAULT NULL AFTER og_image_url" ); } // Twitter image url columnn. if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'twitter_image_url' ) ) { aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD twitter_image_url text DEFAULT NULL AFTER twitter_image_type" ); } // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; } } /** * Adds the limit modified date column to our posts table. * * @since 4.1.8 * * @return void */ private function addLimitModifiedDateColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'limit_modified_date' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD limit_modified_date tinyint(1) NOT NULL DEFAULT 0 AFTER local_seo" ); // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; } } /** * Fixes tags that should not be in the search appearance taxonomy options. * * @since 4.1.9 * * @return void */ protected function fixTaxonomyTags() { $searchAppearanceTaxonomies = aioseo()->dynamicOptions->searchAppearance->taxonomies->all(); $replaces = [ '#breadcrumb_separator' => '#separator_sa', '#breadcrumb_' => '#', '#blog_title' => '#site_title' ]; foreach ( $searchAppearanceTaxonomies as $taxonomy => $searchAppearanceTaxonomy ) { aioseo()->dynamicOptions->searchAppearance->taxonomies->{$taxonomy}->title = str_replace( array_keys( $replaces ), array_values( $replaces ), $searchAppearanceTaxonomy['title'] ); aioseo()->dynamicOptions->searchAppearance->taxonomies->{$taxonomy}->metaDescription = str_replace( array_keys( $replaces ), array_values( $replaces ), $searchAppearanceTaxonomy['metaDescription'] ); } } /** * Removes any AIOSEO Post records for revisions. * * @since 4.1.9 * * @return void */ public function removeRevisionRecords() { $postsTableName = aioseo()->core->db->prefix . 'posts'; $aioseoPostsTableName = aioseo()->core->db->prefix . 'aioseo_posts'; $limit = 5000; aioseo()->core->db->execute( "DELETE FROM `$aioseoPostsTableName` WHERE `post_id` IN ( SELECT `ID` FROM `$postsTableName` WHERE `post_parent` != 0 AND `post_type` = 'revision' AND `post_status` = 'inherit' ) LIMIT {$limit}" ); // If the limit equals the amount of post IDs found, there might be more revisions left, so we need a new scan. if ( aioseo()->core->db->rowsAffected() === $limit ) { $this->scheduleRemoveRevisionsRecords(); } } /** * Enables the new shortcodes parsing setting if it was already enabled before as a deprecated setting. * * @since 4.2.0 * * @return void */ private function migrateDeprecatedRunShortcodesSetting() { if ( in_array( 'runShortcodesInDescription', aioseo()->internalOptions->deprecatedOptions, true ) && ! aioseo()->options->deprecated->searchAppearance->advanced->runShortcodesInDescription ) { return; } aioseo()->options->searchAppearance->advanced->runShortcodes = true; } /** * Add options column. * * @since 4.2.2 * * @return void */ private function addOptionsColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'options' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD `options` longtext DEFAULT NULL AFTER `limit_modified_date`" ); // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; } } /** * Remove the tabs column as it is unnecessary. * * @since 4.2.2 * * @return void */ protected function removeTabsColumn() { if ( aioseo()->core->db->columnExists( 'aioseo_posts', 'tabs' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} DROP tabs" ); } } /** * Migrates the user contact methods to the new format. * * @since 4.2.2 * * @return void */ private function migrateUserContactMethods() { $userMetaTableName = aioseo()->core->db->prefix . 'usermeta'; aioseo()->core->db->execute( "UPDATE `$userMetaTableName` SET `meta_key` = 'aioseo_facebook_page_url' WHERE `meta_key` = 'aioseo_facebook'" ); aioseo()->core->db->execute( "UPDATE `$userMetaTableName` SET `meta_key` = 'aioseo_twitter_url' WHERE `meta_key` = 'aioseo_twitter'" ); } /** * Migrates some older values in the Knowledge Panel contact type setting that were removed. * * @since 4.2.4 * * @return void */ public function migrateContactTypes() { $oldValue = aioseo()->options->searchAppearance->global->schema->contactType; $oldValueLowerCase = strtolower( (string) $oldValue ); // Return if there is no value set or manual input is being used. if ( ! $oldValue || 'manual' === $oldValueLowerCase ) { return; } switch ( $oldValueLowerCase ) { case 'billing support': case 'customer support': case 'reservations': case 'sales': case 'technical support': // If we still support the value, do nothing. return; default: // Otherwise, migrate the existing value to the manual input field. if ( 'bagage tracking' === $oldValueLowerCase ) { // Let's also fix this old typo. $oldValue = 'Baggage Tracking'; } aioseo()->options->searchAppearance->global->schema->contactType = 'manual'; aioseo()->options->searchAppearance->global->schema->contactTypeManual = $oldValue; } } /** * Add an addon column to the notifications table. * * @since 4.2.4 * * @return void */ private function addNotificationsAddonColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_notifications', 'addon' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_notifications'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD `addon` varchar(64) DEFAULT NULL AFTER `slug`" ); // Reset the cache for the installed tables. aioseo()->internalOptions->database->installedTables = ''; } } /** * Adds the schema column. * * @since 4.2.5 * * @return void */ private function addSchemaColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'schema' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD `schema` longtext DEFAULT NULL AFTER `seo_score`" ); } } /** * Schedules the post schema migration. * * @since 4.2.5 * * @return void */ private function schedulePostSchemaMigration() { aioseo()->actionScheduler->scheduleSingle( 'aioseo_v4_migrate_post_schema', 10 ); if ( ! aioseo()->core->cache->get( 'v4_migrate_post_schema_default_date' ) ) { aioseo()->core->cache->update( 'v4_migrate_post_schema_default_date', gmdate( 'Y-m-d H:i:s' ), 3 * MONTH_IN_SECONDS ); } } /** * Migrates then post schema to the new JSON column. * * @since 4.2.5 * * @return void */ public function migratePostSchema() { $posts = aioseo()->core->db->start( 'aioseo_posts' ) ->select( '*' ) ->whereRaw( '`schema` IS NULL' ) ->limit( 40 ) ->run() ->models( 'AIOSEO\\Plugin\\Common\\Models\\Post' ); if ( empty( $posts ) ) { return; } foreach ( $posts as $post ) { $this->migratePostSchemaHelper( $post ); } // Once done, schedule the next action. aioseo()->actionScheduler->scheduleSingle( 'aioseo_v4_migrate_post_schema', 30, [], true ); } /** * Schedules the post schema migration to fix the default graphs. * * @since 4.2.6 * * @return void */ private function schedulePostSchemaDefaultMigration() { aioseo()->actionScheduler->scheduleSingle( 'aioseo_v4_migrate_post_schema_default', 30 ); } /** * Migrates the post schema to the new JSON column again for posts using the default. * This is needed to fix an oversight because in 4.2.5 we didn't migrate any properties set to the default graph. * * @since 4.2.6 * * @return void */ public function migratePostSchemaDefault() { $migrationStartDate = aioseo()->core->cache->get( 'v4_migrate_post_schema_default_date' ); if ( ! $migrationStartDate ) { return; } $posts = aioseo()->core->db->start( 'aioseo_posts' ) ->select( '*' ) ->where( 'schema_type =', 'default' ) ->whereRaw( "updated < '$migrationStartDate'" ) ->limit( 40 ) ->run() ->models( 'AIOSEO\\Plugin\\Common\\Models\\Post' ); if ( empty( $posts ) ) { aioseo()->core->cache->delete( 'v4_migrate_post_schema_default_date' ); return; } foreach ( $posts as $post ) { $this->migratePostSchemaHelper( $post ); } // Once done, schedule the next action. aioseo()->actionScheduler->scheduleSingle( 'aioseo_v4_migrate_post_schema_default', 30, [], true ); } /** * Helper function for the schema migration. * * @since 4.2.5 * * @param Models\Post $aioseoPost The AIOSEO post object. * @return Models\Post The modified AIOSEO post object. */ public function migratePostSchemaHelper( $aioseoPost ) { $post = aioseo()->helpers->getPost( $aioseoPost->post_id ); $schemaType = $aioseoPost->schema_type; $schemaTypeOptions = json_decode( (string) $aioseoPost->schema_type_options ); $schemaOptions = Models\Post::getDefaultSchemaOptions( '', $post ); if ( empty( $schemaTypeOptions ) ) { $aioseoPost->schema = $schemaOptions; $aioseoPost->save(); return $aioseoPost; } // If the post is set to the default schema type, set the default for post type but then also get the properties. $isDefault = 'default' === $schemaType; if ( $isDefault ) { $dynamicOptions = aioseo()->dynamicOptions->noConflict(); if ( ! empty( $post->post_type ) && $dynamicOptions->searchAppearance->postTypes->has( $post->post_type ) ) { $schemaOptions->default->graphName = $dynamicOptions->searchAppearance->postTypes->{$post->post_type}->schemaType; $schemaType = $dynamicOptions->searchAppearance->postTypes->{$post->post_type}->schemaType; } } $graph = []; switch ( $schemaType ) { case 'Article': $graph = [ 'id' => '#aioseo-article-' . uniqid(), 'slug' => 'article', 'graphName' => 'Article', 'label' => __( 'Article', 'all-in-one-seo-pack' ), 'properties' => [ 'type' => ! empty( $schemaTypeOptions->article->articleType ) ? $schemaTypeOptions->article->articleType : 'Article', 'name' => '#post_title', 'headline' => '#post_title', 'description' => '#post_excerpt', 'image' => '', 'keywords' => '', 'author' => [ 'name' => '#author_name', 'url' => '#author_url' ], 'dates' => [ 'include' => true, 'datePublished' => '', 'dateModified' => '' ] ] ]; break; case 'Course': $graph = [ 'id' => '#aioseo-course-' . uniqid(), 'slug' => 'course', 'graphName' => 'Course', 'label' => __( 'Course', 'all-in-one-seo-pack' ), 'properties' => [ 'name' => ! empty( $schemaTypeOptions->course->name ) ? $schemaTypeOptions->course->name : '#post_title', 'description' => ! empty( $schemaTypeOptions->course->description ) ? $schemaTypeOptions->course->description : '#post_excerpt', 'provider' => [ 'name' => ! empty( $schemaTypeOptions->course->provider ) ? $schemaTypeOptions->course->provider : '', 'url' => '', 'image' => '' ] ] ]; break; case 'Product': $graph = [ 'id' => '#aioseo-product-' . uniqid(), 'slug' => 'product', 'graphName' => 'Product', 'label' => __( 'Product', 'all-in-one-seo-pack' ), 'properties' => [ 'autogenerate' => true, 'name' => '#post_title', 'description' => ! empty( $schemaTypeOptions->product->description ) ? $schemaTypeOptions->product->description : '#post_excerpt', 'brand' => ! empty( $schemaTypeOptions->product->brand ) ? $schemaTypeOptions->product->brand : '', 'image' => '', 'identifiers' => [ 'sku' => ! empty( $schemaTypeOptions->product->sku ) ? $schemaTypeOptions->product->sku : '', 'gtin' => '', 'mpn' => '' ], 'offer' => [ 'price' => ! empty( $schemaTypeOptions->product->price ) ? (float) $schemaTypeOptions->product->price : '', 'currency' => ! empty( $schemaTypeOptions->product->currency ) ? $schemaTypeOptions->product->currency : '', 'availability' => ! empty( $schemaTypeOptions->product->availability ) ? $schemaTypeOptions->product->availability : '', 'validUntil' => ! empty( $schemaTypeOptions->product->priceValidUntil ) ? $schemaTypeOptions->product->priceValidUntil : '' ], 'rating' => [ 'minimum' => 1, 'maximum' => 5 ], 'reviews' => [] ] ]; $identifierType = ! empty( $schemaTypeOptions->product->identifierType ) ? $schemaTypeOptions->product->identifierType : ''; $identifier = ! empty( $schemaTypeOptions->product->identifier ) ? $schemaTypeOptions->product->identifier : ''; if ( preg_match( '/gtin/i', $identifierType ) ) { $graph['properties']['identifiers']['gtin'] = $identifier; } if ( preg_match( '/mpn/i', $identifierType ) ) { $graph['properties']['identifiers']['mpn'] = $identifier; } $reviews = ! empty( $schemaTypeOptions->product->reviews ) ? $schemaTypeOptions->product->reviews : []; if ( ! empty( $reviews ) ) { foreach ( $reviews as $reviewData ) { $reviewData = json_decode( $reviewData ); if ( empty( $reviewData ) ) { continue; } $graph['properties']['reviews'][] = [ 'rating' => $reviewData->rating, 'headline' => $reviewData->headline, 'content' => $reviewData->content, 'author' => $reviewData->author ]; } } break; case 'Recipe': $graph = [ 'id' => '#aioseo-recipe-' . uniqid(), 'slug' => 'recipe', 'graphName' => 'Recipe', 'label' => __( 'Recipe', 'all-in-one-seo-pack' ), 'properties' => [ 'name' => ! empty( $schemaTypeOptions->recipe->name ) ? $schemaTypeOptions->recipe->name : '#post_title', 'description' => ! empty( $schemaTypeOptions->recipe->description ) ? $schemaTypeOptions->recipe->description : '#post_excerpt', 'author' => ! empty( $schemaTypeOptions->recipe->author ) ? $schemaTypeOptions->recipe->author : '#author_name', 'ingredients' => ! empty( $schemaTypeOptions->recipe->ingredients ) ? $schemaTypeOptions->recipe->ingredients : '', 'dishType' => ! empty( $schemaTypeOptions->recipe->dishType ) ? $schemaTypeOptions->recipe->dishType : '', 'cuisineType' => ! empty( $schemaTypeOptions->recipe->cuisineType ) ? $schemaTypeOptions->recipe->cuisineType : '', 'keywords' => ! empty( $schemaTypeOptions->recipe->keywords ) ? $schemaTypeOptions->recipe->keywords : '', 'image' => ! empty( $schemaTypeOptions->recipe->image ) ? $schemaTypeOptions->recipe->image : '', 'nutrition' => [ 'servings' => ! empty( $schemaTypeOptions->recipe->servings ) ? $schemaTypeOptions->recipe->servings : '', 'calories' => ! empty( $schemaTypeOptions->recipe->calories ) ? $schemaTypeOptions->recipe->calories : '' ], 'timeRequired' => [ 'preparation' => ! empty( $schemaTypeOptions->recipe->preparationTime ) ? $schemaTypeOptions->recipe->preparationTime : '', 'cooking' => ! empty( $schemaTypeOptions->recipe->cookingTime ) ? $schemaTypeOptions->recipe->cookingTime : '' ], 'instructions' => [] ] ]; $instructions = ! empty( $schemaTypeOptions->recipe->instructions ) ? $schemaTypeOptions->recipe->instructions : []; if ( ! empty( $instructions ) ) { foreach ( $instructions as $instructionData ) { $instructionData = json_decode( $instructionData ); if ( empty( $instructionData ) ) { continue; } $graph['properties']['instructions'][] = [ 'name' => '', 'text' => $instructionData->content, 'image' => '' ]; } } break; case 'SoftwareApplication': $graph = [ 'id' => '#aioseo-software-application-' . uniqid(), 'slug' => 'software-application', 'graphName' => 'SoftwareApplication', 'label' => __( 'Software', 'all-in-one-seo-pack' ), 'properties' => [ 'name' => ! empty( $schemaTypeOptions->software->name ) ? $schemaTypeOptions->software->name : '#post_title', 'description' => '#post_excerpt', 'price' => ! empty( $schemaTypeOptions->software->price ) ? (float) $schemaTypeOptions->software->price : '', 'currency' => ! empty( $schemaTypeOptions->software->currency ) ? $schemaTypeOptions->software->currency : '', 'operatingSystem' => ! empty( $schemaTypeOptions->software->operatingSystems ) ? $schemaTypeOptions->software->operatingSystems : '', 'category' => ! empty( $schemaTypeOptions->software->category ) ? $schemaTypeOptions->software->category : '', 'rating' => [ 'value' => '', 'minimum' => 1, 'maximum' => 5 ], 'review' => [ 'headline' => '', 'content' => '', 'author' => '' ] ] ]; $reviews = ! empty( $schemaTypeOptions->software->reviews ) ? $schemaTypeOptions->software->reviews : []; if ( ! empty( $reviews[0] ) ) { $reviewData = json_decode( $reviews[0] ); if ( empty( $reviewData ) ) { break; } $graph['properties']['rating']['value'] = $reviewData->rating; $graph['properties']['review'] = [ 'headline' => $reviewData->headline, 'content' => $reviewData->content, 'author' => $reviewData->author ]; } break; case 'WebPage': if ( 'FAQPage' === $schemaTypeOptions->webPage->webPageType ) { $graph = [ 'id' => '#aioseo-faq-page-' . uniqid(), 'slug' => 'faq-page', 'graphName' => 'FAQPage', 'label' => __( 'FAQ Page', 'all-in-one-seo-pack' ), 'properties' => [ 'type' => $schemaTypeOptions->webPage->webPageType, 'name' => '#post_title', 'description' => '#post_excerpt', 'questions' => [] ] ]; $faqs = $schemaTypeOptions->faq->pages; if ( ! empty( $faqs ) ) { foreach ( $faqs as $faqData ) { $faqData = json_decode( $faqData ); if ( empty( $faqData ) ) { continue; } $graph['properties']['questions'][] = [ 'question' => $faqData->question, 'answer' => $faqData->answer ]; } } } else { $graph = [ 'id' => '#aioseo-web-page-' . uniqid(), 'slug' => 'web-page', 'graphName' => 'WebPage', 'label' => __( 'Web Page', 'all-in-one-seo-pack' ), 'properties' => [ 'type' => $schemaTypeOptions->webPage->webPageType, 'name' => '', 'description' => '' ] ]; } break; case 'default': $dynamicOptions = aioseo()->dynamicOptions->noConflict(); if ( ! empty( $post->post_type ) && $dynamicOptions->searchAppearance->postTypes->has( $post->post_type ) ) { $schemaOptions->defaultGraph = $dynamicOptions->searchAppearance->postTypes->{$post->post_type}->schemaType; } break; case 'none': // If "none', we simply don't have to migrate anything. default: break; } if ( ! empty( $graph ) ) { if ( $isDefault ) { $schemaOptions->default->data->{$schemaType} = $graph; } else { $schemaOptions->graphs[] = $graph; $schemaOptions->default->isEnabled = false; } } $aioseoPost->schema = $schemaOptions; $aioseoPost->save(); return $aioseoPost; } /** * Updates the dashboardWidgets with the new array format. * * @since 4.2.8 * * @return void */ private function migrateDashboardWidgetsOptions() { $rawOptions = $this->getRawOptions(); if ( empty( $rawOptions ) || ! is_bool( $rawOptions['advanced']['dashboardWidgets'] ) ) { return; } $widgets = [ 'seoNews' ]; // If the dashboardWidgets was activated, let's turn on the other widgets. if ( ! empty( $rawOptions['advanced']['dashboardWidgets'] ) ) { $widgets[] = 'seoOverview'; $widgets[] = 'seoSetup'; } aioseo()->options->advanced->dashboardWidgets = $widgets; } /** * Adds the primary_term column to the aioseo_posts table. * * @since 4.3.6 * * @return void */ private function addPrimaryTermColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'primary_term' ) ) { $tableName = aioseo()->core->db->db->prefix . 'aioseo_posts'; aioseo()->core->db->execute( "ALTER TABLE {$tableName} ADD `primary_term` longtext DEFAULT NULL AFTER `page_analysis`" ); } } /** * Schedules the revision records removal. * * @since 4.3.1 * * @return void */ private function scheduleRemoveRevisionsRecords() { aioseo()->actionScheduler->scheduleSingle( 'aioseo_v419_remove_revision_records', 10, [], true ); } /** * Casts the priority column to a float. * * @since 4.3.9 * * @return void */ private function migratePriorityColumn() { if ( ! aioseo()->core->db->columnExists( 'aioseo_posts', 'priority' ) ) { return; } $prefix = aioseo()->core->db->prefix; $aioseoPostsTableName = $prefix . 'aioseo_posts'; // First, cast the default value to NULL since it's a string. aioseo()->core->db->execute( "UPDATE {$aioseoPostsTableName} SET priority = NULL WHERE priority = 'default'" ); // Then, alter the column to a float. aioseo()->core->db->execute( "ALTER TABLE {$aioseoPostsTableName} MODIFY priority float" ); } /** * Update the custom robots.txt rules to the new format, * by replacing `rule` and `directoryPath` with `directive` and `fieldValue`, respectively. * * @since 4.4.2 * * @return void */ private function updateRobotsTxtRules() { $rawOptions = $this->getRawOptions(); $currentRules = $rawOptions && ! empty( $rawOptions['tools']['robots']['rules'] ) ? $rawOptions['tools']['robots']['rules'] : []; if ( empty( $currentRules ) || ! is_array( $currentRules ) ) { return; } $newRules = []; foreach ( $currentRules as $oldRule ) { $parsedRule = json_decode( $oldRule, true ); if ( empty( $parsedRule['rule'] ) && empty( $parsedRule['directoryPath'] ) ) { continue; } $newRule = [ 'userAgent' => array_key_exists( 'userAgent', $parsedRule ) ? $parsedRule['userAgent'] : '', 'directive' => array_key_exists( 'rule', $parsedRule ) ? $parsedRule['rule'] : '', 'fieldValue' => array_key_exists( 'directoryPath', $parsedRule ) ? $parsedRule['directoryPath'] : '', ]; $newRules[] = wp_json_encode( $newRule ); } if ( $newRules ) { aioseo()->options->tools->robots->rules = $newRules; } } /** * Checks if the user is currently using the old GA Analytics v3 integration and create a notification. * * @since 4.5.1 * * @return void */ private function checkForGaAnalyticsV3() { // If either MonsterInsights or ExactMetrics is active, let's return early. $pluginData = aioseo()->helpers->getPluginData(); if ( $pluginData['miPro']['activated'] || $pluginData['miLite']['activated'] || $pluginData['emPro']['activated'] || $pluginData['emLite']['activated'] ) { return; } $rawOptions = $this->getRawOptions(); if ( empty( $rawOptions['deprecated']['webmasterTools']['googleAnalytics']['id'] ) ) { return; } // Let's clear the notification if the search is working again. $notification = Models\Notification::getNotificationByName( 'google-analytics-v3-deprecation' ); if ( $notification->exists() ) { $notification->dismissed = false; $notification->save(); return; } // Determine which plugin name to use. $pluginName = 'MonsterInsights'; if ( ( $pluginData['emPro']['installed'] || $pluginData['emLite']['installed'] ) && ! $pluginData['miPro']['installed'] && ! $pluginData['miLite']['installed'] ) { $pluginName = 'ExactMetrics'; } Models\Notification::addNotification( [ 'slug' => uniqid(), 'notification_name' => 'google-analytics-v3-deprecation', 'title' => __( 'Universal Analytics V3 Deprecation Notice', 'all-in-one-seo-pack' ), 'content' => sprintf( // Translators: 1 - Line break HTML tags, 2 - Plugin short name ("AIOSEO"), Analytics plugin name (e.g. "MonsterInsights"). __( 'You have been using the %2$s Google Analytics V3 (Universal Analytics) integration which has been deprecated by Google and is no longer supported. This may affect your website\'s data accuracy and performance.%1$sTo ensure a seamless analytics experience, we recommend migrating to %3$s, a powerful analytics solution.%1$s%3$s offers advanced features such as real-time tracking, enhanced e-commerce analytics, and easy-to-understand reports, helping you make informed decisions to grow your online presence effectively.%1$sClick the button below to be redirected to the %3$s setup process, where you can start benefiting from its robust analytics capabilities immediately.', 'all-in-one-seo-pack' ), // phpcs:ignore Generic.Files.LineLength.MaxExceeded '

', AIOSEO_PLUGIN_SHORT_NAME, $pluginName ), 'type' => 'error', 'level' => [ 'all' ], 'button1_label' => __( 'Fix Now', 'all-in-one-seo-pack' ), 'button1_action' => admin_url( 'admin.php?page=aioseo-monsterinsights' ), 'start' => gmdate( 'Y-m-d H:i:s' ) ] ); } }