PluginProbe
wpForo Forum / 3.1.5
wpForo Forum v3.1.5
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 / wpforo.php

wpforo.php in wpForo Forum 3.1.5, at wpforo.php

1,650 lines 62.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * Plugin Name: wpForo
4 * Plugin URI: https://wpforo.com
5 * Description: WordPress Forum plugin. wpForo is the only AI powered forum solution for your community. Modern design and 5 forum layouts.
6 * Author: gVectors Team
7 * Author URI: https://gvectors.com/
8 * Version: 3.1.5
9 * Requires at least: 5.2
10 * Requires PHP: 7.1
11 * Text Domain: wpforo
12 * Domain Path: /languages
13 */
14
15 namespace wpforo;
16
17 define( 'WPFORO_VERSION', '3.1.5' );
18
19 //Exit if accessed directly
20 if( ! defined( 'ABSPATH' ) ) exit;
21
22 define( 'WPFORO_DIR', rtrim( plugin_dir_path( __FILE__ ), '/' ) );
23 define( 'WPFORO_URL', rtrim( plugins_url( '', __FILE__ ), '/' ) );
24 define( 'WPFORO_BASENAME', plugin_basename( __FILE__ ) ); //wpforo/wpforo.php
25
26 require_once WPFORO_DIR . "/autoload.php";
27
28 use stdClass;
29 use wpdb;
30 use wpforo\classes\Actions;
31 use wpforo\classes\Activity;
32 use wpforo\classes\AIChatbot;
33 use wpforo\classes\AIClient;
34 use wpforo\classes\AIContentModeration;
35 use wpforo\classes\AILogs;
36 use wpforo\classes\AIWordPressIndexer;
37 use wpforo\classes\API;
38 use wpforo\classes\Blocks;
39 use wpforo\classes\Boards;
40 use wpforo\classes\Cache;
41 use wpforo\classes\Feed;
42 use wpforo\classes\Forms;
43 use wpforo\classes\Forums;
44 use wpforo\classes\Logs;
45 use wpforo\classes\Members;
46 use wpforo\classes\Moderation;
47 use wpforo\classes\Notices;
48 use wpforo\classes\Permissions;
49 use wpforo\classes\Phrases;
50 use wpforo\classes\PostMeta;
51 use wpforo\classes\Posts;
52 use wpforo\classes\RamCache;
53 use wpforo\classes\SEO;
54 use wpforo\classes\Settings;
55 use wpforo\classes\TaskManager;
56 use wpforo\classes\EmailQueue;
57 use wpforo\classes\Template;
58 use wpforo\classes\Topics;
59 use wpforo\classes\VectorStorageManager;
60 use wpforo\classes\UserGroups;
61 use wpforo\modules\bookmarks\Bookmarks;
62 use wpforo\modules\reactions\Reactions;
63 use wpforo\modules\revisions\Revisions;
64 use wpforo\modules\subscriptions\Subscriptions;
65
66 final class wpforo {
67 private static $_instance = null;
68
69 /** @var wpdb * */
70 public $db;
71 public $default;
72
73 public $blog_prefix = "wp_";
74 public $base_prefix = "wpforo_";
75 public $prefix = "wpforo_";
76 public $_base_tables = [
77 'boards',
78 'usergroups',
79 'profiles',
80 'logs',
81 'follows',
82 'bookmarks',
83 'accesses',
84 'email_queue',
85 ];
86 public $_tables = [
87 'activity',
88 'ai_cache',
89 'ai_chat_conversations',
90 'ai_chat_messages',
91 'ai_embeddings',
92 'ai_embeddings_cache',
93 'ai_logs',
94 'ai_moderation',
95 'ai_tasks',
96 'ai_task_logs',
97 'forums',
98 'languages',
99 'phrases',
100 'postmeta',
101 'posts',
102 'post_revisions',
103 'subscribes',
104 'topics',
105 'tags',
106 'visits',
107 'reactions',
108 ];
109 public $tables;
110 private $_folders = [
111 'assets',
112 'attachments',
113 'avatars',
114 'covers',
115 'cache',
116 'default_attachments',
117 'emoticons',
118 ];
119 public $folders = [];
120
121 public $current_url;
122 public $canonical_url;
123 public $GET = [];
124 public $current_object;
125
126 public $wp_current_user = null;
127 public $current_user = [];
128 public $current_usermeta = [];
129 public $current_user_groupid = 4;
130 public $current_user_groupids = [ 4 ];
131 public $current_user_secondary_groupids = [];
132 public $current_userid = 0;
133 public $current_user_login = '';
134 public $current_user_email = '';
135 public $current_user_display_name = '';
136 public $current_user_status = '';
137 public $current_user_accesses = [];
138 public $session_token = '';
139
140 public $tools_cleanup;
141 public $tools_misc;
142 public $locale = 'en_US';
143
144 public $dissmissed;
145
146 /** @var Settings */
147 public $settings;
148 /** @var Actions */
149 public $action;
150 /** @var Activity */
151 public $activity;
152 /** @var API */
153 public $api;
154 /** @var AIClient */
155 public $ai_client;
156 /** @var AIChatbot */
157 public $ai_chatbot;
158 /** @var VectorStorageManager */
159 public $vector_storage;
160 /** @var TaskManager */
161 public $task_manager;
162 /** @var AIContentModeration */
163 public $ai_content_moderation;
164 /** @var AILogs */
165 public $ai_logs;
166 /** @var AIWordPressIndexer */
167 public $ai_wp_indexer;
168 /** @var Boards */
169 public $board;
170 /** @var Cache */
171 public $cache;
172 /** @var Feed */
173 public $feed;
174 /** @var Forms */
175 public $form;
176 /** @var Forums */
177 public $forum;
178 /** @var Logs */
179 public $log;
180 /** @var Members */
181 public $member;
182 /** @var Moderation */
183 public $moderation;
184 /** @var Notices */
185 public $notice;
186 /** @var Permissions */
187 public $perm;
188 /** @var Phrases */
189 public $phrase;
190 /** @var PostMeta */
191 public $postmeta;
192 /** @var Posts */
193 public $post;
194 /** @var Blocks */
195 public $blocks;
196 /** @var RamCache */
197 public $ram_cache;
198 /** @var Revisions */
199 public $revision;
200 /** @var Reactions */
201 public $reaction;
202 /** @var Bookmarks */
203 public $bookmark;
204 /** @var SEO */
205 public $seo;
206 /** @var Subscriptions */
207 public $sbscrb;
208 /** @var EmailQueue */
209 public $email_queue;
210 /** @var Template */
211 public $tpl;
212 /** @var Topics */
213 public $topic;
214 /** @var UserGroups */
215 public $usergroup;
216
217 public static function instance() {
218 if( is_null( self::$_instance ) ) self::$_instance = new self();
219
220 return self::$_instance;
221 }
222
223 private function init_db() {
224 global $wpdb;
225 $this->db = $wpdb;
226 }
227
228 public function is_db_mysql8() {
229 if( preg_match( '#([\d.]+)-MariaDB#iu', (string) $this->db->get_var( "SELECT VERSION()" ), $match ) ) {
230 $use_mysql8 = version_compare( $match[1], '10.2.6', '>=' );
231 } else {
232 $use_mysql8 = version_compare( $this->db->db_version(), '8.0.0', '>=' );
233 }
234
235 return apply_filters( 'wpforo_use_mysql8', $use_mysql8 );
236 }
237
238 private function __construct() {
239 $this->init_db();
240 $this->init_hooks();
241 }
242
243 public function generate_prefix( $boardid ) {
244 $boardid = intval( $boardid );
245
246 return $this->base_prefix . ( $boardid ? $boardid . '_' : '' );
247 }
248
249 private function init_prefix() {
250 $boardid = $this->board->get_current( 'boardid' );
251 $this->prefix = $this->generate_prefix( $boardid );
252 }
253
254 public function change_board( $args = [] ) {
255 if( is_null( $this->board ) ) return;
256
257 do_action( 'wpforo_before_change_board', $args );
258
259 $boardid = $this->board->get_current( 'boardid' );
260 $is_inited = (bool) wpfval( $this->board->is_inited, $boardid );
261 $this->board->init_current( $args );
262
263 if( ! $is_inited || $boardid !== $this->board->get_current( 'boardid' ) ) {
264 $this->ram_cache->reset();
265
266 $this->init_prefix();
267 $this->init_tables();
268 $this->init_folders();
269 $this->init_options();
270
271 do_action( 'wpforo_after_change_board', $args );
272 }
273 }
274
275 private function init_base_classes() {
276 $this->requires();
277 $this->init_defaults();
278 $this->reset_current_object();
279 $this->init_base_tables();
280 $this->init_folders();
281
282 do_action( 'wpforo_before_init_base_classes' );
283
284 $this->settings = new Settings();
285 $this->tpl = new Template();
286 $this->ram_cache = new RamCache();
287 $this->cache = new Cache();
288 $this->action = new Actions();
289 $this->board = new Boards();
290 $this->usergroup = new UserGroups();
291 $this->member = new Members();
292 $this->perm = new Permissions();
293 $this->notice = new Notices();
294
295 $this->moderation = new Moderation();
296 $this->phrase = new Phrases();
297 $this->blocks = new Blocks();
298
299 do_action( 'wpforo_after_init_base_classes' );
300 }
301
302 private function init_classes() {
303 do_action( 'wpforo_before_init_classes' );
304
305 $this->activity = new Activity();
306 $this->api = new API();
307 // AIClient: Initialize globally for both admin and front-end features
308 // (semantic search, antispam, auto-translation, etc.)
309 $this->ai_client = new AIClient();
310 // AIChatbot: AI-powered chat functionality in AI Assistant widget
311 $this->ai_chatbot = new AIChatbot();
312 // VectorStorageManager: Unified abstraction for vector storage (local/cloud)
313 $this->vector_storage = new VectorStorageManager();
314 // TaskManager: Initialize for AI task scheduling and execution
315 $this->task_manager = new TaskManager();
316 // AIContentModeration: Central class for AI-powered content moderation
317 $this->ai_content_moderation = AIContentModeration::get_instance();
318 // AILogs: Logging of all AI actions for admin visibility
319 $this->ai_logs = new AILogs();
320 // AIWordPressIndexer: WordPress content type indexing for AI search
321 $this->ai_wp_indexer = new AIWordPressIndexer();
322 $this->feed = new Feed();
323 $this->form = new Forms();
324 $this->forum = new Forums();
325 $this->log = new Logs();
326 $this->postmeta = new PostMeta();
327 $this->post = new Posts();
328 $this->seo = new SEO();
329 $this->topic = new Topics();
330 $this->reaction = new Reactions();
331 $this->bookmark = new Bookmarks();
332 $this->sbscrb = new Subscriptions();
333 $this->email_queue = new EmailQueue();
334 if( wpforo_is_module_enabled( 'revisions' ) ) $this->revision = new Revisions();
335
336 do_action( 'wpforo_after_init_classes' );
337 }
338
339 public function set_locale( $locale ) {
340 $this->locale = $locale;
341 do_action( 'wpforo_after_set_locale', $locale );
342 }
343
344 private function init_hooks() {
345 add_action( 'wpforo_after_init_folders', function( $folders ) {
346 $this->create_folders( $folders );
347 }, 9999 );
348 add_action(
349 'after_setup_theme',
350 function() {
351 do_action( 'wpforo_before_base_init' );
352 $this->init_base_classes();
353 if( ! $this->is_installed() ) $this->init();
354 },
355 0
356 );
357 add_action( 'change_locale', [ $this, 'set_locale' ] );
358 add_action( 'init', function() { $this->set_locale( get_locale() ); } );
359 add_action( 'widgets_init', function() {
360 $boards = $this->board->get_boards( [ 'status' => true ] );
361 foreach( $boards as $board ) {
362 register_sidebar(
363 [
364 'id' => sprintf(
365 'wpforo_%1$ssidebar',
366 $board['boardid'] ? $board['boardid'] . '_' : ''
367 ),
368 'name' => __( 'wpForo Sidebar', 'wpforo' ) . ' - ( ' . $board['title'] . ' )',
369 'description' => __( "NOTE: If you're going to add widgets in this sidebar, please use 'Full Width' template for wpForo index page to avoid sidebar duplication.", 'wpforo' ),
370 'before_widget' => '<aside id="%1$s" class="footer-widget-col %2$s clearfix">',
371 'after_widget' => '</aside>',
372 'before_title' => '<h3 class="widget-title">',
373 'after_title' => '</h3>',
374 ]
375 );
376 }
377
378 register_widget( 'wpforo\widgets\Forums' );
379 register_widget( 'wpforo\widgets\Profile' );
380 register_widget( 'wpforo\widgets\Search' );
381 register_widget( 'wpforo\widgets\OnlineMembers' );
382 register_widget( 'wpforo\widgets\RecentTopics' );
383 register_widget( 'wpforo\widgets\RecentPosts' );
384 register_widget( 'wpforo\widgets\Tags' );
385 } );
386 add_filter( 'plugin_locale', function( $locale, $domain ) {
387 if( $domain === 'wpforo' && defined( 'DOING_AJAX' ) && DOING_AJAX ) $locale = $this->locale;
388
389 return $locale;
390 }, 10, 2 );
391 add_action( 'wpforo_after_init_classes', function() {
392 if( wpforo_setting( 'email', 'disable_new_user_admin_notification' ) ) {
393 remove_action( 'after_password_reset', 'wp_password_change_notification' );
394
395 remove_action( 'register_new_user', 'wp_send_new_user_notifications' );
396 add_action( 'register_new_user', 'wpforo_send_new_user_notifications' );
397
398 remove_action( 'edit_user_created_user', 'wp_send_new_user_notifications' );
399 add_action( 'edit_user_created_user', 'wpforo_send_new_user_notifications', 10, 2 );
400
401 remove_action( 'network_site_new_created_user', 'wp_send_new_user_notifications' );
402 add_action( 'network_site_new_created_user', 'wpforo_send_new_user_notifications' );
403
404 remove_action( 'network_site_users_created_user', 'wp_send_new_user_notifications' );
405 add_action( 'network_site_new_created_user', 'wpforo_send_new_user_notifications' );
406
407 remove_action( 'network_user_new_created_user', 'wp_send_new_user_notifications' );
408 add_action( 'network_user_new_created_user', 'wpforo_send_new_user_notifications' );
409 }
410 } );
411
412 // WP Cron: Ensure classes are initialized early for cron hook callbacks
413 if ( wp_doing_cron() || isset( $_GET['doing_wp_cron'] ) ) {
414 add_action( 'init', [ $this, 'init' ], 1 ); // Priority 1 - before cron runs
415 } elseif( is_admin() || strpos( (string) $_SERVER['REQUEST_URI'], '/peepsoajax/' ) === 0 || preg_match(
416 '#^/?(?:([^\s/?&=<>:\'\"*\\\|]*/)(?1)*)?[^\s/?&=<>:\'\"*\\\|]+\.(?:php|js|css|jpe?g|png|gif|bmp|webp|svg|tiff)/?(?:\?[^/]*)?$#iu',
417 (string) $_SERVER['REQUEST_URI']
418 ) ) {
419 add_action( 'init', [ $this, 'init' ], 99 );
420 add_action( 'admin_init', [ $this, 'admin_init' ] );
421 } elseif( strpos( (string) $_SERVER['REQUEST_URI'], '/wp-json/' ) !== false ) {
422 add_filter( 'rest_authentication_errors', function( $r ) {
423 $this->init();
424
425 return $r;
426 }, 9999 );
427 } else {
428 add_action( 'wp', [ $this, 'init' ], 99 );
429 }
430 add_action( 'switch_blog', [ $this, 'after_switch_blog' ], 10, 2 );
431 add_action( 'wpforo_before_init', [ $this, 'change_board' ] );
432 add_action( 'wpforo_after_init_current_url', [ $this, 'change_board' ] );
433 }
434
435 public function after_switch_blog( $new_blog_id, $prev_blog_id ) {
436 if( intval( $new_blog_id ) !== intval( $prev_blog_id ) ) {
437 $this->init_base_tables();
438 $this->init_tables();
439 $this->init_folders();
440 }
441 }
442
443 private function init_base_tables() {
444 $this->db->query( "SET SESSION group_concat_max_len = 1000000" );
445 $blog_id = apply_filters( 'wpforo_current_blog_id', 0 );
446 $this->tables = new stdClass;
447 if( ! $blog_id ) $blog_id = $this->db->blogid;
448 $this->blog_prefix = $this->db->get_blog_prefix( $blog_id );
449 $this->_base_tables = apply_filters( 'wpforo_init_base_tables', $this->_base_tables );
450 foreach( $this->_base_tables as $table ) $this->tables->$table = $this->fix_table_name( $table );
451 }
452
453 private function init_tables() {
454 $this->_tables = apply_filters( 'wpforo_init_tables', $this->_tables );
455 foreach( $this->_tables as $table ) $this->tables->$table = $this->fix_table_name( $table );
456 }
457
458 public function get_active_boards_tables( $basename ) {
459 $tables = [];
460 if( $boardids = $this->board->get_active_boardids() ) {
461 foreach( $boardids as $boardid ) $tables[] = $this->fix_table_name( $basename, $this->generate_prefix( $boardid ) );
462 }
463
464 return array_unique( $tables );
465 }
466
467 /**
468 * @param string $basename
469 *
470 * @return string
471 */
472 public function fix_table_name( $basename, $prefix = null ) {
473 return $this->blog_prefix . ( in_array( $basename, $this->_base_tables, true ) ? $this->base_prefix : ( is_null( $prefix ) ? $this->prefix : $prefix ) ) . $basename;
474 }
475
476 public function fix_dir_sep( $dir ) {
477 $dir = str_replace( [ '/', '\\', '\\\\' ], DIRECTORY_SEPARATOR, $dir );
478
479 return rtrim( trim( (string) $dir ), DIRECTORY_SEPARATOR );
480 }
481
482 public function fix_url_sep( $url ) {
483 return trim( str_replace( [ '/', '\\', '\\\\' ], '/', (string) $url ) );
484 }
485
486 private function init_folders() {
487 $this->_folders = apply_filters( 'wpforo_init_folders', $this->_folders );
488 $boardid = ( is_callable( [ $this->board, 'get_current' ] ) ? $this->board->get_current( 'boardid' ) : 0 );
489 $upload_dir = wp_get_upload_dir();
490
491 // In many cases people leave the website URL protocol as http, but website works with https
492 // In this case colors.css and phrases.js are not loaded because of http blocking by browser
493 $upload_dir['baseurl'] = is_ssl() ? str_replace( 'http://', 'https://', $upload_dir['baseurl'] ) : $upload_dir['baseurl'];
494
495 $this->folders['wp_upload'] = [
496 'dir' => $this->fix_dir_sep( $upload_dir['basedir'] ),
497 'url' => $this->fix_url_sep( $upload_dir['baseurl'] ),
498 ];
499 $this->folders['wp_upload']['url//'] = preg_replace( '#^https?:#i', '', (string) $this->folders['wp_upload']['url'] );
500 $this->folders['upload'] = [
501 'dir' => $this->folders['wp_upload']['dir'] . DIRECTORY_SEPARATOR . "wpforo" . ( $boardid ? '_' . $boardid : '' ),
502 'url' => $this->folders['wp_upload']['url'] . "/wpforo" . ( $boardid ? '_' . $boardid : '' ),
503 'url//' => $this->folders['wp_upload']['url//'] . "/wpforo" . ( $boardid ? '_' . $boardid : '' ),
504 ];
505 foreach( $this->_folders as $folder ) {
506 $this->folders[ $folder ] = [
507 'dir' => $this->folders['upload']['dir'] . DIRECTORY_SEPARATOR . trim( $this->fix_dir_sep( (string) $folder ), DIRECTORY_SEPARATOR ),
508 'url' => $this->folders['upload']['url'] . "/" . trim( $this->fix_url_sep( (string) $folder ), '/' ),
509 'url//' => $this->folders['upload']['url//'] . "/" . trim( $this->fix_url_sep( (string) $folder ), '/' ),
510 ];
511 }
512
513 $this->folders = apply_filters( 'wpforo_working_folders', $this->folders );
514
515 do_action( 'wpforo_after_init_folders', $this->folders );
516 }
517
518 private function create_folders( array $folders = [] ): void {
519 if( ! $folders ) $folders = WPF()->folders;
520 foreach( $folders as $folder ) {
521 $dir = ( is_scalar( $folder ) ? $folder : ( array_key_exists( 'dir', $folder ) ? (string) $folder['dir'] : '' ) );
522 if( $dir && ! is_dir( $dir ) ) {
523 if( wp_mkdir_p( $dir ) ) {
524 wpforo_write_file( "$dir/index.html", "" );
525 wpforo_write_file( "$dir/robots.txt", "User-agent: *\nDisallow: /" );
526 }
527 }
528 }
529 }
530
531 private function requires() {
532 require_once WPFORO_DIR . '/includes/functions.php';
533 require_once WPFORO_DIR . '/includes/functions-template.php';
534 require_once WPFORO_DIR . '/includes/hooks.php';
535 require_once WPFORO_DIR . '/includes/installation.php';
536 require_once WPFORO_DIR . '/integrations/functions.php';
537 if( wpforo_is_admin() ) require_once WPFORO_DIR . '/admin/index.php';
538 }
539
540 public function admin_init() {
541 if( wpforo_is_admin() ) {
542 $this->change_board();
543
544 $this->check_issues();
545
546 if( strpos( wpforo_get_request_uri(), 'user-new.php' ) === false ) {
547 if( ! $this->member->get_groupid( $this->current_userid ) ) {
548 $this->member->synchronize_user( $this->current_userid );
549 }
550 }
551
552 if( ! $this->usergroup->can_manage_forum() && wpforo_current_user_is( 'admin' ) ) {
553 $this->member->set_groupid( $this->current_userid, 1 );
554 }
555 }
556 }
557
558 public function check_issues() {
559 //Make sure all users profiles are created
560 if( $this->is_installed() ) {
561 if( apply_filters( 'wpforo_profile_synchronization_notice', true ) ) {
562 if( is_multisite() ) {
563 $sql = "SELECT COUNT(`user_id`) FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` LIKE '" . WPF(
564 )->blog_prefix . "capabilities' AND `user_id` NOT IN( SELECT `userid` FROM `" . WPF()->tables->profiles . "` )";
565 } else {
566 $sql = "SELECT COUNT(`ID`) as user_id FROM `" . WPF()->db->users . "` WHERE `ID` NOT IN( SELECT `userid` FROM `" . WPF()->tables->profiles . "` )";
567 }
568 if( (int) $this->db->get_var( $sql ) ) add_action( 'admin_notices', 'wpforo_profile_notice', 10 );
569 }
570 }
571 //Make sure tables structures are correct for current version
572 $wpforo_version_db = wpforo_get_option( 'version_db', null, false );
573 if( ! $wpforo_version_db || version_compare( $wpforo_version_db, WPFORO_VERSION, '<' ) || wpforo_setting( 'general', 'debug_mode' ) ) {
574 if( 'tables' !== wpfval( $_GET, 'view' ) ) {
575 $db_note = false;
576 $problems = wpforo_database_check();
577 if( ! empty( $problems ) ) {
578 foreach( $problems as $key => $problem ) {
579 if( wpfval( $problem, 'fields' ) ) $db_note = true;
580 if( wpfval( $problem, 'exists' ) ) $db_note = true;
581 if( $key === 'data' ) $db_note = true;
582 }
583 if( $db_note ) {
584 add_action( 'admin_notices', 'wpforo_database_notice', 10 );
585 }
586 } else {
587 wpforo_update_option( 'version_db', WPFORO_VERSION );
588 }
589 }
590 }
591 //Check cache plugins
592 $not_excluded_plugins = WPF()->cache->cache_plugins_status();
593 if( ! empty( $not_excluded_plugins ) ) {
594 add_action( 'admin_notices', 'wpforo_cache_information', 10 );
595 }
596 }
597
598 public function init() {
599 do_action( 'wpforo_before_init' );
600 $this->init_classes();
601 $this->init_current_url();
602 do_action( 'wpforo_core_inited' );
603 $this->init_current_object();
604 do_action( 'wpforo_after_init' );
605 }
606
607 public function shortcode_atts_to_url( $atts ) {
608 if( is_null( $this->forum ) ) $this->init_classes();
609
610 $url = wpforo_home_url();
611
612 $args = shortcode_atts(
613 [
614 'boardid' => 0,
615 'item' => 'forum',
616 'id' => 0,
617 'slug' => '',
618 ],
619 (array) $atts
620 );
621
622 WPF()->change_board( $args['boardid'] );
623
624 if( $args['item'] === 'profile' && ! $args['id'] ) $args['id'] = $this->current_userid;
625
626 if( $args['item'] === 'add-topic' ) {
627 $forum = $this->forum->get_forum( ( $args['slug'] ?: $args['id'] ) );
628 $forumid = (int) wpfval( $forum, 'is_cat' ) ? 0 : (int) wpfval( $forum, 'forumid' );
629 $url = wpforo_home_url( wpforo_settings_get_slug( 'add-topic' ) . '/' . $forumid );
630 } elseif( $args['item'] === 'recent' ) {
631 $url = wpforo_settings_get_slug( 'recent' ) . '/';
632 if( trim( (string) $args['slug'] ) ) $url .= '?view=' . wpforo_settings_get_slug( $args['slug'] );
633 $url = wpforo_home_url( $url );
634 } elseif( $args['item'] === 'members' ) {
635 $url = wpforo_members_url();
636 } elseif( $args['id'] || $args['slug'] ) {
637 $getid = ( $args['slug'] ?: $args['id'] );
638 if( $args['item'] === 'topic' ) {
639 $url = $this->topic->get_url( $getid );
640 } elseif( $args['item'] === 'profile' ) {
641 $url = $this->member->get_profile_url( $getid );
642 } elseif( $args['item'] === 'account' ) {
643 $url = $this->member->get_profile_url( $getid, 'account' );
644 } elseif( $args['item'] === 'activity' ) {
645 $url = $this->member->get_profile_url( $getid, 'activity' );
646 } elseif( $args['item'] === 'favored' ) {
647 $url = $this->member->get_profile_url( $getid, 'favored' );
648 } elseif( $args['item'] === 'subscriptions' ) {
649 $url = $this->member->get_profile_url( $getid, 'subscriptions' );
650 } elseif( $args['item'] === 'messages' ) {
651 $url = $this->member->get_profile_url( $getid, 'messages' );
652 } else {
653 $url = $this->forum->get_forum_url( $getid );
654 }
655 } elseif( $args['item'] === 'login' ) {
656 $url = wpforo_login_url();
657 } elseif( $args['item'] === 'register' ) {
658 $url = wpforo_register_url();
659 } elseif( $args['item'] === 'lostpassword' ) {
660 $url = wpforo_lostpassword_url();
661 }
662
663 return $url;
664 }
665
666 public function init_current_url( $atts = [] ) {
667 if( is_null( $this->post ) ) $this->init_classes();
668
669 if( is_scalar( $atts ) ) {
670 $url = $atts;
671 $atts = [];
672 } else {
673 $url = wpforo_get_request_uri();
674 }
675 if( $atts || is_wpforo_shortcode_page( $url ) ) {
676 if( $atts || ( $atts = get_wpforo_shortcode_atts( '', $url ) ) ) {
677 $url = $this->shortcode_atts_to_url( $atts );
678 } else {
679 $url = wpforo_home_url();
680 }
681 }
682
683 if( is_wpforo_url( $url ) && preg_match( '#/' . preg_quote( wpforo_settings_get_slug( 'postid' ) ) . '/(\d+)/?$#isu', strtok( $url, '?' ), $matches ) ) {
684 $post_url = $this->post->get_full_url( $matches[1] );
685 if( $post_url !== wpforo_home_url() ) {
686 $url = $post_url;
687 $this->canonical_url = $this->post->get_url( $matches[1] );
688 }
689 } elseif( is_wpforo_url( $url ) && preg_match( '#/' . preg_quote( wpforo_settings_get_slug( 'topicid' ) ) . '/(\d+)/?$#isu', strtok( $url, '?' ), $matches ) ) {
690 $topic_url = $this->topic->get_full_url( $matches[1] );
691 if( $topic_url !== wpforo_home_url() ) {
692 $url = $topic_url;
693 $this->canonical_url = $this->topic->get_url( $matches[1] );
694 }
695 }
696
697 if( function_exists( 'pll_default_language' ) ) {
698 $qvar = wpforo_get_url_query_vars_str( $url );
699 $qvar = preg_replace( '#^' . preg_quote( pll_default_language() ) . '/#', '', $qvar );
700 $url = $this->user_trailingslashit( home_url( $qvar ) );
701 }
702
703 $url = wpforo_fix_url( $url );
704 $url = preg_replace( '#\#[^/?&]*$#iu', '', $url );
705 parse_str( (string) parse_url( $url, PHP_URL_QUERY ), $get );
706 $get = array_merge( (array) $get, $_GET );
707 $get = wp_unslash( $get );
708
709 if( ! $this->canonical_url ) $this->canonical_url = $this->current_url;
710
711 $this->current_url = apply_filters( 'wpforo_init_current_url', $url );
712 $this->GET = apply_filters( 'wpforo_init_current_url_GET', $get );
713
714 if( ! $this->canonical_url ) $this->canonical_url = $this->current_url;
715 if( strpos( $this->canonical_url, '?wpfin=tag' ) === false && strpos( $this->canonical_url, '?view=' ) === false ) {
716 $this->canonical_url = preg_replace( '#\?.*$#isu', '', $this->canonical_url );
717 }
718 $this->canonical_url = apply_filters( 'wpforo_init_canonical_url', $this->canonical_url );
719
720 do_action( 'wpforo_after_init_current_url', $atts );
721 }
722
723 private function init_defaults() {
724 $this->default = new stdClass;
725
726 $this->default->current_object = [
727 'route' => '',
728 'template' => '',
729 'qvars' => [],
730 'args' => [],
731 'layout' => 1,
732 'og_text' => '',
733 'paged' => 1,
734 'items_count' => 0,
735 'items_per_page' => 15,
736 'is_404' => false,
737 'user_is_same_current_user' => false,
738 'orderby' => null,
739 'members' => [],
740 'categories' => [],
741 'topics' => [],
742 'posts' => [],
743 'user' => [],
744 'userid' => 0,
745 'user_nicename' => '',
746 'forum' => [],
747 'forumid' => 0,
748 'forum_slug' => '',
749 'forum_desc' => '',
750 'forum_meta_key' => '',
751 'forum_meta_desc' => '',
752 'topic' => [],
753 'topicid' => 0,
754 'topic_slug' => '',
755 'tags' => [],
756 'load_tinymce' => false,
757 ];
758
759 $this->default->tools_cleanup = [
760 'user_reg_days_ago' => 7,
761 'auto_cleanup_users' => 0,
762 'usergroup' => [ 1 => '0', 5 => '0', 2 => '1', 3 => '0' ],
763 ];
764
765 $this->default->tools_misc = [
766 'admin_note' => '',
767 'admin_note_groups' => [ 1, 2, 3, 4, 5 ],
768 'admin_note_pages' => [ 'forum' ],
769 ];
770
771 $this->default->stats = [
772 'forums' => 0,
773 'topics' => 0,
774 'posts' => 0,
775 'members' => 0,
776 'online_members_count' => 0,
777 'last_post_title' => '',
778 'last_post_url' => '',
779 'newest_member' => [],
780 'newest_member_dname' => '',
781 'newest_member_profile_url' => '',
782 ];
783
784 $this->default->dissmissed = [
785 'recaptcha_backend_note' => 0,
786 'recaptcha_note' => 0,
787 'addons_css_update' => 0,
788 ];
789 }
790
791 private function reset_current_object() {
792 $this->current_object = $this->default->current_object;
793 }
794
795 private function init_options() {
796 $this->tools_cleanup = wpforo_get_option( 'tools_cleanup', $this->default->tools_cleanup );
797 $this->tools_misc = wpforo_get_option( 'tools_misc', $this->default->tools_misc );
798 $this->dissmissed = wpforo_get_option( 'dissmissed', $this->default->dissmissed );
799 }
800
801 public function can_use_trailing_slashes() {
802 return '/' === substr( (string) get_option( 'permalink_structure', '' ), - 1, 1 );
803 }
804
805 public function user_trailingslashit( $url ) {
806 $rtrimed_url = '';
807 $url_append_vars = '';
808 if( preg_match( '#^(.+?)(/?[?&].*)?$#isu', $url, $match ) ) {
809 if( wpfval( $match, 1 ) ) $rtrimed_url = rtrim( $match[1], '/\\' );
810 if( wpfval( $match, 2 ) ) $url_append_vars = '?' . trim( $match[2], '?&/\\' );
811 if( $rtrimed_url ) {
812 $home_url = rtrim( preg_replace( '#/?\?.*$#isu', '', home_url() ), '/\\' );
813 if( $rtrimed_url == $home_url ) {
814 $url = $rtrimed_url . '/';
815 } else {
816 $url = $rtrimed_url . ( wpforo_ram_get( [ $this, 'can_use_trailing_slashes' ] ) ? '/' : '' );
817 }
818 }
819 }
820
821 return $url . $url_append_vars;
822 }
823
824 public function strip_url_paged_var( $url ) {
825 $patterns = [
826 '#/' . preg_quote( wpforo_settings_get_slug( 'paged' ) ) . '/?\d*/?#isu',
827 '#[&?]wpfpaged=\d*#iu',
828 ];
829 $url = preg_replace( $patterns, '', (string) $url );
830
831 return $this->user_trailingslashit( $url );
832 }
833
834 public function statistic_cache_clean() {
835 $this->db->query( "DELETE FROM `" . $this->db->options . "` WHERE `option_name` REGEXP '^" . wpforo_prefix( 'stat_[0-9]+' ) . "'" );
836 }
837
838 public function statistic( $mode = 'get', $template = 'all' ) {
839 $key = 'stat_' . $this->current_user_groupid;
840 if( $mode === 'get' ) {
841 if( $cached_stat = wpforo_get_option( $key, [], false ) ) {
842 $cached_stat['online_members_count'] = $this->member->online_members_count();
843 if( wpfkey( $cached_stat, 'forums' ) && wpfkey( $cached_stat, 'topics' ) && wpfkey( $cached_stat, 'posts' ) ) {
844 return wpforo_array_args_cast_and_merge( $cached_stat, $this->default->stats );
845 }
846 }
847 }
848
849 if( $mode === 'get' || $template === 'all' ) {
850 $stats['forums'] = $this->forum->get_count( [ 'is_cat' => 0 ] );
851 $stats['topics'] = $this->topic->get_count();
852 $stats['posts'] = $this->post->get_count();
853 $member_status = [ 'p.`status`' => $this->member->get_inlist_enabled_statuses() ];
854 $stats['members'] = $this->member->get_count( $member_status );
855 $stats['online_members_count'] = $this->member->online_members_count();
856 $row_count = apply_filters( 'wpforo_get_statistic_row_count', 1 );
857
858 $posts = $this->topic->get_topics( [ 'orderby' => 'modified', 'order' => 'DESC', 'row_count' => $row_count, 'private' => 0, 'status' => 0, 'permgroup' => 4 ] );
859 $first = key( $posts );
860 if( !is_null( $first ) && ! empty( $posts[ $first ] ) && $this->perm->forum_can( 'vf', $posts[ $first ]['forumid'] ) ) {
861 $stats['last_post_title'] = $posts[ $first ]['title'];
862 $stats['last_post_url'] = $this->post->get_url( $posts[ $first ]['last_post'] );
863 }
864
865 $newest_member = $this->member->get_newest_member();
866 if( ! empty( $newest_member ) ) {
867 $stats['newest_member'] = $newest_member;
868 $stats['newest_member_dname'] = wpforo_user_dname( $newest_member );
869 $stats['newest_member_profile_url'] = $newest_member['profile_url'];
870 }
871 } else {
872 $stats = wpforo_get_option( $key, $this->default->stats, false );
873 switch( $template ) {
874 case 'forum':
875 $stats['forums'] = $this->forum->get_count( [ 'is_cat' => 0 ] );
876 break;
877 case 'topic':
878 $stats['topics'] = $this->topic->get_count();
879 $posts = $this->topic->get_topics( [ 'orderby' => 'modified', 'order' => 'DESC', 'row_count' => 1 ] );
880 if( isset( $posts[0] ) && ! empty( $posts[0] ) && $this->perm->forum_can( 'vf', $posts[0]['forumid'] ) ) {
881 $stats['last_post_title'] = $posts[0]['title'];
882 $stats['last_post_url'] = $this->post->get_url( $posts[0]['last_post'] );
883 }
884 break;
885 case 'post':
886 $stats['posts'] = $this->post->get_count();
887 $posts = $this->topic->get_topics( [ 'orderby' => 'modified', 'order' => 'DESC', 'row_count' => 1 ] );
888 if( isset( $posts[0] ) && ! empty( $posts[0] ) && $this->perm->forum_can( 'vf', $posts[0]['forumid'] ) ) {
889 $stats['last_post_title'] = $posts[0]['title'];
890 $stats['last_post_url'] = $this->post->get_url( $posts[0]['last_post'] );
891 }
892 break;
893 case 'user':
894 //$member_status = [ 'p.`status`' => $this->member->get_inlist_enabled_statuses() ];
895 //$stats['members'] = $this->member->get_count( $member_status );
896 $stats['members'] = $this->member->get_count();
897 $stats['online_members_count'] = $this->member->online_members_count();
898
899 $newest_member = $this->member->get_newest_member( false );
900 if( ! empty( $newest_member ) ) {
901 $stats['newest_member'] = $newest_member;
902 $stats['newest_member_dname'] = wpforo_user_dname( $newest_member );
903 $stats['newest_member_profile_url'] = $newest_member['profile_url'];
904 }
905 break;
906 }
907 }
908
909 $stats = apply_filters( 'wpforo_get_statistic_array_filter', $stats );
910 $stats = wpforo_array_args_cast_and_merge( $stats, $this->default->stats );
911 wpforo_update_option( $key, $stats );
912
913 return $stats;
914 }
915
916 public function init_current_object() {
917 $this->reset_current_object();
918 $this->current_object['items_per_page'] = $this->post->get_option_items_per_page();
919 $url = $this->current_url;
920 $get = $this->GET;
921
922 if( ! is_wpforo_page( $url ) ) return;
923
924 $current_url = wpforo_get_url_query_vars_str( $url );
925
926 $current_object = [];
927 if( wpfkey( $get, 'wpfs' ) || wpfval( $get, 'foro' ) === 'search' ) $this->current_object['template'] = 'search';
928 if( wpfval( $get, 'wpforo' ) || wpfval( $get, 'foro' ) ) {
929 $request = ( wpfval( $get, 'wpforo' ) ) ? wpfval( $get, 'wpforo' ) : wpfval( $get, 'foro' );
930 if( $request === 'page' ) $this->current_object['template'] = 'page';
931 }
932
933 $template_key = '';
934 $wpf_url = preg_replace( '#/?\?.*$#isu', '', $current_url );
935 $wpf_url_parse = array_values( array_filter( explode( '/', trim( (string) $wpf_url, '/' ) ) ) );
936 if( array_key_exists( 0, $wpf_url_parse ) && in_array( $wpf_url_parse[0], $this->board->routes ) ) {
937 $this->current_object['route'] = $wpf_url_parse[0];
938 unset( $wpf_url_parse[0] );
939
940 if( in_array( $this->current_object['route'], $this->board->base_routes, true ) ) {
941 $this->current_object['template'] = ( $template_key = wpforo_settings_get_slug_key( $this->current_object['route'] ) ) !== 'member' ? $template_key : '';
942
943 $current_object['qvars'] = $wpf_url_parse;
944 $current_object['qvars'] = array_values( $current_object['qvars'] );
945
946 if( $this->current_object['template'] === 'register' ) {
947 $this->form->current['template'] = 'register';
948 $this->form->current['value'] = array_merge(
949 (array) wpfval( $this->form->current, 'value' ),
950 (array) wpfval( $_POST, 'wpfreg' ),
951 (array) wpfval( $_POST, 'data' )
952 );
953 $this->form->current['value']['user_login'] = sanitize_user( (string) wpfval( $_POST, 'wpfreg', 'user_login' ) );
954 $this->form->current['value']['user_email'] = sanitize_email( (string) wpfval( $_POST, 'wpfreg', 'user_email' ) );
955 $this->form->current['varname'] = 'wpfreg';
956 } elseif( $this->current_object['template'] === 'logout' && $this->current_userid ) {
957 $this->action->logout();
958 }
959 }
960
961 if( $this->current_userid && in_array( $this->current_object['template'], [ 'register', 'login', 'lostpassword' ], true ) && ! wpforo_is_admin() ) wpforo_redirect_to();
962 }
963 $wpf_url_parse = array_reverse( $wpf_url_parse );
964
965 if( in_array( wpforo_settings_get_slug( 'paged' ), $wpf_url_parse ) ) {
966 foreach( $wpf_url_parse as $key => $value ) {
967 if( $value === wpforo_settings_get_slug( 'paged' ) ) {
968 unset( $wpf_url_parse[ $key ] );
969 break;
970 }
971 if( is_numeric( $value ) ) $paged = intval( $value );
972
973 unset( $wpf_url_parse[ $key ] );
974 }
975 }
976 if( $_paged = intval( wpfval( $get, 'wpfpaged' ) ) ) $paged = $_paged;
977 $current_object['paged'] = ( isset( $paged ) && $paged > 0 ) ? $paged : 1;
978 $current_object['orderby'] = wpfval( $get, 'orderby' );
979
980 $wpf_url_parse = array_values( $wpf_url_parse );
981
982 if( ! $this->current_object['template'] ) {
983 $current_object = apply_filters( 'wpforo_before_init_current_object', $current_object, $wpf_url_parse );
984 if( wpfkey( $current_object, 'template' ) ) $this->current_object['template'] = (string) wpfval( $current_object, 'template' );
985 }
986
987 if( ! $this->current_object['template'] ) {
988 $templates = $template_key === 'member' ? $this->tpl->get_member_templates_list() : $this->tpl->get_templates_list();
989 if( $templates ) {
990 if( $template_key === 'member' ) {
991 if( count( $wpf_url_parse ) > 1 ) {
992 $nickname = end( $wpf_url_parse );
993 $profile_route = prev( $wpf_url_parse );
994 array_pop( $wpf_url_parse );
995 array_pop( $wpf_url_parse );
996 array_push( $wpf_url_parse, $nickname, $profile_route );
997 } else {
998 if( ! in_array( end( $wpf_url_parse ), array_map( 'wpforo_settings_get_slug', $templates ) ) ) $wpf_url_parse[] = wpforo_settings_get_slug( 'profile' );
999 }
1000 }
1001 $__slug = end( $wpf_url_parse );
1002 foreach( $templates as $template ) {
1003 if( $__slug === wpforo_settings_get_slug( $template ) ) {
1004 $this->current_object['template'] = $template;
1005 $current_object['qvars'] = $wpf_url_parse;
1006 array_pop( $current_object['qvars'] );
1007 $current_object['qvars'] = array_reverse( $current_object['qvars'] );
1008 break;
1009 }
1010 }
1011
1012 if( $template_key === 'member' ) {
1013 if( ! $this->current_object['template'] && ! $wpf_url_parse ) {
1014 if( $this->current_userid ) {
1015 $this->current_object['template'] = 'profile';
1016 } else {
1017 wp_safe_redirect( wpforo_login_url() );
1018 exit();
1019 }
1020 } elseif( ! wpforo_is_member_template( $this->current_object['template'] ) ) {
1021 $this->current_object['template'] = '';
1022 $current_object['qvars'] = [];
1023 }
1024 }
1025 }
1026 }
1027
1028 if( ! $this->current_object['template'] ) {
1029 $this->current_object['template'] = 'forum';
1030 $this->form->current['varname'] = 'thread';
1031 if( isset( $wpf_url_parse[0] ) ) {
1032 if( isset( $wpf_url_parse[1] ) ) {
1033 $current_object['topic_slug'] = $wpf_url_parse[0];
1034 $current_object['forum_slug'] = $wpf_url_parse[1];
1035 $this->current_object['template'] = 'post';
1036 $this->form->current['varname'] = 'post';
1037 } else {
1038 $current_object['forum_slug'] = $wpf_url_parse[0];
1039 $this->current_object['template'] = 'topic';
1040 $this->form->current['varname'] = 'thread';
1041 }
1042 }
1043 }
1044
1045 $current_object = apply_filters( 'wpforo_after_init_current_template', $current_object, $wpf_url_parse, $get );
1046 if( wpfkey( $current_object, 'template' ) ) $this->current_object['template'] = (string) wpfval( $current_object, 'template' );
1047
1048 if( $this->current_object['template'] ) {
1049 if( wpforo_is_member_template( $this->current_object['template'] ) ) {
1050 if( ! wpfval( $current_object, 'userid' ) && ! wpfval( $current_object, 'user_nicename' ) ) {
1051 if( $qvar0 = wpfval( $current_object['qvars'], 0 ) ) {
1052 if( wpforo_setting( 'profiles', 'url_structure' ) === 'id' ) {
1053 $current_object['userid'] = $qvar0;
1054 } else {
1055 $current_object['user_nicename'] = $qvar0;
1056 }
1057 } else {
1058 if( $this->current_userid ) {
1059 $current_object['userid'] = $this->current_userid;
1060 } else {
1061 wp_safe_redirect( wpforo_login_url() );
1062 exit();
1063 }
1064 }
1065 }
1066
1067 // redirect old type of member urls to new one
1068 if( $template_key !== 'member' ) {
1069 if( count( $current_object['qvars'] ) === 1 ) {
1070 $redirect = wpforo_url(
1071 array_shift( $current_object['qvars'] ) . ( $this->current_object['template'] !== 'profile' ? '/' . wpforo_settings_get_slug( $this->current_object['template'] ) : '' ),
1072 'member'
1073 );
1074 } else {
1075 $redirect = wpforo_url(
1076 array_shift( $current_object['qvars'] ) . '/' . wpforo_settings_get_slug( $this->current_object['template'] ) . '/' . implode( '/', $current_object['qvars'] ),
1077 'member'
1078 );
1079 }
1080 wp_safe_redirect( $redirect );
1081 exit();
1082 }
1083 // redirection
1084
1085 } elseif( $this->current_object['template'] === 'search' ) {
1086 $args = [
1087 'needle' => sanitize_text_field( wpfval( $get, 'wpfs' ) ),
1088 'type' => sanitize_text_field( wpfval( $get, 'wpfin' ) ),
1089 'date_period' => intval( wpfval( $get, 'wpfd' ) ),
1090 'forumids' => (array) wpfval( $get, 'wpff' ),
1091 'offset' => ( $current_object['paged'] - 1 ) * $this->current_object['items_per_page'],
1092 'row_count' => $this->current_object['items_per_page'],
1093 'orderby' => 'relevancy',
1094 'order' => 'desc',
1095 'postids' => [],
1096 ];
1097 if( ! empty( $get['wpfob'] ) ) {
1098 $args['orderby'] = wpforo_sanitize_orderby( $get['wpfob'], 'search', 'relevancy' );
1099 } elseif( in_array( wpfval( $args, 'type' ), [ 'tag', 'user-posts', 'user-topics' ], true ) ) {
1100 $args['orderby'] = 'date';
1101 }
1102 $wpfo = strtolower( (string) wpfval( $get, 'wpfo' ) );
1103 if( in_array( $wpfo, [ 'asc', 'desc' ], true ) ) $args['order'] = $wpfo;
1104 $sdata = array_filter( (array) wpfval( $get, 'data' ) );
1105 $args['postids'] = $this->postmeta->search( $sdata );
1106 $current_object['args'] = $args;
1107 if( $sdata && ! $args['postids'] ) {
1108 $current_object['items_count'] = 0;
1109 $current_object['posts'] = [];
1110 } else {
1111 $current_object['posts'] = $this->post->search( $args, $current_object['items_count'] );
1112 }
1113 } elseif( $this->current_object['template'] === 'recent' ) {
1114 $args = [];
1115 $view = false;
1116 $current_object['items_count'] = 0;
1117 if( ! empty( WPF()->GET['view'] ) ) $view = sanitize_title( WPF()->GET['view'] );
1118 if( ! empty( WPF()->GET['wpff'] ) ) $args['forumid'] = intval( WPF()->GET['wpff'] );
1119 if( ! empty( WPF()->GET['prefixid'] ) ) {
1120 $args['prefixid'] = intval( WPF()->GET['prefixid'] );
1121 }
1122
1123 $type = ( $view && $view !== 'unapproved' ? 'topics' : wpforo_setting( 'topics', 'recent_posts_type' ) );
1124 if( $view === 'unapproved' ) $type = 'posts';
1125 if( $view === 'prefix' ) $type = 'topics';
1126
1127 if( ! WPF()->current_userid || ! WPF()->usergroup->can( 'aum' ) ) {
1128 $args['private'] = 0;
1129 $args['status'] = 0;
1130 }
1131 $days = apply_filters( 'wpforo_recent_posts_limit', 30 );
1132
1133 $end_date = time() - ( intval( $days ) * 24 * 60 * 60 );
1134 if( $type === 'topics' ) {
1135 $current_object['items_per_page'] = wpforo_setting( 'topics', 'topics_per_page' );
1136
1137 if( wpfval( $args, 'prefixid' ) ) $args['prefix'] = (int) wpfval( $args, 'prefixid' );
1138 $args['where'] = "`modified` > '" . gmdate( 'Y-m-d H:i:s', $end_date ) . "'";
1139 $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'topics', 'modified' ) : 'modified';
1140 $args['order'] = 'DESC';
1141 $args['offset'] = ( $current_object['paged'] - 1 ) * $current_object['items_per_page'];
1142 $args['row_count'] = $current_object['items_per_page'];
1143
1144 switch( $view ) {
1145 case 'unread':
1146 $args['read'] = false;
1147 break;
1148 case 'no-replies':
1149 $args['where'] = null;
1150 $args['include'] = wpforo_get_not_replied_topicids();
1151 break;
1152 case 'solved':
1153 $args['solved'] = 1;
1154 break;
1155 case 'unsolved':
1156 $args['solved'] = 0;
1157 break;
1158 case 'closed':
1159 $args['closed'] = 1;
1160 break;
1161 case 'opened':
1162 $args['closed'] = 0;
1163 break;
1164 case 'sticky':
1165 $args['type'] = 1;
1166 break;
1167 case 'private':
1168 $args['private'] = 1;
1169 break;
1170 case 'unapproved':
1171 $args['status'] = 1;
1172 break;
1173 case 'prefix':
1174 $args['where'] = null;
1175 break;
1176 }
1177 $topics = $view === 'no-replies' && empty( $args['include'] ) ? [] : WPF()->topic->get_topics( $args, $current_object['items_count'] );
1178 if( $topics ) {
1179 $intro_posts = wpforo_setting( 'topics', 'layout_extended_intro_posts_count' );
1180 if( $intro_posts < 1 ) {
1181 $intro_posts = null;
1182 } else {
1183 $intro_posts = ( $intro_posts > 1 ) ? ( $intro_posts - 1 ) : 0;
1184 }
1185 foreach( $topics as $key => $topic ) {
1186 $topics[ $key ]['replies'] = [];
1187 $topics[ $key ]['member'] = wpforo_member( $topic );
1188 $topics[ $key ]['forum'] = wpforo_forum( $topic['forumid'] );
1189 if( isset( $topic['last_post'] ) && $topic['last_post'] != 0 ) {
1190 $topics[ $key ]['last_post'] = wpforo_post( $topic['last_post'] );
1191 $topics[ $key ]['last_poster'] = wpforo_member( $topics[ $key ]['last_post'] );
1192 }
1193 if( isset( $topic['first_postid'] ) && $topic['first_postid'] != 0 ) {
1194 $topics[ $key ]['first_post'] = wpforo_post( $topic['first_postid'] );
1195
1196 $topics[ $key ]['first_poster'] = wpforo_member( $topics[ $key ]['first_post'] );
1197 if( ! $view && apply_filters( 'wpforo_recent_topics_intro', true, $topic ) ) {
1198 $topics[ $key ]['replies'] = WPF()->post->get_posts(
1199 [ 'topicid' => $topic['topicid'], 'orderby' => '`is_first_post` ASC, `created` DESC, `postid` DESC', 'row_count' => $intro_posts ]
1200 );
1201 }
1202 }
1203 $topics[ $key ]['topic_url'] = wpforo_topic( $topic['topicid'], 'url' );
1204 $topics[ $key ]['topic_posturl'] = ( $view === 'unread' && wpfval( $topics[ $key ]['last_post'], 'url' ) ) ? $topics[ $key ]['last_post']['url'] : WPF()->topic->get_url(
1205 $topic['topicid']
1206 );
1207 }
1208 $args['intro_posts'] = $intro_posts;
1209 }
1210 $current_object['topics'] = $topics;
1211 } else {
1212 $current_object['items_per_page'] = wpforo_setting( 'topics', 'posts_per_page' );
1213
1214 if( $view !== 'unapproved' ) $args['where'] = "`created` > '" . gmdate( 'Y-m-d H:i:s', $end_date ) . "'";
1215 $args['orderby'] = ( ! empty( WPF()->GET['wpfob'] ) ) ? wpforo_sanitize_orderby( WPF()->GET['wpfob'], 'posts', 'created' ) : 'created';
1216 $args['order'] = 'DESC';
1217 $args['offset'] = ( $current_object['paged'] - 1 ) * $current_object['items_per_page'];
1218 $args['row_count'] = $current_object['items_per_page'];
1219 if( $view === 'unapproved' ) {
1220 $args['status'] = 1;
1221 }
1222 $current_object['posts'] = WPF()->post->get_posts( $args, $current_object['items_count'] );
1223 }
1224 $args['view'] = $view;
1225 $args['type'] = $type;
1226 $current_object['args'] = $args;
1227 } elseif( $this->current_object['template'] === 'recent-activity' ) {
1228 $current_object['items_per_page'] = 20;
1229 $current_object['items_count'] = 0;
1230
1231 // Get filter parameters from URL
1232 $activity_type = ! empty( WPF()->GET['type'] ) ? sanitize_text_field( WPF()->GET['type'] ) : 'all';
1233 $period = ! empty( WPF()->GET['period'] ) ? sanitize_text_field( WPF()->GET['period'] ) : 'week';
1234
1235 // Calculate date_from based on period
1236 $date_from = null;
1237 switch( $period ) {
1238 case 'day':
1239 $date_from = time() - ( 24 * 60 * 60 );
1240 break;
1241 case 'week':
1242 $date_from = time() - ( 7 * 24 * 60 * 60 );
1243 break;
1244 case 'month':
1245 $date_from = time() - ( 30 * 24 * 60 * 60 );
1246 break;
1247 case 'all':
1248 default:
1249 $date_from = null;
1250 break;
1251 }
1252
1253 // Get enabled activity types from settings
1254 $enabled_types = WPF()->settings->activity['enabled_types'] ?? [];
1255
1256 // Build activity query args
1257 $args = [
1258 'types_exclude' => [ 'new_reply' ], // Exclude notification-only types
1259 'orderby' => 'date',
1260 'order' => 'DESC',
1261 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
1262 'row_count' => $current_object['items_per_page'],
1263 'date_from' => $date_from,
1264 ];
1265
1266 // Filter by activity type
1267 if( $activity_type !== 'all' ) {
1268 $type_mapping = [
1269 'topic' => [ 'wpforo_topic', 'edit_topic' ],
1270 'reply' => [ 'wpforo_post', 'edit_post' ],
1271 'reaction' => [ 'new_like', 'new_dislike', 'new_up_vote', 'new_down_vote', 'new_reaction' ],
1272 'favorite' => [ 'new_favorite' ],
1273 'solved' => [ 'topic_solved' ],
1274 'closed' => [ 'topic_closed' ],
1275 'answer' => [ 'post_answer' ],
1276 ];
1277 if( isset( $type_mapping[ $activity_type ] ) ) {
1278 // Intersect with enabled types to respect settings
1279 $args['types_include'] = array_intersect( $type_mapping[ $activity_type ], $enabled_types );
1280 }
1281 } else {
1282 // Show all enabled types
1283 if( ! empty( $enabled_types ) ) {
1284 $args['types_include'] = $enabled_types;
1285 }
1286 }
1287
1288 $current_object['activities'] = WPF()->activity->get_activities( $args, $current_object['items_count'], true, true );
1289 $current_object['args'] = [
1290 'activity_type' => $activity_type,
1291 'period' => $period,
1292 ];
1293 } elseif( $this->current_object['template'] === 'tags' ) {
1294 $current_object['items_per_page'] = wpforo_setting( 'tags', 'per_page' );
1295 $args = [
1296 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
1297 'row_count' => $current_object['items_per_page'],
1298 ];
1299 $current_object['tags'] = $this->topic->get_tags( $args, $current_object['items_count'] );
1300 } elseif( $this->current_object['template'] === 'members' ) {
1301 $current_object['items_per_page'] = wpforo_setting( 'members', 'members_per_page' );
1302
1303 $this->form->current['template'] = 'members';
1304 $this->form->current['value'] = $get;
1305 $this->form->current['varname'] = '';
1306
1307 if( ! empty( $get['_wpfms'] ) ) {
1308 $users_include = [];
1309 $search_fields_names = $this->member->get_search_fields_names();
1310
1311 $wpfms = ( isset( $get['wpfms'] ) ) ? sanitize_text_field( $get['wpfms'] ) : '';
1312 if( $wpfms ) {
1313 if( apply_filters( 'wpforo_member_search_provider', false, $wpfms, $search_fields_names, $get ) ) {
1314 $users_include = apply_filters( 'wpforo_search_by_provider', [], $wpfms, $search_fields_names, $get );
1315 } else {
1316 $users_include = $this->member->search( $wpfms, $search_fields_names );
1317 }
1318 } else {
1319 if( $filters = array_filter( $get, function( $v ) { return ! ( is_null( $v ) || $v === false || $v === '' ); } ) ) {
1320 $filters = array_merge( array_filter( (array) wpfval( $get, 'data' ), function( $v ) { return ! ( is_null( $v ) || $v === false || $v === '' ); } ), $filters );
1321 unset( $filters['data'] );
1322 $args = [];
1323 foreach( $filters as $filter_key => $filter ) {
1324 if( in_array( $filter_key, $search_fields_names ) ) {
1325 $args[ $filter_key ] = $filter;
1326 }
1327 }
1328 if( apply_filters( 'wpforo_member_filter_provider', false, $args, $get ) ) {
1329 $users_include = apply_filters( 'wpforo_filter_by_provider', [], $args, $get );
1330 } else {
1331 $users_include = $this->member->filter( $args );
1332 }
1333 }
1334 }
1335
1336 $users_include = apply_filters( 'wpforo_member_search_users_include', $users_include );
1337 }
1338 $member_status = $this->member->get_inlist_enabled_statuses();
1339 $args = [
1340 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
1341 'row_count' => $current_object['items_per_page'],
1342 'status' => $member_status,
1343 'groupids' => $this->usergroup->get_visible_usergroup_ids(),
1344 ];
1345
1346 $orderby = wpfval( $get, 'wpfob' ) ? sanitize_text_field( $get['wpfob'] ) : wpforo_setting( 'members', 'list_order' );
1347
1348 switch( $orderby ) {
1349 case 'online_time':
1350 $args['orderby'] = 'online_time';
1351 $args['order'] = 'desc';
1352 break;
1353 case 'posts__asc':
1354 $args['orderby'] = 'posts';
1355 $args['order'] = 'ASC';
1356 break;
1357 case 'user_registered__asc':
1358 $args['orderby'] = 'user_registered';
1359 $args['order'] = 'ASC';
1360 break;
1361 case 'user_registered__desc':
1362 $args['orderby'] = 'user_registered';
1363 $args['order'] = 'DESC';
1364 break;
1365 case 'display_name__asc':
1366 $args['orderby'] = 'display_name';
1367 $args['order'] = 'ASC';
1368 break;
1369 case 'display_name__desc':
1370 $args['orderby'] = 'display_name';
1371 $args['order'] = 'DESC';
1372 break;
1373 default:
1374 $args['orderby'] = 'posts';
1375 $args['order'] = 'DESC';
1376 break;
1377 }
1378 if( ! empty( $users_include ) ) $args['include'] = $users_include;
1379 $args = apply_filters( 'wpforo_current_object_members_args_filter', $args, $get );
1380 $current_object['members'] = $this->member->get_members( $args, $current_object['items_count'] );
1381 if( isset( $users_include ) && empty( $users_include ) ) {
1382 $current_object['members'] = [];
1383 $current_object['items_count'] = 0;
1384 }
1385 } elseif( $this->current_object['template'] === 'add-topic' ) {
1386 if( $qvar0 = (int) wpfval( $current_object['qvars'], 0 ) ) {
1387 $forum = (array) $this->forum->get_forum( $qvar0 );
1388 if( ! $forum || (int) wpfval( $forum, 'is_cat' ) ) {
1389 wp_safe_redirect( wpforo_home_url( wpforo_settings_get_slug( 'add-topic' ) ), 301 );
1390 exit();
1391 }
1392 }
1393 }
1394 }
1395
1396 if( wpfval( $current_object, 'userid' ) || wpfval( $current_object, 'user_nicename' ) ) {
1397 $args = [];
1398 if( isset( $current_object['userid'] ) ) $args['userid'] = $current_object['userid'];
1399 if( isset( $current_object['user_nicename'] ) ) $args['user_nicename'] = $current_object['user_nicename'];
1400 $selected_user = $this->member->get_member( $args );
1401 if( isset( $current_object['userid'] ) && empty( $selected_user ) ) $selected_user = $this->member->get_member( [ 'user_nicename' => $current_object['userid'] ] );
1402 $selected_user = apply_filters( 'wpforo_init_current_selected_user', $selected_user );
1403 if( $selected_user ) {
1404 $current_object['user'] = $selected_user;
1405 $current_object['userid'] = $selected_user['userid'];
1406 $current_object['user_nicename'] = $selected_user['user_nicename'];
1407 $current_object['user_is_same_current_user'] = ! empty( $this->current_userid ) && $selected_user['userid'] === $this->current_userid;
1408
1409 if( $this->tpl->can_view_template( $this->current_object['template'], $current_object['user'] ) ) {
1410 switch( $this->current_object['template'] ) {
1411 case 'activity':
1412 $args = [
1413 'offset' => ( $current_object['paged'] - 1 ) * $this->current_object['items_per_page'],
1414 'row_count' => $this->current_object['items_per_page'],
1415 'userid' => $current_object['userid'],
1416 'orderby' => '`created` DESC, `postid` DESC',
1417 'check_private' => true,
1418 'is_first_post' => wpfval( $this->GET, 'filter' ) ? $this->GET['filter'] === 'topics' : null,
1419 ];
1420 $current_object['items_count'] = 0;
1421 $current_object['activities'] = $this->post->get_posts( $args, $current_object['items_count'] );
1422 break;
1423 case 'favored':
1424 $current_object['filter'] = strtolower( (string) wpfval( WPF()->GET, 'filter' ) );
1425 if( ! in_array( $current_object['filter'], [ 'likes', 'dislikes' ], true ) ) $current_object['filter'] = 'bookmarks';
1426 if( $current_object['filter'] === 'likes' ) {
1427 $postids = $this->reaction->get_reactions_col(
1428 'postid',
1429 [
1430 'userid' => $current_object['userid'],
1431 'type_include' => 'up',
1432 ]
1433 );
1434 } elseif( $current_object['filter'] === 'dislikes' ) {
1435 $postids = $this->reaction->get_reactions_col(
1436 'postid',
1437 [
1438 'userid' => $current_object['userid'],
1439 'type_include' => 'down',
1440 ]
1441 );
1442 } else {
1443 $postids = $this->bookmark->get_bookmarks_col(
1444 'postid',
1445 [
1446 'userid' => $current_object['userid'],
1447 'boardid' => $this->board->get_current( 'boardid' ),
1448 'status' => true,
1449 ]
1450 );
1451 }
1452
1453 if( $postids ) {
1454 $args = [
1455 'offset' => ( $current_object['paged'] - 1 ) * $this->current_object['items_per_page'],
1456 'row_count' => $this->current_object['items_per_page'],
1457 'orderby' => '`created` DESC, `postid` DESC',
1458 'check_private' => true,
1459 'include' => $postids,
1460 ];
1461 $current_object['items_count'] = 0;
1462 $current_object['favored_posts'] = $this->post->get_posts( $args, $current_object['items_count'] );
1463 } else {
1464 $current_object['items_count'] = 0;
1465 $current_object['favored_posts'] = [];
1466 }
1467 break;
1468 case 'account':
1469 $this->form->current['template'] = 'account';
1470 $this->form->current['varname'] = 'member';
1471 $this->form->current['value'] = array_merge( $current_object['user'], (array) wpfval( $_POST, 'member' ) );
1472 break;
1473 default:
1474 $this->form->current['template'] = 'profile';
1475 $this->form->current['value'] = $current_object['user'];
1476 break;
1477 }
1478 } else {
1479 if( ! $this->current_userid ) {
1480 wp_safe_redirect( wpforo_login_url() );
1481 exit();
1482 }
1483 $current_object['is_404'] = true;
1484 }
1485
1486 } else {
1487 $current_object['is_404'] = true;
1488 }
1489 }
1490
1491 if( wpfval( $current_object, 'topic_slug' ) ) {
1492 $topic = $this->topic->get_topic( [ 'slug' => $current_object['topic_slug'] ], false );
1493 if( ! empty( $topic ) ) {
1494 $topic_forumid = intval( wpfval( $topic, 'forumid' ) );
1495 $is_owner = wpforo_is_owner( $topic['userid'], $topic['email'] );
1496
1497 if( apply_filters( 'wpforo_current_object_topic_protect', true, $topic ) && $topic_forumid && ( ! $this->perm->forum_can(
1498 'vf',
1499 $topic_forumid
1500 ) || ( ! $is_owner && ! $this->perm->forum_can( 'vt', $topic_forumid ) ) || ( wpfval( $topic, 'private' ) && ! $is_owner && ! $this->perm->forum_can(
1501 'vp',
1502 $topic_forumid
1503 ) ) || ( wpfval( $topic, 'status' ) && ! $is_owner && ! $this->perm->forum_can( 'au', $topic_forumid ) ) ) ) {
1504 if( ! $this->current_userid ) {
1505 wp_safe_redirect( wpforo_login_url() );
1506 exit();
1507 }
1508 $current_object['is_404'] = true;
1509 } else {
1510 $current_object['topic'] = $topic;
1511 $current_object['topicid'] = $topic['topicid'];
1512 $current_object['og_text'] = (string) wpfval( $topic, 'title' );
1513 }
1514 } else {
1515 $current_object['is_404'] = true;
1516 }
1517 }
1518
1519 if( wpfval( $current_object, 'forum_slug' ) ) {
1520 $args = ( empty( $topic ) ? [ 'slug' => $current_object['forum_slug'] ] : $topic['forumid'] );
1521 if( $forum = $this->forum->get_forum( $args ) ) {
1522 if( ! empty( $topic ) && strtolower( (string) $current_object['forum_slug'] ) !== strtolower( (string) $forum['slug'] ) ) {
1523 wp_safe_redirect( $this->topic->get_url( $topic, $forum ), 301 );
1524 exit();
1525 }
1526 if( $forum['is_cat'] ) $this->current_object['template'] = 'forum';
1527 $current_object['forum'] = $forum;
1528 $current_object['forumid'] = $forum['forumid'];
1529 $current_object['forum_desc'] = $forum['description'];
1530 $current_object['forum_meta_key'] = $forum['meta_key'];
1531 $current_object['forum_meta_desc'] = $forum['meta_desc'];
1532 $current_object['og_text'] = $forum['title'];
1533 $current_object['layout'] = $this->forum->get_layout( $forum );
1534
1535 if( $this->current_object['template'] === 'topic' ) {
1536 $current_object['items_per_page'] = wpforo_setting( 'topics', 'topics_per_page' );
1537 $args = [
1538 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
1539 'row_count' => $current_object['items_per_page'],
1540 'forumid' => $current_object['forumid'],
1541 'orderby' => 'type, modified',
1542 'order' => 'DESC',
1543 ];
1544 $args = apply_filters( 'wpforo_topic_list_args', $args );
1545 $current_object['topics'] = $this->topic->get_topics( $args, $current_object['items_count'] );
1546 }
1547 } else {
1548 $current_object['is_404'] = true;
1549 }
1550 }
1551
1552 if( in_array( $this->current_object['template'], [ 'forum', 'topic' ] ) ) {
1553 if( ! empty( $forum ) ) {
1554 $current_object['categories'] = [ $forum ];
1555 } else {
1556 $current_object['categories'] = $this->forum->get_forums( [ "type" => 'category' ] );
1557 }
1558 }
1559
1560 if( $this->current_object['template'] === 'post' && ! empty( $forum ) && ! empty( $topic ) ) {
1561 $current_object['items_per_page'] = $this->post->get_option_items_per_page( $current_object['layout'] );
1562
1563 $args = [
1564 'forumid' => $forum['forumid'],
1565 'topicid' => $topic['topicid'],
1566 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'],
1567 'row_count' => $current_object['items_per_page'],
1568 ];
1569 if( $current_object['layout'] == 4 ) {
1570 $args['parentid'] = 0;
1571 } elseif( $current_object['layout'] == 3 ) {
1572 $args['parentid'] = 0;
1573 switch( $current_object['orderby'] ) {
1574 case 'oldest':
1575 $args['orderby'] = '`is_first_post` DESC, `is_answer` DESC, `created` ASC, `postid` ASC';
1576 break;
1577 case 'newest':
1578 $args['orderby'] = '`is_first_post` DESC, `is_answer` DESC, `modified` DESC, `postid` DESC';
1579 break;
1580 default:
1581 $args['orderby'] = '`is_first_post` DESC, `is_answer` DESC, `votes` DESC, `created` ASC, `postid` ASC';
1582 break;
1583 }
1584 }
1585 if( $this->post->get_option_union_first_post( $current_object['layout'] ) ) $args['union_first_post'] = true;
1586 $args = apply_filters( 'wpforo_post_list_args', $args );
1587 $current_object['posts'] = $this->post->get_posts( $args, $current_object['items_count'] );
1588 }
1589
1590 $this->current_object = wpforo_parse_args( $current_object, $this->current_object );
1591
1592 $this->current_object = apply_filters( 'wpforo_after_init_current_object', $this->current_object, $wpf_url_parse );
1593
1594 if( $this->current_object['template'] ) {
1595 /**
1596 * redirect not logged-in users to login page when that user no access to this page
1597 */
1598 if( ! $this->current_userid && $this->current_object['forumid'] && ( ( in_array( $this->current_object['template'], [ 'forum', 'topic' ] ) && ! $this->perm->forum_can(
1599 'vf',
1600 $this->current_object['forumid']
1601 ) ) || ( $this->current_object['template'] === 'post' && ! $this->perm->forum_can( 'vt', $this->current_object['forumid'] ) ) ) ) {
1602 wp_safe_redirect( wpforo_login_url() );
1603 exit();
1604 }
1605
1606 if( $this->current_object['template'] === 'cantlogin' ) {
1607 if( $this->current_userid ) {
1608 if( $this->current_user['status'] !== 'active' ) {
1609 WPF()->ram_cache->set( 'USER_LOGIN_REFERER', WPF()->current_user_login );
1610 wp_logout();
1611 } else {
1612 wp_safe_redirect( wpforo_home_url() );
1613 }
1614 } else {
1615 wp_safe_redirect( wpforo_login_url() );
1616 exit();
1617 }
1618 }
1619
1620 /**
1621 * redirect to the first page when paged var is greater items_count
1622 */
1623 if( $this->current_object['items_count'] && $this->current_object['paged'] > 1 && ( ( $this->current_object['paged'] - 1 ) * $this->current_object['items_per_page'] ) >= $this->current_object['items_count'] ) {
1624 wp_safe_redirect( $this->strip_url_paged_var( $this->current_url ), 301 );
1625 exit();
1626 }
1627 } else {
1628 $this->current_object['is_404'] = true;
1629 }
1630 }
1631
1632 public function get_version() {
1633 return wpforo_get_option( 'version', null, false );
1634 }
1635
1636 public function need_activation() {
1637 return WPFORO_VERSION !== $this->get_version();
1638 }
1639
1640 public function is_installed() {
1641 return (bool) $this->get_version();
1642 }
1643
1644 public function can_use_this_slug( $slug ) {
1645 $return = ! in_array( $slug, $this->settings->slugs, true ) && ! in_array( $slug, array_keys( $this->settings->slugs ), true );
1646
1647 return apply_filters( 'wpforo_can_use_this_slug', $return, $slug );
1648 }
1649 }
1650