PluginProbe
wpForo Forum / 3.0.7
wpForo Forum v3.0.7
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / includes / installation.php

installation.php in wpForo Forum 3.0.7, at includes/installation.php

1,874 lines 74.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // Exit if accessed directly
3 if( ! defined( 'ABSPATH' ) ) exit;
4
5 function wpforo_activation() {
6 #################################################################
7 // Create wpForo Tables /////////////////////////////////////////
8 wpforo_create_tables();
9
10 #################################################################
11 // Alter wpForo Tables //////////////////////////////////////////
12 wpforo_alter_tables();
13
14 #################################################################
15 // Permalink Settings ///////////////////////////////////////////
16 wpforo_fix_wp_permalink_structure();
17
18 #################################################################
19 // Access Sets //////////////////////////////////////////////////
20 wpforo_import_default_accesses();
21
22 #################################################################
23 // fix already installed options ////////////////////////////////
24 wpforo_fix_installed_options();
25
26 #################################################################
27 // synchronize users ////////////////////////////////////////////
28 WPF()->member->synchronize_users( 100 );
29 WPF()->member->init_current_user();
30 WPF()->member->clear_db_cache();
31
32 #################################################################
33 // Importing Language Packs and Phrases /////////////////////////
34 WPF()->phrase->xml_import( 'english.xml' );
35 WPF()->phrase->clear_cache();
36
37 #################################################################
38 // Creating Forum Page //////////////////////////////////////////
39 wpforo_create_forum_page();
40
41 #################################################################
42 // Forum Navigation and Menu ////////////////////////////////////
43 wpforo_import_default_menus();
44
45 #################################################################
46 // Boards ////////////////////////////////////////////////////
47 wpforo_import_default_board();
48
49 #################################################################
50 // Usergroup ////////////////////////////////////////////////////
51 wpforo_import_default_usergroups();
52
53 #################################################################
54 // Forums ////////////////////////////////////////////////////
55 wpforo_import_default_forums();
56
57 #################################################################
58 // UPDATE THEME OPTIONS ////////////////////////////////////////
59 wpforo_update_theme_options();
60
61 #################################################################
62 // wpForo Upgrade ///////////////////////////////
63 wpforo_upgrade();
64
65 #################################################################
66 // UPDATE VERSION - END /////////////////////////////////////////
67 wpforo_update_option( 'version', WPFORO_VERSION );
68 WPF()->notice->clear();
69
70 #################################################################
71 // DELETE ALL CAHCES /////////////////////////////////////////
72 wpforo_clean_cache();
73 // At the moment only Redis Object Cache should be flushed because it causes 404 error for all forum pages.
74 if( function_exists( 'redis_object_cache' ) ) {
75 wp_cache_flush();
76 }
77
78 #################################################################
79 // Schedule AI Cache Cleanup Cron ///////////////////////////////
80 WPF()->ai_client->schedule_cache_cleanup();
81
82 // Schedule daily subscription sync (updates cached subscription status)
83 WPF()->ai_client->schedule_daily_subscription_sync();
84
85 // Schedule AI Moderation Log Cleanup Cron
86 \wpforo\classes\AIContentModeration::get_instance()->schedule_moderation_cleanup();
87
88 // Note: AI Pending Topics Indexing Cron is scheduled when user
89 // enables auto-indexing in the admin settings (disabled by default)
90 }
91
92 function wpforo_upgrade() {
93 if( version_compare( wpforo_get_option( 'version', '', false ), '2.0.3', '<' ) ) {
94 // #### migrate old options to new settings
95 require_once WPFORO_DIR . "/includes/options-migration.php";
96 _wpforo_migrate_old_options_to_new();
97 _wpforo_migrate_old_widgets_to_new();
98 }
99
100 // Migrate menu for Recent Activity page (upgrade from 2.x to 3.x)
101 $old_version = wpforo_get_option( 'version', '', false );
102 if( $old_version && version_compare( $old_version, '3.0.0', '<' ) ) {
103 wpforo_migrate_recent_activity_menu();
104 wpforo_migrate_theme_styles();
105 }
106
107 // Migrate AI quality defaults to faster tiers (3.0.3+)
108 // Runs for all versions - has its own one-time flag check
109 wpforo_migrate_ai_quality_defaults();
110 }
111
112 function wpforo_create_tables() {
113 require_once( WPFORO_DIR . '/includes/install-sql.php' );
114 foreach( wpforo_get_install_sqls() as $sql ) {
115 if( false === @WPF()->db->query( $sql ) ) {
116 @WPF()->db->query( preg_replace( '#\)\s*ENGINE.*$#isu', ')', $sql ) );
117 }
118 }
119 }
120
121 function wpforo_alter_tables() {
122 #################################################################
123 // ADD `status` field in `languages` TABLE //////////////////////////////////////////////
124 if( ! wpforo_db_check( [ 'table' => WPF()->tables->languages, 'col' => 'status', 'check' => 'col_exists' ] ) ) {
125 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->languages . "` ADD COLUMN `status` TINYINT(1) NOT NULL DEFAULT 1" );
126 }
127
128 #################################################################
129 // ADD `package` field in `phrases` TABLE //////////////////////////////////////////////
130 if( ! wpforo_db_check( [ 'table' => WPF()->tables->phrases, 'col' => 'package', 'check' => 'col_exists' ] ) ) {
131 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->phrases . "` ADD `package` VARCHAR(255) NOT NULL DEFAULT 'wpforo'" );
132 }
133 // WPF()->phrase->clear_cache();
134
135 #################################################################
136 // ADD `private` field in TOPIC TABLE ///////////////////////////
137 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'private', 'check' => 'col_exists' ] ) ) {
138 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `private` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `is_private` (`private`);" );
139 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD INDEX `own_private` ( `userid`, `private`);" );
140 }
141 // ADD `solved` field in TOPIC TABLE ///////////////////////////
142 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'solved', 'check' => 'col_exists' ] ) ) {
143 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `solved` TINYINT(1) NOT NULL DEFAULT 0 AFTER `type`, ADD INDEX `solved` (`solved`)" );
144 @WPF()->db->query(
145 "UPDATE `" . WPF()->tables->topics . "` t
146 INNER JOIN `" . WPF()->tables->posts . "` p ON p.`topicid` = t.`topicid` AND p.`is_answer` = 1
147 SET t.`solved` = 1
148 WHERE t.`solved` = 0"
149 );
150 wpforo_clean_cache();
151 }
152 // ADD `status` field in TOPICS & POSTS TABLE ///////////////////////////
153 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'status', 'check' => 'col_exists' ] ) ) {
154 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `status` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `status` (`status`);" );
155 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `status` TINYINT(1) NOT NULL DEFAULT '0', ADD INDEX `status` (`status`);" );
156 }
157 // ADD `name` and `email` field in TOPIC TABLE ///////////////////////////
158 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'name', 'check' => 'col_exists' ] ) ) {
159 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD `name` VARCHAR(50) NOT NULL, ADD `email` VARCHAR(50) NOT NULL" );
160 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `name` VARCHAR(50) NOT NULL, ADD `email` VARCHAR(50) NOT NULL" );
161 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `email` (`email`)" );
162 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `email` (`email`)" );
163 }
164 // ADD `utitle`, `role` and `access` to USERGROUP TABLE /////////
165 if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'utitle', 'check' => 'col_exists' ] ) ) {
166 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `utitle` VARCHAR(100), ADD `role` VARCHAR(50), ADD `access` VARCHAR(50)" );
167 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Admin', `role` = 'administrator', `access` = 'full' WHERE `groupid` = 1" );
168 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Moderator', `role` = 'editor', `access` = 'moderator' WHERE `groupid` = 2" );
169 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Registered', `role` = 'subscriber', `access` = 'standard' WHERE `groupid` = 3" );
170 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Guest', `role` = '', `access` = 'read_only' WHERE `groupid` = 4" );
171 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'Customer', `role` = 'customer', `access` = 'standard' WHERE `groupid` = 5" );
172 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `utitle` = 'name', `role` = 'subscriber', `access` = 'standard' WHERE `utitle` IS NULL OR `utitle` = ''" );
173 }
174 #################################################################
175 // ADD `color` field in usergroups TABLE ///////////////////////////
176 if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'color', 'check' => 'col_exists' ] ) ) {
177 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `color` varchar(7) NOT NULL DEFAULT ''" );
178 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#FF3333' WHERE `groupid` = 1" );
179 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#0066FF' WHERE `groupid` = 2" );
180 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#222222' WHERE `groupid` = 4" );
181 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `color` = '#993366' WHERE `groupid` = 5" );
182 }
183 #################################################################
184 // ADD `visible` field in usergroups TABLE ///////////////////////////
185 if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'visible', 'check' => 'col_exists' ] ) ) {
186 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `visible` TINYINT(1) NOT NULL DEFAULT 1;" );
187 }
188 #################################################################
189 // ADD `status` field in `languages` TABLE //////////////////////////////////////////////
190 if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'is_default', 'check' => 'col_exists' ] ) ) {
191 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD COLUMN `is_default` TINYINT(1) NOT NULL DEFAULT 0" );
192 }
193 #################################################################
194 // ADD `online_time` field in profiles TABLE ///////////////////////////
195 if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'online_time', 'check' => 'col_exists' ] ) ) {
196 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `online_time` INT UNSIGNED NOT NULL DEFAULT 0, ADD KEY (`online_time`)" );
197 }
198 // ADD `is_email_confirmed` field in profiles TABLE ///////////////////////////
199 if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'is_email_confirmed', 'check' => 'col_exists' ] ) ) {
200 WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `is_email_confirmed` TINYINT(1) NOT NULL DEFAULT 0, ADD KEY (`is_email_confirmed`)" );
201 WPF()->db->query(
202 "UPDATE `" . WPF()->tables->profiles . "`
203 JOIN `" . WPF()->tables->subscribes . "`
204 ON `" . WPF()->tables->subscribes . "`.`userid` = `" . WPF()->tables->profiles . "`.`userid`
205 SET `" . WPF()->tables->profiles . "`.`is_email_confirmed` = 1
206 WHERE `" . WPF()->tables->subscribes . "`.`active` = 1"
207 );
208 WPF()->db->query( "UPDATE `" . WPF()->tables->profiles . "` SET `is_email_confirmed` = 1 WHERE `groupid` = 1" );
209 }
210 #################################################################
211 // DROP uname unique key from profiles TABLE ///////////////////////////
212 if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'UNIQUE USERNAME', 'check' => 'key_exists' ] ) ) {
213 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` DROP KEY `UNIQUE USERNAME`" );
214 }
215 if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'UNIQUE ID', 'check' => 'key_exists' ] ) ) {
216 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` DROP KEY `UNIQUE ID`" );
217 }
218 #################################################################
219 // ADD `private` field in post TABLE ///////////////////////////
220 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'private', 'check' => 'col_exists' ] ) ) {
221 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `private` TINYINT(1) NOT NULL DEFAULT 0, ADD INDEX `is_private` (`private`)" );
222 }
223 #################################################################
224 //Add user_name col in subsciption table///////////////////////////
225 if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'user_name', 'check' => 'col_exists' ] ) ) {
226 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` ADD `user_name` VARCHAR(60) NOT NULL DEFAULT ''" );
227 }
228 //Add user_email col in subsciption table
229 if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'user_email', 'check' => 'col_exists' ] ) ) {
230 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` ADD `user_email` VARCHAR(60) NOT NULL DEFAULT ''" );
231 }
232 //Add indexes for subscribe new fields
233 if( ! wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'fld_group_unq', 'check' => 'key_exists' ] ) ) {
234 $args = [ 'table' => WPF()->tables->subscribes, 'col' => 'itemid', 'check' => 'key_exists' ];
235 if( wpforo_db_check( $args ) ) @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` DROP KEY `itemid`" );
236 wpforo_add_unique_key( WPF()->tables->subscribes, 'subid', 'fld_group_unq', '`itemid`, `type`, `userid`, `user_email`(60)' );
237 }
238 if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->subscribes, 'col' => 'type', 'check' => 'col_type' ] ) ) !== 'varchar(50)' ) {
239 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->subscribes . "` MODIFY `type` VARCHAR(50) NOT NULL" );
240 }
241 ####################################################################
242 //Add index for double condition queries to avoid SQl caching///////
243 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'forumid_status', 'check' => 'key_exists' ] ) ) {
244 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `forumid_status` (`forumid`,`status`)" );
245 }
246 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_status', 'check' => 'key_exists' ] ) ) {
247 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_status` (`topicid`,`status`)" );
248 }
249 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_solved', 'check' => 'key_exists' ] ) ) {
250 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_solved` (`topicid`,`is_answer`)" );
251 }
252 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'forumid_status', 'check' => 'key_exists' ] ) ) {
253 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `forumid_status` (`forumid`,`status`)" );
254 }
255 $topic_slug_key = @WPF()->db->get_row( "SHOW KEYS FROM `" . WPF()->tables->topics . "` WHERE `Key_name` LIKE 'slug'", ARRAY_A );
256 if( $topic_slug_key && intval( wpfval( $topic_slug_key, 'Non_unique' ) ) === 0 ) {
257 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` DROP INDEX slug, ADD KEY slug (`slug`(191))" );
258 }
259 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'topicid_parentid', 'check' => 'key_exists' ] ) ) {
260 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `topicid_parentid` (`topicid`,`parentid`)" );
261 }
262 #################################################################
263 // ADD `secondary_groupids` field in profiles TABLE //////////////
264 /*if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'secondary_groups', 'check' => 'col_exists' ] ) ) {
265 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `secondary_groups` VARCHAR(255)" );
266 }*/
267 if( ! wpforo_db_check( [ 'table' => WPF()->tables->usergroups, 'col' => 'secondary', 'check' => 'col_exists' ] ) ) {
268 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->usergroups . "` ADD `secondary` TINYINT(1) NOT NULL DEFAULT 0;" );
269 @WPF()->db->query( "UPDATE `" . WPF()->tables->usergroups . "` SET `secondary` = 1 WHERE `groupid` IN(3,5)" );
270 }
271 #################################################################
272 // ADD `fields` field in profiles TABLE ////////////////////////
273 if( ! wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'fields', 'check' => 'col_exists' ] ) ) {
274 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->profiles . "` ADD `fields` LONGTEXT" );
275 }
276 #################################################################
277 // Change `phrase_key` type in phrases TABLE ///////////////////
278 if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->phrases, 'col' => 'phrase_key', 'check' => 'col_type' ] ) ) !== 'text' ) {
279 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->phrases . "` MODIFY `phrase_key` TEXT" );
280 }
281 #################################################################
282 // ADD `prefix` and `tags` fields in TOPIC TABLE ///////////////
283 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'tags', 'check' => 'col_exists' ] ) ) {
284 @WPF()->db->query(
285 "ALTER TABLE `" . WPF()->tables->topics . "`
286 ADD `prefix` VARCHAR(100) NOT NULL DEFAULT '',
287 ADD `tags` TEXT,
288 ADD KEY (`prefix`),
289 ADD KEY (`tags`(190))"
290 );
291 }
292 #################################################################
293 //Add last_post indexes for forums and topics tables ////////////
294 if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'last_postid', 'check' => 'key_exists' ] ) ) {
295 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD KEY(`last_postid`)" );
296 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "` ADD KEY `forumid_status_private` ( `forumid`,`status`,`private` ), ADD KEY(`last_post`)" );
297 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD KEY `forumid_status_private` (`forumid`, `status`, `private`)" );
298 }
299 #################################################################
300 //Add cover and cover_height fields to forums table ////////////
301 if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'cover', 'check' => 'col_exists' ] ) ) {
302 @WPF()->db->query(
303 "ALTER TABLE `" . WPF()->tables->forums . "`
304 ADD COLUMN `cover` BIGINT UNSIGNED NOT NULL DEFAULT 0,
305 ADD COLUMN `cover_height` INT(4) UNSIGNED NOT NULL DEFAULT 150"
306 );
307 }
308
309 #################################################################
310 //rename column from cat_layout to layout in forums table ////////////
311 if( wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'cat_layout', 'check' => 'col_exists' ] ) ) {
312 @WPF()->db->query(
313 "ALTER TABLE `" . WPF()->tables->forums . "`
314 CHANGE COLUMN `cat_layout` `layout` tinyint(1) UNSIGNED NOT NULL DEFAULT 0"
315 );
316 }
317 #################################################################
318 //Add unique keys in VISITS TABLE ///////////////////////////////
319 if( ! wpforo_db_check( [ 'table' => WPF()->tables->visits, 'col' => 'unique_tracking', 'check' => 'key_exists' ] ) ) {
320 wpforo_add_unique_key( WPF()->tables->visits, 'id', 'unique_tracking', '`userid`,`ip`,`forumid`,`topicid`' );
321 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->visits . "` ADD KEY `time_forumid` (`time`, `forumid`)" );
322 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->visits . "` ADD KEY `time_topicid` (`time`, `topicid`)" );
323 }
324 #################################################################
325 // ADD `color` field in forums TABLE ///////////////////////////
326 if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'color', 'check' => 'col_exists' ] ) ) {
327 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD `color` varchar(7) NOT NULL DEFAULT ''" );
328 @WPF()->db->query( "UPDATE `" . WPF()->tables->forums . "` SET `color` = concat('#',SUBSTRING((lpad(hex(round(rand() * 10000000)),6,0)),-6))" );
329 }
330 #################################################################
331 // ADD `root` fields in POSTS TABLE ///////////////
332 //$post_table_count = @WPF()->db->get_var("SELECT COUNT(*) FROM `".WPF()->tables->posts."`");
333 //if( $post_table_count < 100000 ){
334 if( ! wpforo_db_check( [ 'table' => WPF()->tables->posts, 'col' => 'root', 'check' => 'col_exists' ] ) ) {
335 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->posts . "` ADD `root` BIGINT, ADD KEY(`root`)" );
336 }
337 //}
338 #################################################################
339 // ADD `new` in ACTIVITY TABLE /////////////////////////////////
340 if( ! wpforo_db_check( [ 'table' => WPF()->tables->activity, 'col' => 'new', 'check' => 'col_exists' ] ) ) {
341 @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`)" );
342 }
343
344 #################################################################
345 // Change `last_userid` type in forums TABLE ///////////////////
346 if( strtolower( (string) wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'last_userid', 'check' => 'col_type' ] ) ) !== 'varchar(255)' ) {
347 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` CHANGE `last_userid` `last_userid` VARCHAR(255) NOT NULL" );
348 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD KEY `last_userid` (`last_userid`(191));" );
349 }
350
351 ################################################################
352 // likes and votes Table to new reactions table ////////////////
353 if( wpforo_db_check( [ 'table' => wpforo_fix_table_name( 'votes' ), 'check' => 'table_exists' ] ) ) {
354 $sql = "INSERT IGNORE INTO `" . WPF()->tables->reactions . "` (
355 SELECT NULL, `userid`, `postid`, `post_userid`, `reaction`, IF( `reaction` = 1, 'up', 'down' ), NULL, NULL
356 FROM `" . wpforo_fix_table_name( 'votes' ) . "`
357 )";
358 WPF()->db->query( $sql );
359 $sql = "DROP TABLE `" . wpforo_fix_table_name( 'votes' ) . "`";
360 WPF()->db->query( $sql );
361 }
362
363 if( wpforo_db_check( [ 'table' => wpforo_fix_table_name( 'likes' ), 'check' => 'table_exists' ] ) ) {
364 $sql = "INSERT IGNORE INTO `" . WPF()->tables->reactions . "` (
365 SELECT NULL, `userid`, `postid`, `post_userid`, 1, 'up', NULL, NULL
366 FROM `" . wpforo_fix_table_name( 'likes' ) . "`
367 )";
368 WPF()->db->query( $sql );
369 $sql = "DROP TABLE `" . wpforo_fix_table_name( 'likes' ) . "`";
370 WPF()->db->query( $sql );
371 }
372
373 if( ! wpforo_db_check( [ 'table' => WPF()->tables->reactions, 'col' => 'performance', 'check' => 'key_exists' ] ) ) {
374 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->reactions . "` ADD KEY `performance` (`postid`,`post_userid`,`type`)" );
375 }
376
377 #################################################################
378 // ADD `type` field in forums TABLE for ticket/private forums
379 // Default is 'forum', 'ticket_forum' forces all topics to be private
380 if( ! wpforo_db_check( [ 'table' => WPF()->tables->forums, 'col' => 'type', 'check' => 'col_exists' ] ) ) {
381 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->forums . "` ADD `type` VARCHAR(20) NOT NULL DEFAULT 'forum'" );
382 }
383
384 // ADD indexed, local, cloud, task_tag fields and indexes in TOPICS TABLE ///////////////////////////
385 if( ! wpforo_db_check( [ 'table' => WPF()->tables->topics, 'col' => 'indexed', 'check' => 'col_exists' ] ) ) {
386 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->topics . "`
387 ADD `indexed` VARCHAR(32),
388 ADD `local` TINYINT(1) NOT NULL DEFAULT 0,
389 ADD `cloud` TINYINT(1) NOT NULL DEFAULT 0,
390 ADD `task_tag` TINYINT(1) NOT NULL DEFAULT 0,
391 ADD INDEX `indexed` (`indexed`),
392 ADD INDEX `local` (`local`),
393 ADD INDEX `cloud` (`cloud`),
394 ADD INDEX `task_tag` (`task_tag`)" );
395 }
396
397 ################################################################
398 // profiles table fields fixing ////////////////////////////////
399 if( wpforo_db_check( [ 'table' => WPF()->tables->profiles, 'col' => 'facebook', 'check' => 'col_exists' ] ) ) {
400 $sql = "SELECT `userid`, `facebook`, `twitter`, `skype`, `fields`
401 FROM `" . WPF()->tables->profiles . "`
402 WHERE (`facebook` IS NOT NULL AND `facebook` <> '')
403 OR (`twitter` IS NOT NULL AND `twitter` <> '')
404 OR (`skype` IS NOT NULL AND `skype` <> '')";
405 if( $users = (array) WPF()->db->get_results( $sql, ARRAY_A ) ) {
406 foreach( $users as $user ) {
407 $user['userid'] = wpforo_bigintval( $user['userid'] );
408 $facebook = trim( (string) $user['facebook'] );
409 $twitter = trim( (string) $user['twitter'] );
410 $skype = trim( (string) $user['skype'] );
411 $user['fields'] = array_merge( (array) json_decode( $user['fields'], true ), compact( 'facebook', 'twitter', 'skype' ) );
412 $user['fields'] = wp_json_encode( $user['fields'] );
413 WPF()->db->update(
414 WPF()->tables->profiles,
415 [ 'fields' => $user['fields'] ],
416 [ 'userid' => $user['userid'] ],
417 '%s',
418 '%d'
419 );
420 }
421 }
422
423 @WPF()->db->query(
424 "ALTER TABLE `" . WPF()->tables->profiles . "`
425 DROP COLUMN `username`,
426 DROP COLUMN `last_login`,
427 DROP COLUMN `icq`,
428 DROP COLUMN `aim`,
429 DROP COLUMN `yahoo`,
430 DROP COLUMN `msn`,
431 DROP COLUMN `gtalk`,
432 DROP COLUMN `like`,
433 DROP COLUMN `site`,
434 DROP COLUMN `facebook`,
435 DROP COLUMN `twitter`,
436 DROP COLUMN `skype`,
437 ADD COLUMN `topics` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `posts`,
438 ADD COLUMN `reactions_in` TEXT AFTER `comments`,
439 ADD COLUMN `reactions_out` TEXT AFTER `reactions_in`,
440 ADD COLUMN `points` INT NOT NULL DEFAULT 0 AFTER `reactions_out`,
441 CHANGE COLUMN `secondary_groups` `secondary_groupids` VARCHAR(255) AFTER `groupid`,
442 CHANGE COLUMN `rank` `custom_points` INT NOT NULL DEFAULT 0 AFTER `points`,
443 MODIFY COLUMN `avatar` VARCHAR(255) AFTER `secondary_groupids`,
444 MODIFY COLUMN `online_time` INT UNSIGNED AFTER `custom_points`,
445 MODIFY COLUMN `timezone` VARCHAR(255) NOT NULL DEFAULT '' AFTER `online_time`,
446 MODIFY COLUMN `location` VARCHAR(255) AFTER `timezone`,
447 ADD COLUMN `cover` VARCHAR(255) NOT NULL DEFAULT '' AFTER `avatar`,
448 ADD COLUMN `is_mention_muted` TINYINT(1) NOT NULL DEFAULT 0 AFTER `is_email_confirmed`"
449 );
450 }
451
452 #################################################################
453 // Add content_type column to ai_embeddings table //////////////
454 if( ! wpforo_db_check( [ 'table' => WPF()->tables->ai_embeddings, 'col' => 'content_type', 'check' => 'col_exists' ] ) ) {
455 @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'" );
456 @WPF()->db->query( "ALTER TABLE `" . WPF()->tables->ai_embeddings . "` ADD KEY `idx_content_type` (`content_type`)" );
457 }
458
459 WPF()->db->query( "TRUNCATE TABLE `" . WPF()->tables->visits . "`" );
460 }
461
462 function wpforo_board_repair( $boardid ) {
463 if( ! wpforo_is_admin() ) return;
464 if( ! current_user_can( 'activate_plugins' ) ) return;
465
466 WPF()->change_board( $boardid );
467
468 wpforo_activation();
469
470 foreach( wpforo_get_addons_info() as $key => $addon ) {
471 if( ( $addon['base'] || wpforo_is_module_enabled( $key ) ) && is_callable( wpfval( $addon, 'install_func' ) ) ) {
472 call_user_func( $addon['install_func'] );
473 }
474 }
475 }
476
477 function wpforo_board_uninstall( $boardid ) {
478 if( ! wpforo_is_admin() ) return;
479 if( ! current_user_can( 'activate_plugins' ) ) return;
480
481 WPF()->change_board( $boardid );
482 WPF()->board->delete( $boardid );
483 foreach( WPF()->_tables as $table ) WPF()->db->query( "DROP TABLE IF EXISTS `" . wpforo_fix_table_name( $table ) . "`" );
484
485 WPF()->db->query(
486 "DELETE FROM `" . WPF()->db->options . "`
487 WHERE `option_name` LIKE 'widget_wpforo_widget_%'
488 OR `option_name` REGEXP '^" . wpforo_prefix() . "'"
489 );
490 WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` REGEXP '^" . wpforo_prefix() . "'" );
491
492 wpforo_remove_directory( WPF()->folders['upload']['dir'] );
493 }
494
495 function wpforo_uninstall() {
496 if( ! wpforo_is_admin() ) return;
497 if( ! current_user_can( 'activate_plugins' ) ) return;
498 require_once ABSPATH . 'wp-admin/includes/plugin.php';
499 foreach( WPF()->board->get_boards( [ 'orderby' => '`boardid` DESC' ] ) as $board ) wpforo_board_uninstall( $board['boardid'] );
500 WPF()->db->query(
501 "DELETE FROM `" . WPF()->db->options . "`
502 WHERE `option_name` LIKE 'widget_wpforo%'
503 OR `option_name` REGEXP '^" . WPF()->base_prefix . "'"
504 );
505 WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` REGEXP '^" . WPF()->base_prefix . "'" );
506 foreach( WPF()->_base_tables as $table ) WPF()->db->query( "DROP TABLE IF EXISTS `" . wpforo_fix_table_name( $table ) . "`" );
507 deactivate_plugins( WPFORO_BASENAME );
508 }
509
510 function wpforo_profile_notice() {
511 if( is_multisite() ) {
512 $users = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` LIKE '" . WPF()->blog_prefix . "capabilities'" );
513 } else {
514 $users = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->db->users . "`" );
515 }
516 $profiles = WPF()->db->get_var( "SELECT COUNT(*) FROM `" . WPF()->tables->profiles . "`" );
517 $delta = $users - $profiles;
518 $status = ( $delta > 2 ) ? round( ( ( $profiles * 100 ) / $users ), 1 ) . '% (' . $profiles . ' / ' . $users . ') ' : '100%';
519 if( $status === '100%' ) return null;
520 $btext = ( $profiles == 0 ) ? __( 'Start Profile Synchronization', 'wpforo' ) : __( 'Continue Synchronization', 'wpforo' );
521 $url = wp_nonce_url( admin_url( 'admin.php?page=wpforo-overview&wpfaction=synch_user_profiles' ), 'wpforo_synch_user_profiles' );
522 $class = 'wpforo-mnote notice notice-warning is-dismissible';
523 $note = __( 'This process may take a few seconds or dozens of minutes, please be patient and don\'t close this page.', 'wpforo' );
524 $info = __( 'You can permanently disable this message using this documentation', 'wpforo' );
525 $button = '<a href="' . $url . '" class="button button-primary button-large" style="font-size:14px;">' . $btext . ' &gt;&gt;</a>';
526 $header = __( 'wpForo Forum Installation | ', 'wpforo' );
527 $message = __( 'Forum users\' profile data are not synchronized yet, this step is required! Please click the button below to complete installation.', 'wpforo' );
528 echo '<div class="' . $class . '" style="padding:15px 20px;"><h2 style="margin:0;">' . esc_html(
529 $header
530 ) . $status . ' </h2><p style="font-size:15px;margin:5px 0;">' . $message . '</p><p style="margin:0 0 10px 0;">' . $button . '</p><hr /><p style="margin:0;color:#dd0000;">' . $note . '</p></div>';
531 }
532
533 function wpforo_database_notice() {
534 $url = admin_url( 'admin.php?page=' . wpforo_prefix_slug( 'tools' ) . '&tab=tables' );
535 $class = 'wpforo-dbnote notice notice-error is-dismissible';
536 $button = '<a href="' . $url . '" class="button button-primary button-large" style="font-size:13px;">Go to Database Troubleshooter &gt;&gt;</a>';
537 $header = __( 'wpForo Database Update Problem - Action Required!', 'wpforo' );
538 $message = __( 'Forum database is not updated properly. Please click the button below for further instruction.', 'wpforo' );
539 echo '<div class="' . $class . '" style="padding:15px 20px;"><h2 style="margin:0;">' . esc_html(
540 $header
541 ) . ' </h2><p style="font-size:15px;margin:5px 0;">' . $message . '</p><p style="margin:15px 0 0 0;">' . $button . '</p></div>';
542 }
543
544 function wpforo_cache_information() {
545 $plugin_names = [];
546 $plugin_steps = [];
547 $not_excluded_plugins = WPF()->cache->cache_plugins_status();
548 if( ! empty( $not_excluded_plugins ) ) {
549 foreach( $not_excluded_plugins as $key => $plugin ) {
550 $plugin_names[ $key ] = '&laquo;' . $plugin['name'] . '&raquo;';
551 $plugin_steps[ $key ] = '<h4 style="margin: 0; font-size: 14px;">' . __( 'Exclude forum page(s) from' ) . ' ' . $plugin['name'] . ' ' . __(
552 'plugin',
553 'wpforo'
554 ) . '</h4><ol style="font-size: 14px; color: #6203a2;"><li style="margin-bottom: 4px;">' . implode( '</li><li style="margin-bottom: 4px;">', $plugin['steps'] ) . '</ol></li>';
555 }
556 }
557 $class = 'wpforo-cache-conflict-note notice notice-error is-dismissible';
558 $note = sprintf(
559 __( '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' ),
560 ( is_rtl() ? __( 'left', 'wpforo' ) : __( 'right', 'wpforo' ) )
561 );
562 $info = __( 'Please find more information here: ', 'wpforo' ) . '<a href="https://wpforo.com/community/faq/wpforo-and-cache-plugins/" target="_blank">wpForo and Cache Plugins</a>';
563 $header = 'wpForo ' . __( ' and ', 'wpforo' ) . implode( __( ' and ', 'wpforo' ), $plugin_names ) . ' ' . __( 'conflict', 'wpforo' ) . ' - ' . __( 'Action Required!', 'wpforo' );
564 $message = __(
565 '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.',
566 'wpforo'
567 ) . '<br />' . implode( '<br>', $plugin_steps );
568
569 echo '<div class="' . $class . '" style="padding:15px 20px;"><h2 style="margin:0;">' . esc_html(
570 $header
571 ) . ' </h2><p style="font-size:15px;margin:10px 0;">' . $message . '</p><hr /><p style="margin:0;color:#dd0000; font-size:14px;">' . $note . '</p><p style="margin:0;font-size:12px;">' . $info . '</p></div>';
572 echo "<script>jQuery(document).on('click', '.wpforo-cache-conflict-note .notice-dismiss', function () {jQuery.ajax({ url: ajaxurl, data: { action: 'dismiss_wpforo_cache_conflict_note' } })})</script>";
573 }
574
575 function wpforo_get_shortcode_pageid( $exclude = [] ) {
576 $exclude = array_filter( array_map( 'wpforo_bigintval', (array) $exclude ) );
577 $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "`
578 WHERE `post_content` LIKE '%[wpforo]%'
579 AND `post_status` LIKE 'publish'
580 AND `post_type` IN('" . implode( "','", wpforo_get_blog_content_types() ) . "')";
581 if( $exclude ) $sql .= " AND `ID` NOT IN(" . implode( ',', $exclude ) . ")";
582
583 return WPF()->db->get_var( $sql );
584 }
585
586 function wpforo_create_forum_page() {
587 $pageid = WPF()->board->get_current( 'pageid' );
588 if( ! $pageid || ! WPF()->db->get_var(
589 "SELECT `ID` FROM `" . WPF(
590 )->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(
591 "','",
592 wpforo_get_blog_content_types()
593 ) . "')"
594 ) ) {
595 if( ! $page_id = wpforo_get_shortcode_pageid( get_option( 'page_on_front' ) ) ) {
596 $wpforo_page = [
597 'post_date' => current_time( 'mysql', 1 ),
598 'post_date_gmt' => current_time( 'mysql', 1 ),
599 'post_content' => '[wpforo]',
600 'post_title' => 'Forum',
601 'post_status' => 'publish',
602 'comment_status' => 'close',
603 'ping_status' => 'close',
604 'post_name' => 'community',
605 'post_modified' => current_time( 'mysql', 1 ),
606 'post_modified_gmt' => current_time( 'mysql', 1 ),
607 'post_parent' => 0,
608 'menu_order' => 0,
609 'post_type' => 'page',
610 ];
611 $page_id = wp_insert_post( $wpforo_page );
612 }
613 if( $page_id && ! is_wp_error( $page_id ) ) {
614 wpforo_update_option( 'wpforo_pageid', $page_id );
615 }
616 }
617
618 flush_rewrite_rules( false );
619 nocache_headers();
620 }
621
622 function wpforo_repair_main_shortcode_page() {
623 $pageid = WPF()->board->get_current( 'pageid' );
624 $sql = "SELECT `ID` FROM `" . WPF()->db->posts . "`
625 WHERE `ID` = " . $pageid . "
626 AND `post_content` LIKE '%[wpforo%'
627 AND `post_status` LIKE 'publish'
628 AND `post_type` IN('" . implode( "','", wpforo_get_blog_content_types() ) . "')";
629 if( ! $pageid || ! WPF()->db->get_var( $sql ) ) {
630 wpforo_create_forum_page();
631 } else {
632 flush_rewrite_rules( false );
633 nocache_headers();
634 }
635 }
636
637 function wpforo_import_default_board() {
638 if( ! WPF()->board->get_current( 'boardid' ) && ! WPF()->board->_get_board( 0 ) ) {
639 $blogname = get_option( 'blogname', '' );
640 $_general = wpforo_get_option( 'wpforo_general_options', [
641 'title' => $blogname . ' ' . __( 'Forum', 'wpforo' ),
642 'description' => $blogname . ' ' . __( 'Discussion Board', 'wpforo' ),
643 'lang' => 1,
644 ], false );
645 $board = WPF()->board->get_current();
646 $board['slug'] = basename( trim( (string) wpforo_get_option( 'wpforo_permastruct', 'community', false ), '/' ) );
647 $board['is_standalone'] = wpforo_get_option( 'wpforo_use_home_url', false );
648 $board['excld_urls'] = array_filter( array_map( 'trim', explode( PHP_EOL, (string) wpforo_get_option( 'wpforo_excld_urls', '' ) ) ) );
649 $board['settings'] = [];
650 $board['settings']['title'] = $_general['title'];
651 $board['settings']['desc'] = $_general['description'];
652 if( $boardid = WPF()->board->add( $board ) ) {
653 WPF()->board->edit( [ 'boardid' => 0 ], $boardid );
654 }
655 }
656 }
657
658 function wpforo_import_default_menus() {
659 $boardid = WPF()->board->get_current( 'boardid' );
660 $menu_name = wpforo_phrase( 'wpForo Navigation', false, 'orig' ) . ( $boardid ? ' #' . $boardid : '' );
661 $menu_location = wpforo_prefix_slug( 'menu' );
662 $menu_exists = wp_get_nav_menu_object( $menu_name );
663 if( ! $menu_exists ) {
664 $id = [];
665 $menu_id = wp_create_nav_menu( $menu_name );
666 $id['wpforo-home'] = wp_update_nav_menu_item( $menu_id, 0, [
667 'menu-item-title' => wpforo_phrase( 'Forums', false ),
668 'menu-item-classes' => 'wpforo-home',
669 'menu-item-url' => '/%wpforo-home%/',
670 'menu-item-status' => 'publish',
671 'menu-item-parent-id' => 0,
672 'menu-item-position' => 0,
673 ] );
674
675 $id['wpforo-recent-activity'] = wp_update_nav_menu_item( $menu_id, 0, [
676 'menu-item-title' => wpforo_phrase( 'What\'s New', false ),
677 'menu-item-classes' => 'wpforo-whats-new',
678 'menu-item-url' => '/%wpforo-recent-activity%/',
679 'menu-item-status' => 'publish',
680 'menu-item-parent-id' => 0,
681 'menu-item-position' => 0,
682 ] );
683
684 $id['wpforo-recent'] = wp_update_nav_menu_item( $menu_id, 0, [
685 'menu-item-title' => wpforo_phrase( 'Recent Posts', false ),
686 'menu-item-classes' => 'wpforo-recent',
687 'menu-item-url' => '/%wpforo-recent%/',
688 'menu-item-status' => 'publish',
689 'menu-item-parent-id' => 0,
690 'menu-item-position' => 0,
691 ] );
692
693 $id['wpforo-members'] = wp_update_nav_menu_item( $menu_id, 0, [
694 'menu-item-title' => wpforo_phrase( 'Members', false ),
695 'menu-item-classes' => 'wpforo-members',
696 'menu-item-url' => '/%wpforo-members%/',
697 'menu-item-status' => 'publish',
698 'menu-item-parent-id' => 0,
699 'menu-item-position' => 0,
700 ] );
701
702 $id['wpforo-profile'] = wp_update_nav_menu_item( $menu_id, 0, [
703 'menu-item-title' => wpforo_phrase( 'My Profile', false ),
704 'menu-item-classes' => 'wpforo-profile',
705 'menu-item-url' => '/%wpforo-profile-home%/',
706 'menu-item-status' => 'publish',
707 'menu-item-parent-id' => 0,
708 'menu-item-position' => 0,
709 ] );
710
711 if( isset( $id['wpforo-profile'] ) && $id['wpforo-profile'] ) {
712 $id['wpforo-profile-account'] = wp_update_nav_menu_item( $menu_id, 0, [
713 'menu-item-title' => wpforo_phrase( 'Account', false ),
714 'menu-item-classes' => 'wpforo-profile-account',
715 'menu-item-url' => '/%wpforo-profile-account%/',
716 'menu-item-status' => 'publish',
717 'menu-item-parent-id' => $id['wpforo-profile'],
718 'menu-item-position' => 1,
719 ] );
720 $id['wpforo-profile-activity'] = wp_update_nav_menu_item( $menu_id, 0, [
721 'menu-item-title' => wpforo_phrase( 'Activity', false ),
722 'menu-item-classes' => 'wpforo-profile-activity',
723 'menu-item-url' => '/%wpforo-profile-activity%/',
724 'menu-item-status' => 'publish',
725 'menu-item-parent-id' => $id['wpforo-profile'],
726 'menu-item-position' => 1,
727 ] );
728 $id['wpforo-profile-subscriptions'] = wp_update_nav_menu_item( $menu_id, 0, [
729 'menu-item-title' => wpforo_phrase( 'Subscriptions', false ),
730 'menu-item-classes' => 'wpforo-profile-subscriptions',
731 'menu-item-url' => '/%wpforo-profile-subscriptions%/',
732 'menu-item-status' => 'publish',
733 'menu-item-parent-id' => $id['wpforo-profile'],
734 'menu-item-position' => 2,
735 ] );
736 }
737
738 $id['wpforo-register'] = wp_update_nav_menu_item( $menu_id, 0, [
739 'menu-item-title' => wpforo_phrase( 'Register', false ),
740 'menu-item-classes' => 'wpforo-register',
741 'menu-item-url' => '/%wpforo-register%/',
742 'menu-item-status' => 'publish',
743 'menu-item-parent-id' => 0,
744 'menu-item-position' => 0,
745 ] );
746
747 $id['wpforo-login'] = wp_update_nav_menu_item( $menu_id, 0, [
748 'menu-item-title' => wpforo_phrase( 'Login', false ),
749 'menu-item-classes' => 'wpforo-login',
750 'menu-item-url' => '/%wpforo-login%/',
751 'menu-item-status' => 'publish',
752 'menu-item-parent-id' => 0,
753 'menu-item-position' => 0,
754 ] );
755
756 $id['wpforo-logout'] = wp_update_nav_menu_item( $menu_id, 0, [
757 'menu-item-title' => wpforo_phrase( 'Logout', false ),
758 'menu-item-classes' => 'wpforo-logout',
759 'menu-item-url' => '/%wpforo-logout%/',
760 'menu-item-status' => 'publish',
761 'menu-item-parent-id' => 0,
762 'menu-item-position' => 0,
763 ] );
764
765 if( ! has_nav_menu( $menu_location ) ) {
766 $locations = get_theme_mod( 'nav_menu_locations' );
767 if( empty( $locations ) ) $locations = [];
768 $locations[ $menu_location ] = $menu_id;
769 set_theme_mod( 'nav_menu_locations', $locations );
770 }
771 }
772 }
773
774 function wpforo_import_default_accesses() {
775 $cans_n = [
776 'vf' => 0,
777 'enf' => 0,
778 'ct' => 0,
779 'vt' => 0,
780 'ent' => 0,
781 'et' => 0,
782 'dt' => 0,
783 'cr' => 0,
784 'ocr' => 0,
785 'vr' => 0,
786 'er' => 0,
787 'dr' => 0,
788 'tag' => 0,
789 'eot' => 0,
790 'eor' => 0,
791 'dot' => 0,
792 'dor' => 0,
793 'sb' => 0,
794 'l' => 0,
795 'r' => 0,
796 's' => 0,
797 'au' => 0,
798 'p' => 0,
799 'op' => 0,
800 'vp' => 0,
801 'sv' => 0,
802 'osv' => 0,
803 'v' => 0,
804 'vop' => 0,
805 'vlp' => 0,
806 'a' => 0,
807 'va' => 0,
808 'at' => 0,
809 'oat' => 0,
810 'aot' => 0,
811 'cot' => 0,
812 'mt' => 0,
813 // Poll-related permissions moved to wpForo Polls plugin
814 ];
815 $cans_r = [
816 'vf' => 1,
817 'enf' => 1,
818 'ct' => 0,
819 'vt' => 1,
820 'ent' => 1,
821 'et' => 0,
822 'dt' => 0,
823 'cr' => 0,
824 'ocr' => 0,
825 'vr' => 1,
826 'er' => 0,
827 'dr' => 0,
828 'tag' => 0,
829 'eot' => 0,
830 'eor' => 0,
831 'dot' => 0,
832 'dor' => 0,
833 'sb' => 1,
834 'l' => 0,
835 'r' => 0,
836 's' => 0,
837 'au' => 0,
838 'p' => 0,
839 'op' => 0,
840 'vp' => 0,
841 'sv' => 0,
842 'osv' => 0,
843 'v' => 0,
844 'vop' => 0,
845 'vlp' => 1,
846 'a' => 0,
847 'va' => 1,
848 'at' => 0,
849 'oat' => 0,
850 'aot' => 0,
851 'cot' => 0,
852 'mt' => 0,
853 // Poll-related permissions moved to wpForo Polls plugin
854 ];
855 $cans_s = [
856 'vf' => 1,
857 'enf' => 1,
858 'ct' => 1,
859 'vt' => 1,
860 'ent' => 1,
861 'et' => 0,
862 'dt' => 0,
863 'cr' => 1,
864 'ocr' => 1,
865 'vr' => 1,
866 'er' => 0,
867 'dr' => 0,
868 'tag' => 1,
869 'eot' => 1,
870 'eor' => 1,
871 'dot' => 1,
872 'dor' => 1,
873 'sb' => 1,
874 'l' => 1,
875 'r' => 1,
876 's' => 0,
877 'au' => 0,
878 'p' => 0,
879 'op' => 1,
880 'vp' => 0,
881 'sv' => 0,
882 'osv' => 1,
883 'v' => 1,
884 'vop' => 1,
885 'vlp' => 1,
886 'a' => 1,
887 'va' => 1,
888 'at' => 0,
889 'oat' => 1,
890 'aot' => 1,
891 'cot' => 0,
892 'mt' => 0,
893 // Poll-related permissions moved to wpForo Polls plugin
894 ];
895 $cans_m = [
896 'vf' => 1,
897 'enf' => 1,
898 'ct' => 1,
899 'vt' => 1,
900 'ent' => 1,
901 'et' => 1,
902 'dt' => 1,
903 'cr' => 1,
904 'ocr' => 1,
905 'vr' => 1,
906 'er' => 1,
907 'dr' => 1,
908 'tag' => 1,
909 'eot' => 1,
910 'eor' => 1,
911 'dot' => 1,
912 'dor' => 1,
913 'sb' => 1,
914 'l' => 1,
915 'r' => 1,
916 's' => 1,
917 'au' => 1,
918 'p' => 1,
919 'op' => 1,
920 'vp' => 1,
921 'sv' => 1,
922 'osv' => 1,
923 'v' => 1,
924 'vop' => 1,
925 'vlp' => 1,
926 'a' => 1,
927 'va' => 1,
928 'at' => 1,
929 'oat' => 1,
930 'aot' => 1,
931 'cot' => 1,
932 'mt' => 1,
933 // Poll-related permissions moved to wpForo Polls plugin
934 ];
935 $cans_a = [
936 'vf' => 1,
937 'enf' => 1,
938 'ct' => 1,
939 'vt' => 1,
940 'ent' => 1,
941 'et' => 1,
942 'dt' => 1,
943 'cr' => 1,
944 'ocr' => 1,
945 'vr' => 1,
946 'er' => 1,
947 'dr' => 1,
948 'tag' => 1,
949 'eot' => 1,
950 'eor' => 1,
951 'dot' => 1,
952 'dor' => 1,
953 'sb' => 1,
954 'l' => 1,
955 'r' => 1,
956 's' => 1,
957 'au' => 1,
958 'p' => 1,
959 'op' => 1,
960 'vp' => 1,
961 'sv' => 1,
962 'osv' => 1,
963 'v' => 1,
964 'vop' => 1,
965 'vlp' => 1,
966 'a' => 1,
967 'va' => 1,
968 'at' => 1,
969 'oat' => 1,
970 'aot' => 1,
971 'cot' => 1,
972 'mt' => 1,
973 // Poll-related permissions moved to wpForo Polls plugin
974 ];
975
976 //Add new Accesses in this array to add those in custom Accesses created by forum admin
977 $cans_default = [
978 'sb' => 1,
979 'au' => 1,
980 'p' => 0,
981 'op' => 1,
982 'vp' => 0,
983 // Poll-related permissions moved to wpForo Polls plugin
984 'aot' => 1,
985 'tag' => 1,
986 'ocr' => 0,
987 'enf' => 1,
988 'ent' => 1,
989 'vop' => 1,
990 'vlp' => 1,
991 ];
992
993 $sql = "SELECT * FROM `" . WPF()->tables->accesses . "`";
994 $accesses = WPF()->db->get_results( $sql, ARRAY_A );
995 if( empty( $accesses ) ) {
996 $cans_n = serialize( $cans_n );
997 $cans_r = serialize( $cans_r );
998 $cans_s = serialize( $cans_s );
999 $cans_m = serialize( $cans_m );
1000 $cans_a = serialize( $cans_a );
1001
1002 $sql = "INSERT IGNORE INTO `" . WPF()->tables->accesses . "`
1003 (`access`, `title`, cans) VALUES
1004 ('no_access', 'No access', '" . $cans_n . "'),
1005 ('read_only', 'Read only access', '" . $cans_r . "'),
1006 ('standard', 'Standard access', '" . $cans_s . "'),
1007 ('moderator', 'Moderator access', '" . $cans_m . "'),
1008 ('full', 'Full access', '" . $cans_a . "')";
1009
1010 WPF()->db->query( $sql );
1011 } else {
1012 foreach( $accesses as $access ) {
1013 $current = unserialize( $access['cans'] );
1014 if( strtolower( (string) $access['access'] ) === 'no_access' ) {
1015 $default = $cans_n;
1016 } elseif( strtolower( (string) $access['access'] ) === 'read_only' ) {
1017 $default = $cans_r;
1018 } elseif( strtolower( (string) $access['access'] ) === 'standard' ) {
1019 $default = $cans_s;
1020 } elseif( strtolower( (string) $access['access'] ) === 'moderator' ) {
1021 $default = $cans_m;
1022 } elseif( strtolower( (string) $access['access'] ) === 'full' ) {
1023 $default = $cans_a;
1024 } else {
1025 $default = $cans_default;
1026 }
1027 if( ! empty( $default ) ) {
1028 $data_update = array_merge( $default, $current );
1029 if( ! empty( $data_update ) ) {
1030 $data_update = serialize( $data_update );
1031 WPF()->db->query( "UPDATE `" . WPF()->tables->accesses . "` SET `cans` = '" . WPF()->db->_real_escape( $data_update ) . "' WHERE `accessid` = " . intval( $access['accessid'] ) );
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 function wpforo_import_default_usergroups() {
1039 $cans_admin = [
1040 'mf' => 1,
1041 'mai' => 1,
1042 'ms' => 1,
1043 'mt' => 1,
1044 'mp' => 1,
1045 'mth' => 1,
1046 'vm' => 1,
1047 'aum' => 1,
1048 'em' => 1,
1049 'vmg' => 1,
1050 'aup' => 1,
1051 'vmem' => 1,
1052 'view_stat' => 1,
1053 'vprf' => 1,
1054 'vpra' => 1,
1055 'vprs' => 1,
1056 'bm' => 1,
1057 'dm' => 1,
1058 'upc' => 1,
1059 'upa' => 1,
1060 'ups' => 1,
1061 'va' => 1,
1062 'vmu' => 1,
1063 'vmm' => 1,
1064 'vmt' => 1,
1065 'vmct' => 1,
1066 'vmr' => 1,
1067 'vmw' => 1,
1068 'vmsn' => 1,
1069 'vmrd' => 1,
1070 'vml' => 1,
1071 'vmo' => 1,
1072 'vms' => 1,
1073 'vmam' => 1,
1074 'vwpm' => 1,
1075 'caa' => 1,
1076 'vt_add_topic' => 1,
1077 'ai_search' => 1,
1078 'ai_summary' => 1,
1079 'ai_translation' => 1,
1080 'ai_suggestion' => 1,
1081 ];
1082 $cans_moder = [
1083 'mf' => 0,
1084 'ms' => 0,
1085 'mai' => 0,
1086 'mt' => 0,
1087 'mp' => 0,
1088 'mth' => 0,
1089 'vm' => 0,
1090 'aum' => 1,
1091 'em' => 0,
1092 'vmg' => 0,
1093 'aup' => 1,
1094 'vmem' => 1,
1095 'view_stat' => 1,
1096 'vprf' => 1,
1097 'vpra' => 1,
1098 'vprs' => 1,
1099 'bm' => 1,
1100 'dm' => 1,
1101 'upc' => 1,
1102 'upa' => 1,
1103 'ups' => 1,
1104 'va' => 1,
1105 'vmu' => 0,
1106 'vmm' => 1,
1107 'vmt' => 1,
1108 'vmct' => 1,
1109 'vmr' => 1,
1110 'vmw' => 1,
1111 'vmsn' => 1,
1112 'vmrd' => 1,
1113 'vml' => 1,
1114 'vmo' => 1,
1115 'vms' => 1,
1116 'vmam' => 1,
1117 'vwpm' => 1,
1118 'caa' => 1,
1119 'vt_add_topic' => 1,
1120 'ai_search' => 1,
1121 'ai_summary' => 1,
1122 'ai_translation' => 1,
1123 'ai_suggestion' => 1,
1124 ];
1125 $cans_reg = [
1126 'mf' => 0,
1127 'ms' => 0,
1128 'mai' => 0,
1129 'mt' => 0,
1130 'mp' => 0,
1131 'mth' => 0,
1132 'vm' => 0,
1133 'aum' => 0,
1134 'em' => 0,
1135 'vmg' => 0,
1136 'aup' => 1,
1137 'vmem' => 1,
1138 'view_stat' => 1,
1139 'vprf' => 1,
1140 'vpra' => 1,
1141 'vprs' => 0,
1142 'bm' => 0,
1143 'dm' => 0,
1144 'upc' => 1,
1145 'upa' => 1,
1146 'ups' => 1,
1147 'va' => 1,
1148 'vmu' => 0,
1149 'vmm' => 0,
1150 'vmt' => 1,
1151 'vmct' => 1,
1152 'vmr' => 1,
1153 'vmw' => 1,
1154 'vmsn' => 1,
1155 'vmrd' => 1,
1156 'vml' => 1,
1157 'vmo' => 1,
1158 'vms' => 1,
1159 'vmam' => 1,
1160 'vwpm' => 1,
1161 'caa' => 1,
1162 'vt_add_topic' => 1,
1163 'ai_search' => 1,
1164 'ai_summary' => 1,
1165 'ai_translation' => 1,
1166 'ai_suggestion' => 1,
1167 ];
1168 $cans_guest = [
1169 'mf' => 0,
1170 'ms' => 0,
1171 'mai' => 0,
1172 'mt' => 0,
1173 'mp' => 0,
1174 'mth' => 0,
1175 'vm' => 0,
1176 'aum' => 0,
1177 'em' => 0,
1178 'vmg' => 0,
1179 'aup' => 0,
1180 'vmem' => 1,
1181 'view_stat' => 1,
1182 'vprf' => 1,
1183 'vpra' => 1,
1184 'vprs' => 0,
1185 'bm' => 0,
1186 'dm' => 0,
1187 'upc' => 0,
1188 'upa' => 0,
1189 'ups' => 0,
1190 'va' => 1,
1191 'vmu' => 0,
1192 'vmm' => 0,
1193 'vmt' => 1,
1194 'vmct' => 1,
1195 'vmr' => 1,
1196 'vmw' => 0,
1197 'vmsn' => 1,
1198 'vmrd' => 1,
1199 'vml' => 1,
1200 'vmo' => 1,
1201 'vms' => 1,
1202 'vmam' => 1,
1203 'vwpm' => 0,
1204 'caa' => 1,
1205 'vt_add_topic' => 0,
1206 'ai_search' => 1,
1207 'ai_summary' => 1,
1208 'ai_translation' => 1,
1209 'ai_suggestion' => 0,
1210 ];
1211 $cans_customer = [
1212 'mf' => 0,
1213 'ms' => 0,
1214 'mai' => 0,
1215 'mt' => 0,
1216 'mp' => 0,
1217 'mth' => 0,
1218 'vm' => 0,
1219 'aum' => 0,
1220 'em' => 0,
1221 'vmg' => 0,
1222 'aup' => 0,
1223 'vmem' => 1,
1224 'view_stat' => 1,
1225 'vprf' => 1,
1226 'vpra' => 1,
1227 'vprs' => 0,
1228 'bm' => 0,
1229 'dm' => 0,
1230 'upc' => 1,
1231 'upa' => 1,
1232 'ups' => 1,
1233 'va' => 1,
1234 'vmu' => 0,
1235 'vmm' => 0,
1236 'vmt' => 1,
1237 'vmct' => 1,
1238 'vmr' => 1,
1239 'vmw' => 1,
1240 'vmsn' => 1,
1241 'vmrd' => 1,
1242 'vml' => 1,
1243 'vmo' => 1,
1244 'vms' => 1,
1245 'vmam' => 1,
1246 'vwpm' => 1,
1247 'caa' => 1,
1248 'vt_add_topic' => 1,
1249 'ai_search' => 1,
1250 'ai_summary' => 1,
1251 'ai_translation' => 1,
1252 'ai_suggestion' => 1,
1253 ];
1254
1255 //Add new Cans in this array to add those in custom Usergroup created by forum admin
1256 $cans_defaults = [
1257 'mf' => 0,
1258 'ms' => 0,
1259 'mai' => 0,
1260 'mt' => 0,
1261 'mp' => 0,
1262 'mth' => 0,
1263 'vmem' => 1,
1264 'view_stat' => 1,
1265 'vprf' => 1,
1266 'caa' => 1,
1267 'vt_add_topic' => 1,
1268 'upc' => 1,
1269 'ai_search' => 1,
1270 'ai_summary' => 1,
1271 'ai_translation' => 1,
1272 'ai_suggestion' => 1,
1273 ];
1274
1275 $sql = "SELECT * FROM `" . WPF()->tables->usergroups . "`";
1276 if( ! $usergroups = WPF()->db->get_results( $sql, ARRAY_A ) ) {
1277 WPF()->usergroup->add( 'Admin', $cans_admin, '', 'administrator', 'full', '#FF3333' );
1278 WPF()->usergroup->add( 'Moderator', $cans_moder, '', 'editor', 'moderator', '#0066FF' );
1279 WPF()->usergroup->add( 'Registered', $cans_reg, '', 'subscriber', 'standard', '', 1, 1 );
1280 WPF()->usergroup->add( 'Guest', $cans_guest, '', '', 'read_only', '#222222', 0 );
1281 WPF()->usergroup->add( 'Customer', $cans_customer, '', 'customer', 'standard', '#993366', 1, 1 );
1282 } else {
1283 foreach( $usergroups as $usergroup ) {
1284 $current = unserialize( $usergroup['cans'] );
1285 if( strtolower( (string) $usergroup['name'] ) === 'admin' ) {
1286 $default = $cans_admin;
1287 } elseif( strtolower( (string) $usergroup['name'] ) === 'moderator' ) $default = $cans_moder;
1288 elseif( strtolower( (string) $usergroup['name'] ) === 'registered' ) $default = $cans_reg;
1289 elseif( strtolower( (string) $usergroup['name'] ) === 'guest' ) $default = $cans_guest;
1290 elseif( strtolower( (string) $usergroup['name'] ) === 'customer' ) $default = $cans_customer;
1291 else {
1292 $default = $cans_defaults;
1293 }
1294 if( ! empty( $default ) ) {
1295 $data_update = array_merge( $default, $current );
1296 if( ! empty( $data_update ) ) {
1297 $data_update = serialize( $data_update );
1298 WPF()->db->query(
1299 "UPDATE `" . WPF()->tables->usergroups . "` SET `cans` = '" . WPF()->db->_real_escape( $data_update ) . "' WHERE `groupid` = " . intval( $usergroup['groupid'] )
1300 );
1301 }
1302 }
1303 }
1304 }
1305 }
1306
1307 function wpforo_import_default_forums() {
1308 $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->forums . "`";
1309 $count = WPF()->db->get_var( $sql );
1310 if( ! $count ) {
1311 if( $parentid = WPF()->forum->add(
1312 [ 'title' => __( 'Main Category', 'wpforo' ), 'description' => __( 'This is a simple category / section', 'wpforo' ), 'layout' => 4, 'icon' => 'fas fa-comments' ],
1313 false
1314 ) ) {
1315 WPF()->forum->add(
1316 [ 'title' => __( 'Main Forum', 'wpforo' ), 'description' => __( 'This is a simple parent forum', 'wpforo' ), 'parentid' => $parentid, 'layout' => 4, 'icon' => 'fas fa-comments' ],
1317 false
1318 );
1319 }
1320 }
1321 }
1322
1323 function wpforo_fix_wp_permalink_structure() {
1324 $permalink_structure = get_option( 'permalink_structure' );
1325 if( ! $permalink_structure ) {
1326 global $wp_rewrite;
1327 $wp_rewrite->set_permalink_structure( '/%postname%/' );
1328 }
1329 }
1330
1331 function wpforo_fix_installed_options() {
1332 /**
1333 * in the update process, using old features settings set the right profile setting for the new option
1334 */
1335 if( $features = wpforo_get_option( 'features', [], false ) ) {
1336 if( ! (int) wpfval( $features, 'profile' ) ) {
1337 if( (int) wpfval( $features, 'bp_profile' ) && class_exists( 'BP_Component' ) ) {
1338 $features['profile'] = 3;
1339 } elseif( (int) wpfval( $features, 'um_profile' ) && function_exists( 'UM' ) ) {
1340 $features['profile'] = 4;
1341 } elseif( (int) wpfval( $features, 'comment-author-link' ) ) {
1342 $features['profile'] = 2;
1343 } else {
1344 $features['profile'] = 1;
1345 }
1346 wpforo_update_option( 'features', array_map( 'intval', $features ) );
1347 }
1348 }
1349
1350 #################################################################
1351 // CHECK Addon Notice /////////////////////////////////////////
1352 $lastHash = get_option( 'wpforo_addon_note_dismissed' );
1353 $first = get_option( 'wpforo_addon_note_first' );
1354 if( $lastHash && $first === 'true' ) {
1355 update_option( 'wpforo_addon_note_first', 'false' );
1356 }
1357
1358 #################################################################
1359 // AVOID PLUGIN CONFLICTS ///////////////////////////////////////
1360 /* Autoptimize *************************************************/
1361 $autopt = get_option( 'autoptimize_js_exclude' );
1362 if( $autopt && strpos( (string) $autopt, 'wp-includes/js/tinymce' ) === false ) {
1363 $autopt = $autopt . ', wp-includes/js/tinymce';
1364 update_option( 'autoptimize_js_exclude', $autopt );
1365 if( class_exists( 'autoptimizeCache' ) && is_callable( [ 'autoptimizeCache', 'clearall' ] ) ) {
1366 autoptimizeCache::clearall();
1367 }
1368 }
1369
1370 #################################################################
1371 // Adding #wpforo to custom css /////////////////////////////////
1372 if( $style_options = wpforo_get_option( 'style_options', [], false ) ) {
1373 $custom_css = wpfval( $style_options, 'custom_css' );
1374 if( $custom_css && strpos( (string) $custom_css, '#wpforo #wpforo-wrap' ) === false ) {
1375 $style_options['custom_css'] = str_replace( '#wpforo-wrap', '#wpforo #wpforo-wrap', $style_options['custom_css'] );
1376 wpforo_update_option( 'style_options', $style_options );
1377 }
1378 }
1379 }
1380
1381 function wpforo_update_theme_options() {
1382 if( $current_theme = wpforo_get_option( 'theme_options_' . WPF()->tpl->theme, [], false ) ) {
1383 $theme = WPF()->tpl->find_theme( '2026' );
1384 if( wpfval( $theme, 'layouts' ) ) {
1385 $current_theme['layouts'] = $theme['layouts'];
1386 $theme = wpforo_deep_merge( $theme, $current_theme );
1387 wpforo_update_option( 'theme_options_' . WPF()->tpl->theme, $theme );
1388 }
1389 }
1390 }
1391
1392 function wpforo_clean_up() {
1393 WPF()->db->delete( WPF()->tables->topics, [ 'first_postid' => 0 ], [ '%d' ] );
1394 }
1395
1396 // wpforo database checker fixer tools
1397 function wpforo_update_db() {
1398 $problems = wpforo_database_check();
1399 if( ! empty( $problems ) ) {
1400 $SQL = wpforo_database_fixer( $problems );
1401 if( wpfval( $SQL, 'fields' ) ) {
1402 foreach( $SQL['fields'] as $query ) WPF()->db->query( $query );
1403 }
1404 if( wpfval( $SQL, 'keys' ) ) {
1405 foreach( $SQL['keys'] as $query ) WPF()->db->query( $query );
1406 }
1407 if( wpfval( $SQL, 'tables' ) ) {
1408 foreach( $SQL['tables'] as $query ) {
1409 if( false === WPF()->db->query( $query ) ) {
1410 WPF()->db->query( preg_replace( '#\)[\r\n\t\s]*ENGINE.*$#isu', ')', $query ) );
1411 }
1412 }
1413 }
1414 if( wpfval( $SQL, 'data' ) ) {
1415 foreach( $SQL['data'] as $query ) {
1416 WPF()->db->query( $query );
1417 }
1418 }
1419 }
1420 wpforo_update_option( 'version_db', WPFORO_VERSION );
1421 }
1422
1423 /**
1424 * @param array $args
1425 *
1426 * @return bool|string|null
1427 */
1428 function wpforo_db_check( $args = [] ) {
1429 $key = [ 'wpforo_db_check', $args ];
1430 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
1431
1432 global $wpdb;
1433
1434 $col = esc_sql( trim( (string) wpfval( $args, 'col' ) ) );
1435 $table = esc_sql( trim( (string) wpfval( $args, 'table' ) ) );
1436
1437 $result = null;
1438 switch( trim( (string) wpfval( $args, 'check' ) ) ) {
1439 case 'table_exists':
1440 $result = (bool) $wpdb->get_var( "SHOW TABLES LIKE '$table'" );
1441 break;
1442 case 'col_exists':
1443 $result = (bool) $wpdb->get_var( "SHOW COLUMNS FROM `$table` LIKE '$col'" );
1444 break;
1445 case 'key_exists':
1446 $result = (bool) $wpdb->get_var( "SHOW KEYS FROM `$table` WHERE `Key_name` = '$col'" );
1447 break;
1448 case 'default_value':
1449 $c = (array) $wpdb->get_row( "SHOW COLUMNS FROM `$table` LIKE '$col'", ARRAY_A );
1450 $result = wpfval( $c, 'Default' );
1451 break;
1452 case 'col_type':
1453 $c = (array) $wpdb->get_row( "SHOW COLUMNS FROM `$table` LIKE '$col'", ARRAY_A );
1454 $result = wpfval( $c, 'Type' );
1455 break;
1456 }
1457
1458 WPF()->ram_cache->set( $key, $result );
1459
1460 return $result;
1461 }
1462
1463 function wpforo_database_check() {
1464 $_tables = [];
1465 $_table_diffs = [];
1466 require_once( WPFORO_DIR . '/includes/install-sql.php' );
1467 $wpforo_sql = wpforo_get_install_sqls();
1468 if( ! empty( $wpforo_sql ) ) {
1469 foreach( $wpforo_sql as $sql ) {
1470 if( preg_match( '|EXISTS \`([^\(]+)\`\s*\((.+)(PRIMARY.+)\)\s*ENGINE|is', $sql, $table ) ) {
1471 if( wpfval( $table, 1 ) ) {
1472 if( wpfval( $table, 2 ) ) {
1473 if( preg_match_all( '|\`([^\`]+)\`|is', $table[2], $fields, PREG_SET_ORDER ) ) {
1474 foreach( $fields as $field ) {
1475 if( wpfval( $field, 1 ) ) $_tables[ $table[1] ]['fields'][] = $field[1];
1476 }
1477 }
1478 }
1479 if( wpfval( $table, 3 ) ) {
1480 if( preg_match( '|PRIMARY KEY \(\`([^\`]+)\`\)|is', $table[3], $primary_key ) ) {
1481 $_tables[ $table[1] ]['keys'][] = $primary_key[1];
1482 }
1483 if( preg_match_all( '|KEY \`([^\`]+)\`|is', $table[3], $keys, PREG_SET_ORDER ) ) {
1484 foreach( $keys as $key ) {
1485 if( wpfval( $key, 1 ) ) $_tables[ $table[1] ]['keys'][] = $key[1];
1486 }
1487 }
1488 }
1489 }
1490 }
1491 }
1492 if( ! empty( $_tables ) ) {
1493 foreach( $_tables as $_name => $_structure ) {
1494 $_table_fields = [];
1495 $_table_keys = [];
1496 $_table_exists = WPF()->db->get_var( "SHOW TABLES LIKE '" . esc_sql( $_name ) . "'" );
1497 if( $_table_exists ) {
1498 //Problems - Missing Field
1499 if( wpfval( $_structure, 'fields' ) ) {
1500 $_fields = WPF()->db->get_results( "SHOW FULL COLUMNS FROM " . esc_sql( $_name ), ARRAY_A );
1501 foreach( $_fields as $_field ) $_table_fields[] = $_field['Field'];
1502 $_count_orig = count( $_structure['fields'] );
1503 $_count_curr = count( $_table_fields );
1504 if( $_count_curr < $_count_orig ) {
1505 $diff = array_diff( $_structure['fields'], $_table_fields );
1506 if( ! empty( $diff ) ) $_table_diffs[ $_name ]['fields'][ $_name ] = $diff;
1507 }
1508 }
1509 //Problems - Missing Key
1510 if( wpfval( $_structure, 'keys' ) ) {
1511 $_keys = WPF()->db->get_results( "SHOW KEYS FROM " . esc_sql( $_name ), ARRAY_A );
1512 foreach( $_keys as $_key ) {
1513 if( strpos( (string) $_key['Key_name'], 'PRIMARY' ) !== false ) {
1514 $_table_keys[] = $_key['Column_name'];
1515 } else {
1516 $_table_keys[] = $_key['Key_name'];
1517 }
1518 }
1519 $_table_keys = array_unique( $_table_keys );
1520 $_table_keys = array_values( $_table_keys );
1521 $_count_orig = count( $_structure['keys'] );
1522 $_count_curr = count( $_table_keys );
1523 if( $_count_curr < $_count_orig ) {
1524 $diff_keys = array_diff( $_structure['keys'], $_table_keys );
1525 if( ! empty( $diff_keys ) ) $_table_diffs[ $_name ]['keys'][ $_name ] = $diff_keys;
1526 }
1527 }
1528 } else {
1529 //Problems - Missing Table
1530 $_table_diffs[ $_name ]['exists'] = 'no';
1531 }
1532 }
1533 }
1534 }
1535
1536 $first_board = WPF()->db->get_row( "SELECT `boardid` FROM `" . WPF()->tables->boards . "` WHERE `boardid` = 0", ARRAY_A );
1537 if( empty( $first_board ) ) {
1538 $_table_diffs['data']['default_board'] = 'no';
1539 }
1540
1541 return $_table_diffs;
1542 }
1543
1544 function wpforo_database_parse() {
1545 $_tables = [];
1546 $_table_diffs = [];
1547 require_once( WPFORO_DIR . '/includes/install-sql.php' );
1548 $wpforo_sql = wpforo_get_install_sqls();
1549 if( ! empty( $wpforo_sql ) ) {
1550 foreach( $wpforo_sql as $sql ) {
1551 if( preg_match( '|EXISTS \`([^\(]+)\`\s*\((.+)(PRIMARY.+)\)\s*ENGINE|is', $sql, $table ) ) {
1552 if( wpfval( $table, 1 ) ) {
1553 if( wpfval( $table, 2 ) ) {
1554 $_tables[ $table[1] ]['fields'] = array_map( 'trim', explode( ',', $table[2] ) );
1555 }
1556 if( wpfval( $table, 3 ) ) {
1557 $_tables[ $table[1] ]['keys'] = array_map( 'trim', explode( PHP_EOL, $table[3] ) );
1558 }
1559 }
1560 }
1561 }
1562 }
1563
1564 return $_tables;
1565 }
1566
1567 function wpforo_database_fixer( $problems ) {
1568 $SQL = [];
1569 if( ! empty( $problems ) ) {
1570 require_once( WPFORO_DIR . '/includes/install-sql.php' );
1571 $table_structure = wpforo_database_parse();
1572 if( ! empty( $table_structure ) ) {
1573 foreach( $problems as $table_name => $problem ) {
1574 if( wpfval( $problem, 'fields' ) ) {
1575 foreach( $problem['fields'] as $problem_fields ) {
1576 if( ! empty( $problem_fields ) ) {
1577 foreach( $problem_fields as $problem_field ) {
1578 if( wpfval( $table_structure, $table_name, 'fields' ) ) {
1579 foreach( $table_structure[ $table_name ]['fields'] as $field_sql ) {
1580 if( strpos( (string) $field_sql, '`' . $problem_field . '`' ) !== false ) {
1581 $SQL['fields'][] = 'ALTER TABLE `' . $table_name . '` ADD ' . $field_sql . ';';
1582 }
1583 }
1584 }
1585 }
1586 }
1587 }
1588 }
1589 if( wpfval( $problem, 'keys' ) ) {
1590 foreach( $problem['keys'] as $problem_keys ) {
1591 if( ! empty( $problem_keys ) ) {
1592 foreach( $problem_keys as $problem_key ) {
1593 if( wpfval( $table_structure, $table_name, 'keys' ) ) {
1594 foreach( $table_structure[ $table_name ]['keys'] as $key_sql ) {
1595 if( preg_match( '|KEY \`' . $problem_key . '\`|is', (string) $key_sql ) ) {
1596 $SQL['keys'][] = 'ALTER TABLE `' . $table_name . '` ADD ' . trim( (string) $key_sql, ',' ) . ';';
1597 }
1598 }
1599 }
1600 }
1601 }
1602 }
1603 }
1604 if( wpfval( $problem, 'exists' ) ) {
1605 $wpforo_sql = wpforo_get_install_sqls();
1606 if( wpfval( $wpforo_sql, $table_name ) ) {
1607 $SQL['tables'][] = preg_replace( '|\t+|', ' ', $wpforo_sql[ $table_name ] );
1608 }
1609 }
1610 }
1611 }
1612 if( wpfval( $problems, 'data' ) ) {
1613 if( wpfval( $problems, 'data', 'default_board' ) ) {
1614 // Missing default board (ID:0)
1615 $board = [];
1616 $blogname = get_option( 'blogname', '' );
1617 $_general = wpforo_get_option( 'wpforo_general_options', [
1618 'title' => $blogname . ' ' . __( 'Forum', 'wpforo' ),
1619 'description' => $blogname . ' ' . __( 'Discussion Board', 'wpforo' ),
1620 ], false );
1621 $board['title'] = ( wpfval( $_general, 'title' ) ) ?: 'Forum';
1622 $board['settings']['title'] = ( wpfval( $_general, 'title' ) ) ?: 'Forum';
1623 $board['settings']['desc'] = ( wpfval( $_general, 'description' ) ) ?: 'Discussion Board';
1624 $settings = wp_json_encode( $board['settings'] );
1625 WPF()->db->query( "UPDATE `" . WPF()->tables->boards . "` SET `slug` = CONCAT('community-', `boardid`) WHERE `slug` = 'community' AND `boardid` != 0" );
1626 $slug = basename( trim( (string) wpforo_get_option( 'wpforo_permastruct', 'community', false ), '/' ) );
1627 $board['slug'] = ( $slug ) ?: 'community';
1628 $board['locale'] = wpforo_get_site_default_locale();
1629 $board['pageid'] = wpforo_get_option( 'wpforo_pageid', 0 );
1630 $all_modules = array_map( '__return_true', wpforo_get_modules_info() );
1631 $all_addons = array_map( '__return_true', wpforo_get_addons_info() );
1632 $modules = array_merge( $all_modules, $all_addons );
1633 $board['modules'] = wp_json_encode( array_map( function( $a ) { return (bool) intval( $a ); }, $modules ) );
1634 $SQL['data']['pre_board'] = "SET sql_mode='NO_AUTO_VALUE_ON_ZERO';";
1635 $SQL['data']['default_board'] = "INSERT INTO `" . WPF()->tables->boards . "` (`boardid`, `title`, `slug`, `pageid`, `modules`, `locale`, `is_standalone`, `excld_urls`, `status`, `settings`)
1636 VALUES(0, '" . esc_sql( $board['title'] ) . "', '" . esc_sql( $board['slug'] ) . "', " . intval( $board['pageid'] ) . ",
1637 '" . esc_sql( $board['modules'] ) . "',
1638 '" . esc_sql( $board['locale'] ) . "', 0, '[]', 1, '" . esc_sql( $settings ) . "');";
1639 }
1640 //Other data checking here...
1641 }
1642 }
1643
1644 return $SQL;
1645 }
1646
1647 function wpforo_add_unique_key( $table, $primary_key, $unique_key_name = '', $unique_fields = '' ) {
1648
1649 $table = esc_sql( trim( (string) $table ) );
1650 $primary_key = esc_sql( trim( (string) $primary_key ) );
1651 $unique_fields = esc_sql( trim( (string) $unique_fields, ',' ) );
1652 $unique_fields_clean = preg_replace( '|\([^()]+\)|', '', (string) $unique_fields );
1653 $remove_rows = '';
1654 $sql = "SELECT GROUP_CONCAT(`$primary_key`) duplicated_row_ids,
1655 COUNT(*) duplication_count FROM
1656 `$table` GROUP BY $unique_fields_clean HAVING duplication_count > 1";
1657
1658 $rows = WPF()->db->get_results( $sql, ARRAY_A );
1659 if( ! empty( $rows ) ) {
1660 foreach( $rows as $row ) {
1661 $ids = explode( ',', $row['duplicated_row_ids'] );
1662 $ids = array_reverse( $ids );
1663 $ids = array_slice( $ids, 1 );
1664 $remove_rows .= trim( implode( ',', $ids ), ',' ) . ',';
1665 }
1666 $remove_rows = esc_sql( trim( $remove_rows, ',' ) );
1667 if( $remove_rows ) {
1668 WPF()->db->query( "DELETE FROM `$table` WHERE `$primary_key` IN($remove_rows)" );
1669 }
1670 }
1671 $sql = "ALTER TABLE `$table` ADD UNIQUE KEY `$unique_key_name`( $unique_fields )";
1672 WPF()->db->query( $sql );
1673 }
1674
1675 /**
1676 * Add What’s New menu item on plugin update (2.x to 3.x migration only)
1677 * Adds "What’s New" right after the first [Forums] menu item.
1678 * This only runs once during the 2.x to 3.x update.
1679 *
1680 * @since 3.0.0
1681 */
1682 function wpforo_migrate_recent_activity_menu() {
1683 // Only run this migration once (2.x to 3.x update)
1684 if( get_option( 'wpforo_recent_activity_menu_migrated' ) ) {
1685 return;
1686 }
1687
1688 // Get all boards to migrate menus for each
1689 $boards = [];
1690 if( isset( WPF()->board ) && method_exists( WPF()->board, 'get_boards' ) ) {
1691 $boards = WPF()->board->get_boards();
1692 }
1693
1694 // If no boards found or multi-board not initialized, just migrate default board
1695 if( empty( $boards ) ) {
1696 $boards = [ [ 'boardid' => 0 ] ];
1697 }
1698
1699 foreach( $boards as $board ) {
1700 $boardid = intval( wpfval( $board, 'boardid' ) );
1701 $menu_name = wpforo_phrase( 'wpForo Navigation', false, 'orig' ) . ( $boardid ? ' #' . $boardid : '' );
1702 $menu_obj = wp_get_nav_menu_object( $menu_name );
1703
1704 if( ! $menu_obj ) {
1705 continue; // Skip boards without a menu (fresh install will create menu with What's New)
1706 }
1707
1708 $menu_id = $menu_obj->term_id;
1709 $menu_items = wp_get_nav_menu_items( $menu_id );
1710
1711 if( empty( $menu_items ) ) {
1712 continue;
1713 }
1714
1715 // Check if "What's New" already exists (check both old and new CSS class names)
1716 $has_whats_new = false;
1717 foreach( $menu_items as $item ) {
1718 if( is_array( $item->classes ) && ( in_array( 'wpforo-whats-new', $item->classes ) || in_array( 'wpforo-recent-activity', $item->classes ) ) ) {
1719 $has_whats_new = true;
1720 break;
1721 }
1722 }
1723
1724 if( $has_whats_new ) {
1725 continue; // What's New already exists for this board, skip
1726 }
1727
1728 // Find the first [Forums] menu item (wpforo-home) to get its position
1729 $forums_position = 1;
1730 foreach( $menu_items as $item ) {
1731 if( is_array( $item->classes ) && in_array( 'wpforo-home', $item->classes ) ) {
1732 $forums_position = $item->menu_order;
1733 break;
1734 }
1735 }
1736
1737 // Shift all menu items after [Forums] by 1 position
1738 foreach( $menu_items as $item ) {
1739 if( $item->menu_order > $forums_position ) {
1740 wp_update_nav_menu_item( $menu_id, $item->db_id, [
1741 'menu-item-title' => $item->title,
1742 'menu-item-classes' => implode( ' ', (array) $item->classes ),
1743 'menu-item-url' => $item->url,
1744 'menu-item-status' => 'publish',
1745 'menu-item-parent-id' => $item->menu_item_parent,
1746 'menu-item-position' => $item->menu_order + 1,
1747 ] );
1748 }
1749 }
1750
1751 // Add "What's New" right after [Forums]
1752 wp_update_nav_menu_item( $menu_id, 0, [
1753 'menu-item-title' => wpforo_phrase( 'What\'s New', false ),
1754 'menu-item-classes' => 'wpforo-whats-new',
1755 'menu-item-url' => '/%wpforo-recent-activity%/',
1756 'menu-item-status' => 'publish',
1757 'menu-item-parent-id' => 0,
1758 'menu-item-position' => $forums_position + 1,
1759 ] );
1760 }
1761
1762 // Mark migration as complete - won't run again on future updates
1763 update_option( 'wpforo_recent_activity_menu_migrated', 1 );
1764 }
1765
1766 /**
1767 * Migrate theme styles from 2022 to 2026 theme on plugin update (2.x to 3.x migration)
1768 * Preserves user's custom colors and CSS when the default theme changes from 2022 to 2026.
1769 * Supports multi-board installations.
1770 * This only runs once during the 2.x to 3.x update.
1771 *
1772 * @since 3.0.0
1773 */
1774 function wpforo_migrate_theme_styles() {
1775 // Only run this migration once
1776 if( get_option( 'wpforo_theme_styles_migrated' ) ) {
1777 return;
1778 }
1779
1780 // Get all boards to migrate styles for each
1781 $boards = [];
1782 if( isset( WPF()->board ) && method_exists( WPF()->board, 'get_boards' ) ) {
1783 $boards = WPF()->board->get_boards();
1784 }
1785
1786 // If no boards found or multi-board not initialized, just migrate default board
1787 if( empty( $boards ) ) {
1788 $boards = [ [ 'boardid' => 0 ] ];
1789 }
1790
1791 foreach( $boards as $board ) {
1792 $boardid = intval( wpfval( $board, 'boardid' ) );
1793 $prefix = WPF()->generate_prefix( $boardid );
1794
1795 // Get the old 2022 theme styles for this board
1796 $styles_2022 = get_option( $prefix . 'styles_2022', [] );
1797
1798 // If user had 2022 styles, copy them to 2026 theme
1799 if( ! empty( $styles_2022 ) && ! get_option( $prefix . 'styles_2026', [] ) ) {
1800 update_option( $prefix . 'styles_2026', $styles_2022 );
1801 }
1802 }
1803
1804 // Mark migration as complete
1805 update_option( 'wpforo_theme_styles_migrated', 1 );
1806 }
1807
1808 /**
1809 * Migrate AI quality settings to optimized defaults for better performance.
1810 * Changes quality tiers from 'advanced' to 'fast'/'balanced' to reduce API timeouts.
1811 * This only runs once during plugin update.
1812 *
1813 * New defaults (3.0.3+):
1814 * - search_quality: 'fast' (was 'balanced')
1815 * - search_enhance_quality: 'balanced' (was 'advanced')
1816 * - translation_quality: 'fast' (was 'advanced')
1817 * - topic_summary_quality: 'balanced' (was 'advanced')
1818 * - moderation_spam_quality: 'balanced' (was 'advanced')
1819 * - chatbot_quality: 'balanced' (was 'fast')
1820 *
1821 * @since 3.0.3
1822 */
1823 function wpforo_migrate_ai_quality_defaults() {
1824 // Only run this migration once
1825 if( get_option( 'wpforo_ai_quality_defaults_migrated' ) ) {
1826 return;
1827 }
1828
1829 // Define quality settings to update (old_value => new_value)
1830 $quality_changes = [
1831 'search_quality' => 'fast',
1832 'search_enhance_quality' => 'balanced',
1833 'translation_quality' => 'fast',
1834 'topic_summary_quality' => 'balanced',
1835 'moderation_spam_quality' => 'balanced',
1836 'chatbot_quality' => 'balanced',
1837 ];
1838
1839 // Get all board IDs (including default board 0)
1840 $boardids = [ 0 ];
1841 if( class_exists( 'wpforo\\classes\\Board' ) && method_exists( WPF()->board, 'get_active_boardids' ) ) {
1842 $boardids = array_unique( array_merge( $boardids, (array) WPF()->board->get_active_boardids() ) );
1843 }
1844
1845 foreach( $boardids as $boardid ) {
1846 // Build option name: wpforo_ai (board 0) or wpforo_{boardid}_ai (other boards)
1847 $option_name = 'wpforo_' . ( $boardid ? $boardid . '_' : '' ) . 'ai';
1848 $ai_settings = get_option( $option_name, [] );
1849
1850 // Skip if no settings saved (new install, will use new defaults)
1851 if( empty( $ai_settings ) ) {
1852 continue;
1853 }
1854
1855 // Update only the quality settings
1856 $updated = false;
1857 foreach( $quality_changes as $key => $new_value ) {
1858 // Only update if the key exists (user has this setting saved)
1859 if( isset( $ai_settings[ $key ] ) ) {
1860 $ai_settings[ $key ] = $new_value;
1861 $updated = true;
1862 }
1863 }
1864
1865 // Save updated settings
1866 if( $updated ) {
1867 update_option( $option_name, $ai_settings );
1868 }
1869 }
1870
1871 // Mark migration as complete
1872 update_option( 'wpforo_ai_quality_defaults_migrated', 1 );
1873 }
1874