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' )
] );
}
}