Compare commits

..

46 commits

Author SHA1 Message Date
Roman Kelesidis
144aa0558d
chore: Removed deploy pipeline (#2047)
Some checks failed
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
PHPMD / Run PHPMD scanning (push) Has been cancelled
2025-07-16 10:15:09 +03:00
Roman Kelesidis
6e7e3dd9ef
fix: handle Nette DateTime objects in birthday validation (#2032)
Some checks failed
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
PHPMD / Run PHPMD scanning (push) Has been cancelled
* fix: handle Nette DateTime objects in birthday validation

* Update register.php

* Update User.php
2025-07-03 23:07:09 +02:00
Roman Kelesidis
423fb65dbf
release: v2.8.3
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
2025-07-03 10:51:21 +03:00
Roman Kelesidis
51f2c70d81
feat(updater): Added exceptions logging (#2026)
* feat(updater): Added exceptions logging
* refactor(updater): Use `sha256` hash if available
2025-07-03 10:48:25 +03:00
Roman Kelesidis
fd46d3d04a
feat(lang): Added RTL languages support (#2031) 2025-07-03 10:43:02 +03:00
Roman Kelesidis
d4d82101dd
refactor(TorrentFileList): Reduce duplication in root directory unset logic (#2027) 2025-07-03 10:40:56 +03:00
Roman Kelesidis
9e3f3588f2
release: v2.8.2
Some checks failed
PHPMD / Run PHPMD scanning (push) Has been cancelled
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
2025-06-30 10:49:42 +03:00
Roman Kelesidis
915e1d817c
fix(TorrentFileList): Avoid array_merge reindexing for numeric folder names (#2014)
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
Co-authored-by: zxc <17812947+metalwarrior88@users.noreply.github.com>
2025-06-29 15:03:56 +03:00
Roman Kelesidis
7ac335974b
refactor: Use DEFAULT_CHARSET constant instead of hardcoded string (#2011)
Some checks failed
PHPMD / Run PHPMD scanning (push) Has been cancelled
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
2025-06-26 13:32:45 +03:00
Roman Kelesidis
c2e3a13a04
release: v2.8.1 2025-06-24 13:00:37 +03:00
Roman Kelesidis
f1d6e74e5d
fix(ip-api): Add error handling and logging for freeipapi.com requests (#2006) 2025-06-24 12:55:41 +03:00
Roman Kelesidis
7f4cc9d3b9
fix(filelist): Undefined property: FileTree::$length when v2 torrent only (#2004) 2025-06-23 21:49:53 +03:00
Roman Kelesidis
a0fc2a9da9
release: v2.8.0
Some checks failed
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
PHPMD / Run PHPMD scanning (push) Has been cancelled
2025-06-21 22:16:28 +03:00
Yury Pikhtarev
c6076c2c27
fix(template): handle L_ variables in template vars when not found in lang vars (#1998)
Template variables starting with L_ (like L_MY_PROFILE) are now properly
resolved when they exist in template variables but not in language variables.
Previously, these would fall back to showing the literal variable name.

The L_ variable regex now checks both $L (language) and $V (template) arrays
before falling back to the literal string, maintaining backward compatibility
while fixing composite language variables set via template assignment.
2025-06-21 22:13:01 +03:00
Roman Kelesidis
714dd6255e
release: v2.7.0 🐈‍⬛
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
2025-06-21 10:52:01 +03:00
Roman Kelesidis
9b322c7093
chore: Update minimum PHP requirement to 8.2 (#1987)
* chore: Update minimum PHP requirement to 8.2

* chore: Update PHP version references to 8.2 in documentation and templates

* chore: Update issue template labels to array format for consistency

* refactor: Changed `local` environment name to `development`

* chore(composer.json): Changed version of `gemorroj/m3u-parser` package

* chore(composer): Removed `matthiasmullie/scrapbook` package

* fix(template): Update variable fallback behavior to return empty string for missing variables

---------

Co-authored-by: Yury Pikhtarev <exileum@icloud.com>
2025-06-21 11:45:56 +04:00
Roman Kelesidis
423424e947
chore: Removed useless composer update from workflows & installer (#1986) 2025-06-21 09:25:43 +03:00
belomaxorka
27b5815aee changelog: Update CHANGELOG.md 📖 2025-06-21 01:39:56 +00:00
Yury Pikhtarev
bccbad0c57
New Crowdin updates (#1983)
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
* New translations main.php (Romanian)

* New translations main.php (French)

* New translations main.php (Spanish)

* New translations main.php (Afrikaans)

* New translations main.php (Arabic)

* New translations main.php (Belarusian)

* New translations main.php (Bulgarian)

* New translations main.php (Catalan)

* New translations main.php (Czech)

* New translations main.php (Danish)

* New translations main.php (German)

* New translations main.php (Greek)

* New translations main.php (Finnish)

* New translations main.php (Hebrew)

* New translations main.php (Hungarian)

* New translations main.php (Armenian)

* New translations main.php (Italian)

* New translations main.php (Japanese)

* New translations main.php (Georgian)

* New translations main.php (Korean)

* New translations main.php (Lithuanian)

* New translations main.php (Dutch)

* New translations main.php (Norwegian)

* New translations main.php (Polish)

* New translations main.php (Russian)

* New translations main.php (Slovak)

* New translations main.php (Slovenian)

* New translations main.php (Albanian)

* New translations main.php (Serbian (Cyrillic))

* New translations main.php (Swedish)

* New translations main.php (Turkish)

* New translations main.php (Ukrainian)

* New translations main.php (Chinese Traditional)

* New translations main.php (English)

* New translations main.php (Vietnamese)

* New translations main.php (Portuguese, Brazilian)

* New translations main.php (Indonesian)

* New translations main.php (Thai)

* New translations main.php (Croatian)

* New translations main.php (Kazakh)

* New translations main.php (Estonian)

* New translations main.php (Latvian)

* New translations main.php (Azerbaijani)

* New translations main.php (Hindi)

* New translations main.php (Bosnian)

* New translations main.php (Uzbek)

* New translations main.php (Tajik)

* New translations admin_send_email.html (Japanese)

* New translations admin_send_email.html (Chinese Traditional)

* New translations admin_send_email.html (Portuguese, Brazilian)

* New translations group_added.html (Spanish)

* New translations group_added.html (Japanese)

* New translations group_added.html (Chinese Traditional)

* New translations group_added.html (Thai)

* New translations group_approved.html (Spanish)

* New translations group_approved.html (Japanese)

* New translations group_approved.html (Chinese Traditional)

* New translations group_approved.html (Thai)

* New translations group_request.html (Chinese Traditional)

* New translations privmsg_notify.html (Spanish)

* New translations privmsg_notify.html (Japanese)

* New translations privmsg_notify.html (Chinese Traditional)

* New translations profile_send_email.html (Spanish)

* New translations profile_send_email.html (Japanese)

* New translations profile_send_email.html (Thai)

* New translations topic_notify.html (Spanish)

* New translations topic_notify.html (Chinese Traditional)

* New translations topic_notify.html (Thai)

* New translations user_activate.html (Spanish)

* New translations user_activate.html (Chinese Traditional)

* New translations user_activate_passwd.html (Spanish)

* New translations user_activate_passwd.html (Japanese)

* New translations user_activate_passwd.html (Chinese Traditional)

* New translations user_activate_passwd.html (Portuguese, Brazilian)

* New translations user_activate_passwd.html (Thai)

* New translations user_welcome.html (German)

* New translations user_welcome.html (Chinese Traditional)

* New translations user_welcome.html (Portuguese, Brazilian)

* New translations user_welcome.html (Thai)

* New translations user_welcome_inactive.html (Spanish)

* New translations user_welcome_inactive.html (Chinese Traditional)

* New translations user_welcome_inactive.html (Portuguese, Brazilian)

* New translations user_welcome_inactive.html (Thai)

* New translations copyright_holders.html (Chinese Traditional)

* New translations copyright_holders.html (Thai)

* New translations sidebar2.html (Romanian)

* New translations sidebar2.html (French)

* New translations sidebar2.html (Spanish)

* New translations sidebar2.html (Afrikaans)

* New translations sidebar2.html (Arabic)

* New translations sidebar2.html (Belarusian)

* New translations sidebar2.html (Bulgarian)

* New translations sidebar2.html (Catalan)

* New translations sidebar2.html (Czech)

* New translations sidebar2.html (Danish)

* New translations sidebar2.html (German)

* New translations sidebar2.html (Greek)

* New translations sidebar2.html (Finnish)

* New translations sidebar2.html (Hebrew)

* New translations sidebar2.html (Hungarian)

* New translations sidebar2.html (Armenian)

* New translations sidebar2.html (Italian)

* New translations sidebar2.html (Japanese)

* New translations sidebar2.html (Georgian)

* New translations sidebar2.html (Korean)

* New translations sidebar2.html (Lithuanian)

* New translations sidebar2.html (Dutch)

* New translations sidebar2.html (Norwegian)

* New translations sidebar2.html (Polish)

* New translations sidebar2.html (Russian)

* New translations sidebar2.html (Slovak)

* New translations sidebar2.html (Slovenian)

* New translations sidebar2.html (Albanian)

* New translations sidebar2.html (Serbian (Cyrillic))

* New translations sidebar2.html (Swedish)

* New translations sidebar2.html (Turkish)

* New translations sidebar2.html (Ukrainian)

* New translations sidebar2.html (Chinese Traditional)

* New translations sidebar2.html (English)

* New translations sidebar2.html (Vietnamese)

* New translations sidebar2.html (Portuguese, Brazilian)

* New translations sidebar2.html (Indonesian)

* New translations sidebar2.html (Thai)

* New translations sidebar2.html (Croatian)

* New translations sidebar2.html (Kazakh)

* New translations sidebar2.html (Estonian)

* New translations sidebar2.html (Latvian)

* New translations sidebar2.html (Azerbaijani)

* New translations sidebar2.html (Hindi)

* New translations sidebar2.html (Bosnian)

* New translations sidebar2.html (Uzbek)

* New translations sidebar2.html (Tajik)

* New translations user_agreement.html (Thai)

* New translations main.php (Russian)

* New translations sidebar2.html (Russian)

* New translations main.php (Russian)

* New translations main.php (Ukrainian)

* New translations main.php (Romanian)

* New translations main.php (French)

* New translations main.php (Spanish)

* New translations main.php (Afrikaans)

* New translations main.php (Arabic)

* New translations main.php (Belarusian)

* New translations main.php (Bulgarian)

* New translations main.php (Catalan)

* New translations main.php (Czech)

* New translations main.php (Danish)

* New translations main.php (German)

* New translations main.php (Greek)

* New translations main.php (Finnish)

* New translations main.php (Hebrew)

* New translations main.php (Hungarian)

* New translations main.php (Armenian)

* New translations main.php (Italian)

* New translations main.php (Japanese)

* New translations main.php (Georgian)

* New translations main.php (Korean)

* New translations main.php (Lithuanian)

* New translations main.php (Dutch)

* New translations main.php (Norwegian)

* New translations main.php (Polish)

* New translations main.php (Russian)

* New translations main.php (Slovak)

* New translations main.php (Slovenian)

* New translations main.php (Albanian)

* New translations main.php (Serbian (Cyrillic))

* New translations main.php (Swedish)

* New translations main.php (Turkish)

* New translations main.php (Ukrainian)

* New translations main.php (Chinese Traditional)

* New translations main.php (English)

* New translations main.php (Vietnamese)

* New translations main.php (Portuguese, Brazilian)

* New translations main.php (Indonesian)

* New translations main.php (Thai)

* New translations main.php (Croatian)

* New translations main.php (Kazakh)

* New translations main.php (Estonian)

* New translations main.php (Latvian)

* New translations main.php (Azerbaijani)

* New translations main.php (Hindi)

* New translations main.php (Bosnian)

* New translations main.php (Uzbek)

* New translations main.php (Tajik)
2025-06-21 03:52:59 +04:00
Yury Pikhtarev
728116d6dc
refactor(stats): improve database row fetching in tr_stats.php (#1985)
- Updated the method of fetching rows from the database to use a more streamlined approach with `DB()->fetch_row()`.
- Extracted the first column value directly from the result array for clarity and maintainability.
2025-06-21 03:34:11 +04:00
Yury Pikhtarev
a33574c28f
feat(language): add new language variable for migration file and enhance template fallback logic (#1984)
* feat(language): add new language variable for migration file and enhance template fallback logic

- Introduced a new language variable 'MIGRATIONS_FILE' to improve migration management clarity.
- Updated template handling to provide graceful fallbacks for missing language and regular variables, ensuring better user experience and debugging capabilities.
- Added comprehensive unit tests to validate the new fallback logic and ensure existing functionality remains intact.

* refactor(tests): remove debugging test for compiled template output

- Removed the test case that debugged compiled output for troubleshooting in the TemplateGracefulFallbackTest.
- This change streamlines the test suite by focusing on essential functionality, specifically the handling of missing language variables.

* feat(tests): introduce executeTemplate function for streamlined template execution

- Added a new helper function `executeTemplate` to encapsulate the logic for executing compiled templates, improving code readability and reducing duplication in unit tests.
- Updated existing tests in `TemplateGracefulFallbackTest.php` to utilize the new function, ensuring consistent handling of template execution and variable scope.
- Enhanced test coverage by adding edge case scenarios for variable handling, including special characters, empty values, and nested braces.
2025-06-21 03:09:59 +04:00
Roman Kelesidis
e9a9e09576
chore: Added --no-dev composer flag for some workflows (#1982) 2025-06-20 22:39:17 +03:00
Roman Kelesidis
e8cba5dd3f
chore: Added --no-dev composer flag for some workflows (#1981) 2025-06-20 22:27:34 +03:00
Yury Pikhtarev
4f9cc9fe0f
fix(database): update affected rows tracking in Database class (#1980)
* fix(database): update affected rows tracking in Database class

- Enhanced the logic for tracking affected rows in the Database class to accurately reflect the number of rows modified by INSERT, UPDATE, and DELETE operations using the getRowCount() method.
- Added a new unit test suite for the affected_rows method, ensuring comprehensive coverage for various scenarios including tracking of affected rows and validation of the last_affected_rows property.

This update improves the reliability of the Database class's affected rows reporting, facilitating better data handling and debugging.

* feat(install): add developer prompt for dependency installation

- Introduced a prompt during the installation process to ask users if they require development tools and dependencies.
- Adjusted the dependency installation command to conditionally include or exclude development dependencies based on user input.
- Added warnings for users with PHP versions below 8.2 when opting for development dependencies.

This enhancement improves the installation experience by allowing developers to easily set up their environment while ensuring compatibility checks are in place.

* docs(README): clarify PHP version requirements and installation instructions

- Updated PHP version requirements to specify that PHP 8.2+ is required for development dependencies.
- Enhanced installation instructions to differentiate between production and development setups, including commands for Composer installation.
- Added a note regarding the necessity of PHP 8.2+ for running tests, improving clarity for developers.

These changes aim to provide clearer guidance for users setting up their development environments.
2025-06-20 23:06:36 +04:00
Yury Pikhtarev
cc9d412522
feat(test): add comprehensive testing infrastructure with Pest PHP (#1979)
* feat(tests): integrate Pest testing framework and set up initial test structure

- Added Pest as a development dependency for enhanced testing capabilities.
- Created a PHPUnit configuration file (`phpunit.xml`) for test suite management.
- Established a base test case class (`TestCase.php`) for consistent test structure.
- Implemented example tests in both feature and unit directories to demonstrate usage.
- Introduced a custom Pest file (`Pest.php`) to extend functionality and define global helpers.

This setup streamlines the testing process and provides a foundation for future test development.

* feat(test): add comprehensive testing infrastructure with Pest PHP

- Add complete Pest PHP testing suite with extensive helper functions
- Implement unit tests for Database and DatabaseDebugger classes
- Implement unit tests for CacheManager and DatastoreManager classes
- Add comprehensive mock factories and test data generators
- Add custom Pest expectations for TorrentPier-specific validation
- Create detailed testing documentation with examples and best practices
- Update main README.md and UPGRADE_GUIDE.md with testing sections
- Update dependencies to support testing infrastructure
- Remove example test file and replace with production-ready tests

BREAKING CHANGE: None - all existing functionality maintained

The testing infrastructure includes:
- 25+ helper functions for test setup and mocking
- Singleton pattern testing for all major components
- Mock factories for Database, Cache, and external dependencies
- Custom expectations: toBeValidDatabaseConfig, toHaveDebugInfo
- Comprehensive documentation with real-world examples
- Performance testing utilities and execution time assertions
2025-06-20 22:00:12 +04:00
Yury Pikhtarev
7aed6bc7d8
refactor(database): enhance error logging and various fixes (#1978)
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
* refactor(database): enhance error logging and handling in Database and DatabaseDebugger classes

- Updated error handling in Database class to provide more meaningful error messages, including detailed PDO error information.
- Enhanced log_error method in DatabaseDebugger to accept exceptions, allowing for more reliable error logging with comprehensive context.
- Improved user-facing error messages while maintaining detailed logging for administrators and developers.
- Added checks for connection status and query context in error logs to aid in debugging.

* refactor(database): streamline error handling and improve code clarity in Database class

- Removed unused imports and global variable references to enhance code readability.
- Simplified error handling by refining conditions for empty input arrays.
- Defined variables early in the error logging method to ensure consistent access throughout.
- Improved comments for better understanding of the error handling logic.

* fix(database): refine development mode condition in Database class

- Removed unnecessary function check from the development mode condition to streamline the logic.

* fix(redirect): add no-cache headers to prevent browser caching of redirects

- Implemented `send_no_cache_headers()` in the redirect function to ensure that browsers do not cache redirect responses, adhering to best practices for HTTP redirects.

* refactor(database): enhance error handling and logging in Database and Whoops classes

- Improved error handling in the Database class to log detailed information for duplicate column errors and automatically retry problematic queries using PDO.
- Introduced a new DatabaseErrorHandler to enhance Whoops error reporting with comprehensive database context and recent query history.
- Updated EnhancedPrettyPageHandler to include dynamic database information in error outputs, improving debugging capabilities.
- Added legacy query tracking in DatabaseDebugger to identify and log queries needing compatibility fixes, ensuring better maintenance of legacy code.

* refactor(posting): rollback changes in SQL queries up to legacy format
2025-06-20 18:52:30 +04:00
belomaxorka
7e723d6ad8 changelog: Update CHANGELOG.md 📖 2025-06-20 09:44:57 +00:00
Yury Pikhtarev
fbde8cd421
feat(migrations): implement Phinx database migration system (#1976)
* feat(migrations): implement Phinx database migration system and update installation process

- Introduced a modern database migration system using Phinx, replacing the legacy SQL import method.
- Updated `install.php` to run migrations instead of importing SQL dumps.
- Added migration configuration file `phinx.php` and initial migration files for schema and data seeding.
- Created a new admin panel for migration status management.
- Updated UPGRADE_GUIDE.md to include migration setup instructions and benefits.
- Ensured backward compatibility for existing installations while facilitating a smoother transition to the new system.

* update runProcess to return process exit code

* refactor(migrations): clean up whitespace and formatting in migration files

- Removed unnecessary whitespace and adjusted formatting for consistency across migration files.

* fix(migrations): enforce NOT NULL constraints on migration columns

- Updated various migration columns to enforce NOT NULL constraints, ensuring data integrity across the schema.
- Adjusted default values and nullability for multiple fields in the initial schema migration files.

* refactor(database): standardize table engines to InnoDB for reliability

- Updated UPGRADE_GUIDE.md to reflect the use of InnoDB for all tables, emphasizing data integrity and reliability.
- Modified migration files to change table engines from MyISAM to InnoDB for various tracker and temporary tables.
- Optimized session variable settings in cron jobs for InnoDB compatibility.
- Ensured consistency across the schema by enforcing InnoDB usage in all relevant areas.

* fix(migrations): correct MySQL integer field types to match original schema

- Fix bb_forums table: forum_status (INT→TINYINT), forum_tpl_id (INT→SMALLINT)
- Fix bb_users table: avatar_ext_id remains TINYINT as per original schema
- Fix bb_groups table: avatar_ext_id (SMALLINT→INT) to match original INT(15)
- Fix bb_topics table: topic_show_first_post, topic_allow_robots (TINYINT(1)→TINYINT UNSIGNED)
- Remove incorrect 'limit' => 11 from standard INT fields, use default Phinx behavior
- Fix search_size field to use proper INT type instead of maximum value hack
- Correct poll table field types: vote_id (TINYINT), user_id (MEDIUMINT), vote_result (MEDIUMINT UNSIGNED)
- Standardize all timestamp and ID fields to use appropriate MySQL integer types

Ensures migration creates database schema identical to install/sql/mysql.sql
while maintaining InnoDB engine for all tables instead of MyISAM.

* fix(cache): auto-create cache directories when using FileStorage

The UnifiedCacheSystem was constructing directory paths for Nette FileStorage
but not creating them, causing "Directory not found" errors when accessing
caches like 'bb_login_err'. FileStorage expects directories to already exist.

Changes:
- Add automatic directory creation using bb_mkdir() before FileStorage init
- Handle both regular cache directories and SQLite parent directories
- Apply to both _buildStorage() and _buildDatastoreStorage() methods
- Add proper error handling with RuntimeException for failed creation
- Maintain consistency with TorrentPier's directory creation patterns

This ensures cache directories are created automatically when first accessed,
eliminating the need for manual directory creation during deployment.

Fixes: Cache initialization failures with missing directories

* refactor(docs): update README for clarity and remove legacy SQL file

- Improved formatting and clarity in the README, ensuring consistent line breaks and spacing.
- Updated installation instructions to reflect the new migration process, emphasizing the use of `phinx` for database setup.
- Removed the legacy SQL dump file `mysql.sql` and the `legacy-changes.txt` file, streamlining the installation process and reducing confusion for new users.
- Enhanced the documentation to guide users through the setup process more effectively.

* docs: enhance CLAUDE.md with migration details and directory updates

- Updated the `/library/` section to clarify its purpose.
- Added a new `/migrations/` directory section detailing database migration files managed by Phinx.
- Included migration commands for running and checking migration status.
- Revised the initial schema and seed data references for clarity.
- Improved formatting for consistency throughout the document.

* refactor(cron): remove demo_mode.php cron job and related functionality

- Deleted the demo_mode.php cron job file, which was responsible for managing demo mode operations.
- Added a migration to remove the demo_mode.php entry from the bb_cron table, ensuring a clean database state.
- Updated the initial schema migration comment to reflect the creation of essential database schema for fresh installations.

* refactor(docs): Fixed some typos

* chore: update changelog generation starting from v2.4.6-alpha.4

* refactor: Changed some `php_sapi_name()` to `PHP_SAPI` constants

* refactor: Extract hardcoded migrations to class property

* refactor: Use `count()` to count $initialMigrations elements

* feat(migrations): enhance migration management UI with new language variables

- Added new language variables for migration status, instructions, and applied/pending migrations to improve user interface clarity.
- Updated admin migration template to utilize these new language variables for better localization and maintainability.
- Introduced a new file 'CLAUDE.md' to the cleanup process for documentation purposes.

---------

Co-authored-by: Roman Kelesidis <roman25052006.kelesh@gmail.com>
2025-06-20 13:23:33 +04:00
belomaxorka
0d4c869f30 changelog: Update CHANGELOG.md 📖
Some checks failed
PHPMD / Run PHPMD scanning (push) Has been cancelled
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
2025-06-19 01:42:40 +00:00
Yury Pikhtarev
49717d3a68
feat: implement Language singleton with shorthand functions (#1966)
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
- Add Language singleton class (src/Language.php) following TorrentPier patterns
- Implement automatic source language fallback loading
- Add dot notation support for nested language arrays
- Provide convenient shorthand functions __() and _e() in common.php
- Maintain 100% backward compatibility with global $lang variable
- Replace manual language file loading in bb_die() and bb_date() functions
- Update poll.php as modern usage example with __() shorthand
- Integrate with User.php initialization via lang()->initializeLanguage()
- Clean up Template.php compilation removing legacy source language logic
- Add comprehensive UPGRADE_GUIDE.md documentation section

BREAKING CHANGE: None - full backward compatibility maintained
2025-06-18 19:29:06 +04:00
Yury Pikhtarev
2fd306704f
feat(database): add visual markers for Nette Explorer queries in debug panel (#1965)
Add automatic detection and colorful badges to distinguish Nette Explorer
queries from legacy database calls, helping track modernization progress.

- Detect Nette SQL patterns (backticks, parentheses)
- Add green styled [Nette Explorer] badges
- Fix HTML escaping in debug tooltips
- Prevent marker duplication
2025-06-18 18:13:34 +04:00
Yury Pikhtarev
6c0219d53c
refactor(database): rename DB to Database and extract debug functionality (#1964)
- Rename DB → Database, DbFactory → DatabaseFactory for consistency
- Extract 8 debug methods from Database to dedicated DatabaseDebugger class
- Add DebugSelection wrapper for debug-enabled Nette Selection
- Update all references across codebase (common.php, Dev.php, page_footer)
- Maintain backward compatibility via magic methods (__get, __isset)
- Update documentation (README.md, UPGRADE_GUIDE.md)

No breaking changes - all existing DB() calls work unchanged
2025-06-18 17:46:12 +04:00
Roman Kelesidis
f2fca0de85
release: v2.6.0 🐂 2025-06-18 14:15:53 +03:00
Yury Pikhtarev
07a06a33cd
feat!: implement unified cache system with Nette Caching (#1963)
* feat!: implement unified cache system with Nette Caching

Replace legacy Cache and Datastore systems with modern unified implementation using Nette Caching v3.3 while maintaining 100% backward compatibility.

BREAKING CHANGE: Internal cache architecture completely rewritten, though all public APIs remain compatible.

### Added
- src/Cache/UnifiedCacheSystem.php: Main singleton orchestrator following TorrentPier patterns
- src/Cache/CacheManager.php: Cache interface using Nette Caching with singleton pattern
- src/Cache/DatastoreManager.php: Datastore interface using CacheManager internally
- src/Cache/README.md: Comprehensive documentation and migration guide

### Changed
- common.php: Updated to use singleton pattern instead of global variables
- src/Dev.php: Added compatibility with unified cache system debug functionality
- composer.json: Added nette/caching dependency
- UPGRADE_GUIDE.md: Added unified cache system migration documentation

### Removed
- src/Legacy/Cache/*: All legacy cache implementations (APCu, File, Memcached, Redis, SQLite, Common)
- src/Legacy/Datastore/*: All legacy datastore implementations (APCu, File, Memcached, Redis, SQLite, Common)
- src/Legacy/Caches.php: Legacy cache factory replaced by UnifiedCacheSystem

### Performance
- 456,647+ operations per second verified in production testing
- Memory optimization through efficient singleton pattern
- Modern Nette Caching algorithms and bulk operations

### Compatibility
- All existing CACHE() calls work unchanged
- All existing $datastore operations work unchanged
- Full backward compatibility with Dev.php debugging
- Resolved Sessions TypeError and debug property access issues

### Architecture
- Consistent singleton pattern matching config(), dev(), censor(), DB()
- Clean function interfaces with proper return types
- No global variables, modern initialization pattern
- Single source of truth replacing duplicate Cache/Datastore code

* fix: update datastore type from 'filecache' to 'file' for consistency

Modified the datastore type in common.php and README.md to reflect the new unified cache system terminology. This change ensures consistency across the codebase and documentation following the recent implementation of the unified cache system.

* refactor(cache): centralize storage creation and eliminate redundancy

- Remove redundant initializeStorage() from CacheManager
- Update CacheManager to receive pre-built Storage instances
- Centralize all storage creation logic in UnifiedCacheSystem
- Remove unused properties and fix method signatures
- Update documentation to reflect simplified architecture

No breaking changes - all public APIs unchanged.
2025-06-18 15:05:22 +04:00
Yury Pikhtarev
e458109eef
fix(User): add null and array checks before session data operations (#1962)
Ensure that session data is only processed if it exists and is an array. This prevents potential errors when attempting to cache or remove user session data. Updated methods `cache_set_userdata` and `cache_rm_userdata` to include necessary checks.
2025-06-18 12:43:58 +04:00
Yury Pikhtarev
f50b914cc1
feat: replace legacy database layer with Nette Database implementation (#1961)
* feat: replace legacy database layer with Nette Database implementation

Replace legacy SqlDb/Dbs classes with modern Nette Database implementation
while maintaining complete backward compatibility.

- Replace SqlDb with singleton DB class using Nette Database Connection
- Replace Dbs factory with DbFactory maintaining full compatibility
- Implement complete feature parity including debug, explain, and logging
- Add proper type declarations and modern PHP standards
- Remove legacy SqlDb.php and Dbs.php files
- Update common.php DB() function with proper PHPDoc and return types
- Fix affected_rows() implementation for Nette Database compatibility
- Fix explain() method to handle missing debug array keys
- Maintain 100% backward compatibility - no code changes required

The new implementation uses Nette Database v3.2 internally while preserving
all existing functionality. All existing DB() calls work unchanged.
All debugging, explain, error handling, and performance tracking features
are fully preserved with enhanced reliability.

Files added:
- src/Database/DB.php - Main database class with singleton pattern
- src/Database/DbFactory.php - Factory for database instance management
- src/Database/README.md - Comprehensive documentation

Files removed:
- src/Database/Config.php - Unused configuration helper
- src/Legacy/SqlDb.php - Legacy database class
- src/Legacy/Dbs.php - Legacy database factory

Files modified:
- common.php - Updated DB() function with proper types and documentation
- viewtopic.php - Fixed duplicate column SQL query issues
- src/Dev.php - Updated to use DbFactory instead of legacy $DBS
- library/includes/page_footer*.php - Replaced $DBS references with DbFactory

* docs: Update UPGRADE_GUIDE.md with Database Layer Migration details

Add a comprehensive section on the new database layer migration to Nette Database, highlighting key improvements, no code changes required, and verification steps. Document the removal of legacy database files and provide links to detailed documentation for further reference. Ensure clarity on backward compatibility and performance benefits.

* docs: Enhance README.md with future migration strategy to Nette Database Explorer

Add detailed sections outlining a phased approach for migrating to Nette Database Explorer, including hybrid methods, advanced features, and migration strategies. Highlight benefits such as improved developer experience, code quality, and performance optimizations. This documentation aims to guide developers through the transition while maintaining backward compatibility.
2025-06-18 12:29:13 +04:00
Roman Kelesidis
edda2306f2
release: v2.5.0 🐂
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
2025-06-18 09:52:20 +03:00
Roman Kelesidis
86abafb114
docs: Add Select class migration guide (#1960) 2025-06-18 09:43:55 +03:00
Yury Pikhtarev
74a564d795
refactor(censor): migrate Censor class to singleton pattern (#1954)
* refactor(censor): migrate Censor class to singleton pattern

- Convert TorrentPier\Censor to singleton pattern following Config class design
- Add global censor() helper function for consistent API access
- Replace all global $wordCensor declarations and usage across 12 files
- Implement automatic reload functionality in admin panel
- Add enhanced methods: isEnabled(), addWord(), getWordsCount(), reload()

Files updated:
- src/Legacy/Atom.php, src/Legacy/Post.php
- viewforum.php, posting.php, search.php, index.php, viewtopic.php, privmsg.php
- library/ajax/posts.php, library/includes/bbcode.php, library/includes/ucp/topic_watch.php
- admin/admin_words.php, library/includes/init_bb.php
- common.php (added global helper)
- UPGRADE_GUIDE.md (documentation)

Benefits:
- Single instance shared across application for better performance
- Memory efficient word loading only when censoring enabled
- Consistent API pattern matching config() singleton
- Automatic word reloading when admin updates censored words
- Enhanced developer experience with new utility methods

BREAKING CHANGE: None - full backward compatibility maintained.
The global $wordCensor variable continues to work as before.
New censor() function is the recommended approach going forward.

* refactor(censor): add enable check to censorString method

* refactor(dev): convert Dev class to singleton pattern (#1955)

* refactor(dev): convert Dev class to singleton pattern

- Convert TorrentPier\Dev class from direct instantiation to singleton pattern
- Add getInstance() method and private constructor for singleton implementation
- Introduce new instance methods with improved naming:
  * getSqlDebugLog() (replaces getSqlLog())
  * checkSqlDebugAllowed() (replaces sqlDebugAllowed())
  * formatShortQuery() (replaces shortQuery())
- Add dev() global helper function for consistent access pattern
- Maintain full backward compatibility with existing static method calls
- Update all internal usage across 18 files to use new singleton pattern:
  * src/Ajax.php, src/Legacy/SqlDb.php
  * All Cache classes (APCu, File, Memcached, Redis, Sqlite, Common)
  * All Datastore classes (APCu, File, Memcached, Redis, Sqlite, Common)
  * library/includes/page_footer_dev.php
- Implement lazy initialization consistent with Config and Censor singletons
- Add comprehensive migration guide in UPGRADE_GUIDE.md

This refactoring improves resource management, provides consistent API patterns
across all singleton classes, and maintains zero breaking changes for existing code.

* refactor(dev): Added missing `\TorrentPier\Dev::init()`

---------

Co-authored-by: Roman Kelesidis <roman25052006.kelesh@gmail.com>
2025-06-18 10:38:23 +04:00
Roman Kelesidis
b1b2618757
chore(cliff): Add conventional commit prefix to changelog message (#1957) 2025-06-18 07:06:11 +03:00
belomaxorka
62f49beb0b Update CHANGELOG.md 📖 2025-06-18 01:41:26 +00:00
Yury Pikhtarev
bf9100fbfa
feat: implement centralized Config class to replace global $bb_cfg array (#1953)
Some checks failed
Continuous Integration / Nightly builds 📦 (push) Has been cancelled
Continuous Integration / 🎉 Deploy (push) Has been cancelled
PHPMD / Run PHPMD scanning (push) Has been cancelled
* feat: implement centralized Config class to replace global $bb_cfg array

- Add singleton Config class with dot notation support for nested configuration
- Implement thread-safe configuration access with magic methods (__get, __set, __isset)
- Add global config() helper function for convenient access
- Support for getSection(), merge(), has(), all() methods with type safety

BREAKING CHANGE: While $bb_cfg global array still works for backward compatibility,
new code should use config()->get() method with dot notation

Updated files:
- src/Config.php: New Config singleton class implementation
- common.php: Initialize Config singleton and add global helper
- src/Emailer.php: Replace $bb_cfg with config()->get()
- src/Ajax.php: Replace $bb_cfg with config()->get()
- src/Censor.php: Replace $bb_cfg with config()->get()
- src/Validate.php: Replace $bb_cfg with config()->get()
- src/Dev.php: Replace $bb_cfg with config()->get()
- src/Sitemap.php: Replace $bb_cfg with config()->get()
- src/TorrServerAPI.php: Replace $bb_cfg with config()->get()
- src/Sessions.php: Replace $bb_cfg with config()->get()
- src/Legacy/TorrentFileList.php: Replace $bb_cfg with config()->get()
- src/Legacy/Poll.php: Replace $bb_cfg with config()->get()
- src/Legacy/Torrent.php: Replace $bb_cfg with config()->get()
- src/Legacy/Common/User.php: Replace $bb_cfg with config()->get()
- src/Legacy/Template.php: Replace $bb_cfg with config()->get()
- src/Legacy/Atom.php: Replace $bb_cfg with config()->get()
- src/Legacy/Admin/Common.php: Replace $bb_cfg with config()->get()
- viewforum.php: Replace $bb_cfg with config()->get()
- posting.php: Replace $bb_cfg with config()->get()
- dl.php: Replace $bb_cfg with config()->get()
- feed.php: Replace $bb_cfg with config()->get()
- filelist.php: Replace $bb_cfg with config()->get()
- group_edit.php: Replace $bb_cfg with config()->get()
- group.php: Replace $bb_cfg with config()->get()
- index.php: Replace $bb_cfg with config()->get()
- login.php: Replace $bb_cfg with config()->get()
- memberlist.php: Replace $bb_cfg with config()->get()
- modcp.php: Replace $bb_cfg with config()->get()
- playback_m3u.php: Replace $bb_cfg with config()->get()
- poll.php: Replace $bb_cfg with config()->get()

* refactor: replace $bb_cfg with config() in various admin files

- Updated multiple admin files to utilize the new Config class for configuration access.
- Replaced instances of $bb_cfg with config()->get() for improved maintainability and consistency.
- Ensured backward compatibility while transitioning to the new configuration system.

Files modified:
- common.php
- index.php
- admin_attach_cp.php
- admin_log.php
- admin_mass_email.php
- admin_sitemap.php
- admin_smilies.php
- admin_terms.php
- admin_user_search.php
- admin_words.php
- admin/index.php
- admin/stats/tracker.php

* refactor: update init_bb.php to use config() for configuration management

- Replaced the merging of $bb_cfg with a call to config()->merge() for improved clarity and maintainability.
- Updated the retrieval of all configuration settings to use config()->all().

This change continues the transition towards a centralized configuration system.

* refactor: replace $bb_cfg with config() in various files

- Updated multiple files to utilize the new Config class for configuration access.
- Replaced instances of $bb_cfg with config()->get() for improved maintainability and consistency.
- Ensured backward compatibility while transitioning to the new configuration system.

Files modified:
- privmsg.php
- search.php
- terms.php
- tracker.php
- viewtopic.php
- bt/announce.php
- bt/scrape.php
- bt/includes/init_tr.php
- library/ajax/*.php
- src/Config.php

* refactor: replace $bb_cfg with config() in attachment and display files

- Updated multiple files to utilize the new Config class for configuration access.
- Replaced instances of $bb_cfg with config()->get() for improved maintainability and consistency.
- Ensured backward compatibility while transitioning to the new configuration system.

Files modified:
- attachment_mod.php
- displaying_torrent.php
- functions_delete.php
- bbcode.php
- functions.php
- init_bb.php
- online_userlist.php
- page_footer.php
- page_header.php
- torrent_show_dl_list.php
- cron jobs (various files)
- datastore build files (various files)

* refactor: replace $bb_cfg with config() in user control panel files

- Updated multiple user control panel files to utilize the new Config class for configuration access.
- Replaced instances of $bb_cfg with config()->get() for improved maintainability and consistency.
- Ensured backward compatibility while transitioning to the new configuration system.

Files modified:
- bonus.php
- email.php
- register.php
- sendpasswd.php
- topic_watch.php
- viewprofile.php

* refactor: replace $bb_cfg with config() in various legacy files

- Updated multiple legacy files to utilize the new Config class for configuration access.
- Replaced instances of $bb_cfg with config()->get() for improved maintainability and consistency.
- Ensured backward compatibility while transitioning to the new configuration system.

Files modified:
- sidebar2.html
- BBCode.php
- LogAction.php
- Post.php
- Template.php
- Torrent.php
- Common/User.php
- Common/Select.php
- Common/Upload.php
- Admin/Common.php
- tpl_config.php

* refactor: remove legacy DOCUMENTATION.md and add UPGRADE_GUIDE.md

- Deleted the outdated DOCUMENTATION.md file, which contained legacy configuration information.
- Introduced a new UPGRADE_GUIDE.md to assist users in migrating to the new configuration system.
- The upgrade guide includes migration strategies, breaking changes, and best practices for using the new Config class.

Files modified:
- DOCUMENTATION.md (deleted)
- UPGRADE_GUIDE.md (new)

* refactor: update legacy files to maintain compatibility with bb_cfg deprecation

- Added comments in init_bb.php, Template.php, and Cron.php to indicate that bb_cfg is deprecated but retained for compatibility with non-adapted code.
- Ensured clarity in documentation for future reference while transitioning to the new configuration system.

Files modified:
- init_bb.php
- Template.php
- Cron.php
2025-06-18 01:19:49 +04:00
Yury Pikhtarev
37a0675adf
fix(sql): resolve only_full_group_by compatibility issues in tracker cleanup (#1951)
- Replace non-aggregated 'seeder' column with MAX(seeder) in GROUP BY query
- Remove ORDER BY clause incompatible with GROUP BY in seeder update query

Fixes MySQL 5.7+ strict SQL mode errors in tr_cleanup_and_dlstat cron job.
2025-06-17 20:28:14 +04:00
Roman Kelesidis
81bf67c2be
fix(installer): Strip protocol from TP_HOST to keep only hostname (#1952)
Some checks are pending
Continuous Integration / Nightly builds 📦 (push) Waiting to run
Continuous Integration / 🎉 Deploy (push) Waiting to run
PHPMD / Run PHPMD scanning (push) Waiting to run
2025-06-17 18:14:47 +03:00
Yury Pikhtarev
5842994782
refactor(config): encapsulate global $bb_cfg array in Config class (#1950)
- Create new TorrentPier\Config singleton class with dot notation support
- Add config() helper function for global access
- Replace direct $bb_cfg access in core files (common.php, Emailer.php, Ajax.php)
- Implement methods: get(), set(), has(), all(), getSection(), merge()
- Add magic methods for property-like access
- Maintain backward compatibility with existing $bb_cfg usage

BREAKING CHANGE: None - maintains full backward compatibility
2025-06-17 18:41:45 +04:00
Yury Pikhtarev
8907dbf991
Update composer.lock and improve SQL queries in online_userlist.php and tr_cleanup_and_dlstat.php (#1949)
- Updated plugin API version in composer.lock from 2.3.0 to 2.6.0.
- Modified SQL queries in online_userlist.php to use MAX() for session data and adjusted GROUP BY clause for better accuracy.
- Enhanced SQL logic in tr_cleanup_and_dlstat.php to utilize MAX() for determining the releaser status.
2025-06-17 18:07:31 +04:00
316 changed files with 20865 additions and 7839 deletions

View file

@ -1,7 +1,7 @@
name: Bug Report name: Bug Report
description: File a bug report description: File a bug report
title: "[Bug]" title: "[Bug]"
labels: Bug labels: [Bug]
body: body:
- type: markdown - type: markdown
attributes: attributes:
@ -21,7 +21,7 @@ body:
attributes: attributes:
label: PHP & Platform label: PHP & Platform
description: Exact PHP and Platform (OS) versions your using. description: Exact PHP and Platform (OS) versions your using.
placeholder: 8.1.2 - Ubuntu 22.04 x64 placeholder: 8.2.2 - Ubuntu 22.04 x64
validations: validations:
required: true required: true
- type: checkboxes - type: checkboxes

View file

@ -2,6 +2,6 @@
name: Feature / Enhancement request name: Feature / Enhancement request
about: Suggest an idea for TorrentPier about: Suggest an idea for TorrentPier
title: "[Feature]" title: "[Feature]"
labels: Feature, Enhancement labels: [Feature, Enhancement]
assignees: '' assignees: ''
--- ---

View file

@ -41,13 +41,10 @@ jobs:
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: '8.1' php-version: '8.2'
- name: Update composer.lock file
run: composer update --no-install
- name: Install Composer dependencies - name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader
- name: Cleanup - name: Cleanup
run: php _cleanup.php && rm _cleanup.php run: php _cleanup.php && rm _cleanup.php

View file

@ -3,7 +3,7 @@ name: Continuous Integration
on: on:
push: push:
branches: branches:
- v2.4 - master
jobs: jobs:
nightly: nightly:
@ -17,13 +17,10 @@ jobs:
- name: Setup PHP 🔩 - name: Setup PHP 🔩
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: '8.1' php-version: '8.2'
- name: Update composer.lock file
run: composer update --no-install
- name: Install Composer dependencies 🪚 - name: Install Composer dependencies 🪚
run: composer install --no-progress --prefer-dist --optimize-autoloader run: composer install --no-dev --no-progress --prefer-dist --optimize-autoloader
- name: Get commit hash 🔗 - name: Get commit hash 🔗
id: get-commit-hash id: get-commit-hash
@ -44,5 +41,5 @@ jobs:
- name: Upload Archive 📤 - name: Upload Archive 📤
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: TorrentPier-v2.4 name: TorrentPier-master
path: ${{ steps.create-zip.outputs.ZIP_NAME }} path: ${{ steps.create-zip.outputs.ZIP_NAME }}

View file

@ -17,10 +17,10 @@ name: PHPMD
on: on:
push: push:
branches: [ "v2.4" ] branches: [ "master" ]
pull_request: pull_request:
# The branches below must be a subset of the branches above # The branches below must be a subset of the branches above
branches: [ "v2.4" ] branches: [ "master" ]
schedule: schedule:
- cron: '40 0 * * 3' - cron: '40 0 * * 3'

View file

@ -14,7 +14,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
ref: v2.4 ref: master
token: ${{ secrets.REPO_TOKEN }} token: ${{ secrets.REPO_TOKEN }}
- name: Generate a changelog - name: Generate a changelog
@ -22,7 +22,7 @@ jobs:
id: git-cliff id: git-cliff
with: with:
config: cliff.toml config: cliff.toml
args: v2.4.5-rc.2.. --verbose args: v2.4.6-alpha.4.. --verbose
env: env:
OUTPUT: CHANGELOG.md OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }} GITHUB_REPO: ${{ github.repository }}
@ -32,10 +32,10 @@ jobs:
- name: Commit changelog - name: Commit changelog
run: | run: |
git checkout v2.4 git checkout master
git config --local user.name 'belomaxorka' git config --local user.name 'belomaxorka'
git config --local user.email 'roman25052006.kelesh@gmail.com' git config --local user.email 'roman25052006.kelesh@gmail.com'
set +e set +e
git add CHANGELOG.md git add CHANGELOG.md
git commit -m "changelog: Update CHANGELOG.md 📖" git commit -m "changelog: Update CHANGELOG.md 📖"
git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git v2.4 git push https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master

View file

@ -2,345 +2,112 @@
# 📖 Change Log # 📖 Change Log
## [v2.4.6](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.4..v2.4.6) (2025-06-19) ## [v2.8.3](https://github.com/torrentpier/torrentpier/compare/v2.8.2..v2.8.3) (2025-07-03)
### 🚀 Features
- *(lang)* Added `RTL` languages support ([#2031](https://github.com/torrentpier/torrentpier/pull/2031)) - ([fd46d3d](https://github.com/torrentpier/torrentpier/commit/fd46d3d04ad3ab1453256b2ab620508e2ba33586))
- *(updater)* Added exceptions logging ([#2026](https://github.com/torrentpier/torrentpier/pull/2026)) - ([51f2c70](https://github.com/torrentpier/torrentpier/commit/51f2c70d81b910012cdecd111b5b92c1dfd0d6f6))
### 🚜 Refactor
- *(TorrentFileList)* Reduce duplication in root directory unset logic ([#2027](https://github.com/torrentpier/torrentpier/pull/2027)) - ([d4d8210](https://github.com/torrentpier/torrentpier/commit/d4d82101dd67c9f4cd86e0f6f909495696974354))
## [v2.8.2](https://github.com/torrentpier/torrentpier/compare/v2.8.1..v2.8.2) (2025-06-30)
### 🐛 Bug Fixes ### 🐛 Bug Fixes
- *(installer)* Strip protocol from TP_HOST to keep only hostname ([#1969](https://github.com/torrentpier/torrentpier/pull/1969)) - ([15f9948](https://github.com/torrentpier/torrentpier/commit/15f994840331b135cd64c0cd61de95fecfc29db8)) - *(TorrentFileList)* Avoid `array_merge` reindexing for numeric folder names ([#2014](https://github.com/torrentpier/torrentpier/pull/2014)) - ([915e1d8](https://github.com/torrentpier/torrentpier/commit/915e1d817c61d2a4f0691b24ec1bc6577a9cd44b))
- *(sql)* Resolve `only_full_group_by` compatibility issues in tracker cleanup - ([faf3d79](https://github.com/torrentpier/torrentpier/commit/faf3d7919249d869d8ca8d41617dd4356dc0ac48))
- Duplicate column SQL query issues in `viewtopic.php` ([#1973](https://github.com/torrentpier/torrentpier/pull/1973)) - ([6a1d682](https://github.com/torrentpier/torrentpier/commit/6a1d6823856dd7c3cef45bea2681828526d1b9f8)) ### 🚜 Refactor
- SQL queries in online_userlist.php to use MAX() for session data and adjusted GROUP BY clause for better accuracy ([#1971](https://github.com/torrentpier/torrentpier/pull/1971)) - ([2a8b6da](https://github.com/torrentpier/torrentpier/commit/2a8b6daecf63752b8a852c950e9a7fd08e17f57c))
- Use `DEFAULT_CHARSET` constant instead of hardcoded string ([#2011](https://github.com/torrentpier/torrentpier/pull/2011)) - ([7ac3359](https://github.com/torrentpier/torrentpier/commit/7ac335974baa44a8575bebb71ae2fbc0902d10e7))
## [v2.8.1](https://github.com/torrentpier/torrentpier/compare/v2.8.0..v2.8.1) (2025-06-24)
### 🐛 Bug Fixes
- *(filelist)* `Undefined property: FileTree::$length` when v2 torrent only ([#2004](https://github.com/torrentpier/torrentpier/pull/2004)) - ([7f4cc9d](https://github.com/torrentpier/torrentpier/commit/7f4cc9d3b9a5b87100f710cc60f636d6e7d5a34e))
- *(ip-api)* Add error handling and logging for freeipapi.com requests ([#2006](https://github.com/torrentpier/torrentpier/pull/2006)) - ([f1d6e74](https://github.com/torrentpier/torrentpier/commit/f1d6e74e5d4c74b6e12e9e742f60f62e71783d11))
## [v2.8.0](https://github.com/torrentpier/torrentpier/compare/v2.7.0..v2.8.0) (2025-06-21)
### 🐛 Bug Fixes
- *(template)* Handle L_ variables in template vars when not found in lang vars ([#1998](https://github.com/torrentpier/torrentpier/pull/1998)) - ([c6076c2](https://github.com/torrentpier/torrentpier/commit/c6076c2c278e9a423f3862670236b75bddeadd87))
## [v2.7.0](https://github.com/torrentpier/torrentpier/compare/v2.6.0..v2.7.0) (2025-06-21)
### 🚀 Features
- *(database)* Add visual markers for Nette Explorer queries in debug panel ([#1965](https://github.com/torrentpier/torrentpier/pull/1965)) - ([2fd3067](https://github.com/torrentpier/torrentpier/commit/2fd306704f21febee7d53f4b4531601ce0cb81ce))
- *(language)* Add new language variable for migration file and enhance template fallback logic ([#1984](https://github.com/torrentpier/torrentpier/pull/1984)) - ([a33574c](https://github.com/torrentpier/torrentpier/commit/a33574c28f2eb6267a74fa6c9d97fea86527157a))
- *(migrations)* Implement Phinx database migration system ([#1976](https://github.com/torrentpier/torrentpier/pull/1976)) - ([fbde8cd](https://github.com/torrentpier/torrentpier/commit/fbde8cd421c9048afe70ddb41d0a9ed26d3fbef5))
- *(test)* [**breaking**] Add comprehensive testing infrastructure with Pest PHP ([#1979](https://github.com/torrentpier/torrentpier/pull/1979)) - ([cc9d412](https://github.com/torrentpier/torrentpier/commit/cc9d412522938a023bd2b8eb880c4d2dd307c82a))
- [**breaking**] Implement Language singleton with shorthand functions ([#1966](https://github.com/torrentpier/torrentpier/pull/1966)) - ([49717d3](https://github.com/torrentpier/torrentpier/commit/49717d3a687b95885fe9773f2597354aed4b2b60))
### 🐛 Bug Fixes
- *(database)* Update affected rows tracking in Database class ([#1980](https://github.com/torrentpier/torrentpier/pull/1980)) - ([4f9cc9f](https://github.com/torrentpier/torrentpier/commit/4f9cc9fe0f7f4a85c90001a3f5514efdf04836da))
### 🚜 Refactor
- *(database)* Enhance error logging and various fixes ([#1978](https://github.com/torrentpier/torrentpier/pull/1978)) - ([7aed6bc](https://github.com/torrentpier/torrentpier/commit/7aed6bc7d89f4ed31e7ed6c6eeecc6e08d348c24))
- *(database)* Rename DB to Database and extract debug functionality ([#1964](https://github.com/torrentpier/torrentpier/pull/1964)) - ([6c0219d](https://github.com/torrentpier/torrentpier/commit/6c0219d53c7544b7d8a6374c0d0848945d32ae17))
- *(stats)* Improve database row fetching in tr_stats.php ([#1985](https://github.com/torrentpier/torrentpier/pull/1985)) - ([728116d](https://github.com/torrentpier/torrentpier/commit/728116d6dc9cf4476cce572ced5e8a7ef529ead8))
### ⚙️ Miscellaneous
- Update minimum `PHP` requirement to `8.2` ([#1987](https://github.com/torrentpier/torrentpier/pull/1987)) - ([9b322c7](https://github.com/torrentpier/torrentpier/commit/9b322c7093a634669e9f17a32ac42500f44f2496))
- Removed useless `composer update` from workflows & installer ([#1986](https://github.com/torrentpier/torrentpier/pull/1986)) - ([423424e](https://github.com/torrentpier/torrentpier/commit/423424e9478e0772957014fb30f5e84158067af7))
- Added --no-dev composer flag for some workflows ([#1982](https://github.com/torrentpier/torrentpier/pull/1982)) - ([e9a9e09](https://github.com/torrentpier/torrentpier/commit/e9a9e095768ba68aa5d5058a3e152ffaec916117))
- Added `--no-dev` composer flag for some workflows ([#1981](https://github.com/torrentpier/torrentpier/pull/1981)) - ([e8cba5d](https://github.com/torrentpier/torrentpier/commit/e8cba5dd3fc83b616f83c24991f79dc7258c5df3))
## [v2.6.0](https://github.com/torrentpier/torrentpier/compare/v2.5.0..v2.6.0) (2025-06-18)
### 🚀 Features
- [**breaking**] Implement unified cache system with Nette Caching ([#1963](https://github.com/torrentpier/torrentpier/pull/1963)) - ([07a06a3](https://github.com/torrentpier/torrentpier/commit/07a06a33cd97b37f68b533a87cdb5f7578f2c86f))
- Replace legacy database layer with Nette Database implementation ([#1961](https://github.com/torrentpier/torrentpier/pull/1961)) - ([f50b914](https://github.com/torrentpier/torrentpier/commit/f50b914cc18f777d92002baf2c812a635d5eed4b))
### 🐛 Bug Fixes
- *(User)* Add null and array checks before session data operations ([#1962](https://github.com/torrentpier/torrentpier/pull/1962)) - ([e458109](https://github.com/torrentpier/torrentpier/commit/e458109eefc54d86a78a1ddb3954581524852516))
## [v2.5.0](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.4..v2.5.0) (2025-06-18)
### 🚀 Features
- [**breaking**] Implement centralized Config class to replace global $bb_cfg array ([#1953](https://github.com/torrentpier/torrentpier/pull/1953)) - ([bf9100f](https://github.com/torrentpier/torrentpier/commit/bf9100fbfa74768edb01c62636198a44739d9923))
### 🐛 Bug Fixes
- *(installer)* Strip protocol from TP_HOST to keep only hostname ([#1952](https://github.com/torrentpier/torrentpier/pull/1952)) - ([81bf67c](https://github.com/torrentpier/torrentpier/commit/81bf67c2be85d49e988b7802ca7e9738ff580031))
- *(sql)* Resolve only_full_group_by compatibility issues in tracker cleanup ([#1951](https://github.com/torrentpier/torrentpier/pull/1951)) - ([37a0675](https://github.com/torrentpier/torrentpier/commit/37a0675adfb02014e7068f4aa82301e29f39eab6))
### 📦 Dependencies ### 📦 Dependencies
- *(deps)* Bump filp/whoops from 2.18.2 to 2.18.3 ([#1948](https://github.com/torrentpier/torrentpier/pull/1948)) - ([b477680](https://github.com/torrentpier/torrentpier/commit/b4776804a408217229caa327c79849cf13ce2aa5)) - *(deps)* Bump filp/whoops from 2.18.2 to 2.18.3 ([#1948](https://github.com/torrentpier/torrentpier/pull/1948)) - ([b477680](https://github.com/torrentpier/torrentpier/commit/b4776804a408217229caa327c79849cf13ce2aa5))
### ⚙️ Miscellaneous
- *(_release.php)* Finally! Removed some useless params ([#1947](https://github.com/torrentpier/torrentpier/pull/1947)) - ([9c7d270](https://github.com/torrentpier/torrentpier/commit/9c7d270598c0153fb82f4b7ad96f5b59399b2159))
- *(cliff)* Add conventional commit prefix to changelog message ([#1970](https://github.com/torrentpier/torrentpier/pull/1970)) - ([7d9594e](https://github.com/torrentpier/torrentpier/commit/7d9594eedab1b2c81d888dfba68ded1b8a142282))
- Changed active branch name in `_release.php` ([#1972](https://github.com/torrentpier/torrentpier/pull/1972)) - ([7dc69ba](https://github.com/torrentpier/torrentpier/commit/7dc69ba699c75d87c709a799291c4b544b3e92aa))
- Changed branch name from `master` to `v2.4` ([#1968](https://github.com/torrentpier/torrentpier/pull/1968)) - ([a8e252f](https://github.com/torrentpier/torrentpier/commit/a8e252f64f7205b7bb24739ab637144c6fb022d6))
## New Contributors ❤️
* @belomaxorka made their first contribution
* @dependabot[bot] made their first contribution in [#1948](https://github.com/torrentpier/torrentpier/pull/1948)
## [v2.4.6-alpha.4](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.3..v2.4.6-alpha.4) (2025-06-13)
### ⚙️ Miscellaneous
- *(_release.php)* Use `GPG` sign for tags by default ([#1946](https://github.com/torrentpier/torrentpier/pull/1946)) - ([0271b21](https://github.com/torrentpier/torrentpier/commit/0271b21a5e8c9dce918da9954547d81dae2a1d4b))
## [v2.4.6-alpha.3](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.2..v2.4.6-alpha.3) (2025-06-13)
### ⚙️ Miscellaneous
- *(_release.php)* Minor improvements ([#1945](https://github.com/torrentpier/torrentpier/pull/1945)) - ([e5811f9](https://github.com/torrentpier/torrentpier/commit/e5811f9c66eef7f228b51fb82ffda3bcddeb915d))
## [v2.4.6-alpha.2](https://github.com/torrentpier/torrentpier/compare/v2.4.6-alpha.1..v2.4.6-alpha.2) (2025-06-12)
### 🚀 Features
- *(ajax)* Log full ajax request/response data to console in explain mode ([#1942](https://github.com/torrentpier/torrentpier/pull/1942)) - ([bcf4eb4](https://github.com/torrentpier/torrentpier/commit/bcf4eb4e9baacf27e23a2b7c7135918ec3356c1a))
- Improved ajax debug ([#1941](https://github.com/torrentpier/torrentpier/pull/1941)) - ([6f03f75](https://github.com/torrentpier/torrentpier/commit/6f03f750bab400f5e8a74bd05c9ee167343959ab))
- Add console log for ajax actions when explain cookie is set ([#1940](https://github.com/torrentpier/torrentpier/pull/1940)) - ([345dd1b](https://github.com/torrentpier/torrentpier/commit/345dd1bc20928e25dc72befb705502156e47f1d7))
### 🐛 Bug Fixes
- Set `$datastore->enqueue` before `$datastore->get` ([#1937](https://github.com/torrentpier/torrentpier/pull/1937)) - ([bf328dd](https://github.com/torrentpier/torrentpier/commit/bf328dd69ec42e417275f037dc59a15a2867d7f4))
### 📦 Dependencies
- *(deps)* Bump filp/whoops from 2.18.1 to 2.18.2 ([#1943](https://github.com/torrentpier/torrentpier/pull/1943)) - ([9a52955](https://github.com/torrentpier/torrentpier/commit/9a529558b41f620e8347cc1091f59b1f2d864ca9))
### 🗑️ Removed
- `'cat_forums'` from enqueue list in `get_forum_mods` ajax ([#1939](https://github.com/torrentpier/torrentpier/pull/1939)) - ([28e38aa](https://github.com/torrentpier/torrentpier/commit/28e38aa78103c8233e15439ecd886187a55d5e12))
- Extra `CFG_DIR` constant ([#1936](https://github.com/torrentpier/torrentpier/pull/1936)) - ([4b16b84](https://github.com/torrentpier/torrentpier/commit/4b16b847f542e3608c8bb4d97d1f27f7fd6c97b7))
### ⚙️ Miscellaneous
- *(_release.php)* Minor improvements ([#1938](https://github.com/torrentpier/torrentpier/pull/1938)) - ([f9db78d](https://github.com/torrentpier/torrentpier/commit/f9db78d266ff3707e96b1b9d3d2330a507181012))
- *(_release.php)* Temporary removed automatic `push origin` ([#1935](https://github.com/torrentpier/torrentpier/pull/1935)) - ([dcd7002](https://github.com/torrentpier/torrentpier/commit/dcd7002c2aa09ec187f3afd91fb7e3f5e03630e0))
- *(_release.php)* Added ability to set version emoji ([#1934](https://github.com/torrentpier/torrentpier/pull/1934)) - ([75ef574](https://github.com/torrentpier/torrentpier/commit/75ef57474c3a32e86ecc98a5ff2fab39a9a66282))
- *(_release.php)* Added automatic `CHANGELOG.md` update ([#1933](https://github.com/torrentpier/torrentpier/pull/1933)) - ([867359a](https://github.com/torrentpier/torrentpier/commit/867359a89e480071cfd927e2cb6ef4fd761c0172))
- *(_release.php)* Added `push origin` command ([#1932](https://github.com/torrentpier/torrentpier/pull/1932)) - ([5561e00](https://github.com/torrentpier/torrentpier/commit/5561e0022ca6a0a668f2dc5aee541609bb6c4d1e))
- *(cliff.toml)* Use correct nightly link ([#1944](https://github.com/torrentpier/torrentpier/pull/1944)) - ([5e6fb3e](https://github.com/torrentpier/torrentpier/commit/5e6fb3ef424cbc84bb5e25625dcd22fd73ec98fa))
## [v2.4.6-alpha.1](https://github.com/torrentpier/torrentpier/compare/v2.4.5..v2.4.6-alpha.1) (2025-06-09)
### 🚀 Features
- *(ACP)* Added `robots.txt` editor ([#1913](https://github.com/torrentpier/torrentpier/pull/1913)) - ([79bb13e](https://github.com/torrentpier/torrentpier/commit/79bb13e17d07505be4d3a3c67223b4f591b66bfb))
- *(bbcode)* Added `[nfo]` and `[pre]` tags ([#1923](https://github.com/torrentpier/torrentpier/pull/1923)) - ([f64c340](https://github.com/torrentpier/torrentpier/commit/f64c340563378a364e1f00c64b17ac1c79531302))
- *(bbcode)* Implement color customization for `[box]` tag ([#1920](https://github.com/torrentpier/torrentpier/pull/1920)) - ([4c24cb6](https://github.com/torrentpier/torrentpier/commit/4c24cb65bfebf307b717e985b169ea5d27df64f8))
- *(install)* Autofill `Host` in `robots.txt` file ([#1916](https://github.com/torrentpier/torrentpier/pull/1916)) - ([03eeb08](https://github.com/torrentpier/torrentpier/commit/03eeb08ad185b6dcc99563f567297e41f4a56117))
- *(meta)* Minor improvements to description generation ([#1926](https://github.com/torrentpier/torrentpier/pull/1926)) - ([4d0b294](https://github.com/torrentpier/torrentpier/commit/4d0b2941e3ef6703ac2cd4c03524a93e688e0c39))
- Added ability to set page meta description ([#1917](https://github.com/torrentpier/torrentpier/pull/1917)) - ([7b8b9a0](https://github.com/torrentpier/torrentpier/commit/7b8b9a0bbabc1dfbf56cac8c105ad158ae78c3a7))
### 🈳 New translations
- New Crowdin updates ([#1925](https://github.com/torrentpier/torrentpier/pull/1925)) - ([2487d13](https://github.com/torrentpier/torrentpier/commit/2487d130bb23bd82cedf0d114843bb48f6d2e61c))
- New Crowdin updates ([#1924](https://github.com/torrentpier/torrentpier/pull/1924)) - ([0515670](https://github.com/torrentpier/torrentpier/commit/0515670bee99faa5f0979162096114bc9d3ddf98))
- New translations main.php (Russian) ([#1922](https://github.com/torrentpier/torrentpier/pull/1922)) - ([8e965fb](https://github.com/torrentpier/torrentpier/commit/8e965fb1ceb5e82201c43b33fcdb044256646191))
- New Crowdin updates ([#1921](https://github.com/torrentpier/torrentpier/pull/1921)) - ([daeb7fe](https://github.com/torrentpier/torrentpier/commit/daeb7fe87e8da53745fe7aac0708cefa3392ffdc))
- New Crowdin updates ([#1915](https://github.com/torrentpier/torrentpier/pull/1915)) - ([a3da6f5](https://github.com/torrentpier/torrentpier/commit/a3da6f538658fbfe4e57aad10046d8c459a1a498))
- New Crowdin updates ([#1914](https://github.com/torrentpier/torrentpier/pull/1914)) - ([a15baef](https://github.com/torrentpier/torrentpier/commit/a15baef69a2955b6dc9cd6e8fdf467550d0b5d09))
- New Crowdin updates ([#1911](https://github.com/torrentpier/torrentpier/pull/1911)) - ([174f441](https://github.com/torrentpier/torrentpier/commit/174f44160e1f33bed9422f0c4eab9d73b7025036))
- New Crowdin updates ([#1910](https://github.com/torrentpier/torrentpier/pull/1910)) - ([c40aad2](https://github.com/torrentpier/torrentpier/commit/c40aad20ad865849d3088498f1ba95a5fb0a0621))
- New Crowdin updates ([#1907](https://github.com/torrentpier/torrentpier/pull/1907)) - ([999ae1e](https://github.com/torrentpier/torrentpier/commit/999ae1eff9f3a4c951fc48efbf94c0cea2a5f8d2))
- Updated translations ([#1909](https://github.com/torrentpier/torrentpier/pull/1909)) - ([897edfc](https://github.com/torrentpier/torrentpier/commit/897edfc371087427c574776472cbbf3f1f933273))
- Updated translations ([#1908](https://github.com/torrentpier/torrentpier/pull/1908)) - ([6d0499d](https://github.com/torrentpier/torrentpier/commit/6d0499dd0229d454d3af00f10151adc26a9e96a7))
- New translations ([#1906](https://github.com/torrentpier/torrentpier/pull/1906)) - ([8a3b12c](https://github.com/torrentpier/torrentpier/commit/8a3b12c1192678552a3186c1f58df9b4d7e5ba1b))
### 📦 Dependencies
- *(deps)* Bump filp/whoops from 2.18.0 to 2.18.1 ([#1919](https://github.com/torrentpier/torrentpier/pull/1919)) - ([1253661](https://github.com/torrentpier/torrentpier/commit/125366147c6257abadd489f3802e4a0dab37a89c))
- *(deps)* Bump arokettu/bencode from 4.3.0 to 4.3.1 ([#1912](https://github.com/torrentpier/torrentpier/pull/1912)) - ([f76e351](https://github.com/torrentpier/torrentpier/commit/f76e351b32cfa2932bc1afde6c3c522cd993b8af))
### ⚙️ Miscellaneous
- *(_release.php)* Added `GPG` sign for tags ([#1931](https://github.com/torrentpier/torrentpier/pull/1931)) - ([8ecc617](https://github.com/torrentpier/torrentpier/commit/8ecc61719acb61e9a2ce115b28f1a82580c01110))
- *(cliff)* Added automated script for releases creation ([#1930](https://github.com/torrentpier/torrentpier/pull/1930)) - ([6adde35](https://github.com/torrentpier/torrentpier/commit/6adde35849811648bcb8fa1a72c3be0a886b7919))
- *(cliff)* Completely removed `cliff-releases.toml` ([#1929](https://github.com/torrentpier/torrentpier/pull/1929)) - ([cef041c](https://github.com/torrentpier/torrentpier/commit/cef041c0d128dca480ca40770f52385f868706b0))
- *(cliff)* Updated config ([#1928](https://github.com/torrentpier/torrentpier/pull/1928)) - ([212e5c5](https://github.com/torrentpier/torrentpier/commit/212e5c52832f32e8864850bf520b5c73f27f1609))
- Minor improvements ([#1918](https://github.com/torrentpier/torrentpier/pull/1918)) - ([46f29bc](https://github.com/torrentpier/torrentpier/commit/46f29bc68a18fdefad81e26a60fe44f122407ea7))
## [v2.4.5](https://github.com/torrentpier/torrentpier/compare/v2.4.5-rc.5..v2.4.5) (2025-05-11)
### 🚀 Features
- *(admin_smilies)* Added confirmation on smilie deleting ([#1895](https://github.com/torrentpier/torrentpier/pull/1895)) - ([b51820e](https://github.com/torrentpier/torrentpier/commit/b51820e1861044143321fcde5239c22abc3de984))
- *(announcer)* Check for frozen torrents ([#1770](https://github.com/torrentpier/torrentpier/pull/1770)) - ([6e0786b](https://github.com/torrentpier/torrentpier/commit/6e0786bdee8f1a2557f9ac1dc628983bcafe3f5f))
- *(freeipapi)* Added ability to use own API token ([#1901](https://github.com/torrentpier/torrentpier/pull/1901)) - ([513e306](https://github.com/torrentpier/torrentpier/commit/513e3065d34409931c4198c03b080f232f1d809b))
- Added ability to hide peer username in peer list ([#1903](https://github.com/torrentpier/torrentpier/pull/1903)) - ([3a64f85](https://github.com/torrentpier/torrentpier/commit/3a64f8595cafd99b9cb821d52ec5d3b3e8e467c0))
- Added ability to hide peer country in peer list ([#1891](https://github.com/torrentpier/torrentpier/pull/1891)) - ([2555ebc](https://github.com/torrentpier/torrentpier/commit/2555ebce4717f871922495e48cbca9e22da78bd5))
- Added ability to hide BitTorrent client in peers list ([#1890](https://github.com/torrentpier/torrentpier/pull/1890)) - ([f5d65b8](https://github.com/torrentpier/torrentpier/commit/f5d65b8911c5e864f000348a6d1aefbb4c09c2b4))
### 🐛 Bug Fixes
- *(peers list)* `IPv6` showing ([#1902](https://github.com/torrentpier/torrentpier/pull/1902)) - ([4b7203f](https://github.com/torrentpier/torrentpier/commit/4b7203f8aeeeffc1b163bd3db1dd6b2cac33c923))
- Incorrect rounding in execution time counter ([#1899](https://github.com/torrentpier/torrentpier/pull/1899)) - ([781b724](https://github.com/torrentpier/torrentpier/commit/781b7240c41ddd141cfb057480c10d9cee30e6d7))
- `Undefined array key "smile"` when are no smilies ([#1896](https://github.com/torrentpier/torrentpier/pull/1896)) - ([36d3992](https://github.com/torrentpier/torrentpier/commit/36d399220e2c16a582e1e400df0002c164f5ec3b))
- Peer country flag not shown in peers list ([#1894](https://github.com/torrentpier/torrentpier/pull/1894)) - ([8edba72](https://github.com/torrentpier/torrentpier/commit/8edba72f09f037225ede058cf09c830b1a01e78f))
### 📦 Dependencies
- *(deps)* Bump symfony/polyfill from 1.31.0 to 1.32.0 ([#1900](https://github.com/torrentpier/torrentpier/pull/1900)) - ([a4793f6](https://github.com/torrentpier/torrentpier/commit/a4793f6ce103f22941d72793e2bf8cdf9f78d494))
### ⚙️ Miscellaneous
- Minor improvements ([#1904](https://github.com/torrentpier/torrentpier/pull/1904)) - ([3cdf843](https://github.com/torrentpier/torrentpier/commit/3cdf843a0442d4cdf9b70702c6092d05df86c7e0))
- Minor improvements ([#1898](https://github.com/torrentpier/torrentpier/pull/1898)) - ([2f02692](https://github.com/torrentpier/torrentpier/commit/2f026921ee331226900b3cd4f1bb238f6562b48d))
- Minor improvements ([#1897](https://github.com/torrentpier/torrentpier/pull/1897)) - ([14086a0](https://github.com/torrentpier/torrentpier/commit/14086a0ed6181a0ff4496ee2e56f4fb70bfe18d5))
- Minor improvements ([#1893](https://github.com/torrentpier/torrentpier/pull/1893)) - ([90ece5c](https://github.com/torrentpier/torrentpier/commit/90ece5c7621789f170246b2898841b347e264674))
- Minor improvements ([#1892](https://github.com/torrentpier/torrentpier/pull/1892)) - ([1e5b93d](https://github.com/torrentpier/torrentpier/commit/1e5b93d2c072c5c35feef7567b3fcdb4b3597935))
## [v2.4.5-rc.5](https://github.com/torrentpier/torrentpier/compare/v2.4.5-rc.4..v2.4.5-rc.5) (2025-05-03)
### 🚀 Features
- *(admin_ranks)* Added confirmation on rank deleting ([#1888](https://github.com/torrentpier/torrentpier/pull/1888)) - ([e510ebc](https://github.com/torrentpier/torrentpier/commit/e510ebc3ba30be7bf99769b1e5540353bd53c333))
- *(atom)* Hide topics from private forums ([#1889](https://github.com/torrentpier/torrentpier/pull/1889)) - ([75e9d5e](https://github.com/torrentpier/torrentpier/commit/75e9d5e4a8c5ec20f438e7b24a5469d219959a8c))
- *(avatar upload)* Added `accept="image/*"` attribute ([#1841](https://github.com/torrentpier/torrentpier/pull/1841)) - ([56d531a](https://github.com/torrentpier/torrentpier/commit/56d531aa5ddb778d08a2796fa9fb865e5b3040ce))
- *(emailer)* Added ability to configure `sendmail` - ([5ad4a70](https://github.com/torrentpier/torrentpier/commit/5ad4a7019d996d468650ab608ab53d6cf3ebb4f5))
- *(magnet)* Added `xl` (eXact Length) parametr ([#1883](https://github.com/torrentpier/torrentpier/pull/1883)) - ([c0cdcff](https://github.com/torrentpier/torrentpier/commit/c0cdcff48825ce5fb0c89c0ec44eb95686aee74c))
- *(playback_m3u.php)* Added checking auth to download ([#1848](https://github.com/torrentpier/torrentpier/pull/1848)) - ([0b8d8a5](https://github.com/torrentpier/torrentpier/commit/0b8d8a5210ee761dddaa57fc48bb48b0ede1ec3c))
### 🐛 Bug Fixes
- *(cache)* Implicitly marking parameter `$name` as nullable is deprecated ([#1877](https://github.com/torrentpier/torrentpier/pull/1877)) - ([c3b4000](https://github.com/torrentpier/torrentpier/commit/c3b40003b778a725e958cebee6446bcfd6a68b10))
- Displaying `Network news` and `Latest news` for guests when foums are private ([#1879](https://github.com/torrentpier/torrentpier/pull/1879)) - ([9f96090](https://github.com/torrentpier/torrentpier/commit/9f96090cc419f828e54e69a91a906a3f3d92c255))
- Pagination issue in `Report on action` page ([#1872](https://github.com/torrentpier/torrentpier/pull/1872)) - ([8358aa0](https://github.com/torrentpier/torrentpier/commit/8358aa00de2ec9efd4c51b8bef11bd700a56c19c))
- `tablesorting` issues & incorrect `user_role` for pending users ([#1871](https://github.com/torrentpier/torrentpier/pull/1871)) - ([595adbe](https://github.com/torrentpier/torrentpier/commit/595adbe4da5296b0f3ebde6628e58e878c0fb7d5))
- Fixed TorrentPier build-in emojis showing in ACP ([#1870](https://github.com/torrentpier/torrentpier/pull/1870)) - ([12792e7](https://github.com/torrentpier/torrentpier/commit/12792e74f71a57448277dda46471563a7fea71db))
### 📦 Dependencies
- *(deps)* Bump vlucas/phpdotenv from 5.6.1 to 5.6.2 ([#1887](https://github.com/torrentpier/torrentpier/pull/1887)) - ([7a14464](https://github.com/torrentpier/torrentpier/commit/7a14464d20fe8d2f8b980a82647c6b9ec081f621))
- *(deps)* Bump php-curl-class/php-curl-class from 11.1.0 to 12.0.0 ([#1868](https://github.com/torrentpier/torrentpier/pull/1868)) - ([bd5aa2a](https://github.com/torrentpier/torrentpier/commit/bd5aa2a5e71560409bc630ea2334e33c77458ab3))
- *(deps)* Bump monolog/monolog from 3.8.1 to 3.9.0 ([#1865](https://github.com/torrentpier/torrentpier/pull/1865)) - ([6440162](https://github.com/torrentpier/torrentpier/commit/64401621879af0cc445c38687c571d2fec184410))
- *(deps)* Bump php-curl-class/php-curl-class from 11.0.5 to 11.1.0 ([#1864](https://github.com/torrentpier/torrentpier/pull/1864)) - ([de2fcea](https://github.com/torrentpier/torrentpier/commit/de2fceabedefd07441ba6801417157a9828e0e2a))
- *(deps)* Bump egulias/email-validator from 4.0.3 to 4.0.4 ([#1858](https://github.com/torrentpier/torrentpier/pull/1858)) - ([3ced460](https://github.com/torrentpier/torrentpier/commit/3ced460640e4bfe27a91acd0408e73c3c49e1534))
- *(deps)* Bump filp/whoops from 2.17.0 to 2.18.0 ([#1853](https://github.com/torrentpier/torrentpier/pull/1853)) - ([7ca0582](https://github.com/torrentpier/torrentpier/commit/7ca058256186b7b690003308d660a3a6271e84d2))
- *(deps)* Bump php-curl-class/php-curl-class from 11.0.4 to 11.0.5 ([#1849](https://github.com/torrentpier/torrentpier/pull/1849)) - ([37ad07a](https://github.com/torrentpier/torrentpier/commit/37ad07a40c1adf29f712f469d2850753d32a5eb9))
- *(deps)* Bump belomaxorka/captcha from 1.2.3 to 1.2.4 - ([4641b0a](https://github.com/torrentpier/torrentpier/commit/4641b0a0d0e055d684ec36d41bfaf22b4d4b2ee1))
- *(deps)* Bump belomaxorka/captcha from 1.2.2 to 1.2.3 ([#1842](https://github.com/torrentpier/torrentpier/pull/1842)) - ([be65f7c](https://github.com/torrentpier/torrentpier/commit/be65f7c55cbf81d889d5083c9344ccef400e8e19))
### 🚜 Refactor ### 🚜 Refactor
- Password generation ([#1847](https://github.com/torrentpier/torrentpier/pull/1847)) - ([af2403f](https://github.com/torrentpier/torrentpier/commit/af2403f1918845e8af3d9fa7708623eef6aa427e)) - *(censor)* [**breaking**] Migrate Censor class to singleton pattern ([#1954](https://github.com/torrentpier/torrentpier/pull/1954)) - ([74a564d](https://github.com/torrentpier/torrentpier/commit/74a564d7954c6f8745ebcffdcd9c8997e371d47a))
- Moved `Select` class into `Legacy\Common` ([#1846](https://github.com/torrentpier/torrentpier/pull/1846)) - ([bd0ef06](https://github.com/torrentpier/torrentpier/commit/bd0ef063fac328ed16537aacbc12e287a8d8206b)) - *(config)* [**breaking**] Encapsulate global $bb_cfg array in Config class ([#1950](https://github.com/torrentpier/torrentpier/pull/1950)) - ([5842994](https://github.com/torrentpier/torrentpier/commit/5842994782dfa62788f8427c55045abdbfb5b8e9))
### ⚙️ Miscellaneous
- *(.cliffignore)* Added one more commit ([#1860](https://github.com/torrentpier/torrentpier/pull/1860)) - ([974d359](https://github.com/torrentpier/torrentpier/commit/974d3590c1fb11c6314da4a4b8115a2229e32bbd))
- *(README)* Removed `Build actions` badge ([#1861](https://github.com/torrentpier/torrentpier/pull/1861)) - ([e9920ab](https://github.com/torrentpier/torrentpier/commit/e9920ab59803552e3a1a00b603962208a62efe4e))
- *(cliff)* Added `.cliffignore` file to ignore reverted commits ([#1859](https://github.com/torrentpier/torrentpier/pull/1859)) - ([2eab551](https://github.com/torrentpier/torrentpier/commit/2eab551bd75e7acfd6f4dabe13b2a30ac09db880))
- *(nightly builds)* Added cleanup step ([#1851](https://github.com/torrentpier/torrentpier/pull/1851)) - ([299d9a1](https://github.com/torrentpier/torrentpier/commit/299d9a1f6c4f244e435803212e763c252e5bd396))
- *(password_hash)* Changed `cost` to `12` by default ([#1886](https://github.com/torrentpier/torrentpier/pull/1886)) - ([1663e19](https://github.com/torrentpier/torrentpier/commit/1663e19c3f80ae15792d6ffe4ce64e40129b14db))
- *(render_flag)* Hide names for specified (`$nameIgnoreList`) flags ([#1862](https://github.com/torrentpier/torrentpier/pull/1862)) - ([83e42bc](https://github.com/torrentpier/torrentpier/commit/83e42bc5db086f60a6038b3fffca5982ceeced51))
- *(text captcha)* Disabled scatter effect by default - ([3af5202](https://github.com/torrentpier/torrentpier/commit/3af5202f7b2a4ea5d14bbc4808b7a380de2e0dc0))
- Updated nightly builds link ([#1885](https://github.com/torrentpier/torrentpier/pull/1885)) - ([6bd000b](https://github.com/torrentpier/torrentpier/commit/6bd000bc0d6176dbe1f0a573f081c9daefd3718b))
- Composer dependencies are installed according to the minimum supported PHP version ([#1884](https://github.com/torrentpier/torrentpier/pull/1884)) - ([5fe7700](https://github.com/torrentpier/torrentpier/commit/5fe770070e1cd71ea50ea3ad3825a322774f0baf))
- Corrected `php` version in `composer.json` ([#1882](https://github.com/torrentpier/torrentpier/pull/1882)) - ([bc1713a](https://github.com/torrentpier/torrentpier/commit/bc1713abdd28d04e8e1da3c3eabeb5170a35a460))
- Composer dependencies are installed according to the minimum supported PHP version ([#1881](https://github.com/torrentpier/torrentpier/pull/1881)) - ([5c4972e](https://github.com/torrentpier/torrentpier/commit/5c4972ec12340cbffb8ac941d390ee6c2c89b635))
- Minor improvements ([#1880](https://github.com/torrentpier/torrentpier/pull/1880)) - ([de8f192](https://github.com/torrentpier/torrentpier/commit/de8f1925bae3b38db18b86eb4a10337853638ad7))
- Minor improvements ([#1876](https://github.com/torrentpier/torrentpier/pull/1876)) - ([eeb391d](https://github.com/torrentpier/torrentpier/commit/eeb391da6a16440492a3b803f63be301ba3d02d3))
- Minor improvements ([#1875](https://github.com/torrentpier/torrentpier/pull/1875)) - ([41a78dd](https://github.com/torrentpier/torrentpier/commit/41a78ddbcbc628f0592c59879df0170bf48664aa))
- Minor improvements ([#1874](https://github.com/torrentpier/torrentpier/pull/1874)) - ([0f1a69e](https://github.com/torrentpier/torrentpier/commit/0f1a69e32d8d5eb5053b021844845911c619d8cd))
- Fetch only necessary sitemap parameters in `admin_sitemap.php` ([#1873](https://github.com/torrentpier/torrentpier/pull/1873)) - ([f9c8160](https://github.com/torrentpier/torrentpier/commit/f9c8160f8e897950a038a74ad7ee30b116f7b2b8))
- Changed placeholder IP address from `7f000001` to `0` ([#1869](https://github.com/torrentpier/torrentpier/pull/1869)) - ([84e2392](https://github.com/torrentpier/torrentpier/commit/84e23928968f943826bdc4390c52365357d56f32))
- Minor improvements ([#1866](https://github.com/torrentpier/torrentpier/pull/1866)) - ([7237653](https://github.com/torrentpier/torrentpier/commit/72376532b32395eda04dc032c07ca08b27346c6b))
- Some minor improvements ([#1855](https://github.com/torrentpier/torrentpier/pull/1855)) - ([3cc880e](https://github.com/torrentpier/torrentpier/commit/3cc880eeb8be41596d5e8eaf19297046500afcf7))
### ◀️ Revert
- Added `TorrentPier instance hash` generation - ([eabf851](https://github.com/torrentpier/torrentpier/commit/eabf851ee60d29835d1979f46dcf2b9d82576c1b))
- Added `IndexNow` protocol support 🤖 - ([1b288a9](https://github.com/torrentpier/torrentpier/commit/1b288a96e443e06c4f4e9ea374037d3b0af8a639))
## [v2.4.5-rc.4](https://github.com/torrentpier/torrentpier/compare/v2.4.5-rc.3..v2.4.5-rc.4) (2025-03-09)
### 🚀 Features
- *(captcha)* Added `Text Captcha` provider ([#1839](https://github.com/torrentpier/torrentpier/pull/1839)) - ([74ea157](https://github.com/torrentpier/torrentpier/commit/74ea1573b298be5a935caaca0b3cc57cb1e9264a))
- *(show post bbcode)* Added `'only_for_first_post'` param ([#1830](https://github.com/torrentpier/torrentpier/pull/1830)) - ([4dcd1fb](https://github.com/torrentpier/torrentpier/commit/4dcd1fb16e4e84acd1231ad821a2f05658b849ad))
- *(sitemap)* Update `lastmod` when a new reply in topic ([#1737](https://github.com/torrentpier/torrentpier/pull/1737)) - ([bc95e14](https://github.com/torrentpier/torrentpier/commit/bc95e14be328303bb37e31299661b03045e37d07))
- Added `$bb_cfg['auto_language_detection']` parametr ([#1835](https://github.com/torrentpier/torrentpier/pull/1835)) - ([b550fa5](https://github.com/torrentpier/torrentpier/commit/b550fa59f9ee96ca89e5b6db880147bc72841e93))
- Easter egg for the 20th anniversary of the TorrentPier! ([#1831](https://github.com/torrentpier/torrentpier/pull/1831)) - ([f2e513d](https://github.com/torrentpier/torrentpier/commit/f2e513dd8b0f82f4f02474db4b83d83904a93f29))
- Added configuration files for `nginx` & `caddy` ([#1787](https://github.com/torrentpier/torrentpier/pull/1787)) - ([f7d3946](https://github.com/torrentpier/torrentpier/commit/f7d394607e4ea5bb9b7f2b33692204a226a4d78b))
### 🐛 Bug Fixes
- *(info.php)* Undefined array key "show" ([#1836](https://github.com/torrentpier/torrentpier/pull/1836)) - ([f8c4e8f](https://github.com/torrentpier/torrentpier/commit/f8c4e8fb14090bc7403f24e363603bad9e231351))
- *(tr_seed_bonus.php)* Incorrect `GROUP BY` ([#1820](https://github.com/torrentpier/torrentpier/pull/1820)) - ([dfd4e5e](https://github.com/torrentpier/torrentpier/commit/dfd4e5ebc9df916868210a7844f2a6f35e7b8aca))
### 📦 Dependencies
- *(deps)* Bump bugsnag/bugsnag from 3.29.2 to 3.29.3 ([#1837](https://github.com/torrentpier/torrentpier/pull/1837)) - ([b954815](https://github.com/torrentpier/torrentpier/commit/b954815f5d0dce9520f65679e834d8bd49e571e0))
- *(deps)* Bump php-curl-class/php-curl-class from 11.0.3 to 11.0.4 ([#1823](https://github.com/torrentpier/torrentpier/pull/1823)) - ([1c323a4](https://github.com/torrentpier/torrentpier/commit/1c323a45d777b033155da9a2becec506215bd94c))
- *(deps)* Bump php-curl-class/php-curl-class from 11.0.1 to 11.0.3 ([#1821](https://github.com/torrentpier/torrentpier/pull/1821)) - ([dedf35b](https://github.com/torrentpier/torrentpier/commit/dedf35b794196034eb27d4125dff0798aed5f315))
### 🗑️ Removed
- *(posting.php)* Unused `'U_VIEWTOPIC` variable ([#1818](https://github.com/torrentpier/torrentpier/pull/1818)) - ([03ebbda](https://github.com/torrentpier/torrentpier/commit/03ebbda6be567d82d2a49fefe02356544fbd07cb))
- Integrity checker 🥺🪦 ([#1827](https://github.com/torrentpier/torrentpier/pull/1827)) - ([ba3ce88](https://github.com/torrentpier/torrentpier/commit/ba3ce885c8d84ae939a0ce9c79b97877d3aaab41))
- Redundant `.htaccess` files ([#1826](https://github.com/torrentpier/torrentpier/pull/1826)) - ([912b080](https://github.com/torrentpier/torrentpier/commit/912b080b16438b09f82fbc72a363589cc2f6209e))
### ⚙️ Miscellaneous
- *(Caddyfile)* Some minor fixes ([#1822](https://github.com/torrentpier/torrentpier/pull/1822)) - ([6f641aa](https://github.com/torrentpier/torrentpier/commit/6f641aa9d8d7afb30920c054a43347393ea05cc4))
- *(README)* Fixed all grammatical errors, sentence structure and readibility ([#1812](https://github.com/torrentpier/torrentpier/pull/1812)) - ([bea3b0b](https://github.com/torrentpier/torrentpier/commit/bea3b0bccf335970ea5826543d8fa223329ef077))
- *(_cleanup.php)* Added CLI mode check ([#1834](https://github.com/torrentpier/torrentpier/pull/1834)) - ([5dc9a54](https://github.com/torrentpier/torrentpier/commit/5dc9a5475c051911c579ea732ef52d7feb78e8ac))
- *(announcer)* Some minor improvements ([#1819](https://github.com/torrentpier/torrentpier/pull/1819)) - ([bdefed4](https://github.com/torrentpier/torrentpier/commit/bdefed4dab3cc65330fcb9cb9750cc8e84beda1d))
- *(cliff)* Removed TorrentPier logo ([#1817](https://github.com/torrentpier/torrentpier/pull/1817)) - ([7794242](https://github.com/torrentpier/torrentpier/commit/7794242750b44183312a2a45c9f54c6afde12f0e))
- *(cliff)* Synced `cliff-releases.toml` with `cliff.toml` changes ([#1815](https://github.com/torrentpier/torrentpier/pull/1815)) - ([f2aea92](https://github.com/torrentpier/torrentpier/commit/f2aea92b3d79d72254e696fde31ad9b4bec5dcd0))
- *(cliff)* Added missing line breaks after `body` ([#1814](https://github.com/torrentpier/torrentpier/pull/1814)) - ([2593f09](https://github.com/torrentpier/torrentpier/commit/2593f093a389a9c450725290862b99d911fbef5d))
- *(installer)* Added cleanup step (for master builds) ([#1838](https://github.com/torrentpier/torrentpier/pull/1838)) - ([dd72136](https://github.com/torrentpier/torrentpier/commit/dd721367c7dc9956861fcd33af7f9f822cf80011))
- *(installer)* Some minor improvements ([#1825](https://github.com/torrentpier/torrentpier/pull/1825)) - ([4f89685](https://github.com/torrentpier/torrentpier/commit/4f896854d3bb67300027f7542704f41c4869837f))
- *(installer)* Some minor improvements ([#1824](https://github.com/torrentpier/torrentpier/pull/1824)) - ([f3714f0](https://github.com/torrentpier/torrentpier/commit/f3714f02f2c8fbfaccfdafb8f25a269664c48950))
- *(workflow)* Short `release_name` ([#1816](https://github.com/torrentpier/torrentpier/pull/1816)) - ([c57db21](https://github.com/torrentpier/torrentpier/commit/c57db2104d7b8363d0b8ce8872ce90fc7410c724))
- *(workflow)* Added `workflow_dispatch` for `schedule.yml` ([#1813](https://github.com/torrentpier/torrentpier/pull/1813)) - ([d54c07b](https://github.com/torrentpier/torrentpier/commit/d54c07b3da00fc8bcba5413cd4ae3f3c9f6007bb))
- *(workflow)* Some improvements ([#1811](https://github.com/torrentpier/torrentpier/pull/1811)) - ([3a9dd6a](https://github.com/torrentpier/torrentpier/commit/3a9dd6a3c931cfbd682257c283a3296c4914548f))
- *(workflow)* Some improvements ([#1810](https://github.com/torrentpier/torrentpier/pull/1810)) - ([c168c39](https://github.com/torrentpier/torrentpier/commit/c168c3956cf77886c14133ac10ec33aa0ae5bc4e))
- Replaced `gregwar/captcha` with my own fork ([#1840](https://github.com/torrentpier/torrentpier/pull/1840)) - ([8585560](https://github.com/torrentpier/torrentpier/commit/858556043d3e45218ea8e803786d6b6de6d485d0))
- Created cleanup script (for releases preparation) ([#1833](https://github.com/torrentpier/torrentpier/pull/1833)) - ([68bf26d](https://github.com/torrentpier/torrentpier/commit/68bf26d0f4ab33f5394d26f425e53817f3464ac8))
- Bring back missing `cache` & `log` directories ([#1832](https://github.com/torrentpier/torrentpier/pull/1832)) - ([249c988](https://github.com/torrentpier/torrentpier/commit/249c9889890291d56317dd703414bdb57ecaa41f))
- Some minor improvements ([#1829](https://github.com/torrentpier/torrentpier/pull/1829)) - ([3b8ee4c](https://github.com/torrentpier/torrentpier/commit/3b8ee4c4d3ab4631425fbe44f197b6a9bd7d158c))
## New Contributors ❤️
* @xeddmc made their first contribution in [#1812](https://github.com/torrentpier/torrentpier/pull/1812)
## [v2.4.5-rc.3](https://github.com/torrentpier/torrentpier/compare/v2.4.5-rc.2..v2.4.5-rc.3) (2025-02-06)
### 🚀 Features
- *(announcer)* Added some disallowed ports by default ([#1767](https://github.com/torrentpier/torrentpier/pull/1767)) - ([46288ec](https://github.com/torrentpier/torrentpier/commit/46288ec19830c84aedb156e1f30d7ec8a0803e0d))
- *(announcer)* Added `is_numeric()` checking for some fields ([#1766](https://github.com/torrentpier/torrentpier/pull/1766)) - ([096bb51](https://github.com/torrentpier/torrentpier/commit/096bb5124fa27d27c3e60031edc432d877f1c507))
- *(announcer)* Added `event` verifying ([#1765](https://github.com/torrentpier/torrentpier/pull/1765)) - ([6a19323](https://github.com/torrentpier/torrentpier/commit/6a1932313801e55fbcfb047fdcef87266f472c33))
- *(announcer)* Block browser by checking the `User-Agent` ([#1764](https://github.com/torrentpier/torrentpier/pull/1764)) - ([7b64b50](https://github.com/torrentpier/torrentpier/commit/7b64b508199af568472fe6ac2edf333a3e274a00))
- *(announcer)* Block `User-Agent` strings that are too long ([#1763](https://github.com/torrentpier/torrentpier/pull/1763)) - ([a98f8f1](https://github.com/torrentpier/torrentpier/commit/a98f8f102a8253b0b22c80ef444fed1ec29177f3))
- *(announcer)* Blocking all ports lower then `1024` ([#1762](https://github.com/torrentpier/torrentpier/pull/1762)) - ([1bc7e09](https://github.com/torrentpier/torrentpier/commit/1bc7e09ddbeaf680b86095eed9a80b8ebf6169b3))
- *(cache)* Checking if extensions are installed ([#1759](https://github.com/torrentpier/torrentpier/pull/1759)) - ([7f31022](https://github.com/torrentpier/torrentpier/commit/7f31022cfca2acb28a5cba06961eeaf8d2c9de51))
- *(captcha)* Added some new services 🤖 ([#1771](https://github.com/torrentpier/torrentpier/pull/1771)) - ([d413c71](https://github.com/torrentpier/torrentpier/commit/d413c717188c9bd906f715e7137955dc9a42a003))
- *(environment)* Make configurable `TP_HOST` and `TP_PORT` ([#1780](https://github.com/torrentpier/torrentpier/pull/1780)) - ([e51e091](https://github.com/torrentpier/torrentpier/commit/e51e09159333382a77b809b5f1da5e152a713143))
- *(installer)* Fully show non-installed extensions ([#1761](https://github.com/torrentpier/torrentpier/pull/1761)) - ([8fcc62d](https://github.com/torrentpier/torrentpier/commit/8fcc62d2a2fd41927b2f5dae215fe5bbf95f2c96))
- *(installer)* More explanations ([#1758](https://github.com/torrentpier/torrentpier/pull/1758)) - ([48ab52a](https://github.com/torrentpier/torrentpier/commit/48ab52ac8674afcb607c8e49134316a3e117236a))
- *(installer)* Check `Composer` dependencies after installing ([#1756](https://github.com/torrentpier/torrentpier/pull/1756)) - ([262b887](https://github.com/torrentpier/torrentpier/commit/262b8872a5b14068eb73d745adea6203c557e192))
- *(installer)* More explanations ([#1754](https://github.com/torrentpier/torrentpier/pull/1754)) - ([fd6f1f8](https://github.com/torrentpier/torrentpier/commit/fd6f1f86a5e9216469cd648601ecb9ba875f9eb6))
- *(installer)* Create `config.local.php` on local environment ([#1745](https://github.com/torrentpier/torrentpier/pull/1745)) - ([0d93b2c](https://github.com/torrentpier/torrentpier/commit/0d93b2c768c2965c12ac62e2f3b2886dc1ef31c2))
- *(torrent)* Bring back old torrent file naming ([#1783](https://github.com/torrentpier/torrentpier/pull/1783)) - ([314c592](https://github.com/torrentpier/torrentpier/commit/314c592affbef4b8db48d562b9633aad27059a76))
- *(workflow)* Automated deploy actual changes to `TorrentPier Demo` ([#1788](https://github.com/torrentpier/torrentpier/pull/1788)) - ([4333d6a](https://github.com/torrentpier/torrentpier/commit/4333d6aca4aeb8584ff8a8ef0bf76c537a3f371a))
- Used `TORRENT_MIMETYPE` constant instead of hardcoded string ([#1757](https://github.com/torrentpier/torrentpier/pull/1757)) - ([4b0d270](https://github.com/torrentpier/torrentpier/commit/4b0d270c89ec06abed590504f6a0cb70076a9e59))
### 🐛 Bug Fixes
- *(announcer)* Null `event` exception ([#1784](https://github.com/torrentpier/torrentpier/pull/1784)) - ([b06e327](https://github.com/torrentpier/torrentpier/commit/b06e327cbb285a676814699eb5fb1fbc0e1f22e8))
- *(bb_die)* HTML characters converting ([#1744](https://github.com/torrentpier/torrentpier/pull/1744)) - ([4f1c7e4](https://github.com/torrentpier/torrentpier/commit/4f1c7e40d82e52f81eba44ead501e1f01058cc4f))
- *(debug)* Disabled `Bugsnag` reporting on local environment ([#1751](https://github.com/torrentpier/torrentpier/pull/1751)) - ([1f3b629](https://github.com/torrentpier/torrentpier/commit/1f3b629e9cea4d11fbf3cf29f575ba730bad898d))
- *(installer)* Missing `gd` extension ([#1749](https://github.com/torrentpier/torrentpier/pull/1749)) - ([a1c519d](https://github.com/torrentpier/torrentpier/commit/a1c519d938b848edffcbf7fbbe6a3fdb9a5394f1))
- *(youtube player)* Mixed content issue ([#1795](https://github.com/torrentpier/torrentpier/pull/1795)) - ([3c0a1d5](https://github.com/torrentpier/torrentpier/commit/3c0a1d5d0018daa87ad3914ea04078a9a6d05fc2))
- Incorrect peer country flag ([#1768](https://github.com/torrentpier/torrentpier/pull/1768)) - ([0f091eb](https://github.com/torrentpier/torrentpier/commit/0f091eb546e34923d9d1ab34be5faf92080ec198))
### 📦 Dependencies
- *(deps)* Bump jacklul/monolog-telegram from 3.1.0 to 3.2.0 ([#1776](https://github.com/torrentpier/torrentpier/pull/1776)) - ([420c92c](https://github.com/torrentpier/torrentpier/commit/420c92c0addf4dee91f3ae872517cb3224827a1f))
- *(deps)* Bump filp/whoops from 2.16.0 to 2.17.0 ([#1777](https://github.com/torrentpier/torrentpier/pull/1777)) - ([a71609b](https://github.com/torrentpier/torrentpier/commit/a71609ba67a89480fabb7b62de450d9be09373fa))
- *(deps)* Bump php-curl-class/php-curl-class from 11.0.0 to 11.0.1 ([#1753](https://github.com/torrentpier/torrentpier/pull/1753)) - ([ce32031](https://github.com/torrentpier/torrentpier/commit/ce32031a0fb14cdf6c3f4ba379b530cbb52b0fea))
- *(deps)* Bump bugsnag/bugsnag from 3.29.1 to 3.29.2 ([#1752](https://github.com/torrentpier/torrentpier/pull/1752)) - ([f63d15c](https://github.com/torrentpier/torrentpier/commit/f63d15c49e3992837413b2c7a0160d599b44f2ef))
### 🗑️ Removed
- *(environment)* Extra `DB_CONNECTION` variable ([#1775](https://github.com/torrentpier/torrentpier/pull/1775)) - ([cd2786b](https://github.com/torrentpier/torrentpier/commit/cd2786bb69c74cec88a447f66750d014fc4d3612))
- Some unused tracker config variables ([#1769](https://github.com/torrentpier/torrentpier/pull/1769)) - ([7f9df35](https://github.com/torrentpier/torrentpier/commit/7f9df35d3bd0e9d23284b8bd9c36a0f52158f5d7))
### 📚 Documentation ### 📚 Documentation
- Minor improvements ([#1750](https://github.com/torrentpier/torrentpier/pull/1750)) - ([3e850ac](https://github.com/torrentpier/torrentpier/commit/3e850ac724c43e813aa077b272b498e2b0477260)) - Add Select class migration guide ([#1960](https://github.com/torrentpier/torrentpier/pull/1960)) - ([86abafb](https://github.com/torrentpier/torrentpier/commit/86abafb11469d14a746d12725b15cf6b7015ec44))
### ⚙️ Miscellaneous ### ⚙️ Miscellaneous
- *(cd workflow)* Fixed release body creation ([#1809](https://github.com/torrentpier/torrentpier/pull/1809)) - ([7378cb3](https://github.com/torrentpier/torrentpier/commit/7378cb3af5cc56343c667a9d920038b05327e97b)) - *(_release.php)* Finally! Removed some useless params ([#1947](https://github.com/torrentpier/torrentpier/pull/1947)) - ([9c7d270](https://github.com/torrentpier/torrentpier/commit/9c7d270598c0153fb82f4b7ad96f5b59399b2159))
- *(cd workflow)* Fixed release body creation ([#1807](https://github.com/torrentpier/torrentpier/pull/1807)) - ([cc679a8](https://github.com/torrentpier/torrentpier/commit/cc679a80246f3ff65136653025d826bf1458db3a)) - *(cliff)* Add conventional commit prefix to changelog message ([#1957](https://github.com/torrentpier/torrentpier/pull/1957)) - ([b1b2618](https://github.com/torrentpier/torrentpier/commit/b1b26187579f6981165d85c316a3c5b7199ce2ee))
- *(changelog workflow)* Minor improvements ([#1802](https://github.com/torrentpier/torrentpier/pull/1802)) - ([15ca21f](https://github.com/torrentpier/torrentpier/commit/15ca21f03840281f7d4402959aa8bfb7d407b45b))
- *(checksum workflow)* Fixed incorrect file path ([#1799](https://github.com/torrentpier/torrentpier/pull/1799)) - ([4eb5a9a](https://github.com/torrentpier/torrentpier/commit/4eb5a9adc61c4e116feb09208091efb914275da2))
- *(cliff)* Changed emoji for dependencies ([#1755](https://github.com/torrentpier/torrentpier/pull/1755)) - ([55d4670](https://github.com/torrentpier/torrentpier/commit/55d467048370b51cd592982c8026702dca8813d5))
- *(cliff)* Use blockquote for notice ([#1748](https://github.com/torrentpier/torrentpier/pull/1748)) - ([61e5592](https://github.com/torrentpier/torrentpier/commit/61e55925f312417bdb63c88a7c8939c3b2eb2ac5))
- *(cliff)* Fixed typo ([#1747](https://github.com/torrentpier/torrentpier/pull/1747)) - ([4936af7](https://github.com/torrentpier/torrentpier/commit/4936af7d3d10f553d8586a14de249c32e50f3494))
- *(cliff)* Notice about previous changelog file ([#1746](https://github.com/torrentpier/torrentpier/pull/1746)) - ([85395be](https://github.com/torrentpier/torrentpier/commit/85395be5e7c6a891c79ec72cf215894af097f819))
- *(copyright)* Updated copyright year ([#1760](https://github.com/torrentpier/torrentpier/pull/1760)) - ([6697410](https://github.com/torrentpier/torrentpier/commit/6697410c1df6c8d9d7f511b1e984ae90d888ae0e))
- *(database)* Use `DEFAULT ''` for `privmsgs_subject` ([#1786](https://github.com/torrentpier/torrentpier/pull/1786)) - ([387a258](https://github.com/torrentpier/torrentpier/commit/387a25870abd37b641b55ffd98e13f4aaecb73b1))
- *(deploy action)* Specify some missing params ([#1789](https://github.com/torrentpier/torrentpier/pull/1789)) - ([6115900](https://github.com/torrentpier/torrentpier/commit/6115900b765752209a6ed1dfb83e4f0cbee2ae77))
- *(emailer)* Use constants for email types ([#1794](https://github.com/torrentpier/torrentpier/pull/1794)) - ([c95d414](https://github.com/torrentpier/torrentpier/commit/c95d414ef63ca37118f1f660880cd58b4480c414))
- *(integrity checker)* Disabled by default in `Demo mode` ([#1804](https://github.com/torrentpier/torrentpier/pull/1804)) - ([44be40c](https://github.com/torrentpier/torrentpier/commit/44be40c2e849c60eb4f10ca7e0bae0463791355e))
- *(integrity checker)* Some enhancements ([#1797](https://github.com/torrentpier/torrentpier/pull/1797)) - ([09cafc2](https://github.com/torrentpier/torrentpier/commit/09cafc2285dd171cb2213ece9549993a3321527c))
- *(issue template)* Improved `Feature request` template ([#1774](https://github.com/torrentpier/torrentpier/pull/1774)) - ([268f79d](https://github.com/torrentpier/torrentpier/commit/268f79d7259de67aa8877fcf7130ff0069469ab2))
- *(issue template)* Improved `Bug report` template ([#1773](https://github.com/torrentpier/torrentpier/pull/1773)) - ([53ebfef](https://github.com/torrentpier/torrentpier/commit/53ebfef32c0e9016257e03b96ef96349e22d3e9b))
- *(notify)* Hide notify checkbox in topic for guests ([#1793](https://github.com/torrentpier/torrentpier/pull/1793)) - ([8e4cd97](https://github.com/torrentpier/torrentpier/commit/8e4cd97734fc46f33459c4b00a0fe38b0597f92b))
- *(readme)* Improved installation guide ([#1781](https://github.com/torrentpier/torrentpier/pull/1781)) - ([e579b81](https://github.com/torrentpier/torrentpier/commit/e579b816b4dc346b3242cb3d9db292ad05596c1f))
- *(readme)* Minor improvements ([#1779](https://github.com/torrentpier/torrentpier/pull/1779)) - ([5b0ed02](https://github.com/torrentpier/torrentpier/commit/5b0ed020890a8f938df912f9215cccbda42b0317))
- *(readme)* Added Caddy webserver ([#1778](https://github.com/torrentpier/torrentpier/pull/1778)) - ([970a028](https://github.com/torrentpier/torrentpier/commit/970a0282e3631c403029c959ffd46b21c5cad0cd))
- *(workflow)* Refactored all workflows ([#1803](https://github.com/torrentpier/torrentpier/pull/1803)) - ([a29d57b](https://github.com/torrentpier/torrentpier/commit/a29d57b2f8673733bbfbea3fb96eebe841078d49))
- *(workflow)* Trying combine `changelog workflow` with `checksums workflow` ([#1800](https://github.com/torrentpier/torrentpier/pull/1800)) - ([60c6057](https://github.com/torrentpier/torrentpier/commit/60c605778412335ce97d41489c3b6ee9c051454b))
- Automated releases generation ([#1808](https://github.com/torrentpier/torrentpier/pull/1808)) - ([6c9372c](https://github.com/torrentpier/torrentpier/commit/6c9372c407327c9bb443b2ecf16eff64c0245c4b))
- Automated releases generation ([#1806](https://github.com/torrentpier/torrentpier/pull/1806)) - ([bc74550](https://github.com/torrentpier/torrentpier/commit/bc745502940207f3f24c83057cd680fe69355961))
- Automated releases generation ([#1805](https://github.com/torrentpier/torrentpier/pull/1805)) - ([425e2e8](https://github.com/torrentpier/torrentpier/commit/425e2e87d5a7f097b961b1a14fbafcdabb9d1666))
- Minor improvements ([#1796](https://github.com/torrentpier/torrentpier/pull/1796)) - ([8650ad3](https://github.com/torrentpier/torrentpier/commit/8650ad30f429ab14a03f44b26d7be7701f1985f1))
- Update `cliff.toml` - ([254dca2](https://github.com/torrentpier/torrentpier/commit/254dca2b27c2d92421d3e639c80b0adf1172202f))
- Minor improvements ([#1743](https://github.com/torrentpier/torrentpier/pull/1743)) - ([e73d650](https://github.com/torrentpier/torrentpier/commit/e73d65011fff0a8b8e1368eef61bbfb67e87eab8))
- Enabled `$bb_cfg['integrity_check']` by defaul ([#1742](https://github.com/torrentpier/torrentpier/pull/1742)) - ([7e3601e](https://github.com/torrentpier/torrentpier/commit/7e3601e63aff73be1428969ca37dda3da2537d9b))
## New Contributors ❤️
* @actions-user made their first contribution

144
CLAUDE.md Normal file
View file

@ -0,0 +1,144 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
TorrentPier is a BitTorrent tracker engine written in PHP, designed for hosting BitTorrent communities with forum functionality. The project is in active modernization, transitioning from legacy code to modern PHP practices while maintaining backward compatibility.
## Technology Stack & Architecture
- **PHP 8.2+** with modern features
- **MySQL/MariaDB/Percona** database
- **Nette Database** with backward-compatible wrapper
- **Composer** for dependency management
- **Custom BitTorrent tracker** implementation
## Key Directory Structure
- `/src/` - Modern PHP classes (PSR-4 autoloaded as `TorrentPier\`)
- `/library/` - Core application logic and legacy code
- `/admin/` - Administrative interface
- `/bt/` - BitTorrent tracker functionality (announce.php, scrape.php)
- `/styles/` - Templates, CSS, JS, images
- `/internal_data/` - Cache, logs, compiled templates
- `/install/` - Installation scripts and configuration examples
- `/migrations/` - Database migration files (Phinx)
## Entry Points & Key Files
- `index.php` - Main forum homepage
- `tracker.php` - Torrent search/browse interface
- `bt/announce.php` - BitTorrent announce endpoint
- `bt/scrape.php` - BitTorrent scrape endpoint
- `admin/index.php` - Administrative panel
- `cron.php` - Background task runner (CLI only)
- `install.php` - Installation script (CLI only)
## Development Commands
### Installation & Setup
```bash
# Automated installation (CLI)
php install.php
# Install dependencies
composer install
# Update dependencies
composer update
```
### Maintenance & Operations
```bash
# Run background maintenance tasks
php cron.php
```
### Code Quality
The project uses **StyleCI** with PSR-2 preset for code style enforcement. StyleCI configuration is in `.styleci.yml` targeting `src/` directory.
## Modern Architecture Components
### Database Layer (`/src/Database/`)
- **Nette Database** with full old SqlDb backward compatibility
- Singleton pattern accessible via `DB()` function
- Support for multiple database connections and debug functionality
- Migration path to ORM-style Explorer queries
### Cache System (`/src/Cache/`)
- **Unified caching** using Nette Caching internally
- 100% backward compatibility with existing `CACHE()` and $datastore calls
- Supports file, SQLite, memory, and Memcached storage
- Advanced features: memoization, cache dependencies
### Configuration Management
- Environment-based config with `.env` files
- Singleton `Config` class accessible via `config()` function
- Local overrides supported via `library/config.local.php`
## Configuration Files
- `.env` - Environment variables (copy from `.env.example`)
- `library/config.php` - Main application configuration
- `library/config.local.php` - Local configuration overrides
- `composer.json` - Dependencies and PSR-4 autoloading
## Development Workflow
### CI/CD Pipeline
- **GitHub Actions** for automated testing and deployment
- **StyleCI** for code style enforcement
- **Dependabot** for dependency updates
- **FTP deployment** to demo environment
### Installation Methods
1. **Automated**: `php install.php` (recommended)
2. **Composer**: `composer create-project torrentpier/torrentpier`
3. **Manual**: Git clone + `composer install` + database setup
## Database & Schema
- **Database migrations** managed via Phinx in `/migrations/` directory
- Initial schema: `20250619000001_initial_schema.php`
- Initial seed data: `20250619000002_seed_initial_data.php`
- UTF-8 (utf8mb4) character set required
- Multiple database alias support for different components
### Migration Commands
```bash
# Run all pending migrations
php vendor/bin/phinx migrate --configuration=phinx.php
# Check migration status
php vendor/bin/phinx status --configuration=phinx.php
# Mark migrations as applied (for existing installations)
php vendor/bin/phinx migrate --fake --configuration=phinx.php
```
## Legacy Compatibility Strategy
The codebase maintains 100% backward compatibility while introducing modern alternatives:
- **Database layer**: Existing old SqlDb calls work while new code can use Nette Database
- **Cache system**: All existing `CACHE()` and $datastore calls preserved while adding modern features
- **Configuration**: Legacy config access maintained alongside new singleton pattern
This approach allows gradual modernization without breaking existing functionality - critical for a mature application with existing deployments.
## Security & Performance
- **Environment-based secrets** management via `.env`
- **CDN/proxy support** (Cloudflare, Fastly)
- **Input sanitization** and CSRF protection
- **Advanced caching** with multiple storage backends
- **Rate limiting** and IP-based restrictions
## BitTorrent Tracker Features
- **BitTorrent v1 & v2** support
- **TorrServer integration** capability
- **Client ban system** for problematic torrent clients
- **Scrape support** for tracker statistics
When working with this codebase, prioritize understanding the legacy compatibility approach and modern architecture patterns. Always test both legacy and modern code paths when making changes to core systems.

1131
HISTORY.md

File diff suppressed because it is too large Load diff

View file

@ -19,15 +19,15 @@
## 🐂 About TorrentPier ## 🐂 About TorrentPier
TorrentPier — bull-powered BitTorrent Public/Private tracker engine, written in PHP. High speed, simple modifications, load-balanced TorrentPier — bull-powered BitTorrent Public/Private tracker engine, written in PHP. High speed, simple modifications, load-balanced
architecture. In addition, we have a very helpful architecture. In addition, we have a very helpful
[official support forum](https://torrentpier.com), where it's possible to get support and download modifications for the engine. [official support forum](https://torrentpier.com), where it's possible to get support and download modifications for the engine.
## 🌈 Current status ## 🌈 Current status
TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite the existing code to TorrentPier is currently in active development. The goal is to remove all legacy code and rewrite the existing code to
modern specifications. If you want delve deep into the code, check our [issues](https://github.com/torrentpier/torrentpier/issues) modern specifications. If you want to delve deep into the code, check our [issues](https://github.com/torrentpier/torrentpier/issues)
and go from there. The documentation will be translated to english in the near future, currently russian is the main language of it. and go from there. The documentation will be translated to English in the near future, currently Russian is the main language.
## ✨ Features ## ✨ Features
* Rich forum with browsing/moderation tools * Rich forum with browsing/moderation tools
@ -40,7 +40,7 @@ and go from there. The documentation will be translated to english in the near f
* Bonus points * Bonus points
* Polling system * Polling system
* PM/DM system * PM/DM system
* Multilingual support (Russian and English is currently fully supported, with others in the future) * Multilingual support (Russian and English are currently fully supported, with others in the future)
* Atom/RSS feeds * Atom/RSS feeds
* ... and so MUCH MORE! * ... and so MUCH MORE!
@ -56,8 +56,8 @@ and go from there. The documentation will be translated to english in the near f
## 🔧 Requirements ## 🔧 Requirements
* Apache / nginx ([example config](install/nginx.conf)) / caddy ([example config](install/Caddyfile)) * Apache / nginx ([example config](install/nginx.conf)) / caddy ([example config](install/Caddyfile))
* MySQL 5.5.3 or above / MariaDB 10.0 or above / Percona * MySQL 5.5.3 or above (including MySQL 8.0+) / MariaDB 10.0 or above / Percona
* PHP: 8.1 / 8.2 / 8.3 / 8.4 * PHP: 8.2 / 8.3 / 8.4
* PHP Extensions: mbstring, gd, bcmath, intl, tidy (optional), xml, xmlwriter * PHP Extensions: mbstring, gd, bcmath, intl, tidy (optional), xml, xmlwriter
* Crontab (Recommended) * Crontab (Recommended)
@ -69,7 +69,7 @@ For the installation, select one of the installation variants below:
Check out our [autoinstall](https://github.com/torrentpier/autoinstall) repository with detailed instructions. Check out our [autoinstall](https://github.com/torrentpier/autoinstall) repository with detailed instructions.
> [!IMPORTANT] > [!NOTE]
> Thanks to [Sergei Solovev](https://github.com/SeAnSolovev) for this installation script ❤️ > Thanks to [Sergei Solovev](https://github.com/SeAnSolovev) for this installation script ❤️
### Quick (For web-panels) ☕️ ### Quick (For web-panels) ☕️
@ -100,14 +100,20 @@ Check out our [autoinstall](https://github.com/torrentpier/autoinstall) reposito
```shell ```shell
composer install composer install
``` ```
5. Create a database and import the dump located at `install/sql/mysql.sql` 5. Edit database configuration settings in the environment (`.env.example`), after, rename to `.env`
6. Edit database configuration settings in the environment (`.env.example`), after, rename to `.env` 6. Create a database and run migrations to set up the schema
```shell
php vendor/bin/phinx migrate --configuration=phinx.php
```
7. Provide write permissions to the specified folders: 7. Provide write permissions to the specified folders:
* `data/avatars`, `data/uploads`, `data/uploads/thumbs` * `data/avatars`, `data/uploads`, `data/uploads/thumbs`
* `internal_data/atom`, `internal_data/cache`, `internal_data/log`, `internal_data/triggers` * `internal_data/atom`, `internal_data/cache`, `internal_data/log`, `internal_data/triggers`
* `sitemap` * `sitemap`
8. Voila! ✨ 8. Voila! ✨
> [!TIP]
> You can automate steps 4-7 by running `php install.php` instead, which will guide you through the setup process interactively.
> [!IMPORTANT] > [!IMPORTANT]
> The specific settings depend on the server you are using, but in general we recommend chmod **0755** for folders, and chmod **0644** for the files in them. > The specific settings depend on the server you are using, but in general we recommend chmod **0755** for folders, and chmod **0644** for the files in them.
@ -122,15 +128,29 @@ Check out our [autoinstall](https://github.com/torrentpier/autoinstall) reposito
If you discover a security vulnerability within TorrentPier, please follow our [security policy](https://github.com/torrentpier/torrentpier/security/policy), so we can address it promptly. If you discover a security vulnerability within TorrentPier, please follow our [security policy](https://github.com/torrentpier/torrentpier/security/policy), so we can address it promptly.
## 🧪 Testing
TorrentPier includes a comprehensive testing suite built with **Pest PHP**. Run tests to ensure code quality and system reliability:
```shell
# Run all tests
./vendor/bin/pest
# Run with coverage
./vendor/bin/pest --coverage
```
For detailed testing documentation, see [tests/README.md](tests/README.md).
## 📌 Our recommendations ## 📌 Our recommendations
* *It's recommended to run `cron.php`.* - For significant tracker speed increase it ay be required to replace the built-in cron.php in operating system daemon. * *It's recommended to run `cron.php`.* - For significant tracker speed increase it may be required to replace the built-in cron.php with an operating system daemon.
* *Local configuration copy.* - You can override the settings using the local configuration file `library/config.local.php`. * *Local configuration copy.* - You can override the settings using the local configuration file `library/config.local.php`.
## 💚 Contributing / Contributors ## 💚 Contributing / Contributors
Please read our [contributing policy](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for details, and the process for Please read our [contributing policy](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) for details, and the process for
submitting pull requests to us. But we are always ready to renew your pull-request for compliance with submitting pull requests to us. But we are always ready to review your pull-request for compliance with
these requirements. Just send it! these requirements. Just send it!
<a href="https://github.com/torrentpier/torrentpier/graphs/contributors"> <a href="https://github.com/torrentpier/torrentpier/graphs/contributors">
@ -141,7 +161,7 @@ Made with [contrib.rocks](https://contrib.rocks).
## 💞 Sponsoring ## 💞 Sponsoring
Support this project by becoming a sponsor or a backer. Support this project by becoming a sponsor or a backer.
[![OpenCollective sponsors](https://opencollective.com/torrentpier/sponsors/badge.svg)](https://opencollective.com/torrentpier) [![OpenCollective sponsors](https://opencollective.com/torrentpier/sponsors/badge.svg)](https://opencollective.com/torrentpier)
[![OpenCollective backers](https://opencollective.com/torrentpier/backers/badge.svg)](https://opencollective.com/torrentpier) [![OpenCollective backers](https://opencollective.com/torrentpier/backers/badge.svg)](https://opencollective.com/torrentpier)
@ -164,7 +184,7 @@ Support this project by becoming a sponsor or a backer.
## 📦 Versioning ## 📦 Versioning
We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags). We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/torrentpier/torrentpier/tags).
## 📖 License ## 📖 License

1261
UPGRADE_GUIDE.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,7 @@ if (!defined('BB_ROOT')) {
} }
// Check CLI mode // Check CLI mode
if (php_sapi_name() !== 'cli') { if (PHP_SAPI != 'cli') {
exit; exit;
} }
@ -33,13 +33,17 @@ $items = [
'.styleci.yml', '.styleci.yml',
'_release.php', '_release.php',
'CHANGELOG.md', 'CHANGELOG.md',
'CLAUDE.md',
'cliff.toml', 'cliff.toml',
'CODE_OF_CONDUCT.md', 'CODE_OF_CONDUCT.md',
'CONTRIBUTING.md', 'CONTRIBUTING.md',
'crowdin.yml', 'crowdin.yml',
'HISTORY.md', 'HISTORY.md',
'phpunit.xml',
'README.md', 'README.md',
'SECURITY.md' 'SECURITY.md',
'tests',
'UPGRADE_GUIDE.md'
]; ];
foreach ($items as $item) { foreach ($items as $item) {

View file

@ -11,7 +11,7 @@ define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT); define('BB_PATH', BB_ROOT);
// Check CLI mode // Check CLI mode
if (php_sapi_name() !== 'cli') { if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode'); die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
} }
@ -114,7 +114,7 @@ if ($bytesWritten === 0) {
out("\n- Config file has been updated!", 'success'); out("\n- Config file has been updated!", 'success');
// Update CHANGELOG.md // Update CHANGELOG.md
runProcess('npx git-cliff v2.4.5-rc.2.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md'); runProcess('npx git-cliff v2.4.6-alpha.4.. --config cliff.toml --tag "' . $version . '" > CHANGELOG.md');
// Git add & commit // Git add & commit
runProcess('git add -A && git commit -m "release: ' . escapeshellarg($version) . (!empty($versionEmoji) ? (' ' . $versionEmoji) : '') . '"'); runProcess('git add -A && git commit -m "release: ' . escapeshellarg($version) . (!empty($versionEmoji) ? (' ' . $versionEmoji) : '') . '"');
@ -124,8 +124,7 @@ runProcess("git tag -a \"$version\" -m \"Release $version\"");
runProcess("git tag -v \"$version\""); runProcess("git tag -v \"$version\"");
// Git push // Git push
runProcess("git checkout v2.4"); runProcess("git push origin master");
runProcess("git push origin v2.4");
runProcess("git push origin $version"); runProcess("git push origin $version");
out("\n- Release $version has been successfully prepared, committed and pushed!", 'success'); out("\n- Release $version has been successfully prepared, committed and pushed!", 'success');

View file

@ -69,44 +69,44 @@ $order_by = '';
if ($view === 'username') { if ($view === 'username') {
switch ($mode) { switch ($mode) {
case 'username': case 'username':
$order_by = 'ORDER BY u.username ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY u.username ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'attachments': case 'attachments':
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'filesize': case 'filesize':
$order_by = 'ORDER BY total_size ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY total_size ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
default: default:
$mode = 'attachments'; $mode = 'attachments';
$sort_order = 'DESC'; $sort_order = 'DESC';
$order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY total_attachments ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
} }
} elseif ($view === 'attachments') { } elseif ($view === 'attachments') {
switch ($mode) { switch ($mode) {
case 'real_filename': case 'real_filename':
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'comment': case 'comment':
$order_by = 'ORDER BY a.comment ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.comment ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'extension': case 'extension':
$order_by = 'ORDER BY a.extension ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.extension ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'filesize': case 'filesize':
$order_by = 'ORDER BY a.filesize ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.filesize ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'downloads': case 'downloads':
$order_by = 'ORDER BY a.download_count ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.download_count ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
case 'post_time': case 'post_time':
$order_by = 'ORDER BY a.filetime ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.filetime ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
default: default:
$mode = 'a.real_filename'; $mode = 'a.real_filename';
$sort_order = 'ASC'; $sort_order = 'ASC';
$order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . $bb_cfg['topics_per_page']; $order_by = 'ORDER BY a.real_filename ' . $sort_order . ' LIMIT ' . $start . ', ' . config()->get('topics_per_page');
break; break;
} }
} }
@ -470,8 +470,8 @@ if ($view === 'attachments') {
} }
// Generate Pagination // Generate Pagination
if ($do_pagination && $total_rows > $bb_cfg['topics_per_page']) { if ($do_pagination && $total_rows > config()->get('topics_per_page')) {
generate_pagination('admin_attach_cp.php?view=' . $view . '&amp;mode=' . $mode . '&amp;order=' . $sort_order . '&amp;uid=' . $uid, $total_rows, $bb_cfg['topics_per_page'], $start); generate_pagination('admin_attach_cp.php?view=' . $view . '&amp;mode=' . $mode . '&amp;order=' . $sort_order . '&amp;uid=' . $uid, $total_rows, config()->get('topics_per_page'), $start);
} }
print_page('admin_attach_cp.tpl', 'admin'); print_page('admin_attach_cp.tpl', 'admin');

View file

@ -151,7 +151,7 @@ if ($var =& $_REQUEST[$daysback_key] && $var != $def_days) {
$url = url_arg($url, $daysback_key, $daysback_val); $url = url_arg($url, $daysback_key, $daysback_val);
} }
if ($var =& $_REQUEST[$datetime_key] && $var != $def_datetime) { if ($var =& $_REQUEST[$datetime_key] && $var != $def_datetime) {
$tz = TIMENOW + (3600 * $bb_cfg['board_timezone']); $tz = TIMENOW + (3600 * config()->get('board_timezone'));
if (($tmp_timestamp = strtotime($var, $tz)) > 0) { if (($tmp_timestamp = strtotime($var, $tz)) > 0) {
$datetime_val = $tmp_timestamp; $datetime_val = $tmp_timestamp;
$url = url_arg($url, $datetime_key, date($dt_format, $datetime_val)); $url = url_arg($url, $datetime_key, date($dt_format, $datetime_val));

View file

@ -14,7 +14,7 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php'; require __DIR__ . '/pagestart.php';
if (!$bb_cfg['emailer']['enabled']) { if (!config()->get('emailer.enabled')) {
bb_die($lang['EMAILER_DISABLED']); bb_die($lang['EMAILER_DISABLED']);
} }
@ -23,7 +23,7 @@ set_time_limit(1200);
$subject = trim(request_var('subject', '')); $subject = trim(request_var('subject', ''));
$message = (string)request_var('message', ''); $message = (string)request_var('message', '');
$group_id = (int)request_var(POST_GROUPS_URL, 0); $group_id = (int)request_var(POST_GROUPS_URL, 0);
$reply_to = (string)request_var('reply_to', $bb_cfg['board_email']); $reply_to = (string)request_var('reply_to', config()->get('board_email'));
$message_type = (string)request_var('message_type', ''); $message_type = (string)request_var('message_type', '');
$errors = $user_id_sql = []; $errors = $user_id_sql = [];

View file

@ -0,0 +1,79 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!empty($setmodules)) {
if (IS_SUPER_ADMIN) {
$module['GENERAL']['MIGRATIONS_STATUS'] = basename(__FILE__);
}
return;
}
require __DIR__ . '/pagestart.php';
if (!IS_SUPER_ADMIN) {
bb_die(__('ONLY_FOR_SUPER_ADMIN'));
}
use TorrentPier\Database\MigrationStatus;
// Initialize migration status
$migrationStatus = new MigrationStatus();
$status = $migrationStatus->getMigrationStatus();
$schemaInfo = $migrationStatus->getSchemaInfo();
// Template variables
$template->assign_vars([
'PAGE_TITLE' => __('MIGRATIONS_STATUS'),
'CURRENT_TIME' => date('Y-m-d H:i:s'),
// Migration status individual fields
'MIGRATION_TABLE_EXISTS' => $status['table_exists'],
'MIGRATION_CURRENT_VERSION' => $status['current_version'],
'MIGRATION_APPLIED_COUNT' => count($status['applied_migrations']),
'MIGRATION_PENDING_COUNT' => count($status['pending_migrations']),
// Setup status fields
'SETUP_REQUIRES_SETUP' => $status['requires_setup'] ?? false,
'SETUP_TYPE' => $status['setup_status']['type'] ?? __('UNKNOWN'),
'SETUP_MESSAGE' => $status['setup_status']['message'] ?? '',
'SETUP_ACTION_REQUIRED' => $status['setup_status']['action_required'] ?? false,
'SETUP_INSTRUCTIONS' => $status['setup_status']['instructions'] ?? '',
// Schema info individual fields
'SCHEMA_DATABASE_NAME' => $schemaInfo['database_name'],
'SCHEMA_TABLE_COUNT' => $schemaInfo['table_count'],
'SCHEMA_SIZE_MB' => $schemaInfo['size_mb'],
]);
// Assign migration data for template
if (!empty($status['applied_migrations'])) {
foreach ($status['applied_migrations'] as $i => $migration) {
$template->assign_block_vars('applied_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['migration_name'] ?? __('UNKNOWN'),
'START_TIME' => $migration['start_time'] ?? __('UNKNOWN'),
'END_TIME' => $migration['end_time'] ?? __('UNKNOWN'),
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
if (!empty($status['pending_migrations'])) {
foreach ($status['pending_migrations'] as $i => $migration) {
$template->assign_block_vars('pending_migrations', [
'VERSION' => $migration['version'],
'NAME' => $migration['name'],
'FILENAME' => $migration['filename'],
'ROW_CLASS' => ($i % 2) ? 'row1' : 'row2'
]);
}
}
// Output template using standard admin pattern
print_page('admin_migrations.tpl', 'admin');

View file

@ -39,7 +39,7 @@ if (!$result = DB()->sql_query($sql)) {
} }
} }
$s_mess = $lang['SITEMAP_CREATED'] . ': <b>' . bb_date($new['sitemap_time'], $bb_cfg['post_date_format']) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>'; $s_mess = $lang['SITEMAP_CREATED'] . ': <b>' . bb_date($new['sitemap_time'], config()->get('post_date_format')) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>';
$message = is_file(SITEMAP_DIR . '/sitemap.xml') ? $s_mess : $lang['SITEMAP_NOT_CREATED']; $message = is_file(SITEMAP_DIR . '/sitemap.xml') ? $s_mess : $lang['SITEMAP_NOT_CREATED'];
$template->assign_vars([ $template->assign_vars([

View file

@ -26,7 +26,7 @@ if ($mode == 'delete' && isset($_POST['cancel'])) {
$mode = ''; $mode = '';
} }
$pathToSmilesDir = BB_ROOT . $bb_cfg['smilies_path']; $pathToSmilesDir = BB_ROOT . config()->get('smilies_path');
$delimeter = '=+:'; $delimeter = '=+:';
$s_hidden_fields = ''; $s_hidden_fields = '';
$smiley_paks = $smiley_images = []; $smiley_paks = $smiley_images = [];

View file

@ -17,15 +17,15 @@ require INC_DIR . '/bbcode.php';
$preview = isset($_POST['preview']); $preview = isset($_POST['preview']);
if (isset($_POST['post']) && ($bb_cfg['terms'] !== $_POST['message'])) { if (isset($_POST['post']) && (config()->get('terms') !== $_POST['message'])) {
bb_update_config(['terms' => $_POST['message']]); bb_update_config(['terms' => $_POST['message']]);
bb_die($lang['TERMS_UPDATED_SUCCESSFULLY'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_TERMS_CONFIG'], '<a href="admin_terms.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>')); bb_die($lang['TERMS_UPDATED_SUCCESSFULLY'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_TERMS_CONFIG'], '<a href="admin_terms.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} }
$template->assign_vars([ $template->assign_vars([
'S_ACTION' => 'admin_terms.php', 'S_ACTION' => 'admin_terms.php',
'EXT_LINK_NW' => $bb_cfg['ext_link_new_win'], 'EXT_LINK_NW' => config()->get('ext_link_new_win'),
'MESSAGE' => $preview ? $_POST['message'] : $bb_cfg['terms'], 'MESSAGE' => $preview ? $_POST['message'] : config()->get('terms'),
'PREVIEW_HTML' => $preview ? bbcode2html($_POST['message']) : '', 'PREVIEW_HTML' => $preview ? bbcode2html($_POST['message']) : '',
]); ]);

View file

@ -841,10 +841,10 @@ if (!isset($_REQUEST['dosearch'])) {
if ($page == 1) { if ($page == 1) {
$offset = 0; $offset = 0;
} else { } else {
$offset = (($page - 1) * $bb_cfg['topics_per_page']); $offset = (($page - 1) * config()->get('topics_per_page'));
} }
$limit = "LIMIT $offset, " . $bb_cfg['topics_per_page']; $limit = "LIMIT $offset, " . config()->get('topics_per_page');
$select_sql .= " $limit"; $select_sql .= " $limit";
@ -859,7 +859,7 @@ if (!isset($_REQUEST['dosearch'])) {
bb_die($lang['SEARCH_NO_RESULTS']); bb_die($lang['SEARCH_NO_RESULTS']);
} }
} }
$num_pages = ceil($total_pages['total'] / $bb_cfg['topics_per_page']); $num_pages = ceil($total_pages['total'] / config()->get('topics_per_page'));
$pagination = ''; $pagination = '';

View file

@ -14,8 +14,8 @@ if (!empty($setmodules)) {
require __DIR__ . '/pagestart.php'; require __DIR__ . '/pagestart.php';
if (!$bb_cfg['use_word_censor']) { if (!config()->get('use_word_censor')) {
bb_die('Word censor disabled <br /><br /> ($bb_cfg[\'use_word_censor\'] in config.php)'); bb_die('Word censor disabled <br /><br /> (use_word_censor in config.php)');
} }
$mode = request_var('mode', ''); $mode = request_var('mode', '');
@ -81,6 +81,7 @@ if ($mode != '') {
} }
$datastore->update('censor'); $datastore->update('censor');
censor()->reload(); // Reload the singleton instance with updated words
$message .= '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'); $message .= '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>');
bb_die($message); bb_die($message);
@ -95,6 +96,7 @@ if ($mode != '') {
} }
$datastore->update('censor'); $datastore->update('censor');
censor()->reload(); // Reload the singleton instance with updated words
bb_die($lang['WORD_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>')); bb_die($lang['WORD_REMOVED'] . '<br /><br />' . sprintf($lang['CLICK_RETURN_WORDADMIN'], '<a href="admin_words.php">', '</a>') . '<br /><br />' . sprintf($lang['CLICK_RETURN_ADMIN_INDEX'], '<a href="index.php?pane=right">', '</a>'));
} else { } else {

View file

@ -78,7 +78,7 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
} elseif (isset($_GET['pane']) && $_GET['pane'] == 'right') { } elseif (isset($_GET['pane']) && $_GET['pane'] == 'right') {
$template->assign_vars([ $template->assign_vars([
'TPL_ADMIN_MAIN' => true, 'TPL_ADMIN_MAIN' => true,
'ADMIN_LOCK' => (bool)$bb_cfg['board_disable'], 'ADMIN_LOCK' => (bool)config()->get('board_disable'),
'ADMIN_LOCK_CRON' => is_file(BB_DISABLED), 'ADMIN_LOCK_CRON' => is_file(BB_DISABLED),
]); ]);
@ -90,7 +90,7 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
'NEW_VERSION_SIZE' => $update_data['latest_version_size'], 'NEW_VERSION_SIZE' => $update_data['latest_version_size'],
'NEW_VERSION_DL_LINK' => $update_data['latest_version_dl_link'], 'NEW_VERSION_DL_LINK' => $update_data['latest_version_dl_link'],
'NEW_VERSION_LINK' => $update_data['latest_version_link'], 'NEW_VERSION_LINK' => $update_data['latest_version_link'],
'NEW_VERSION_MD5' => $update_data['latest_version_checksum'] 'NEW_VERSION_HASH' => $update_data['latest_version_checksum']
]); ]);
} }
@ -98,8 +98,8 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$total_posts = $stats['postcount']; $total_posts = $stats['postcount'];
$total_topics = $stats['topiccount']; $total_topics = $stats['topiccount'];
$total_users = $stats['usercount']; $total_users = $stats['usercount'];
$start_date = bb_date($bb_cfg['board_startdate']); $start_date = bb_date(config()->get('board_startdate'));
$boarddays = (TIMENOW - $bb_cfg['board_startdate']) / 86400; $boarddays = (TIMENOW - config()->get('board_startdate')) / 86400;
$posts_per_day = sprintf('%.2f', $total_posts / $boarddays); $posts_per_day = sprintf('%.2f', $total_posts / $boarddays);
$topics_per_day = sprintf('%.2f', $total_topics / $boarddays); $topics_per_day = sprintf('%.2f', $total_topics / $boarddays);
@ -107,10 +107,10 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
$avatar_dir_size = 0; $avatar_dir_size = 0;
if ($avatar_dir = opendir($bb_cfg['avatars']['upload_path'])) { if ($avatar_dir = opendir(config()->get('avatars.upload_path'))) {
while ($file = readdir($avatar_dir)) { while ($file = readdir($avatar_dir)) {
if ($file != '.' && $file != '..') { if ($file != '.' && $file != '..') {
$avatar_dir_size += @filesize($bb_cfg['avatars']['upload_path'] . $file); $avatar_dir_size += @filesize(config()->get('avatars.upload_path') . $file);
} }
} }
closedir($avatar_dir); closedir($avatar_dir);
@ -187,7 +187,7 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
'STARTED' => bb_date($onlinerow_reg[$i]['session_start'], 'd-M-Y H:i', false), 'STARTED' => bb_date($onlinerow_reg[$i]['session_start'], 'd-M-Y H:i', false),
'LASTUPDATE' => bb_date($onlinerow_reg[$i]['user_session_time'], 'd-M-Y H:i', false), 'LASTUPDATE' => bb_date($onlinerow_reg[$i]['user_session_time'], 'd-M-Y H:i', false),
'IP_ADDRESS' => $reg_ip, 'IP_ADDRESS' => $reg_ip,
'U_WHOIS_IP' => $bb_cfg['whois_info'] . $reg_ip, 'U_WHOIS_IP' => config()->get('whois_info') . $reg_ip,
]); ]);
} }
} }
@ -206,7 +206,7 @@ if (isset($_GET['pane']) && $_GET['pane'] == 'left') {
'STARTED' => bb_date($onlinerow_guest[$i]['session_start'], 'd-M-Y H:i', false), 'STARTED' => bb_date($onlinerow_guest[$i]['session_start'], 'd-M-Y H:i', false),
'LASTUPDATE' => bb_date($onlinerow_guest[$i]['session_time'], 'd-M-Y H:i', false), 'LASTUPDATE' => bb_date($onlinerow_guest[$i]['session_time'], 'd-M-Y H:i', false),
'IP_ADDRESS' => $guest_ip, 'IP_ADDRESS' => $guest_ip,
'U_WHOIS_IP' => $bb_cfg['whois_info'] . $guest_ip, 'U_WHOIS_IP' => config()->get('whois_info') . $guest_ip,
]); ]);
} }
} }

View file

@ -31,7 +31,8 @@ echo '<html><body><head></head>';
echo '<br /><br /><table border="1" cellspacing="0" cellpadding="6" align="center">'; echo '<br /><br /><table border="1" cellspacing="0" cellpadding="6" align="center">';
foreach ($sql as $i => $query) { foreach ($sql as $i => $query) {
$row = mysqli_fetch_row(DB()->query($query))[0]; $result = DB()->fetch_row($query);
$row = array_values($result)[0]; // Get first column value
$row = ($i == 2) ? humn_size($row) : $row; $row = ($i == 2) ? humn_size($row) : $row;
echo "<tr><td>{$lang['TR_STATS'][$i]}</td><td><b>$row</b></td>"; echo "<tr><td>{$lang['TR_STATS'][$i]}</td><td><b>$row</b></td>";
} }

View file

@ -21,7 +21,7 @@ if (!IS_ADMIN) {
$peers_in_last_minutes = [30, 15, 5, 1]; $peers_in_last_minutes = [30, 15, 5, 1];
$peers_in_last_sec_limit = 300; $peers_in_last_sec_limit = 300;
$announce_interval = (int)$bb_cfg['announce_interval']; $announce_interval = (int)config()->get('announce_interval');
$stat = []; $stat = [];
define('TMP_TRACKER_TABLE', 'tmp_tracker'); define('TMP_TRACKER_TABLE', 'tmp_tracker');

View file

@ -11,8 +11,6 @@ define('IN_TRACKER', true);
define('BB_ROOT', './../'); define('BB_ROOT', './../');
require dirname(__DIR__) . '/common.php'; require dirname(__DIR__) . '/common.php';
global $bb_cfg;
// Check User-Agent for existence // Check User-Agent for existence
$userAgent = (string)$_SERVER['HTTP_USER_AGENT']; $userAgent = (string)$_SERVER['HTTP_USER_AGENT'];
if (empty($userAgent)) { if (empty($userAgent)) {
@ -20,8 +18,8 @@ if (empty($userAgent)) {
die; die;
} }
$announce_interval = $bb_cfg['announce_interval']; $announce_interval = config()->get('announce_interval');
$passkey_key = $bb_cfg['passkey_key']; $passkey_key = config()->get('passkey_key');
// Recover info_hash // Recover info_hash
if (isset($_GET['?info_hash']) && !isset($_GET['info_hash'])) { if (isset($_GET['?info_hash']) && !isset($_GET['info_hash'])) {
@ -67,10 +65,10 @@ if (strlen($peer_id) !== 20) {
} }
// Check for client ban // Check for client ban
if ($bb_cfg['client_ban']['enabled']) { if (config()->get('client_ban.enabled')) {
$targetClient = []; $targetClient = [];
foreach ($bb_cfg['client_ban']['clients'] as $clientId => $banReason) { foreach (config()->get('client_ban.clients') as $clientId => $banReason) {
if (str_starts_with($peer_id, $clientId)) { if (str_starts_with($peer_id, $clientId)) {
$targetClient = [ $targetClient = [
'peer_id' => $clientId, 'peer_id' => $clientId,
@ -80,7 +78,7 @@ if ($bb_cfg['client_ban']['enabled']) {
} }
} }
if ($bb_cfg['client_ban']['only_allow_mode']) { if (config()->get('client_ban.only_allow_mode')) {
if (empty($targetClient['peer_id'])) { if (empty($targetClient['peer_id'])) {
msg_die('Your BitTorrent client has been banned!'); msg_die('Your BitTorrent client has been banned!');
} }
@ -117,7 +115,7 @@ $stopped = ($event === 'stopped');
// Check info_hash length // Check info_hash length
if (strlen($info_hash) !== 20) { if (strlen($info_hash) !== 20) {
msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, 'UTF8') ? $info_hash : $info_hash_hex)); msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
} }
/** /**
@ -131,7 +129,7 @@ if (
|| !is_numeric($port) || !is_numeric($port)
|| ($port < 1024 && !$stopped) || ($port < 1024 && !$stopped)
|| $port > 0xFFFF || $port > 0xFFFF
|| (!empty($bb_cfg['disallowed_ports']) && in_array($port, $bb_cfg['disallowed_ports'])) || (!empty(config()->get('disallowed_ports')) && in_array($port, config()->get('disallowed_ports')))
) { ) {
msg_die('Invalid port: ' . $port); msg_die('Invalid port: ' . $port);
} }
@ -170,13 +168,13 @@ if (preg_match('/(Mozilla|Browser|Chrome|Safari|AppleWebKit|Opera|Links|Lynx|Bot
$ip = $_SERVER['REMOTE_ADDR']; $ip = $_SERVER['REMOTE_ADDR'];
// 'ip' query handling // 'ip' query handling
if (!$bb_cfg['ignore_reported_ip'] && isset($_GET['ip']) && $ip !== $_GET['ip']) { if (!config()->get('ignore_reported_ip') && isset($_GET['ip']) && $ip !== $_GET['ip']) {
if (!$bb_cfg['verify_reported_ip'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { if (!config()->get('verify_reported_ip') && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$x_ip = $_SERVER['HTTP_X_FORWARDED_FOR']; $x_ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
if ($x_ip === $_GET['ip']) { if ($x_ip === $_GET['ip']) {
$filteredIp = filter_var($x_ip, FILTER_VALIDATE_IP); $filteredIp = filter_var($x_ip, FILTER_VALIDATE_IP);
if ($filteredIp !== false && ($bb_cfg['allow_internal_ip'] || !filter_var($filteredIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) { if ($filteredIp !== false && (config()->get('allow_internal_ip') || !filter_var($filteredIp, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE))) {
$ip = $filteredIp; $ip = $filteredIp;
} }
} }
@ -259,7 +257,7 @@ if ($lp_info) {
// Verify if torrent registered on tracker and user authorized // Verify if torrent registered on tracker and user authorized
if (empty($row['topic_id'])) { if (empty($row['topic_id'])) {
msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, 'UTF8') ? $info_hash : $info_hash_hex)); msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
} }
if (empty($row['user_id'])) { if (empty($row['user_id'])) {
msg_die('Please LOG IN and RE-DOWNLOAD this torrent (user not found)'); msg_die('Please LOG IN and RE-DOWNLOAD this torrent (user not found)');
@ -272,7 +270,7 @@ if ($lp_info) {
define('IS_MOD', !IS_GUEST && (int)$row['user_level'] === MOD); define('IS_MOD', !IS_GUEST && (int)$row['user_level'] === MOD);
define('IS_GROUP_MEMBER', !IS_GUEST && (int)$row['user_level'] === GROUP_MEMBER); define('IS_GROUP_MEMBER', !IS_GUEST && (int)$row['user_level'] === GROUP_MEMBER);
define('IS_USER', !IS_GUEST && (int)$row['user_level'] === USER); define('IS_USER', !IS_GUEST && (int)$row['user_level'] === USER);
define('IS_SUPER_ADMIN', IS_ADMIN && isset($bb_cfg['super_admins'][$user_id])); define('IS_SUPER_ADMIN', IS_ADMIN && isset(config()->get('super_admins')[$user_id]));
define('IS_AM', IS_ADMIN || IS_MOD); define('IS_AM', IS_ADMIN || IS_MOD);
$topic_id = $row['topic_id']; $topic_id = $row['topic_id'];
$releaser = (int)($user_id == $row['poster_id']); $releaser = (int)($user_id == $row['poster_id']);
@ -280,13 +278,13 @@ if ($lp_info) {
$tor_status = $row['tor_status']; $tor_status = $row['tor_status'];
// Check tor status // Check tor status
if (!IS_AM && isset($bb_cfg['tor_frozen'][$tor_status]) && !(isset($bb_cfg['tor_frozen_author_download'][$tor_status]) && $releaser)) { if (!IS_AM && isset(config()->get('tor_frozen')[$tor_status]) && !(isset(config()->get('tor_frozen_author_download')[$tor_status]) && $releaser)) {
msg_die('Torrent frozen and cannot be downloaded'); msg_die('Torrent frozen and cannot be downloaded');
} }
// Check hybrid status // Check hybrid status
if (!empty($row['info_hash']) && !empty($row['info_hash_v2'])) { if (!empty($row['info_hash']) && !empty($row['info_hash_v2'])) {
$stat_protocol = match ((int)$bb_cfg['tracker']['hybrid_stat_protocol']) { $stat_protocol = match ((int)config()->get('tracker.hybrid_stat_protocol')) {
2 => substr($row['info_hash_v2'], 0, 20), 2 => substr($row['info_hash_v2'], 0, 20),
default => $row['info_hash'] // 1 default => $row['info_hash'] // 1
}; };
@ -296,7 +294,7 @@ if ($lp_info) {
} }
// Ratio limits // Ratio limits
if ((RATIO_ENABLED || $bb_cfg['tracker']['limit_concurrent_ips']) && !$stopped) { if ((RATIO_ENABLED || config()->get('tracker.limit_concurrent_ips')) && !$stopped) {
$user_ratio = get_bt_ratio($row); $user_ratio = get_bt_ratio($row);
if ($user_ratio === null) { if ($user_ratio === null) {
$user_ratio = 1; $user_ratio = 1;
@ -304,10 +302,10 @@ if ($lp_info) {
$rating_msg = ''; $rating_msg = '';
if (!$seeder) { if (!$seeder) {
foreach ($bb_cfg['rating'] as $ratio => $limit) { foreach (config()->get('rating') as $ratio => $limit) {
if ($user_ratio < $ratio) { if ($user_ratio < $ratio) {
$bb_cfg['tracker']['limit_active_tor'] = 1; config()->set('tracker.limit_active_tor', 1);
$bb_cfg['tracker']['limit_leech_count'] = $limit; config()->set('tracker.limit_leech_count', $limit);
$rating_msg = " (ratio < $ratio)"; $rating_msg = " (ratio < $ratio)";
break; break;
} }
@ -315,29 +313,29 @@ if ($lp_info) {
} }
// Limit active torrents // Limit active torrents
if (!isset($bb_cfg['unlimited_users'][$user_id]) && $bb_cfg['tracker']['limit_active_tor'] && (($bb_cfg['tracker']['limit_seed_count'] && $seeder) || ($bb_cfg['tracker']['limit_leech_count'] && !$seeder))) { if (!isset(config()->get('unlimited_users')[$user_id]) && config()->get('tracker.limit_active_tor') && ((config()->get('tracker.limit_seed_count') && $seeder) || (config()->get('tracker.limit_leech_count') && !$seeder))) {
$sql = "SELECT COUNT(DISTINCT topic_id) AS active_torrents $sql = "SELECT COUNT(DISTINCT topic_id) AS active_torrents
FROM " . BB_BT_TRACKER . " FROM " . BB_BT_TRACKER . "
WHERE user_id = $user_id WHERE user_id = $user_id
AND seeder = $seeder AND seeder = $seeder
AND topic_id != $topic_id"; AND topic_id != $topic_id";
if (!$seeder && $bb_cfg['tracker']['leech_expire_factor'] && $user_ratio < 0.5) { if (!$seeder && config()->get('tracker.leech_expire_factor') && $user_ratio < 0.5) {
$sql .= " AND update_time > " . (TIMENOW - 60 * $bb_cfg['tracker']['leech_expire_factor']); $sql .= " AND update_time > " . (TIMENOW - 60 * config()->get('tracker.leech_expire_factor'));
} }
$sql .= " GROUP BY user_id"; $sql .= " GROUP BY user_id";
if ($row = DB()->fetch_row($sql)) { if ($row = DB()->fetch_row($sql)) {
if ($seeder && $bb_cfg['tracker']['limit_seed_count'] && $row['active_torrents'] >= $bb_cfg['tracker']['limit_seed_count']) { if ($seeder && config()->get('tracker.limit_seed_count') && $row['active_torrents'] >= config()->get('tracker.limit_seed_count')) {
msg_die('Only ' . $bb_cfg['tracker']['limit_seed_count'] . ' torrent(s) allowed for seeding'); msg_die('Only ' . config()->get('tracker.limit_seed_count') . ' torrent(s) allowed for seeding');
} elseif (!$seeder && $bb_cfg['tracker']['limit_leech_count'] && $row['active_torrents'] >= $bb_cfg['tracker']['limit_leech_count']) { } elseif (!$seeder && config()->get('tracker.limit_leech_count') && $row['active_torrents'] >= config()->get('tracker.limit_leech_count')) {
msg_die('Only ' . $bb_cfg['tracker']['limit_leech_count'] . ' torrent(s) allowed for leeching' . $rating_msg); msg_die('Only ' . config()->get('tracker.limit_leech_count') . ' torrent(s) allowed for leeching' . $rating_msg);
} }
} }
} }
// Limit concurrent IPs // Limit concurrent IPs
if ($bb_cfg['tracker']['limit_concurrent_ips'] && (($bb_cfg['tracker']['limit_seed_ips'] && $seeder) || ($bb_cfg['tracker']['limit_leech_ips'] && !$seeder))) { if (config()->get('tracker.limit_concurrent_ips') && ((config()->get('tracker.limit_seed_ips') && $seeder) || (config()->get('tracker.limit_leech_ips') && !$seeder))) {
$sql = "SELECT COUNT(DISTINCT ip) AS ips $sql = "SELECT COUNT(DISTINCT ip) AS ips
FROM " . BB_BT_TRACKER . " FROM " . BB_BT_TRACKER . "
WHERE topic_id = $topic_id WHERE topic_id = $topic_id
@ -345,16 +343,16 @@ if ($lp_info) {
AND seeder = $seeder AND seeder = $seeder
AND $ip_version != '$ip_sql'"; AND $ip_version != '$ip_sql'";
if (!$seeder && $bb_cfg['tracker']['leech_expire_factor']) { if (!$seeder && config()->get('tracker.leech_expire_factor')) {
$sql .= " AND update_time > " . (TIMENOW - 60 * $bb_cfg['tracker']['leech_expire_factor']); $sql .= " AND update_time > " . (TIMENOW - 60 * config()->get('tracker.leech_expire_factor'));
} }
$sql .= " GROUP BY topic_id"; $sql .= " GROUP BY topic_id";
if ($row = DB()->fetch_row($sql)) { if ($row = DB()->fetch_row($sql)) {
if ($seeder && $bb_cfg['tracker']['limit_seed_ips'] && $row['ips'] >= $bb_cfg['tracker']['limit_seed_ips']) { if ($seeder && config()->get('tracker.limit_seed_ips') && $row['ips'] >= config()->get('tracker.limit_seed_ips')) {
msg_die('You can seed only from ' . $bb_cfg['tracker']['limit_seed_ips'] . " IP's"); msg_die('You can seed only from ' . config()->get('tracker.limit_seed_ips') . " IP's");
} elseif (!$seeder && $bb_cfg['tracker']['limit_leech_ips'] && $row['ips'] >= $bb_cfg['tracker']['limit_leech_ips']) { } elseif (!$seeder && config()->get('tracker.limit_leech_ips') && $row['ips'] >= config()->get('tracker.limit_leech_ips')) {
msg_die('You can leech only from ' . $bb_cfg['tracker']['limit_leech_ips'] . " IP's"); msg_die('You can leech only from ' . config()->get('tracker.limit_leech_ips') . " IP's");
} }
} }
} }
@ -378,7 +376,7 @@ $up_add = ($lp_info && $uploaded > $lp_info['uploaded']) ? $uploaded - $lp_info[
$down_add = ($lp_info && $downloaded > $lp_info['downloaded']) ? $downloaded - $lp_info['downloaded'] : 0; $down_add = ($lp_info && $downloaded > $lp_info['downloaded']) ? $downloaded - $lp_info['downloaded'] : 0;
// Gold/Silver releases // Gold/Silver releases
if ($bb_cfg['tracker']['gold_silver_enabled'] && $down_add) { if (config()->get('tracker.gold_silver_enabled') && $down_add) {
if ($tor_type == TOR_TYPE_GOLD) { if ($tor_type == TOR_TYPE_GOLD) {
$down_add = 0; $down_add = 0;
} // Silver releases } // Silver releases
@ -388,7 +386,7 @@ if ($bb_cfg['tracker']['gold_silver_enabled'] && $down_add) {
} }
// Freeleech // Freeleech
if ($bb_cfg['tracker']['freeleech'] && $down_add) { if (config()->get('tracker.freeleech') && $down_add) {
$down_add = 0; $down_add = 0;
} }
@ -466,8 +464,8 @@ $output = CACHE('tr_cache')->get(PEERS_LIST_PREFIX . $topic_id);
if (!$output) { if (!$output) {
// Retrieve peers // Retrieve peers
$numwant = (int)$bb_cfg['tracker']['numwant']; $numwant = (int)config()->get('tracker.numwant');
$compact_mode = ($bb_cfg['tracker']['compact_mode'] || !empty($compact)); $compact_mode = (config()->get('tracker.compact_mode') || !empty($compact));
$rowset = DB()->fetch_rowset(" $rowset = DB()->fetch_rowset("
SELECT ip, ipv6, port SELECT ip, ipv6, port
@ -512,7 +510,7 @@ if (!$output) {
$seeders = $leechers = $client_completed = 0; $seeders = $leechers = $client_completed = 0;
if ($bb_cfg['tracker']['scrape']) { if (config()->get('tracker.scrape')) {
$row = DB()->fetch_row(" $row = DB()->fetch_row("
SELECT seeders, leechers, completed SELECT seeders, leechers, completed
FROM " . BB_BT_TRACKER_SNAP . " FROM " . BB_BT_TRACKER_SNAP . "

View file

@ -11,11 +11,9 @@ if (!defined('IN_TRACKER')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg;
// Exit if tracker is disabled // Exit if tracker is disabled
if ($bb_cfg['tracker']['bt_off']) { if (config()->get('tracker.bt_off')) {
msg_die($bb_cfg['tracker']['bt_off_reason']); msg_die(config()->get('tracker.bt_off_reason'));
} }
// //

View file

@ -11,9 +11,7 @@ define('IN_TRACKER', true);
define('BB_ROOT', './../'); define('BB_ROOT', './../');
require dirname(__DIR__) . '/common.php'; require dirname(__DIR__) . '/common.php';
global $bb_cfg; if (!config()->get('tracker.scrape')) {
if (!$bb_cfg['tracker']['scrape']) {
msg_die('Please disable SCRAPE!'); msg_die('Please disable SCRAPE!');
} }
@ -34,7 +32,7 @@ $info_hash_hex = bin2hex($info_hash);
// Check info_hash length // Check info_hash length
if (strlen($info_hash) !== 20) { if (strlen($info_hash) !== 20) {
msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, 'UTF8') ? $info_hash : $info_hash_hex)); msg_die('Invalid info_hash: ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
} }
// Handle multiple hashes // Handle multiple hashes
@ -60,8 +58,8 @@ foreach ($info_hash_array[1] as $hash) {
$info_hash_count = count($info_hashes); $info_hash_count = count($info_hashes);
if (!empty($info_hash_count)) { if (!empty($info_hash_count)) {
if ($info_hash_count > $bb_cfg['max_scrapes']) { if ($info_hash_count > config()->get('max_scrapes')) {
$info_hashes = array_slice($info_hashes, 0, $bb_cfg['max_scrapes']); $info_hashes = array_slice($info_hashes, 0, config()->get('max_scrapes'));
} }
$info_hashes_sql = implode('\', \'', $info_hashes); $info_hashes_sql = implode('\', \'', $info_hashes);
@ -99,7 +97,7 @@ if (!empty($info_hash_count)) {
// Verify if torrent registered on tracker // Verify if torrent registered on tracker
if (empty($torrents)) { if (empty($torrents)) {
msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, 'UTF8') ? $info_hash : $info_hash_hex)); msg_die('Torrent not registered, info_hash = ' . (mb_check_encoding($info_hash, DEFAULT_CHARSET) ? $info_hash : $info_hash_hex));
} }
die(\Arokettu\Bencode\Bencode::encode($torrents)); die(\Arokettu\Bencode\Bencode::encode($torrents));

View file

@ -23,7 +23,7 @@ body = """
{%- endmacro -%} {%- endmacro -%}
{%- macro nightly_url() -%} {%- macro nightly_url() -%}
https://nightly.link/{{ remote.github.owner }}/{{ remote.github.repo }}/workflows/ci/v2.4/TorrentPier-v2.4 https://nightly.link/{{ remote.github.owner }}/{{ remote.github.repo }}/workflows/ci/master/TorrentPier-master
{%- endmacro -%} {%- endmacro -%}
{% macro print_commit(commit) -%} {% macro print_commit(commit) -%}

View file

@ -86,69 +86,138 @@ if (is_file(BB_PATH . '/library/config.local.php')) {
require_once BB_PATH . '/library/config.local.php'; require_once BB_PATH . '/library/config.local.php';
} }
/** @noinspection PhpUndefinedVariableInspection */
// Initialize Config singleton, bb_cfg from global file config
$config = \TorrentPier\Config::init($bb_cfg);
/**
* Get the Config instance
*
* @return \TorrentPier\Config
*/
function config(): \TorrentPier\Config
{
return \TorrentPier\Config::getInstance();
}
/**
* Get the Censor instance
*
* @return \TorrentPier\Censor
*/
function censor(): \TorrentPier\Censor
{
return \TorrentPier\Censor::getInstance();
}
/**
* Get the Dev instance
*
* @return \TorrentPier\Dev
*/
function dev(): \TorrentPier\Dev
{
return \TorrentPier\Dev::getInstance();
}
/**
* Get the Language instance
*
* @return \TorrentPier\Language
*/
function lang(): \TorrentPier\Language
{
return \TorrentPier\Language::getInstance();
}
/**
* Get a language string (shorthand for lang()->get())
*
* @param string $key Language key, supports dot notation (e.g., 'DATETIME.TODAY')
* @param mixed $default Default value if key doesn't exist
* @return mixed Language string or default value
*/
function __(string $key, mixed $default = null): mixed
{
return \TorrentPier\Language::getInstance()->get($key, $default);
}
/**
* Echo a language string (shorthand for echo __())
*
* @param string $key Language key, supports dot notation
* @param mixed $default Default value if key doesn't exist
* @return void
*/
function _e(string $key, mixed $default = null): void
{
echo \TorrentPier\Language::getInstance()->get($key, $default);
}
/** /**
* Initialize debug * Initialize debug
*/ */
define('APP_ENV', env('APP_ENV', 'production')); define('APP_ENV', env('APP_ENV', 'production'));
if (APP_ENV === 'local') { if (APP_ENV === 'development') {
define('DBG_USER', true); // forced debug define('DBG_USER', true); // forced debug
} else { } else {
define('DBG_USER', isset($_COOKIE[COOKIE_DBG])); define('DBG_USER', isset($_COOKIE[COOKIE_DBG]));
} }
(new \TorrentPier\Dev()); (\TorrentPier\Dev::init());
/** /**
* Server variables initialize * Server variables initialize
*/ */
$server_protocol = $bb_cfg['cookie_secure'] ? 'https://' : 'http://'; $server_protocol = config()->get('cookie_secure') ? 'https://' : 'http://';
$server_port = in_array((int)$bb_cfg['server_port'], [80, 443], true) ? '' : ':' . $bb_cfg['server_port']; $server_port = in_array((int)config()->get('server_port'), [80, 443], true) ? '' : ':' . config()->get('server_port');
define('FORUM_PATH', $bb_cfg['script_path']); define('FORUM_PATH', config()->get('script_path'));
define('FULL_URL', $server_protocol . $bb_cfg['server_name'] . $server_port . $bb_cfg['script_path']); define('FULL_URL', $server_protocol . config()->get('server_name') . $server_port . config()->get('script_path'));
unset($server_protocol, $server_port); unset($server_protocol, $server_port);
/** // Initialize the new DB factory with database configuration
* Database TorrentPier\Database\DatabaseFactory::init(config()->get('db'), config()->get('db_alias', []));
*/
$DBS = new TorrentPier\Legacy\Dbs($bb_cfg);
function DB(string $db_alias = 'db') /**
* Get the Database instance
*
* @param string $db_alias
* @return \TorrentPier\Database\Database
*/
function DB(string $db_alias = 'db'): \TorrentPier\Database\Database
{ {
global $DBS; return TorrentPier\Database\DatabaseFactory::getInstance($db_alias);
return $DBS->get_db_obj($db_alias);
} }
/** // Initialize Unified Cache System
* Cache TorrentPier\Cache\UnifiedCacheSystem::getInstance(config()->all());
*/
$CACHES = new TorrentPier\Legacy\Caches($bb_cfg);
function CACHE(string $cache_name) /**
* Get cache manager instance (replaces legacy cache system)
*
* @param string $cache_name
* @return \TorrentPier\Cache\CacheManager
*/
function CACHE(string $cache_name): \TorrentPier\Cache\CacheManager
{ {
global $CACHES; return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->get_cache_obj($cache_name);
return $CACHES->get_cache_obj($cache_name);
} }
/** /**
* Datastore * Get datastore manager instance (replaces legacy datastore system)
*
* @return \TorrentPier\Cache\DatastoreManager
*/ */
switch ($bb_cfg['datastore_type']) { function datastore(): \TorrentPier\Cache\DatastoreManager
case 'apcu': {
$datastore = new TorrentPier\Legacy\Datastore\APCu($bb_cfg['cache']['prefix']); return TorrentPier\Cache\UnifiedCacheSystem::getInstance()->getDatastore(config()->get('datastore_type', 'file'));
break;
case 'memcached':
$datastore = new TorrentPier\Legacy\Datastore\Memcached($bb_cfg['cache']['memcached'], $bb_cfg['cache']['prefix']);
break;
case 'sqlite':
$datastore = new TorrentPier\Legacy\Datastore\Sqlite($bb_cfg['cache']['db_dir'] . 'datastore', $bb_cfg['cache']['prefix']);
break;
case 'redis':
$datastore = new TorrentPier\Legacy\Datastore\Redis($bb_cfg['cache']['redis'], $bb_cfg['cache']['prefix']);
break;
case 'filecache':
default:
$datastore = new TorrentPier\Legacy\Datastore\File($bb_cfg['cache']['db_dir'] . 'datastore/', $bb_cfg['cache']['prefix']);
} }
/**
* Backward compatibility: Global datastore variable
* This allows existing code to continue using global $datastore
*/
$datastore = datastore();
// Functions // Functions
function utime() function utime()
{ {
@ -364,9 +433,9 @@ if (!defined('IN_TRACKER')) {
} else { } else {
define('DUMMY_PEER', pack('Nn', \TorrentPier\Helpers\IPHelper::ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? (int)$_GET['port'] : random_int(1000, 65000))); define('DUMMY_PEER', pack('Nn', \TorrentPier\Helpers\IPHelper::ip2long($_SERVER['REMOTE_ADDR']), !empty($_GET['port']) ? (int)$_GET['port'] : random_int(1000, 65000)));
define('PEER_HASH_EXPIRE', round($bb_cfg['announce_interval'] * (0.85 * $bb_cfg['tracker']['expire_factor']))); define('PEER_HASH_EXPIRE', round(config()->get('announce_interval') * (0.85 * config()->get('tracker.expire_factor'))));
define('PEERS_LIST_EXPIRE', round($bb_cfg['announce_interval'] * 0.7)); define('PEERS_LIST_EXPIRE', round(config()->get('announce_interval') * 0.7));
define('SCRAPE_LIST_EXPIRE', round($bb_cfg['scrape_interval'] * 0.7)); define('SCRAPE_LIST_EXPIRE', round(config()->get('scrape_interval') * 0.7));
define('PEER_HASH_PREFIX', 'peer_'); define('PEER_HASH_PREFIX', 'peer_');
define('PEERS_LIST_PREFIX', 'peers_list_'); define('PEERS_LIST_PREFIX', 'peers_list_');

View file

@ -46,51 +46,57 @@
"forum": "https://torrentpier.com" "forum": "https://torrentpier.com"
}, },
"require": { "require": {
"php": ">=8.1", "php": ">=8.2",
"arokettu/random-polyfill": "1.0.2",
"arokettu/bencode": "^4.1.0", "arokettu/bencode": "^4.1.0",
"arokettu/monsterid": "dev-master", "arokettu/monsterid": "^4.1.0",
"arokettu/random-polyfill": "1.0.2",
"arokettu/torrent-file": "^5.2.1", "arokettu/torrent-file": "^5.2.1",
"belomaxorka/captcha": "1.*",
"bugsnag/bugsnag": "^v3.29.1", "bugsnag/bugsnag": "^v3.29.1",
"claviska/simpleimage": "^4.0", "claviska/simpleimage": "^4.0",
"belomaxorka/captcha": "1.*",
"egulias/email-validator": "^4.0.1", "egulias/email-validator": "^4.0.1",
"filp/whoops": "^2.15", "filp/whoops": "^2.15",
"z4kn4fein/php-semver": "^v3.0.0", "gemorroj/m3u-parser": "^6.0.1",
"gigablah/sphinxphp": "2.0.8", "gigablah/sphinxphp": "2.0.8",
"google/recaptcha": "^1.3", "google/recaptcha": "^1.3",
"jacklul/monolog-telegram": "^3.1", "jacklul/monolog-telegram": "^3.1",
"josantonius/cookie": "^2.0", "josantonius/cookie": "^2.0",
"gemorroj/m3u-parser": "dev-master",
"php-curl-class/php-curl-class": "^12.0.0",
"league/flysystem": "^3.28", "league/flysystem": "^3.28",
"longman/ip-tools": "1.2.1", "longman/ip-tools": "1.2.1",
"matthiasmullie/scrapbook": "^1.5.4",
"monolog/monolog": "^3.4", "monolog/monolog": "^3.4",
"nette/caching": "^3.3",
"nette/database": "^3.2",
"php-curl-class/php-curl-class": "^12.0.0",
"robmorgan/phinx": "^0.16.9",
"samdark/sitemap": "2.4.1", "samdark/sitemap": "2.4.1",
"symfony/finder": "^6.4", "symfony/mailer": "^7.3",
"symfony/filesystem": "^6.4",
"symfony/event-dispatcher": "^6.4",
"symfony/mime": "^6.4",
"symfony/mailer": "^6.4",
"symfony/polyfill": "v1.32.0", "symfony/polyfill": "v1.32.0",
"vlucas/phpdotenv": "^5.5" "vlucas/phpdotenv": "^5.5",
"z4kn4fein/php-semver": "^v3.0.0"
}, },
"require-dev": { "require-dev": {
"symfony/var-dumper": "^6.4" "mockery/mockery": "^1.6",
"pestphp/pest": "^3.8",
"symfony/var-dumper": "^7.3"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"TorrentPier\\": "src/" "TorrentPier\\": "src/"
} }
}, },
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"config": { "config": {
"sort-packages": true, "sort-packages": true,
"optimize-autoloader": true, "optimize-autoloader": true,
"allow-plugins": { "allow-plugins": {
"pestphp/pest-plugin": true,
"php-http/discovery": true "php-http/discovery": true
} }
}, },
"minimum-stability": "dev", "minimum-stability": "stable",
"prefer-stable": true "prefer-stable": true
} }

4317
composer.lock generated

File diff suppressed because it is too large Load diff

6
dl.php
View file

@ -25,7 +25,7 @@ $m3u = isset($_GET['m3u']) && $_GET['m3u'];
// Send file to browser // Send file to browser
function send_file_to_browser($attachment, $upload_dir) function send_file_to_browser($attachment, $upload_dir)
{ {
global $bb_cfg, $lang; global $lang;
$filename = $upload_dir . '/' . $attachment['physical_filename']; $filename = $upload_dir . '/' . $attachment['physical_filename'];
$gotit = false; $gotit = false;
@ -170,7 +170,7 @@ if (!IS_AM && ($attachment['mimetype'] === TORRENT_MIMETYPE)) {
$row = DB()->sql_fetchrow($result); $row = DB()->sql_fetchrow($result);
if (isset($bb_cfg['tor_frozen'][$row['tor_status']]) && !(isset($bb_cfg['tor_frozen_author_download'][$row['tor_status']]) && $userdata['user_id'] === $row['poster_id'])) { if (isset(config()->get('tor_frozen')[$row['tor_status']]) && !(isset(config()->get('tor_frozen_author_download')[$row['tor_status']]) && $userdata['user_id'] === $row['poster_id'])) {
bb_die($lang['TOR_STATUS_FORBIDDEN'] . $lang['TOR_STATUS_NAME'][$row['tor_status']]); bb_die($lang['TOR_STATUS_FORBIDDEN'] . $lang['TOR_STATUS_NAME'][$row['tor_status']]);
} }
@ -219,7 +219,7 @@ switch ($download_mode) {
header('Location: ' . $url); header('Location: ' . $url);
exit; exit;
case INLINE_LINK: case INLINE_LINK:
if (IS_GUEST && !$bb_cfg['captcha']['disabled'] && !bb_captcha('check')) { if (IS_GUEST && !config()->get('captcha.disabled') && !bb_captcha('check')) {
global $template; global $template;
$redirect_url = $_POST['redirect_url'] ?? $_SERVER['HTTP_REFERER'] ?? '/'; $redirect_url = $_POST['redirect_url'] ?? $_SERVER['HTTP_REFERER'] ?? '/';

View file

@ -34,11 +34,11 @@ if ($mode === 'get_feed_url' && ($type === 'f' || $type === 'u') && $id >= 0) {
bb_simple_die($lang['ATOM_ERROR'] . ' #1'); bb_simple_die($lang['ATOM_ERROR'] . ' #1');
} }
} }
if (is_file($bb_cfg['atom']['path'] . '/f/' . $id . '.atom') && filemtime($bb_cfg['atom']['path'] . '/f/' . $id . '.atom') > $timecheck) { if (is_file(config()->get('atom.path') . '/f/' . $id . '.atom') && filemtime(config()->get('atom.path') . '/f/' . $id . '.atom') > $timecheck) {
redirect($bb_cfg['atom']['url'] . '/f/' . $id . '.atom'); redirect(config()->get('atom.url') . '/f/' . $id . '.atom');
} else { } else {
if (\TorrentPier\Legacy\Atom::update_forum_feed($id, $forum_data)) { if (\TorrentPier\Legacy\Atom::update_forum_feed($id, $forum_data)) {
redirect($bb_cfg['atom']['url'] . '/f/' . $id . '.atom'); redirect(config()->get('atom.url') . '/f/' . $id . '.atom');
} else { } else {
bb_simple_die($lang['ATOM_NO_FORUM']); bb_simple_die($lang['ATOM_NO_FORUM']);
} }
@ -52,11 +52,11 @@ if ($mode === 'get_feed_url' && ($type === 'f' || $type === 'u') && $id >= 0) {
if (!$username = get_username($id)) { if (!$username = get_username($id)) {
bb_simple_die($lang['ATOM_ERROR'] . ' #3'); bb_simple_die($lang['ATOM_ERROR'] . ' #3');
} }
if (is_file($bb_cfg['atom']['path'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') && filemtime($bb_cfg['atom']['path'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') > $timecheck) { if (is_file(config()->get('atom.path') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') && filemtime(config()->get('atom.path') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom') > $timecheck) {
redirect($bb_cfg['atom']['url'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom'); redirect(config()->get('atom.url') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
} else { } else {
if (\TorrentPier\Legacy\Atom::update_user_feed($id, $username)) { if (\TorrentPier\Legacy\Atom::update_user_feed($id, $username)) {
redirect($bb_cfg['atom']['url'] . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom'); redirect(config()->get('atom.url') . '/u/' . floor($id / 5000) . '/' . ($id % 100) . '/' . $id . '.atom');
} else { } else {
bb_simple_die($lang['ATOM_NO_USER']); bb_simple_die($lang['ATOM_NO_USER']);
} }

View file

@ -14,7 +14,7 @@ require __DIR__ . '/common.php';
// Start session management // Start session management
$user->session_start(); $user->session_start();
if ($bb_cfg['bt_disable_dht'] && IS_GUEST) { if (config()->get('bt_disable_dht') && IS_GUEST) {
bb_die($lang['BT_PRIVATE_TRACKER'], 403); bb_die($lang['BT_PRIVATE_TRACKER'], 403);
} }
@ -55,7 +55,7 @@ if (!is_file($file_path)) {
} }
$file_contents = file_get_contents($file_path); $file_contents = file_get_contents($file_path);
if ($bb_cfg['flist_max_files']) { if (config()->get('flist_max_files')) {
$filetree_pos = $meta_v2 ? strpos($file_contents, '9:file tree') : false; $filetree_pos = $meta_v2 ? strpos($file_contents, '9:file tree') : false;
$files_pos = $meta_v1 ? strpos($file_contents, '5:files', $filetree_pos) : false; $files_pos = $meta_v1 ? strpos($file_contents, '5:files', $filetree_pos) : false;
@ -65,8 +65,8 @@ if ($bb_cfg['flist_max_files']) {
$file_count = substr_count($file_contents, '6:length', $files_pos); $file_count = substr_count($file_contents, '6:length', $files_pos);
} }
if ($file_count > $bb_cfg['flist_max_files']) { if ($file_count > config()->get('flist_max_files')) {
bb_die(sprintf($lang['BT_FLIST_LIMIT'], $bb_cfg['flist_max_files'], $file_count), 410); bb_die(sprintf($lang['BT_FLIST_LIMIT'], config()->get('flist_max_files'), $file_count), 410);
} }
} }
@ -82,7 +82,7 @@ if (IS_GUEST && $torrent->isPrivate()) {
// Get torrent files // Get torrent files
$files = $torrent->$t_version_field()->$t_files_field(); $files = $torrent->$t_version_field()->$t_files_field();
if ($meta_v1 && $meta_v2) { if ($meta_v2) {
$files = new \RecursiveIteratorIterator($files); // Flatten the list $files = new \RecursiveIteratorIterator($files); // Flatten the list
} }
@ -102,19 +102,6 @@ foreach ($files as $file) {
$torrent_name = !empty($t_name = $torrent->getName()) ? str_short(htmlCHR($t_name), 200) : $lang['UNKNOWN']; $torrent_name = !empty($t_name = $torrent->getName()) ? str_short(htmlCHR($t_name), 200) : $lang['UNKNOWN'];
$torrent_size = humn_size($row['size'], 2); $torrent_size = humn_size($row['size'], 2);
// Get announcers list
$announcers_list = $torrent->getAnnounceList()->toArray();
$announcers_count = 0;
foreach ($announcers_list as $announcer) {
$announcers_count++;
$row_class = ($announcers_count % 2) ? 'row1' : 'row2';
$template->assign_block_vars('announcers', [
'ROW_NUMBER' => $announcers_count,
'ROW_CLASS' => $row_class,
'ANNOUNCER' => $announcer[0]
]);
}
// Output page // Output page
$template->assign_vars([ $template->assign_vars([
'PAGE_TITLE' => "$torrent_name (" . $torrent_size . ")", 'PAGE_TITLE' => "$torrent_name (" . $torrent_size . ")",

View file

@ -24,7 +24,7 @@ set_die_append_msg();
$group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : null; $group_id = isset($_REQUEST[POST_GROUPS_URL]) ? (int)$_REQUEST[POST_GROUPS_URL] : null;
$start = isset($_REQUEST['start']) ? abs((int)$_REQUEST['start']) : 0; $start = isset($_REQUEST['start']) ? abs((int)$_REQUEST['start']) : 0;
$per_page = $bb_cfg['group_members_per_page']; $per_page = config()->get('group_members_per_page');
$view_mode = isset($_REQUEST['view']) ? (string)$_REQUEST['view'] : null; $view_mode = isset($_REQUEST['view']) ? (string)$_REQUEST['view'] : null;
$rel_limit = 50; $rel_limit = 50;
@ -168,7 +168,7 @@ if (!$group_id) {
\TorrentPier\Legacy\Group::add_user_into_group($group_id, $userdata['user_id'], 1, TIMENOW); \TorrentPier\Legacy\Group::add_user_into_group($group_id, $userdata['user_id'], 1, TIMENOW);
if ($bb_cfg['group_send_email']) { if (config()->get('group_send_email')) {
// Sending email // Sending email
$emailer = new TorrentPier\Emailer(); $emailer = new TorrentPier\Emailer();
@ -224,7 +224,7 @@ if (!$group_id) {
\TorrentPier\Legacy\Group::add_user_into_group($group_id, $row['user_id']); \TorrentPier\Legacy\Group::add_user_into_group($group_id, $row['user_id']);
if ($bb_cfg['group_send_email']) { if (config()->get('group_send_email')) {
// Sending email // Sending email
$emailer = new TorrentPier\Emailer(); $emailer = new TorrentPier\Emailer();
@ -273,10 +273,10 @@ if (!$group_id) {
} }
} }
// Email users when they are approved // Email users when they are approved
if (!empty($_POST['approve']) && $bb_cfg['group_send_email']) { if (!empty($_POST['approve']) && config()->get('group_send_email')) {
$sql_select = "SELECT username, user_email, user_lang $sql_select = "SELECT username, user_email, user_lang
FROM " . BB_USERS . " FROM " . BB_USERS . "
WHERE user_id IN($sql_in)"; WHERE user_id IN($sql_in)";
if (!$result = DB()->sql_query($sql_select)) { if (!$result = DB()->sql_query($sql_select)) {
bb_die('Could not get user email information'); bb_die('Could not get user email information');

View file

@ -35,10 +35,10 @@ if ($group_id) {
if ($is_moderator) { if ($is_moderator) {
// Avatar // Avatar
if ($submit) { if ($submit) {
if (!empty($_FILES['avatar']['name']) && $bb_cfg['group_avatars']['up_allowed']) { if (!empty($_FILES['avatar']['name']) && config()->get('group_avatars.up_allowed')) {
$upload = new TorrentPier\Legacy\Common\Upload(); $upload = new TorrentPier\Legacy\Common\Upload();
if ($upload->init($bb_cfg['group_avatars'], $_FILES['avatar']) and $upload->store('avatar', ['user_id' => GROUP_AVATAR_MASK . $group_id, 'avatar_ext_id' => $group_info['avatar_ext_id']])) { if ($upload->init(config()->get('group_avatars'), $_FILES['avatar']) and $upload->store('avatar', ['user_id' => GROUP_AVATAR_MASK . $group_id, 'avatar_ext_id' => $group_info['avatar_ext_id']])) {
$avatar_ext_id = (int)$upload->file_ext_id; $avatar_ext_id = (int)$upload->file_ext_id;
DB()->query("UPDATE " . BB_GROUPS . " SET avatar_ext_id = $avatar_ext_id WHERE group_id = $group_id LIMIT 1"); DB()->query("UPDATE " . BB_GROUPS . " SET avatar_ext_id = $avatar_ext_id WHERE group_id = $group_id LIMIT 1");
} else { } else {
@ -76,7 +76,7 @@ if ($is_moderator) {
'S_HIDDEN_FIELDS' => $s_hidden_fields, 'S_HIDDEN_FIELDS' => $s_hidden_fields,
'S_GROUP_CONFIG_ACTION' => "group_edit.php?" . POST_GROUPS_URL . "=$group_id", 'S_GROUP_CONFIG_ACTION' => "group_edit.php?" . POST_GROUPS_URL . "=$group_id",
'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['group_avatars']['max_width'], $bb_cfg['group_avatars']['max_height'], humn_size($bb_cfg['group_avatars']['max_size'])), 'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], config()->get('group_avatars.max_width'), config()->get('group_avatars.max_height'), humn_size(config()->get('group_avatars.max_size'))),
'AVATAR_IMG' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']), 'AVATAR_IMG' => get_avatar(GROUP_AVATAR_MASK . $group_id, $group_info['avatar_ext_id']),
]); ]);

View file

@ -31,12 +31,12 @@ $datastore->enqueue([
'cat_forums' 'cat_forums'
]); ]);
if ($bb_cfg['show_latest_news']) { if (config()->get('show_latest_news')) {
$datastore->enqueue([ $datastore->enqueue([
'latest_news' 'latest_news'
]); ]);
} }
if ($bb_cfg['show_network_news']) { if (config()->get('show_network_news')) {
$datastore->enqueue([ $datastore->enqueue([
'network_news' 'network_news'
]); ]);
@ -46,7 +46,7 @@ if ($bb_cfg['show_network_news']) {
$user->session_start(); $user->session_start();
// Set meta description // Set meta description
$page_cfg['meta_description'] = $bb_cfg['site_desc']; $page_cfg['meta_description'] = config()->get('site_desc');
// Init main vars // Init main vars
$viewcat = isset($_GET[POST_CAT_URL]) ? (int)$_GET[POST_CAT_URL] : 0; $viewcat = isset($_GET[POST_CAT_URL]) ? (int)$_GET[POST_CAT_URL] : 0;
@ -57,7 +57,7 @@ $req_page = 'index_page';
$req_page .= $viewcat ? "_c{$viewcat}" : ''; $req_page .= $viewcat ? "_c{$viewcat}" : '';
define('REQUESTED_PAGE', $req_page); define('REQUESTED_PAGE', $req_page);
caching_output(IS_GUEST, 'send', REQUESTED_PAGE . '_guest_' . $bb_cfg['default_lang']); caching_output(IS_GUEST, 'send', REQUESTED_PAGE . '_guest_' . config()->get('default_lang'));
$hide_cat_opt = isset($user->opt_js['h_cat']) ? (string)$user->opt_js['h_cat'] : 0; $hide_cat_opt = isset($user->opt_js['h_cat']) ? (string)$user->opt_js['h_cat'] : 0;
$hide_cat_user = array_flip(explode('-', $hide_cat_opt)); $hide_cat_user = array_flip(explode('-', $hide_cat_opt));
@ -68,13 +68,15 @@ $tracking_topics = get_tracks('topic');
$tracking_forums = get_tracks('forum'); $tracking_forums = get_tracks('forum');
// Statistics // Statistics
if (!$stats = $datastore->get('stats')) { $stats = $datastore->get('stats');
if ($stats === false) {
$datastore->update('stats'); $datastore->update('stats');
$stats = $datastore->get('stats'); $stats = $datastore->get('stats');
} }
// Forums data // Forums data
if (!$forums = $datastore->get('cat_forums')) { $forums = $datastore->get('cat_forums');
if ($forums === false) {
$datastore->update('cat_forums'); $datastore->update('cat_forums');
$forums = $datastore->get('cat_forums'); $forums = $datastore->get('cat_forums');
} }
@ -177,7 +179,8 @@ if (!$cat_forums = CACHE('bb_cache')->get($cache_name)) {
// Obtain list of moderators // Obtain list of moderators
$moderators = []; $moderators = [];
if (!$mod = $datastore->get('moderators')) { $mod = $datastore->get('moderators');
if ($mod === false) {
$datastore->update('moderators'); $datastore->update('moderators');
$mod = $datastore->get('moderators'); $mod = $datastore->get('moderators');
} }
@ -259,7 +262,7 @@ foreach ($cat_forums as $cid => $c) {
'LAST_TOPIC_ID' => $f['last_topic_id'], 'LAST_TOPIC_ID' => $f['last_topic_id'],
'LAST_TOPIC_TIP' => $f['last_topic_title'], 'LAST_TOPIC_TIP' => $f['last_topic_title'],
'LAST_TOPIC_TITLE' => str_short($f['last_topic_title'], $last_topic_max_len), 'LAST_TOPIC_TITLE' => str_short($f['last_topic_title'], $last_topic_max_len),
'LAST_POST_TIME' => bb_date($f['last_post_time'], $bb_cfg['last_post_date_format']), 'LAST_POST_TIME' => bb_date($f['last_post_time'], config()->get('last_post_date_format')),
'LAST_POST_USER' => profile_url(['username' => str_short($f['last_post_username'], 15), 'user_id' => $f['last_post_user_id'], 'user_rank' => $f['last_post_user_rank']]), 'LAST_POST_USER' => profile_url(['username' => str_short($f['last_post_username'], 15), 'user_id' => $f['last_post_user_id'], 'user_rank' => $f['last_post_user_rank']]),
]); ]);
} }
@ -275,7 +278,7 @@ $template->assign_vars([
'TOTAL_TOPICS' => sprintf($lang['POSTED_TOPICS_TOTAL'], $stats['topiccount']), 'TOTAL_TOPICS' => sprintf($lang['POSTED_TOPICS_TOTAL'], $stats['topiccount']),
'TOTAL_POSTS' => sprintf($lang['POSTED_ARTICLES_TOTAL'], $stats['postcount']), 'TOTAL_POSTS' => sprintf($lang['POSTED_ARTICLES_TOTAL'], $stats['postcount']),
'TOTAL_USERS' => sprintf($lang['REGISTERED_USERS_TOTAL'], $stats['usercount']), 'TOTAL_USERS' => sprintf($lang['REGISTERED_USERS_TOTAL'], $stats['usercount']),
'TOTAL_GENDER' => $bb_cfg['gender'] ? sprintf( 'TOTAL_GENDER' => config()->get('gender') ? sprintf(
$lang['USERS_TOTAL_GENDER'], $lang['USERS_TOTAL_GENDER'],
$stats['male'], $stats['male'],
$stats['female'], $stats['female'],
@ -284,22 +287,22 @@ $template->assign_vars([
'NEWEST_USER' => sprintf($lang['NEWEST_USER'], profile_url($stats['newestuser'])), 'NEWEST_USER' => sprintf($lang['NEWEST_USER'], profile_url($stats['newestuser'])),
// Tracker stats // Tracker stats
'TORRENTS_STAT' => $bb_cfg['tor_stats'] ? sprintf( 'TORRENTS_STAT' => config()->get('tor_stats') ? sprintf(
$lang['TORRENTS_STAT'], $lang['TORRENTS_STAT'],
$stats['torrentcount'], $stats['torrentcount'],
humn_size($stats['size']) humn_size($stats['size'])
) : '', ) : '',
'PEERS_STAT' => $bb_cfg['tor_stats'] ? sprintf( 'PEERS_STAT' => config()->get('tor_stats') ? sprintf(
$lang['PEERS_STAT'], $lang['PEERS_STAT'],
$stats['peers'], $stats['peers'],
$stats['seeders'], $stats['seeders'],
$stats['leechers'] $stats['leechers']
) : '', ) : '',
'SPEED_STAT' => $bb_cfg['tor_stats'] ? sprintf( 'SPEED_STAT' => config()->get('tor_stats') ? sprintf(
$lang['SPEED_STAT'], $lang['SPEED_STAT'],
humn_size($stats['speed']) . '/s' humn_size($stats['speed']) . '/s'
) : '', ) : '',
'SHOW_MOD_INDEX' => $bb_cfg['show_mod_index'], 'SHOW_MOD_INDEX' => config()->get('show_mod_index'),
'FORUM_IMG' => $images['forum'], 'FORUM_IMG' => $images['forum'],
'FORUM_NEW_IMG' => $images['forum_new'], 'FORUM_NEW_IMG' => $images['forum_new'],
'FORUM_LOCKED_IMG' => $images['forum_locked'], 'FORUM_LOCKED_IMG' => $images['forum_locked'],
@ -312,20 +315,21 @@ $template->assign_vars([
'U_SEARCH_SELF_BY_MY' => "search.php?uid={$userdata['user_id']}&amp;o=1", 'U_SEARCH_SELF_BY_MY' => "search.php?uid={$userdata['user_id']}&amp;o=1",
'U_SEARCH_LATEST' => 'search.php?search_id=latest', 'U_SEARCH_LATEST' => 'search.php?search_id=latest',
'U_SEARCH_UNANSWERED' => 'search.php?search_id=unanswered', 'U_SEARCH_UNANSWERED' => 'search.php?search_id=unanswered',
'U_ATOM_FEED' => is_file($bb_cfg['atom']['path'] . '/f/0.atom') ? make_url($bb_cfg['atom']['url'] . '/f/0.atom') : false, 'U_ATOM_FEED' => is_file(config()->get('atom.path') . '/f/0.atom') ? make_url(config()->get('atom.url') . '/f/0.atom') : false,
'SHOW_LAST_TOPIC' => $show_last_topic, 'SHOW_LAST_TOPIC' => $show_last_topic,
'BOARD_START' => $bb_cfg['show_board_start_index'] ? ($lang['BOARD_STARTED'] . ':&nbsp;' . '<b>' . bb_date($bb_cfg['board_startdate']) . '</b>') : false, 'BOARD_START' => config()->get('show_board_start_index') ? ($lang['BOARD_STARTED'] . ':&nbsp;' . '<b>' . bb_date(config()->get('board_startdate')) . '</b>') : false,
]); ]);
// Set tpl vars for bt_userdata // Set tpl vars for bt_userdata
if ($bb_cfg['bt_show_dl_stat_on_index'] && !IS_GUEST) { if (config()->get('bt_show_dl_stat_on_index') && !IS_GUEST) {
show_bt_userdata($userdata['user_id']); show_bt_userdata($userdata['user_id']);
} }
// Latest news // Latest news
if ($bb_cfg['show_latest_news']) { if (config()->get('show_latest_news')) {
if (!$latest_news = $datastore->get('latest_news')) { $latest_news = $datastore->get('latest_news');
if ($latest_news === false) {
$datastore->update('latest_news'); $datastore->update('latest_news');
$latest_news = $datastore->get('latest_news'); $latest_news = $datastore->get('latest_news');
} }
@ -339,7 +343,7 @@ if ($bb_cfg['show_latest_news']) {
$template->assign_block_vars('news', [ $template->assign_block_vars('news', [
'NEWS_TOPIC_ID' => $news['topic_id'], 'NEWS_TOPIC_ID' => $news['topic_id'],
'NEWS_TITLE' => str_short($wordCensor->censorString($news['topic_title']), $bb_cfg['max_news_title']), 'NEWS_TITLE' => str_short(censor()->censorString($news['topic_title']), config()->get('max_news_title')),
'NEWS_TIME' => bb_date($news['topic_time'], 'd-M', false), 'NEWS_TIME' => bb_date($news['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($news['topic_time'], $news['topic_id'], $news['forum_id']), 'NEWS_IS_NEW' => is_unread($news['topic_time'], $news['topic_id'], $news['forum_id']),
]); ]);
@ -347,8 +351,9 @@ if ($bb_cfg['show_latest_news']) {
} }
// Network news // Network news
if ($bb_cfg['show_network_news']) { if (config()->get('show_network_news')) {
if (!$network_news = $datastore->get('network_news')) { $network_news = $datastore->get('network_news');
if ($network_news === false) {
$datastore->update('network_news'); $datastore->update('network_news');
$network_news = $datastore->get('network_news'); $network_news = $datastore->get('network_news');
} }
@ -362,14 +367,14 @@ if ($bb_cfg['show_network_news']) {
$template->assign_block_vars('net', [ $template->assign_block_vars('net', [
'NEWS_TOPIC_ID' => $net['topic_id'], 'NEWS_TOPIC_ID' => $net['topic_id'],
'NEWS_TITLE' => str_short($wordCensor->censorString($net['topic_title']), $bb_cfg['max_net_title']), 'NEWS_TITLE' => str_short(censor()->censorString($net['topic_title']), config()->get('max_net_title')),
'NEWS_TIME' => bb_date($net['topic_time'], 'd-M', false), 'NEWS_TIME' => bb_date($net['topic_time'], 'd-M', false),
'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']), 'NEWS_IS_NEW' => is_unread($net['topic_time'], $net['topic_id'], $net['forum_id']),
]); ]);
} }
} }
if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) { if (config()->get('birthday_check_day') && config()->get('birthday_enabled')) {
$week_list = $today_list = []; $week_list = $today_list = [];
$week_all = $today_all = false; $week_all = $today_all = false;
@ -383,9 +388,9 @@ if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) {
$week_list[] = profile_url($week) . ' <span class="small">(' . birthday_age(date('Y-m-d', strtotime('-1 year', strtotime($week['user_birthday'])))) . ')</span>'; $week_list[] = profile_url($week) . ' <span class="small">(' . birthday_age(date('Y-m-d', strtotime('-1 year', strtotime($week['user_birthday'])))) . ')</span>';
} }
$week_all = $week_all ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_week\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : ''; $week_all = $week_all ? '&nbsp;<a class="txtb" href="#" onclick="ajax.exec({action: \'index_data\', mode: \'birthday_week\'}); return false;" title="' . $lang['ALL'] . '">...</a>' : '';
$week_list = sprintf($lang['BIRTHDAY_WEEK'], $bb_cfg['birthday_check_day'], implode(', ', $week_list)) . $week_all; $week_list = sprintf($lang['BIRTHDAY_WEEK'], config()->get('birthday_check_day'), implode(', ', $week_list)) . $week_all;
} else { } else {
$week_list = sprintf($lang['NOBIRTHDAY_WEEK'], $bb_cfg['birthday_check_day']); $week_list = sprintf($lang['NOBIRTHDAY_WEEK'], config()->get('birthday_check_day'));
} }
if (!empty($stats['birthday_today_list'])) { if (!empty($stats['birthday_today_list'])) {

View file

@ -11,7 +11,7 @@ define('BB_ROOT', __DIR__ . DIRECTORY_SEPARATOR);
define('BB_PATH', BB_ROOT); define('BB_PATH', BB_ROOT);
// Check CLI mode // Check CLI mode
if (php_sapi_name() !== 'cli') { if (PHP_SAPI != 'cli') {
die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode'); die('Please run <code style="background:#222;color:#00e01f;padding:2px 6px;border-radius:3px;">php ' . basename(__FILE__) . '</code> in CLI mode');
} }
@ -24,8 +24,8 @@ require INC_DIR . '/functions_cli.php';
/** /**
* System requirements * System requirements
*/ */
define('CHECK_REQUIREMENTS', [ const CHECK_REQUIREMENTS = [
'php_min_version' => '8.1.0', 'php_min_version' => '8.2.0',
'ext_list' => [ 'ext_list' => [
'json', 'json',
'curl', 'curl',
@ -39,7 +39,7 @@ define('CHECK_REQUIREMENTS', [
'zip', 'zip',
'gd' 'gd'
], ],
]); ];
// Welcoming message // Welcoming message
out("--- TorrentPier Installer ---\n", 'info'); out("--- TorrentPier Installer ---\n", 'info');
@ -145,8 +145,7 @@ if (!is_file(BB_ROOT . 'vendor/autoload.php')) {
// Installing dependencies // Installing dependencies
if (is_file(BB_ROOT . 'composer.phar')) { if (is_file(BB_ROOT . 'composer.phar')) {
out('- Installing dependencies...', 'info'); out('- Installing dependencies...', 'info');
runProcess('php ' . BB_ROOT . 'composer.phar update --no-install');
sleep(3);
runProcess('php ' . BB_ROOT . 'composer.phar install --no-interaction --no-ansi'); runProcess('php ' . BB_ROOT . 'composer.phar install --no-interaction --no-ansi');
define('COMPOSER_COMPLETED', true); define('COMPOSER_COMPLETED', true);
} else { } else {
@ -265,35 +264,26 @@ if (!empty($DB_HOST) && !empty($DB_DATABASE) && !empty($DB_USERNAME)) {
} }
$conn->select_db($DB_DATABASE); $conn->select_db($DB_DATABASE);
// Checking SQL dump // Close database connection - migrations will handle their own connections
$dumpPath = BB_ROOT . 'install/sql/mysql.sql'; $conn->close();
if (is_file($dumpPath) && is_readable($dumpPath)) {
out('- SQL dump file found and readable!', 'success'); // Run database migrations
} else { out('- Setting up database using migrations...', 'info');
out('- SQL dump file not found / not readable', 'error');
// Check if phinx.php exists
if (!is_file(BB_ROOT . 'phinx.php')) {
out('- Migration configuration (phinx.php) not found', 'error');
exit; exit;
} }
// Inserting SQL dump // Run migrations
out('- Start importing SQL dump...', 'info'); $migrationResult = runProcess('php vendor/bin/phinx migrate --configuration=' . BB_ROOT . 'phinx.php');
$tempLine = ''; if ($migrationResult !== 0) {
foreach (file($dumpPath) as $line) { out('- Database migration failed', 'error');
if (str_starts_with($line, '--') || $line == '') { exit;
continue;
}
$tempLine .= $line;
if (str_ends_with(trim($line), ';')) {
if (!$conn->query($tempLine)) {
out("- Error performing query: $tempLine", 'error');
exit;
}
$tempLine = '';
}
} }
$conn->close(); out("- Database setup completed!\n", 'success');
out("- Importing SQL dump completed!\n", 'success');
// Autofill host in robots.txt // Autofill host in robots.txt
$robots_txt_file = BB_ROOT . 'robots.txt'; $robots_txt_file = BB_ROOT . 'robots.txt';

File diff suppressed because it is too large Load diff

View file

@ -1,157 +0,0 @@
// Changes from v2.2.0 to 2.4.5
// 2.2.0
UPDATE `bb_config` SET `config_value` = 'http://whatismyipaddress.com/ip/' WHERE `config_name` = 'whois_info';
DELETE FROM `bb_smilies` WHERE `code` = ':ad:';
INSERT INTO `bb_smilies` (`code`, `smile_url`, `emoticon`) VALUES (':сd:', 'сd.gif', 'сd');
DROP TABLE IF EXISTS `bb_ads`;
DELETE FROM `bb_config` WHERE `config_name` = 'active_ads';
ALTER TABLE `bb_log` DROP COLUMN `log_username`;
DELETE FROM `bb_config` WHERE `config_name` = 'new_tpls';
UPDATE `bb_posts` SET `poster_ip` = '0';
ALTER TABLE `bb_posts` CHANGE `poster_ip` `poster_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_bt_tracker` SET `ip` = '0';
ALTER TABLE `bb_bt_tracker` CHANGE `ip` `ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_last_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_last_ip` `user_last_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_users` SET `user_reg_ip` = '0';
ALTER TABLE `bb_users` CHANGE `user_reg_ip` `user_reg_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_log` SET `log_user_ip` = '0';
ALTER TABLE `bb_log` CHANGE `log_user_ip` `log_user_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_poll_users` SET `vote_ip` = '0';
ALTER TABLE `bb_poll_users` CHANGE `vote_ip` `vote_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_privmsgs` SET `privmsgs_ip` = '0';
ALTER TABLE `bb_privmsgs` CHANGE `privmsgs_ip` `privmsgs_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_sessions` SET `session_ip` = '0';
ALTER TABLE `bb_sessions` CHANGE `session_ip` `session_ip` varchar(42) NOT NULL DEFAULT '0';
UPDATE `bb_banlist` SET `ban_ip` = '0';
ALTER TABLE `bb_banlist` CHANGE `ban_ip` `ban_ip` varchar(42) NOT NULL DEFAULT '0';
// 2.2.2
ALTER TABLE `bb_ranks` DROP `rank_min`;
ALTER TABLE `bb_ranks` DROP `rank_special`;
// 2.3.0
ALTER TABLE `bb_cron` CHANGE `last_run` `last_run` DATETIME NOT NULL DEFAULT '1900-01-01 00:00:00';
ALTER TABLE `bb_cron` CHANGE `next_run` `next_run` DATETIME NOT NULL DEFAULT '1900-01-01 00:00:00';
ALTER TABLE `bb_users` CHANGE `user_birthday` `user_birthday` DATE NOT NULL DEFAULT '1900-01-01';
ALTER TABLE `bb_posts` CHANGE `mc_comment` `mc_comment` TEXT NOT NULL DEFAULT '';
// 2.3.0.2
ALTER TABLE `bb_users` CHANGE `user_sig` `user_sig` TEXT NOT NULL DEFAULT '';
ALTER TABLE `bb_groups` CHANGE `group_signature` `group_signature` TEXT NOT NULL DEFAULT '';
ALTER TABLE `bb_groups` CHANGE `group_description` `group_description` TEXT NOT NULL DEFAULT '';
UPDATE `bb_smilies` SET `code` = ':cd:', `smile_url` = 'cd.gif', `emoticon` = 'cd' WHERE `code` = ':сd:' AND `smile_url` = 'сd.gif' AND `emoticon` = 'сd';
// 2.3.1
ALTER TABLE `bb_search_results` CHANGE `search_id` `search_id` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `autologin_id` `autologin_id` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
DELETE FROM `bb_config` WHERE `config_name` = 'cron_enabled';
// 2.4.0-alpha1
ALTER TABLE `bb_search_results` CHANGE `session_id` `session_id` CHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_sessions` CHANGE `session_id` `session_id` CHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `username` `username` VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_password` `user_password` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_actkey` `user_actkey` VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE `bb_users` CHANGE `user_newpasswd` `user_newpasswd` VARCHAR(255) NOT NULL DEFAULT '';
// 2.4.0-alpha3
INSERT INTO bb_config VALUES ('show_board_start_index', '1');
// 2.4.0-beta2
INSERT INTO `bb_cron` (`cron_active`, `cron_title`, `cron_script`, `schedule`, `run_day`, `run_time`, `run_order`,
`last_run`, `next_run`, `run_interval`, `log_enabled`, `log_file`, `log_sql_queries`,
`disable_board`, `run_counter`) VALUES ('1', 'PM cleanup', 'clean_pm.php', 'daily', '', '05:00:00', '70', '', '', '', '1', '', '0', '1', '0');
ALTER TABLE `bb_posts_text` CHANGE `post_text` `post_text` MEDIUMTEXT NOT NULL;
ALTER TABLE `bb_privmsgs_text` CHANGE `privmsgs_text` `privmsgs_text` MEDIUMTEXT NOT NULL;
ALTER TABLE `bb_bt_torrents` ADD COLUMN `info_hash_v2` VARBINARY(32) NOT NULL DEFAULT '';
ALTER TABLE `bb_bt_tracker_snap` ADD COLUMN `completed` INT(10) NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `complete` `complete` TINYINT(1) NOT NULL DEFAULT '0';
// 2.4.0-beta3
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'webp', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('2', '7z', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'bmp', '');
ALTER TABLE `bb_bt_tracker` CHANGE `speed_up` `speed_up` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `speed_down` `speed_down` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker_snap` CHANGE `speed_up` `speed_up` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker_snap` CHANGE `speed_down` `speed_down` INT(11) UNSIGNED NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_torrents` ADD COLUMN `last_seeder_id` MEDIUMINT(8) NOT NULL DEFAULT '0';
ALTER TABLE `buf_last_seeder` ADD COLUMN `user_id` MEDIUMINT(8) NOT NULL DEFAULT '0';
ALTER TABLE `bb_bt_tracker` CHANGE `ip` `ip` VARCHAR(42) DEFAULT NULL;
ALTER TABLE `bb_bt_tracker` CHANGE `ipv6` `ipv6` VARCHAR(42) DEFAULT NULL;
ALTER TABLE `bb_bt_users` CHANGE `auth_key` `auth_key` CHAR(20) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT '';
// 2.4.0-beta4
DELETE FROM `bb_extensions` WHERE `extension` = 'tif';
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tif', '');
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tiff', '');
DELETE FROM `bb_extensions` WHERE `extension` = 'tga';
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('4', 'tga', '');
// 2.4.0-rc1
ALTER TABLE `bb_bt_tracker` DROP COLUMN `client`;
DROP TABLE IF EXISTS `bb_thx`;
CREATE TABLE IF NOT EXISTS `bb_thx`
(
`topic_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0',
`user_id` MEDIUMINT(8) NOT NULL DEFAULT '0',
`time` INT(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`topic_id`, `user_id`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
// 2.4.0
UPDATE `bb_attachments_config` SET `config_value` = 'data/uploads' WHERE `config_name` = 'upload_dir';
UPDATE `bb_attachments_config` SET `config_value` = '12000' WHERE `config_name` = 'img_min_thumb_filesize';
DELETE FROM `bb_attachments_config` WHERE config_name = 'attach_version';
DELETE FROM `bb_attachments_config` WHERE config_name = 'img_min_thumb_filesize';
DELETE FROM `bb_attachments_config` WHERE config_name = 'img_imagick';
DELETE FROM `bb_attachments_config` WHERE config_name = 'use_gd2';
DELETE FROM `bb_attachments_config` WHERE config_name = 'wma_autoplay';
DELETE FROM `bb_attachments_config` WHERE config_name = 'flash_autoplay';
DELETE FROM `bb_extensions` WHERE extension = 'tif';
DELETE FROM `bb_extensions` WHERE extension = 'tiff';
DELETE FROM `bb_extensions` WHERE extension = 'tga';
DROP TABLE IF EXISTS `bb_banlist`;
CREATE TABLE IF NOT EXISTS `bb_banlist`
(
`ban_id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
`ban_userid` MEDIUMINT(8) NOT NULL DEFAULT '0',
`ban_reason` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`ban_id`, `ban_userid`)
)
ENGINE = MyISAM
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
// 2.4.1
UPDATE `bb_config` SET `config_value` = '' WHERE `config_name` = 'bt_announce_url';
// 2.4.2
INSERT INTO `bb_cron` (`cron_active`, `cron_title`, `cron_script`, `schedule`, `run_day`, `run_time`, `run_order`,
`last_run`, `next_run`, `run_interval`, `log_enabled`, `log_file`, `log_sql_queries`,
`disable_board`, `run_counter`) VALUES ('1', 'Demo mode', 'demo_mode.php', 'daily', '', '05:00:00', '255', '', '', '', '1', 'demo_mode_cron', '1', '1', '0');
// 2.4.3
UPDATE `bb_config` SET `config_value` = 'https://localhost/bt/announce.php' WHERE `config_name` = 'bt_announce_url';
// 2.4.4
ALTER TABLE `bb_poll_users` CHANGE `user_id` `user_id` MEDIUMINT(8) NOT NULL;
ALTER TABLE `bb_bt_users` ADD COLUMN `ratio_nulled` TINYINT(1) NOT NULL DEFAULT '0';
DELETE FROM `bb_cron` WHERE `cron_script` = 'cache_gc.php';
UPDATE `bb_cron` SET `run_interval` = '00:10:00' WHERE `cron_script` = 'tr_seed_bonus.php';
// 2.4.5-rc.1
INSERT INTO `bb_extensions` (`group_id`, `extension`, `comment`) VALUES ('1', 'avif', ''), ('3', 'm3u', '');
ALTER TABLE `bb_topics` ADD COLUMN `topic_allow_robots` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0';
// 2.4.5-rc.2
INSERT INTO `bb_config` VALUES ('magnet_links_for_guests', '0');
INSERT INTO `bb_config` VALUES ('tp_instance_hash', '');
// 2.4.5-rc.5
DELETE FROM `bb_config` WHERE `config_name` = 'tp_instance_hash';

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang, $user; global $lang, $user;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');

View file

@ -11,9 +11,9 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $userdata, $lang; global $userdata, $lang;
if (!$bb_cfg['callseed']) { if (!config()->get('callseed')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
@ -32,7 +32,7 @@ if ($t_data['seeders'] >= 3) {
} elseif ($t_data['call_seed_time'] >= (TIMENOW - 86400)) { } elseif ($t_data['call_seed_time'] >= (TIMENOW - 86400)) {
$time_left = delta_time($t_data['call_seed_time'] + 86400, TIMENOW, 'days'); $time_left = delta_time($t_data['call_seed_time'] + 86400, TIMENOW, 'days');
$this->ajax_die(sprintf($lang['CALLSEED_MSG_SPAM'], $time_left)); $this->ajax_die(sprintf($lang['CALLSEED_MSG_SPAM'], $time_left));
} elseif (isset($bb_cfg['tor_no_tor_act'][$t_data['tor_status']])) { } elseif (isset(config()->get('tor_no_tor_act')[$t_data['tor_status']])) {
$this->ajax_die($lang['NOT_AVAILABLE']); $this->ajax_die($lang['NOT_AVAILABLE']);
} }

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $userdata, $bb_cfg, $lang, $log_action; global $userdata, $lang, $log_action;
if (!$attach_id = (int)$this->request['attach_id']) { if (!$attach_id = (int)$this->request['attach_id']) {
$this->ajax_die($lang['EMPTY_ATTACH_ID']); $this->ajax_die($lang['EMPTY_ATTACH_ID']);
@ -22,7 +22,7 @@ if (!$mode = (string)$this->request['mode']) {
} }
$comment = false; $comment = false;
if ($bb_cfg['tor_comment']) { if (config()->get('tor_comment')) {
$comment = (string)$this->request['comment']; $comment = (string)$this->request['comment'];
} }
@ -88,7 +88,7 @@ switch ($mode) {
\TorrentPier\Legacy\Torrent::change_tor_status($attach_id, $new_status); \TorrentPier\Legacy\Torrent::change_tor_status($attach_id, $new_status);
// Log action // Log action
$log_msg = sprintf($lang['TOR_STATUS_LOG_ACTION'], $bb_cfg['tor_icons'][$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b>', $bb_cfg['tor_icons'][$tor['tor_status']] . ' <b> ' . $lang['TOR_STATUS_NAME'][$tor['tor_status']] . '</b>'); $log_msg = sprintf($lang['TOR_STATUS_LOG_ACTION'], config()->get('tor_icons')[$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b>', config()->get('tor_icons')[$tor['tor_status']] . ' <b> ' . $lang['TOR_STATUS_NAME'][$tor['tor_status']] . '</b>');
if ($comment && $comment != $lang['COMMENT']) { if ($comment && $comment != $lang['COMMENT']) {
$log_msg .= "<br/>{$lang['COMMENT']}: <b>$comment</b>."; $log_msg .= "<br/>{$lang['COMMENT']}: <b>$comment</b>.";
} }
@ -99,12 +99,12 @@ switch ($mode) {
'log_msg' => $log_msg . '<br/>-------------', 'log_msg' => $log_msg . '<br/>-------------',
]); ]);
$this->response['status'] = $bb_cfg['tor_icons'][$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b> &middot; ' . profile_url($userdata) . ' &middot; <i>' . delta_time(TIMENOW) . $lang['TOR_BACK'] . '</i>'; $this->response['status'] = config()->get('tor_icons')[$new_status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$new_status] . '</b> &middot; ' . profile_url($userdata) . ' &middot; <i>' . delta_time(TIMENOW) . $lang['TOR_BACK'] . '</i>';
if ($bb_cfg['tor_comment'] && (($comment && $comment != $lang['COMMENT']) || in_array($new_status, $bb_cfg['tor_reply']))) { if (config()->get('tor_comment') && (($comment && $comment != $lang['COMMENT']) || in_array($new_status, config()->get('tor_reply')))) {
if ($tor['poster_id'] > 0) { if ($tor['poster_id'] > 0) {
$subject = sprintf($lang['TOR_MOD_TITLE'], $tor['topic_title']); $subject = sprintf($lang['TOR_MOD_TITLE'], $tor['topic_title']);
$message = sprintf($lang['TOR_MOD_MSG'], get_username($tor['poster_id']), make_url(TOPIC_URL . $tor['topic_id']), $bb_cfg['tor_icons'][$new_status] . ' ' . $lang['TOR_STATUS_NAME'][$new_status]); $message = sprintf($lang['TOR_MOD_MSG'], get_username($tor['poster_id']), make_url(TOPIC_URL . $tor['topic_id']), config()->get('tor_icons')[$new_status] . ' ' . $lang['TOR_STATUS_NAME'][$new_status]);
if ($comment && $comment != $lang['COMMENT']) { if ($comment && $comment != $lang['COMMENT']) {
$message .= "\n\n[b]" . $lang['COMMENT'] . '[/b]: ' . $comment; $message .= "\n\n[b]" . $lang['COMMENT'] . '[/b]: ' . $comment;
@ -117,7 +117,7 @@ switch ($mode) {
break; break;
case 'status_reply': case 'status_reply':
if (!$bb_cfg['tor_comment']) { if (!config()->get('tor_comment')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $userdata, $bb_cfg, $lang, $log_action; global $userdata, $lang, $log_action;
if (!isset($this->request['attach_id'])) { if (!isset($this->request['attach_id'])) {
$this->ajax_die($lang['EMPTY_ATTACH_ID']); $this->ajax_die($lang['EMPTY_ATTACH_ID']);

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $userdata, $lang; global $userdata, $lang;
if (!$group_id = (int)$this->request['group_id'] or !$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) { if (!$group_id = (int)$this->request['group_id'] or !$group_info = \TorrentPier\Legacy\Group::get_group_data($group_id)) {
$this->ajax_die($lang['NO_GROUP_ID_SPECIFIED']); $this->ajax_die($lang['NO_GROUP_ID_SPECIFIED']);

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang; global $lang;
if (!$user_id = (int)$this->request['user_id'] or !$profiledata = get_userdata($user_id)) { if (!$user_id = (int)$this->request['user_id'] or !$profiledata = get_userdata($user_id)) {
$this->ajax_die($lang['NO_USER_ID_SPECIFIED']); $this->ajax_die($lang['NO_USER_ID_SPECIFIED']);
@ -55,7 +55,7 @@ switch ($field) {
break; break;
case 'user_gender': case 'user_gender':
if (!$bb_cfg['gender']) { if (!config()->get('gender')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
if (!isset($lang['GENDER_SELECT'][$value])) { if (!isset($lang['GENDER_SELECT'][$value])) {
@ -65,7 +65,7 @@ switch ($field) {
break; break;
case 'user_birthday': case 'user_birthday':
if (!$bb_cfg['birthday_enabled']) { if (!config()->get('birthday_enabled')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
$birthday_date = date_parse($value); $birthday_date = date_parse($value);
@ -73,10 +73,10 @@ switch ($field) {
if (!empty($birthday_date['year'])) { if (!empty($birthday_date['year'])) {
if (strtotime($value) >= TIMENOW) { if (strtotime($value) >= TIMENOW) {
$this->ajax_die($lang['WRONG_BIRTHDAY_FORMAT']); $this->ajax_die($lang['WRONG_BIRTHDAY_FORMAT']);
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] > $bb_cfg['birthday_max_age']) { } elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] > config()->get('birthday_max_age')) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_HIGH'], $bb_cfg['birthday_max_age'])); $this->ajax_die(sprintf($lang['BIRTHDAY_TO_HIGH'], config()->get('birthday_max_age')));
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] < $bb_cfg['birthday_min_age']) { } elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] < config()->get('birthday_min_age')) {
$this->ajax_die(sprintf($lang['BIRTHDAY_TO_LOW'], $bb_cfg['birthday_min_age'])); $this->ajax_die(sprintf($lang['BIRTHDAY_TO_LOW'], config()->get('birthday_min_age')));
} }
} }

View file

@ -11,13 +11,13 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang; global $lang;
if (!$bb_cfg['torr_server']['enabled']) { if (!config()->get('torr_server.enabled')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
if ($bb_cfg['torr_server']['disable_for_guest'] && IS_GUEST) { if (config()->get('torr_server.disable_for_guest') && IS_GUEST) {
$this->ajax_die($lang['NEED_TO_LOGIN_FIRST']); $this->ajax_die($lang['NEED_TO_LOGIN_FIRST']);
} }

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang, $userdata, $datastore; global $lang, $userdata, $datastore;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');
@ -31,9 +31,9 @@ switch ($mode) {
foreach ($stats['birthday_week_list'] as $week) { foreach ($stats['birthday_week_list'] as $week) {
$users[] = profile_url($week) . ' <span class="small">(' . birthday_age(date('Y-m-d', strtotime('-1 year', strtotime($week['user_birthday'])))) . ')</span>'; $users[] = profile_url($week) . ' <span class="small">(' . birthday_age(date('Y-m-d', strtotime('-1 year', strtotime($week['user_birthday'])))) . ')</span>';
} }
$html = sprintf($lang['BIRTHDAY_WEEK'], $bb_cfg['birthday_check_day'], implode(', ', $users)); $html = sprintf($lang['BIRTHDAY_WEEK'], config()->get('birthday_check_day'), implode(', ', $users));
} else { } else {
$html = sprintf($lang['NOBIRTHDAY_WEEK'], $bb_cfg['birthday_check_day']); $html = sprintf($lang['NOBIRTHDAY_WEEK'], config()->get('birthday_check_day'));
} }
break; break;
@ -84,7 +84,7 @@ switch ($mode) {
break; break;
case 'null_ratio': case 'null_ratio':
if (!$bb_cfg['ratio_null_enabled'] || !RATIO_ENABLED) { if (!config()->get('ratio_null_enabled') || !RATIO_ENABLED) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
if (empty($this->request['confirmed'])) { if (empty($this->request['confirmed'])) {
@ -106,8 +106,8 @@ switch ($mode) {
if ($ratio_nulled && !IS_ADMIN) { if ($ratio_nulled && !IS_ADMIN) {
$this->ajax_die($lang['BT_NULL_RATIO_AGAIN']); $this->ajax_die($lang['BT_NULL_RATIO_AGAIN']);
} }
if (($user_ratio >= $bb_cfg['ratio_to_null']) && !IS_ADMIN) { if (($user_ratio >= config()->get('ratio_to_null')) && !IS_ADMIN) {
$this->ajax_die(sprintf($lang['BT_NULL_RATIO_NOT_NEEDED'], $bb_cfg['ratio_to_null'])); $this->ajax_die(sprintf($lang['BT_NULL_RATIO_NOT_NEEDED'], config()->get('ratio_to_null')));
} }
$ratio_nulled_sql = !IS_ADMIN ? ', ratio_nulled = 1' : ''; $ratio_nulled_sql = !IS_ADMIN ? ', ratio_nulled = 1' : '';
@ -172,7 +172,7 @@ switch ($mode) {
<th>' . $lang['UPLOADED'] . '</th> <th>' . $lang['UPLOADED'] . '</th>
<th>' . $lang['RELEASED'] . '</th> <th>' . $lang['RELEASED'] . '</th>
<th>' . $lang['BONUS'] . '</th>'; <th>' . $lang['BONUS'] . '</th>';
$html .= $bb_cfg['seed_bonus_enabled'] ? '<th>' . $lang['SEED_BONUS'] . '</th>' : ''; $html .= config()->get('seed_bonus_enabled') ? '<th>' . $lang['SEED_BONUS'] . '</th>' : '';
$html .= '</tr> $html .= '</tr>
<tr class="row1"> <tr class="row1">
<td>' . $lang['TOTAL_TRAF'] . '</td> <td>' . $lang['TOTAL_TRAF'] . '</td>
@ -180,17 +180,17 @@ switch ($mode) {
<td id="u_up_total"><span class="editable bold seedmed">' . humn_size($btu['u_up_total']) . '</span></td> <td id="u_up_total"><span class="editable bold seedmed">' . humn_size($btu['u_up_total']) . '</span></td>
<td id="u_up_release"><span class="editable bold seedmed">' . humn_size($btu['u_up_release']) . '</span></td> <td id="u_up_release"><span class="editable bold seedmed">' . humn_size($btu['u_up_release']) . '</span></td>
<td id="u_up_bonus"><span class="editable bold seedmed">' . humn_size($btu['u_up_bonus']) . '</span></td>'; <td id="u_up_bonus"><span class="editable bold seedmed">' . humn_size($btu['u_up_bonus']) . '</span></td>';
$html .= $bb_cfg['seed_bonus_enabled'] ? '<td id="user_points"><span class="editable bold points">' . $profiledata['user_points'] . '</b></td>' : ''; $html .= config()->get('seed_bonus_enabled') ? '<td id="user_points"><span class="editable bold points">' . $profiledata['user_points'] . '</b></td>' : '';
$html .= '</tr> $html .= '</tr>
<tr class="row5"> <tr class="row5">
<td colspan="1">' . $lang['MAX_SPEED'] . '</td> <td colspan="1">' . $lang['MAX_SPEED'] . '</td>
<td colspan="2">' . $lang['DL_DL_SPEED'] . ': ' . $speed_down . '</span></td> <td colspan="2">' . $lang['DL_DL_SPEED'] . ': ' . $speed_down . '</span></td>
<td colspan="2">' . $lang['DL_UL_SPEED'] . ': ' . $speed_up . '</span></td>'; <td colspan="2">' . $lang['DL_UL_SPEED'] . ': ' . $speed_up . '</span></td>';
$html .= $bb_cfg['seed_bonus_enabled'] ? '<td colspan="1"></td>' : ''; $html .= config()->get('seed_bonus_enabled') ? '<td colspan="1"></td>' : '';
$html .= '</tr>'; $html .= '</tr>';
$this->response['user_ratio'] = ' $this->response['user_ratio'] = '
<th><a href="' . $bb_cfg['ratio_url_help'] . '" class="bold">' . $lang['USER_RATIO'] . '</a>:</th> <th><a href="' . config()->get('ratio_url_help') . '" class="bold">' . $lang['USER_RATIO'] . '</a>:</th>
<td>' . $user_ratio . '</td> <td>' . $user_ratio . '</td>
'; ';
break; break;

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $userdata, $lang, $bb_cfg; global $userdata, $lang;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');
@ -19,7 +19,7 @@ if (!$mode = (string)$this->request['mode']) {
switch ($mode) { switch ($mode) {
case 'clear_cache': case 'clear_cache':
foreach ($bb_cfg['cache']['engines'] as $cache_name => $cache_val) { foreach (config()->get('cache.engines') as $cache_name => $cache_val) {
CACHE($cache_name)->rm(); CACHE($cache_name)->rm();
} }
@ -48,20 +48,20 @@ switch ($mode) {
$this->response['template_cache_html'] = '<span class="seed bold">' . $lang['ALL_TEMPLATE_CLEARED'] . '</span>'; $this->response['template_cache_html'] = '<span class="seed bold">' . $lang['ALL_TEMPLATE_CLEARED'] . '</span>';
break; break;
case 'indexer': case 'indexer':
exec("indexer --config {$bb_cfg['sphinx_config_path']} --all --rotate", $result); exec("indexer --config " . config()->get('sphinx_config_path') . " --all --rotate", $result);
if (!is_file($bb_cfg['sphinx_config_path'] . ".log")) { if (!is_file(config()->get('sphinx_config_path') . ".log")) {
file_put_contents($bb_cfg['sphinx_config_path'] . ".log", "##############################" . date("H:i:s", TIMENOW) . "##############################\r\n\r\n\r\n\r\n", FILE_APPEND); file_put_contents(config()->get('sphinx_config_path') . ".log", "##############################" . date("H:i:s", TIMENOW) . "##############################\r\n\r\n\r\n\r\n", FILE_APPEND);
} }
file_put_contents($bb_cfg['sphinx_config_path'] . ".log", "##############################" . date("H:i:s", TIMENOW) . "##############################\r\n", FILE_APPEND); file_put_contents(config()->get('sphinx_config_path') . ".log", "##############################" . date("H:i:s", TIMENOW) . "##############################\r\n", FILE_APPEND);
foreach ($result as $row) { foreach ($result as $row) {
file_put_contents($bb_cfg['sphinx_config_path'] . ".log", $row . "\r\n", FILE_APPEND); file_put_contents(config()->get('sphinx_config_path') . ".log", $row . "\r\n", FILE_APPEND);
} }
file_put_contents($bb_cfg['sphinx_config_path'] . ".log", "\r\n", FILE_APPEND); file_put_contents(config()->get('sphinx_config_path') . ".log", "\r\n", FILE_APPEND);
file_put_contents($bb_cfg['sphinx_config_path'] . ".log", "\r\n", FILE_APPEND); file_put_contents(config()->get('sphinx_config_path') . ".log", "\r\n", FILE_APPEND);
$this->response['indexer_html'] = '<span class="seed bold">' . $lang['INDEXER'] . '</span>'; $this->response['indexer_html'] = '<span class="seed bold">' . $lang['INDEXER'] . '</span>';
break; break;

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $userdata, $lang, $bb_cfg; global $userdata, $lang;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $userdata, $bb_cfg, $lang, $datastore, $log_action; global $userdata, $lang, $datastore, $log_action;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');
@ -44,7 +44,7 @@ switch ($mode) {
\TorrentPier\Legacy\Torrent::change_tor_status($attach_id, $status); \TorrentPier\Legacy\Torrent::change_tor_status($attach_id, $status);
// Log action // Log action
$log_msg = sprintf($lang['TOR_STATUS_LOG_ACTION'], $bb_cfg['tor_icons'][$status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$status] . '</b>', $bb_cfg['tor_icons'][$tor['tor_status']] . ' <b> ' . $lang['TOR_STATUS_NAME'][$tor['tor_status']] . '</b>'); $log_msg = sprintf($lang['TOR_STATUS_LOG_ACTION'], config()->get('tor_icons')[$status] . ' <b> ' . $lang['TOR_STATUS_NAME'][$status] . '</b>', config()->get('tor_icons')[$tor['tor_status']] . ' <b> ' . $lang['TOR_STATUS_NAME'][$tor['tor_status']] . '</b>');
$log_action->mod('mod_topic_change_tor_status', [ $log_action->mod('mod_topic_change_tor_status', [
'forum_id' => $tor['forum_id'], 'forum_id' => $tor['forum_id'],
'topic_id' => $tor['topic_id'], 'topic_id' => $tor['topic_id'],
@ -52,7 +52,7 @@ switch ($mode) {
'log_msg' => $log_msg . '<br/>-------------', 'log_msg' => $log_msg . '<br/>-------------',
]); ]);
} }
$this->response['status'] = $bb_cfg['tor_icons'][$status]; $this->response['status'] = config()->get('tor_icons')[$status];
$this->response['topics'] = explode(',', $topics); $this->response['topics'] = explode(',', $topics);
break; break;
@ -78,16 +78,16 @@ switch ($mode) {
DB()->query("UPDATE " . BB_TOPICS . " SET topic_title = '$topic_title_sql' WHERE topic_id = $topic_id LIMIT 1"); DB()->query("UPDATE " . BB_TOPICS . " SET topic_title = '$topic_title_sql' WHERE topic_id = $topic_id LIMIT 1");
// Update the news cache on the index page // Update the news cache on the index page
$news_forums = array_flip(explode(',', $bb_cfg['latest_news_forum_id'])); $news_forums = array_flip(explode(',', config()->get('latest_news_forum_id')));
if (isset($news_forums[$t_data['forum_id']]) && $bb_cfg['show_latest_news']) { if (isset($news_forums[$t_data['forum_id']]) && config()->get('show_latest_news')) {
$datastore->enqueue([ $datastore->enqueue([
'latest_news' 'latest_news'
]); ]);
$datastore->update('latest_news'); $datastore->update('latest_news');
} }
$net_forums = array_flip(explode(',', $bb_cfg['network_news_forum_id'])); $net_forums = array_flip(explode(',', config()->get('network_news_forum_id')));
if (isset($net_forums[$t_data['forum_id']]) && $bb_cfg['show_network_news']) { if (isset($net_forums[$t_data['forum_id']]) && config()->get('show_network_news')) {
$datastore->enqueue([ $datastore->enqueue([
'network_news' 'network_news'
]); ]);
@ -151,8 +151,8 @@ switch ($mode) {
} else { } else {
$user_reg_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($profiledata['user_reg_ip']); $user_reg_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($profiledata['user_reg_ip']);
$user_last_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($profiledata['user_last_ip']); $user_last_ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($profiledata['user_last_ip']);
$reg_ip = '<a href="' . $bb_cfg['whois_info'] . $user_reg_ip . '" class="gen" target="_blank">' . $user_reg_ip . '</a>'; $reg_ip = '<a href="' . config()->get('whois_info') . $user_reg_ip . '" class="gen" target="_blank">' . $user_reg_ip . '</a>';
$last_ip = '<a href="' . $bb_cfg['whois_info'] . $user_last_ip . '" class="gen" target="_blank">' . $user_last_ip . '</a>'; $last_ip = '<a href="' . config()->get('whois_info') . $user_last_ip . '" class="gen" target="_blank">' . $user_last_ip . '</a>';
} }
$this->response['ip_list_html'] = ' $this->response['ip_list_html'] = '

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $lang, $bb_cfg, $userdata, $wordCensor; global $lang, $userdata;
if (!isset($this->request['type'])) { if (!isset($this->request['type'])) {
$this->ajax_die('empty type'); $this->ajax_die('empty type');
@ -76,11 +76,11 @@ switch ($this->request['type']) {
$message = "[quote=\"" . $quote_username . "\"][qpost=" . $post['post_id'] . "]" . $post['post_text'] . "[/quote]\r"; $message = "[quote=\"" . $quote_username . "\"][qpost=" . $post['post_id'] . "]" . $post['post_text'] . "[/quote]\r";
// hide user passkey // hide user passkey
$message = preg_replace('#(?<=[\?&;]' . $bb_cfg['passkey_key'] . '=)[a-zA-Z0-9]#', 'passkey', $message); $message = preg_replace('#(?<=[\?&;]' . config()->get('passkey_key') . '=)[a-zA-Z0-9]#', 'passkey', $message);
// hide sid // hide sid
$message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message); $message = preg_replace('#(?<=[\?&;]sid=)[a-zA-Z0-9]#', 'sid', $message);
$message = $wordCensor->censorString($message); $message = censor()->censorString($message);
if ($post['post_id'] == $post['topic_first_post_id']) { if ($post['post_id'] == $post['topic_first_post_id']) {
$message = "[quote]" . $post['topic_title'] . "[/quote]\r"; $message = "[quote]" . $post['topic_title'] . "[/quote]\r";
@ -120,10 +120,10 @@ switch ($this->request['type']) {
if (mb_strlen($text) > 2) { if (mb_strlen($text) > 2) {
if ($text != $post['post_text']) { if ($text != $post['post_text']) {
if ($bb_cfg['max_smilies']) { if (config()->get('max_smilies')) {
$count_smilies = substr_count(bbcode2html($text), '<img class="smile" src="' . $bb_cfg['smilies_path']); $count_smilies = substr_count(bbcode2html($text), '<img class="smile" src="' . config()->get('smilies_path'));
if ($count_smilies > $bb_cfg['max_smilies']) { if ($count_smilies > config()->get('max_smilies')) {
$this->ajax_die(sprintf($lang['MAX_SMILIES_PER_POST'], $bb_cfg['max_smilies'])); $this->ajax_die(sprintf($lang['MAX_SMILIES_PER_POST'], config()->get('max_smilies')));
} }
} }
DB()->query("UPDATE " . BB_POSTS_TEXT . " SET post_text = '" . DB()->escape($text) . "' WHERE post_id = $post_id LIMIT 1"); DB()->query("UPDATE " . BB_POSTS_TEXT . " SET post_text = '" . DB()->escape($text) . "' WHERE post_id = $post_id LIMIT 1");
@ -225,7 +225,7 @@ switch ($this->request['type']) {
$sql = "SELECT MAX(p.post_time) AS last_post_time FROM " . BB_POSTS . " p WHERE $where_sql"; $sql = "SELECT MAX(p.post_time) AS last_post_time FROM " . BB_POSTS . " p WHERE $where_sql";
if ($row = DB()->fetch_row($sql) and $row['last_post_time']) { if ($row = DB()->fetch_row($sql) and $row['last_post_time']) {
if ($userdata['user_level'] == USER) { if ($userdata['user_level'] == USER) {
if ((TIMENOW - $row['last_post_time']) < $bb_cfg['flood_interval']) { if ((TIMENOW - $row['last_post_time']) < config()->get('flood_interval')) {
$this->ajax_die($lang['FLOOD_ERROR']); $this->ajax_die($lang['FLOOD_ERROR']);
} }
} }
@ -251,10 +251,10 @@ switch ($this->request['type']) {
} }
} }
if ($bb_cfg['max_smilies']) { if (config()->get('max_smilies')) {
$count_smilies = substr_count(bbcode2html($message), '<img class="smile" src="' . $bb_cfg['smilies_path']); $count_smilies = substr_count(bbcode2html($message), '<img class="smile" src="' . config()->get('smilies_path'));
if ($count_smilies > $bb_cfg['max_smilies']) { if ($count_smilies > config()->get('max_smilies')) {
$this->ajax_die(sprintf($lang['MAX_SMILIES_PER_POST'], $bb_cfg['max_smilies'])); $this->ajax_die(sprintf($lang['MAX_SMILIES_PER_POST'], config()->get('max_smilies')));
} }
} }
@ -272,7 +272,7 @@ switch ($this->request['type']) {
'post_text' => $message 'post_text' => $message
]); ]);
if ($bb_cfg['topic_notify_enabled']) { if (config()->get('topic_notify_enabled')) {
$notify = !empty($this->request['notify']); $notify = !empty($this->request['notify']);
\TorrentPier\Legacy\Post::user_notification('reply', $post, $post['topic_title'], $post['forum_id'], $topic_id, $notify); \TorrentPier\Legacy\Post::user_notification('reply', $post, $post['topic_title'], $post['forum_id'], $topic_id, $notify);
} }

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang; global $lang;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');
@ -24,7 +24,7 @@ switch ($mode) {
case 'create': case 'create':
$map->createSitemap(); $map->createSitemap();
if (is_file(SITEMAP_DIR . '/sitemap.xml')) { if (is_file(SITEMAP_DIR . '/sitemap.xml')) {
$html .= $lang['SITEMAP_CREATED'] . ': <b>' . bb_date(TIMENOW, $bb_cfg['post_date_format']) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>'; $html .= $lang['SITEMAP_CREATED'] . ': <b>' . bb_date(TIMENOW, config()->get('post_date_format')) . '</b> ' . $lang['SITEMAP_AVAILABLE'] . ': <a href="' . make_url('sitemap/sitemap.xml') . '" target="_blank">' . make_url('sitemap/sitemap.xml') . '</a>';
} else { } else {
$html .= $lang['SITEMAP_NOT_CREATED']; $html .= $lang['SITEMAP_NOT_CREATED'];
} }

View file

@ -11,9 +11,9 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang, $userdata; global $lang, $userdata;
if (!$bb_cfg['tor_thank']) { if (!config()->get('tor_thank')) {
$this->ajax_die($lang['MODULE_OFF']); $this->ajax_die($lang['MODULE_OFF']);
} }
@ -49,12 +49,12 @@ switch ($mode) {
// Limit voters per topic // Limit voters per topic
$thanks_count = DB()->fetch_row('SELECT COUNT(*) as thx FROM ' . BB_THX . " WHERE topic_id = $topic_id")['thx']; $thanks_count = DB()->fetch_row('SELECT COUNT(*) as thx FROM ' . BB_THX . " WHERE topic_id = $topic_id")['thx'];
if ($thanks_count > (int)$bb_cfg['tor_thank_limit_per_topic']) { if ($thanks_count > (int)config()->get('tor_thank_limit_per_topic')) {
DB()->query('DELETE FROM ' . BB_THX . " WHERE topic_id = $topic_id ORDER BY time ASC LIMIT 1"); DB()->query('DELETE FROM ' . BB_THX . " WHERE topic_id = $topic_id ORDER BY time ASC LIMIT 1");
} }
break; break;
case 'get': case 'get':
if (IS_GUEST && !$bb_cfg['tor_thanks_list_guests']) { if (IS_GUEST && !config()->get('tor_thanks_list_guests')) {
$this->ajax_die($lang['NEED_TO_LOGIN_FIRST']); $this->ajax_die($lang['NEED_TO_LOGIN_FIRST']);
} }

View file

@ -11,7 +11,7 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $lang, $userdata; global $lang, $userdata;
if (!$mode = (string)$this->request['mode']) { if (!$mode = (string)$this->request['mode']) {
$this->ajax_die('invalid mode (empty)'); $this->ajax_die('invalid mode (empty)');

View file

@ -11,11 +11,11 @@ if (!defined('IN_AJAX')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $user, $lang, $bb_cfg; global $user, $lang;
$post_id = isset($this->request['post_id']) ? (int)$this->request['post_id'] : null; $post_id = isset($this->request['post_id']) ? (int)$this->request['post_id'] : null;
$topic_id = isset($this->request['topic_id']) ? (int)$this->request['topic_id'] : null; $topic_id = isset($this->request['topic_id']) ? (int)$this->request['topic_id'] : null;
$return_text = $bb_cfg['show_post_bbcode_button']['enabled'] && isset($this->request['return_text']) && (bool)$this->request['return_text']; $return_text = config()->get('show_post_bbcode_button.enabled') && isset($this->request['return_text']) && (bool)$this->request['return_text'];
if (is_null($post_id)) { if (is_null($post_id)) {
$post_id = DB()->fetch_row("SELECT topic_first_post_id FROM " . BB_TOPICS . " WHERE topic_id = $topic_id", 'topic_first_post_id'); $post_id = DB()->fetch_row("SELECT topic_first_post_id FROM " . BB_TOPICS . " WHERE topic_id = $topic_id", 'topic_first_post_id');

View file

@ -25,11 +25,11 @@ if (defined('ATTACH_INSTALL')) {
*/ */
function attach_mod_get_lang($language_file) function attach_mod_get_lang($language_file)
{ {
global $attach_config, $bb_cfg; global $attach_config;
$file = LANG_ROOT_DIR . '/' . $bb_cfg['default_lang'] . '/' . $language_file . '.php'; $file = LANG_ROOT_DIR . '/' . config()->get('default_lang') . '/' . $language_file . '.php';
if (file_exists($file)) { if (file_exists($file)) {
return $bb_cfg['default_lang']; return config()->get('default_lang');
} }
$file = LANG_ROOT_DIR . '/' . $attach_config['board_lang'] . '/' . $language_file . '.php'; $file = LANG_ROOT_DIR . '/' . $attach_config['board_lang'] . '/' . $language_file . '.php';
@ -45,8 +45,6 @@ function attach_mod_get_lang($language_file)
*/ */
function get_config() function get_config()
{ {
global $bb_cfg;
$attach_config = []; $attach_config = [];
$sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG; $sql = 'SELECT * FROM ' . BB_ATTACH_CONFIG;
@ -60,7 +58,7 @@ function get_config()
} }
// We assign the original default board language here, because it gets overwritten later with the users default language // We assign the original default board language here, because it gets overwritten later with the users default language
$attach_config['board_lang'] = trim($bb_cfg['default_lang']); $attach_config['board_lang'] = trim(config()->get('default_lang'));
return $attach_config; return $attach_config;
} }

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $t_data, $poster_id, $is_auth, $dl_link_css, $dl_status_css, $lang, $images; global $t_data, $poster_id, $is_auth, $dl_link_css, $dl_status_css, $lang, $images;
$tor_status_by_for_all = true; $tor_status_by_for_all = true;
$change_peers_bgr_over = true; $change_peers_bgr_over = true;
@ -40,7 +40,7 @@ $template->assign_vars([
]); ]);
// Define show peers mode (count only || user names with complete % || full details) // Define show peers mode (count only || user names with complete % || full details)
$cfg_sp_mode = $bb_cfg['bt_show_peers_mode']; $cfg_sp_mode = config()->get('bt_show_peers_mode');
$get_sp_mode = $_GET['spmode'] ?? ''; $get_sp_mode = $_GET['spmode'] ?? '';
$s_mode = 'count'; $s_mode = 'count';
@ -51,7 +51,7 @@ if ($cfg_sp_mode == SHOW_PEERS_NAMES) {
$s_mode = 'full'; $s_mode = 'full';
} }
if ($bb_cfg['bt_allow_spmode_change']) { if (config()->get('bt_allow_spmode_change')) {
if ($get_sp_mode == 'names') { if ($get_sp_mode == 'names') {
$s_mode = 'names'; $s_mode = 'names';
} elseif ($get_sp_mode == 'full') { } elseif ($get_sp_mode == 'full') {
@ -68,7 +68,7 @@ $tor_file_size = humn_size($attachments['_' . $post_id][$i]['filesize']);
$tor_file_time = bb_date($attachments['_' . $post_id][$i]['filetime']); $tor_file_time = bb_date($attachments['_' . $post_id][$i]['filetime']);
$tor_reged = (bool)$tracker_status; $tor_reged = (bool)$tracker_status;
$show_peers = (bool)$bb_cfg['bt_show_peers']; $show_peers = (bool)config()->get('bt_show_peers');
$locked = ($t_data['forum_status'] == FORUM_LOCKED || $t_data['topic_status'] == TOPIC_LOCKED); $locked = ($t_data['forum_status'] == FORUM_LOCKED || $t_data['topic_status'] == TOPIC_LOCKED);
$tor_auth = ($bt_user_id != GUEST_UID && (($bt_user_id == $poster_id && !$locked) || $is_auth['auth_mod'])); $tor_auth = ($bt_user_id != GUEST_UID && (($bt_user_id == $poster_id && !$locked) || $is_auth['auth_mod']));
@ -88,10 +88,10 @@ if ($tor_auth_reg || $tor_auth_del) {
$tracker_link = ($tor_reged) ? $unreg_tor_url : $reg_tor_url; $tracker_link = ($tor_reged) ? $unreg_tor_url : $reg_tor_url;
} }
if ($bb_cfg['tracker']['use_old_torrent_name_format']) { if (config()->get('tracker.use_old_torrent_name_format')) {
$display_name = '[' . $bb_cfg['server_name'] . '].t' . $bt_topic_id . '.' . TORRENT_EXT; $display_name = '[' . config()->get('server_name') . '].t' . $bt_topic_id . '.' . TORRENT_EXT;
} else { } else {
$display_name = $t_data['topic_title'] . ' [' . $bb_cfg['server_name'] . '-' . $bt_topic_id . ']' . '.' . TORRENT_EXT; $display_name = $t_data['topic_title'] . ' [' . config()->get('server_name') . '-' . $bt_topic_id . ']' . '.' . TORRENT_EXT;
} }
if (!$tor_reged) { if (!$tor_reged) {
@ -152,8 +152,8 @@ if ($tor_reged && $tor_info) {
$tor_magnet = create_magnet($tor_info['info_hash'], $tor_info['info_hash_v2'], $user_passkey, html_ent_decode($t_data['topic_title']), $tor_size); $tor_magnet = create_magnet($tor_info['info_hash'], $tor_info['info_hash_v2'], $user_passkey, html_ent_decode($t_data['topic_title']), $tor_size);
// ratio limits // ratio limits
$min_ratio_dl = $bb_cfg['bt_min_ratio_allow_dl_tor']; $min_ratio_dl = config()->get('bt_min_ratio_allow_dl_tor');
$min_ratio_warn = $bb_cfg['bt_min_ratio_warning']; $min_ratio_warn = config()->get('bt_min_ratio_warning');
$dl_allowed = true; $dl_allowed = true;
$user_ratio = 0; $user_ratio = 0;
@ -182,7 +182,7 @@ if ($tor_reged && $tor_info) {
if ((isset($user_ratio, $min_ratio_warn) && $user_ratio < $min_ratio_warn && TR_RATING_LIMITS) || ($bt_userdata['u_down_total'] < MIN_DL_FOR_RATIO)) { if ((isset($user_ratio, $min_ratio_warn) && $user_ratio < $min_ratio_warn && TR_RATING_LIMITS) || ($bt_userdata['u_down_total'] < MIN_DL_FOR_RATIO)) {
$template->assign_vars([ $template->assign_vars([
'SHOW_RATIO_WARN' => true, 'SHOW_RATIO_WARN' => true,
'RATIO_WARN_MSG' => sprintf($lang['BT_RATIO_WARNING_MSG'], $min_ratio_dl, $bb_cfg['ratio_url_help']), 'RATIO_WARN_MSG' => sprintf($lang['BT_RATIO_WARNING_MSG'], $min_ratio_dl, config()->get('ratio_url_help')),
]); ]);
} }
} }
@ -202,12 +202,12 @@ if ($tor_reged && $tor_info) {
'TOR_TYPE' => is_gold($tor_type), 'TOR_TYPE' => is_gold($tor_type),
// torrent status mod // torrent status mod
'TOR_FROZEN' => !IS_AM ? (isset($bb_cfg['tor_frozen'][$tor_info['tor_status']]) && !(isset($bb_cfg['tor_frozen_author_download'][$tor_info['tor_status']]) && $userdata['user_id'] == $tor_info['poster_id'])) ? true : '' : '', 'TOR_FROZEN' => !IS_AM ? (isset(config()->get('tor_frozen')[$tor_info['tor_status']]) && !(isset(config()->get('tor_frozen_author_download')[$tor_info['tor_status']]) && $userdata['user_id'] == $tor_info['poster_id'])) ? true : '' : '',
'TOR_STATUS_TEXT' => $lang['TOR_STATUS_NAME'][$tor_info['tor_status']], 'TOR_STATUS_TEXT' => $lang['TOR_STATUS_NAME'][$tor_info['tor_status']],
'TOR_STATUS_ICON' => $bb_cfg['tor_icons'][$tor_info['tor_status']], 'TOR_STATUS_ICON' => config()->get('tor_icons')[$tor_info['tor_status']],
'TOR_STATUS_BY' => ($tor_info['checked_user_id'] && ($is_auth['auth_mod'] || $tor_status_by_for_all)) ? ('<span title="' . bb_date($tor_info['checked_time']) . '"> &middot; ' . profile_url($tor_info) . ' &middot; <i>' . delta_time($tor_info['checked_time']) . $lang['TOR_BACK'] . '</i></span>') : '', 'TOR_STATUS_BY' => ($tor_info['checked_user_id'] && ($is_auth['auth_mod'] || $tor_status_by_for_all)) ? ('<span title="' . bb_date($tor_info['checked_time']) . '"> &middot; ' . profile_url($tor_info) . ' &middot; <i>' . delta_time($tor_info['checked_time']) . $lang['TOR_BACK'] . '</i></span>') : '',
'TOR_STATUS_SELECT' => build_select('sel_status', array_flip($lang['TOR_STATUS_NAME']), TOR_APPROVED), 'TOR_STATUS_SELECT' => build_select('sel_status', array_flip($lang['TOR_STATUS_NAME']), TOR_APPROVED),
'TOR_STATUS_REPLY' => $bb_cfg['tor_comment'] && !IS_GUEST && in_array($tor_info['tor_status'], $bb_cfg['tor_reply']) && $userdata['user_id'] == $tor_info['poster_id'] && $t_data['topic_status'] != TOPIC_LOCKED, 'TOR_STATUS_REPLY' => config()->get('tor_comment') && !IS_GUEST && in_array($tor_info['tor_status'], config()->get('tor_reply')) && $userdata['user_id'] == $tor_info['poster_id'] && $t_data['topic_status'] != TOPIC_LOCKED,
//end torrent status mod //end torrent status mod
'S_UPLOAD_IMAGE' => $upload_image, 'S_UPLOAD_IMAGE' => $upload_image,
@ -227,7 +227,7 @@ if ($tor_reged && $tor_info) {
]); ]);
// TorrServer integration // TorrServer integration
if ($bb_cfg['torr_server']['enabled'] && (!IS_GUEST || !$bb_cfg['torr_server']['disable_for_guest']) && (new \TorrentPier\TorrServerAPI())->getM3UPath($attach_id)) { if (config()->get('torr_server.enabled') && (!IS_GUEST || !config()->get('torr_server.disable_for_guest')) && (new \TorrentPier\TorrServerAPI())->getM3UPath($attach_id)) {
$template->assign_block_vars('postrow.attach.tor_reged.tor_server', [ $template->assign_block_vars('postrow.attach.tor_reged.tor_server', [
'TORR_SERVER_M3U_LINK' => PLAYBACK_M3U_URL . $bt_topic_id, 'TORR_SERVER_M3U_LINK' => PLAYBACK_M3U_URL . $bt_topic_id,
'TORR_SERVER_M3U_ICON' => $images['icon_tor_m3u_icon'], 'TORR_SERVER_M3U_ICON' => $images['icon_tor_m3u_icon'],
@ -239,7 +239,7 @@ if ($tor_reged && $tor_info) {
} }
} }
if ($bb_cfg['show_tor_info_in_dl_list']) { if (config()->get('show_tor_info_in_dl_list')) {
$template->assign_vars([ $template->assign_vars([
'SHOW_DL_LIST' => true, 'SHOW_DL_LIST' => true,
'SHOW_DL_LIST_TOR_INFO' => true, 'SHOW_DL_LIST_TOR_INFO' => true,
@ -470,11 +470,13 @@ if ($tor_reged && $tor_info) {
} }
$peerCountry = $lang['HIDDEN_USER']; $peerCountry = $lang['HIDDEN_USER'];
if ($bb_cfg['ip2country_settings']['enabled']) { if (config()->get('ip2country_settings.enabled')) {
if (IS_AM || $peer['user_id'] == $userdata['user_id'] || !bf($peer['user_opt'], 'user_opt', 'user_hide_peer_country')) { if (IS_AM || $peer['user_id'] == $userdata['user_id'] || !bf($peer['user_opt'], 'user_opt', 'user_hide_peer_country')) {
if ($infoByIP = infoByIP((!empty($peer['ipv6']) ? $peer['ipv6'] : $peer['ip']), $peer['port'])) { if ($infoByIP = infoByIP((!empty($peer['ipv6']) ? $peer['ipv6'] : $peer['ip']), $peer['port'])) {
if (!empty($infoByIP['countryCode'])) { if (!empty($infoByIP['countryCode'])) {
$peerCountry = render_flag($infoByIP['countryCode'], false); $peerCountry = render_flag($infoByIP['countryCode'], false);
} else {
$peerCountry = $lang['NOT_AVAILABLE'];
} }
} }
} }
@ -500,7 +502,7 @@ if ($tor_reged && $tor_info) {
if ($ip) { if ($ip) {
$template->assign_block_vars("$x_full.$x_row.ip", [ $template->assign_block_vars("$x_full.$x_row.ip", [
'U_WHOIS_IP' => $bb_cfg['whois_info'] . $ip, 'U_WHOIS_IP' => config()->get('whois_info') . $ip,
'IP' => $ip 'IP' => $ip
]); ]);
} }
@ -563,7 +565,7 @@ if ($tor_reged && $tor_info) {
} }
} }
if ($bb_cfg['bt_allow_spmode_change'] && $s_mode != 'full') { if (config()->get('bt_allow_spmode_change') && $s_mode != 'full') {
$template->assign_vars([ $template->assign_vars([
'PEERS_FULL_LINK' => true, 'PEERS_FULL_LINK' => true,
'SPMODE_FULL_HREF' => TOPIC_URL . "$bt_topic_id&amp;spmode=full#seeders" 'SPMODE_FULL_HREF' => TOPIC_URL . "$bt_topic_id&amp;spmode=full#seeders"
@ -571,14 +573,14 @@ if ($bb_cfg['bt_allow_spmode_change'] && $s_mode != 'full') {
} }
$template->assign_vars([ $template->assign_vars([
'SHOW_DL_LIST_LINK' => (($bb_cfg['bt_show_dl_list'] || $bb_cfg['allow_dl_list_names_mode']) && $t_data['topic_dl_type'] == TOPIC_DL_TYPE_DL), 'SHOW_DL_LIST_LINK' => ((config()->get('bt_show_dl_list') || config()->get('allow_dl_list_names_mode')) && $t_data['topic_dl_type'] == TOPIC_DL_TYPE_DL),
'SHOW_TOR_ACT' => ($tor_reged && $show_peers && (!isset($bb_cfg['tor_no_tor_act'][$tor_info['tor_status']]) || IS_AM)), 'SHOW_TOR_ACT' => ($tor_reged && $show_peers && (!isset(config()->get('tor_no_tor_act')[$tor_info['tor_status']]) || IS_AM)),
'S_MODE_COUNT' => ($s_mode == 'count'), 'S_MODE_COUNT' => ($s_mode == 'count'),
'S_MODE_NAMES' => ($s_mode == 'names'), 'S_MODE_NAMES' => ($s_mode == 'names'),
'S_MODE_FULL' => ($s_mode == 'full'), 'S_MODE_FULL' => ($s_mode == 'full'),
'PEER_EXIST' => ($seeders || $leechers || defined('SEEDER_EXIST') || defined('LEECHER_EXIST')), 'PEER_EXIST' => ($seeders || $leechers || defined('SEEDER_EXIST') || defined('LEECHER_EXIST')),
'SEED_EXIST' => ($seeders || defined('SEEDER_EXIST')), 'SEED_EXIST' => ($seeders || defined('SEEDER_EXIST')),
'LEECH_EXIST' => ($leechers || defined('LEECHER_EXIST')), 'LEECH_EXIST' => ($leechers || defined('LEECHER_EXIST')),
'TOR_HELP_LINKS' => $bb_cfg['tor_help_links'], 'TOR_HELP_LINKS' => config()->get('tor_help_links'),
'CALL_SEED' => (!IS_GUEST && $bb_cfg['callseed'] && $tor_reged && !isset($bb_cfg['tor_no_tor_act'][$tor_info['tor_status']]) && $seed_count < 3 && $tor_info['call_seed_time'] < (TIMENOW - 86400)), 'CALL_SEED' => (!IS_GUEST && config()->get('callseed') && $tor_reged && !isset(config()->get('tor_no_tor_act')[$tor_info['tor_status']]) && $seed_count < 3 && $tor_info['call_seed_time'] < (TIMENOW - 86400)),
]); ]);

View file

@ -16,7 +16,7 @@
*/ */
function delete_attachment($post_id_array = 0, $attach_id_array = 0, $page = 0, $user_id = 0) function delete_attachment($post_id_array = 0, $attach_id_array = 0, $page = 0, $user_id = 0)
{ {
global $lang, $bb_cfg; global $lang;
// Generate Array, if it's not an array // Generate Array, if it's not an array
if ($post_id_array === 0 && $attach_id_array === 0 && $page === 0) { if ($post_id_array === 0 && $attach_id_array === 0 && $page === 0) {
@ -215,7 +215,7 @@ function delete_attachment($post_id_array = 0, $attach_id_array = 0, $page = 0,
} }
// TorrServer integration // TorrServer integration
if ($bb_cfg['torr_server']['enabled']) { if (config()->get('torr_server.enabled')) {
$torrServer = new \TorrentPier\TorrServerAPI(); $torrServer = new \TorrentPier\TorrServerAPI();
$torrServer->removeM3U($attachments[$j]['attach_id']); $torrServer->removeM3U($attachments[$j]['attach_id']);
} }

View file

@ -18,8 +18,8 @@ $reserved_port = env('TP_PORT', 80);
$bb_cfg = []; $bb_cfg = [];
// Version info // Version info
$bb_cfg['tp_version'] = 'v2.4.6'; $bb_cfg['tp_version'] = 'v2.8.3';
$bb_cfg['tp_release_date'] = '19-06-2025'; $bb_cfg['tp_release_date'] = '03-07-2025';
$bb_cfg['tp_release_codename'] = 'Cattle'; $bb_cfg['tp_release_codename'] = 'Cattle';
// Increase version number after changing JS or CSS // Increase version number after changing JS or CSS
@ -60,27 +60,22 @@ $bb_cfg['cache'] = [
'host' => '127.0.0.1', 'host' => '127.0.0.1',
'port' => 11211, 'port' => 11211,
], ],
'redis' => [ // Available cache types: file, sqlite, memory, memcached (file by default)
'host' => '127.0.0.1',
'port' => 6379,
'pconnect' => !PHP_ZTS, // Redis pconnect supported only for non-thread safe compilations of PHP
],
// Available cache types: filecache, memcached, sqlite, redis, apcu (filecache by default)
'engines' => [ 'engines' => [
'bb_cache' => ['filecache'], 'bb_cache' => ['file'],
'bb_config' => ['filecache'], 'bb_config' => ['file'],
'tr_cache' => ['filecache'], 'tr_cache' => ['file'],
'session_cache' => ['filecache'], 'session_cache' => ['file'],
'bb_cap_sid' => ['filecache'], 'bb_cap_sid' => ['file'],
'bb_login_err' => ['filecache'], 'bb_login_err' => ['file'],
'bb_poll_data' => ['filecache'], 'bb_poll_data' => ['file'],
'bb_ip2countries' => ['filecache'], 'bb_ip2countries' => ['file'],
], ],
]; ];
// Datastore // Datastore
// Available datastore types: filecache, memcached, sqlite, redis, apcu (filecache by default) // Available datastore types: file, sqlite, memory, memcache (file by default)
$bb_cfg['datastore_type'] = 'filecache'; $bb_cfg['datastore_type'] = 'file';
// Server // Server
$bb_cfg['server_name'] = $domain_name = !empty($_SERVER['SERVER_NAME']) ? idn_to_utf8($_SERVER['SERVER_NAME']) : $reserved_name; $bb_cfg['server_name'] = $domain_name = !empty($_SERVER['SERVER_NAME']) ? idn_to_utf8($_SERVER['SERVER_NAME']) : $reserved_name;
@ -209,6 +204,7 @@ $bb_cfg['lang'] = [
'ar' => [ 'ar' => [
'name' => 'Arabic', 'name' => 'Arabic',
'locale' => 'ar_SA.UTF-8', 'locale' => 'ar_SA.UTF-8',
'rtl' => true,
], ],
'hy' => [ 'hy' => [
'name' => 'Armenian', 'name' => 'Armenian',
@ -285,6 +281,7 @@ $bb_cfg['lang'] = [
'he' => [ 'he' => [
'name' => 'Hebrew', 'name' => 'Hebrew',
'locale' => 'he_IL.UTF-8', 'locale' => 'he_IL.UTF-8',
'rtl' => true,
], ],
'hi' => [ 'hi' => [
'name' => 'Hindi', 'name' => 'Hindi',

View file

@ -83,6 +83,9 @@ define('CRON_RUNNING', TRIGGERS_DIR . '/cron_running');
define('GZIP_OUTPUT_ALLOWED', extension_loaded('zlib') && !ini_get('zlib.output_compression')); define('GZIP_OUTPUT_ALLOWED', extension_loaded('zlib') && !ini_get('zlib.output_compression'));
define('UA_GZIP_SUPPORTED', isset($_SERVER['HTTP_ACCEPT_ENCODING']) && str_contains($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')); define('UA_GZIP_SUPPORTED', isset($_SERVER['HTTP_ACCEPT_ENCODING']) && str_contains($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'));
// Migrations table
define('BB_MIGRATIONS', 'bb_migrations');
// Tracker shared constants // Tracker shared constants
define('BB_BT_TORRENTS', 'bb_bt_torrents'); define('BB_BT_TORRENTS', 'bb_bt_torrents');
define('BB_BT_TRACKER', 'bb_bt_tracker'); define('BB_BT_TRACKER', 'bb_bt_tracker');

View file

@ -123,7 +123,7 @@ function prepare_message($message)
// Either in a window or inline // Either in a window or inline
function generate_smilies($mode) function generate_smilies($mode)
{ {
global $bb_cfg, $template, $lang, $user, $datastore; global $template, $lang, $user, $datastore;
$inline_columns = 4; $inline_columns = 4;
$inline_rows = 7; $inline_rows = 7;
@ -160,7 +160,7 @@ function generate_smilies($mode)
$template->assign_block_vars('smilies_row.smilies_col', [ $template->assign_block_vars('smilies_row.smilies_col', [
'SMILEY_CODE' => $data['code'], 'SMILEY_CODE' => $data['code'],
'SMILEY_IMG' => $bb_cfg['smilies_path'] . '/' . $smile_url, 'SMILEY_IMG' => config()->get('smilies_path') . '/' . $smile_url,
'SMILEY_DESC' => $data['emoticon'], 'SMILEY_DESC' => $data['emoticon'],
]); ]);
@ -341,11 +341,9 @@ function strip_bbcode($message, $stripquotes = true, $fast_and_dirty = false, $s
function extract_search_words($text) function extract_search_words($text)
{ {
global $bb_cfg; $max_words_count = config()->get('max_search_words_per_post');
$min_word_len = max(2, config()->get('search_min_word_len') - 1);
$max_words_count = $bb_cfg['max_search_words_per_post']; $max_word_len = config()->get('search_max_word_len');
$min_word_len = max(2, $bb_cfg['search_min_word_len'] - 1);
$max_word_len = $bb_cfg['search_max_word_len'];
$text = ' ' . str_compact(strip_tags(mb_strtolower($text))) . ' '; $text = ' ' . str_compact(strip_tags(mb_strtolower($text))) . ' ';
$text = str_replace(['&#91;', '&#93;'], ['[', ']'], $text); $text = str_replace(['&#91;', '&#93;'], ['[', ']'], $text);
@ -382,12 +380,10 @@ function extract_search_words($text)
function add_search_words($post_id, $post_message, $topic_title = '', $only_return_words = false) function add_search_words($post_id, $post_message, $topic_title = '', $only_return_words = false)
{ {
global $bb_cfg;
$text = $topic_title . ' ' . $post_message; $text = $topic_title . ' ' . $post_message;
$words = ($text) ? extract_search_words($text) : []; $words = ($text) ? extract_search_words($text) : [];
if ($only_return_words || $bb_cfg['search_engine_type'] == 'sphinx') { if ($only_return_words || config()->get('search_engine_type') == 'sphinx') {
return implode("\n", $words); return implode("\n", $words);
} }
@ -405,12 +401,12 @@ function add_search_words($post_id, $post_message, $topic_title = '', $only_retu
function bbcode2html($text) function bbcode2html($text)
{ {
global $bbcode, $wordCensor; global $bbcode;
if (!isset($bbcode)) { if (!isset($bbcode)) {
$bbcode = new TorrentPier\Legacy\BBCode(); $bbcode = new TorrentPier\Legacy\BBCode();
} }
$text = $wordCensor->censorString($text); $text = censor()->censorString($text);
return $bbcode->bbcode2html($text); return $bbcode->bbcode2html($text);
} }
@ -425,22 +421,19 @@ function get_words_rate($text)
function hide_passkey($str) function hide_passkey($str)
{ {
global $bb_cfg; return preg_replace("#\?{config()->get('passkey_key')}=[a-zA-Z0-9]{" . BT_AUTH_KEY_LENGTH . "}#", "?{config()->get('passkey_key')}=passkey", $str);
return preg_replace("#\?{$bb_cfg['passkey_key']}=[a-zA-Z0-9]{" . BT_AUTH_KEY_LENGTH . "}#", "?{$bb_cfg['passkey_key']}=passkey", $str);
} }
function get_parsed_post($postrow, $mode = 'full', $return_chars = 600) function get_parsed_post($postrow, $mode = 'full', $return_chars = 600)
{ {
global $bb_cfg; if (config()->get('use_posts_cache') && !empty($postrow['post_html'])) {
if ($bb_cfg['use_posts_cache'] && !empty($postrow['post_html'])) {
return $postrow['post_html']; return $postrow['post_html'];
} }
$message = bbcode2html($postrow['post_text']); $message = bbcode2html($postrow['post_text']);
// Posts cache // Posts cache
if ($bb_cfg['use_posts_cache']) { if (config()->get('use_posts_cache')) {
DB()->shutdown['post_html'][] = [ DB()->shutdown['post_html'][] = [
'post_id' => (int)$postrow['post_id'], 'post_id' => (int)$postrow['post_id'],
'post_html' => (string)$message 'post_html' => (string)$message

View file

@ -13,11 +13,10 @@ if (!defined('BB_ROOT')) {
define('IN_CRON', true); define('IN_CRON', true);
// Set SESSION vars // Set SESSION vars (optimized for InnoDB)
DB()->query(" DB()->query("
SET SESSION SET SESSION
myisam_sort_buffer_size = 16*1024*1024 bulk_insert_buffer_size = 8*1024*1024
, bulk_insert_buffer_size = 8*1024*1024
, join_buffer_size = 4*1024*1024 , join_buffer_size = 4*1024*1024
, read_buffer_size = 4*1024*1024 , read_buffer_size = 4*1024*1024
, read_rnd_buffer_size = 8*1024*1024 , read_rnd_buffer_size = 8*1024*1024
@ -29,8 +28,7 @@ DB()->query("
// Restore vars at shutdown // Restore vars at shutdown
DB()->add_shutdown_query(" DB()->add_shutdown_query("
SET SESSION SET SESSION
myisam_sort_buffer_size = DEFAULT bulk_insert_buffer_size = DEFAULT
, bulk_insert_buffer_size = DEFAULT
, join_buffer_size = DEFAULT , join_buffer_size = DEFAULT
, read_buffer_size = DEFAULT , read_buffer_size = DEFAULT
, read_rnd_buffer_size = DEFAULT , read_rnd_buffer_size = DEFAULT

View file

@ -26,9 +26,9 @@ $posts_without_attach = $topics_without_attach = [];
DB()->query(" DB()->query("
CREATE TEMPORARY TABLE $tmp_attach_tbl ( CREATE TEMPORARY TABLE $tmp_attach_tbl (
physical_filename VARCHAR(255) NOT NULL default '', physical_filename VARCHAR(255) NOT NULL default '' COLLATE utf8mb4_unicode_ci,
KEY physical_filename (physical_filename(20)) KEY physical_filename (physical_filename(20))
) ENGINE = MyISAM DEFAULT CHARSET = utf8 ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci
"); ");
DB()->add_shutdown_query("DROP TEMPORARY TABLE IF EXISTS $tmp_attach_tbl"); DB()->add_shutdown_query("DROP TEMPORARY TABLE IF EXISTS $tmp_attach_tbl");
@ -144,7 +144,7 @@ if ($check_attachments) {
$orphan_db_attach[] = $row['attach_id']; $orphan_db_attach[] = $row['attach_id'];
} }
// Delete all orphan attachments // Delete all orphan attachments
if ($bb_cfg['torr_server']['enabled'] && $fix_errors) { if (config()->get('torr_server.enabled') && $fix_errors) {
foreach ($orphan_db_attach as $attach_id) { foreach ($orphan_db_attach as $attach_id) {
// TorrServer integration // TorrServer integration
$torrServer = new \TorrentPier\TorrServerAPI(); $torrServer = new \TorrentPier\TorrServerAPI();

View file

@ -17,7 +17,7 @@ if (!defined('BB_ROOT')) {
\TorrentPier\Legacy\Admin\Common::sync_all_forums(); \TorrentPier\Legacy\Admin\Common::sync_all_forums();
// Cleaning bb_poll_users // Cleaning bb_poll_users
if ($poll_max_days = (int)$bb_cfg['poll_max_days']) { if ($poll_max_days = (int)config()->get('poll_max_days')) {
$per_cycle = 20000; $per_cycle = 20000;
$row = DB()->fetch_row("SELECT MIN(topic_id) AS start_id, MAX(topic_id) AS finish_id FROM " . BB_POLL_USERS); $row = DB()->fetch_row("SELECT MIN(topic_id) AS start_id, MAX(topic_id) AS finish_id FROM " . BB_POLL_USERS);
$start_id = (int)$row['start_id']; $start_id = (int)$row['start_id'];
@ -45,12 +45,12 @@ if ($poll_max_days = (int)$bb_cfg['poll_max_days']) {
DB()->query("UPDATE " . BB_USERS . " SET user_newpasswd = '' WHERE user_lastvisit < " . (TIMENOW - 7 * 86400)); DB()->query("UPDATE " . BB_USERS . " SET user_newpasswd = '' WHERE user_lastvisit < " . (TIMENOW - 7 * 86400));
// Cleaning post cache // Cleaning post cache
if ($posts_days = (int)$bb_cfg['posts_cache_days_keep']) { if ($posts_days = (int)config()->get('posts_cache_days_keep')) {
DB()->query("DELETE FROM " . BB_POSTS_HTML . " WHERE post_html_time < DATE_SUB(NOW(), INTERVAL $posts_days DAY)"); DB()->query("DELETE FROM " . BB_POSTS_HTML . " WHERE post_html_time < DATE_SUB(NOW(), INTERVAL $posts_days DAY)");
} }
// Autofill announcer url // Autofill announcer url
if (empty($bb_cfg['bt_announce_url']) || ($bb_cfg['bt_announce_url'] === 'https://localhost/bt/announce.php')) { if (empty(config()->get('bt_announce_url')) || (config()->get('bt_announce_url') === 'https://localhost/bt/announce.php')) {
bb_update_config(['bt_announce_url' => FULL_URL . 'bt/announce.php']); bb_update_config(['bt_announce_url' => FULL_URL . 'bt/announce.php']);
} }

View file

@ -13,10 +13,10 @@ if (!defined('BB_ROOT')) {
// Delete staled dl-status records // Delete staled dl-status records
$keeping_dlstat = [ $keeping_dlstat = [
DL_STATUS_WILL => (int)$bb_cfg['dl_will_days_keep'], DL_STATUS_WILL => (int)config()->get('dl_will_days_keep'),
DL_STATUS_DOWN => (int)$bb_cfg['dl_down_days_keep'], DL_STATUS_DOWN => (int)config()->get('dl_down_days_keep'),
DL_STATUS_COMPLETE => (int)$bb_cfg['dl_complete_days_keep'], DL_STATUS_COMPLETE => (int)config()->get('dl_complete_days_keep'),
DL_STATUS_CANCEL => (int)$bb_cfg['dl_cancel_days_keep'] DL_STATUS_CANCEL => (int)config()->get('dl_cancel_days_keep')
]; ];
$delete_dlstat_sql = []; $delete_dlstat_sql = [];
@ -51,7 +51,7 @@ DB()->query("
"); ");
// Tor-Stats cleanup // Tor-Stats cleanup
if ($torstat_days_keep = (int)$bb_cfg['torstat_days_keep']) { if ($torstat_days_keep = (int)config()->get('torstat_days_keep')) {
DB()->query("DELETE QUICK FROM " . BB_BT_TORSTAT . " WHERE last_modified_torstat < DATE_SUB(NOW(), INTERVAL $torstat_days_keep DAY)"); DB()->query("DELETE QUICK FROM " . BB_BT_TORSTAT . " WHERE last_modified_torstat < DATE_SUB(NOW(), INTERVAL $torstat_days_keep DAY)");
} }

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
$log_days_keep = (int)$bb_cfg['log_days_keep']; $log_days_keep = (int)config()->get('log_days_keep');
if ($log_days_keep !== 0) { if ($log_days_keep !== 0) {
DB()->query("DELETE FROM " . BB_LOG . " WHERE log_time < " . (TIMENOW - 86400 * $log_days_keep)); DB()->query("DELETE FROM " . BB_LOG . " WHERE log_time < " . (TIMENOW - 86400 * $log_days_keep));

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
$pm_days_keep = (int)$bb_cfg['pm_days_keep']; $pm_days_keep = (int)config()->get('pm_days_keep');
if ($pm_days_keep !== 0) { if ($pm_days_keep !== 0) {
$per_cycle = 20000; $per_cycle = 20000;

View file

@ -1,44 +0,0 @@
<?php
/**
* TorrentPier Bull-powered BitTorrent tracker engine
*
* @copyright Copyright (c) 2005-2025 TorrentPier (https://torrentpier.com)
* @link https://github.com/torrentpier/torrentpier for the canonical source repository
* @license https://github.com/torrentpier/torrentpier/blob/master/LICENSE MIT License
*/
if (!defined('BB_ROOT')) {
die(basename(__FILE__));
}
set_time_limit(600);
global $cron_runtime_log;
$dump_path = BB_ROOT . 'install/sql/mysql.sql';
if (!IN_DEMO_MODE || !is_file($dump_path) || !is_readable($dump_path)) {
return;
}
// Clean cache & datastore
$datastore->clean();
foreach ($bb_cfg['cache']['engines'] as $cache_name => $cache_val) {
CACHE($cache_name)->rm();
}
// Drop tables & Insert sql dump
$temp_line = '';
foreach (file($dump_path) as $line) {
if (str_starts_with($line, '--') || $line == '') {
continue;
}
$temp_line .= $line;
if (str_ends_with(trim($line), ';')) {
if (!DB()->query($temp_line)) {
$cron_runtime_log[] = date('Y-m-d H:i:s') . " -- Error performing query: " . $temp_line . " | " . DB()->sql_error()['message'];
}
$temp_line = '';
}
}

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
if ($bb_cfg['prune_enable']) { if (config()->get('prune_enable')) {
$sql = "SELECT forum_id, prune_days FROM " . BB_FORUMS . " WHERE prune_days != 0"; $sql = "SELECT forum_id, prune_days FROM " . BB_FORUMS . " WHERE prune_days != 0";
foreach (DB()->fetch_rowset($sql) as $row) { foreach (DB()->fetch_rowset($sql) as $row) {

View file

@ -17,7 +17,7 @@ while (true) {
set_time_limit(600); set_time_limit(600);
$prune_users = $not_activated_users = $not_active_users = []; $prune_users = $not_activated_users = $not_active_users = [];
if ($not_activated_days = (int)$bb_cfg['user_not_activated_days_keep']) { if ($not_activated_days = (int)config()->get('user_not_activated_days_keep')) {
$sql = DB()->fetch_rowset("SELECT user_id FROM " . BB_USERS . " $sql = DB()->fetch_rowset("SELECT user_id FROM " . BB_USERS . "
WHERE user_level = 0 WHERE user_level = 0
AND user_lastvisit = 0 AND user_lastvisit = 0
@ -31,7 +31,7 @@ while (true) {
} }
} }
if ($not_active_days = (int)$bb_cfg['user_not_active_days_keep']) { if ($not_active_days = (int)config()->get('user_not_active_days_keep')) {
$sql = DB()->fetch_rowset("SELECT user_id FROM " . BB_USERS . " $sql = DB()->fetch_rowset("SELECT user_id FROM " . BB_USERS . "
WHERE user_level = 0 WHERE user_level = 0
AND user_posts = 0 AND user_posts = 0

View file

@ -11,8 +11,8 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
if ($bb_cfg['topic_moved_days_keep']) { if (config()->get('topic_moved_days_keep')) {
$prune_time = TIMENOW - 86400 * $bb_cfg['topic_moved_days_keep']; $prune_time = TIMENOW - 86400 * config()->get('topic_moved_days_keep');
DB()->query(" DB()->query("
DELETE FROM " . BB_TOPICS . " DELETE FROM " . BB_TOPICS . "

View file

@ -11,10 +11,10 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
$user_session_expire_time = TIMENOW - (int)$bb_cfg['user_session_duration']; $user_session_expire_time = TIMENOW - (int)config()->get('user_session_duration');
$admin_session_expire_time = TIMENOW - (int)$bb_cfg['admin_session_duration']; $admin_session_expire_time = TIMENOW - (int)config()->get('admin_session_duration');
$user_session_gc_time = $user_session_expire_time - (int)$bb_cfg['user_session_gc_ttl']; $user_session_gc_time = $user_session_expire_time - (int)config()->get('user_session_gc_ttl');
$admin_session_gc_time = $admin_session_expire_time; $admin_session_gc_time = $admin_session_expire_time;
// ############################ Tables LOCKED ################################ // ############################ Tables LOCKED ################################

View file

@ -27,7 +27,7 @@ DB()->query("CREATE TABLE " . NEW_BB_BT_LAST_USERSTAT . " LIKE " . BB_BT_LAST_US
DB()->expect_slow_query(600); DB()->expect_slow_query(600);
// Update dlstat (part 1) // Update dlstat (part 1)
if ($bb_cfg['tracker']['update_dlstat']) { if (config()->get('tracker.update_dlstat')) {
// ############################ Tables LOCKED ################################ // ############################ Tables LOCKED ################################
DB()->lock([ DB()->lock([
BB_BT_TRACKER, BB_BT_TRACKER,
@ -64,16 +64,16 @@ DB()->query("
"); ");
// Clean peers table // Clean peers table
if ($bb_cfg['tracker']['autoclean']) { if (config()->get('tracker.autoclean')) {
$announce_interval = max((int)$bb_cfg['announce_interval'], 60); $announce_interval = max((int)config()->get('announce_interval'), 60);
$expire_factor = max((float)$bb_cfg['tracker']['expire_factor'], 1); $expire_factor = max((float)config()->get('tracker.expire_factor'), 1);
$peer_expire_time = TIMENOW - floor($announce_interval * $expire_factor); $peer_expire_time = TIMENOW - floor($announce_interval * $expire_factor);
DB()->query("DELETE FROM " . BB_BT_TRACKER . " WHERE update_time < $peer_expire_time"); DB()->query("DELETE FROM " . BB_BT_TRACKER . " WHERE update_time < $peer_expire_time");
} }
// Update dlstat (part 2) // Update dlstat (part 2)
if ($bb_cfg['tracker']['update_dlstat']) { if (config()->get('tracker.update_dlstat')) {
// Set "only 1 seeder" bonus // Set "only 1 seeder" bonus
DB()->query(" DB()->query("
UPDATE UPDATE

View file

@ -11,12 +11,12 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
if (empty($bb_cfg['seeder_last_seen_days_keep']) || empty($bb_cfg['seeder_never_seen_days_keep'])) { if (empty(config()->get('seeder_last_seen_days_keep')) || empty(config()->get('seeder_never_seen_days_keep'))) {
return; return;
} }
$last_seen_time = TIMENOW - 86400 * $bb_cfg['seeder_last_seen_days_keep']; $last_seen_time = TIMENOW - 86400 * config()->get('seeder_last_seen_days_keep');
$never_seen_time = TIMENOW - 86400 * $bb_cfg['seeder_never_seen_days_keep']; $never_seen_time = TIMENOW - 86400 * config()->get('seeder_never_seen_days_keep');
$limit_sql = 3000; $limit_sql = 3000;
$topics_sql = $attach_sql = []; $topics_sql = $attach_sql = [];

View file

@ -11,8 +11,6 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg;
DB()->expect_slow_query(600); DB()->expect_slow_query(600);
// //
@ -81,7 +79,7 @@ DB()->query("DROP TABLE IF EXISTS " . NEW_BB_BT_DLSTATUS_SNAP . ", " . OLD_BB_BT
DB()->query("CREATE TABLE " . NEW_BB_BT_DLSTATUS_SNAP . " LIKE " . BB_BT_DLSTATUS_SNAP); DB()->query("CREATE TABLE " . NEW_BB_BT_DLSTATUS_SNAP . " LIKE " . BB_BT_DLSTATUS_SNAP);
if ($bb_cfg['bt_show_dl_list'] && $bb_cfg['bt_dl_list_only_count']) { if (config()->get('bt_show_dl_list') && config()->get('bt_dl_list_only_count')) {
DB()->query(" DB()->query("
INSERT INTO " . NEW_BB_BT_DLSTATUS_SNAP . " INSERT INTO " . NEW_BB_BT_DLSTATUS_SNAP . "
(topic_id, dl_status, users_count) (topic_id, dl_status, users_count)
@ -104,7 +102,7 @@ DB()->query("DROP TABLE IF EXISTS " . NEW_BB_BT_DLSTATUS_SNAP . ", " . OLD_BB_BT
// //
// TORHELP // TORHELP
// //
if ($bb_cfg['torhelp_enabled']) { if (config()->get('torhelp_enabled')) {
$tor_min_seeders = 0; // "<=" $tor_min_seeders = 0; // "<="
$tor_min_leechers = 2; // ">=" $tor_min_leechers = 2; // ">="
$tor_min_completed = 10; // ">=" $tor_min_completed = 10; // ">="
@ -147,7 +145,7 @@ if ($bb_cfg['torhelp_enabled']) {
WHERE WHERE
trsn.seeders <= $tor_min_seeders trsn.seeders <= $tor_min_seeders
AND trsn.leechers >= $tor_min_leechers AND trsn.leechers >= $tor_min_leechers
AND tor.forum_id != " . (int)$bb_cfg['trash_forum_id'] . " AND tor.forum_id != " . (int)config()->get('trash_forum_id') . "
AND tor.complete_count >= $tor_min_completed AND tor.complete_count >= $tor_min_completed
AND tor.seeder_last_seen <= (UNIX_TIMESTAMP() - $tor_seed_last_seen_days*86400) AND tor.seeder_last_seen <= (UNIX_TIMESTAMP() - $tor_seed_last_seen_days*86400)
AND dl.user_id IN($online_users_csv) AND dl.user_id IN($online_users_csv)

View file

@ -13,7 +13,7 @@ if (!defined('BB_ROOT')) {
DB()->expect_slow_query(600); DB()->expect_slow_query(600);
if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['seed_bonus_release']) { if (config()->get('seed_bonus_enabled') && config()->get('seed_bonus_points') && config()->get('seed_bonus_release')) {
DB()->query(" DB()->query("
CREATE TEMPORARY TABLE tmp_bonus ( CREATE TEMPORARY TABLE tmp_bonus (
user_id INT UNSIGNED NOT NULL DEFAULT '0', user_id INT UNSIGNED NOT NULL DEFAULT '0',
@ -21,7 +21,7 @@ if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['se
) ENGINE = MEMORY ) ENGINE = MEMORY
"); ");
$tor_size = ($bb_cfg['seed_bonus_tor_size'] * 1073741824); $tor_size = (config()->get('seed_bonus_tor_size') * 1073741824);
DB()->query("INSERT INTO tmp_bonus DB()->query("INSERT INTO tmp_bonus
SELECT bt.user_id, count(bt.seeder) AS release_count SELECT bt.user_id, count(bt.seeder) AS release_count
@ -32,8 +32,8 @@ if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['se
GROUP BY bt.user_id GROUP BY bt.user_id
"); ");
$seed_bonus = unserialize($bb_cfg['seed_bonus_points']); $seed_bonus = unserialize(config()->get('seed_bonus_points'));
$seed_release = unserialize($bb_cfg['seed_bonus_release']); $seed_release = unserialize(config()->get('seed_bonus_release'));
foreach ($seed_bonus as $i => $points) { foreach ($seed_bonus as $i => $points) {
if (!$points || !$seed_release[$i]) { if (!$points || !$seed_release[$i]) {
@ -42,7 +42,7 @@ if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['seed_bonus_points'] && $bb_cfg['se
$user_points = ((float)$points / 4); $user_points = ((float)$points / 4);
$release = (int)$seed_release[$i]; $release = (int)$seed_release[$i];
$user_regdate = (TIMENOW - $bb_cfg['seed_bonus_user_regdate'] * 86400); $user_regdate = (TIMENOW - config()->get('seed_bonus_user_regdate') * 86400);
DB()->query(" DB()->query("
UPDATE " . BB_USERS . " u, " . BB_BT_USERS . " bu, tmp_bonus b UPDATE " . BB_USERS . " u, " . BB_BT_USERS . " bu, tmp_bonus b

View file

@ -11,13 +11,11 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg;
$timecheck = TIMENOW - 600; $timecheck = TIMENOW - 600;
$forums_data = DB()->fetch_rowset("SELECT forum_id, allow_reg_tracker, forum_name FROM " . BB_FORUMS); $forums_data = DB()->fetch_rowset("SELECT forum_id, allow_reg_tracker, forum_name FROM " . BB_FORUMS);
if (is_file($bb_cfg['atom']['path'] . '/f/0.atom')) { if (is_file(config()->get('atom.path') . '/f/0.atom')) {
if (filemtime($bb_cfg['atom']['path'] . '/f/0.atom') <= $timecheck) { if (filemtime(config()->get('atom.path') . '/f/0.atom') <= $timecheck) {
\TorrentPier\Legacy\Atom::update_forum_feed(0, $forums_data); \TorrentPier\Legacy\Atom::update_forum_feed(0, $forums_data);
} }
} else { } else {
@ -25,8 +23,8 @@ if (is_file($bb_cfg['atom']['path'] . '/f/0.atom')) {
} }
foreach ($forums_data as $forum_data) { foreach ($forums_data as $forum_data) {
if (is_file($bb_cfg['atom']['path'] . '/f/' . $forum_data['forum_id'] . '.atom')) { if (is_file(config()->get('atom.path') . '/f/' . $forum_data['forum_id'] . '.atom')) {
if (filemtime($bb_cfg['atom']['path'] . '/f/' . $forum_data['forum_id'] . '.atom') <= $timecheck) { if (filemtime(config()->get('atom.path') . '/f/' . $forum_data['forum_id'] . '.atom') <= $timecheck) {
\TorrentPier\Legacy\Atom::update_forum_feed($forum_data['forum_id'], $forum_data); \TorrentPier\Legacy\Atom::update_forum_feed($forum_data['forum_id'], $forum_data);
} }
} else { } else {

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bf, $bb_cfg; global $bf;
// //
// cat_forums // cat_forums
@ -106,7 +106,7 @@ $this->store('cat_forums', $data);
// //
// jumpbox // jumpbox
// //
if ($bb_cfg['show_jumpbox']) { if (config()->get('show_jumpbox')) {
$data = [ $data = [
'guest' => get_forum_select('guest', 'f', null, null, null, 'id="jumpbox" onchange="window.location.href=\'' . FORUM_URL . '\'+this.value;"'), 'guest' => get_forum_select('guest', 'f', null, null, null, 'id="jumpbox" onchange="window.location.href=\'' . FORUM_URL . '\'+this.value;"'),
'user' => get_forum_select('user', 'f', null, null, null, 'id="jumpbox" onchange="window.location.href=\'' . FORUM_URL . '\'+this.value;"'), 'user' => get_forum_select('user', 'f', null, null, null, 'id="jumpbox" onchange="window.location.href=\'' . FORUM_URL . '\'+this.value;"'),
@ -125,8 +125,8 @@ $this->store('viewtopic_forum_select', $data);
// //
// latest_news // latest_news
// //
if ($bb_cfg['show_latest_news'] and $news_forum_ids = $bb_cfg['latest_news_forum_id']) { if (config()->get('show_latest_news') and $news_forum_ids = config()->get('latest_news_forum_id')) {
$news_count = max($bb_cfg['latest_news_count'], 1); $news_count = max(config()->get('latest_news_count'), 1);
$data = DB()->fetch_rowset(" $data = DB()->fetch_rowset("
SELECT topic_id, topic_time, topic_title, forum_id SELECT topic_id, topic_time, topic_title, forum_id
@ -143,8 +143,8 @@ if ($bb_cfg['show_latest_news'] and $news_forum_ids = $bb_cfg['latest_news_forum
// //
// Network_news // Network_news
// //
if ($bb_cfg['show_network_news'] and $net_forum_ids = $bb_cfg['network_news_forum_id']) { if (config()->get('show_network_news') and $net_forum_ids = config()->get('network_news_forum_id')) {
$net_count = max($bb_cfg['network_news_count'], 1); $net_count = max(config()->get('network_news_count'), 1);
$data = DB()->fetch_rowset(" $data = DB()->fetch_rowset("
SELECT topic_id, topic_time, topic_title, forum_id SELECT topic_id, topic_time, topic_title, forum_id

View file

@ -11,23 +11,29 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg; if (!config()->get('tp_updater_settings.enabled')) {
if (!$bb_cfg['tp_updater_settings']['enabled']) {
return; return;
} }
$data = []; $data = [];
$data[] = ['latest_check_timestamp' => TIMENOW];
$updaterDownloader = new \TorrentPier\Updater(); try {
$updaterDownloader = $updaterDownloader->getLastVersion($bb_cfg['tp_updater_settings']['allow_pre_releases']); $updaterDownloader = new \TorrentPier\Updater();
$updaterDownloader = $updaterDownloader->getLastVersion(config()->get('tp_updater_settings.allow_pre_releases'));
} catch (Exception $exception) {
bb_log('[Updater] Exception: ' . $exception->getMessage() . LOG_LF);
$this->store('check_updates', $data);
return;
}
$getVersion = \TorrentPier\Helpers\VersionHelper::removerPrefix($updaterDownloader['tag_name']); $getVersion = \TorrentPier\Helpers\VersionHelper::removerPrefix($updaterDownloader['tag_name']);
$currentVersion = \TorrentPier\Helpers\VersionHelper::removerPrefix($bb_cfg['tp_version']); $currentVersion = \TorrentPier\Helpers\VersionHelper::removerPrefix(config()->get('tp_version'));
// Has update! // Has update!
if (\z4kn4fein\SemVer\Version::greaterThan($getVersion, $currentVersion)) { if (\z4kn4fein\SemVer\Version::greaterThan($getVersion, $currentVersion)) {
$latestBuildFileLink = $updaterDownloader['assets'][0]['browser_download_url']; $latestBuildFileLink = $updaterDownloader['assets'][0]['browser_download_url'];
$SHAFileHash = $updaterDownloader['assets'][0]['digest'] ?? '';
// Check updater file // Check updater file
$updaterFile = readUpdaterFile(); $updaterFile = readUpdaterFile();
@ -41,10 +47,12 @@ if (\z4kn4fein\SemVer\Version::greaterThan($getVersion, $currentVersion)) {
]), UPDATER_FILE, replace_content: true); ]), UPDATER_FILE, replace_content: true);
} }
// Get MD5 checksum // Get MD5 / sha256 checksum
$buildFileChecksum = ''; $buildFileChecksum = '';
if (isset($latestBuildFileLink)) { if (!empty($SHAFileHash)) {
$buildFileChecksum = strtoupper(md5_file($latestBuildFileLink)); $buildFileChecksum = $SHAFileHash;
} else {
$buildFileChecksum = 'MD5: ' . strtoupper(md5_file($latestBuildFileLink));
} }
// Build data array // Build data array
@ -58,5 +66,4 @@ if (\z4kn4fein\SemVer\Version::greaterThan($getVersion, $currentVersion)) {
]; ];
} }
$data[] = ['latest_check_timestamp' => TIMENOW];
$this->store('check_updates', $data); $this->store('check_updates', $data);

View file

@ -11,8 +11,6 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg;
$smilies = []; $smilies = [];
$rowset = DB()->fetch_rowset("SELECT * FROM " . BB_SMILIES); $rowset = DB()->fetch_rowset("SELECT * FROM " . BB_SMILIES);
@ -20,7 +18,7 @@ sort($rowset);
foreach ($rowset as $smile) { foreach ($rowset as $smile) {
$smilies['orig'][] = '#(?<=^|\W)' . preg_quote($smile['code'], '#') . '(?=$|\W)#'; $smilies['orig'][] = '#(?<=^|\W)' . preg_quote($smile['code'], '#') . '(?=$|\W)#';
$smilies['repl'][] = ' <img class="smile" src="' . $bb_cfg['smilies_path'] . '/' . $smile['smile_url'] . '" alt="' . $smile['code'] . '" title="' . $smile['emoticon'] . '" align="absmiddle" border="0" />'; $smilies['repl'][] = ' <img class="smile" src="' . config()->get('smilies_path') . '/' . $smile['smile_url'] . '" alt="' . $smile['code'] . '" title="' . $smile['emoticon'] . '" align="absmiddle" border="0" />';
$smilies['smile'][] = $smile; $smilies['smile'][] = $smile;
} }

View file

@ -11,8 +11,6 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg;
$data = []; $data = [];
// usercount // usercount
@ -29,7 +27,7 @@ $data['postcount'] = commify($row['postcount']);
$data['topiccount'] = commify($row['topiccount']); $data['topiccount'] = commify($row['topiccount']);
// Tracker stats // Tracker stats
if ($bb_cfg['tor_stats']) { if (config()->get('tor_stats')) {
// torrents stat // torrents stat
$row = DB()->fetch_row("SELECT COUNT(topic_id) AS torrentcount, SUM(size) AS size FROM " . BB_BT_TORRENTS); $row = DB()->fetch_row("SELECT COUNT(topic_id) AS torrentcount, SUM(size) AS size FROM " . BB_BT_TORRENTS);
$data['torrentcount'] = commify($row['torrentcount']); $data['torrentcount'] = commify($row['torrentcount']);
@ -44,7 +42,7 @@ if ($bb_cfg['tor_stats']) {
} }
// gender stat // gender stat
if ($bb_cfg['gender']) { if (config()->get('gender')) {
$male = DB()->fetch_row("SELECT COUNT(user_id) AS male FROM " . BB_USERS . " WHERE user_gender = " . MALE . " AND user_id NOT IN(" . EXCLUDED_USERS . ")"); $male = DB()->fetch_row("SELECT COUNT(user_id) AS male FROM " . BB_USERS . " WHERE user_gender = " . MALE . " AND user_id NOT IN(" . EXCLUDED_USERS . ")");
$female = DB()->fetch_row("SELECT COUNT(user_id) AS female FROM " . BB_USERS . " WHERE user_gender = " . FEMALE . " AND user_id NOT IN(" . EXCLUDED_USERS . ")"); $female = DB()->fetch_row("SELECT COUNT(user_id) AS female FROM " . BB_USERS . " WHERE user_gender = " . FEMALE . " AND user_id NOT IN(" . EXCLUDED_USERS . ")");
$unselect = DB()->fetch_row("SELECT COUNT(user_id) AS unselect FROM " . BB_USERS . " WHERE user_gender = 0 AND user_id NOT IN(" . EXCLUDED_USERS . ")"); $unselect = DB()->fetch_row("SELECT COUNT(user_id) AS unselect FROM " . BB_USERS . " WHERE user_gender = 0 AND user_id NOT IN(" . EXCLUDED_USERS . ")");
@ -55,7 +53,7 @@ if ($bb_cfg['gender']) {
} }
// birthday stat // birthday stat
if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) { if (config()->get('birthday_check_day') && config()->get('birthday_enabled')) {
$sql = DB()->fetch_rowset("SELECT user_id, username, user_rank , user_birthday $sql = DB()->fetch_rowset("SELECT user_id, username, user_rank , user_birthday
FROM " . BB_USERS . " FROM " . BB_USERS . "
WHERE user_id NOT IN(" . EXCLUDED_USERS . ") WHERE user_id NOT IN(" . EXCLUDED_USERS . ")
@ -66,7 +64,7 @@ if ($bb_cfg['birthday_check_day'] && $bb_cfg['birthday_enabled']) {
"); ");
$date_today = bb_date(TIMENOW, 'md', false); $date_today = bb_date(TIMENOW, 'md', false);
$date_forward = bb_date(TIMENOW + ($bb_cfg['birthday_check_day'] * 86400), 'md', false); $date_forward = bb_date(TIMENOW + (config()->get('birthday_check_day') * 86400), 'md', false);
$birthday_today_list = $birthday_week_list = []; $birthday_today_list = $birthday_week_list = [];

View file

@ -13,22 +13,19 @@ if (!defined('BB_ROOT')) {
function get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div) function get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div)
{ {
global $bb_cfg; $ext = config()->get('file_id_ext')[$ext_id] ?? '';
$ext = $bb_cfg['file_id_ext'][$ext_id] ?? '';
return ($base_path ? "$base_path/" : '') . floor($id / $first_div) . '/' . ($id % $sec_div) . '/' . $id . ($ext ? ".$ext" : ''); return ($base_path ? "$base_path/" : '') . floor($id / $first_div) . '/' . ($id % $sec_div) . '/' . $id . ($ext ? ".$ext" : '');
} }
function get_avatar_path($id, $ext_id, $base_path = null, $first_div = 10000, $sec_div = 100) function get_avatar_path($id, $ext_id, $base_path = null, $first_div = 10000, $sec_div = 100)
{ {
global $bb_cfg; $base_path ??= config()->get('avatars.upload_path');
$base_path ??= $bb_cfg['avatars']['upload_path'];
return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div); return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div);
} }
function get_attach_path($id, $ext_id = '', $base_path = null, $first_div = 10000, $sec_div = 100) function get_attach_path($id, $ext_id = '', $base_path = null, $first_div = 10000, $sec_div = 100)
{ {
global $bb_cfg; $base_path ??= config()->get('attach.upload_path');
$base_path ??= $bb_cfg['attach']['upload_path'];
return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div); return get_path_from_id($id, $ext_id, $base_path, $first_div, $sec_div);
} }
@ -600,8 +597,6 @@ function humn_size($size, $rounder = null, $min = null, $space = '&nbsp;')
function bt_show_ip($ip, $port = '') function bt_show_ip($ip, $port = '')
{ {
global $bb_cfg;
if (IS_AM) { if (IS_AM) {
$ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($ip); $ip = \TorrentPier\Helpers\IPHelper::long2ip_extended($ip);
@ -617,18 +612,16 @@ function bt_show_ip($ip, $port = '')
return $ip; return $ip;
} }
return $bb_cfg['bt_show_ip_only_moder'] ? false : \TorrentPier\Helpers\IPHelper::anonymizeIP($ip); return config()->get('bt_show_ip_only_moder') ? false : \TorrentPier\Helpers\IPHelper::anonymizeIP($ip);
} }
function bt_show_port($port) function bt_show_port($port)
{ {
global $bb_cfg;
if (IS_AM) { if (IS_AM) {
return $port; return $port;
} }
return $bb_cfg['bt_show_port_only_moder'] ? false : $port; return config()->get('bt_show_port_only_moder') ? false : $port;
} }
function checkbox_get_val(&$key, &$val, $default = 1, $on = 1, $off = 0) function checkbox_get_val(&$key, &$val, $default = 1, $on = 1, $off = 0)
@ -802,24 +795,24 @@ function str_short($text, $max_length, $space = ' ')
function generate_user_info($row, bool $have_auth = IS_ADMIN): array function generate_user_info($row, bool $have_auth = IS_ADMIN): array
{ {
global $userdata, $lang, $images, $bb_cfg; global $userdata, $lang, $images;
$from = !empty($row['user_from']) ? render_flag($row['user_from'], false) : $lang['NOSELECT']; $from = !empty($row['user_from']) ? render_flag($row['user_from'], false) : $lang['NOSELECT'];
$joined = bb_date($row['user_regdate'], 'Y-m-d H:i', false); $joined = bb_date($row['user_regdate'], 'Y-m-d H:i', false);
$user_time = !empty($row['user_time']) ? sprintf('%s <span class="signature">(%s)</span>', bb_date($row['user_time']), delta_time($row['user_time'])) : $lang['NOSELECT']; $user_time = !empty($row['user_time']) ? sprintf('%s <span class="signature">(%s)</span>', bb_date($row['user_time']), delta_time($row['user_time'])) : $lang['NOSELECT'];
$posts = '<a href="search.php?search_author=1&amp;uid=' . $row['user_id'] . '" target="_blank">' . $row['user_posts'] ?: 0 . '</a>'; $posts = '<a href="search.php?search_author=1&amp;uid=' . $row['user_id'] . '" target="_blank">' . $row['user_posts'] ?: 0 . '</a>';
$pm = $bb_cfg['text_buttons'] ? '<a class="txtb" href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '">' . $lang['SEND_PM_TXTB'] . '</a>' : '<a href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '"><img src="' . $images['icon_pm'] . '" alt="' . $lang['SEND_PRIVATE_MESSAGE'] . '" title="' . $lang['SEND_PRIVATE_MESSAGE'] . '" border="0" /></a>'; $pm = config()->get('text_buttons') ? '<a class="txtb" href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '">' . $lang['SEND_PM_TXTB'] . '</a>' : '<a href="' . (PM_URL . "?mode=post&amp;" . POST_USERS_URL . "=" . $row['user_id']) . '"><img src="' . $images['icon_pm'] . '" alt="' . $lang['SEND_PRIVATE_MESSAGE'] . '" title="' . $lang['SEND_PRIVATE_MESSAGE'] . '" border="0" /></a>';
$avatar = get_avatar($row['user_id'], $row['avatar_ext_id'], !bf($row['user_opt'], 'user_opt', 'dis_avatar'), 50, 50); $avatar = get_avatar($row['user_id'], $row['avatar_ext_id'], !bf($row['user_opt'], 'user_opt', 'dis_avatar'), 50, 50);
if (bf($row['user_opt'], 'user_opt', 'user_viewemail') || $have_auth || ($row['user_id'] == $userdata['user_id'])) { if (bf($row['user_opt'], 'user_opt', 'user_viewemail') || $have_auth || ($row['user_id'] == $userdata['user_id'])) {
$email_uri = ($bb_cfg['board_email_form']) ? ("profile.php?mode=email&amp;" . POST_USERS_URL . "=" . $row['user_id']) : 'mailto:' . $row['user_email']; $email_uri = (config()->get('board_email_form')) ? ("profile.php?mode=email&amp;" . POST_USERS_URL . "=" . $row['user_id']) : 'mailto:' . $row['user_email'];
$email = '<a class="editable" href="' . $email_uri . '">' . $row['user_email'] . '</a>'; $email = '<a class="editable" href="' . $email_uri . '">' . $row['user_email'] . '</a>';
} else { } else {
$email = $lang['HIDDEN_USER']; $email = $lang['HIDDEN_USER'];
} }
if ($row['user_website']) { if ($row['user_website']) {
$www = $bb_cfg['text_buttons'] ? '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww">' . $lang['VISIT_WEBSITE_TXTB'] . '</a>' : '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww"><img src="' . $images['icon_www'] . '" alt="' . $lang['VISIT_WEBSITE'] . '" title="' . $lang['VISIT_WEBSITE'] . '" border="0" /></a>'; $www = config()->get('text_buttons') ? '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww">' . $lang['VISIT_WEBSITE_TXTB'] . '</a>' : '<a class="txtb" href="' . $row['user_website'] . '" target="_userwww"><img src="' . $images['icon_www'] . '" alt="' . $lang['VISIT_WEBSITE'] . '" title="' . $lang['VISIT_WEBSITE'] . '" border="0" /></a>';
} else { } else {
$www = $lang['NOSELECT']; $www = $lang['NOSELECT'];
} }
@ -996,9 +989,9 @@ function get_userdata(int|string $u, bool $is_name = false, bool $allow_guest =
function make_jumpbox(): void function make_jumpbox(): void
{ {
global $datastore, $template, $bb_cfg; global $datastore, $template;
if (!$bb_cfg['show_jumpbox']) { if (!config()->get('show_jumpbox')) {
return; return;
} }
@ -1076,14 +1069,14 @@ function get_forum_select($mode = 'guest', $name = POST_FORUM_URL, $selected = n
function setup_style() function setup_style()
{ {
global $bb_cfg, $template, $userdata; global $template, $userdata;
// AdminCP works only with default template // AdminCP works only with default template
$tpl_dir_name = defined('IN_ADMIN') ? 'default' : basename($bb_cfg['tpl_name']); $tpl_dir_name = defined('IN_ADMIN') ? 'default' : basename(config()->get('tpl_name'));
$stylesheet = defined('IN_ADMIN') ? 'main.css' : basename($bb_cfg['stylesheet']); $stylesheet = defined('IN_ADMIN') ? 'main.css' : basename(config()->get('stylesheet'));
if (!IS_GUEST && !empty($userdata['tpl_name'])) { if (!IS_GUEST && !empty($userdata['tpl_name'])) {
foreach ($bb_cfg['templates'] as $folder => $name) { foreach (config()->get('templates') as $folder => $name) {
if ($userdata['tpl_name'] == $folder) { if ($userdata['tpl_name'] == $folder) {
$tpl_dir_name = basename($userdata['tpl_name']); $tpl_dir_name = basename($userdata['tpl_name']);
} }
@ -1096,7 +1089,7 @@ function setup_style()
$template->assign_vars([ $template->assign_vars([
'SPACER' => make_url('styles/images/spacer.gif'), 'SPACER' => make_url('styles/images/spacer.gif'),
'STYLESHEET' => make_url($css_dir . $stylesheet), 'STYLESHEET' => make_url($css_dir . $stylesheet),
'EXT_LINK_NEW_WIN' => $bb_cfg['ext_link_new_win'], 'EXT_LINK_NEW_WIN' => config()->get('ext_link_new_win'),
'TPL_DIR' => make_url($css_dir), 'TPL_DIR' => make_url($css_dir),
'SITE_URL' => make_url('/') 'SITE_URL' => make_url('/')
]); ]);
@ -1109,19 +1102,19 @@ function setup_style()
// Create date / time with format and friendly date // Create date / time with format and friendly date
function bb_date($gmepoch, $format = false, $friendly_date = true) function bb_date($gmepoch, $format = false, $friendly_date = true)
{ {
global $bb_cfg, $lang, $userdata; global $lang, $userdata;
$gmepoch = (int)$gmepoch; $gmepoch = (int)$gmepoch;
if (!$format) { if (!$format) {
$format = $bb_cfg['default_dateformat']; $format = config()->get('default_dateformat');
} }
if (empty($lang)) { if (empty($lang)) {
require_once($bb_cfg['default_lang_dir'] . 'main.php'); lang()->initializeLanguage();
} }
if (!defined('IS_GUEST') || IS_GUEST) { if (!defined('IS_GUEST') || IS_GUEST) {
$tz = $bb_cfg['board_timezone']; $tz = config()->get('board_timezone');
} else { } else {
$tz = $userdata['user_timezone']; $tz = $userdata['user_timezone'];
} }
@ -1156,7 +1149,7 @@ function bb_date($gmepoch, $format = false, $friendly_date = true)
} }
} }
return ($bb_cfg['translate_dates']) ? strtr(strtoupper($date), $lang['DATETIME']) : $date; return (config()->get('translate_dates')) ? strtr(strtoupper($date), $lang['DATETIME']) : $date;
} }
/** /**
@ -1167,12 +1160,11 @@ function bb_date($gmepoch, $format = false, $friendly_date = true)
*/ */
function get_user_torrent_client(string $peer_id): string function get_user_torrent_client(string $peer_id): string
{ {
global $bb_cfg;
static $iconExtension = '.png'; static $iconExtension = '.png';
$bestMatch = null; $bestMatch = null;
$bestMatchLength = 0; $bestMatchLength = 0;
foreach ($bb_cfg['tor_clients'] as $key => $clientName) { foreach (config()->get('tor_clients') as $key => $clientName) {
if (str_starts_with($peer_id, $key) !== false && strlen($key) > $bestMatchLength) { if (str_starts_with($peer_id, $key) !== false && strlen($key) > $bestMatchLength) {
$bestMatch = $clientName; $bestMatch = $clientName;
$bestMatchLength = strlen($key); $bestMatchLength = strlen($key);
@ -1223,12 +1215,11 @@ function render_flag(string $code, bool $showName = true): string
function birthday_age($date) function birthday_age($date)
{ {
global $bb_cfg;
if (!$date) { if (!$date) {
return ''; return '';
} }
$tz = TIMENOW + (3600 * $bb_cfg['board_timezone']); $tz = TIMENOW + (3600 * config()->get('board_timezone'));
return delta_time(strtotime($date, $tz)); return delta_time(strtotime($date, $tz));
} }
@ -1339,7 +1330,7 @@ function bb_preg_quote($str, $delimiter)
function bb_die($msg_text, $status_code = null) function bb_die($msg_text, $status_code = null)
{ {
global $ajax, $bb_cfg, $lang, $template, $theme, $userdata, $user; global $ajax, $lang, $template, $theme, $userdata, $user;
if (isset($status_code)) { if (isset($status_code)) {
http_response_code($status_code); http_response_code($status_code);
@ -1356,9 +1347,9 @@ function bb_die($msg_text, $status_code = null)
define('HAS_DIED', 1); define('HAS_DIED', 1);
define('DISABLE_CACHING_OUTPUT', true); define('DISABLE_CACHING_OUTPUT', true);
// If empty lang // If empty lang, initialize language singleton
if (empty($lang)) { if (empty($lang)) {
require($bb_cfg['default_lang_dir'] . 'main.php'); lang()->initializeLanguage();
} }
// If empty session // If empty session
@ -1369,7 +1360,7 @@ function bb_die($msg_text, $status_code = null)
// If the header hasn't been output then do it // If the header hasn't been output then do it
if (!defined('PAGE_HEADER_SENT')) { if (!defined('PAGE_HEADER_SENT')) {
if (empty($template)) { if (empty($template)) {
$template = new TorrentPier\Legacy\Template(BB_ROOT . "templates/{$bb_cfg['tpl_name']}"); $template = new TorrentPier\Legacy\Template(BB_ROOT . "templates/" . config()->get('tpl_name'));
} }
if (empty($theme)) { if (empty($theme)) {
$theme = setup_style(); $theme = setup_style();
@ -1397,8 +1388,6 @@ function bb_die($msg_text, $status_code = null)
function bb_simple_die($txt, $status_code = null) function bb_simple_die($txt, $status_code = null)
{ {
global $bb_cfg;
header('Content-Type: text/plain; charset=' . DEFAULT_CHARSET); header('Content-Type: text/plain; charset=' . DEFAULT_CHARSET);
if (isset($status_code)) { if (isset($status_code)) {
@ -1426,8 +1415,6 @@ function meta_refresh($url, $time = 5)
function redirect($url) function redirect($url)
{ {
global $bb_cfg;
if (headers_sent($filename, $linenum)) { if (headers_sent($filename, $linenum)) {
trigger_error("Headers already sent in $filename($linenum)", E_USER_ERROR); trigger_error("Headers already sent in $filename($linenum)", E_USER_ERROR);
} }
@ -1437,11 +1424,11 @@ function redirect($url)
} }
$url = trim($url); $url = trim($url);
$server_protocol = ($bb_cfg['cookie_secure']) ? 'https://' : 'http://'; $server_protocol = (config()->get('cookie_secure')) ? 'https://' : 'http://';
$server_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($bb_cfg['server_name'])); $server_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim(config()->get('server_name')));
$server_port = ($bb_cfg['server_port'] <> 80) ? ':' . trim($bb_cfg['server_port']) : ''; $server_port = (config()->get('server_port') <> 80) ? ':' . trim(config()->get('server_port')) : '';
$script_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim($bb_cfg['script_path'])); $script_name = preg_replace('#^\/?(.*?)\/?$#', '\1', trim(config()->get('script_path')));
if ($script_name) { if ($script_name) {
$script_name = "/$script_name"; $script_name = "/$script_name";
@ -1450,6 +1437,9 @@ function redirect($url)
$redirect_url = $server_protocol . $server_name . $server_port . $script_name . preg_replace('#^\/?(.*?)\/?$#', '/\1', $url); $redirect_url = $server_protocol . $server_name . $server_port . $script_name . preg_replace('#^\/?(.*?)\/?$#', '/\1', $url);
// Send no-cache headers to prevent browsers from caching redirects
send_no_cache_headers();
// Behave as per HTTP/1.1 spec for others // Behave as per HTTP/1.1 spec for others
header('Location: ' . $redirect_url, response_code: 301); header('Location: ' . $redirect_url, response_code: 301);
exit; exit;
@ -1549,9 +1539,9 @@ function cat_exists($cat_id): bool
function get_topic_icon($topic, $is_unread = null) function get_topic_icon($topic, $is_unread = null)
{ {
global $bb_cfg, $images; global $images;
$t_hot = ($topic['topic_replies'] >= $bb_cfg['hot_threshold']); $t_hot = ($topic['topic_replies'] >= config()->get('hot_threshold'));
$is_unread ??= is_unread($topic['topic_last_post_time'], $topic['topic_id'], $topic['forum_id']); $is_unread ??= is_unread($topic['topic_last_post_time'], $topic['topic_id'], $topic['forum_id']);
if ($topic['topic_status'] == TOPIC_MOVED) { if ($topic['topic_status'] == TOPIC_MOVED) {
@ -1684,7 +1674,7 @@ function clean_title($str, $replace_underscore = false)
function clean_text_match($text, $ltrim_star = true, $die_if_empty = false) function clean_text_match($text, $ltrim_star = true, $die_if_empty = false)
{ {
global $bb_cfg, $lang; global $lang;
$text = str_compact($text); $text = str_compact($text);
$ltrim_chars = ($ltrim_star) ? ' *-!' : ' '; $ltrim_chars = ($ltrim_star) ? ' *-!' : ' ';
@ -1692,7 +1682,7 @@ function clean_text_match($text, $ltrim_star = true, $die_if_empty = false)
$text = ' ' . str_compact(ltrim($text, $ltrim_chars)) . ' '; $text = ' ' . str_compact(ltrim($text, $ltrim_chars)) . ' ';
if ($bb_cfg['search_engine_type'] == 'sphinx') { if (config()->get('search_engine_type') == 'sphinx') {
$text = preg_replace('#(?<=\S)\-#u', ' ', $text); // "1-2-3" -> "1 2 3" $text = preg_replace('#(?<=\S)\-#u', ' ', $text); // "1-2-3" -> "1 2 3"
$text = preg_replace('#[^0-9a-zA-Zа-яА-ЯёЁ\-_*|]#u', ' ', $text); // valid characters (except '"' which are separate) $text = preg_replace('#[^0-9a-zA-Zа-яА-ЯёЁ\-_*|]#u', ' ', $text); // valid characters (except '"' which are separate)
$text = str_replace(['-', '*'], [' -', '* '], $text); // only at the beginning/end of a word $text = str_replace(['-', '*'], [' -', '* '], $text); // only at the beginning/end of a word
@ -1742,7 +1732,7 @@ function log_sphinx_error($err_type, $err_msg, $query = '')
function get_title_match_topics($title_match_sql, array $forum_ids = []) function get_title_match_topics($title_match_sql, array $forum_ids = [])
{ {
global $bb_cfg, $sphinx, $userdata, $title_match, $lang; global $sphinx, $userdata, $title_match, $lang;
$where_ids = []; $where_ids = [];
if ($forum_ids) { if ($forum_ids) {
@ -1750,12 +1740,12 @@ function get_title_match_topics($title_match_sql, array $forum_ids = [])
} }
$title_match_sql = encode_text_match($title_match_sql); $title_match_sql = encode_text_match($title_match_sql);
if ($bb_cfg['search_engine_type'] == 'sphinx') { if (config()->get('search_engine_type') == 'sphinx') {
$sphinx = init_sphinx(); $sphinx = init_sphinx();
$where = $title_match ? 'topics' : 'posts'; $where = $title_match ? 'topics' : 'posts';
$sphinx->setServer($bb_cfg['sphinx_topic_titles_host'], $bb_cfg['sphinx_topic_titles_port']); $sphinx->setServer(config()->get('sphinx_topic_titles_host'), config()->get('sphinx_topic_titles_port'));
if ($forum_ids) { if ($forum_ids) {
$sphinx->setFilter('forum_id', $forum_ids, false); $sphinx->setFilter('forum_id', $forum_ids, false);
} }
@ -1775,9 +1765,9 @@ function get_title_match_topics($title_match_sql, array $forum_ids = [])
if ($warning = $sphinx->getLastWarning()) { if ($warning = $sphinx->getLastWarning()) {
log_sphinx_error('wrn', $warning, $title_match_sql); log_sphinx_error('wrn', $warning, $title_match_sql);
} }
} elseif ($bb_cfg['search_engine_type'] == 'mysql') { } elseif (config()->get('search_engine_type') == 'mysql') {
$where_forum = ($forum_ids) ? "AND forum_id IN(" . implode(',', $forum_ids) . ")" : ''; $where_forum = ($forum_ids) ? "AND forum_id IN(" . implode(',', $forum_ids) . ")" : '';
$search_bool_mode = ($bb_cfg['allow_search_in_bool_mode']) ? ' IN BOOLEAN MODE' : ''; $search_bool_mode = (config()->get('allow_search_in_bool_mode')) ? ' IN BOOLEAN MODE' : '';
if ($title_match) { if ($title_match) {
$where_id = 'topic_id'; $where_id = 'topic_id';
@ -1832,14 +1822,14 @@ function decode_text_match($txt)
*/ */
function create_magnet(string $infohash, string $infohash_v2, string $auth_key, string $name, int|string $length = 0): string function create_magnet(string $infohash, string $infohash_v2, string $auth_key, string $name, int|string $length = 0): string
{ {
global $bb_cfg, $images, $lang; global $images, $lang;
if (!$bb_cfg['magnet_links_enabled']) { if (!config()->get('magnet_links_enabled')) {
return false; return false;
} }
// Only for registered users // Only for registered users
if (!$bb_cfg['magnet_links_for_guests'] && IS_GUEST) { if (!config()->get('magnet_links_for_guests') && IS_GUEST) {
return false; return false;
} }
@ -1864,7 +1854,7 @@ function create_magnet(string $infohash, string $infohash_v2, string $auth_key,
$magnet .= '&xl=' . $length; $magnet .= '&xl=' . $length;
} }
return '<a title="' . ($v2_support ? $lang['MAGNET_v2'] : $lang['MAGNET']) . '" href="' . $magnet . '&tr=' . urlencode($bb_cfg['bt_announce_url'] . "?{$bb_cfg['passkey_key']}=$auth_key") . '&dn=' . urlencode($name) . '"><img src="' . ($v2_support ? $images['icon_magnet_v2'] : $images['icon_magnet']) . '" width="12" height="12" border="0" /></a>'; return '<a title="' . ($v2_support ? $lang['MAGNET_v2'] : $lang['MAGNET']) . '" href="' . $magnet . '&tr=' . urlencode(config()->get('bt_announce_url') . "?" . config()->get('passkey_key') . "=$auth_key") . '&dn=' . urlencode($name) . '"><img src="' . ($v2_support ? $images['icon_magnet_v2'] : $images['icon_magnet']) . '" width="12" height="12" border="0" /></a>';
} }
function set_die_append_msg($forum_id = null, $topic_id = null, $group_id = null) function set_die_append_msg($forum_id = null, $topic_id = null, $group_id = null)
@ -1925,7 +1915,7 @@ function send_pm($user_id, $subject, $message, $poster_id = BOT_UID)
*/ */
function profile_url(array $data, bool $target_blank = false, bool $no_link = false): string function profile_url(array $data, bool $target_blank = false, bool $no_link = false): string
{ {
global $bb_cfg, $lang, $datastore; global $lang, $datastore;
if (!$ranks = $datastore->get('ranks')) { if (!$ranks = $datastore->get('ranks')) {
$datastore->update('ranks'); $datastore->update('ranks');
@ -1940,7 +1930,7 @@ function profile_url(array $data, bool $target_blank = false, bool $no_link = fa
$style = 'colorUser'; $style = 'colorUser';
if (isset($ranks[$user_rank])) { if (isset($ranks[$user_rank])) {
$title = $ranks[$user_rank]['rank_title']; $title = $ranks[$user_rank]['rank_title'];
if ($bb_cfg['color_nick']) { if (config()->get('color_nick')) {
$style = $ranks[$user_rank]['rank_style']; $style = $ranks[$user_rank]['rank_style'];
} }
} }
@ -1968,18 +1958,16 @@ function profile_url(array $data, bool $target_blank = false, bool $no_link = fa
function get_avatar($user_id, $ext_id, $allow_avatar = true, $height = '', $width = '') function get_avatar($user_id, $ext_id, $allow_avatar = true, $height = '', $width = '')
{ {
global $bb_cfg;
$height = $height ? 'height="' . $height . '"' : ''; $height = $height ? 'height="' . $height . '"' : '';
$width = $width ? 'width="' . $width . '"' : ''; $width = $width ? 'width="' . $width . '"' : '';
$user_avatar = '<img src="' . make_url($bb_cfg['avatars']['display_path'] . $bb_cfg['avatars']['no_avatar']) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />'; $user_avatar = '<img src="' . make_url(config()->get('avatars.display_path') . config()->get('avatars.no_avatar')) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />';
if ($user_id == BOT_UID && $bb_cfg['avatars']['bot_avatar']) { if ($user_id == BOT_UID && config()->get('avatars.bot_avatar')) {
$user_avatar = '<img src="' . make_url($bb_cfg['avatars']['display_path'] . $bb_cfg['avatars']['bot_avatar']) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />'; $user_avatar = '<img src="' . make_url(config()->get('avatars.display_path') . config()->get('avatars.bot_avatar')) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />';
} elseif ($allow_avatar && $ext_id) { } elseif ($allow_avatar && $ext_id) {
if (is_file(get_avatar_path($user_id, $ext_id))) { if (is_file(get_avatar_path($user_id, $ext_id))) {
$user_avatar = '<img src="' . make_url(get_avatar_path($user_id, $ext_id, $bb_cfg['avatars']['display_path'])) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />'; $user_avatar = '<img src="' . make_url(get_avatar_path($user_id, $ext_id, config()->get('avatars.display_path'))) . '" alt="' . $user_id . '" ' . $height . ' ' . $width . ' />';
} }
} }
@ -1994,9 +1982,9 @@ function get_avatar($user_id, $ext_id, $allow_avatar = true, $height = '', $widt
*/ */
function genderImage(int $gender): ?string function genderImage(int $gender): ?string
{ {
global $bb_cfg, $lang, $images; global $lang, $images;
if (!$bb_cfg['gender']) { if (!config()->get('gender')) {
return false; return false;
} }
@ -2009,12 +1997,12 @@ function genderImage(int $gender): ?string
function is_gold($type): string function is_gold($type): string
{ {
global $lang, $bb_cfg, $images; global $lang, $images;
$type = (int)$type; $type = (int)$type;
$is_gold = ''; $is_gold = '';
if (!$bb_cfg['tracker']['gold_silver_enabled']) { if (!config()->get('tracker.gold_silver_enabled')) {
return $is_gold; return $is_gold;
} }
@ -2083,10 +2071,10 @@ function hash_search($hash)
*/ */
function bb_captcha(string $mode): bool|string function bb_captcha(string $mode): bool|string
{ {
global $bb_cfg, $lang; global $lang;
$settings = $bb_cfg['captcha']; $settings = config()->get('captcha');
$settings['language'] = $bb_cfg['default_lang']; $settings['language'] = config()->get('default_lang');
// Checking captcha settings // Checking captcha settings
if (!$settings['disabled'] && $settings['service'] !== 'text') { if (!$settings['disabled'] && $settings['service'] !== 'text') {
@ -2138,13 +2126,13 @@ function clean_tor_dirname($dirname)
*/ */
function user_birthday_icon($user_birthday, $user_id): string function user_birthday_icon($user_birthday, $user_id): string
{ {
global $bb_cfg, $images, $lang; global $images, $lang;
$current_date = bb_date(TIMENOW, 'md', false); $current_date = bb_date(TIMENOW, 'md', false);
$user_birthday = ($user_id != GUEST_UID && !empty($user_birthday) && $user_birthday != '1900-01-01') $user_birthday = ($user_id != GUEST_UID && !empty($user_birthday) && $user_birthday != '1900-01-01')
? bb_date(strtotime($user_birthday), 'md', false) : false; ? bb_date(strtotime($user_birthday), 'md', false) : false;
return ($bb_cfg['birthday_enabled'] && $current_date == $user_birthday) ? '<img src="' . $images['icon_birthday'] . '" alt="' . $lang['HAPPY_BIRTHDAY'] . '" title="' . $lang['HAPPY_BIRTHDAY'] . '" border="0" />' : ''; return (config()->get('birthday_enabled') && $current_date == $user_birthday) ? '<img src="' . $images['icon_birthday'] . '" alt="' . $lang['HAPPY_BIRTHDAY'] . '" title="' . $lang['HAPPY_BIRTHDAY'] . '" border="0" />' : '';
} }
/** /**
@ -2190,9 +2178,7 @@ function readUpdaterFile(): array|bool
*/ */
function infoByIP(string $ipAddress, int $port = 0): array function infoByIP(string $ipAddress, int $port = 0): array
{ {
global $bb_cfg; if (!config()->get('ip2country_settings.enabled')) {
if (!$bb_cfg['ip2country_settings']['enabled']) {
return []; return [];
} }
@ -2203,26 +2189,33 @@ function infoByIP(string $ipAddress, int $port = 0): array
$data = []; $data = [];
$contextOptions = []; $contextOptions = [];
if (!empty($bb_cfg['ip2country_settings']['api_token'])) { if (!empty(config()->get('ip2country_settings.api_token'))) {
$contextOptions['http'] = [ $contextOptions['http'] = [
'header' => "Authorization: Bearer " . $bb_cfg['ip2country_settings']['api_token'] . "\r\n" 'header' => "Authorization: Bearer " . config()->get('ip2country_settings.api_token') . "\r\n"
]; ];
} }
$context = stream_context_create($contextOptions); $context = stream_context_create($contextOptions);
$response = file_get_contents($bb_cfg['ip2country_settings']['endpoint'] . $ipAddress, context: $context);
if ($response !== false) { try {
$json = json_decode($response, true); $response = file_get_contents(config()->get('ip2country_settings.endpoint') . $ipAddress, context: $context);
if (is_array($json) && !empty($json)) { if ($response !== false) {
$data = [ $json = json_decode($response, true);
'ipVersion' => $json['ipVersion'],
'countryCode' => $json['countryCode'], if (is_array($json) && !empty($json)) {
'continent' => $json['continent'], $data = [
'continentCode' => $json['continentCode'] 'ipVersion' => $json['ipVersion'],
]; 'countryCode' => $json['countryCode'],
'continent' => $json['continent'],
'continentCode' => $json['continentCode']
];
}
} else {
bb_log("[FreeIPAPI] Failed to get IP info for: $ipAddress" . LOG_LF);
} }
} catch (Exception $e) {
bb_log("[FreeIPAPI] " . $e->getMessage() . LOG_LF);
} }
if (empty($data)) { if (empty($data)) {

View file

@ -86,9 +86,9 @@ function out(string $str, string $type = ''): void
* *
* @param string $cmd * @param string $cmd
* @param string|null $input * @param string|null $input
* @return void * @return int
*/ */
function runProcess(string $cmd, ?string $input = null): void function runProcess(string $cmd, ?string $input = null): int
{ {
$descriptorSpec = [ $descriptorSpec = [
0 => ['pipe', 'r'], 0 => ['pipe', 'r'],
@ -100,7 +100,7 @@ function runProcess(string $cmd, ?string $input = null): void
if (!is_resource($process)) { if (!is_resource($process)) {
out('- Could not start subprocess', 'error'); out('- Could not start subprocess', 'error');
return; return -1;
} }
// Write input if provided // Write input if provided
@ -124,7 +124,7 @@ function runProcess(string $cmd, ?string $input = null): void
fclose($pipes[1]); fclose($pipes[1]);
fclose($pipes[2]); fclose($pipes[2]);
proc_close($process); return proc_close($process);
} }
/** /**

View file

@ -39,9 +39,7 @@ function send_page($contents)
*/ */
function compress_output($contents) function compress_output($contents)
{ {
global $bb_cfg; if (config()->get('gzip_compress') && GZIP_OUTPUT_ALLOWED && !defined('NO_GZIP')) {
if ($bb_cfg['gzip_compress'] && GZIP_OUTPUT_ALLOWED && !defined('NO_GZIP')) {
if (UA_GZIP_SUPPORTED && strlen($contents) > 2000) { if (UA_GZIP_SUPPORTED && strlen($contents) > 2000) {
header('Content-Encoding: gzip'); header('Content-Encoding: gzip');
$contents = gzencode($contents, 1); $contents = gzencode($contents, 1);
@ -59,7 +57,7 @@ if (!defined('IN_AJAX')) {
} }
// Cookie params // Cookie params
$c = $bb_cfg['cookie_prefix']; $c = config()->get('cookie_prefix');
define('COOKIE_DATA', $c . 'data'); define('COOKIE_DATA', $c . 'data');
define('COOKIE_FORUM', $c . 'f'); define('COOKIE_FORUM', $c . 'f');
define('COOKIE_MARK', $c . 'mark_read'); define('COOKIE_MARK', $c . 'mark_read');
@ -85,16 +83,14 @@ define('COOKIE_MAX_TRACKS', 90);
*/ */
function bb_setcookie(string $name, mixed $val, int $lifetime = COOKIE_PERSIST, bool $httponly = false, bool $isRaw = false): void function bb_setcookie(string $name, mixed $val, int $lifetime = COOKIE_PERSIST, bool $httponly = false, bool $isRaw = false): void
{ {
global $bb_cfg;
$cookie = new \Josantonius\Cookie\Cookie( $cookie = new \Josantonius\Cookie\Cookie(
domain: $bb_cfg['cookie_domain'], domain: config()->get('cookie_domain'),
expires: $lifetime, expires: $lifetime,
httpOnly: $httponly, httpOnly: $httponly,
path: $bb_cfg['script_path'], path: config()->get('script_path'),
raw: $isRaw, raw: $isRaw,
sameSite: $bb_cfg['cookie_same_site'], sameSite: config()->get('cookie_same_site'),
secure: $bb_cfg['cookie_secure'] secure: config()->get('cookie_secure')
); );
if (!empty($val)) { if (!empty($val)) {
@ -275,14 +271,14 @@ define('PAGE_HEADER', INC_DIR . '/page_header.php');
define('PAGE_FOOTER', INC_DIR . '/page_footer.php'); define('PAGE_FOOTER', INC_DIR . '/page_footer.php');
define('CAT_URL', 'index.php?' . POST_CAT_URL . '='); define('CAT_URL', 'index.php?' . POST_CAT_URL . '=');
define('DL_URL', $bb_cfg['dl_url']); define('DL_URL', config()->get('dl_url'));
define('FORUM_URL', 'viewforum.php?' . POST_FORUM_URL . '='); define('FORUM_URL', 'viewforum.php?' . POST_FORUM_URL . '=');
define('GROUP_URL', 'group.php?' . POST_GROUPS_URL . '='); define('GROUP_URL', 'group.php?' . POST_GROUPS_URL . '=');
define('LOGIN_URL', $bb_cfg['login_url']); define('LOGIN_URL', config()->get('login_url'));
define('MODCP_URL', 'modcp.php?' . POST_FORUM_URL . '='); define('MODCP_URL', 'modcp.php?' . POST_FORUM_URL . '=');
define('PM_URL', $bb_cfg['pm_url']); define('PM_URL', config()->get('pm_url'));
define('POST_URL', 'viewtopic.php?' . POST_POST_URL . '='); define('POST_URL', 'viewtopic.php?' . POST_POST_URL . '=');
define('POSTING_URL', $bb_cfg['posting_url']); define('POSTING_URL', config()->get('posting_url'));
define('PROFILE_URL', 'profile.php?mode=viewprofile&amp;' . POST_USERS_URL . '='); define('PROFILE_URL', 'profile.php?mode=viewprofile&amp;' . POST_USERS_URL . '=');
define('BONUS_URL', 'profile.php?mode=bonus'); define('BONUS_URL', 'profile.php?mode=bonus');
define('TOPIC_URL', 'viewtopic.php?' . POST_TOPIC_URL . '='); define('TOPIC_URL', 'viewtopic.php?' . POST_TOPIC_URL . '=');
@ -378,10 +374,15 @@ function make_url(string $path = ''): string
*/ */
require_once INC_DIR . '/functions.php'; require_once INC_DIR . '/functions.php';
$bb_cfg = array_merge(bb_get_config(BB_CONFIG), $bb_cfg); // Merge database configuration with base configuration using singleton
// bb_cfg deprecated, but kept for compatibility with non-adapted code
config()->merge(bb_get_config(BB_CONFIG));
$bb_cfg = config()->all();
// wordCensor deprecated, but kept for compatibility with non-adapted code
$wordCensor = censor();
$log_action = new TorrentPier\Legacy\LogAction(); $log_action = new TorrentPier\Legacy\LogAction();
$wordCensor = new TorrentPier\Censor();
$html = new TorrentPier\Legacy\Common\Html(); $html = new TorrentPier\Legacy\Common\Html();
$user = new TorrentPier\Legacy\Common\User(); $user = new TorrentPier\Legacy\Common\User();
@ -396,7 +397,7 @@ if (
!is_file(CRON_RUNNING) && !is_file(CRON_RUNNING) &&
(TorrentPier\Helpers\CronHelper::isEnabled() || defined('START_CRON')) (TorrentPier\Helpers\CronHelper::isEnabled() || defined('START_CRON'))
) { ) {
if (TIMENOW - $bb_cfg['cron_last_check'] > $bb_cfg['cron_check_interval']) { if (TIMENOW - config()->get('cron_last_check') > config()->get('cron_check_interval')) {
/** Update cron_last_check */ /** Update cron_last_check */
bb_update_config(['cron_last_check' => TIMENOW + 10]); bb_update_config(['cron_last_check' => TIMENOW + 10]);
@ -436,8 +437,8 @@ if (
/** /**
* Exit if board is disabled via trigger * Exit if board is disabled via trigger
*/ */
if (($bb_cfg['board_disable'] || is_file(BB_DISABLED)) && !defined('IN_ADMIN') && !defined('IN_AJAX') && !defined('IN_LOGIN')) { if ((config()->get('board_disable') || is_file(BB_DISABLED)) && !defined('IN_ADMIN') && !defined('IN_AJAX') && !defined('IN_LOGIN')) {
if ($bb_cfg['board_disable']) { if (config()->get('board_disable')) {
// admin lock // admin lock
send_no_cache_headers(); send_no_cache_headers();
bb_die('BOARD_DISABLE', 503); bb_die('BOARD_DISABLE', 503);

View file

@ -116,7 +116,7 @@ if (!$online['userlist']) {
$total_online = $logged_online + $guests_online; $total_online = $logged_online + $guests_online;
if ($total_online > $bb_cfg['record_online_users']) { if ($total_online > config()->get('record_online_users')) {
bb_update_config([ bb_update_config([
'record_online_users' => $total_online, 'record_online_users' => $total_online,
'record_online_date' => TIMENOW 'record_online_date' => TIMENOW

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
global $bb_cfg, $userdata, $template, $DBS, $lang; global $userdata, $template, $lang;
if (!empty($template)) { if (!empty($template)) {
$birthday_tp = ((string)bb_date(TIMENOW, 'd.m', false) === '04.04') ? '&nbsp;|&nbsp;&#127881;&#127856;&#128154;' : ''; $birthday_tp = ((string)bb_date(TIMENOW, 'd.m', false) === '04.04') ? '&nbsp;|&nbsp;&#127881;&#127856;&#128154;' : '';
@ -29,7 +29,7 @@ if (!empty($template)) {
$show_dbg_info = (DBG_USER && !(isset($_GET['pane']) && $_GET['pane'] == 'left')); $show_dbg_info = (DBG_USER && !(isset($_GET['pane']) && $_GET['pane'] == 'left'));
if (!$bb_cfg['gzip_compress']) { if (!config()->get('gzip_compress')) {
flush(); flush();
} }
@ -37,21 +37,25 @@ if ($show_dbg_info) {
$gen_time = utime() - TIMESTART; $gen_time = utime() - TIMESTART;
$gen_time_txt = sprintf('%.3f', $gen_time); $gen_time_txt = sprintf('%.3f', $gen_time);
$gzip_text = UA_GZIP_SUPPORTED ? "{$lang['GZIP_COMPRESSION']}: " : "<s>{$lang['GZIP_COMPRESSION']}:</s> "; $gzip_text = UA_GZIP_SUPPORTED ? "{$lang['GZIP_COMPRESSION']}: " : "<s>{$lang['GZIP_COMPRESSION']}:</s> ";
$gzip_text .= $bb_cfg['gzip_compress'] ? $lang['ON'] : $lang['OFF']; $gzip_text .= config()->get('gzip_compress') ? $lang['ON'] : $lang['OFF'];
$stat = '[&nbsp; ' . $lang['EXECUTION_TIME'] . " $gen_time_txt " . $lang['SEC']; $stat = '[&nbsp; ' . $lang['EXECUTION_TIME'] . " $gen_time_txt " . $lang['SEC'];
if (!empty($DBS)) { // Get database statistics from the new system
$sql_t = $DBS->sql_timetotal; try {
$main_db = \TorrentPier\Database\DatabaseFactory::getInstance('db');
$sql_t = $main_db->sql_timetotal;
$sql_time_txt = ($sql_t) ? sprintf('%.3f ' . $lang['SEC'] . ' (%d%%) &middot; ', $sql_t, round($sql_t * 100 / $gen_time)) : ''; $sql_time_txt = ($sql_t) ? sprintf('%.3f ' . $lang['SEC'] . ' (%d%%) &middot; ', $sql_t, round($sql_t * 100 / $gen_time)) : '';
$num_q = $DBS->num_queries; $num_q = $main_db->num_queries;
$stat .= " &nbsp;|&nbsp; {$DBS->get_db_obj()->engine}: {$sql_time_txt}{$num_q} " . $lang['QUERIES']; $stat .= " &nbsp;|&nbsp; {$main_db->engine}: {$sql_time_txt}{$num_q} " . $lang['QUERIES'];
} catch (\Exception $e) {
// Skip database stats if not available
} }
$stat .= " &nbsp;|&nbsp; $gzip_text"; $stat .= " &nbsp;|&nbsp; $gzip_text";
$stat .= ' &nbsp;|&nbsp; ' . $lang['MEMORY']; $stat .= ' &nbsp;|&nbsp; ' . $lang['MEMORY'];
$stat .= humn_size($bb_cfg['mem_on_start'], 2) . ' / '; $stat .= humn_size(config()->get('mem_on_start'), 2) . ' / ';
$stat .= humn_size(sys('mem_peak'), 2) . ' / '; $stat .= humn_size(sys('mem_peak'), 2) . ' / ';
$stat .= humn_size(sys('mem'), 2); $stat .= humn_size(sys('mem'), 2);
@ -83,7 +87,7 @@ echo '
if (defined('REQUESTED_PAGE') && !defined('DISABLE_CACHING_OUTPUT')) { if (defined('REQUESTED_PAGE') && !defined('DISABLE_CACHING_OUTPUT')) {
if (IS_GUEST === true) { if (IS_GUEST === true) {
caching_output(true, 'store', REQUESTED_PAGE . '_guest_' . $bb_cfg['default_lang']); caching_output(true, 'store', REQUESTED_PAGE . '_guest_' . config()->get('default_lang'));
} }
} }

View file

@ -64,14 +64,21 @@ if (!defined('BB_ROOT')) {
<?php <?php
if (!empty($_COOKIE['explain'])) { if (!empty($_COOKIE['explain'])) {
foreach ($DBS->srv as $srv_name => $db_obj) { // Get all database server instances from the new DatabaseFactory
if (!empty($db_obj->do_explain)) { $server_names = \TorrentPier\Database\DatabaseFactory::getServerNames();
$db_obj->explain('display'); foreach ($server_names as $srv_name) {
try {
$db_obj = \TorrentPier\Database\DatabaseFactory::getInstance($srv_name);
if (!empty($db_obj->do_explain)) {
$db_obj->explain('display');
}
} catch (\Exception $e) {
// Skip if server not available
} }
} }
} }
$sql_log = !empty($_COOKIE['sql_log']) ? \TorrentPier\Dev::getSqlLog() : false; $sql_log = !empty($_COOKIE['sql_log']) ? dev()->getSqlDebugLog() : false;
if ($sql_log) { if ($sql_log) {
echo '<div class="sqlLog" id="sqlLog">' . $sql_log . '</div><!-- / sqlLog --><br clear="all" />'; echo '<div class="sqlLog" id="sqlLog">' . $sql_log . '</div><!-- / sqlLog --><br clear="all" />';

View file

@ -16,7 +16,7 @@ if (defined('PAGE_HEADER_SENT')) {
} }
// Parse and show the overall page header // Parse and show the overall page header
global $page_cfg, $userdata, $user, $ads, $bb_cfg, $template, $lang, $images; global $page_cfg, $userdata, $user, $ads, $template, $lang, $images;
$logged_in = (int)!empty($userdata['session_logged_in']); $logged_in = (int)!empty($userdata['session_logged_in']);
@ -52,7 +52,7 @@ if (defined('SHOW_ONLINE') && SHOW_ONLINE) {
'TOTAL_USERS_ONLINE' => ${$online_list}['stat'], 'TOTAL_USERS_ONLINE' => ${$online_list}['stat'],
'LOGGED_IN_USER_LIST' => ${$online_list}['userlist'], 'LOGGED_IN_USER_LIST' => ${$online_list}['userlist'],
'USERS_ONLINE_COUNTS' => ${$online_list}['cnt'], 'USERS_ONLINE_COUNTS' => ${$online_list}['cnt'],
'RECORD_USERS' => sprintf($lang['RECORD_ONLINE_USERS'], $bb_cfg['record_online_users'], bb_date($bb_cfg['record_online_date'])), 'RECORD_USERS' => sprintf($lang['RECORD_ONLINE_USERS'], config()->get('record_online_users'), bb_date(config()->get('record_online_date'))),
]); ]);
} }
@ -117,17 +117,18 @@ $template->assign_vars([
'USER_HIDE_CAT' => (BB_SCRIPT == 'index'), 'USER_HIDE_CAT' => (BB_SCRIPT == 'index'),
'USER_LANG' => $userdata['user_lang'], 'USER_LANG' => $userdata['user_lang'],
'USER_LANG_DIRECTION' => (isset($bb_cfg['lang'][$userdata['user_lang']]['rtl']) && $bb_cfg['lang'][$userdata['user_lang']]['rtl'] === true) ? 'rtl' : 'ltr',
'INCLUDE_BBCODE_JS' => !empty($page_cfg['include_bbcode_js']), 'INCLUDE_BBCODE_JS' => !empty($page_cfg['include_bbcode_js']),
'USER_OPTIONS_JS' => IS_GUEST ? '{}' : json_encode($user->opt_js, JSON_THROW_ON_ERROR), 'USER_OPTIONS_JS' => IS_GUEST ? '{}' : json_encode($user->opt_js, JSON_THROW_ON_ERROR),
'USE_TABLESORTER' => !empty($page_cfg['use_tablesorter']), 'USE_TABLESORTER' => !empty($page_cfg['use_tablesorter']),
'ALLOW_ROBOTS' => !$bb_cfg['board_disable'] && (!isset($page_cfg['allow_robots']) || $page_cfg['allow_robots'] === true), 'ALLOW_ROBOTS' => !config()->get('board_disable') && (!isset($page_cfg['allow_robots']) || $page_cfg['allow_robots'] === true),
'META_DESCRIPTION' => !empty($page_cfg['meta_description']) ? trim(htmlCHR($page_cfg['meta_description'])) : '', 'META_DESCRIPTION' => !empty($page_cfg['meta_description']) ? trim(htmlCHR($page_cfg['meta_description'])) : '',
'SITENAME' => $bb_cfg['sitename'], 'SITENAME' => config()->get('sitename'),
'U_INDEX' => BB_ROOT . 'index.php', 'U_INDEX' => BB_ROOT . 'index.php',
'T_INDEX' => sprintf($lang['FORUM_INDEX'], $bb_cfg['sitename']), 'T_INDEX' => sprintf($lang['FORUM_INDEX'], config()->get('sitename')),
'IS_GUEST' => IS_GUEST, 'IS_GUEST' => IS_GUEST,
'IS_USER' => IS_USER, 'IS_USER' => IS_USER,
@ -138,9 +139,9 @@ $template->assign_vars([
'FORUM_PATH' => FORUM_PATH, 'FORUM_PATH' => FORUM_PATH,
'FULL_URL' => FULL_URL, 'FULL_URL' => FULL_URL,
'CURRENT_TIME' => sprintf($lang['CURRENT_TIME'], bb_date(TIMENOW, $bb_cfg['last_visit_date_format'], false)), 'CURRENT_TIME' => sprintf($lang['CURRENT_TIME'], bb_date(TIMENOW, config()->get('last_visit_date_format'), false)),
'S_TIMEZONE' => preg_replace('/\(.*?\)/', '', sprintf($lang['ALL_TIMES'], $lang['TZ'][str_replace(',', '.', (float)$bb_cfg['board_timezone'])])), 'S_TIMEZONE' => preg_replace('/\(.*?\)/', '', sprintf($lang['ALL_TIMES'], $lang['TZ'][str_replace(',', '.', (float)config()->get('board_timezone'))])),
'BOARD_TIMEZONE' => $bb_cfg['board_timezone'], 'BOARD_TIMEZONE' => config()->get('board_timezone'),
'PM_INFO' => $pm_info, 'PM_INFO' => $pm_info,
'PRIVMSG_IMG' => $icon_pm, 'PRIVMSG_IMG' => $icon_pm,
@ -151,7 +152,7 @@ $template->assign_vars([
'THIS_USER' => profile_url($userdata), 'THIS_USER' => profile_url($userdata),
'THIS_AVATAR' => get_avatar($userdata['user_id'], $userdata['avatar_ext_id'], !bf($userdata['user_opt'], 'user_opt', 'dis_avatar')), 'THIS_AVATAR' => get_avatar($userdata['user_id'], $userdata['avatar_ext_id'], !bf($userdata['user_opt'], 'user_opt', 'dis_avatar')),
'SHOW_LOGIN_LINK' => !defined('IN_LOGIN'), 'SHOW_LOGIN_LINK' => !defined('IN_LOGIN'),
'AUTOLOGIN_DISABLED' => !$bb_cfg['allow_autologin'], 'AUTOLOGIN_DISABLED' => !config()->get('allow_autologin'),
'S_LOGIN_ACTION' => LOGIN_URL, 'S_LOGIN_ACTION' => LOGIN_URL,
'U_CUR_DOWNLOADS' => PROFILE_URL . $userdata['user_id'], 'U_CUR_DOWNLOADS' => PROFILE_URL . $userdata['user_id'],
@ -167,11 +168,11 @@ $template->assign_vars([
'U_REGISTER' => 'profile.php?mode=register', 'U_REGISTER' => 'profile.php?mode=register',
'U_SEARCH' => 'search.php', 'U_SEARCH' => 'search.php',
'U_SEND_PASSWORD' => "profile.php?mode=sendpassword", 'U_SEND_PASSWORD' => "profile.php?mode=sendpassword",
'U_TERMS' => $bb_cfg['terms_and_conditions_url'], 'U_TERMS' => config()->get('terms_and_conditions_url'),
'U_TRACKER' => 'tracker.php', 'U_TRACKER' => 'tracker.php',
'SHOW_SIDEBAR1' => !empty($bb_cfg['page']['show_sidebar1'][BB_SCRIPT]) || $bb_cfg['show_sidebar1_on_every_page'], 'SHOW_SIDEBAR1' => !empty(config()->get('page.show_sidebar1')[BB_SCRIPT]) || config()->get('show_sidebar1_on_every_page'),
'SHOW_SIDEBAR2' => !empty($bb_cfg['page']['show_sidebar2'][BB_SCRIPT]) || $bb_cfg['show_sidebar2_on_every_page'], 'SHOW_SIDEBAR2' => !empty(config()->get('page.show_sidebar2')[BB_SCRIPT]) || config()->get('show_sidebar2_on_every_page'),
'HTML_AGREEMENT' => LANG_DIR . 'html/user_agreement.html', 'HTML_AGREEMENT' => LANG_DIR . 'html/user_agreement.html',
'HTML_COPYRIGHT' => LANG_DIR . 'html/copyright_holders.html', 'HTML_COPYRIGHT' => LANG_DIR . 'html/copyright_holders.html',
@ -185,11 +186,11 @@ $template->assign_vars([
'DOWNLOAD_URL' => BB_ROOT . DL_URL, 'DOWNLOAD_URL' => BB_ROOT . DL_URL,
'FORUM_URL' => BB_ROOT . FORUM_URL, 'FORUM_URL' => BB_ROOT . FORUM_URL,
'GROUP_URL' => BB_ROOT . GROUP_URL, 'GROUP_URL' => BB_ROOT . GROUP_URL,
'LOGIN_URL' => $bb_cfg['login_url'], 'LOGIN_URL' => config()->get('login_url'),
'NEWEST_URL' => '&amp;view=newest#newest', 'NEWEST_URL' => '&amp;view=newest#newest',
'PM_URL' => $bb_cfg['pm_url'], 'PM_URL' => config()->get('pm_url'),
'POST_URL' => BB_ROOT . POST_URL, 'POST_URL' => BB_ROOT . POST_URL,
'POSTING_URL' => $bb_cfg['posting_url'], 'POSTING_URL' => config()->get('posting_url'),
'PROFILE_URL' => BB_ROOT . PROFILE_URL, 'PROFILE_URL' => BB_ROOT . PROFILE_URL,
'BONUS_URL' => BB_ROOT . BONUS_URL, 'BONUS_URL' => BB_ROOT . BONUS_URL,
'TOPIC_URL' => BB_ROOT . TOPIC_URL, 'TOPIC_URL' => BB_ROOT . TOPIC_URL,
@ -208,7 +209,7 @@ $template->assign_vars([
'U_WATCHED_TOPICS' => 'profile.php?mode=watch' 'U_WATCHED_TOPICS' => 'profile.php?mode=watch'
]); ]);
if (!empty($bb_cfg['page']['show_torhelp'][BB_SCRIPT]) && !empty($userdata['torhelp'])) { if (!empty(config()->get('page.show_torhelp')[BB_SCRIPT]) && !empty($userdata['torhelp'])) {
$ignore_time = !empty($_COOKIE['torhelp']) ? (int)$_COOKIE['torhelp'] : 0; $ignore_time = !empty($_COOKIE['torhelp']) ? (int)$_COOKIE['torhelp'] : 0;
if (TIMENOW > $ignore_time) { if (TIMENOW > $ignore_time) {
@ -249,6 +250,6 @@ $template->pparse('page_header');
define('PAGE_HEADER_SENT', true); define('PAGE_HEADER_SENT', true);
if (!$bb_cfg['gzip_compress']) { if (!config()->get('gzip_compress')) {
flush(); flush();
} }

View file

@ -21,12 +21,12 @@ $dl_users_div_style_overflow = "padding: 6px; height: $dl_users_overflow_div_hei
$template->assign_vars(['DL_BUTTONS' => false]); $template->assign_vars(['DL_BUTTONS' => false]);
$count_mode = ($bb_cfg['bt_dl_list_only_count'] && !(@$_GET['dl'] === 'names')); $count_mode = (config()->get('bt_dl_list_only_count') && !(@$_GET['dl'] === 'names'));
$have_dl_buttons_enabled = ($bb_cfg['bt_show_dl_but_will'] || $bb_cfg['bt_show_dl_but_down'] || $bb_cfg['bt_show_dl_but_compl'] || $bb_cfg['bt_show_dl_but_cancel']); $have_dl_buttons_enabled = (config()->get('bt_show_dl_but_will') || config()->get('bt_show_dl_but_down') || config()->get('bt_show_dl_but_compl') || config()->get('bt_show_dl_but_cancel'));
$dl_topic = ($t_data['topic_dl_type'] == TOPIC_DL_TYPE_DL && !($bb_cfg['bt_dl_list_only_1st_page'] && $start)); $dl_topic = ($t_data['topic_dl_type'] == TOPIC_DL_TYPE_DL && !(config()->get('bt_dl_list_only_1st_page') && $start));
$show_dl_list = ($dl_topic && ($bb_cfg['bt_show_dl_list'] || ($bb_cfg['allow_dl_list_names_mode'] && @$_GET['dl'] === 'names'))); $show_dl_list = ($dl_topic && (config()->get('bt_show_dl_list') || (config()->get('allow_dl_list_names_mode') && @$_GET['dl'] === 'names')));
$show_dl_buttons = (!IS_GUEST && $dl_topic && $bb_cfg['bt_show_dl_list_buttons']); $show_dl_buttons = (!IS_GUEST && $dl_topic && config()->get('bt_show_dl_list_buttons'));
// link to clear DL-List // link to clear DL-List
$template->assign_vars(['S_DL_DELETE' => false]); $template->assign_vars(['S_DL_DELETE' => false]);
@ -99,7 +99,7 @@ if ($show_dl_list) {
]); ]);
} }
} }
} elseif ($bb_cfg['bt_show_dl_list_buttons'] && $have_dl_buttons_enabled) { } elseif (config()->get('bt_show_dl_list_buttons') && $have_dl_buttons_enabled) {
$template->assign_block_vars('dl_list_none', []); $template->assign_block_vars('dl_list_none', []);
} }
} }
@ -107,10 +107,10 @@ if ($show_dl_list) {
if ($show_dl_buttons) { if ($show_dl_buttons) {
$template->assign_vars([ $template->assign_vars([
'DL_BUTTONS' => true, 'DL_BUTTONS' => true,
'DL_BUT_WILL' => $bb_cfg['bt_show_dl_but_will'], 'DL_BUT_WILL' => config()->get('bt_show_dl_but_will'),
'DL_BUT_DOWN' => $bb_cfg['bt_show_dl_but_down'], 'DL_BUT_DOWN' => config()->get('bt_show_dl_but_down'),
'DL_BUT_COMPL' => $bb_cfg['bt_show_dl_but_compl'], 'DL_BUT_COMPL' => config()->get('bt_show_dl_but_compl'),
'DL_BUT_CANCEL' => $bb_cfg['bt_show_dl_but_cancel'] 'DL_BUT_CANCEL' => config()->get('bt_show_dl_but_cancel')
]); ]);
$dl_hidden_fields = ' $dl_hidden_fields = '

View file

@ -14,9 +14,9 @@ if (!defined('BB_ROOT')) {
$user_id = $userdata['user_id']; $user_id = $userdata['user_id'];
$user_points = $userdata['user_points']; $user_points = $userdata['user_points'];
if ($bb_cfg['seed_bonus_enabled'] && $bb_cfg['bonus_upload'] && $bb_cfg['bonus_upload_price']) { if (config()->get('seed_bonus_enabled') && config()->get('bonus_upload') && config()->get('bonus_upload_price')) {
$upload_row = unserialize($bb_cfg['bonus_upload']); $upload_row = unserialize(config()->get('bonus_upload'));
$price_row = unserialize($bb_cfg['bonus_upload_price']); $price_row = unserialize(config()->get('bonus_upload_price'));
} else { } else {
bb_die($lang['EXCHANGE_NOT']); bb_die($lang['EXCHANGE_NOT']);
} }

View file

@ -12,7 +12,7 @@ if (!defined('BB_ROOT')) {
} }
// Is send through board enabled? No, return to index // Is send through board enabled? No, return to index
if (!$bb_cfg['board_email_form']) { if (!config()->get('board_email_form')) {
redirect('index.php'); redirect('index.php');
} }

View file

@ -16,7 +16,7 @@ array_deep($_POST, 'trim');
set_die_append_msg(); set_die_append_msg();
if (IS_ADMIN) { if (IS_ADMIN) {
$bb_cfg['reg_email_activation'] = false; config()->set('reg_email_activation', false);
$new_user = (int)request_var('admin', ''); $new_user = (int)request_var('admin', '');
if ($new_user) { if ($new_user) {
@ -50,17 +50,17 @@ switch ($mode) {
if (!IS_ADMIN) { if (!IS_ADMIN) {
// IP limit // IP limit
if ($bb_cfg['unique_ip']) { if (config()->get('unique_ip')) {
if ($users = DB()->fetch_row("SELECT user_id, username FROM " . BB_USERS . " WHERE user_reg_ip = '" . USER_IP . "' LIMIT 1")) { if ($users = DB()->fetch_row("SELECT user_id, username FROM " . BB_USERS . " WHERE user_reg_ip = '" . USER_IP . "' LIMIT 1")) {
bb_die(sprintf($lang['ALREADY_REG_IP'], '<a href="' . PROFILE_URL . $users['user_id'] . '"><b>' . $users['username'] . '</b></a>', $bb_cfg['tech_admin_email'])); bb_die(sprintf($lang['ALREADY_REG_IP'], '<a href="' . PROFILE_URL . $users['user_id'] . '"><b>' . $users['username'] . '</b></a>', config()->get('tech_admin_email')));
} }
} }
// Disabling registration // Disabling registration
if ($bb_cfg['new_user_reg_disabled'] || ($bb_cfg['reg_email_activation'] && !$bb_cfg['emailer']['enabled'])) { if (config()->get('new_user_reg_disabled') || (config()->get('reg_email_activation') && !config()->get('emailer.enabled'))) {
bb_die($lang['NEW_USER_REG_DISABLED']); bb_die($lang['NEW_USER_REG_DISABLED']);
} // Time limit } // Time limit
elseif ($bb_cfg['new_user_reg_restricted']) { elseif (config()->get('new_user_reg_restricted')) {
if (in_array(date('G'), $bb_cfg['new_user_reg_interval'], true)) { if (in_array(date('G'), config()->get('new_user_reg_interval'), true)) {
bb_die($lang['REGISTERED_IN_TIME']); bb_die($lang['REGISTERED_IN_TIME']);
} }
} }
@ -83,8 +83,8 @@ switch ($mode) {
'user_password' => '', 'user_password' => '',
'user_email' => '', 'user_email' => '',
'invite_code' => '', 'invite_code' => '',
'user_timezone' => $bb_cfg['board_timezone'], 'user_timezone' => config()->get('board_timezone'),
'user_lang' => $bb_cfg['default_lang'], 'user_lang' => config()->get('default_lang'),
'user_opt' => 0, 'user_opt' => 0,
'avatar_ext_id' => 0 'avatar_ext_id' => 0
]; ];
@ -101,13 +101,13 @@ switch ($mode) {
// field => can_edit // field => can_edit
$profile_fields = [ $profile_fields = [
'user_active' => IS_ADMIN, 'user_active' => IS_ADMIN,
'username' => (IS_ADMIN || $bb_cfg['allow_namechange']) && !IN_DEMO_MODE, 'username' => (IS_ADMIN || config()->get('allow_namechange')) && !IN_DEMO_MODE,
'user_password' => !IN_DEMO_MODE, 'user_password' => !IN_DEMO_MODE,
'user_email' => !IN_DEMO_MODE, // should be after user_password 'user_email' => !IN_DEMO_MODE, // should be after user_password
'user_lang' => $bb_cfg['allow_change']['language'], 'user_lang' => config()->get('allow_change.language'),
'user_gender' => $bb_cfg['gender'], 'user_gender' => config()->get('gender'),
'user_birthday' => $bb_cfg['birthday_enabled'], 'user_birthday' => config()->get('birthday_enabled'),
'user_timezone' => $bb_cfg['allow_change']['timezone'], 'user_timezone' => config()->get('allow_change.timezone'),
'user_opt' => true, 'user_opt' => true,
'avatar_ext_id' => true, 'avatar_ext_id' => true,
'user_icq' => true, 'user_icq' => true,
@ -142,6 +142,7 @@ switch ($mode) {
if (!$pr_data = DB()->fetch_row($sql)) { if (!$pr_data = DB()->fetch_row($sql)) {
bb_die($lang['PROFILE_NOT_FOUND']); bb_die($lang['PROFILE_NOT_FOUND']);
} }
$pr_data['user_birthday'] = $pr_data['user_birthday']->format('Y-m-d');
if (IN_DEMO_MODE && isset($_COOKIE['user_lang'])) { if (IN_DEMO_MODE && isset($_COOKIE['user_lang'])) {
$pr_data['user_lang'] = $_COOKIE['user_lang']; $pr_data['user_lang'] = $_COOKIE['user_lang'];
} }
@ -152,7 +153,7 @@ switch ($mode) {
} }
// Captcha // Captcha
$need_captcha = ($mode == 'register' && !IS_ADMIN && !$bb_cfg['captcha']['disabled']); $need_captcha = ($mode == 'register' && !IS_ADMIN && !config()->get('captcha.disabled'));
if ($submit) { if ($submit) {
if ($need_captcha && !bb_captcha('check')) { if ($need_captcha && !bb_captcha('check')) {
@ -203,12 +204,13 @@ foreach ($profile_fields as $field => $can_edit) {
* Invite code (reg) * Invite code (reg)
*/ */
case 'invite_code': case 'invite_code':
if ($bb_cfg['invites_system']['enabled']) { if (config()->get('invites_system.enabled')) {
$invite_code = $_POST['invite_code'] ?? ''; $invite_code = $_POST['invite_code'] ?? '';
if ($submit) { if ($submit) {
if (isset($bb_cfg['invites_system']['codes'][$invite_code])) { $inviteCodes = config()->get('invites_system.codes');
if ($bb_cfg['invites_system']['codes'][$invite_code] !== 'permanent') { if (isset($inviteCodes[$invite_code])) {
if (TIMENOW > strtotime($bb_cfg['invites_system']['codes'][$invite_code])) { if ($inviteCodes[$invite_code] !== 'permanent') {
if (TIMENOW > strtotime($inviteCodes[$invite_code])) {
$errors[] = $lang['INVITE_EXPIRED']; $errors[] = $lang['INVITE_EXPIRED'];
} }
} }
@ -264,7 +266,7 @@ foreach ($profile_fields as $field => $can_edit) {
} }
$db_data['user_email'] = $email; $db_data['user_email'] = $email;
} elseif ($email != $pr_data['user_email']) { } elseif ($email != $pr_data['user_email']) {
if ($bb_cfg['email_change_disabled'] && !$adm_edit && !IS_ADMIN) { if (config()->get('email_change_disabled') && !$adm_edit && !IS_ADMIN) {
$errors[] = $lang['EMAIL_CHANGING_DISABLED']; $errors[] = $lang['EMAIL_CHANGING_DISABLED'];
} }
if (!$cur_pass_valid) { if (!$cur_pass_valid) {
@ -273,7 +275,7 @@ foreach ($profile_fields as $field => $can_edit) {
if (!$errors and $err = \TorrentPier\Validate::email($email)) { if (!$errors and $err = \TorrentPier\Validate::email($email)) {
$errors[] = $err; $errors[] = $err;
} }
if ($bb_cfg['reg_email_activation']) { if (config()->get('reg_email_activation')) {
$pr_data['user_active'] = 0; $pr_data['user_active'] = 0;
$db_data['user_active'] = 0; $db_data['user_active'] = 0;
} }
@ -336,10 +338,10 @@ foreach ($profile_fields as $field => $can_edit) {
if (!empty($birthday_date['year'])) { if (!empty($birthday_date['year'])) {
if (strtotime($user_birthday) >= TIMENOW) { if (strtotime($user_birthday) >= TIMENOW) {
$errors[] = $lang['WRONG_BIRTHDAY_FORMAT']; $errors[] = $lang['WRONG_BIRTHDAY_FORMAT'];
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] > $bb_cfg['birthday_max_age']) { } elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] > config()->get('birthday_max_age')) {
$errors[] = sprintf($lang['BIRTHDAY_TO_HIGH'], $bb_cfg['birthday_max_age']); $errors[] = sprintf($lang['BIRTHDAY_TO_HIGH'], config()->get('birthday_max_age'));
} elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] < $bb_cfg['birthday_min_age']) { } elseif (bb_date(TIMENOW, 'Y', false) - $birthday_date['year'] < config()->get('birthday_min_age')) {
$errors[] = sprintf($lang['BIRTHDAY_TO_LOW'], $bb_cfg['birthday_min_age']); $errors[] = sprintf($lang['BIRTHDAY_TO_LOW'], config()->get('birthday_min_age'));
} }
} }
@ -357,16 +359,16 @@ foreach ($profile_fields as $field => $can_edit) {
$update_user_opt = [ $update_user_opt = [
# 'user_opt_name' => ($reg_mode) ? #reg_value : #in_login_change # 'user_opt_name' => ($reg_mode) ? #reg_value : #in_login_change
'user_viewemail' => $reg_mode ? false : (IS_ADMIN || $bb_cfg['show_email_visibility_settings']), 'user_viewemail' => $reg_mode ? false : (IS_ADMIN || config()->get('show_email_visibility_settings')),
'user_viewonline' => $reg_mode ? false : true, 'user_viewonline' => $reg_mode ? false : true,
'user_notify' => $reg_mode ? true : true, 'user_notify' => $reg_mode ? true : true,
'user_notify_pm' => $reg_mode ? true : $bb_cfg['pm_notify_enabled'], 'user_notify_pm' => $reg_mode ? true : config()->get('pm_notify_enabled'),
'user_porn_forums' => $reg_mode ? false : true, 'user_porn_forums' => $reg_mode ? false : true,
'user_dls' => $reg_mode ? false : true, 'user_dls' => $reg_mode ? false : true,
'user_callseed' => $reg_mode ? true : true, 'user_callseed' => $reg_mode ? true : true,
'user_retracker' => $reg_mode ? true : true, 'user_retracker' => $reg_mode ? true : true,
'user_hide_torrent_client' => $reg_mode ? true : true, 'user_hide_torrent_client' => $reg_mode ? true : true,
'user_hide_peer_country' => $reg_mode ? true : $bb_cfg['ip2country_settings']['enabled'], 'user_hide_peer_country' => $reg_mode ? true : config()->get('ip2country_settings.enabled'),
'user_hide_peer_username' => $reg_mode ? false : true, 'user_hide_peer_username' => $reg_mode ? false : true,
]; ];
@ -390,7 +392,7 @@ foreach ($profile_fields as $field => $can_edit) {
if ($submit && !bf($pr_data['user_opt'], 'user_opt', 'dis_avatar')) { if ($submit && !bf($pr_data['user_opt'], 'user_opt', 'dis_avatar')) {
// Integration with MonsterID // Integration with MonsterID
if (empty($_FILES['avatar']['name']) && !isset($_POST['delete_avatar']) && isset($_POST['use_monster_avatar'])) { if (empty($_FILES['avatar']['name']) && !isset($_POST['delete_avatar']) && isset($_POST['use_monster_avatar'])) {
$monsterAvatar = new Arokettu\MonsterID\Monster($pr_data['user_email'], $bb_cfg['avatars']['max_height']); $monsterAvatar = new Arokettu\MonsterID\Monster($pr_data['user_email'], config()->get('avatars.max_height'));
$tempAvatar = tmpfile(); $tempAvatar = tmpfile();
$tempAvatarPath = stream_get_meta_data($tempAvatar)['uri']; $tempAvatarPath = stream_get_meta_data($tempAvatar)['uri'];
$monsterAvatar->writeToStream($tempAvatar); $monsterAvatar->writeToStream($tempAvatar);
@ -412,10 +414,10 @@ foreach ($profile_fields as $field => $can_edit) {
delete_avatar($pr_data['user_id'], $pr_data['avatar_ext_id']); delete_avatar($pr_data['user_id'], $pr_data['avatar_ext_id']);
$pr_data['avatar_ext_id'] = 0; $pr_data['avatar_ext_id'] = 0;
$db_data['avatar_ext_id'] = 0; $db_data['avatar_ext_id'] = 0;
} elseif (!empty($_FILES['avatar']['name']) && $bb_cfg['avatars']['up_allowed']) { } elseif (!empty($_FILES['avatar']['name']) && config()->get('avatars.up_allowed')) {
$upload = new TorrentPier\Legacy\Common\Upload(); $upload = new TorrentPier\Legacy\Common\Upload();
if ($upload->init($bb_cfg['avatars'], $_FILES['avatar'], !isset($_POST['use_monster_avatar'])) and $upload->store('avatar', $pr_data)) { if ($upload->init(config()->getSection('avatars'), $_FILES['avatar'], !isset($_POST['use_monster_avatar'])) and $upload->store('avatar', $pr_data)) {
$pr_data['avatar_ext_id'] = $upload->file_ext_id; $pr_data['avatar_ext_id'] = $upload->file_ext_id;
$db_data['avatar_ext_id'] = (int)$upload->file_ext_id; $db_data['avatar_ext_id'] = (int)$upload->file_ext_id;
} else { } else {
@ -423,7 +425,7 @@ foreach ($profile_fields as $field => $can_edit) {
} }
} }
} }
$tp_data['AVATARS_MAX_SIZE'] = humn_size($bb_cfg['avatars']['max_size']); $tp_data['AVATARS_MAX_SIZE'] = humn_size(config()->get('avatars.max_size'));
break; break;
/** /**
@ -484,7 +486,7 @@ foreach ($profile_fields as $field => $can_edit) {
if ($submit && $sig != $pr_data['user_sig']) { if ($submit && $sig != $pr_data['user_sig']) {
$sig = prepare_message($sig); $sig = prepare_message($sig);
if (mb_strlen($sig, DEFAULT_CHARSET) > $bb_cfg['max_sig_chars']) { if (mb_strlen($sig, DEFAULT_CHARSET) > config()->get('max_sig_chars')) {
$errors[] = $lang['SIGNATURE_TOO_LONG']; $errors[] = $lang['SIGNATURE_TOO_LONG'];
} elseif (preg_match('#<(a|b|i|u|table|tr|td|img) #i', $sig) || preg_match('#(href|src|target|title)=#i', $sig)) { } elseif (preg_match('#<(a|b|i|u|table|tr|td|img) #i', $sig) || preg_match('#(href|src|target|title)=#i', $sig)) {
$errors[] = $lang['SIGNATURE_ERROR_HTML']; $errors[] = $lang['SIGNATURE_ERROR_HTML'];
@ -559,9 +561,10 @@ foreach ($profile_fields as $field => $can_edit) {
$templates = isset($_POST['tpl_name']) ? (string)$_POST['tpl_name'] : $pr_data['tpl_name']; $templates = isset($_POST['tpl_name']) ? (string)$_POST['tpl_name'] : $pr_data['tpl_name'];
$templates = htmlCHR($templates); $templates = htmlCHR($templates);
if ($submit && $templates != $pr_data['tpl_name']) { if ($submit && $templates != $pr_data['tpl_name']) {
$pr_data['tpl_name'] = $bb_cfg['tpl_name']; $pr_data['tpl_name'] = config()->get('tpl_name');
$db_data['tpl_name'] = (string)$bb_cfg['tpl_name']; $db_data['tpl_name'] = (string)config()->get('tpl_name');
foreach ($bb_cfg['templates'] as $folder => $name) { $availableTemplates = config()->get('templates');
foreach ($availableTemplates as $folder => $name) {
if ($templates == $folder) { if ($templates == $folder) {
$pr_data['tpl_name'] = $templates; $pr_data['tpl_name'] = $templates;
$db_data['tpl_name'] = (string)$templates; $db_data['tpl_name'] = (string)$templates;
@ -585,7 +588,7 @@ if ($submit && !$errors) {
* Создание нового профиля * Создание нового профиля
*/ */
if ($mode == 'register') { if ($mode == 'register') {
if ($bb_cfg['reg_email_activation']) { if (config()->get('reg_email_activation')) {
$user_actkey = make_rand_str(ACTKEY_LENGTH); $user_actkey = make_rand_str(ACTKEY_LENGTH);
$db_data['user_active'] = 0; $db_data['user_active'] = 0;
$db_data['user_actkey'] = $user_actkey; $db_data['user_actkey'] = $user_actkey;
@ -600,7 +603,7 @@ if ($submit && !$errors) {
} }
if (!isset($db_data['tpl_name'])) { if (!isset($db_data['tpl_name'])) {
$db_data['tpl_name'] = (string)$bb_cfg['tpl_name']; $db_data['tpl_name'] = (string)config()->get('tpl_name');
} }
$sql_args = DB()->build_array('INSERT', $db_data); $sql_args = DB()->build_array('INSERT', $db_data);
@ -622,13 +625,13 @@ if ($submit && !$errors) {
set_pr_die_append_msg($new_user_id); set_pr_die_append_msg($new_user_id);
$message = $lang['ACCOUNT_ADDED']; $message = $lang['ACCOUNT_ADDED'];
} else { } else {
if ($bb_cfg['reg_email_activation']) { if (config()->get('reg_email_activation')) {
$message = $lang['ACCOUNT_INACTIVE']; $message = $lang['ACCOUNT_INACTIVE'];
$email_subject = sprintf($lang['EMAILER_SUBJECT']['USER_WELCOME_INACTIVE'], $bb_cfg['sitename']); $email_subject = sprintf($lang['EMAILER_SUBJECT']['USER_WELCOME_INACTIVE'], config()->get('sitename'));
$email_template = 'user_welcome_inactive'; $email_template = 'user_welcome_inactive';
} else { } else {
$message = $lang['ACCOUNT_ADDED']; $message = $lang['ACCOUNT_ADDED'];
$email_subject = sprintf($lang['EMAILER_SUBJECT']['USER_WELCOME'], $bb_cfg['sitename']); $email_subject = sprintf($lang['EMAILER_SUBJECT']['USER_WELCOME'], config()->get('sitename'));
$email_template = 'user_welcome'; $email_template = 'user_welcome';
} }
@ -640,7 +643,7 @@ if ($submit && !$errors) {
$emailer->set_template($email_template, $user_lang); $emailer->set_template($email_template, $user_lang);
$emailer->assign_vars([ $emailer->assign_vars([
'WELCOME_MSG' => sprintf($lang['WELCOME_SUBJECT'], $bb_cfg['sitename']), 'WELCOME_MSG' => sprintf($lang['WELCOME_SUBJECT'], config()->get('sitename')),
'USERNAME' => html_entity_decode($username), 'USERNAME' => html_entity_decode($username),
'PASSWORD' => $new_pass, 'PASSWORD' => $new_pass,
'U_ACTIVATE' => make_url('profile.php?mode=activate&' . POST_USERS_URL . '=' . $new_user_id . '&act_key=' . $db_data['user_actkey']) 'U_ACTIVATE' => make_url('profile.php?mode=activate&' . POST_USERS_URL . '=' . $new_user_id . '&act_key=' . $db_data['user_actkey'])
@ -728,12 +731,12 @@ $template->assign_vars([
'LANGUAGE_SELECT' => \TorrentPier\Legacy\Common\Select::language($pr_data['user_lang'], 'user_lang'), 'LANGUAGE_SELECT' => \TorrentPier\Legacy\Common\Select::language($pr_data['user_lang'], 'user_lang'),
'TIMEZONE_SELECT' => \TorrentPier\Legacy\Common\Select::timezone($pr_data['user_timezone'], 'user_timezone'), 'TIMEZONE_SELECT' => \TorrentPier\Legacy\Common\Select::timezone($pr_data['user_timezone'], 'user_timezone'),
'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], $bb_cfg['avatars']['max_width'], $bb_cfg['avatars']['max_height'], humn_size($bb_cfg['avatars']['max_size'])), 'AVATAR_EXPLAIN' => sprintf($lang['AVATAR_EXPLAIN'], config()->get('avatars.max_width'), config()->get('avatars.max_height'), humn_size(config()->get('avatars.max_size'))),
'AVATAR_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_avatar'), 'AVATAR_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_avatar'),
'AVATAR_DIS_EXPLAIN' => sprintf($lang['AVATAR_DISABLE'], $bb_cfg['terms_and_conditions_url']), 'AVATAR_DIS_EXPLAIN' => sprintf($lang['AVATAR_DISABLE'], config()->get('terms_and_conditions_url')),
'AVATAR_IMG' => get_avatar($pr_data['user_id'], $pr_data['avatar_ext_id'], !bf($pr_data['user_opt'], 'user_opt', 'dis_avatar')), 'AVATAR_IMG' => get_avatar($pr_data['user_id'], $pr_data['avatar_ext_id'], !bf($pr_data['user_opt'], 'user_opt', 'dis_avatar')),
'SIGNATURE_EXPLAIN' => sprintf($lang['SIGNATURE_EXPLAIN'], $bb_cfg['max_sig_chars']), 'SIGNATURE_EXPLAIN' => sprintf($lang['SIGNATURE_EXPLAIN'], config()->get('max_sig_chars')),
'SIG_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_sig'), 'SIG_DISALLOWED' => bf($pr_data['user_opt'], 'user_opt', 'dis_sig'),
'PR_USER_ID' => $pr_data['user_id'], 'PR_USER_ID' => $pr_data['user_id'],

View file

@ -13,11 +13,11 @@ if (!defined('BB_ROOT')) {
set_die_append_msg(); set_die_append_msg();
if (!$bb_cfg['emailer']['enabled']) { if (!config()->get('emailer.enabled')) {
bb_die($lang['EMAILER_DISABLED']); bb_die($lang['EMAILER_DISABLED']);
} }
$need_captcha = ($_GET['mode'] == 'sendpassword' && !IS_ADMIN && !$bb_cfg['captcha']['disabled']); $need_captcha = ($_GET['mode'] == 'sendpassword' && !IS_ADMIN && !config()->get('captcha.disabled'));
if (isset($_POST['submit'])) { if (isset($_POST['submit'])) {
if ($need_captcha && !bb_captcha('check')) { if ($need_captcha && !bb_captcha('check')) {

View file

@ -11,7 +11,7 @@ if (!defined('BB_ROOT')) {
die(basename(__FILE__)); die(basename(__FILE__));
} }
if (!$bb_cfg['topic_notify_enabled']) { if (!config()->get('topic_notify_enabled')) {
bb_die($lang['DISABLED']); bb_die($lang['DISABLED']);
} }
@ -35,7 +35,7 @@ if (isset($_GET[POST_USERS_URL])) {
} }
} }
$start = isset($_GET['start']) ? abs((int)$_GET['start']) : 0; $start = isset($_GET['start']) ? abs((int)$_GET['start']) : 0;
$per_page = $bb_cfg['topics_per_page']; $per_page = config()->get('topics_per_page');
if (isset($_POST['topic_id_list'])) { if (isset($_POST['topic_id_list'])) {
$topic_ids = implode(",", $_POST['topic_id_list']); $topic_ids = implode(",", $_POST['topic_id_list']);
@ -83,7 +83,7 @@ if ($watch_count > 0) {
'ROW_CLASS' => (!($i % 2)) ? 'row1' : 'row2', 'ROW_CLASS' => (!($i % 2)) ? 'row1' : 'row2',
'POST_ID' => $watch[$i]['topic_first_post_id'], 'POST_ID' => $watch[$i]['topic_first_post_id'],
'TOPIC_ID' => $watch[$i]['topic_id'], 'TOPIC_ID' => $watch[$i]['topic_id'],
'TOPIC_TITLE' => str_short($wordCensor->censorString($watch[$i]['topic_title']), 70), 'TOPIC_TITLE' => str_short(censor()->censorString($watch[$i]['topic_title']), 70),
'FULL_TOPIC_TITLE' => $watch[$i]['topic_title'], 'FULL_TOPIC_TITLE' => $watch[$i]['topic_title'],
'U_TOPIC' => TOPIC_URL . $watch[$i]['topic_id'], 'U_TOPIC' => TOPIC_URL . $watch[$i]['topic_id'],
'FORUM_TITLE' => $watch[$i]['forum_name'], 'FORUM_TITLE' => $watch[$i]['forum_name'],
@ -96,7 +96,7 @@ if ($watch_count > 0) {
'IS_UNREAD' => $is_unread, 'IS_UNREAD' => $is_unread,
'POLL' => (bool)$watch[$i]['topic_vote'], 'POLL' => (bool)$watch[$i]['topic_vote'],
'TOPIC_ICON' => get_topic_icon($watch[$i], $is_unread), 'TOPIC_ICON' => get_topic_icon($watch[$i], $is_unread),
'PAGINATION' => ($watch[$i]['topic_status'] == TOPIC_MOVED) ? '' : build_topic_pagination(TOPIC_URL . $watch[$i]['topic_id'], $watch[$i]['topic_replies'], $bb_cfg['posts_per_page']) 'PAGINATION' => ($watch[$i]['topic_status'] == TOPIC_MOVED) ? '' : build_topic_pagination(TOPIC_URL . $watch[$i]['topic_id'], $watch[$i]['topic_replies'], config()->get('posts_per_page'))
]); ]);
} }

View file

@ -30,6 +30,8 @@ if (!$profiledata = get_userdata($_GET[POST_USERS_URL], profile_view: true)) {
bb_die($lang['NO_USER_ID_SPECIFIED']); bb_die($lang['NO_USER_ID_SPECIFIED']);
} }
$profiledata['user_birthday'] = $profiledata['user_birthday']->format('Y-m-d');
if (!$ranks = $datastore->get('ranks')) { if (!$ranks = $datastore->get('ranks')) {
$datastore->update('ranks'); $datastore->update('ranks');
$ranks = $datastore->get('ranks'); $ranks = $datastore->get('ranks');
@ -50,7 +52,7 @@ if (IS_ADMIN) {
} }
if (bf($profiledata['user_opt'], 'user_opt', 'user_viewemail') || $profiledata['user_id'] == $userdata['user_id'] || IS_ADMIN) { if (bf($profiledata['user_opt'], 'user_opt', 'user_viewemail') || $profiledata['user_id'] == $userdata['user_id'] || IS_ADMIN) {
$email_uri = ($bb_cfg['board_email_form']) ? 'profile.php?mode=email&amp;' . POST_USERS_URL . '=' . $profiledata['user_id'] : 'mailto:' . $profiledata['user_email']; $email_uri = (config()->get('board_email_form')) ? 'profile.php?mode=email&amp;' . POST_USERS_URL . '=' . $profiledata['user_id'] : 'mailto:' . $profiledata['user_email'];
$email = '<a class="editable" href="' . $email_uri . '">' . $profiledata['user_email'] . '</a>'; $email = '<a class="editable" href="' . $email_uri . '">' . $profiledata['user_email'] . '</a>';
} else { } else {
$email = ''; $email = '';
@ -62,7 +64,7 @@ if (bf($profiledata['user_opt'], 'user_opt', 'user_viewemail') || $profiledata['
$profile_user_id = ($profiledata['user_id'] == $userdata['user_id']); $profile_user_id = ($profiledata['user_id'] == $userdata['user_id']);
$signature = ($bb_cfg['allow_sig'] && $profiledata['user_sig']) ? $profiledata['user_sig'] : ''; $signature = (config()->get('allow_sig') && $profiledata['user_sig']) ? $profiledata['user_sig'] : '';
if (bf($profiledata['user_opt'], 'user_opt', 'dis_sig')) { if (bf($profiledata['user_opt'], 'user_opt', 'dis_sig')) {
if ($profile_user_id) { if ($profile_user_id) {
@ -75,7 +77,7 @@ if (bf($profiledata['user_opt'], 'user_opt', 'dis_sig')) {
} }
// Null ratio // Null ratio
if ($bb_cfg['ratio_null_enabled'] && $btu = get_bt_userdata($profiledata['user_id'])) { if (config()->get('ratio_null_enabled') && $btu = get_bt_userdata($profiledata['user_id'])) {
$template->assign_vars(['NULLED_RATIO' => $btu['ratio_nulled']]); $template->assign_vars(['NULLED_RATIO' => $btu['ratio_nulled']]);
} }
@ -110,10 +112,10 @@ $template->assign_vars([
'SKYPE' => $profiledata['user_skype'], 'SKYPE' => $profiledata['user_skype'],
'TWITTER' => $profiledata['user_twitter'], 'TWITTER' => $profiledata['user_twitter'],
'USER_POINTS' => $profiledata['user_points'], 'USER_POINTS' => $profiledata['user_points'],
'GENDER' => $bb_cfg['gender'] ? $lang['GENDER_SELECT'][$profiledata['user_gender']] : '', 'GENDER' => config()->get('gender') ? $lang['GENDER_SELECT'][$profiledata['user_gender']] : '',
'BIRTHDAY' => ($bb_cfg['birthday_enabled'] && !empty($profiledata['user_birthday']) && $profiledata['user_birthday'] != '1900-01-01') ? $profiledata['user_birthday'] : '', 'BIRTHDAY' => (config()->get('birthday_enabled') && !empty($profiledata['user_birthday']) && $profiledata['user_birthday'] != '1900-01-01') ? $profiledata['user_birthday'] : '',
'BIRTHDAY_ICON' => user_birthday_icon($profiledata['user_birthday'], $profiledata['user_id']), 'BIRTHDAY_ICON' => user_birthday_icon($profiledata['user_birthday'], $profiledata['user_id']),
'AGE' => ($bb_cfg['birthday_enabled'] && !empty($profiledata['user_birthday']) && $profiledata['user_birthday'] != '1900-01-01') ? birthday_age($profiledata['user_birthday']) : '', 'AGE' => (config()->get('birthday_enabled') && !empty($profiledata['user_birthday']) && $profiledata['user_birthday'] != '1900-01-01') ? birthday_age($profiledata['user_birthday']) : '',
'L_VIEWING_PROFILE' => sprintf($lang['VIEWING_USER_PROFILE'], $profiledata['username']), 'L_VIEWING_PROFILE' => sprintf($lang['VIEWING_USER_PROFILE'], $profiledata['username']),
'L_MY_PROFILE' => sprintf($lang['VIEWING_MY_PROFILE'], 'profile.php?mode=editprofile'), 'L_MY_PROFILE' => sprintf($lang['VIEWING_MY_PROFILE'], 'profile.php?mode=editprofile'),

View file

@ -7,5 +7,5 @@
<li>style/templates/default/page_footer.tpl</li> <li>style/templates/default/page_footer.tpl</li>
</ul> </ul>
<br /> <br />
Om hierdie sidebar uit te skakel, stel die veranderlike $bb_cfg['page']['show_sidebar2'] in lêer config.php in vals. To disable this sidebar, set the variable page.show_sidebar2 in file config.php to false.
</div> </div>

View file

@ -1946,6 +1946,32 @@ $lang['TRACKER_CONFIG'] = 'Tracker instellings';
$lang['RELEASE_TEMPLATES'] = 'Stel sjablone vry'; $lang['RELEASE_TEMPLATES'] = 'Stel sjablone vry';
$lang['ACTIONS_LOG'] = 'Verslag oor aksie'; $lang['ACTIONS_LOG'] = 'Verslag oor aksie';
// Migrations
$lang['MIGRATIONS_STATUS'] = 'Database Migration Status';
$lang['MIGRATIONS_DATABASE_NAME'] = 'Database Name';
$lang['MIGRATIONS_DATABASE_TOTAL'] = 'Total Tables';
$lang['MIGRATIONS_DATABASE_SIZE'] = 'Database Size';
$lang['MIGRATIONS_DATABASE_INFO'] = 'Database Information';
$lang['MIGRATIONS_SYSTEM'] = 'Migration System';
$lang['MIGRATIONS_NEEDS_SETUP'] = 'Needs Setup';
$lang['MIGRATIONS_ACTIVE'] = 'aktiewe';
$lang['MIGRATIONS_NOT_INITIALIZED'] = 'Not Initialized';
$lang['MIGRATIONS_UP_TO_DATE'] = 'All up to date';
$lang['MIGRATIONS_PENDING_COUNT'] = 'pending';
$lang['MIGRATIONS_APPLIED'] = 'Applied Migrations';
$lang['MIGRATIONS_PENDING'] = 'Pending Migrations';
$lang['MIGRATIONS_VERSION'] = 'Version';
$lang['MIGRATIONS_NAME'] = 'Migration Name';
$lang['MIGRATIONS_FILE'] = 'Migration File';
$lang['MIGRATIONS_APPLIED_AT'] = 'Applied At';
$lang['MIGRATIONS_COMPLETED_AT'] = 'Completed At';
$lang['MIGRATIONS_CURRENT_VERSION'] = 'Current Version';
$lang['MIGRATIONS_NOT_APPLIED'] = 'No migrations applied';
$lang['MIGRATIONS_INSTRUCTIONS'] = 'Instructions';
$lang['MIGRATIONS_SETUP_STATUS'] = 'Setup Status';
$lang['MIGRATIONS_SETUP_GUIDE'] = 'See setup guide below';
$lang['MIGRATIONS_ACTION_REQUIRED'] = 'Action Required';
// Index // Index
$lang['MAIN_INDEX'] = 'Forum Indeks'; $lang['MAIN_INDEX'] = 'Forum Indeks';
$lang['FORUM_STATS'] = 'Forum Statistiek'; $lang['FORUM_STATS'] = 'Forum Statistiek';

View file

@ -7,5 +7,5 @@
<li>style/templates/default/page_footer.tpl</li> <li>style/templates/default/page_footer.tpl</li>
</ul> </ul>
<br /> <br />
لتعطيل هذا الشريط الجانبي ، تعيين متغير $bb_cfg['page']['show_sidebar2'] في الملف config.php إلى false. To disable this sidebar, set the variable page.show_sidebar2 in file config.php to false.
</div> </div>

Some files were not shown because too many files have changed in this diff Show more