member->synchronize_users( 100 ); WPF()->member->init_current_user(); WPF()->member->clear_db_cache(); ################################################################# // Importing Language Packs and Phrases ///////////////////////// WPF()->phrase->xml_import( 'english.xml' ); WPF()->phrase->clear_cache(); ################################################################# // Creating Forum Page ////////////////////////////////////////// wpforo_create_forum_page(); ################################################################# // Forum Navigation and Menu //////////////////////////////////// wpforo_import_default_menus(); ################################################################# // Boards //////////////////////////////////////////////////// wpforo_import_default_board(); ################################################################# // Usergroup //////////////////////////////////////////////////// wpforo_import_default_usergroups(); ################################################################# // Forums //////////////////////////////////////////////////// wpforo_import_default_forums(); ################################################################# // UPDATE THEME OPTIONS //////////////////////////////////////// wpforo_update_theme_options(); ################################################################# // wpForo Upgrade /////////////////////////////// wpforo_upgrade(); ################################################################# // UPDATE VERSION - END ///////////////////////////////////////// wpforo_update_option( 'version', WPFORO_VERSION ); WPF()->notice->clear(); ################################################################# // DELETE ALL CAHCES ///////////////////////////////////////// wpforo_clean_cache(); // At the moment only Redis Object Cache should be flushed because it causes 404 error for all forum pages. if( function_exists( 'redis_object_cache' ) ) { wp_cache_flush(); } ################################################################# // Reconcile AI crons against current connection state. // Connected tenants get the recurring AI maintenance crons; everyone // else (free installs that never enabled AI, disconnected accounts) gets // stale AI events cleared out of wp_options.cron. WPF()->ai_client->sync_cron_state(); // Schedule Email Queue Cleanup Cron (non-AI, always on) WPF()->email_queue->schedule_cleanup_cron(); WPF()->email_queue->reset_stuck_processing(); // Note: AI Pending Topics Indexing Cron is scheduled when user // enables auto-indexing in the admin settings (disabled by default) } function wpforo_upgrade() { if( version_compare( wpforo_get_option( 'version', '', false ), '2.0.3', '<' ) ) { // #### migrate old options to new settings require_once WPFORO_DIR . "/includes/options-migration.php"; _wpforo_migrate_old_options_to_new(); _wpforo_migrate_old_widgets_to_new(); } // Migrate menu for Recent Activity page (upgrade from 2.x to 3.x) $old_version = wpforo_get_option( 'version', '', false ); if( $old_version && version_compare( $old_version, '3.0.0', '<' ) ) { wpforo_migrate_recent_activity_menu(); wpforo_migrate_theme_styles(); } // Migrate AI quality defaults to faster tiers (3.0.3+) // Runs for all versions - has its own one-time flag check wpforo_migrate_ai_quality_defaults(); } function wpforo_create_tables() { require_once( WPFORO_DIR . '/includes/install-sql.php' ); foreach( wpforo_get_install_sqls() as $sql ) { if( false === @WPF()->db->query( $sql ) ) { @WPF()->db->query( preg_replace( '#\)\s*ENGINE.*$#isu', ')', $sql ) ); } } } function wpforo_alter_tables() { ################################################################# // ADD `status` field in `languages` TABLE ////////////////////////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->languages, 'col' => 'status', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->languages . "` ADD COLUMN `status` TINYINT(1) NOT NULL DEFAULT 1" ); } ################################################################# // ADD `package` field in `phrases` TABLE ////////////////////////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->phrases, 'col' => 'package', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->phrases . "` ADD `package` VARCHAR(255) NOT NULL DEFAULT 'wpforo'" ); } // WPF()->phrase->clear_cache(); ################################################################# // ADD `private` field in TOPIC TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'private', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `private` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `is_private` (`private`);" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD INDEX `own_private` ( `userid`, `private`);" ); } // ADD `solved` field in TOPIC TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'solved', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `solved` TINYINT(1) NOT NULL DEFAULT 0 AFTER `type`, ADD INDEX `solved` (`solved`)" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->topics . "` t INNER JOIN `" . WPF()->tables->posts . "` p ON p.`topicid` = t.`topicid` AND p.`is_answer` = 1 SET t.`solved` = 1 WHERE t.`solved` = 0" ); wpforo_clean_cache(); } // ADD `status` field in TOPICS & POSTS TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'status', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `status` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `status` (`status`);" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `status` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `status` (`status`);" ); } // ADD `name` and `email` field in TOPIC TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'name', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `name` VARCHAR(50) NOT NULL, ADD `email` VARCHAR(50) NOT NULL" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `name` VARCHAR(50) NOT NULL, ADD `email` VARCHAR(50) NOT NULL" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `email` (`email`)" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `email` (`email`)" ); } // ADD `utitle`, `role` and `access` to USERGROUP TABLE ///////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'utitle', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `utitle` VARCHAR(100), ADD `role` VARCHAR(50), ADD `access` VARCHAR(50)" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Admin', `role` = 'administrator', `access` = 'full' WHERE `groupid` = 1" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Moderator', `role` = 'editor', `access` = 'moderator' WHERE `groupid` = 2" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Registered', `role` = 'subscriber', `access` = 'standard' WHERE `groupid` = 3" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Guest', `role` = '', `access` = 'read_only' WHERE `groupid` = 4" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Customer', `role` = 'customer', `access` = 'standard' WHERE `groupid` = 5" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'name', `role` = 'subscriber', `access` = 'standard' WHERE `utitle` IS NULL OR `utitle` = ''" ); } ################################################################# // ADD `color` field in usergroups TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'color', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `color` varchar(7) NOT NULL DEFAULT ''" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#FF3333' WHERE `groupid` = 1" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#0066FF' WHERE `groupid` = 2" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#222222' WHERE `groupid` = 4" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#993366' WHERE `groupid` = 5" ); } ################################################################# // ADD `visible` field in usergroups TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'visible', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `visible` TINYINT(1) NOT NULL DEFAULT 1;" ); } ################################################################# // ADD `status` field in `languages` TABLE ////////////////////////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'is_default', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD COLUMN `is_default` TINYINT(1) NOT NULL DEFAULT 0" ); } ################################################################# // ADD `online_time` field in profiles TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'online_time', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `online_time` INT UNSIGNED NOT NULL DEFAULT 0, ADD KEY (`online_time`)" ); } // ADD `is_email_confirmed` field in profiles TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'is_email_confirmed', 'check' => 'col_exists' ] ) ) { WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `is_email_confirmed` TINYINT(1) NOT NULL DEFAULT 0, ADD KEY (`is_email_confirmed`)" ); WPF()->db->query( "UPDATE `" . WPF()->tables->profiles . "` JOIN `" . WPF()->tables->subscribes . "` ON `" . WPF()->tables->subscribes . "`.`userid` = `" . WPF()->tables->profiles . "`.`userid` SET `" . WPF()->tables->profiles . "`.`is_email_confirmed` = 1 WHERE `" . WPF()->tables->subscribes . "`.`active` = 1" ); WPF()->db->query( "UPDATE `" . WPF()->tables->profiles . "` SET `is_email_confirmed` = 1 WHERE `groupid` = 1" ); } ################################################################# // DROP uname unique key from profiles TABLE /////////////////////////// if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'UNIQUE USERNAME', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` DROP KEY `UNIQUE USERNAME`" ); } if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'UNIQUE ID', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` DROP KEY `UNIQUE ID`" ); } ################################################################# // ADD `private` field in post TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'private', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `private` TINYINT(1) NOT NULL DEFAULT 0, ADD INDEX `is_private` (`private`)" ); } ################################################################# //Add user_name col in subsciption table/////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'user_name', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` ADD `user_name` VARCHAR(60) NOT NULL DEFAULT ''" ); } //Add user_email col in subsciption table if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'user_email', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` ADD `user_email` VARCHAR(60) NOT NULL DEFAULT ''" ); } //Add indexes for subscribe new fields if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'fld_group_unq', 'check' => 'key_exists' ] ) ) { $args = [ 'table' => WPF()->tables->subscribes, 'col' => 'itemid', 'check' => 'key_exists' ]; if( wpforo_db_check( $args ) ) @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` DROP KEY `itemid`" ); wpforo_add_unique_key( WPF()->tables->subscribes, 'subid', 'fld_group_unq', '`itemid`, `type`, `userid`, `user_email`(60)' ); } if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'type', 'check' => 'col_type' ] ) ) !== 'varchar(50)' ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` MODIFY `type` VARCHAR(50) NOT NULL" ); } #################################################################### //Add index for double condition queries to avoid SQl caching/////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'forumid_status', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `forumid_status` (`forumid`,`status`)" ); } if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_status', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_status` (`topicid`,`status`)" ); } if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_solved', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_solved` (`topicid`,`is_answer`)" ); } if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'forumid_status', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `forumid_status` (`forumid`,`status`)" ); } $topic_slug_key = @WPF()->db->get_row( "SHOW KEYS FROM `" . WPF()->tables->topics . "` WHERE `Key_name` LIKE 'slug'", ARRAY_A ); if( $topic_slug_key && intval( wpfval( $topic_slug_key, 'Non_unique' ) ) === 0 ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` DROP INDEX slug, ADD KEY slug (`slug`(191))" ); } if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_parentid', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_parentid` (`topicid`,`parentid`)" ); } ################################################################# // ADD `secondary_groupids` field in profiles TABLE ////////////// /*if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'secondary_groups', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `secondary_groups` VARCHAR(255)" ); }*/ if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'secondary', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `secondary` TINYINT(1) NOT NULL DEFAULT 0;" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `secondary` = 1 WHERE `groupid` IN(3,5)" ); } ################################################################# // ADD `fields` field in profiles TABLE //////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'fields', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `fields` LONGTEXT" ); } ################################################################# // Change `phrase_key` type in phrases TABLE /////////////////// if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->phrases, 'col' => 'phrase_key', 'check' => 'col_type' ] ) ) !== 'text' ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->phrases . "` MODIFY `phrase_key` TEXT" ); } ################################################################# // ADD `prefix` and `tags` fields in TOPIC TABLE /////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'tags', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `prefix` VARCHAR(100) NOT NULL DEFAULT '', ADD `tags` TEXT, ADD KEY (`prefix`), ADD KEY (`tags`(190))" ); } ################################################################# //Add last_post indexes for forums and topics tables //////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'last_postid', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD KEY(`last_postid`)" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `forumid_status_private` ( `forumid`,`status`,`private` ), ADD KEY(`last_post`)" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `forumid_status_private` (`forumid`, `status`, `private`)" ); } ################################################################# //Add cover and cover_height fields to forums table //////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'cover', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD COLUMN `cover` BIGINT UNSIGNED NOT NULL DEFAULT 0, ADD COLUMN `cover_height` INT(4) UNSIGNED NOT NULL DEFAULT 150" ); } ################################################################# //rename column from cat_layout to layout in forums table //////////// if( wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'cat_layout', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` CHANGE COLUMN `cat_layout` `layout` tinyint(1) UNSIGNED NOT NULL DEFAULT 0" ); } ################################################################# //Add unique keys in VISITS TABLE /////////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->visits, 'col' => 'unique_tracking', 'check' => 'key_exists' ] ) ) { wpforo_add_unique_key( WPF()->tables->visits, 'id', 'unique_tracking', '`userid`,`ip`,`forumid`,`topicid`' ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->visits . "` ADD KEY `time_forumid` (`time`, `forumid`)" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->visits . "` ADD KEY `time_topicid` (`time`, `topicid`)" ); } ################################################################# // ADD `color` field in forums TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'color', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD `color` varchar(7) NOT NULL DEFAULT ''" ); @WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `color` = concat('#',SUBSTRING((lpad(hex(round(rand() * 10000000)),6,0)),-6))" ); } ################################################################# // ADD `root` fields in POSTS TABLE /////////////// //$post_table_count = @WPF()->db->get_var("SELECT COUNT(*) FROM `".WPF()->tables->posts."`"); //if( $post_table_count < 100000 ){ if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'root', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `root` BIGINT, ADD KEY(`root`)" ); } //} ################################################################# // ADD `new` in ACTIVITY TABLE ///////////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->activity, 'col' => 'new', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->activity . "` ADD `new` TINYINT NOT NULL DEFAULT '0' AFTER `permalink`, ADD KEY `itemtype_userid_new` (`itemtype`, `userid`, `new`)" ); } ################################################################# // Change `last_userid` type in forums TABLE /////////////////// if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'last_userid', 'check' => 'col_type' ] ) ) !== 'varchar(255)' ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` CHANGE `last_userid` `last_userid` VARCHAR(255) NOT NULL" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD KEY `last_userid` (`last_userid`(191));" ); } ################################################################ // likes and votes Table to new reactions table //////////////// if( wpforo_db_check( [ 'table' => wpforo_fix_table_name( 'votes' ), 'check' => 'table_exists' ] ) ) { $sql = "INSERT IGNORE INTO `" . WPF()->tables->reactions . "` ( SELECT NULL, `userid`, `postid`, `post_userid`, `reaction`, IF( `reaction` = 1, 'up', 'down' ), NULL, NULL FROM `" . wpforo_fix_table_name( 'votes' ) . "` )"; WPF()->db->query( $sql ); $sql = "DROP TABLE `" . wpforo_fix_table_name( 'votes' ) . "`"; WPF()->db->query( $sql ); } if( wpforo_db_check( [ 'table' => wpforo_fix_table_name( 'likes' ), 'check' => 'table_exists' ] ) ) { $sql = "INSERT IGNORE INTO `" . WPF()->tables->reactions . "` ( SELECT NULL, `userid`, `postid`, `post_userid`, 1, 'up', NULL, NULL FROM `" . wpforo_fix_table_name( 'likes' ) . "` )"; WPF()->db->query( $sql ); $sql = "DROP TABLE `" . wpforo_fix_table_name( 'likes' ) . "`"; WPF()->db->query( $sql ); } if( ! wpforo_db_check( [ 'table' => WPF()->tables->reactions, 'col' => 'performance', 'check' => 'key_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->reactions . "` ADD KEY `performance` (`postid`,`post_userid`,`type`)" ); } ################################################################# // ADD `type` field in forums TABLE for ticket/private forums // Default is 'forum', 'ticket_forum' forces all topics to be private if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'type', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD `type` VARCHAR(20) NOT NULL DEFAULT 'forum'" ); } // ADD indexed, local, cloud, task_tag fields and indexes in TOPICS TABLE /////////////////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'indexed', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `indexed` VARCHAR(32), ADD `local` TINYINT(1) NOT NULL DEFAULT 0, ADD `cloud` TINYINT(1) NOT NULL DEFAULT 0, ADD `task_tag` TINYINT(1) NOT NULL DEFAULT 0, ADD INDEX `indexed` (`indexed`), ADD INDEX `local` (`local`), ADD INDEX `cloud` (`cloud`), ADD INDEX `task_tag` (`task_tag`)" ); } ################################################################ // profiles table fields fixing //////////////////////////////// if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'facebook', 'check' => 'col_exists' ] ) ) { $sql = "SELECT `userid`, `facebook`, `twitter`, `skype`, `fields` FROM `" . WPF()->tables->profiles . "` WHERE (`facebook` IS NOT NULL AND `facebook` <> '') OR (`twitter` IS NOT NULL AND `twitter` <> '') OR (`skype` IS NOT NULL AND `skype` <> '')"; if( $users = (array) WPF()->db->get_results( $sql, ARRAY_A ) ) { foreach( $users as $user ) { $user['userid'] = wpforo_bigintval( $user['userid'] ); $facebook = trim( (string) $user['facebook'] ); $twitter = trim( (string) $user['twitter'] ); $skype = trim( (string) $user['skype'] ); $user['fields'] = array_merge( (array) json_decode( $user['fields'], true ), compact( 'facebook', 'twitter', 'skype' ) ); $user['fields'] = wp_json_encode( $user['fields'] ); WPF()->db->update( WPF()->tables->profiles, [ 'fields' => $user['fields'] ], [ 'userid' => $user['userid'] ], '%s', '%d' ); } } @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` DROP COLUMN `username`, DROP COLUMN `last_login`, DROP COLUMN `icq`, DROP COLUMN `aim`, DROP COLUMN `yahoo`, DROP COLUMN `msn`, DROP COLUMN `gtalk`, DROP COLUMN `like`, DROP COLUMN `site`, DROP COLUMN `facebook`, DROP COLUMN `twitter`, DROP COLUMN `skype`, ADD COLUMN `topics` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `posts`, ADD COLUMN `reactions_in` TEXT AFTER `comments`, ADD COLUMN `reactions_out` TEXT AFTER `reactions_in`, ADD COLUMN `points` INT NOT NULL DEFAULT 0 AFTER `reactions_out`, CHANGE COLUMN `secondary_groups` `secondary_groupids` VARCHAR(255) AFTER `groupid`, CHANGE COLUMN `rank` `custom_points` INT NOT NULL DEFAULT 0 AFTER `points`, MODIFY COLUMN `avatar` VARCHAR(255) AFTER `secondary_groupids`, MODIFY COLUMN `online_time` INT UNSIGNED AFTER `custom_points`, MODIFY COLUMN `timezone` VARCHAR(255) NOT NULL DEFAULT '' AFTER `online_time`, MODIFY COLUMN `location` VARCHAR(255) AFTER `timezone`, ADD COLUMN `cover` VARCHAR(255) NOT NULL DEFAULT '' AFTER `avatar`, ADD COLUMN `is_mention_muted` TINYINT(1) NOT NULL DEFAULT 0 AFTER `is_email_confirmed`" ); } ################################################################# // Add content_type column to ai_embeddings table ////////////// if( ! wpforo_db_check( [ 'table' => WPF()->tables->ai_embeddings, 'col' => 'content_type', 'check' => 'col_exists' ] ) ) { @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->ai_embeddings . "` ADD COLUMN `content_type` VARCHAR(20) NOT NULL DEFAULT 'forum' COMMENT 'forum or WordPress post_type'" ); @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->ai_embeddings . "` ADD KEY `idx_content_type` (`content_type`)" ); } WPF()->db->query( "TRUNCATE TABLE `" . WPF()->tables->visits . "`" ); } function wpforo_board_repair( $boardid ) { if( ! wpforo_is_admin() ) return; if( ! current_user_can( 'activate_plugins' ) ) return; WPF()->change_board( $boardid ); wpforo_activation(); foreach( wpforo_get_addons_info() as $key => $addon ) { if( ( $addon['base'] || wpforo_is_module_enabled( $key ) ) && is_callable( wpfval( $addon, 'install_func' ) ) ) { call_user_func( $addon['install_func'] ); } } } function wpforo_board_uninstall( $boardid ) { if( ! wpforo_is_admin() ) return; if( ! current_user_can( 'activate_plugins' ) ) return; WPF()->change_board( $boardid ); WPF()->board->delete( $boardid ); foreach( WPF()->_tables as $table ) WPF()->db->query( "DROP TABLE IF EXISTS `" . wpforo_fix_table_name( $table ) . "`" ); WPF()->db->query( "DELETE FROM `" . WPF()->db->options . "` WHERE `option_name` LIKE 'widget_wpforo_widget_%' OR `option_name` REGEXP '^" . wpforo_prefix() . "'" ); WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` REGEXP '^" . wpforo_prefix() . "'" ); wpforo_remove_directory( WPF()->folders['upload']['dir'] ); } function wpforo_uninstall() { if( ! wpforo_is_admin() ) return; if( ! current_user_can( 'activate_plugins' ) ) return; require_once ABSPATH . 'wp-admin/includes/plugin.php'; foreach( WPF()->board->get_boards( [ 'orderby' => '`boardid` DESC' ] ) as $board ) wpforo_board_uninstall( $board['boardid'] ); WPF()->db->query( "DELETE FROM `" . WPF()->db->options . "` WHERE `option_name` LIKE 'widget_wpforo%' OR `option_name` REGEXP '^" . WPF()->base_prefix . "'" ); WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` REGEXP '^" . WPF()->base_prefix . "'" ); foreach( WPF()->_base_tables as $table ) WPF()->db->query( "DROP TABLE IF EXISTS `" . wpforo_fix_table_name( $table ) . "`" ); deactivate_plugins( WPFORO_BASENAME ); } function wpforo_profile_notice() { if( is_multisite() ) { $users = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` LIKE '" . WPF()->blog_prefix . "capabilities'" ); } else { $users = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->db->users . "`" ); } $profiles = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->profiles . "`" ); $delta = $users - $profiles; $status = ( $delta > 2 ) ? round( ( ( $profiles * 100 ) / $users ), 1 ) . '% (' . $profiles . ' / ' . $users . ') ' : '100%'; if( $status === '100%' ) return null; $btext = ( $profiles == 0 ) ? __( 'Start Profile Synchronization', 'wpforo' ) : __( 'Continue Synchronization', 'wpforo' ); $url = wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=synch_user_profiles' ), 'wpforo_synch_user_profiles' ); $class = 'wpforo-mnote notice notice-warning is-dismissible'; $note = __( 'This process may take a few seconds or dozens of minutes, please be patient and don\'t close this page.', 'wpforo' ); $info = __( 'You can permanently disable this message using this documentation', 'wpforo' ); $button = '' . $btext . ' >>'; $header = __( 'wpForo Forum Installation | ', 'wpforo' ); $message = __( 'Forum users\' profile data are not synchronized yet, this step is required! Please click the button below to complete installation.', 'wpforo' ); echo '

' . esc_html( $header ) . $status . '

' . $message . '

' . $button . '


' . $note . '

'; } function wpforo_database_notice() { $url = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=tables' ); $class = 'wpforo-dbnote notice notice-error is-dismissible'; $button = 'Go to Database Troubleshooter >>'; $header = __( 'wpForo Database Update Problem - Action Required!', 'wpforo' ); $message = __( 'Forum database is not updated properly. Please click the button below for further instruction.', 'wpforo' ); echo '

' . esc_html( $header ) . '

' . $message . '

' . $button . '

'; } function wpforo_cache_information() { $plugin_names = []; $plugin_steps = []; $not_excluded_plugins = WPF()->cache->cache_plugins_status(); if( ! empty( $not_excluded_plugins ) ) { foreach( $not_excluded_plugins as $key => $plugin ) { $plugin_names[ $key ] = '«' . $plugin['name'] . '»'; $plugin_steps[ $key ] = '

' . __( 'Exclude forum page(s) from' ) . ' ' . $plugin['name'] . ' ' . __( 'plugin', 'wpforo' ) . '

  1. ' . implode( '
  2. ', $plugin['steps'] ) . '
'; } } $class = 'wpforo-cache-conflict-note notice notice-error is-dismissible'; $note = sprintf( __( 'If you have already excluded the forum page from your cache plugin please ignore and close this message using the top %s (x) button.', 'wpforo' ), ( is_rtl() ? __( 'left', 'wpforo' ) : __( 'right', 'wpforo' ) ) ); $info = __( 'Please find more information here: ', 'wpforo' ) . 'wpForo and Cache Plugins'; $header = 'wpForo ' . __( ' and ', 'wpforo' ) . implode( __( ' and ', 'wpforo' ), $plugin_names ) . ' ' . __( 'conflict', 'wpforo' ) . ' - ' . __( 'Action Required!', 'wpforo' ); $message = __( 'Please exclude the forum page from your cache plugin! wpForo has a built-in cache system. It does dynamic cache of all forum pages, which will be affected by your cache plugin and the forum data will not be updated on the front-end. The user login and logout actions will also be corrupted.', 'wpforo' ) . '
' . implode( '
', $plugin_steps ); echo '

' . esc_html( $header ) . '

' . $message . '


' . $note . '

' . $info . '

'; echo ""; } function wpforo_get_shortcode_pageid( $exclude = [] ) { $exclude = array_filter( array_map( 'wpforo_bigintval', (array) $exclude ) ); $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "` WHERE `post_content` LIKE '%[wpforo]%' AND `post_status` LIKE 'publish' AND `post_type` IN('" . implode( "','", wpforo_get_blog_content_types() ) . "')"; if( $exclude ) $sql .= " AND `ID` NOT IN(" . implode( ',', $exclude ) . ")"; return WPF()->db->get_var( $sql ); } function wpforo_create_forum_page() { $pageid = WPF()->board->get_current( 'pageid' ); if( ! $pageid || ! WPF()->db->get_var( "SELECT `ID` FROM `" . WPF( )->db->posts . "` WHERE `ID` = '" . $pageid . "' AND ( `post_content` LIKE '%[wpforo]%' OR `post_content` LIKE '%[wpforo-index]%' ) AND `post_status` LIKE 'publish' AND `post_type` IN('" . implode( "','", wpforo_get_blog_content_types() ) . "')" ) ) { if( ! $page_id = wpforo_get_shortcode_pageid( get_option( 'page_on_front' ) ) ) { $wpforo_page = [ 'post_date' => current_time( 'mysql', 1 ), 'post_date_gmt' => current_time( 'mysql', 1 ), 'post_content' => '[wpforo]', 'post_title' => 'Forum', 'post_status' => 'publish', 'comment_status' => 'close', 'ping_status' => 'close', 'post_name' => 'community', 'post_modified' => current_time( 'mysql', 1 ), 'post_modified_gmt' => current_time( 'mysql', 1 ), 'post_parent' => 0, 'menu_order' => 0, 'post_type' => 'page', ]; $page_id = wp_insert_post( $wpforo_page ); } if( $page_id && ! is_wp_error( $page_id ) ) { wpforo_update_option( 'wpforo_pageid', $page_id ); } } flush_rewrite_rules( false ); nocache_headers(); } function wpforo_repair_main_shortcode_page() { $pageid = WPF()->board->get_current( 'pageid' ); $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "` WHERE `ID` = " . $pageid . " AND `post_content` LIKE '%[wpforo%' AND `post_status` LIKE 'publish' AND `post_type` IN('" . implode( "','", wpforo_get_blog_content_types() ) . "')"; if( ! $pageid || ! WPF()->db->get_var( $sql ) ) { wpforo_create_forum_page(); } else { flush_rewrite_rules( false ); nocache_headers(); } } function wpforo_import_default_board() { if( ! WPF()->board->get_current( 'boardid' ) && ! WPF()->board->_get_board( 0 ) ) { $blogname = get_option( 'blogname', '' ); $_general = wpforo_get_option( 'wpforo_general_options', [ 'title' => $blogname . ' ' . __( 'Forum', 'wpforo' ), 'description' => $blogname . ' ' . __( 'Discussion Board', 'wpforo' ), 'lang' => 1, ], false ); $board = WPF()->board->get_current(); $board['slug'] = basename( trim( (string) wpforo_get_option( 'wpforo_permastruct', 'community', false ), '/' ) ); $board['is_standalone'] = wpforo_get_option( 'wpforo_use_home_url', false ); $board['excld_urls'] = array_filter( array_map( 'trim', explode( PHP_EOL, (string) wpforo_get_option( 'wpforo_excld_urls', '' ) ) ) ); $board['settings'] = []; $board['settings']['title'] = $_general['title']; $board['settings']['desc'] = $_general['description']; if( $boardid = WPF()->board->add( $board ) ) { WPF()->board->edit( [ 'boardid' => 0 ], $boardid ); } } } function wpforo_import_default_menus() { $boardid = WPF()->board->get_current( 'boardid' ); $menu_name = wpforo_phrase( 'wpForo Navigation', false, 'orig' ) . ( $boardid ? ' #' . $boardid : '' ); $menu_location = wpforo_prefix_slug( 'menu' ); $menu_exists = wp_get_nav_menu_object( $menu_name ); if( ! $menu_exists ) { $id = []; $menu_id = wp_create_nav_menu( $menu_name ); $id['wpforo-home'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Forums', false ), 'menu-item-classes' => 'wpforo-home', 'menu-item-url' => '/%wpforo-home%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-recent-activity'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'What\'s New', false ), 'menu-item-classes' => 'wpforo-whats-new', 'menu-item-url' => '/%wpforo-recent-activity%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-recent'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Recent Posts', false ), 'menu-item-classes' => 'wpforo-recent', 'menu-item-url' => '/%wpforo-recent%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-members'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Members', false ), 'menu-item-classes' => 'wpforo-members', 'menu-item-url' => '/%wpforo-members%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-profile'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'My Profile', false ), 'menu-item-classes' => 'wpforo-profile', 'menu-item-url' => '/%wpforo-profile-home%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); if( isset( $id['wpforo-profile'] ) && $id['wpforo-profile'] ) { $id['wpforo-profile-account'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Account', false ), 'menu-item-classes' => 'wpforo-profile-account', 'menu-item-url' => '/%wpforo-profile-account%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => $id['wpforo-profile'], 'menu-item-position' => 1, ] ); $id['wpforo-profile-activity'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Activity', false ), 'menu-item-classes' => 'wpforo-profile-activity', 'menu-item-url' => '/%wpforo-profile-activity%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => $id['wpforo-profile'], 'menu-item-position' => 1, ] ); $id['wpforo-profile-subscriptions'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Subscriptions', false ), 'menu-item-classes' => 'wpforo-profile-subscriptions', 'menu-item-url' => '/%wpforo-profile-subscriptions%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => $id['wpforo-profile'], 'menu-item-position' => 2, ] ); } $id['wpforo-register'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Register', false ), 'menu-item-classes' => 'wpforo-register', 'menu-item-url' => '/%wpforo-register%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-login'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Login', false ), 'menu-item-classes' => 'wpforo-login', 'menu-item-url' => '/%wpforo-login%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); $id['wpforo-logout'] = wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'Logout', false ), 'menu-item-classes' => 'wpforo-logout', 'menu-item-url' => '/%wpforo-logout%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => 0, ] ); if( ! has_nav_menu( $menu_location ) ) { $locations = get_theme_mod( 'nav_menu_locations' ); if( empty( $locations ) ) $locations = []; $locations[ $menu_location ] = $menu_id; set_theme_mod( 'nav_menu_locations', $locations ); } } } function wpforo_import_default_accesses() { $cans_n = [ 'vf' => 0, 'enf' => 0, 'ct' => 0, 'vt' => 0, 'ent' => 0, 'et' => 0, 'dt' => 0, 'cr' => 0, 'ocr' => 0, 'vr' => 0, 'er' => 0, 'dr' => 0, 'tag' => 0, 'eot' => 0, 'eor' => 0, 'dot' => 0, 'dor' => 0, 'sb' => 0, 'l' => 0, 'r' => 0, 's' => 0, 'au' => 0, 'p' => 0, 'op' => 0, 'vp' => 0, 'sv' => 0, 'osv' => 0, 'v' => 0, 'vop' => 0, 'vlp' => 0, 'a' => 0, 'va' => 0, 'at' => 0, 'oat' => 0, 'aot' => 0, 'cot' => 0, 'mt' => 0, // Poll-related permissions moved to wpForo Polls plugin ]; $cans_r = [ 'vf' => 1, 'enf' => 1, 'ct' => 0, 'vt' => 1, 'ent' => 1, 'et' => 0, 'dt' => 0, 'cr' => 0, 'ocr' => 0, 'vr' => 1, 'er' => 0, 'dr' => 0, 'tag' => 0, 'eot' => 0, 'eor' => 0, 'dot' => 0, 'dor' => 0, 'sb' => 1, 'l' => 0, 'r' => 0, 's' => 0, 'au' => 0, 'p' => 0, 'op' => 0, 'vp' => 0, 'sv' => 0, 'osv' => 0, 'v' => 0, 'vop' => 0, 'vlp' => 1, 'a' => 0, 'va' => 1, 'at' => 0, 'oat' => 0, 'aot' => 0, 'cot' => 0, 'mt' => 0, // Poll-related permissions moved to wpForo Polls plugin ]; $cans_s = [ 'vf' => 1, 'enf' => 1, 'ct' => 1, 'vt' => 1, 'ent' => 1, 'et' => 0, 'dt' => 0, 'cr' => 1, 'ocr' => 1, 'vr' => 1, 'er' => 0, 'dr' => 0, 'tag' => 1, 'eot' => 1, 'eor' => 1, 'dot' => 1, 'dor' => 1, 'sb' => 1, 'l' => 1, 'r' => 1, 's' => 0, 'au' => 0, 'p' => 0, 'op' => 1, 'vp' => 0, 'sv' => 0, 'osv' => 1, 'v' => 1, 'vop' => 1, 'vlp' => 1, 'a' => 1, 'va' => 1, 'at' => 0, 'oat' => 1, 'aot' => 1, 'cot' => 0, 'mt' => 0, // Poll-related permissions moved to wpForo Polls plugin ]; $cans_m = [ 'vf' => 1, 'enf' => 1, 'ct' => 1, 'vt' => 1, 'ent' => 1, 'et' => 1, 'dt' => 1, 'cr' => 1, 'ocr' => 1, 'vr' => 1, 'er' => 1, 'dr' => 1, 'tag' => 1, 'eot' => 1, 'eor' => 1, 'dot' => 1, 'dor' => 1, 'sb' => 1, 'l' => 1, 'r' => 1, 's' => 1, 'au' => 1, 'p' => 1, 'op' => 1, 'vp' => 1, 'sv' => 1, 'osv' => 1, 'v' => 1, 'vop' => 1, 'vlp' => 1, 'a' => 1, 'va' => 1, 'at' => 1, 'oat' => 1, 'aot' => 1, 'cot' => 1, 'mt' => 1, // Poll-related permissions moved to wpForo Polls plugin ]; $cans_a = [ 'vf' => 1, 'enf' => 1, 'ct' => 1, 'vt' => 1, 'ent' => 1, 'et' => 1, 'dt' => 1, 'cr' => 1, 'ocr' => 1, 'vr' => 1, 'er' => 1, 'dr' => 1, 'tag' => 1, 'eot' => 1, 'eor' => 1, 'dot' => 1, 'dor' => 1, 'sb' => 1, 'l' => 1, 'r' => 1, 's' => 1, 'au' => 1, 'p' => 1, 'op' => 1, 'vp' => 1, 'sv' => 1, 'osv' => 1, 'v' => 1, 'vop' => 1, 'vlp' => 1, 'a' => 1, 'va' => 1, 'at' => 1, 'oat' => 1, 'aot' => 1, 'cot' => 1, 'mt' => 1, // Poll-related permissions moved to wpForo Polls plugin ]; //Add new Accesses in this array to add those in custom Accesses created by forum admin $cans_default = [ 'sb' => 1, 'au' => 1, 'p' => 0, 'op' => 1, 'vp' => 0, // Poll-related permissions moved to wpForo Polls plugin 'aot' => 1, 'tag' => 1, 'ocr' => 0, 'enf' => 1, 'ent' => 1, 'vop' => 1, 'vlp' => 1, ]; $sql = "SELECT * FROM `" . WPF()->tables->accesses . "`"; $accesses = WPF()->db->get_results( $sql, ARRAY_A ); if( empty( $accesses ) ) { $cans_n = serialize( $cans_n ); $cans_r = serialize( $cans_r ); $cans_s = serialize( $cans_s ); $cans_m = serialize( $cans_m ); $cans_a = serialize( $cans_a ); $sql = "INSERT IGNORE INTO `" . WPF()->tables->accesses . "` (`access`, `title`, cans) VALUES ('no_access', 'No access', '" . $cans_n . "'), ('read_only', 'Read only access', '" . $cans_r . "'), ('standard', 'Standard access', '" . $cans_s . "'), ('moderator', 'Moderator access', '" . $cans_m . "'), ('full', 'Full access', '" . $cans_a . "')"; WPF()->db->query( $sql ); } else { foreach( $accesses as $access ) { $current = unserialize( $access['cans'] ); if( strtolower( (string) $access['access'] ) === 'no_access' ) { $default = $cans_n; } elseif( strtolower( (string) $access['access'] ) === 'read_only' ) { $default = $cans_r; } elseif( strtolower( (string) $access['access'] ) === 'standard' ) { $default = $cans_s; } elseif( strtolower( (string) $access['access'] ) === 'moderator' ) { $default = $cans_m; } elseif( strtolower( (string) $access['access'] ) === 'full' ) { $default = $cans_a; } else { $default = $cans_default; } if( ! empty( $default ) ) { $data_update = array_merge( $default, $current ); if( ! empty( $data_update ) ) { $data_update = serialize( $data_update ); WPF()->db->query( "UPDATE `" . WPF()->tables->accesses . "` SET `cans` = '" . WPF()->db->_real_escape( $data_update ) . "' WHERE `accessid` = " . intval( $access['accessid'] ) ); } } } } } function wpforo_import_default_usergroups() { $cans_admin = [ 'mf' => 1, 'mai' => 1, 'ms' => 1, 'mt' => 1, 'mp' => 1, 'mth' => 1, 'vm' => 1, 'aum' => 1, 'em' => 1, 'vmg' => 1, 'aup' => 1, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'vpra' => 1, 'vprs' => 1, 'bm' => 1, 'dm' => 1, 'upc' => 1, 'upa' => 1, 'ups' => 1, 'va' => 1, 'vmu' => 1, 'vmm' => 1, 'vmt' => 1, 'vmct' => 1, 'vmr' => 1, 'vmw' => 1, 'vmsn' => 1, 'vmrd' => 1, 'vml' => 1, 'vmo' => 1, 'vms' => 1, 'vmam' => 1, 'vwpm' => 1, 'caa' => 1, 'vt_add_topic' => 1, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 1, ]; $cans_moder = [ 'mf' => 0, 'ms' => 0, 'mai' => 0, 'mt' => 0, 'mp' => 0, 'mth' => 0, 'vm' => 0, 'aum' => 1, 'em' => 0, 'vmg' => 0, 'aup' => 1, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'vpra' => 1, 'vprs' => 1, 'bm' => 1, 'dm' => 1, 'upc' => 1, 'upa' => 1, 'ups' => 1, 'va' => 1, 'vmu' => 0, 'vmm' => 1, 'vmt' => 1, 'vmct' => 1, 'vmr' => 1, 'vmw' => 1, 'vmsn' => 1, 'vmrd' => 1, 'vml' => 1, 'vmo' => 1, 'vms' => 1, 'vmam' => 1, 'vwpm' => 1, 'caa' => 1, 'vt_add_topic' => 1, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 1, ]; $cans_reg = [ 'mf' => 0, 'ms' => 0, 'mai' => 0, 'mt' => 0, 'mp' => 0, 'mth' => 0, 'vm' => 0, 'aum' => 0, 'em' => 0, 'vmg' => 0, 'aup' => 1, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'vpra' => 1, 'vprs' => 0, 'bm' => 0, 'dm' => 0, 'upc' => 1, 'upa' => 1, 'ups' => 1, 'va' => 1, 'vmu' => 0, 'vmm' => 0, 'vmt' => 1, 'vmct' => 1, 'vmr' => 1, 'vmw' => 1, 'vmsn' => 1, 'vmrd' => 1, 'vml' => 1, 'vmo' => 1, 'vms' => 1, 'vmam' => 1, 'vwpm' => 1, 'caa' => 1, 'vt_add_topic' => 1, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 1, ]; $cans_guest = [ 'mf' => 0, 'ms' => 0, 'mai' => 0, 'mt' => 0, 'mp' => 0, 'mth' => 0, 'vm' => 0, 'aum' => 0, 'em' => 0, 'vmg' => 0, 'aup' => 0, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'vpra' => 1, 'vprs' => 0, 'bm' => 0, 'dm' => 0, 'upc' => 0, 'upa' => 0, 'ups' => 0, 'va' => 1, 'vmu' => 0, 'vmm' => 0, 'vmt' => 1, 'vmct' => 1, 'vmr' => 1, 'vmw' => 0, 'vmsn' => 1, 'vmrd' => 1, 'vml' => 1, 'vmo' => 1, 'vms' => 1, 'vmam' => 1, 'vwpm' => 0, 'caa' => 1, 'vt_add_topic' => 0, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 0, ]; $cans_customer = [ 'mf' => 0, 'ms' => 0, 'mai' => 0, 'mt' => 0, 'mp' => 0, 'mth' => 0, 'vm' => 0, 'aum' => 0, 'em' => 0, 'vmg' => 0, 'aup' => 0, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'vpra' => 1, 'vprs' => 0, 'bm' => 0, 'dm' => 0, 'upc' => 1, 'upa' => 1, 'ups' => 1, 'va' => 1, 'vmu' => 0, 'vmm' => 0, 'vmt' => 1, 'vmct' => 1, 'vmr' => 1, 'vmw' => 1, 'vmsn' => 1, 'vmrd' => 1, 'vml' => 1, 'vmo' => 1, 'vms' => 1, 'vmam' => 1, 'vwpm' => 1, 'caa' => 1, 'vt_add_topic' => 1, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 1, ]; //Add new Cans in this array to add those in custom Usergroup created by forum admin $cans_defaults = [ 'mf' => 0, 'ms' => 0, 'mai' => 0, 'mt' => 0, 'mp' => 0, 'mth' => 0, 'vmem' => 1, 'view_stat' => 1, 'vprf' => 1, 'caa' => 1, 'vt_add_topic' => 1, 'upc' => 1, 'ai_search' => 1, 'ai_summary' => 1, 'ai_translation' => 1, 'ai_suggestion' => 1, ]; $sql = "SELECT * FROM `" . WPF()->tables->usergroups . "`"; if( ! $usergroups = WPF()->db->get_results( $sql, ARRAY_A ) ) { WPF()->usergroup->add( 'Admin', $cans_admin, '', 'administrator', 'full', '#FF3333' ); WPF()->usergroup->add( 'Moderator', $cans_moder, '', 'editor', 'moderator', '#0066FF' ); WPF()->usergroup->add( 'Registered', $cans_reg, '', 'subscriber', 'standard', '', 1, 1 ); WPF()->usergroup->add( 'Guest', $cans_guest, '', '', 'read_only', '#222222', 0 ); WPF()->usergroup->add( 'Customer', $cans_customer, '', 'customer', 'standard', '#993366', 1, 1 ); } else { foreach( $usergroups as $usergroup ) { $current = unserialize( $usergroup['cans'] ); if( strtolower( (string) $usergroup['name'] ) === 'admin' ) { $default = $cans_admin; } elseif( strtolower( (string) $usergroup['name'] ) === 'moderator' ) $default = $cans_moder; elseif( strtolower( (string) $usergroup['name'] ) === 'registered' ) $default = $cans_reg; elseif( strtolower( (string) $usergroup['name'] ) === 'guest' ) $default = $cans_guest; elseif( strtolower( (string) $usergroup['name'] ) === 'customer' ) $default = $cans_customer; else { $default = $cans_defaults; } if( ! empty( $default ) ) { $data_update = array_merge( $default, $current ); if( ! empty( $data_update ) ) { $data_update = serialize( $data_update ); WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `cans` = '" . WPF()->db->_real_escape( $data_update ) . "' WHERE `groupid` = " . intval( $usergroup['groupid'] ) ); } } } } } function wpforo_import_default_forums() { $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->forums . "`"; $count = WPF()->db->get_var( $sql ); if( ! $count ) { if( $parentid = WPF()->forum->add( [ 'title' => __( 'Main Category', 'wpforo' ), 'description' => __( 'This is a simple category / section', 'wpforo' ), 'layout' => 4, 'icon' => 'fas fa-comments' ], false ) ) { WPF()->forum->add( [ 'title' => __( 'Main Forum', 'wpforo' ), 'description' => __( 'This is a simple parent forum', 'wpforo' ), 'parentid' => $parentid, 'layout' => 4, 'icon' => 'fas fa-comments' ], false ); } } } function wpforo_fix_wp_permalink_structure() { $permalink_structure = get_option( 'permalink_structure' ); if( ! $permalink_structure ) { global $wp_rewrite; $wp_rewrite->set_permalink_structure( '/%postname%/' ); } } function wpforo_fix_installed_options() { /** * in the update process, using old features settings set the right profile setting for the new option */ if( $features = wpforo_get_option( 'features', [], false ) ) { if( ! (int) wpfval( $features, 'profile' ) ) { if( (int) wpfval( $features, 'bp_profile' ) && class_exists( 'BP_Component' ) ) { $features['profile'] = 3; } elseif( (int) wpfval( $features, 'um_profile' ) && function_exists( 'UM' ) ) { $features['profile'] = 4; } elseif( (int) wpfval( $features, 'comment-author-link' ) ) { $features['profile'] = 2; } else { $features['profile'] = 1; } wpforo_update_option( 'features', array_map( 'intval', $features ) ); } } ################################################################# // CHECK Addon Notice ///////////////////////////////////////// $lastHash = get_option( 'wpforo_addon_note_dismissed' ); $first = get_option( 'wpforo_addon_note_first' ); if( $lastHash && $first === 'true' ) { update_option( 'wpforo_addon_note_first', 'false' ); } ################################################################# // AVOID PLUGIN CONFLICTS /////////////////////////////////////// /* Autoptimize *************************************************/ $autopt = get_option( 'autoptimize_js_exclude' ); if( $autopt && strpos( (string) $autopt, 'wp-includes/js/tinymce' ) === false ) { $autopt = $autopt . ', wp-includes/js/tinymce'; update_option( 'autoptimize_js_exclude', $autopt ); if( class_exists( 'autoptimizeCache' ) && is_callable( [ 'autoptimizeCache', 'clearall' ] ) ) { autoptimizeCache::clearall(); } } ################################################################# // Adding #wpforo to custom css ///////////////////////////////// if( $style_options = wpforo_get_option( 'style_options', [], false ) ) { $custom_css = wpfval( $style_options, 'custom_css' ); if( $custom_css && strpos( (string) $custom_css, '#wpforo #wpforo-wrap' ) === false ) { $style_options['custom_css'] = str_replace( '#wpforo-wrap', '#wpforo #wpforo-wrap', $style_options['custom_css'] ); wpforo_update_option( 'style_options', $style_options ); } } } function wpforo_update_theme_options() { if( $current_theme = wpforo_get_option( 'theme_options_' . WPF()->tpl->theme, [], false ) ) { $theme = WPF()->tpl->find_theme( '2026' ); if( wpfval( $theme, 'layouts' ) ) { $current_theme['layouts'] = $theme['layouts']; $theme = wpforo_deep_merge( $theme, $current_theme ); wpforo_update_option( 'theme_options_' . WPF()->tpl->theme, $theme ); } } } function wpforo_clean_up() { WPF()->db->delete( WPF()->tables->topics, [ 'first_postid' => 0 ], [ '%d' ] ); } // wpforo database checker fixer tools function wpforo_update_db() { $problems = wpforo_database_check(); if( ! empty( $problems ) ) { $SQL = wpforo_database_fixer( $problems ); if( wpfval( $SQL, 'fields' ) ) { foreach( $SQL['fields'] as $query ) WPF()->db->query( $query ); } if( wpfval( $SQL, 'keys' ) ) { foreach( $SQL['keys'] as $query ) WPF()->db->query( $query ); } if( wpfval( $SQL, 'tables' ) ) { foreach( $SQL['tables'] as $query ) { if( false === WPF()->db->query( $query ) ) { WPF()->db->query( preg_replace( '#\)[\r\n\t\s]*ENGINE.*$#isu', ')', $query ) ); } } } if( wpfval( $SQL, 'data' ) ) { foreach( $SQL['data'] as $query ) { WPF()->db->query( $query ); } } } wpforo_update_option( 'version_db', WPFORO_VERSION ); } /** * @param array $args * * @return bool|string|null */ function wpforo_db_check( $args = [] ) { $key = [ 'wpforo_db_check', $args ]; if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key ); global $wpdb; $col = esc_sql( trim( (string) wpfval( $args, 'col' ) ) ); $table = esc_sql( trim( (string) wpfval( $args, 'table' ) ) ); $result = null; switch( trim( (string) wpfval( $args, 'check' ) ) ) { case 'table_exists': $result = (bool) $wpdb->get_var( "SHOW TABLES LIKE '$table'" ); break; case 'col_exists': $result = (bool) $wpdb->get_var( "SHOW COLUMNS FROM `$table` LIKE '$col'" ); break; case 'key_exists': $result = (bool) $wpdb->get_var( "SHOW KEYS FROM `$table` WHERE `Key_name` = '$col'" ); break; case 'default_value': $c = (array) $wpdb->get_row( "SHOW COLUMNS FROM `$table` LIKE '$col'", ARRAY_A ); $result = wpfval( $c, 'Default' ); break; case 'col_type': $c = (array) $wpdb->get_row( "SHOW COLUMNS FROM `$table` LIKE '$col'", ARRAY_A ); $result = wpfval( $c, 'Type' ); break; } WPF()->ram_cache->set( $key, $result ); return $result; } function wpforo_database_check() { $_tables = []; $_table_diffs = []; require_once( WPFORO_DIR . '/includes/install-sql.php' ); $wpforo_sql = wpforo_get_install_sqls(); if( ! empty( $wpforo_sql ) ) { foreach( $wpforo_sql as $sql ) { if( preg_match( '|EXISTS \`([^\(]+)\`\s*\((.+)(PRIMARY.+)\)\s*ENGINE|is', $sql, $table ) ) { if( wpfval( $table, 1 ) ) { if( wpfval( $table, 2 ) ) { if( preg_match_all( '|\`([^\`]+)\`|is', $table[2], $fields, PREG_SET_ORDER ) ) { foreach( $fields as $field ) { if( wpfval( $field, 1 ) ) $_tables[ $table[1] ]['fields'][] = $field[1]; } } } if( wpfval( $table, 3 ) ) { if( preg_match( '|PRIMARY KEY \(\`([^\`]+)\`\)|is', $table[3], $primary_key ) ) { $_tables[ $table[1] ]['keys'][] = $primary_key[1]; } if( preg_match_all( '|KEY \`([^\`]+)\`|is', $table[3], $keys, PREG_SET_ORDER ) ) { foreach( $keys as $key ) { if( wpfval( $key, 1 ) ) $_tables[ $table[1] ]['keys'][] = $key[1]; } } } } } } if( ! empty( $_tables ) ) { foreach( $_tables as $_name => $_structure ) { $_table_fields = []; $_table_keys = []; $_table_exists = WPF()->db->get_var( "SHOW TABLES LIKE '" . esc_sql( $_name ) . "'" ); if( $_table_exists ) { //Problems - Missing Field if( wpfval( $_structure, 'fields' ) ) { $_fields = WPF()->db->get_results( "SHOW FULL COLUMNS FROM " . esc_sql( $_name ), ARRAY_A ); foreach( $_fields as $_field ) $_table_fields[] = $_field['Field']; $_count_orig = count( $_structure['fields'] ); $_count_curr = count( $_table_fields ); if( $_count_curr < $_count_orig ) { $diff = array_diff( $_structure['fields'], $_table_fields ); if( ! empty( $diff ) ) $_table_diffs[ $_name ]['fields'][ $_name ] = $diff; } } //Problems - Missing Key if( wpfval( $_structure, 'keys' ) ) { $_keys = WPF()->db->get_results( "SHOW KEYS FROM " . esc_sql( $_name ), ARRAY_A ); foreach( $_keys as $_key ) { if( strpos( (string) $_key['Key_name'], 'PRIMARY' ) !== false ) { $_table_keys[] = $_key['Column_name']; } else { $_table_keys[] = $_key['Key_name']; } } $_table_keys = array_unique( $_table_keys ); $_table_keys = array_values( $_table_keys ); $_count_orig = count( $_structure['keys'] ); $_count_curr = count( $_table_keys ); if( $_count_curr < $_count_orig ) { $diff_keys = array_diff( $_structure['keys'], $_table_keys ); if( ! empty( $diff_keys ) ) $_table_diffs[ $_name ]['keys'][ $_name ] = $diff_keys; } } } else { //Problems - Missing Table $_table_diffs[ $_name ]['exists'] = 'no'; } } } } $first_board = WPF()->db->get_row( "SELECT `boardid` FROM `" . WPF()->tables->boards . "` WHERE `boardid` = 0", ARRAY_A ); if( empty( $first_board ) ) { $_table_diffs['data']['default_board'] = 'no'; } return $_table_diffs; } function wpforo_database_parse() { $_tables = []; $_table_diffs = []; require_once( WPFORO_DIR . '/includes/install-sql.php' ); $wpforo_sql = wpforo_get_install_sqls(); if( ! empty( $wpforo_sql ) ) { foreach( $wpforo_sql as $sql ) { if( preg_match( '|EXISTS \`([^\(]+)\`\s*\((.+)(PRIMARY.+)\)\s*ENGINE|is', $sql, $table ) ) { if( wpfval( $table, 1 ) ) { if( wpfval( $table, 2 ) ) { $_tables[ $table[1] ]['fields'] = array_map( 'trim', explode( ',', $table[2] ) ); } if( wpfval( $table, 3 ) ) { $_tables[ $table[1] ]['keys'] = array_map( 'trim', explode( PHP_EOL, $table[3] ) ); } } } } } return $_tables; } function wpforo_database_fixer( $problems ) { $SQL = []; if( ! empty( $problems ) ) { require_once( WPFORO_DIR . '/includes/install-sql.php' ); $table_structure = wpforo_database_parse(); if( ! empty( $table_structure ) ) { foreach( $problems as $table_name => $problem ) { if( wpfval( $problem, 'fields' ) ) { foreach( $problem['fields'] as $problem_fields ) { if( ! empty( $problem_fields ) ) { foreach( $problem_fields as $problem_field ) { if( wpfval( $table_structure, $table_name, 'fields' ) ) { foreach( $table_structure[ $table_name ]['fields'] as $field_sql ) { if( strpos( (string) $field_sql, '`' . $problem_field . '`' ) !== false ) { $SQL['fields'][] = 'ALTER TABLE `' . $table_name . '` ADD ' . $field_sql . ';'; } } } } } } } if( wpfval( $problem, 'keys' ) ) { foreach( $problem['keys'] as $problem_keys ) { if( ! empty( $problem_keys ) ) { foreach( $problem_keys as $problem_key ) { if( wpfval( $table_structure, $table_name, 'keys' ) ) { foreach( $table_structure[ $table_name ]['keys'] as $key_sql ) { if( preg_match( '|KEY \`' . $problem_key . '\`|is', (string) $key_sql ) ) { $SQL['keys'][] = 'ALTER TABLE `' . $table_name . '` ADD ' . trim( (string) $key_sql, ',' ) . ';'; } } } } } } } if( wpfval( $problem, 'exists' ) ) { $wpforo_sql = wpforo_get_install_sqls(); if( wpfval( $wpforo_sql, $table_name ) ) { $SQL['tables'][] = preg_replace( '|\t+|', ' ', $wpforo_sql[ $table_name ] ); } } } } if( wpfval( $problems, 'data' ) ) { if( wpfval( $problems, 'data', 'default_board' ) ) { // Missing default board (ID:0) $board = []; $blogname = get_option( 'blogname', '' ); $_general = wpforo_get_option( 'wpforo_general_options', [ 'title' => $blogname . ' ' . __( 'Forum', 'wpforo' ), 'description' => $blogname . ' ' . __( 'Discussion Board', 'wpforo' ), ], false ); $board['title'] = ( wpfval( $_general, 'title' ) ) ?: 'Forum'; $board['settings']['title'] = ( wpfval( $_general, 'title' ) ) ?: 'Forum'; $board['settings']['desc'] = ( wpfval( $_general, 'description' ) ) ?: 'Discussion Board'; $settings = wp_json_encode( $board['settings'] ); WPF()->db->query( "UPDATE `" . WPF()->tables->boards . "` SET `slug` = CONCAT('community-', `boardid`) WHERE `slug` = 'community' AND `boardid` != 0" ); $slug = basename( trim( (string) wpforo_get_option( 'wpforo_permastruct', 'community', false ), '/' ) ); $board['slug'] = ( $slug ) ?: 'community'; $board['locale'] = wpforo_get_site_default_locale(); $board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 ); $all_modules = array_map( '__return_true', wpforo_get_modules_info() ); $all_addons = array_map( '__return_true', wpforo_get_addons_info() ); $modules = array_merge( $all_modules, $all_addons ); $board['modules'] = wp_json_encode( array_map( function( $a ) { return (bool) intval( $a ); }, $modules ) ); $SQL['data']['pre_board'] = "SET sql_mode='NO_AUTO_VALUE_ON_ZERO';"; $SQL['data']['default_board'] = "INSERT INTO `" . WPF()->tables->boards . "` (`boardid`, `title`, `slug`, `pageid`, `modules`, `locale`, `is_standalone`, `excld_urls`, `status`, `settings`) VALUES(0, '" . esc_sql( $board['title'] ) . "', '" . esc_sql( $board['slug'] ) . "', " . intval( $board['pageid'] ) . ", '" . esc_sql( $board['modules'] ) . "', '" . esc_sql( $board['locale'] ) . "', 0, '[]', 1, '" . esc_sql( $settings ) . "');"; } //Other data checking here... } } return $SQL; } function wpforo_add_unique_key( $table, $primary_key, $unique_key_name = '', $unique_fields = '' ) { $table = esc_sql( trim( (string) $table ) ); $primary_key = esc_sql( trim( (string) $primary_key ) ); $unique_fields = esc_sql( trim( (string) $unique_fields, ',' ) ); $unique_fields_clean = preg_replace( '|\([^()]+\)|', '', (string) $unique_fields ); $remove_rows = ''; $sql = "SELECT GROUP_CONCAT(`$primary_key`) duplicated_row_ids, COUNT(*) duplication_count FROM `$table` GROUP BY $unique_fields_clean HAVING duplication_count > 1"; $rows = WPF()->db->get_results( $sql, ARRAY_A ); if( ! empty( $rows ) ) { foreach( $rows as $row ) { $ids = explode( ',', $row['duplicated_row_ids'] ); $ids = array_reverse( $ids ); $ids = array_slice( $ids, 1 ); $remove_rows .= trim( implode( ',', $ids ), ',' ) . ','; } $remove_rows = esc_sql( trim( $remove_rows, ',' ) ); if( $remove_rows ) { WPF()->db->query( "DELETE FROM `$table` WHERE `$primary_key` IN($remove_rows)" ); } } $sql = "ALTER TABLE `$table` ADD UNIQUE KEY `$unique_key_name`( $unique_fields )"; WPF()->db->query( $sql ); } /** * Add What’s New menu item on plugin update (2.x to 3.x migration only) * Adds "What’s New" right after the first [Forums] menu item. * This only runs once during the 2.x to 3.x update. * * @since 3.0.0 */ function wpforo_migrate_recent_activity_menu() { // Only run this migration once (2.x to 3.x update) if( get_option( 'wpforo_recent_activity_menu_migrated' ) ) { return; } // Get all boards to migrate menus for each $boards = []; if( isset( WPF()->board ) && method_exists( WPF()->board, 'get_boards' ) ) { $boards = WPF()->board->get_boards(); } // If no boards found or multi-board not initialized, just migrate default board if( empty( $boards ) ) { $boards = [ [ 'boardid' => 0 ] ]; } foreach( $boards as $board ) { $boardid = intval( wpfval( $board, 'boardid' ) ); $menu_name = wpforo_phrase( 'wpForo Navigation', false, 'orig' ) . ( $boardid ? ' #' . $boardid : '' ); $menu_obj = wp_get_nav_menu_object( $menu_name ); if( ! $menu_obj ) { continue; // Skip boards without a menu (fresh install will create menu with What's New) } $menu_id = $menu_obj->term_id; $menu_items = wp_get_nav_menu_items( $menu_id ); if( empty( $menu_items ) ) { continue; } // Check if "What's New" already exists (check both old and new CSS class names) $has_whats_new = false; foreach( $menu_items as $item ) { if( is_array( $item->classes ) && ( in_array( 'wpforo-whats-new', $item->classes ) || in_array( 'wpforo-recent-activity', $item->classes ) ) ) { $has_whats_new = true; break; } } if( $has_whats_new ) { continue; // What's New already exists for this board, skip } // Find the first [Forums] menu item (wpforo-home) to get its position $forums_position = 1; foreach( $menu_items as $item ) { if( is_array( $item->classes ) && in_array( 'wpforo-home', $item->classes ) ) { $forums_position = $item->menu_order; break; } } // Shift all menu items after [Forums] by 1 position foreach( $menu_items as $item ) { if( $item->menu_order > $forums_position ) { wp_update_nav_menu_item( $menu_id, $item->db_id, [ 'menu-item-title' => $item->title, 'menu-item-classes' => implode( ' ', (array) $item->classes ), 'menu-item-url' => $item->url, 'menu-item-status' => 'publish', 'menu-item-parent-id' => $item->menu_item_parent, 'menu-item-position' => $item->menu_order + 1, ] ); } } // Add "What's New" right after [Forums] wp_update_nav_menu_item( $menu_id, 0, [ 'menu-item-title' => wpforo_phrase( 'What\'s New', false ), 'menu-item-classes' => 'wpforo-whats-new', 'menu-item-url' => '/%wpforo-recent-activity%/', 'menu-item-status' => 'publish', 'menu-item-parent-id' => 0, 'menu-item-position' => $forums_position + 1, ] ); } // Mark migration as complete - won't run again on future updates update_option( 'wpforo_recent_activity_menu_migrated', 1 ); } /** * Migrate theme styles from 2022 to 2026 theme on plugin update (2.x to 3.x migration) * Preserves user's custom colors and CSS when the default theme changes from 2022 to 2026. * Supports multi-board installations. * This only runs once during the 2.x to 3.x update. * * @since 3.0.0 */ function wpforo_migrate_theme_styles() { // Only run this migration once if( get_option( 'wpforo_theme_styles_migrated' ) ) { return; } // Get all boards to migrate styles for each $boards = []; if( isset( WPF()->board ) && method_exists( WPF()->board, 'get_boards' ) ) { $boards = WPF()->board->get_boards(); } // If no boards found or multi-board not initialized, just migrate default board if( empty( $boards ) ) { $boards = [ [ 'boardid' => 0 ] ]; } foreach( $boards as $board ) { $boardid = intval( wpfval( $board, 'boardid' ) ); $prefix = WPF()->generate_prefix( $boardid ); // Get the old 2022 theme styles for this board $styles_2022 = get_option( $prefix . 'styles_2022', [] ); // If user had 2022 styles, copy them to 2026 theme if( ! empty( $styles_2022 ) && ! get_option( $prefix . 'styles_2026', [] ) ) { update_option( $prefix . 'styles_2026', $styles_2022 ); } } // Mark migration as complete update_option( 'wpforo_theme_styles_migrated', 1 ); } /** * Migrate AI quality settings to optimized defaults for better performance. * Changes quality tiers from 'advanced' to 'fast'/'balanced' to reduce API timeouts. * This only runs once during plugin update. * * New defaults (3.0.3+): * - search_quality: 'fast' (was 'balanced') * - search_enhance_quality: 'balanced' (was 'advanced') * - translation_quality: 'fast' (was 'advanced') * - topic_summary_quality: 'balanced' (was 'advanced') * - moderation_spam_quality: 'balanced' (was 'advanced') * - chatbot_quality: 'balanced' (was 'fast') * * @since 3.0.3 */ function wpforo_migrate_ai_quality_defaults() { // Only run this migration once if( get_option( 'wpforo_ai_quality_defaults_migrated' ) ) { return; } // Define quality settings to update (old_value => new_value) $quality_changes = [ 'search_quality' => 'fast', 'search_enhance_quality' => 'balanced', 'translation_quality' => 'fast', 'topic_summary_quality' => 'balanced', 'moderation_spam_quality' => 'balanced', 'chatbot_quality' => 'balanced', ]; // Get all board IDs (including default board 0) $boardids = [ 0 ]; if( class_exists( 'wpforo\\classes\\Board' ) && method_exists( WPF()->board, 'get_active_boardids' ) ) { $boardids = array_unique( array_merge( $boardids, (array) WPF()->board->get_active_boardids() ) ); } foreach( $boardids as $boardid ) { // Build option name: wpforo_ai (board 0) or wpforo_{boardid}_ai (other boards) $option_name = 'wpforo_' . ( $boardid ? $boardid . '_' : '' ) . 'ai'; $ai_settings = get_option( $option_name, [] ); // Skip if no settings saved (new install, will use new defaults) if( empty( $ai_settings ) ) { continue; } // Update only the quality settings $updated = false; foreach( $quality_changes as $key => $new_value ) { // Only update if the key exists (user has this setting saved) if( isset( $ai_settings[ $key ] ) ) { $ai_settings[ $key ] = $new_value; $updated = true; } } // Save updated settings if( $updated ) { update_option( $option_name, $ai_settings ); } } // Mark migration as complete update_option( 'wpforo_ai_quality_defaults_migrated', 1 ); }