ghost
1 year ago
114 changed files with 11523 additions and 10996 deletions
@ -0,0 +1,50 @@ |
|||||||
|
# In all environments, the following files are loaded if they exist, |
||||||
|
# the latter taking precedence over the former: |
||||||
|
# |
||||||
|
# * .env contains default values for the environment variables needed by the app |
||||||
|
# * .env.local uncommitted file with local overrides |
||||||
|
# * .env.$APP_ENV committed environment-specific defaults |
||||||
|
# * .env.$APP_ENV.local uncommitted environment-specific overrides |
||||||
|
# |
||||||
|
# Real environment variables win over .env files. |
||||||
|
# |
||||||
|
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES. |
||||||
|
# https://symfony.com/doc/current/configuration/secrets.html |
||||||
|
# |
||||||
|
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2). |
||||||
|
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration |
||||||
|
|
||||||
|
###> symfony/framework-bundle ### |
||||||
|
APP_ENV=dev |
||||||
|
APP_SECRET=EDITME |
||||||
|
###< symfony/framework-bundle ### |
||||||
|
|
||||||
|
###> doctrine/doctrine-bundle ### |
||||||
|
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url |
||||||
|
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml |
||||||
|
# |
||||||
|
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db" |
||||||
|
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4" |
||||||
|
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4" |
||||||
|
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8" |
||||||
|
###< doctrine/doctrine-bundle ### |
||||||
|
|
||||||
|
###> symfony/messenger ### |
||||||
|
# Choose one of the transports below |
||||||
|
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages |
||||||
|
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages |
||||||
|
MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0 |
||||||
|
###< symfony/messenger ### |
||||||
|
|
||||||
|
###> symfony/mailer ### |
||||||
|
# MAILER_DSN=null://null |
||||||
|
###< symfony/mailer ### |
||||||
|
|
||||||
|
###> symfony/crowdin-translation-provider ### |
||||||
|
# CROWDIN_DSN=crowdin://PROJECT_ID:API_TOKEN@ORGANIZATION_DOMAIN.default |
||||||
|
###< symfony/crowdin-translation-provider ### |
||||||
|
|
||||||
|
# YGGtracker |
||||||
|
APP_VERSION='2.0.0' |
||||||
|
|
||||||
|
APP_NAME=YGGtracker |
@ -0,0 +1,6 @@ |
|||||||
|
# define your env variables for the test env here |
||||||
|
KERNEL_CLASS='App\Kernel' |
||||||
|
APP_SECRET='$ecretf0rt3st' |
||||||
|
SYMFONY_DEPRECATIONS_HELPER=999999 |
||||||
|
PANTHER_APP_ENV=panther |
||||||
|
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots |
@ -1,30 +1,22 @@ |
|||||||
/.vscode/ |
|
||||||
|
|
||||||
|
###> symfony/framework-bundle ### |
||||||
|
/.env.local |
||||||
|
/.env.local.php |
||||||
|
/.env.*.local |
||||||
|
/config/secrets/prod/prod.decrypt.private.php |
||||||
|
/public/bundles/ |
||||||
|
/var/ |
||||||
/vendor/ |
/vendor/ |
||||||
|
###< symfony/framework-bundle ### |
||||||
|
|
||||||
/database/* |
###> phpunit/phpunit ### |
||||||
!/database/yggtracker.mwb |
/phpunit.xml |
||||||
|
.phpunit.result.cache |
||||||
|
###< phpunit/phpunit ### |
||||||
|
|
||||||
/src/public/api/*.json |
###> symfony/phpunit-bridge ### |
||||||
|
.phpunit.result.cache |
||||||
|
/phpunit.xml |
||||||
|
###< symfony/phpunit-bridge ### |
||||||
|
|
||||||
/src/config/* |
.vscode |
||||||
!/src/config/bootstrap.php |
|
||||||
!/src/config/website.json |
|
||||||
!/src/config/locales.json |
|
||||||
!/src/config/themes.json |
|
||||||
!/src/config/sphinx.json |
|
||||||
!/src/config/memcached.json |
|
||||||
!/src/config/database.json |
|
||||||
!/src/config/validator.json |
|
||||||
!/src/config/moderators.json |
|
||||||
!/src/config/nodes.json |
|
||||||
!/src/config/trackers.json |
|
||||||
!/src/config/peers.json |
|
||||||
|
|
||||||
/src/public/sitemap.xml |
|
||||||
|
|
||||||
/src/storage/log/*.log |
|
||||||
|
|
||||||
/composer.lock |
|
||||||
|
|
||||||
*test* |
|
@ -0,0 +1,17 @@ |
|||||||
|
#!/usr/bin/env php |
||||||
|
<?php |
||||||
|
|
||||||
|
use App\Kernel; |
||||||
|
use Symfony\Bundle\FrameworkBundle\Console\Application; |
||||||
|
|
||||||
|
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { |
||||||
|
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".'); |
||||||
|
} |
||||||
|
|
||||||
|
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||||
|
|
||||||
|
return function (array $context) { |
||||||
|
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||||
|
|
||||||
|
return new Application($kernel); |
||||||
|
}; |
@ -0,0 +1,19 @@ |
|||||||
|
#!/usr/bin/env php |
||||||
|
<?php |
||||||
|
|
||||||
|
if (!ini_get('date.timezone')) { |
||||||
|
ini_set('date.timezone', 'UTC'); |
||||||
|
} |
||||||
|
|
||||||
|
if (is_file(dirname(__DIR__).'/vendor/phpunit/phpunit/phpunit')) { |
||||||
|
define('PHPUNIT_COMPOSER_INSTALL', dirname(__DIR__).'/vendor/autoload.php'); |
||||||
|
require PHPUNIT_COMPOSER_INSTALL; |
||||||
|
PHPUnit\TextUI\Command::main(); |
||||||
|
} else { |
||||||
|
if (!is_file(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) { |
||||||
|
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n"; |
||||||
|
exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php'; |
||||||
|
} |
@ -1,24 +1,108 @@ |
|||||||
{ |
{ |
||||||
"name": "yggverse/yggtracker", |
"name": "yggverse/yggtracker", |
||||||
"description": "Public BitTorrent tracker for Yggdrasil network", |
"description": "BitTorrent tracker for Yggdrasil network", |
||||||
"type": "project", |
"type": "project", |
||||||
|
"license": "MIT", |
||||||
|
"minimum-stability": "stable", |
||||||
|
"prefer-stable": true, |
||||||
"require": { |
"require": { |
||||||
"php": "^8.1", |
"php": ">=8.1", |
||||||
"yggverse/cache": ">=0.3.0", |
"ext-ctype": "*", |
||||||
"yggverse/parser": ">=0.4.0", |
"ext-iconv": "*", |
||||||
|
"christeredvartsen/php-bittorrent": "^2.0", |
||||||
|
"doctrine/annotations": "^2.0", |
||||||
|
"doctrine/doctrine-bundle": "^2.10", |
||||||
|
"doctrine/doctrine-migrations-bundle": "^3.2", |
||||||
|
"doctrine/orm": "^2.16", |
||||||
"jdenticon/jdenticon": "^1.0", |
"jdenticon/jdenticon": "^1.0", |
||||||
"christeredvartsen/php-bittorrent": "^2.0" |
"phpdocumentor/reflection-docblock": "^5.3", |
||||||
|
"phpstan/phpdoc-parser": "^1.24", |
||||||
|
"symfony/asset": "6.3.*", |
||||||
|
"symfony/console": "6.3.*", |
||||||
|
"symfony/crowdin-translation-provider": "6.3.*", |
||||||
|
"symfony/doctrine-messenger": "6.3.*", |
||||||
|
"symfony/dotenv": "6.3.*", |
||||||
|
"symfony/expression-language": "6.3.*", |
||||||
|
"symfony/flex": "^2", |
||||||
|
"symfony/form": "6.3.*", |
||||||
|
"symfony/framework-bundle": "6.3.*", |
||||||
|
"symfony/http-client": "6.3.*", |
||||||
|
"symfony/intl": "6.3.*", |
||||||
|
"symfony/mailer": "6.3.*", |
||||||
|
"symfony/mime": "6.3.*", |
||||||
|
"symfony/monolog-bundle": "^3.0", |
||||||
|
"symfony/notifier": "6.3.*", |
||||||
|
"symfony/process": "6.3.*", |
||||||
|
"symfony/property-access": "6.3.*", |
||||||
|
"symfony/property-info": "6.3.*", |
||||||
|
"symfony/runtime": "6.3.*", |
||||||
|
"symfony/security-bundle": "6.3.*", |
||||||
|
"symfony/serializer": "6.3.*", |
||||||
|
"symfony/string": "6.3.*", |
||||||
|
"symfony/translation": "6.3.*", |
||||||
|
"symfony/twig-bundle": "6.3.*", |
||||||
|
"symfony/validator": "6.3.*", |
||||||
|
"symfony/web-link": "6.3.*", |
||||||
|
"symfony/yaml": "6.3.*", |
||||||
|
"twig/extra-bundle": "^2.12|^3.0", |
||||||
|
"twig/twig": "^2.12|^3.0" |
||||||
|
}, |
||||||
|
"config": { |
||||||
|
"allow-plugins": { |
||||||
|
"php-http/discovery": true, |
||||||
|
"symfony/flex": true, |
||||||
|
"symfony/runtime": true |
||||||
|
}, |
||||||
|
"sort-packages": true |
||||||
}, |
}, |
||||||
"license": "MIT", |
|
||||||
"autoload": { |
"autoload": { |
||||||
"psr-4": { |
"psr-4": { |
||||||
"Yggverse\\Yggtracker\\": "src/" |
"App\\": "src/" |
||||||
|
} |
||||||
|
}, |
||||||
|
"autoload-dev": { |
||||||
|
"psr-4": { |
||||||
|
"App\\Tests\\": "tests/" |
||||||
} |
} |
||||||
}, |
}, |
||||||
"authors": [ |
"replace": { |
||||||
{ |
"symfony/polyfill-ctype": "*", |
||||||
"name": "YGGverse" |
"symfony/polyfill-iconv": "*", |
||||||
|
"symfony/polyfill-php72": "*", |
||||||
|
"symfony/polyfill-php73": "*", |
||||||
|
"symfony/polyfill-php74": "*", |
||||||
|
"symfony/polyfill-php80": "*", |
||||||
|
"symfony/polyfill-php81": "*" |
||||||
|
}, |
||||||
|
"scripts": { |
||||||
|
"auto-scripts": { |
||||||
|
"cache:clear": "symfony-cmd", |
||||||
|
"assets:install %PUBLIC_DIR%": "symfony-cmd" |
||||||
|
}, |
||||||
|
"post-install-cmd": [ |
||||||
|
"@auto-scripts" |
||||||
|
], |
||||||
|
"post-update-cmd": [ |
||||||
|
"@auto-scripts" |
||||||
|
] |
||||||
|
}, |
||||||
|
"conflict": { |
||||||
|
"symfony/symfony": "*" |
||||||
|
}, |
||||||
|
"extra": { |
||||||
|
"symfony": { |
||||||
|
"allow-contrib": false, |
||||||
|
"require": "6.3.*" |
||||||
} |
} |
||||||
], |
}, |
||||||
"minimum-stability": "alpha" |
"require-dev": { |
||||||
|
"phpunit/phpunit": "^9.5", |
||||||
|
"symfony/browser-kit": "6.3.*", |
||||||
|
"symfony/css-selector": "6.3.*", |
||||||
|
"symfony/debug-bundle": "6.3.*", |
||||||
|
"symfony/maker-bundle": "^1.0", |
||||||
|
"symfony/phpunit-bridge": "^6.3", |
||||||
|
"symfony/stopwatch": "6.3.*", |
||||||
|
"symfony/web-profiler-bundle": "6.3.*" |
||||||
|
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
return [ |
||||||
|
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], |
||||||
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], |
||||||
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true], |
||||||
|
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true], |
||||||
|
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], |
||||||
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], |
||||||
|
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true], |
||||||
|
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], |
||||||
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], |
||||||
|
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], |
||||||
|
]; |
@ -0,0 +1,19 @@ |
|||||||
|
framework: |
||||||
|
cache: |
||||||
|
# Unique name of your app: used to compute stable namespaces for cache keys. |
||||||
|
#prefix_seed: your_vendor_name/app_name |
||||||
|
|
||||||
|
# The "app" cache stores to the filesystem by default. |
||||||
|
# The data in this cache should persist between deploys. |
||||||
|
# Other options include: |
||||||
|
|
||||||
|
# Redis |
||||||
|
#app: cache.adapter.redis |
||||||
|
#default_redis_provider: redis://localhost |
||||||
|
|
||||||
|
# APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) |
||||||
|
#app: cache.adapter.apcu |
||||||
|
|
||||||
|
# Namespaced pools use the above "app" backend by default |
||||||
|
#pools: |
||||||
|
#my.dedicated.cache: null |
@ -0,0 +1,5 @@ |
|||||||
|
when@dev: |
||||||
|
debug: |
||||||
|
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. |
||||||
|
# See the "server:dump" command to start a new server. |
||||||
|
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" |
@ -0,0 +1,48 @@ |
|||||||
|
doctrine: |
||||||
|
dbal: |
||||||
|
url: '%env(resolve:DATABASE_URL)%' |
||||||
|
|
||||||
|
# IMPORTANT: You MUST configure your server version, |
||||||
|
# either here or in the DATABASE_URL env var (see .env file) |
||||||
|
#server_version: '15' |
||||||
|
|
||||||
|
profiling_collect_backtrace: '%kernel.debug%' |
||||||
|
orm: |
||||||
|
auto_generate_proxy_classes: true |
||||||
|
enable_lazy_ghost_objects: true |
||||||
|
report_fields_where_declared: true |
||||||
|
validate_xml_mapping: true |
||||||
|
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware |
||||||
|
auto_mapping: true |
||||||
|
mappings: |
||||||
|
App: |
||||||
|
is_bundle: false |
||||||
|
dir: '%kernel.project_dir%/src/Entity' |
||||||
|
prefix: 'App\Entity' |
||||||
|
alias: App |
||||||
|
|
||||||
|
when@test: |
||||||
|
doctrine: |
||||||
|
dbal: |
||||||
|
# "TEST_TOKEN" is typically set by ParaTest |
||||||
|
dbname_suffix: '_test%env(default::TEST_TOKEN)%' |
||||||
|
|
||||||
|
when@prod: |
||||||
|
doctrine: |
||||||
|
orm: |
||||||
|
auto_generate_proxy_classes: false |
||||||
|
proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' |
||||||
|
query_cache_driver: |
||||||
|
type: pool |
||||||
|
pool: doctrine.system_cache_pool |
||||||
|
result_cache_driver: |
||||||
|
type: pool |
||||||
|
pool: doctrine.result_cache_pool |
||||||
|
|
||||||
|
framework: |
||||||
|
cache: |
||||||
|
pools: |
||||||
|
doctrine.result_cache_pool: |
||||||
|
adapter: cache.app |
||||||
|
doctrine.system_cache_pool: |
||||||
|
adapter: cache.system |
@ -0,0 +1,6 @@ |
|||||||
|
doctrine_migrations: |
||||||
|
migrations_paths: |
||||||
|
# namespace is arbitrary but should be different from App\Migrations |
||||||
|
# as migrations classes should NOT be autoloaded |
||||||
|
'DoctrineMigrations': '%kernel.project_dir%/migrations' |
||||||
|
enable_profiler: false |
@ -0,0 +1,25 @@ |
|||||||
|
# see https://symfony.com/doc/current/reference/configuration/framework.html |
||||||
|
framework: |
||||||
|
secret: '%env(APP_SECRET)%' |
||||||
|
#csrf_protection: true |
||||||
|
http_method_override: false |
||||||
|
handle_all_throwables: true |
||||||
|
|
||||||
|
# Enables session support. Note that the session will ONLY be started if you read or write from it. |
||||||
|
# Remove or comment this section to explicitly disable session support. |
||||||
|
session: |
||||||
|
handler_id: null |
||||||
|
cookie_secure: auto |
||||||
|
cookie_samesite: lax |
||||||
|
storage_factory_id: session.storage.factory.native |
||||||
|
|
||||||
|
#esi: true |
||||||
|
#fragments: true |
||||||
|
php_errors: |
||||||
|
log: true |
||||||
|
|
||||||
|
when@test: |
||||||
|
framework: |
||||||
|
test: true |
||||||
|
session: |
||||||
|
storage_factory_id: session.storage.factory.mock_file |
@ -0,0 +1,3 @@ |
|||||||
|
framework: |
||||||
|
mailer: |
||||||
|
dsn: '%env(MAILER_DSN)%' |
@ -0,0 +1,24 @@ |
|||||||
|
framework: |
||||||
|
messenger: |
||||||
|
failure_transport: failed |
||||||
|
|
||||||
|
transports: |
||||||
|
# https://symfony.com/doc/current/messenger.html#transport-configuration |
||||||
|
async: |
||||||
|
dsn: '%env(MESSENGER_TRANSPORT_DSN)%' |
||||||
|
options: |
||||||
|
use_notify: true |
||||||
|
check_delayed_interval: 60000 |
||||||
|
retry_strategy: |
||||||
|
max_retries: 3 |
||||||
|
multiplier: 2 |
||||||
|
failed: 'doctrine://default?queue_name=failed' |
||||||
|
# sync: 'sync://' |
||||||
|
|
||||||
|
routing: |
||||||
|
Symfony\Component\Mailer\Messenger\SendEmailMessage: async |
||||||
|
Symfony\Component\Notifier\Message\ChatMessage: async |
||||||
|
Symfony\Component\Notifier\Message\SmsMessage: async |
||||||
|
|
||||||
|
# Route your messages to the transports |
||||||
|
# 'App\Message\YourMessage': async |
@ -0,0 +1,61 @@ |
|||||||
|
monolog: |
||||||
|
channels: |
||||||
|
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists |
||||||
|
|
||||||
|
when@dev: |
||||||
|
monolog: |
||||||
|
handlers: |
||||||
|
main: |
||||||
|
type: stream |
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||||
|
level: debug |
||||||
|
channels: ["!event"] |
||||||
|
# uncomment to get logging in your browser |
||||||
|
# you may have to allow bigger header sizes in your Web server configuration |
||||||
|
#firephp: |
||||||
|
# type: firephp |
||||||
|
# level: info |
||||||
|
#chromephp: |
||||||
|
# type: chromephp |
||||||
|
# level: info |
||||||
|
console: |
||||||
|
type: console |
||||||
|
process_psr_3_messages: false |
||||||
|
channels: ["!event", "!doctrine", "!console"] |
||||||
|
|
||||||
|
when@test: |
||||||
|
monolog: |
||||||
|
handlers: |
||||||
|
main: |
||||||
|
type: fingers_crossed |
||||||
|
action_level: error |
||||||
|
handler: nested |
||||||
|
excluded_http_codes: [404, 405] |
||||||
|
channels: ["!event"] |
||||||
|
nested: |
||||||
|
type: stream |
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log" |
||||||
|
level: debug |
||||||
|
|
||||||
|
when@prod: |
||||||
|
monolog: |
||||||
|
handlers: |
||||||
|
main: |
||||||
|
type: fingers_crossed |
||||||
|
action_level: error |
||||||
|
handler: nested |
||||||
|
excluded_http_codes: [404, 405] |
||||||
|
buffer_size: 50 # How many messages should be saved? Prevent memory leaks |
||||||
|
nested: |
||||||
|
type: stream |
||||||
|
path: php://stderr |
||||||
|
level: debug |
||||||
|
formatter: monolog.formatter.json |
||||||
|
console: |
||||||
|
type: console |
||||||
|
process_psr_3_messages: false |
||||||
|
channels: ["!event", "!doctrine"] |
||||||
|
deprecation: |
||||||
|
type: stream |
||||||
|
channels: [deprecation] |
||||||
|
path: php://stderr |
@ -0,0 +1,13 @@ |
|||||||
|
framework: |
||||||
|
notifier: |
||||||
|
chatter_transports: |
||||||
|
texter_transports: |
||||||
|
crowdin: '%env(CROWDIN_DSN)%' |
||||||
|
channel_policy: |
||||||
|
# use chat/slack, chat/telegram, sms/twilio or sms/nexmo |
||||||
|
urgent: ['email'] |
||||||
|
high: ['email'] |
||||||
|
medium: ['email'] |
||||||
|
low: ['email'] |
||||||
|
admin_recipients: |
||||||
|
- { email: admin@example.com } |
@ -0,0 +1,12 @@ |
|||||||
|
framework: |
||||||
|
router: |
||||||
|
utf8: true |
||||||
|
|
||||||
|
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. |
||||||
|
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands |
||||||
|
#default_uri: http://localhost |
||||||
|
|
||||||
|
when@prod: |
||||||
|
framework: |
||||||
|
router: |
||||||
|
strict_requirements: null |
@ -0,0 +1,39 @@ |
|||||||
|
security: |
||||||
|
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords |
||||||
|
password_hashers: |
||||||
|
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' |
||||||
|
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider |
||||||
|
providers: |
||||||
|
users_in_memory: { memory: null } |
||||||
|
firewalls: |
||||||
|
dev: |
||||||
|
pattern: ^/(_(profiler|wdt)|css|images|js)/ |
||||||
|
security: false |
||||||
|
main: |
||||||
|
lazy: true |
||||||
|
provider: users_in_memory |
||||||
|
|
||||||
|
# activate different ways to authenticate |
||||||
|
# https://symfony.com/doc/current/security.html#the-firewall |
||||||
|
|
||||||
|
# https://symfony.com/doc/current/security/impersonating_user.html |
||||||
|
# switch_user: true |
||||||
|
|
||||||
|
# Easy way to control access for large sections of your site |
||||||
|
# Note: Only the *first* access control that matches will be used |
||||||
|
access_control: |
||||||
|
# - { path: ^/admin, roles: ROLE_ADMIN } |
||||||
|
# - { path: ^/profile, roles: ROLE_USER } |
||||||
|
|
||||||
|
when@test: |
||||||
|
security: |
||||||
|
password_hashers: |
||||||
|
# By default, password hashers are resource intensive and take time. This is |
||||||
|
# important to generate secure password hashes. In tests however, secure hashes |
||||||
|
# are not important, waste resources and increase test times. The following |
||||||
|
# reduces the work factor to the lowest possible values. |
||||||
|
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: |
||||||
|
algorithm: auto |
||||||
|
cost: 4 # Lowest possible value for bcrypt |
||||||
|
time_cost: 3 # Lowest possible value for argon |
||||||
|
memory_cost: 10 # Lowest possible value for argon |
@ -0,0 +1,15 @@ |
|||||||
|
framework: |
||||||
|
default_locale: en |
||||||
|
translator: |
||||||
|
default_path: '%kernel.project_dir%/translations' |
||||||
|
fallbacks: |
||||||
|
- en |
||||||
|
# providers: |
||||||
|
# crowdin: |
||||||
|
# dsn: '%env(CROWDIN_DSN)%' |
||||||
|
# loco: |
||||||
|
# dsn: '%env(LOCO_DSN)%' |
||||||
|
# lokalise: |
||||||
|
# dsn: '%env(LOKALISE_DSN)%' |
||||||
|
# phrase: |
||||||
|
# dsn: '%env(PHRASE_DSN)%' |
@ -0,0 +1,9 @@ |
|||||||
|
twig: |
||||||
|
default_path: '%kernel.project_dir%/templates' |
||||||
|
globals: |
||||||
|
version: '%app.version%' |
||||||
|
name: '%app.name%' |
||||||
|
|
||||||
|
when@test: |
||||||
|
twig: |
||||||
|
strict_variables: true |
@ -0,0 +1,13 @@ |
|||||||
|
framework: |
||||||
|
validation: |
||||||
|
email_validation_mode: html5 |
||||||
|
|
||||||
|
# Enables validator auto-mapping support. |
||||||
|
# For instance, basic validation constraints will be inferred from Doctrine's metadata. |
||||||
|
#auto_mapping: |
||||||
|
# App\Entity\: [] |
||||||
|
|
||||||
|
when@test: |
||||||
|
framework: |
||||||
|
validation: |
||||||
|
not_compromised_password: false |
@ -0,0 +1,17 @@ |
|||||||
|
when@dev: |
||||||
|
web_profiler: |
||||||
|
toolbar: true |
||||||
|
intercept_redirects: false |
||||||
|
|
||||||
|
framework: |
||||||
|
profiler: |
||||||
|
only_exceptions: false |
||||||
|
collect_serializer_data: true |
||||||
|
|
||||||
|
when@test: |
||||||
|
web_profiler: |
||||||
|
toolbar: false |
||||||
|
intercept_redirects: false |
||||||
|
|
||||||
|
framework: |
||||||
|
profiler: { collect: false } |
@ -0,0 +1,5 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) { |
||||||
|
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php'; |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
controllers: |
||||||
|
resource: |
||||||
|
path: ../src/Controller/ |
||||||
|
namespace: App\Controller |
||||||
|
type: attribute |
@ -0,0 +1,4 @@ |
|||||||
|
when@dev: |
||||||
|
_errors: |
||||||
|
resource: '@FrameworkBundle/Resources/config/routing/errors.xml' |
||||||
|
prefix: /_error |
@ -0,0 +1,8 @@ |
|||||||
|
when@dev: |
||||||
|
web_profiler_wdt: |
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' |
||||||
|
prefix: /_wdt |
||||||
|
|
||||||
|
web_profiler_profiler: |
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' |
||||||
|
prefix: /_profiler |
@ -0,0 +1,26 @@ |
|||||||
|
# This file is the entry point to configure your own services. |
||||||
|
# Files in the packages/ subdirectory configure your dependencies. |
||||||
|
|
||||||
|
# Put parameters here that don't need to change on each machine where the app is deployed |
||||||
|
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration |
||||||
|
parameters: |
||||||
|
app.version: '%env(APP_VERSION)%' |
||||||
|
app.name: '%env(APP_NAME)%' |
||||||
|
|
||||||
|
services: |
||||||
|
# default configuration for services in *this* file |
||||||
|
_defaults: |
||||||
|
autowire: true # Automatically injects dependencies in your services. |
||||||
|
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. |
||||||
|
|
||||||
|
# makes classes in src/ available to be used as services |
||||||
|
# this creates a service per class whose id is the fully-qualified class name |
||||||
|
App\: |
||||||
|
resource: '../src/' |
||||||
|
exclude: |
||||||
|
- '../src/DependencyInjection/' |
||||||
|
- '../src/Entity/' |
||||||
|
- '../src/Kernel.php' |
||||||
|
|
||||||
|
# add more service definitions when explicit configuration is needed |
||||||
|
# please note that last definitions always *replace* previous ones |
Binary file not shown.
@ -0,0 +1,14 @@ |
|||||||
|
version: '3' |
||||||
|
|
||||||
|
services: |
||||||
|
###> doctrine/doctrine-bundle ### |
||||||
|
database: |
||||||
|
ports: |
||||||
|
- "5432" |
||||||
|
###< doctrine/doctrine-bundle ### |
||||||
|
|
||||||
|
###> symfony/mailer ### |
||||||
|
mailer: |
||||||
|
image: schickling/mailcatcher |
||||||
|
ports: ["1025", "1080"] |
||||||
|
###< symfony/mailer ### |
@ -0,0 +1,21 @@ |
|||||||
|
version: '3' |
||||||
|
|
||||||
|
services: |
||||||
|
###> doctrine/doctrine-bundle ### |
||||||
|
database: |
||||||
|
image: postgres:${POSTGRES_VERSION:-15}-alpine |
||||||
|
environment: |
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-app} |
||||||
|
# You should definitely change the password in production |
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!} |
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-app} |
||||||
|
volumes: |
||||||
|
- database_data:/var/lib/postgresql/data:rw |
||||||
|
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data! |
||||||
|
# - ./docker/db/data:/var/lib/postgresql/data:rw |
||||||
|
###< doctrine/doctrine-bundle ### |
||||||
|
|
||||||
|
volumes: |
||||||
|
###> doctrine/doctrine-bundle ### |
||||||
|
database_data: |
||||||
|
###< doctrine/doctrine-bundle ### |
@ -1,10 +0,0 @@ |
|||||||
@reboot searchd |
|
||||||
@reboot indexer --all --rotate |
|
||||||
|
|
||||||
* * * * * indexer magnet --rotate > /dev/null 2>&1 |
|
||||||
|
|
||||||
* * * * * /usr/bin/php /YGGtracker/src/crontab/scrape.php > /dev/null 2>&1 |
|
||||||
* * * * * /usr/bin/php /YGGtracker/src/crontab/export/push.php > /dev/null 2>&1 |
|
||||||
0 5 * * * /usr/bin/php /YGGtracker/src/crontab/import/feed.php > /dev/null 2>&1 |
|
||||||
0 0 * * * /usr/bin/php /YGGtracker/src/crontab/export/feed.php > /dev/null 2>&1 |
|
||||||
0 0 * * * /usr/bin/php /YGGtracker/src/crontab/sitemap.php > /dev/null 2>&1 |
|
@ -1,29 +0,0 @@ |
|||||||
server { |
|
||||||
listen [::]:80 default; |
|
||||||
|
|
||||||
allow 0200::/7; |
|
||||||
deny all; |
|
||||||
|
|
||||||
root /var/www/html; |
|
||||||
|
|
||||||
index index.html index.htm index.nginx-debian.html index.php; |
|
||||||
|
|
||||||
server_name _; |
|
||||||
|
|
||||||
location / { |
|
||||||
try_files $uri $uri/ =404 @yggtracker; |
|
||||||
} |
|
||||||
|
|
||||||
location ~ \.php$ { |
|
||||||
include snippets/fastcgi-php.conf; |
|
||||||
fastcgi_pass unix:/run/php/php8.1-fpm.sock; |
|
||||||
} |
|
||||||
|
|
||||||
location ~ /\. { |
|
||||||
deny all; |
|
||||||
} |
|
||||||
|
|
||||||
location @yggtracker { |
|
||||||
rewrite ^/(.*)$ /index.php?$1 last; |
|
||||||
} |
|
||||||
} |
|
@ -1,71 +0,0 @@ |
|||||||
source yggtracker |
|
||||||
{ |
|
||||||
type = mysql |
|
||||||
|
|
||||||
sql_port = 3306 |
|
||||||
sql_host = localhost |
|
||||||
sql_user = |
|
||||||
sql_pass = |
|
||||||
sql_db = |
|
||||||
} |
|
||||||
|
|
||||||
source magnet : yggtracker |
|
||||||
{ |
|
||||||
sql_query = \ |
|
||||||
SELECT `magnet`.`timeAdded`, \ |
|
||||||
`magnet`.`timeUpdated`, \ |
|
||||||
`magnet`.`magnetId`, \ |
|
||||||
`magnet`.`title`, \ |
|
||||||
`magnet`.`preview`, \ |
|
||||||
`magnet`.`description`, \ |
|
||||||
`magnet`.`dn`, \ |
|
||||||
(SELECT GROUP_CONCAT(DISTINCT `infoHash`.`value`) \ |
|
||||||
FROM `infoHash` \ |
|
||||||
JOIN `magnetToInfoHash` ON (`magnetToInfoHash`.`magnetId` = `magnet`.`magnetId`) \ |
|
||||||
WHERE `infoHash`.`infoHashId` = `magnetToInfoHash`.`infoHashId`) AS `infoHash`, \ |
|
||||||
(SELECT GROUP_CONCAT(DISTINCT `keywordTopic`.`value`) \ |
|
||||||
FROM `keywordTopic` \ |
|
||||||
JOIN `magnetToKeywordTopic` ON (`magnetToKeywordTopic`.`magnetId` = `magnet`.`magnetId`) \ |
|
||||||
WHERE `keywordTopic`.`keywordTopicId` = `magnetToKeywordTopic`.`keywordTopicId`) AS `keywords`, \ |
|
||||||
(SELECT GROUP_CONCAT(DISTINCT `magnetComment`.`value`) \ |
|
||||||
FROM `magnetComment` \ |
|
||||||
WHERE `magnetComment`.`magnetId` = `magnet`.`magnetId`) AS `comments` \ |
|
||||||
FROM `magnet`\ |
|
||||||
|
|
||||||
sql_attr_uint = magnetId |
|
||||||
} |
|
||||||
|
|
||||||
index magnet |
|
||||||
{ |
|
||||||
source = magnet |
|
||||||
path = /var/lib/sphinxsearch/data/magnet |
|
||||||
|
|
||||||
morphology = stem_cz, stem_ar, stem_enru |
|
||||||
|
|
||||||
min_word_len = 2 |
|
||||||
min_prefix_len = 2 |
|
||||||
|
|
||||||
html_strip = 1 |
|
||||||
|
|
||||||
index_exact_words = 1 |
|
||||||
} |
|
||||||
|
|
||||||
indexer |
|
||||||
{ |
|
||||||
mem_limit = 256M |
|
||||||
} |
|
||||||
|
|
||||||
searchd |
|
||||||
{ |
|
||||||
listen = 127.0.0.1:9306:mysql41 |
|
||||||
log = /var/log/sphinxsearch/searchd.log |
|
||||||
query_log = /var/log/sphinxsearch/query.log |
|
||||||
pid_file = /run/sphinxsearch/searchd.pid |
|
||||||
binlog_path = /var/lib/sphinxsearch/data |
|
||||||
read_timeout = 5 |
|
||||||
max_children = 30 |
|
||||||
seamless_rotate = 1 |
|
||||||
preopen_indexes = 1 |
|
||||||
unlink_old = 1 |
|
||||||
workers = threads # for RT to work |
|
||||||
} |
|
@ -0,0 +1,38 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
|
||||||
|
<!-- https://phpunit.readthedocs.io/en/latest/configuration.html --> |
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" |
||||||
|
backupGlobals="false" |
||||||
|
colors="true" |
||||||
|
bootstrap="tests/bootstrap.php" |
||||||
|
convertDeprecationsToExceptions="false" |
||||||
|
> |
||||||
|
<php> |
||||||
|
<ini name="display_errors" value="1" /> |
||||||
|
<ini name="error_reporting" value="-1" /> |
||||||
|
<server name="APP_ENV" value="test" force="true" /> |
||||||
|
<server name="SHELL_VERBOSITY" value="-1" /> |
||||||
|
<server name="SYMFONY_PHPUNIT_REMOVE" value="" /> |
||||||
|
<server name="SYMFONY_PHPUNIT_VERSION" value="9.5" /> |
||||||
|
</php> |
||||||
|
|
||||||
|
<testsuites> |
||||||
|
<testsuite name="Project Test Suite"> |
||||||
|
<directory>tests</directory> |
||||||
|
</testsuite> |
||||||
|
</testsuites> |
||||||
|
|
||||||
|
<coverage processUncoveredFiles="true"> |
||||||
|
<include> |
||||||
|
<directory suffix=".php">src</directory> |
||||||
|
</include> |
||||||
|
</coverage> |
||||||
|
|
||||||
|
<listeners> |
||||||
|
<listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" /> |
||||||
|
</listeners> |
||||||
|
|
||||||
|
<extensions> |
||||||
|
</extensions> |
||||||
|
</phpunit> |
@ -0,0 +1,9 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use App\Kernel; |
||||||
|
|
||||||
|
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; |
||||||
|
|
||||||
|
return function (array $context) { |
||||||
|
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); |
||||||
|
}; |
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Controller; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||||
|
use Symfony\Component\Routing\Annotation\Route; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\HttpFoundation\Request; |
||||||
|
|
||||||
|
class HomeController extends AbstractController |
||||||
|
{ |
||||||
|
#[Route( |
||||||
|
'/{_locale}/', |
||||||
|
name: 'home_index' |
||||||
|
)] |
||||||
|
public function index(Request $request): Response |
||||||
|
{ |
||||||
|
return $this->render('default/home/index.html.twig'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Controller; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||||
|
use Symfony\Component\Routing\Annotation\Route; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\HttpFoundation\Request; |
||||||
|
|
||||||
|
class PageController extends AbstractController |
||||||
|
{ |
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/submit', |
||||||
|
name: 'page_submit' |
||||||
|
)] |
||||||
|
public function submit(): Response |
||||||
|
{ |
||||||
|
return $this->render('default/page/submit.html.twig', [ |
||||||
|
// @TODO |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/stars', |
||||||
|
name: 'page_stars' |
||||||
|
)] |
||||||
|
public function stars(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/views', |
||||||
|
name: 'page_views' |
||||||
|
)] |
||||||
|
public function views(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/downloads', |
||||||
|
name: 'page_downloads' |
||||||
|
)] |
||||||
|
public function downloads(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/comments', |
||||||
|
name: 'page_comments' |
||||||
|
)] |
||||||
|
public function comments(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/page/editions', |
||||||
|
name: 'page_editions' |
||||||
|
)] |
||||||
|
public function editions(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Controller; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||||
|
use Symfony\Component\Routing\Annotation\Route; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\HttpFoundation\Request; |
||||||
|
|
||||||
|
use App\Service\User; |
||||||
|
|
||||||
|
class ProfileController extends AbstractController |
||||||
|
{ |
||||||
|
#[Route( |
||||||
|
'/{_locale}/profile', |
||||||
|
name: 'profile_index' |
||||||
|
)] |
||||||
|
public function index(Request $request, User $user): Response |
||||||
|
{ |
||||||
|
return $this->render( |
||||||
|
'default/profile/index.html.twig', |
||||||
|
[ |
||||||
|
'user' => $user->init($request->getClientIp()) |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
#[Route( |
||||||
|
'/{_locale}/profile/setting', |
||||||
|
name: 'profile_setting' |
||||||
|
)] |
||||||
|
public function setting(): Response |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
return $this->render( |
||||||
|
'default/profile/setting.html.twig' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function module(string $route = ''): Response |
||||||
|
{ |
||||||
|
return $this->render( |
||||||
|
'default/profile/module.html.twig', |
||||||
|
[ |
||||||
|
'route' => $route, |
||||||
|
'stars' => 0, |
||||||
|
'views' => 0, |
||||||
|
'comments' => 0, |
||||||
|
'downloads' => 0, |
||||||
|
'editions' => 0, |
||||||
|
'identicon' => $this->_getIdenticon( |
||||||
|
'@TODO', |
||||||
|
17, |
||||||
|
[ |
||||||
|
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
||||||
|
] |
||||||
|
) |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
private function _getIdenticon( |
||||||
|
mixed $id, |
||||||
|
int $size, |
||||||
|
array $style, |
||||||
|
string $format = 'webp') : string |
||||||
|
{ |
||||||
|
$identicon = new \Jdenticon\Identicon(); |
||||||
|
|
||||||
|
$identicon->setValue($id); |
||||||
|
$identicon->setSize($size); |
||||||
|
$identicon->setStyle($style); |
||||||
|
|
||||||
|
return $identicon->getImageDataUri($format); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Controller; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; |
||||||
|
use Symfony\Component\Routing\Annotation\Route; |
||||||
|
use Symfony\Component\HttpFoundation\Response; |
||||||
|
use Symfony\Component\HttpFoundation\Request; |
||||||
|
|
||||||
|
class SearchController extends AbstractController |
||||||
|
{ |
||||||
|
#[Route( |
||||||
|
'/{_locale}/search', |
||||||
|
name: 'search_index' |
||||||
|
)] |
||||||
|
public function index(Request $request): Response |
||||||
|
{ |
||||||
|
$query = $request->query->get('query'); |
||||||
|
|
||||||
|
return $this->render('default/search/index.html.twig', [ |
||||||
|
'query' => $query |
||||||
|
]); |
||||||
|
} |
||||||
|
|
||||||
|
public function module(string $query = ''): Response |
||||||
|
{ |
||||||
|
return $this->render('default/search/module.html.twig', [ |
||||||
|
'query' => $query, |
||||||
|
]); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Entity; |
||||||
|
|
||||||
|
use App\Repository\PageRepository; |
||||||
|
use Doctrine\ORM\Mapping as ORM; |
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: PageRepository::class)] |
||||||
|
class Page |
||||||
|
{ |
||||||
|
#[ORM\Id] |
||||||
|
#[ORM\GeneratedValue] |
||||||
|
#[ORM\Column] |
||||||
|
private ?int $id = null; |
||||||
|
|
||||||
|
public function getId(): ?int |
||||||
|
{ |
||||||
|
return $this->id; |
||||||
|
} |
||||||
|
|
||||||
|
public function setId(string $id): static |
||||||
|
{ |
||||||
|
$this->id = $id; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,147 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Entity; |
||||||
|
|
||||||
|
use App\Repository\UserRepository; |
||||||
|
use Doctrine\ORM\Mapping as ORM; |
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: UserRepository::class)] |
||||||
|
class User |
||||||
|
{ |
||||||
|
#[ORM\Id] |
||||||
|
#[ORM\GeneratedValue] |
||||||
|
#[ORM\Column] |
||||||
|
private ?int $id = null; |
||||||
|
|
||||||
|
#[ORM\Column(length: 255)] |
||||||
|
private ?string $address = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?int $added = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?int $updated = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?int $visited = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?bool $public = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?bool $moderator = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?bool $approved = null; |
||||||
|
|
||||||
|
#[ORM\Column] |
||||||
|
private ?bool $status = null; |
||||||
|
|
||||||
|
public function getId(): ?int |
||||||
|
{ |
||||||
|
return $this->id; |
||||||
|
} |
||||||
|
|
||||||
|
public function setId(string $id): static |
||||||
|
{ |
||||||
|
$this->id = $id; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getAddress(): ?string |
||||||
|
{ |
||||||
|
return $this->address; |
||||||
|
} |
||||||
|
|
||||||
|
public function setAddress(string $address): static |
||||||
|
{ |
||||||
|
$this->address = $address; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getAdded(): ?int |
||||||
|
{ |
||||||
|
return $this->added; |
||||||
|
} |
||||||
|
|
||||||
|
public function setAdded(int $added): static |
||||||
|
{ |
||||||
|
$this->added = $added; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getUpdated(): ?int |
||||||
|
{ |
||||||
|
return $this->updated; |
||||||
|
} |
||||||
|
|
||||||
|
public function setUpdated(int $updated): static |
||||||
|
{ |
||||||
|
$this->updated = $updated; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function getVisited(): ?int |
||||||
|
{ |
||||||
|
return $this->visited; |
||||||
|
} |
||||||
|
|
||||||
|
public function setVisited(int $visited): static |
||||||
|
{ |
||||||
|
$this->visited = $visited; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function isPublic(): ?bool |
||||||
|
{ |
||||||
|
return $this->public; |
||||||
|
} |
||||||
|
|
||||||
|
public function setPublic(bool $public): static |
||||||
|
{ |
||||||
|
$this->public = $public; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function isModerator(): ?bool |
||||||
|
{ |
||||||
|
return $this->moderator; |
||||||
|
} |
||||||
|
|
||||||
|
public function setModerator(bool $moderator): static |
||||||
|
{ |
||||||
|
$this->moderator = $moderator; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function isApproved(): ?bool |
||||||
|
{ |
||||||
|
return $this->approved; |
||||||
|
} |
||||||
|
|
||||||
|
public function setApproved(bool $approved): static |
||||||
|
{ |
||||||
|
$this->approved = $approved; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
public function isStatus(): ?bool |
||||||
|
{ |
||||||
|
return $this->status; |
||||||
|
} |
||||||
|
|
||||||
|
public function setStatus(bool $status): static |
||||||
|
{ |
||||||
|
$this->status = $status; |
||||||
|
|
||||||
|
return $this; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App; |
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; |
||||||
|
use Symfony\Component\HttpKernel\Kernel as BaseKernel; |
||||||
|
|
||||||
|
class Kernel extends BaseKernel |
||||||
|
{ |
||||||
|
use MicroKernelTrait; |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Repository; |
||||||
|
|
||||||
|
use App\Entity\Page; |
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
||||||
|
use Doctrine\Persistence\ManagerRegistry; |
||||||
|
|
||||||
|
/** |
||||||
|
* @extends ServiceEntityRepository<Page> |
||||||
|
* |
||||||
|
* @method Page|null find($id, $lockMode = null, $lockVersion = null) |
||||||
|
* @method Page|null findOneBy(array $criteria, array $orderBy = null) |
||||||
|
* @method Page[] findAll() |
||||||
|
* @method Page[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||||||
|
*/ |
||||||
|
class PageRepository extends ServiceEntityRepository |
||||||
|
{ |
||||||
|
public function __construct(ManagerRegistry $registry) |
||||||
|
{ |
||||||
|
parent::__construct($registry, Page::class); |
||||||
|
} |
||||||
|
|
||||||
|
// /** |
||||||
|
// * @return Page[] Returns an array of Page objects |
||||||
|
// */ |
||||||
|
// public function findByExampleField($value): array |
||||||
|
// { |
||||||
|
// return $this->createQueryBuilder('p') |
||||||
|
// ->andWhere('p.exampleField = :val') |
||||||
|
// ->setParameter('val', $value) |
||||||
|
// ->orderBy('p.id', 'ASC') |
||||||
|
// ->setMaxResults(10) |
||||||
|
// ->getQuery() |
||||||
|
// ->getResult() |
||||||
|
// ; |
||||||
|
// } |
||||||
|
|
||||||
|
// public function findOneBySomeField($value): ?Page |
||||||
|
// { |
||||||
|
// return $this->createQueryBuilder('p') |
||||||
|
// ->andWhere('p.exampleField = :val') |
||||||
|
// ->setParameter('val', $value) |
||||||
|
// ->getQuery() |
||||||
|
// ->getOneOrNullResult() |
||||||
|
// ; |
||||||
|
// } |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Repository; |
||||||
|
|
||||||
|
use App\Entity\User; |
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; |
||||||
|
use Doctrine\Persistence\ManagerRegistry; |
||||||
|
|
||||||
|
/** |
||||||
|
* @extends ServiceEntityRepository<User> |
||||||
|
* |
||||||
|
* @method User|null find($id, $lockMode = null, $lockVersion = null) |
||||||
|
* @method User|null findOneBy(array $criteria, array $orderBy = null) |
||||||
|
* @method User[] findAll() |
||||||
|
* @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) |
||||||
|
*/ |
||||||
|
class UserRepository extends ServiceEntityRepository |
||||||
|
{ |
||||||
|
public function __construct(ManagerRegistry $registry) |
||||||
|
{ |
||||||
|
parent::__construct($registry, User::class); |
||||||
|
} |
||||||
|
|
||||||
|
// /** |
||||||
|
// * @return User[] Returns an array of User objects |
||||||
|
// */ |
||||||
|
// public function findByExampleField($value): array |
||||||
|
// { |
||||||
|
// return $this->createQueryBuilder('u') |
||||||
|
// ->andWhere('u.exampleField = :val') |
||||||
|
// ->setParameter('val', $value) |
||||||
|
// ->orderBy('u.id', 'ASC') |
||||||
|
// ->setMaxResults(10) |
||||||
|
// ->getQuery() |
||||||
|
// ->getResult() |
||||||
|
// ; |
||||||
|
// } |
||||||
|
|
||||||
|
// public function findOneBySomeField($value): ?User |
||||||
|
// { |
||||||
|
// return $this->createQueryBuilder('u') |
||||||
|
// ->andWhere('u.exampleField = :val') |
||||||
|
// ->setParameter('val', $value) |
||||||
|
// ->getQuery() |
||||||
|
// ->getOneOrNullResult() |
||||||
|
// ; |
||||||
|
// } |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Service; |
||||||
|
|
||||||
|
class User |
||||||
|
{ |
||||||
|
public function init(string $address): string |
||||||
|
{ |
||||||
|
// @TODO |
||||||
|
return $address; |
||||||
|
} |
||||||
|
} |
@ -1,151 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerIndex |
|
||||||
{ |
|
||||||
private $_database; |
|
||||||
private $_validator; |
|
||||||
private $_website; |
|
||||||
private $_session; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
AppModelDatabase $database, |
|
||||||
AppModelValidator $validator, |
|
||||||
AppModelWebsite $website, |
|
||||||
AppModelSession $session |
|
||||||
) |
|
||||||
{ |
|
||||||
$this->_database = $database; |
|
||||||
$this->_validator = $validator; |
|
||||||
$this->_website = $website; |
|
||||||
$this->_session = $session; |
|
||||||
} |
|
||||||
|
|
||||||
private function _initUser(string $address) |
|
||||||
{ |
|
||||||
$error = []; |
|
||||||
if (!$this->_validator->host($address, $error)) |
|
||||||
{ |
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('406'), |
|
||||||
$error, |
|
||||||
406 |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
try |
|
||||||
{ |
|
||||||
$this->_database->beginTransaction(); |
|
||||||
|
|
||||||
$user = $this->_database->getUser( |
|
||||||
$this->_database->initUserId( |
|
||||||
$address, |
|
||||||
$this->_website->getDefaultUserStatus(), |
|
||||||
$this->_website->getDefaultUserApproved(), |
|
||||||
time() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->_database->commit(); |
|
||||||
} |
|
||||||
|
|
||||||
catch (Exception $error) |
|
||||||
{ |
|
||||||
$this->_database->rollback(); |
|
||||||
|
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('500'), |
|
||||||
$error, |
|
||||||
500 |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Access denied |
|
||||||
if (!$user->status) |
|
||||||
{ |
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('403'), |
|
||||||
_('Access denied'), |
|
||||||
403 |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$user = $this->_initUser( |
|
||||||
$this->_session->getAddress() |
|
||||||
); |
|
||||||
|
|
||||||
$page = isset($_GET['page']) ? (int) $_GET['page'] : 1; |
|
||||||
|
|
||||||
$pages = []; |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/pagination.php'; |
|
||||||
|
|
||||||
$appControllerModulePagination = new appControllerModulePagination(); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/head.php'; |
|
||||||
|
|
||||||
$appControllerModuleHead = new AppControllerModuleHead( |
|
||||||
$this->_website->getUrl(), |
|
||||||
$page > 1 ? |
|
||||||
sprintf( |
|
||||||
_('Page %s - BitTorrent Registry for Yggdrasil - %s'), |
|
||||||
$page, |
|
||||||
$this->_website->getName() |
|
||||||
) : |
|
||||||
sprintf( |
|
||||||
_('%s - BitTorrent Registry for Yggdrasil'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
[ |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/common.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/framework.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/profile.php'; |
|
||||||
|
|
||||||
$appControllerModuleProfile = new AppControllerModuleProfile( |
|
||||||
$this->_database, |
|
||||||
$this->_website, |
|
||||||
$this->_session |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/header.php'; |
|
||||||
|
|
||||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/footer.php'; |
|
||||||
|
|
||||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
|
||||||
|
|
||||||
include __DIR__ . '/../view/theme/default/index.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,13 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModuleFooter |
|
||||||
{ |
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$trackers = Environment::config('trackers'); |
|
||||||
|
|
||||||
$api = Environment::config('website')->api->export; |
|
||||||
|
|
||||||
include __DIR__ . '../../../view/theme/default/module/footer.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModuleHead |
|
||||||
{ |
|
||||||
private $_title; |
|
||||||
private $_base; |
|
||||||
private $_links = []; |
|
||||||
|
|
||||||
public function __construct(string $base, string $title, array $links = []) |
|
||||||
{ |
|
||||||
$this->setBase($base); |
|
||||||
$this->setTitle($title); |
|
||||||
|
|
||||||
foreach ($links as $link) |
|
||||||
{ |
|
||||||
$this->addLink( |
|
||||||
$link['rel'], |
|
||||||
$link['type'], |
|
||||||
$link['href'], |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function setBase(string $base) : void |
|
||||||
{ |
|
||||||
$this->_base = $base; |
|
||||||
} |
|
||||||
|
|
||||||
public function setTitle(string $title) : void |
|
||||||
{ |
|
||||||
$this->_title = $title; |
|
||||||
} |
|
||||||
|
|
||||||
public function addLink(string $rel, string $type, string $href) : void |
|
||||||
{ |
|
||||||
$this->_links[] = (object) |
|
||||||
[ |
|
||||||
'rel' => $rel, |
|
||||||
'type' => $type, |
|
||||||
'href' => $href, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$base = $this->_base; |
|
||||||
|
|
||||||
$links = $this->_links; |
|
||||||
|
|
||||||
$title = htmlentities($this->_title); |
|
||||||
|
|
||||||
include __DIR__ . '../../../view/theme/default/module/head.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,19 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModuleHeader |
|
||||||
{ |
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$name = str_replace( |
|
||||||
'YGG', |
|
||||||
'<span>YGG</span>', |
|
||||||
Environment::config('website')->name |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/search.php'; |
|
||||||
|
|
||||||
$appControllerModuleSearch = new AppControllerModuleSearch(); |
|
||||||
|
|
||||||
include __DIR__ . '../../../view/theme/default/module/header.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,9 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerCommonPage |
|
||||||
{ |
|
||||||
public function render(int $pageId) |
|
||||||
{ |
|
||||||
include __DIR__ . '../../../view/theme/default/common/page.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModulePagination |
|
||||||
{ |
|
||||||
public function render(string $url, int $total, int $limit) |
|
||||||
{ |
|
||||||
if ($total > $limit) |
|
||||||
{ |
|
||||||
parse_str($url, $query); |
|
||||||
|
|
||||||
$pagination->page = isset($query['total']) ? (int) $query['total'] : 1; |
|
||||||
$pagination->pages = ceil($total / $limit); |
|
||||||
|
|
||||||
// Previous |
|
||||||
if ($page > 1) |
|
||||||
{ |
|
||||||
$query['page'] = $page - 1; |
|
||||||
|
|
||||||
$pagination->back = sprintf('%s', WEBSITE_URL, http_build_query($query)); |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$pagination->back = false; |
|
||||||
} |
|
||||||
|
|
||||||
// Next |
|
||||||
if ($page < ceil($total / $limit)) |
|
||||||
{ |
|
||||||
$query['page'] = $page + 1; |
|
||||||
|
|
||||||
$pagination->next = sprintf('%s', WEBSITE_URL, http_build_query($query)); |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$pagination->next = false; |
|
||||||
} |
|
||||||
|
|
||||||
// Render |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModuleProfile |
|
||||||
{ |
|
||||||
private $_database; |
|
||||||
private $_website; |
|
||||||
private $_session; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
AppModelDatabase $database, |
|
||||||
AppModelWebsite $website, |
|
||||||
AppModelSession $session) |
|
||||||
{ |
|
||||||
$this->_database = $database; |
|
||||||
$this->_website = $website; |
|
||||||
$this->_session = $session; |
|
||||||
} |
|
||||||
|
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$route = isset($_GET['_route_']) ? (string) $_GET['_route_'] : ''; |
|
||||||
|
|
||||||
$user = $this->_database->getUser( |
|
||||||
$this->_database->initUserId( |
|
||||||
$this->_session->getAddress(), |
|
||||||
$this->_website->getDefaultUserStatus(), |
|
||||||
$this->_website->getDefaultUserApproved(), |
|
||||||
time() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$stars = $this->_database->findUserPageStarsDistinctTotalByValue($user->userId, true); |
|
||||||
$views = $this->_database->findUserPageViewsDistinctTotal($user->userId); |
|
||||||
$downloads = 0; // @TODO $this->_database->findUserPageDownloadsDistinctTotal($user->userId); |
|
||||||
$comments = $this->_database->findUserPageCommentsDistinctTotal($user->userId); |
|
||||||
$editions = 0; // @TODO $this->_database->findUserPageEditionsDistinctTotal($user->userId); |
|
||||||
|
|
||||||
$address = $user->address; |
|
||||||
|
|
||||||
$icon = new Jdenticon\Identicon(); |
|
||||||
|
|
||||||
$icon->setValue($user->address); |
|
||||||
$icon->setSize(16); |
|
||||||
$icon->setStyle( |
|
||||||
[ |
|
||||||
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
$identicon = $icon->getImageDataUri('webp'); |
|
||||||
|
|
||||||
include __DIR__ . '../../../view/theme/default/module/profile.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerModuleSearch |
|
||||||
{ |
|
||||||
public function render() |
|
||||||
{ |
|
||||||
$query = empty($_GET['query']) ? false : urldecode($_GET['query']); |
|
||||||
$locale = empty($_GET['locale']) ? 'all' : urldecode($_GET['locale']); |
|
||||||
|
|
||||||
$locales = []; |
|
||||||
|
|
||||||
foreach (Environment::config('locales') as $key => $value) |
|
||||||
{ |
|
||||||
$locales[$key] = (object) |
|
||||||
[ |
|
||||||
'key' => $key, |
|
||||||
'value' => $value[0], |
|
||||||
'active' => $key === $locale // false !== stripos($_SERVER['HTTP_ACCEPT_LANGUAGE'], $key) ? true : false, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
include __DIR__ . '../../../view/theme/default/module/search.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,428 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerPage |
|
||||||
{ |
|
||||||
private $_database; |
|
||||||
private $_validator; |
|
||||||
private $_locale; |
|
||||||
private $_website; |
|
||||||
private $_session; |
|
||||||
private $_request; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
AppModelDatabase $database, |
|
||||||
AppModelValidator $validator, |
|
||||||
AppModelLocale $locale, |
|
||||||
AppModelWebsite $website, |
|
||||||
AppModelSession $session, |
|
||||||
AppModelRequest $request |
|
||||||
) |
|
||||||
{ |
|
||||||
$this->_database = $database; |
|
||||||
$this->_validator = $validator; |
|
||||||
$this->_locale = $locale; |
|
||||||
$this->_website = $website; |
|
||||||
$this->_session = $session; |
|
||||||
$this->_request = $request; |
|
||||||
} |
|
||||||
|
|
||||||
private function _response(string $title, string $h1, mixed $data, int $code = 200) |
|
||||||
{ |
|
||||||
require_once __DIR__ . '/response.php'; |
|
||||||
|
|
||||||
if (is_array($data)) |
|
||||||
{ |
|
||||||
$data = implode('<br />', $data); |
|
||||||
} |
|
||||||
|
|
||||||
$appControllerResponse = new AppControllerResponse( |
|
||||||
$title, |
|
||||||
$h1, |
|
||||||
$data, |
|
||||||
$code |
|
||||||
); |
|
||||||
|
|
||||||
$appControllerResponse->render(); |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
private function _initUser(string $address) |
|
||||||
{ |
|
||||||
$error = []; |
|
||||||
if (!$this->_validator->host($address, $error)) |
|
||||||
{ |
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('406'), |
|
||||||
$error, |
|
||||||
406 |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
try |
|
||||||
{ |
|
||||||
$this->_database->beginTransaction(); |
|
||||||
|
|
||||||
$user = $this->_database->getUser( |
|
||||||
$this->_database->initUserId( |
|
||||||
$address, |
|
||||||
$this->_website->getDefaultUserStatus(), |
|
||||||
$this->_website->getDefaultUserApproved(), |
|
||||||
time() |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$this->_database->commit(); |
|
||||||
} |
|
||||||
|
|
||||||
catch (Exception $error) |
|
||||||
{ |
|
||||||
$this->_database->rollback(); |
|
||||||
|
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('500'), |
|
||||||
$error, |
|
||||||
500 |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// Access denied |
|
||||||
if (!$user->status) |
|
||||||
{ |
|
||||||
$this->_response( |
|
||||||
sprintf( |
|
||||||
_('Error - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
_('403'), |
|
||||||
_('Access denied'), |
|
||||||
403 |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private function _initLocale(string $value) |
|
||||||
{ |
|
||||||
if (!$locale = $this->_database->findLocale($value)) |
|
||||||
{ |
|
||||||
$locale = $this->_database->getLocale( |
|
||||||
$this->_database->addLocale( |
|
||||||
$value |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $locale; |
|
||||||
} |
|
||||||
|
|
||||||
private function _initPage(int $pageId = 0) |
|
||||||
{ |
|
||||||
if (!$page = $this->_database->getPage($pageId)) |
|
||||||
{ |
|
||||||
$page = $this->_database->getPage( |
|
||||||
$this->_database->addPage( |
|
||||||
time() |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $page; |
|
||||||
} |
|
||||||
|
|
||||||
private function _initText(string $value, string $mime = 'text/plain') |
|
||||||
{ |
|
||||||
if (!$text = $this->_database->findText($mime, md5($value))) |
|
||||||
{ |
|
||||||
$text = $this->_database->getText( |
|
||||||
$this->_database->addText( |
|
||||||
$mime, |
|
||||||
md5($value), |
|
||||||
$value, |
|
||||||
time() |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
return $text; |
|
||||||
} |
|
||||||
|
|
||||||
private function _commitPageTitle(int $pageId, int $userId, int $localeId, string $text, string $mime = 'text/plain') |
|
||||||
{ |
|
||||||
$textId = $this->_initText( |
|
||||||
$text, |
|
||||||
$mime |
|
||||||
)->textId; |
|
||||||
|
|
||||||
if (!$this->_database->findPageTitleLatest($pageId, |
|
||||||
$userId, |
|
||||||
$localeId, |
|
||||||
$textId)) |
|
||||||
{ |
|
||||||
$this->_database->addPageTitle( |
|
||||||
$pageId, |
|
||||||
$userId, |
|
||||||
$localeId, |
|
||||||
$textId, |
|
||||||
time() |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function renderFormSubmit() |
|
||||||
{ |
|
||||||
// Init user |
|
||||||
$user = $this->_initUser( |
|
||||||
$this->_session->getAddress() |
|
||||||
); |
|
||||||
|
|
||||||
// Init page |
|
||||||
if ($this->_request->get('pageId')) |
|
||||||
{ |
|
||||||
$page = $this->_initPage( |
|
||||||
(int) $this->_request->get('pageId') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
else if ($this->_request->post('pageId')) |
|
||||||
{ |
|
||||||
$page = $this->_initPage( |
|
||||||
(int) $this->_request->post('pageId') |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$page = $this->_initPage(); |
|
||||||
} |
|
||||||
|
|
||||||
// Init locale |
|
||||||
if ($this->_locale->codeExists($this->_request->get('locale'))) |
|
||||||
{ |
|
||||||
$localeCode = (int) $this->_request->get('locale'); |
|
||||||
} |
|
||||||
|
|
||||||
else if ($this->_locale->codeExists($this->_request->post('locale'))) |
|
||||||
{ |
|
||||||
$localeCode = (int) $this->_request->post('locale'); |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$localeCode = $this->_website->getDefaultLocale(); |
|
||||||
|
|
||||||
if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) // @TODO environment |
|
||||||
{ |
|
||||||
foreach ($this->_locale->getList() as $value) |
|
||||||
{ |
|
||||||
if (false !== stripos($_SERVER['HTTP_ACCEPT_LANGUAGE'], $value->code)) |
|
||||||
{ |
|
||||||
$localeCode = $value->code; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$locale = $this->_initLocale($localeCode); |
|
||||||
|
|
||||||
// Init form |
|
||||||
$form = (object) |
|
||||||
[ |
|
||||||
'pageId' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'hidden', |
|
||||||
'attribute' => (object) |
|
||||||
[ |
|
||||||
'value' => $page->pageId, |
|
||||||
] |
|
||||||
], |
|
||||||
'locale' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'select', |
|
||||||
'options' => $this->_locale->getList(), |
|
||||||
'value' => $locale->value, |
|
||||||
'placeholder' => _('Page content language'), |
|
||||||
], |
|
||||||
'title' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'text', |
|
||||||
'attribute' => (object) |
|
||||||
[ |
|
||||||
'value' => null, |
|
||||||
'required' => $this->_validator->getPageTitleRequired(), |
|
||||||
'minlength' => $this->_validator->getPageTitleLengthMin(), |
|
||||||
'maxlength' => $this->_validator->getPageTitleLengthMax(), |
|
||||||
'placeholder' => sprintf( |
|
||||||
_('Page subject (%s-%s chars)'), |
|
||||||
number_format($this->_validator->getPageTitleLengthMin()), |
|
||||||
number_format($this->_validator->getPageTitleLengthMax()) |
|
||||||
), |
|
||||||
] |
|
||||||
], |
|
||||||
'description' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'textarea', |
|
||||||
'attribute' => (object) |
|
||||||
[ |
|
||||||
'value' => null, |
|
||||||
'required' => $this->_validator->getPageDescriptionRequired(), |
|
||||||
'minlength' => $this->_validator->getPageDescriptionLengthMin(), |
|
||||||
'maxlength' => $this->_validator->getPageDescriptionLengthMax(), |
|
||||||
'placeholder' => sprintf( |
|
||||||
_('Page description text (%s-%s chars)'), |
|
||||||
number_format($this->_validator->getPageDescriptionLengthMin()), |
|
||||||
number_format($this->_validator->getPageDescriptionLengthMax()) |
|
||||||
), |
|
||||||
] |
|
||||||
], |
|
||||||
'keywords' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'textarea', |
|
||||||
'attribute' => (object) |
|
||||||
[ |
|
||||||
'value' => null, |
|
||||||
'required' => $this->_validator->getPageKeywordsRequired(), |
|
||||||
'placeholder' => sprintf( |
|
||||||
_('Page keywords (%s-%s total / %s-%s chars per item)'), |
|
||||||
number_format($this->_validator->getPageKeywordsQuantityMin()), |
|
||||||
number_format($this->_validator->getPageKeywordsQuantityMax()), |
|
||||||
number_format($this->_validator->getPageKeywordLengthMin()), |
|
||||||
number_format($this->_validator->getPageKeywordLengthMax()) |
|
||||||
), |
|
||||||
] |
|
||||||
], |
|
||||||
'sensitive' => (object) |
|
||||||
[ |
|
||||||
'error' => [], |
|
||||||
'type' => 'checkbox', |
|
||||||
'attribute' => (object) |
|
||||||
[ |
|
||||||
'value' => null, |
|
||||||
'placeholder' => _('Apply NSFW filters for this publication'), |
|
||||||
] |
|
||||||
] |
|
||||||
]; |
|
||||||
|
|
||||||
// Submit request |
|
||||||
if ($this->_request->hasPost()) |
|
||||||
{ |
|
||||||
/// Title |
|
||||||
if ($title = $this->_request->post('title')) |
|
||||||
{ |
|
||||||
$error = []; |
|
||||||
|
|
||||||
if (!$this->_validator->pageTitle($title, $error)) |
|
||||||
{ |
|
||||||
$form->title->error[] = $error; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
$this->_commitPageTitle( |
|
||||||
$page->pageId, |
|
||||||
$user->userId, |
|
||||||
$locale->localeId, |
|
||||||
$title |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
$form->title->attribute->value = htmlentities($title); |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($_POST['description'])) |
|
||||||
{ |
|
||||||
$error = []; |
|
||||||
|
|
||||||
if (!$this->_validator->pageDescription($_POST['description'], $error)) |
|
||||||
{ |
|
||||||
$form->description->error[] = $error; |
|
||||||
} |
|
||||||
|
|
||||||
$form->description->attribute->value = htmlentities($_POST['description']); |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($_POST['keywords'])) |
|
||||||
{ |
|
||||||
$error = []; |
|
||||||
|
|
||||||
if (!$this->_validator->pageKeywords($_POST['keywords'], $error)) |
|
||||||
{ |
|
||||||
$form->keywords->error[] = $error; |
|
||||||
} |
|
||||||
|
|
||||||
$form->keywords->attribute->value = htmlentities($_POST['keywords']); |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($_POST['sensitive'])) |
|
||||||
{ |
|
||||||
$form->sensitive->attribute->value = (bool) $_POST['sensitive']; |
|
||||||
} |
|
||||||
|
|
||||||
// Request valid |
|
||||||
if (empty($error)) |
|
||||||
{ |
|
||||||
// @TODO redirect |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Render template |
|
||||||
require_once __DIR__ . '/module/head.php'; |
|
||||||
|
|
||||||
$appControllerModuleHead = new AppControllerModuleHead( |
|
||||||
$this->_website->getUrl(), |
|
||||||
sprintf( |
|
||||||
_('Submit - %s'), |
|
||||||
$this->_website->getName() |
|
||||||
), |
|
||||||
[ |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/common.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/framework.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/profile.php'; |
|
||||||
|
|
||||||
$appControllerModuleProfile = new AppControllerModuleProfile( |
|
||||||
$this->_database, |
|
||||||
$this->_website, |
|
||||||
$this->_session |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/header.php'; |
|
||||||
|
|
||||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/footer.php'; |
|
||||||
|
|
||||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
|
||||||
|
|
||||||
include __DIR__ . '../../view/theme/default/page/form/submit.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,65 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerResponse |
|
||||||
{ |
|
||||||
private $_title; |
|
||||||
private $_h1; |
|
||||||
private $_text; |
|
||||||
private $_code; |
|
||||||
|
|
||||||
public function __construct(string $title, string $h1, string $text, int $code = 200) |
|
||||||
{ |
|
||||||
$this->_title = $title; |
|
||||||
$this->_h1 = $h1; |
|
||||||
$this->_text = $text; |
|
||||||
$this->_code = $code; |
|
||||||
} |
|
||||||
|
|
||||||
public function render() |
|
||||||
{ |
|
||||||
header( |
|
||||||
sprintf( |
|
||||||
'HTTP/1.0 %s Not Found', |
|
||||||
$this->_code |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$h1 = $this->_h1; |
|
||||||
$text = $this->_text; |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/head.php'; |
|
||||||
|
|
||||||
$appControllerModuleHead = new AppControllerModuleHead( |
|
||||||
Environment::config('website')->url, |
|
||||||
$this->_title, |
|
||||||
[ |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/common.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
[ |
|
||||||
'rel' => 'stylesheet', |
|
||||||
'type' => 'text/css', |
|
||||||
'href' => sprintf( |
|
||||||
'assets/theme/default/css/framework.css?%s', |
|
||||||
CSS_VERSION |
|
||||||
), |
|
||||||
], |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/header.php'; |
|
||||||
|
|
||||||
$appControllerModuleHeader = new AppControllerModuleHeader(); |
|
||||||
|
|
||||||
require_once __DIR__ . '/module/footer.php'; |
|
||||||
|
|
||||||
$appControllerModuleFooter = new AppControllerModuleFooter(); |
|
||||||
|
|
||||||
include __DIR__ . '../../view/theme/default/response.phtml'; |
|
||||||
} |
|
||||||
} |
|
@ -1,117 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppControllerUser |
|
||||||
{ |
|
||||||
private $_database; |
|
||||||
private $_validator; |
|
||||||
private $_website; |
|
||||||
|
|
||||||
private $_user; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
AppModelDatabase $database, |
|
||||||
AppModelValidator $validator, |
|
||||||
AppModelWebsite $website |
|
||||||
) |
|
||||||
{ |
|
||||||
$this->_database = $database; |
|
||||||
$this->_validator = $validator; |
|
||||||
$this->_website = $website; |
|
||||||
} |
|
||||||
|
|
||||||
private function _response(string $title, string $h1, mixed $data, int $code = 200) |
|
||||||
{ |
|
||||||
require_once __DIR__ . '/response.php'; |
|
||||||
|
|
||||||
if (is_array($data)) |
|
||||||
{ |
|
||||||
$data = implode('<br />', $data); |
|
||||||
} |
|
||||||
|
|
||||||
$appControllerResponse = new AppControllerResponse( |
|
||||||
$title, |
|
||||||
$h1, |
|
||||||
$data, |
|
||||||
$code |
|
||||||
); |
|
||||||
|
|
||||||
$appControllerResponse->render(); |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
public function getIdenticon(int $size) |
|
||||||
{ |
|
||||||
$icon = new Jdenticon\Identicon(); |
|
||||||
|
|
||||||
$icon->setValue($this->_user->public ? $this->_user->address : $this->_user->userId); |
|
||||||
$icon->setSize($size); |
|
||||||
$icon->setStyle( |
|
||||||
[ |
|
||||||
'backgroundColor' => 'rgba(255, 255, 255, 0)', |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
return $icon->getImageDataUri('webp'); |
|
||||||
} |
|
||||||
|
|
||||||
public function getUser() |
|
||||||
{ |
|
||||||
return $this->_user; |
|
||||||
} |
|
||||||
|
|
||||||
public function getPublic() |
|
||||||
{ |
|
||||||
return $this->_user->public; |
|
||||||
} |
|
||||||
|
|
||||||
public function getAddress() |
|
||||||
{ |
|
||||||
return $this->_user->address; |
|
||||||
} |
|
||||||
|
|
||||||
public function findUserPageStarsDistinctTotalByValue(bool $value) : int |
|
||||||
{ |
|
||||||
return $this->_database->findUserPageStarsDistinctTotal( |
|
||||||
$this->_user->userId, |
|
||||||
$value |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function findUserPageViewsDistinctTotal() : int |
|
||||||
{ |
|
||||||
return $this->_database->findUserPageViewsDistinctTotal( |
|
||||||
$this->_user->userId |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function findUserPageDownloadsDistinctTotal() : int |
|
||||||
{ |
|
||||||
return $this->_database->findUserPageDownloadsDistinctTotal( |
|
||||||
$this->_user->userId |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function findUserPageCommentsDistinctTotal() : int |
|
||||||
{ |
|
||||||
return $this->_database->findUserPageCommentsDistinctTotal( |
|
||||||
$this->_user->userId |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function findUserPageEditionsDistinctTotal() : int |
|
||||||
{ |
|
||||||
return $this->_database->findUserPageEditionsDistinctTotal( |
|
||||||
$this->_user->userId |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
public function updateUserPublic(bool $public, int $time) : int |
|
||||||
{ |
|
||||||
return $this->_database->updateUserPublic( |
|
||||||
$this->_user->userId, |
|
||||||
$public, |
|
||||||
$time |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppModelLocale { |
|
||||||
|
|
||||||
private $_locales = []; |
|
||||||
|
|
||||||
public function __construct(object $locales) |
|
||||||
{ |
|
||||||
foreach ($locales as $code => $value) |
|
||||||
{ |
|
||||||
$this->_locales[] = (object) |
|
||||||
[ |
|
||||||
'code' => $code, |
|
||||||
'value' => $value[0], |
|
||||||
'active' => false, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function getList() : object |
|
||||||
{ |
|
||||||
return (object) $this->_locales; |
|
||||||
} |
|
||||||
|
|
||||||
public function codeExists(string $code) : bool |
|
||||||
{ |
|
||||||
foreach ($this->_locales as $locale) |
|
||||||
{ |
|
||||||
if ($locale->code === $code) |
|
||||||
{ |
|
||||||
return true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
@ -1,104 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppModelRequest { |
|
||||||
|
|
||||||
private array $_get; |
|
||||||
private array $_post; |
|
||||||
private array $_files; |
|
||||||
private array $_server; |
|
||||||
|
|
||||||
public function __construct(array $get, array $post, array $files, array $server) |
|
||||||
{ |
|
||||||
$this->_get = $get; |
|
||||||
$this->_post = $post; |
|
||||||
$this->_files = $files; |
|
||||||
$this->_server = $server; |
|
||||||
} |
|
||||||
|
|
||||||
public function get(string $key, mixed $value = null) : mixed |
|
||||||
{ |
|
||||||
if ($value) |
|
||||||
{ |
|
||||||
$this->_get[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($this->_get[$key])) |
|
||||||
{ |
|
||||||
return $this->_get[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function post(string $key, mixed $value = null) : mixed |
|
||||||
{ |
|
||||||
if ($value) |
|
||||||
{ |
|
||||||
$this->_get[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($this->_post[$key])) |
|
||||||
{ |
|
||||||
return $this->_post[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function files(string $key, mixed $value = null) : mixed |
|
||||||
{ |
|
||||||
if ($value) |
|
||||||
{ |
|
||||||
$this->_get[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($this->_files[$key])) |
|
||||||
{ |
|
||||||
return $this->_files[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function server(string $key, mixed $value = null) : mixed |
|
||||||
{ |
|
||||||
if ($value) |
|
||||||
{ |
|
||||||
$this->_get[$key] = $value; |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($this->_get[$key])) |
|
||||||
{ |
|
||||||
return $this->_get[$key]; |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function hasPost() : bool |
|
||||||
{ |
|
||||||
return !empty($this->_post); |
|
||||||
} |
|
||||||
|
|
||||||
public function hasGet() : bool |
|
||||||
{ |
|
||||||
return !empty($this->_get); |
|
||||||
} |
|
||||||
|
|
||||||
public function hasFiles() : bool |
|
||||||
{ |
|
||||||
return !empty($this->_files); |
|
||||||
} |
|
||||||
} |
|
@ -1,111 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppModelSphinx { |
|
||||||
|
|
||||||
private $_sphinx; |
|
||||||
|
|
||||||
public function __construct(string $host, int $port) |
|
||||||
{ |
|
||||||
$this->_sphinx = new PDO('mysql:host=' . $host . ';port=' . $port . ';charset=utf8', false, false, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); |
|
||||||
$this->_sphinx->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
|
||||||
$this->_sphinx->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); |
|
||||||
} |
|
||||||
|
|
||||||
public function searchMagnetsTotal(string $keyword, string $mode = 'default', array $stopWords = []) : int |
|
||||||
{ |
|
||||||
$query = $this->_sphinx->prepare('SELECT COUNT(*) AS `total` FROM `magnet` WHERE MATCH(?)'); |
|
||||||
|
|
||||||
$query->execute( |
|
||||||
[ |
|
||||||
self::_match($keyword, $mode, $stopWords) |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
return $query->fetch()->total; |
|
||||||
} |
|
||||||
|
|
||||||
public function searchMagnets(string $keyword, int $start, int $limit, int $maxMatches, string $mode = 'default', array $stopWords = []) |
|
||||||
{ |
|
||||||
$query = $this->_sphinx->prepare("SELECT * |
|
||||||
|
|
||||||
FROM `magnet` |
|
||||||
|
|
||||||
WHERE MATCH(?) |
|
||||||
|
|
||||||
ORDER BY `magnetId` DESC, WEIGHT() DESC |
|
||||||
|
|
||||||
LIMIT " . (int) ($start >= $maxMatches ? ($maxMatches > 0 ? $maxMatches - 1 : 0) : $start) . "," . (int) $limit . " |
|
||||||
|
|
||||||
OPTION `max_matches`=" . (int) ($maxMatches >= 1 ? $maxMatches : 1)); |
|
||||||
|
|
||||||
$query->execute( |
|
||||||
[ |
|
||||||
self::_match($keyword, $mode, $stopWords) |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
return $query->fetchAll(); |
|
||||||
} |
|
||||||
|
|
||||||
private static function _match(string $keyword, string $mode = 'default', array $stopWords = []) : string |
|
||||||
{ |
|
||||||
$keyword = trim($keyword); |
|
||||||
|
|
||||||
if (empty($keyword)) |
|
||||||
{ |
|
||||||
return $keyword; |
|
||||||
} |
|
||||||
|
|
||||||
$keyword = str_replace(['"'], ' ', $keyword); |
|
||||||
$keyword = preg_replace('/[\W]/ui', ' ', $keyword); |
|
||||||
$keyword = preg_replace('/[\s]+/ui', ' ', $keyword); |
|
||||||
$keyword = trim($keyword); |
|
||||||
|
|
||||||
switch ($mode) |
|
||||||
{ |
|
||||||
case 'similar': |
|
||||||
|
|
||||||
$result = []; |
|
||||||
|
|
||||||
$keyword = preg_replace('/[\d]/ui', ' ', $keyword); |
|
||||||
$keyword = preg_replace('/[\s]+/ui', ' ', $keyword); |
|
||||||
$keyword = trim($keyword); |
|
||||||
|
|
||||||
foreach ((array) explode(' ', $keyword) as $value) |
|
||||||
{ |
|
||||||
if (mb_strlen($value) > 5) |
|
||||||
{ |
|
||||||
if (!in_array(mb_strtolower($value), array_map('strtolower', $stopWords))) |
|
||||||
{ |
|
||||||
$result[] = sprintf('@title "%s" | @dn "%s"', $value, $value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
if (empty($result)) |
|
||||||
{ |
|
||||||
return '*'; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
return implode(' | ', $result); |
|
||||||
} |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
$result = []; |
|
||||||
|
|
||||||
foreach ((array) explode(' ', $keyword) as $value) |
|
||||||
{ |
|
||||||
if (!in_array(mb_strtolower($value), $stopWords)) |
|
||||||
{ |
|
||||||
$result[] = sprintf('@"*%s*"', $value); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return implode(' | ', $result); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,46 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class AppModelWebsite |
|
||||||
{ |
|
||||||
private $_config; |
|
||||||
|
|
||||||
public function __construct(object $config) |
|
||||||
{ |
|
||||||
$this->_config = $config; |
|
||||||
} |
|
||||||
|
|
||||||
public function getConfig() : object |
|
||||||
{ |
|
||||||
return $this->_config->name; |
|
||||||
} |
|
||||||
|
|
||||||
public function getName() : string |
|
||||||
{ |
|
||||||
return $this->_config->name; |
|
||||||
} |
|
||||||
|
|
||||||
public function getUrl() : string |
|
||||||
{ |
|
||||||
return $this->_config->url; |
|
||||||
} |
|
||||||
|
|
||||||
public function getDefaultLocale() : string |
|
||||||
{ |
|
||||||
return $this->_config->default->locale; |
|
||||||
} |
|
||||||
|
|
||||||
public function getDefaultUserStatus() : bool |
|
||||||
{ |
|
||||||
return $this->_config->default->user->status; |
|
||||||
} |
|
||||||
|
|
||||||
public function getDefaultUserApproved() : bool |
|
||||||
{ |
|
||||||
return $this->_config->default->user->approved; |
|
||||||
} |
|
||||||
|
|
||||||
public function getApiExportEnabled() : bool |
|
||||||
{ |
|
||||||
return $this->_config->api->export->enabled; |
|
||||||
} |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<?php $appControllerModuleHead->render() ?> |
|
||||||
<body> |
|
||||||
<?php $appControllerModuleHeader->render() ?> |
|
||||||
<main> |
|
||||||
<div class="container"> |
|
||||||
<div class="row"> |
|
||||||
<div class="column width-100"> |
|
||||||
<?php $appControllerModuleProfile->render() ?> |
|
||||||
<?php if ($pages) { ?> |
|
||||||
<?php foreach ($pages as $page) { ?> |
|
||||||
<?php $appControllerModulePage->render($page->pageId) ?> |
|
||||||
<?php } ?> |
|
||||||
<?php $appControllerModulePagination->render() ?> |
|
||||||
<?php } else { ?> |
|
||||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night text-center"> |
|
||||||
<h1 class="margin-b-8"> |
|
||||||
<?php echo _('Nothing found') ?> |
|
||||||
</h1> |
|
||||||
<div class="text-color-night"> |
|
||||||
<?php echo _('* share your magnet links to change it') ?> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
<?php } ?> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</main> |
|
||||||
<?php $appControllerModuleFooter->render() ?> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,27 +0,0 @@ |
|||||||
<footer> |
|
||||||
<div class="container"> |
|
||||||
<div class="row"> |
|
||||||
<div class="column width-100 text-center margin-y-8"> |
|
||||||
<?php foreach ($trackers as $i => $tracker) { ?> |
|
||||||
<a href="<?php echo $tracker->announce ?>"><?php echo sprintf('Tracker %s', $i + 1) ?></a> |
|
||||||
/ |
|
||||||
<a href="<?php echo $tracker->stats ?>"><?php echo _('Stats') ?></a> |
|
||||||
| |
|
||||||
<?php } ?> |
|
||||||
<a href="faq"><?php echo _('F.A.Q') ?></a> |
|
||||||
| |
|
||||||
<a href="node"><?php echo _('Node') ?></a> |
|
||||||
| |
|
||||||
<a rel="nofollow" href="rss"><?php echo _('RSS') ?></a> |
|
||||||
<?php if ($api) { ?> |
|
||||||
| |
|
||||||
<a rel="nofollow" href="api/manifest.json"><?php echo _('API') ?></a> |
|
||||||
<?php } ?> |
|
||||||
| |
|
||||||
<a href="https://github.com/YGGverse/YGGtracker"><?php echo _('GitHub') ?></a> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</footer> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,7 +0,0 @@ |
|||||||
<head> |
|
||||||
<base href="<?php echo $base ?>" /> |
|
||||||
<title><?php echo $title ?></title> |
|
||||||
<?php foreach ($links as $link) { ?> |
|
||||||
<link rel="<?php echo $link->rel ?>" type="<?php echo $link->type ?>" href="<?php echo $link->href ?>" /> |
|
||||||
<?php } ?> |
|
||||||
</head> |
|
@ -1,10 +0,0 @@ |
|||||||
<header> |
|
||||||
<div class="container"> |
|
||||||
<div class="row margin-t-8 text-center"> |
|
||||||
<a class="logo" href=""> |
|
||||||
<?php echo $name ?> |
|
||||||
</a> |
|
||||||
<?php $appControllerModuleSearch->render() ?> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</header> |
|
@ -1,126 +0,0 @@ |
|||||||
<a name="magnet-<?php echo $pageId ?>"></a> |
|
||||||
<div class="margin-y-8 border-radius-3 background-color-night <?php echo !$approved ? 'opacity-06 opacity-hover-1' : false ?>"> |
|
||||||
<div class="padding-16 <?php echo $sensitive ? 'blur-2 blur-hover-0' : false ?>"> |
|
||||||
<a href="<?php echo sprintf('%s/magnet.php?magnetId=%s', WEBSITE_URL, $pageId) ?>"> |
|
||||||
<h2 class="margin-b-8"><?php echo $title ?></h2> |
|
||||||
<?php if ($leechers && !$seeders) { ?> |
|
||||||
<span class="label label-green margin-x-4 font-size-10 position-relative top--2 cursor-default" |
|
||||||
title="<?php echo _('Active leechers waiting for seeds') ?>"> |
|
||||||
<?php echo _('wanted') ?> |
|
||||||
</span> |
|
||||||
<?php } ?> |
|
||||||
</a> |
|
||||||
<div class="float-right opacity-0 parent-hover-opacity-09"> |
|
||||||
<?php if (!$approved) { ?> |
|
||||||
<span class="margin-l-8" title="<?php echo _('Waiting for approve') ?>"> |
|
||||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-hourglass-split" viewBox="0 0 16 16"> |
|
||||||
<path d="M2.5 15a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1 0-1h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11zm2-13v1c0 .537.12 1.045.337 1.5h6.326c.216-.455.337-.963.337-1.5V2h-7zm3 6.35c0 .701-.478 1.236-1.011 1.492A3.5 3.5 0 0 0 4.5 13s.866-1.299 3-1.48V8.35zm1 0v3.17c2.134.181 3 1.48 3 1.48a3.5 3.5 0 0 0-1.989-3.158C8.978 9.586 8.5 9.052 8.5 8.351z"/> |
|
||||||
</svg> |
|
||||||
</span> |
|
||||||
<?php } ?> |
|
||||||
<a class="text-color-green margin-l-12" href="<?php echo WEBSITE_URL ?>/edit.php?magnetId=<?php echo $magnet->magnetId ?>" title="<?php echo _('Edit') ?>"> |
|
||||||
<svg class="text-color-green" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16"> |
|
||||||
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/> |
|
||||||
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5v11z"/> |
|
||||||
</svg> |
|
||||||
</a> |
|
||||||
</div> |
|
||||||
<?php if ($magnet->preview) { ?> |
|
||||||
<div class="margin-y-8"><?php echo $magnet->preview ?></div> |
|
||||||
<?php } ?> |
|
||||||
<?php if ($magnet->keywords) { ?> |
|
||||||
<div class="margin-y-8"> |
|
||||||
<?php foreach ($magnet->keywords as $keyword) { ?> |
|
||||||
<small> |
|
||||||
<a href="<?php echo WEBSITE_URL ?>/search.php?query=<?php echo urlencode($keyword) ?>">#<?php echo htmlentities($keyword) ?></a> |
|
||||||
</small> |
|
||||||
<?php } ?> |
|
||||||
</div> |
|
||||||
<?php } ?> |
|
||||||
<div class="width-100 padding-y-4"></div> |
|
||||||
<span class="margin-t-8 margin-r-8 cursor-default"> |
|
||||||
<sup> |
|
||||||
<?php echo $magnet->timeUpdated ? _('Updated') : _('Added') ?> |
|
||||||
<?php echo $magnet->timeUpdated ? $magnet->timeUpdated : $magnet->timeAdded ?> |
|
||||||
</sup> |
|
||||||
</span> |
|
||||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Seeds') ?>"> |
|
||||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-up" viewBox="0 0 16 16"> |
|
||||||
<path fill-rule="evenodd" d="M8 15a.5.5 0 0 0 .5-.5V2.707l3.146 3.147a.5.5 0 0 0 .708-.708l-4-4a.5.5 0 0 0-.708 0l-4 4a.5.5 0 1 0 .708.708L7.5 2.707V14.5a.5.5 0 0 0 .5.5z"/> |
|
||||||
</svg> |
|
||||||
<sup><?php echo $magnet->seeders ?></sup> |
|
||||||
</span> |
|
||||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Peers') ?>"> |
|
||||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down" viewBox="0 0 16 16"> |
|
||||||
<path fill-rule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z"/> |
|
||||||
</svg> |
|
||||||
<sup><?php echo $magnet->completed ?></sup> |
|
||||||
</span> |
|
||||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Leechers') ?>"> |
|
||||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-cup-hot" viewBox="0 0 16 16"> |
|
||||||
<path fill-rule="evenodd" d="M.5 6a.5.5 0 0 0-.488.608l1.652 7.434A2.5 2.5 0 0 0 4.104 16h5.792a2.5 2.5 0 0 0 2.44-1.958l.131-.59a3 3 0 0 0 1.3-5.854l.221-.99A.5.5 0 0 0 13.5 6H.5ZM13 12.5a2.01 2.01 0 0 1-.316-.025l.867-3.898A2.001 2.001 0 0 1 13 12.5ZM2.64 13.825 1.123 7h11.754l-1.517 6.825A1.5 1.5 0 0 1 9.896 15H4.104a1.5 1.5 0 0 1-1.464-1.175Z"/> |
|
||||||
<path d="m4.4.8-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 3.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 3.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 3 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 4.4.8Zm3 0-.003.004-.014.019a4.167 4.167 0 0 0-.204.31 2.327 2.327 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.31 3.31 0 0 1-.202.388 5.444 5.444 0 0 1-.253.382l-.018.025-.005.008-.002.002A.5.5 0 0 1 6.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 6.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 6 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 7.4.8Zm3 0-.003.004-.014.019a4.077 4.077 0 0 0-.204.31 2.337 2.337 0 0 0-.141.267c-.026.06-.034.092-.037.103v.004a.593.593 0 0 0 .091.248c.075.133.178.272.308.445l.01.012c.118.158.26.347.37.543.112.2.22.455.22.745 0 .188-.065.368-.119.494a3.198 3.198 0 0 1-.202.388 5.385 5.385 0 0 1-.252.382l-.019.025-.005.008-.002.002A.5.5 0 0 1 9.6 4.2l.003-.004.014-.019a4.149 4.149 0 0 0 .204-.31 2.06 2.06 0 0 0 .141-.267c.026-.06.034-.092.037-.103a.593.593 0 0 0-.09-.252A4.334 4.334 0 0 0 9.6 2.8l-.01-.012a5.099 5.099 0 0 1-.37-.543A1.53 1.53 0 0 1 9 1.5c0-.188.065-.368.119-.494.059-.138.134-.274.202-.388a5.446 5.446 0 0 1 .253-.382l.025-.035A.5.5 0 0 1 10.4.8Z"/> |
|
||||||
</svg> |
|
||||||
<sup><?php echo $magnet->leechers ?></sup> |
|
||||||
</span> |
|
||||||
<?php if ($magnet->directs) { ?> |
|
||||||
<span class="margin-t-8 margin-r-8 cursor-default opacity-0 parent-hover-opacity-09" title="<?php echo _('Direct') ?>"> |
|
||||||
<svg class="width-13px" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-database" viewBox="0 0 16 16"> |
|
||||||
<path d="M4.318 2.687C5.234 2.271 6.536 2 8 2s2.766.27 3.682.687C12.644 3.125 13 3.627 13 4c0 .374-.356.875-1.318 1.313C10.766 5.729 9.464 6 8 6s-2.766-.27-3.682-.687C3.356 4.875 3 4.373 3 4c0-.374.356-.875 1.318-1.313ZM13 5.698V7c0 .374-.356.875-1.318 1.313C10.766 8.729 9.464 9 8 9s-2.766-.27-3.682-.687C3.356 7.875 3 7.373 3 7V5.698c.271.202.58.378.904.525C4.978 6.711 6.427 7 8 7s3.022-.289 4.096-.777A4.92 4.92 0 0 0 13 5.698ZM14 4c0-1.007-.875-1.755-1.904-2.223C11.022 1.289 9.573 1 8 1s-3.022.289-4.096.777C2.875 2.245 2 2.993 2 4v9c0 1.007.875 1.755 1.904 2.223C4.978 15.71 6.427 16 8 16s3.022-.289 4.096-.777C13.125 14.755 14 14.007 14 13V4Zm-1 4.698V10c0 .374-.356.875-1.318 1.313C10.766 11.729 9.464 12 8 12s-2.766-.27-3.682-.687C3.356 10.875 3 10.373 3 10V8.698c.271.202.58.378.904.525C4.978 9.71 6.427 10 8 10s3.022-.289 4.096-.777A4.92 4.92 0 0 0 13 8.698Zm0 3V13c0 .374-.356.875-1.318 1.313C10.766 14.729 9.464 15 8 15s-2.766-.27-3.682-.687C3.356 13.875 3 13.373 3 13v-1.302c.271.202.58.378.904.525C4.978 12.71 6.427 13 8 13s3.022-.289 4.096-.777c.324-.147.633-.323.904-.525Z"/> |
|
||||||
</svg> |
|
||||||
<sup><?php echo $magnet->directs ?></sup> |
|
||||||
</span> |
|
||||||
<?php } ?> |
|
||||||
<span class="float-right margin-l-12"> |
|
||||||
<a rel="nofollow" href="<?php echo sprintf('%s/action.php?target=magnet&toggle=star&magnetId=%s&callback=%s', |
|
||||||
WEBSITE_URL, |
|
||||||
$magnet->magnetId, |
|
||||||
base64_encode(sprintf('%s/search.php?%s#magnet-%s', |
|
||||||
WEBSITE_URL, |
|
||||||
($request->query ? sprintf('&query=%s', urlencode($request->query)) : false). |
|
||||||
($request->page ? sprintf('&page=%s', urlencode($request->page)) : false), |
|
||||||
$magnet->magnetId))) ?>" title="<?php echo _('Star') ?>"> |
|
||||||
<?php if ($magnet->star->status) { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/> |
|
||||||
</svg> |
|
||||||
<?php } else { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star" viewBox="0 0 16 16"> |
|
||||||
<path d="M2.866 14.85c-.078.444.36.791.746.593l4.39-2.256 4.389 2.256c.386.198.824-.149.746-.592l-.83-4.73 3.522-3.356c.33-.314.16-.888-.282-.95l-4.898-.696L8.465.792a.513.513 0 0 0-.927 0L5.354 5.12l-4.898.696c-.441.062-.612.636-.283.95l3.523 3.356-.83 4.73zm4.905-2.767-3.686 1.894.694-3.957a.565.565 0 0 0-.163-.505L1.71 6.745l4.052-.576a.525.525 0 0 0 .393-.288L8 2.223l1.847 3.658a.525.525 0 0 0 .393.288l4.052.575-2.906 2.77a.565.565 0 0 0-.163.506l.694 3.957-3.686-1.894a.503.503 0 0 0-.461 0z"/> |
|
||||||
</svg> |
|
||||||
<?php } ?> |
|
||||||
</a> |
|
||||||
<sup><?php echo $magnet->star->total ?></sup> |
|
||||||
</span> |
|
||||||
<?php if ($magnet->comments) { ?> |
|
||||||
<span class="float-right margin-l-12"> |
|
||||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/magnet.php?magnetId=<?php echo $magnet->magnetId ?>#comment" title="<?php echo _('Comment') ?>"> |
|
||||||
<?php if ($magnet->comment->status) { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 15c4.418 0 8-3.134 8-7s-3.582-7-8-7-8 3.134-8 7c0 1.76.743 3.37 1.97 4.6-.097 1.016-.417 2.13-.771 2.966-.079.186.074.394.273.362 2.256-.37 3.597-.938 4.18-1.234A9.06 9.06 0 0 0 8 15z"/> |
|
||||||
</svg> |
|
||||||
<?php } else { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat" viewBox="0 0 16 16"> |
|
||||||
<path d="M2.678 11.894a1 1 0 0 1 .287.801 10.97 10.97 0 0 1-.398 2c1.395-.323 2.247-.697 2.634-.893a1 1 0 0 1 .71-.074A8.06 8.06 0 0 0 8 14c3.996 0 7-2.807 7-6 0-3.192-3.004-6-7-6S1 4.808 1 8c0 1.468.617 2.83 1.678 3.894zm-.493 3.905a21.682 21.682 0 0 1-.713.129c-.2.032-.352-.176-.273-.362a9.68 9.68 0 0 0 .244-.637l.003-.01c.248-.72.45-1.548.524-2.319C.743 11.37 0 9.76 0 8c0-3.866 3.582-7 8-7s8 3.134 8 7-3.582 7-8 7a9.06 9.06 0 0 1-2.347-.306c-.52.263-1.639.742-3.468 1.105z"/> |
|
||||||
</svg> |
|
||||||
<?php } ?> |
|
||||||
</a> |
|
||||||
<sup><?php echo $magnet->comment->total ?></sup> |
|
||||||
</span> |
|
||||||
<?php } ?> |
|
||||||
<span class="float-right margin-l-12"> |
|
||||||
<a rel="nofollow" href="<?php echo WEBSITE_URL ?>/download.php?magnetId=<?php echo $magnet->magnetId ?>" title="<?php echo _('Download') ?>"> |
|
||||||
<?php if ($magnet->download->status) { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> |
|
||||||
</svg> |
|
||||||
<?php } else { ?> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-circle" viewBox="0 0 16 16"> |
|
||||||
<path fill-rule="evenodd" d="M1 8a7 7 0 1 0 14 0A7 7 0 0 0 1 8zm15 0A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8.5 4.5a.5.5 0 0 0-1 0v5.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V4.5z"/> |
|
||||||
</svg> |
|
||||||
<?php } ?> |
|
||||||
</a> |
|
||||||
<sup><?php echo $magnet->download->total ?></sup> |
|
||||||
</span> |
|
||||||
</div> |
|
||||||
</div> |
|
@ -1,15 +0,0 @@ |
|||||||
<div class="row"> |
|
||||||
<div class="column width-100 text-right"> |
|
||||||
<?php echo sprintf(_('page %s / %s'), $pagination->page, $pagination->total) ?> |
|
||||||
<?php if ($pagination->back) { ?> |
|
||||||
<a class="button margin-l-8" rel="nofollow" href="<?php echo $pagination->back ?>"> |
|
||||||
<?php echo _('back') ?> |
|
||||||
</a> |
|
||||||
<?php } ?> |
|
||||||
<?php if ($pagination->next) { ?> |
|
||||||
<a class="button margin-l-4" rel="nofollow" href="<?php echo $pagination->next ?>"> |
|
||||||
<?php echo _('next') ?> |
|
||||||
</a> |
|
||||||
<?php } ?> |
|
||||||
</div> |
|
||||||
</div> |
|
@ -1,20 +0,0 @@ |
|||||||
<form class="margin-t-8" name="search" method="get" action="search"> |
|
||||||
<input class="min-width-200-px" type="text" name="query" value="<?php echo $query ?>" placeholder="<?php echo _('Keyword, file, extension, hash...') ?>" /> |
|
||||||
<select class="min-width-120-px" type="text" name="locale"> |
|
||||||
<option value=""> |
|
||||||
<?php echo _('All languages') ?> |
|
||||||
</option> |
|
||||||
<?php foreach ($locales as $locale) { ?> |
|
||||||
<?php if ($locale->active) { ?> |
|
||||||
<option value="<?php echo $locale->key ?>" selected="selected"> |
|
||||||
<?php echo $locale->value ?> |
|
||||||
</option> |
|
||||||
<?php } else { ?> |
|
||||||
<option value="<?php echo $locale->key ?>"> |
|
||||||
<?php echo $locale->value ?> |
|
||||||
</option> |
|
||||||
<?php } ?> |
|
||||||
<?php } ?> |
|
||||||
</select> |
|
||||||
<input type="submit" value="<?php echo _('Search') ?>" /> |
|
||||||
</form> |
|
@ -1,140 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<?php $appControllerModuleHead->render() ?> |
|
||||||
<body> |
|
||||||
<?php $appControllerModuleHeader->render() ?> |
|
||||||
<main> |
|
||||||
<div class="container"> |
|
||||||
<div class="row"> |
|
||||||
<div class="column width-100"> |
|
||||||
<?php $appControllerModuleProfile->render() ?> |
|
||||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night"> |
|
||||||
<div class="margin-b-24 padding-b-16 border-bottom-default"> |
|
||||||
<h1><?php echo _('Submit') ?></h1> |
|
||||||
</div> |
|
||||||
<form class="margin-t-8" name="submit" method="post" enctype="multipart/form-data" action="submit"> |
|
||||||
<input type="hidden" name="pageId" value="<?php echo $form->pageId->attribute->value ?>" /> |
|
||||||
<div class="margin-b-16"> |
|
||||||
<label for="locale"> |
|
||||||
<?php echo _('Content language') ?> |
|
||||||
</label> |
|
||||||
<sub class="opacity-0 parent-hover-opacity-09" |
|
||||||
title="<?php echo $form->locale->placeholder ?>"> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
|
||||||
</svg> |
|
||||||
</sub> |
|
||||||
<select class="width-100 margin-t-8" type="text" name="locale" id="locale"> |
|
||||||
<?php foreach ($form->locale->options as $locale) { ?> |
|
||||||
<?php if ($locale->active) { ?> |
|
||||||
<option value="<?php echo $locale->code ?>" selected="selected"> |
|
||||||
<?php echo $locale->value ?> |
|
||||||
</option> |
|
||||||
<?php } else { ?> |
|
||||||
<option value="<?php echo $locale->code ?>"> |
|
||||||
<?php echo $locale->value ?> |
|
||||||
</option> |
|
||||||
<?php } ?> |
|
||||||
<?php } ?> |
|
||||||
</select> |
|
||||||
</div> |
|
||||||
<div class="margin-b-16"> |
|
||||||
<label for="title"> |
|
||||||
<?php echo _('Title') ?> |
|
||||||
</label> |
|
||||||
<sub class="opacity-0 parent-hover-opacity-09" |
|
||||||
title="<?php echo $form->title->attribute->placeholder ?>"> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
|
||||||
</svg> |
|
||||||
</sub> |
|
||||||
<?php foreach ($form->title->error as $errors) { ?> |
|
||||||
<?php foreach ($errors as $error) { ?> |
|
||||||
<div class="text-color-red margin-y-8"> |
|
||||||
<?php echo $error ?> |
|
||||||
</div> |
|
||||||
<?php } ?> |
|
||||||
<?php } ?> |
|
||||||
<input class="width-100 margin-t-8" |
|
||||||
type="text" |
|
||||||
name="title" |
|
||||||
id="title" |
|
||||||
<?php echo $form->title->attribute->required ? 'required="required"' : false ?> |
|
||||||
value="<?php echo $form->title->attribute->value ?>" |
|
||||||
placeholder="<?php echo $form->title->attribute->placeholder ?>" |
|
||||||
minlength="<?php echo $form->title->attribute->minlength ?>" |
|
||||||
maxlength="<?php echo $form->title->attribute->maxlength ?>" /> |
|
||||||
</div> |
|
||||||
<div class="margin-y-8 padding-t-4"> |
|
||||||
<label for="description"> |
|
||||||
<?php echo _('Description') ?> |
|
||||||
</label> |
|
||||||
<sub class="opacity-0 parent-hover-opacity-09" |
|
||||||
title="<?php echo $form->description->attribute->placeholder ?>"> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
|
||||||
</svg> |
|
||||||
</sub> |
|
||||||
<?php foreach ($form->description->error as $errors) { ?> |
|
||||||
<?php foreach ($errors as $error) { ?> |
|
||||||
<div class="text-color-red margin-y-8"> |
|
||||||
<?php echo $error ?> |
|
||||||
</div> |
|
||||||
<?php } ?> |
|
||||||
<?php } ?> |
|
||||||
<textarea class="width-100 margin-t-8" |
|
||||||
name="description" |
|
||||||
id="description" |
|
||||||
<?php echo $form->description->attribute->required ? 'required="required"' : false ?> |
|
||||||
placeholder="<?php echo $form->description->attribute->placeholder ?>" |
|
||||||
minlength="<?php echo $form->description->attribute->minlength ?>" |
|
||||||
maxlength="<?php echo $form->description->attribute->maxlength ?>"><?php echo $form->description->attribute->value ?></textarea> |
|
||||||
</div> |
|
||||||
<div class="margin-y-8 padding-t-4"> |
|
||||||
<label for="keywords"> |
|
||||||
<?php echo _('Keywords') ?> |
|
||||||
</label> |
|
||||||
<sub class="opacity-0 parent-hover-opacity-09" |
|
||||||
title="<?php echo $form->keywords->attribute->placeholder ?>"> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
|
||||||
</svg> |
|
||||||
</sub> |
|
||||||
<?php foreach ($form->keywords->error as $errors) { ?> |
|
||||||
<?php foreach ($errors as $error) { ?> |
|
||||||
<div class="text-color-red margin-y-8"> |
|
||||||
<?php echo $error ?> |
|
||||||
</div> |
|
||||||
<?php } ?> |
|
||||||
<?php } ?> |
|
||||||
<textarea class="width-100 margin-t-8" |
|
||||||
name="keywords" |
|
||||||
<?php echo $form->keywords->attribute->required ? 'required="required"' : false ?> |
|
||||||
placeholder="<?php echo $form->keywords->attribute->placeholder ?>" |
|
||||||
minlength="<?php echo $form->keywords->attribute->minlength ?>" |
|
||||||
maxlength="<?php echo $form->keywords->attribute->maxlength ?>"><?php echo $form->keywords->attribute->value ?></textarea> |
|
||||||
</div> |
|
||||||
<div class="margin-y-16"> |
|
||||||
<input type="checkbox" name="sensitive" id="sensitive" value="true" <?php echo $form->sensitive->attribute->value ? 'checked="checked"' : false ?> /> |
|
||||||
<label for="sensitive"> |
|
||||||
<?php echo _('Sensitive') ?> |
|
||||||
</label> |
|
||||||
<sub class="opacity-0 parent-hover-opacity-09" |
|
||||||
title="<?php echo $form->sensitive->attribute->placeholder ?>"> |
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" class="bi bi-info-circle-fill" viewBox="0 0 16 16"> |
|
||||||
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/> |
|
||||||
</svg> |
|
||||||
</sub> |
|
||||||
</div> |
|
||||||
<div class="text-right"> |
|
||||||
<input class="button-green" type="submit" value="<?php echo _('Submit') ?>" /> |
|
||||||
</div> |
|
||||||
</form> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</main> |
|
||||||
<?php $appControllerModuleFooter->render() ?> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,24 +0,0 @@ |
|||||||
<!DOCTYPE html> |
|
||||||
<html> |
|
||||||
<?php $appControllerModuleHead->render() ?> |
|
||||||
<body> |
|
||||||
<?php $appControllerModuleHeader->render() ?> |
|
||||||
<main> |
|
||||||
<div class="container"> |
|
||||||
<div class="row"> |
|
||||||
<div class="column width-100"> |
|
||||||
<div class="padding-16 margin-y-8 border-radius-3 background-color-night text-center"> |
|
||||||
<h1 class="margin-b-8"> |
|
||||||
<?php echo $h1 ?> |
|
||||||
</h1> |
|
||||||
<div> |
|
||||||
<?php echo $text ?> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</main> |
|
||||||
<?php $appControllerModuleFooter->render() ?> |
|
||||||
</body> |
|
||||||
</html> |
|
@ -1,160 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// PHP |
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
// Debug |
|
||||||
ini_set('display_errors', '1'); |
|
||||||
ini_set('display_startup_errors', '1'); |
|
||||||
error_reporting(E_ALL); |
|
||||||
|
|
||||||
// Application |
|
||||||
define('APP_VERSION', '2.0.0'); |
|
||||||
define('API_VERSION', APP_VERSION); |
|
||||||
define('CSS_VERSION', APP_VERSION); |
|
||||||
|
|
||||||
// Environment |
|
||||||
require_once __DIR__ . '/../library/environment.php'; |
|
||||||
|
|
||||||
// Autoload |
|
||||||
require_once __DIR__ . '/../../vendor/autoload.php'; |
|
||||||
|
|
||||||
// Route |
|
||||||
parse_str($_SERVER['QUERY_STRING'], $request); |
|
||||||
|
|
||||||
if (isset($request['_route_'])) |
|
||||||
{ |
|
||||||
switch ($request['_route_']) |
|
||||||
{ |
|
||||||
case 'stars': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/stars.php'; |
|
||||||
|
|
||||||
$appControllerStars = new AppControllerStars(); |
|
||||||
|
|
||||||
$appControllerStars->render(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'views': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/views.php'; |
|
||||||
|
|
||||||
$appControllerViews = new AppControllerViews(); |
|
||||||
|
|
||||||
$appControllerViews->render(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'downloads': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/downloads.php'; |
|
||||||
|
|
||||||
$appControllerDownloads = new AppControllerDownloads(); |
|
||||||
|
|
||||||
$appControllerDownloads->render(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'comments': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/comments.php'; |
|
||||||
|
|
||||||
$appControllerComments = new AppControllerComments(); |
|
||||||
|
|
||||||
$appControllerComments->render(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'editions': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/editions.php'; |
|
||||||
|
|
||||||
$appControllerEditions = new AppControllerEditions(); |
|
||||||
|
|
||||||
$appControllerEditions->render(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
case 'submit': |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/model/database.php'; |
|
||||||
require_once __DIR__ . '/../app/model/validator.php'; |
|
||||||
require_once __DIR__ . '/../app/model/locale.php'; |
|
||||||
require_once __DIR__ . '/../app/model/website.php'; |
|
||||||
require_once __DIR__ . '/../app/model/session.php'; |
|
||||||
require_once __DIR__ . '/../app/model/request.php'; |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/page.php'; |
|
||||||
|
|
||||||
$appControllerPage = new AppControllerPage( |
|
||||||
new AppModelDatabase( |
|
||||||
Environment::config('database') |
|
||||||
), |
|
||||||
new AppModelValidator( |
|
||||||
Environment::config('validator') |
|
||||||
), |
|
||||||
new AppModelLocale( |
|
||||||
Environment::config('locales') |
|
||||||
), |
|
||||||
new AppModelWebsite( |
|
||||||
Environment::config('website') |
|
||||||
), |
|
||||||
new AppModelSession( |
|
||||||
$_SERVER['REMOTE_ADDR'] |
|
||||||
), |
|
||||||
new AppModelRequest( |
|
||||||
$_GET, |
|
||||||
$_POST, |
|
||||||
$_FILES |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$appControllerPage->renderFormSubmit(); |
|
||||||
|
|
||||||
break; |
|
||||||
|
|
||||||
default: |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/response.php'; |
|
||||||
|
|
||||||
$appControllerResponse = new AppControllerResponse( |
|
||||||
sprintf( |
|
||||||
_('404 - Not found - %s'), |
|
||||||
Environment::config('website')->name |
|
||||||
), |
|
||||||
_('404'), |
|
||||||
_('Page not found'), |
|
||||||
404 |
|
||||||
); |
|
||||||
|
|
||||||
$appControllerResponse->render(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
require_once __DIR__ . '/../app/model/database.php'; |
|
||||||
require_once __DIR__ . '/../app/model/validator.php'; |
|
||||||
require_once __DIR__ . '/../app/model/website.php'; |
|
||||||
require_once __DIR__ . '/../app/model/session.php'; |
|
||||||
|
|
||||||
require_once __DIR__ . '/../app/controller/index.php'; |
|
||||||
|
|
||||||
$appControllerIndex = new AppControllerIndex( |
|
||||||
new AppModelDatabase( |
|
||||||
Environment::config('database') |
|
||||||
), |
|
||||||
new AppModelValidator( |
|
||||||
Environment::config('validator') |
|
||||||
), |
|
||||||
new AppModelWebsite( |
|
||||||
Environment::config('website') |
|
||||||
), |
|
||||||
new AppModelSession( |
|
||||||
$_SERVER['REMOTE_ADDR'] |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
$appControllerIndex->render(); |
|
||||||
} |
|
@ -1,7 +0,0 @@ |
|||||||
{ |
|
||||||
"port":3306, |
|
||||||
"host":"127.0.0.1", |
|
||||||
"name":"", |
|
||||||
"user":"", |
|
||||||
"password":"" |
|
||||||
} |
|
@ -1,6 +0,0 @@ |
|||||||
{ |
|
||||||
"port": 11211, |
|
||||||
"host": "127.0.0.1", |
|
||||||
"namespace": "", |
|
||||||
"timeout": 3600 |
|
||||||
} |
|
@ -1,12 +0,0 @@ |
|||||||
[ |
|
||||||
{ |
|
||||||
"description":"YGGtracker instance #1 running latest stable release", |
|
||||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker", |
|
||||||
"manifest":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker/api/manifest.json" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"description":"YGGtracker instance #2 running latest stable release", |
|
||||||
"url":"http://[200:e6fd:bb3c:b354:cd3a:f939:753e:cd72]/yggtracker", |
|
||||||
"manifest":"http://[200:e6fd:bb3c:b354:cd3a:f939:753e:cd72]/yggtracker/api/manifest.json" |
|
||||||
} |
|
||||||
] |
|
@ -1,7 +0,0 @@ |
|||||||
[ |
|
||||||
{ |
|
||||||
"description":"YGGtracker public peer instance without traffic limit", |
|
||||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggstate", |
|
||||||
"address":"tls://94.140.114.241:4708" |
|
||||||
} |
|
||||||
] |
|
@ -1,23 +0,0 @@ |
|||||||
[ |
|
||||||
{ |
|
||||||
"description":"YGGtracker instance, yggdrasil-only connections", |
|
||||||
"url":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]/yggtracker", |
|
||||||
"announce":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/announce", |
|
||||||
"stats":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/stats", |
|
||||||
"scrape":"http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/scrape" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"description":"Yggdrasil-only torrent tracker, operated by jeff", |
|
||||||
"url":false, |
|
||||||
"announce":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/announce", |
|
||||||
"stats":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/stats", |
|
||||||
"scrape":"http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/scrape" |
|
||||||
}, |
|
||||||
{ |
|
||||||
"description":"Yggdrasil torrent tracker, operated by R4SAS", |
|
||||||
"url":false, |
|
||||||
"announce":"http://[316:c51a:62a3:8b9::5]/announce", |
|
||||||
"stats":"http://[316:c51a:62a3:8b9::5]/stats", |
|
||||||
"scrape":"http://[316:c51a:62a3:8b9::5]/scrape" |
|
||||||
} |
|
||||||
] |
|
@ -1,74 +0,0 @@ |
|||||||
{ |
|
||||||
"host": |
|
||||||
{ |
|
||||||
"regex": "/^0{0,1}[2-3][a-f0-9]{0,2}:/" |
|
||||||
}, |
|
||||||
"page": |
|
||||||
{ |
|
||||||
"title": |
|
||||||
{ |
|
||||||
"required": true, |
|
||||||
"length": |
|
||||||
{ |
|
||||||
"min": 10, |
|
||||||
"max": 255 |
|
||||||
}, |
|
||||||
"regex": "/.*/ui" |
|
||||||
}, |
|
||||||
"description": |
|
||||||
{ |
|
||||||
"required": false, |
|
||||||
"length": |
|
||||||
{ |
|
||||||
"min": 0, |
|
||||||
"max": 10000 |
|
||||||
}, |
|
||||||
"regex": "/.*/ui" |
|
||||||
}, |
|
||||||
"keyword": |
|
||||||
{ |
|
||||||
"length": |
|
||||||
{ |
|
||||||
"min": 0, |
|
||||||
"max": 140 |
|
||||||
}, |
|
||||||
"regex": "/[\\w]+/ui" |
|
||||||
}, |
|
||||||
"keywords": |
|
||||||
{ |
|
||||||
"required": false, |
|
||||||
"quantity": |
|
||||||
{ |
|
||||||
"min": 0, |
|
||||||
"max": 20 |
|
||||||
} |
|
||||||
}, |
|
||||||
"image": |
|
||||||
{ |
|
||||||
"required": false, |
|
||||||
"mime": [ |
|
||||||
"image/png", |
|
||||||
"image/gif", |
|
||||||
"image/jpeg", |
|
||||||
"image/webp" |
|
||||||
], |
|
||||||
"quantity": |
|
||||||
{ |
|
||||||
"min": 0, |
|
||||||
"max": 20 |
|
||||||
} |
|
||||||
}, |
|
||||||
"torrent": |
|
||||||
{ |
|
||||||
"required": true, |
|
||||||
"mime": [ |
|
||||||
"application/x-bittorrent" |
|
||||||
], |
|
||||||
"quantity": |
|
||||||
{ |
|
||||||
"min": 0, |
|
||||||
"max": 20 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
{ |
|
||||||
"name":"YGGtracker", |
|
||||||
"scheme":"", |
|
||||||
"host":"", |
|
||||||
"port":"", |
|
||||||
"path":"", |
|
||||||
"default": |
|
||||||
{ |
|
||||||
"locale":"en-US", |
|
||||||
"user": |
|
||||||
{ |
|
||||||
"status": true, |
|
||||||
"approved": false |
|
||||||
} |
|
||||||
}, |
|
||||||
"api": |
|
||||||
{ |
|
||||||
"export": |
|
||||||
{ |
|
||||||
"enabled" : true |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,558 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// Lock multi-thread execution |
|
||||||
$semaphore = sem_get(crc32('yggtracker.crontab.export.feed'), 1); |
|
||||||
|
|
||||||
if (false === sem_acquire($semaphore, true)) |
|
||||||
{ |
|
||||||
exit (_('yggtracker.crontab.export.feed process locked by another thread.')); |
|
||||||
} |
|
||||||
|
|
||||||
// Bootstrap |
|
||||||
require_once __DIR__ . '/../../config/bootstrap.php'; |
|
||||||
|
|
||||||
// Init Debug |
|
||||||
$debug = |
|
||||||
[ |
|
||||||
'dump' => [], |
|
||||||
'time' => [ |
|
||||||
'ISO8601' => date('c'), |
|
||||||
'total' => microtime(true), |
|
||||||
], |
|
||||||
'http' => |
|
||||||
[ |
|
||||||
'total' => 0, |
|
||||||
], |
|
||||||
'memory' => |
|
||||||
[ |
|
||||||
'start' => memory_get_usage(), |
|
||||||
'total' => 0, |
|
||||||
'peaks' => 0 |
|
||||||
], |
|
||||||
]; |
|
||||||
|
|
||||||
// Define public registry |
|
||||||
$public = [ |
|
||||||
'user' => [], |
|
||||||
'magnet' => [], |
|
||||||
]; |
|
||||||
|
|
||||||
// Begin export |
|
||||||
try |
|
||||||
{ |
|
||||||
// Init API folder if not exists |
|
||||||
@mkdir(__DIR__ . '/../public/api'); |
|
||||||
|
|
||||||
// Delete cached feeds |
|
||||||
@unlink(__DIR__ . '/../public/api/manifest.json'); |
|
||||||
|
|
||||||
@unlink(__DIR__ . '/../public/api/users.json'); |
|
||||||
@unlink(__DIR__ . '/../public/api/magnets.json'); |
|
||||||
@unlink(__DIR__ . '/../public/api/magnetComments.json'); |
|
||||||
@unlink(__DIR__ . '/../public/api/magnetDownloads.json'); |
|
||||||
@unlink(__DIR__ . '/../public/api/magnetStars.json'); |
|
||||||
@unlink(__DIR__ . '/../public/api/magnetViews.json'); |
|
||||||
|
|
||||||
if (API_EXPORT_ENABLED) |
|
||||||
{ |
|
||||||
// Manifest |
|
||||||
$manifest = |
|
||||||
[ |
|
||||||
'updated' => time(), |
|
||||||
'version' => (string) API_VERSION, |
|
||||||
|
|
||||||
'settings' => (object) |
|
||||||
[ |
|
||||||
'YGGDRASIL_HOST_REGEX' => (string) YGGDRASIL_HOST_REGEX, |
|
||||||
|
|
||||||
'NODE_RULE_SUBJECT' => (string) NODE_RULE_SUBJECT, |
|
||||||
'NODE_RULE_LANGUAGES' => (string) NODE_RULE_LANGUAGES, |
|
||||||
|
|
||||||
'USER_DEFAULT_APPROVED' => (bool) USER_DEFAULT_APPROVED, |
|
||||||
'USER_AUTO_APPROVE_ON_MAGNET_APPROVE' => (bool) USER_AUTO_APPROVE_ON_MAGNET_APPROVE, |
|
||||||
'USER_AUTO_APPROVE_ON_COMMENT_APPROVE' => (bool) USER_AUTO_APPROVE_ON_COMMENT_APPROVE, |
|
||||||
'USER_DEFAULT_IDENTICON' => (string) USER_DEFAULT_IDENTICON, |
|
||||||
'USER_IDENTICON_FIELD' => (string) USER_IDENTICON_FIELD, |
|
||||||
|
|
||||||
'MAGNET_DEFAULT_APPROVED' => (bool) MAGNET_DEFAULT_APPROVED, |
|
||||||
'MAGNET_DEFAULT_PUBLIC' => (bool) MAGNET_DEFAULT_PUBLIC, |
|
||||||
'MAGNET_DEFAULT_COMMENTS' => (bool) MAGNET_DEFAULT_COMMENTS, |
|
||||||
'MAGNET_DEFAULT_SENSITIVE' => (bool) MAGNET_DEFAULT_SENSITIVE, |
|
||||||
|
|
||||||
'MAGNET_EDITOR_LOCK_TIMEOUT' => (int) MAGNET_EDITOR_LOCK_TIMEOUT, |
|
||||||
|
|
||||||
'MAGNET_TITLE_MIN_LENGTH' => (int) MAGNET_TITLE_MIN_LENGTH, |
|
||||||
'MAGNET_TITLE_MAX_LENGTH' => (int) MAGNET_TITLE_MAX_LENGTH, |
|
||||||
'MAGNET_TITLE_REGEX' => (string) MAGNET_TITLE_REGEX, |
|
||||||
|
|
||||||
'MAGNET_PREVIEW_MIN_LENGTH' => (int) MAGNET_PREVIEW_MIN_LENGTH, |
|
||||||
'MAGNET_PREVIEW_MAX_LENGTH' => (int) MAGNET_PREVIEW_MAX_LENGTH, |
|
||||||
'MAGNET_PREVIEW_REGEX' => (string) MAGNET_PREVIEW_REGEX, |
|
||||||
|
|
||||||
'MAGNET_DESCRIPTION_MIN_LENGTH' => (int) MAGNET_DESCRIPTION_MIN_LENGTH, |
|
||||||
'MAGNET_DESCRIPTION_MAX_LENGTH' => (int) MAGNET_DESCRIPTION_MAX_LENGTH, |
|
||||||
'MAGNET_DESCRIPTION_REGEX' => (string) MAGNET_DESCRIPTION_REGEX, |
|
||||||
|
|
||||||
'MAGNET_DN_MIN_LENGTH' => (int) MAGNET_DN_MIN_LENGTH, |
|
||||||
'MAGNET_DN_MAX_LENGTH' => (int) MAGNET_DN_MAX_LENGTH, |
|
||||||
'MAGNET_DN_REGEX' => (string) MAGNET_DN_REGEX, |
|
||||||
|
|
||||||
'MAGNET_KT_MIN_LENGTH' => (int) MAGNET_KT_MIN_LENGTH, |
|
||||||
'MAGNET_KT_MAX_LENGTH' => (int) MAGNET_KT_MAX_LENGTH, |
|
||||||
'MAGNET_KT_MIN_QUANTITY' => (int) MAGNET_KT_MIN_QUANTITY, |
|
||||||
'MAGNET_KT_MAX_QUANTITY' => (int) MAGNET_KT_MAX_QUANTITY, |
|
||||||
'MAGNET_KT_REGEX' => (string) MAGNET_KT_REGEX, |
|
||||||
|
|
||||||
'MAGNET_TR_MIN_QUANTITY' => (int) MAGNET_TR_MIN_QUANTITY, |
|
||||||
'MAGNET_TR_MAX_QUANTITY' => (int) MAGNET_TR_MAX_QUANTITY, |
|
||||||
|
|
||||||
'MAGNET_AS_MIN_QUANTITY' => (int) MAGNET_AS_MIN_QUANTITY, |
|
||||||
'MAGNET_AS_MAX_QUANTITY' => (int) MAGNET_AS_MAX_QUANTITY, |
|
||||||
|
|
||||||
'MAGNET_WS_MIN_QUANTITY' => (int) MAGNET_WS_MIN_QUANTITY, |
|
||||||
'MAGNET_WS_MAX_QUANTITY' => (int) MAGNET_WS_MAX_QUANTITY, |
|
||||||
|
|
||||||
'MAGNET_COMMENT_DEFAULT_APPROVED' => (bool) MAGNET_COMMENT_DEFAULT_APPROVED, |
|
||||||
'MAGNET_COMMENT_DEFAULT_PUBLIC' => (bool) MAGNET_COMMENT_DEFAULT_PUBLIC, |
|
||||||
'MAGNET_COMMENT_DEFAULT_PUBLIC' => (bool) MAGNET_COMMENT_DEFAULT_PUBLIC, |
|
||||||
'MAGNET_COMMENT_MIN_LENGTH' => (int) MAGNET_COMMENT_MIN_LENGTH, |
|
||||||
'MAGNET_COMMENT_MAX_LENGTH' => (int) MAGNET_COMMENT_MAX_LENGTH, |
|
||||||
|
|
||||||
'MAGNET_STOP_WORDS_SIMILAR' => (object) MAGNET_STOP_WORDS_SIMILAR, |
|
||||||
|
|
||||||
'API_USER_AGENT' => (string) API_USER_AGENT, |
|
||||||
|
|
||||||
'API_EXPORT_ENABLED' => (bool) API_EXPORT_ENABLED, |
|
||||||
'API_EXPORT_PUSH_ENABLED' => (bool) API_EXPORT_PUSH_ENABLED, |
|
||||||
'API_EXPORT_USERS_ENABLED' => (bool) API_EXPORT_USERS_ENABLED, |
|
||||||
'API_EXPORT_MAGNETS_ENABLED' => (bool) API_EXPORT_MAGNETS_ENABLED, |
|
||||||
'API_EXPORT_MAGNET_DOWNLOADS_ENABLED' => (bool) API_EXPORT_MAGNET_DOWNLOADS_ENABLED, |
|
||||||
'API_EXPORT_MAGNET_COMMENTS_ENABLED' => (bool) API_EXPORT_MAGNET_COMMENTS_ENABLED, |
|
||||||
'API_EXPORT_MAGNET_STARS_ENABLED' => (bool) API_EXPORT_MAGNET_STARS_ENABLED, |
|
||||||
'API_EXPORT_MAGNET_STARS_ENABLED' => (bool) API_EXPORT_MAGNET_STARS_ENABLED, |
|
||||||
'API_EXPORT_MAGNET_VIEWS_ENABLED' => (bool) API_EXPORT_MAGNET_VIEWS_ENABLED, |
|
||||||
|
|
||||||
'API_IMPORT_ENABLED' => (bool) API_IMPORT_ENABLED, |
|
||||||
'API_IMPORT_PUSH_ENABLED' => (bool) API_IMPORT_PUSH_ENABLED, |
|
||||||
'API_IMPORT_USERS_ENABLED' => (bool) API_IMPORT_USERS_ENABLED, |
|
||||||
|
|
||||||
'API_IMPORT_USERS_APPROVED_ONLY' => (bool) API_IMPORT_USERS_APPROVED_ONLY, |
|
||||||
'API_IMPORT_MAGNETS_ENABLED' => (bool) API_IMPORT_MAGNETS_ENABLED, |
|
||||||
'API_IMPORT_MAGNETS_APPROVED_ONLY' => (bool) API_IMPORT_MAGNETS_APPROVED_ONLY, |
|
||||||
'API_IMPORT_MAGNET_DOWNLOADS_ENABLED' => (bool) API_IMPORT_MAGNET_DOWNLOADS_ENABLED, |
|
||||||
'API_IMPORT_MAGNET_COMMENTS_ENABLED' => (bool) API_IMPORT_MAGNET_COMMENTS_ENABLED, |
|
||||||
'API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY' => (bool) API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY, |
|
||||||
'API_IMPORT_MAGNET_STARS_ENABLED' => (bool) API_IMPORT_MAGNET_STARS_ENABLED, |
|
||||||
'API_IMPORT_MAGNET_VIEWS_ENABLED' => (bool) API_IMPORT_MAGNET_VIEWS_ENABLED, |
|
||||||
], |
|
||||||
'totals' => (object) |
|
||||||
[ |
|
||||||
'magnets' => (object) |
|
||||||
[ |
|
||||||
'total' => $db->getMagnetsTotal(), |
|
||||||
'distributed' => $db->getMagnetsTotalByUsersPublic(true), |
|
||||||
'local' => $db->getMagnetsTotalByUsersPublic(false), |
|
||||||
], |
|
||||||
'downloads' => (object) |
|
||||||
[ |
|
||||||
'total' => $db->getMagnetDownloadsTotal(), |
|
||||||
'distributed' => $db->findMagnetDownloadsTotalByUsersPublic(true), |
|
||||||
'local' => $db->findMagnetDownloadsTotalByUsersPublic(false), |
|
||||||
], |
|
||||||
'comments' => (object) |
|
||||||
[ |
|
||||||
'total' => $db->getMagnetCommentsTotal(), |
|
||||||
'distributed' => $db->findMagnetCommentsTotalByUsersPublic(true), |
|
||||||
'local' => $db->findMagnetCommentsTotalByUsersPublic(false), |
|
||||||
], |
|
||||||
'stars' => (object) |
|
||||||
[ |
|
||||||
'total' => $db->getMagnetStarsTotal(), |
|
||||||
'distributed' => $db->findMagnetStarsTotalByUsersPublic(true), |
|
||||||
'local' => $db->findMagnetStarsTotalByUsersPublic(false), |
|
||||||
], |
|
||||||
'views' => (object) |
|
||||||
[ |
|
||||||
'total' => $db->getMagnetViewsTotal(), |
|
||||||
'distributed' => $db->findMagnetViewsTotalByUsersPublic(true), |
|
||||||
'local' => $db->findMagnetViewsTotalByUsersPublic(false), |
|
||||||
], |
|
||||||
], |
|
||||||
'import' => (object) |
|
||||||
[ |
|
||||||
'push' => API_IMPORT_PUSH_ENABLED ? sprintf('%s/api/push.php', WEBSITE_URL) : false, |
|
||||||
], |
|
||||||
'export' => (object) |
|
||||||
[ |
|
||||||
'users' => API_EXPORT_USERS_ENABLED ? sprintf('%s/api/users.json', WEBSITE_URL) : false, |
|
||||||
'magnets' => API_EXPORT_MAGNETS_ENABLED ? sprintf('%s/api/magnets.json', WEBSITE_URL) : false, |
|
||||||
'magnetDownloads' => API_EXPORT_MAGNET_DOWNLOADS_ENABLED ? sprintf('%s/api/magnetDownloads.json', WEBSITE_URL) : false, |
|
||||||
'magnetComments' => API_EXPORT_MAGNET_COMMENTS_ENABLED ? sprintf('%s/api/magnetComments.json', WEBSITE_URL) : false, |
|
||||||
'magnetStars' => API_EXPORT_MAGNET_STARS_ENABLED ? sprintf('%s/api/magnetStars.json', WEBSITE_URL) : false, |
|
||||||
'magnetViews' => API_EXPORT_MAGNET_VIEWS_ENABLED ? sprintf('%s/api/magnetViews.json', WEBSITE_URL) : false, |
|
||||||
], |
|
||||||
'trackers' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/trackers.json')), |
|
||||||
'nodes' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/nodes.json')), |
|
||||||
'peers' => (object) json_decode(file_get_contents(__DIR__ . '/../../config/peers.json')), |
|
||||||
]; |
|
||||||
|
|
||||||
/// Dump feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/manifest.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($manifest)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/manifest.json', 0774); |
|
||||||
} |
|
||||||
|
|
||||||
// Users |
|
||||||
if (API_EXPORT_USERS_ENABLED) |
|
||||||
{ |
|
||||||
$users = []; |
|
||||||
|
|
||||||
foreach ($db->getUsers() as $user) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if ($user->public) |
|
||||||
{ |
|
||||||
$users[] = (object) |
|
||||||
[ |
|
||||||
'userId' => (int) $user->userId, |
|
||||||
'address' => (string) $user->address, |
|
||||||
'timeAdded' => (int) $user->timeAdded, |
|
||||||
'timeUpdated' => (int) $user->timeUpdated, |
|
||||||
'approved' => (bool) $user->approved, |
|
||||||
'magnets' => (int) $db->findMagnetsTotalByUserId($user->userId), |
|
||||||
'downloads' => (int) $db->findMagnetDownloadsTotalByUserId($user->userId), |
|
||||||
'comments' => (int) $db->findMagnetCommentsTotalByUserId($user->userId), |
|
||||||
'stars' => (int) $db->findMagnetStarsTotalByUserId($user->userId), |
|
||||||
'views' => (int) $db->findMagnetViewsTotalByUserId($user->userId), |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// Cache public status |
|
||||||
$public['user'][$user->userId] = (bool) $user->public; |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump users feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/users.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($users)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/users.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnets |
|
||||||
if (API_EXPORT_MAGNETS_ENABLED) |
|
||||||
{ |
|
||||||
$magnets = []; |
|
||||||
|
|
||||||
foreach ($db->getMagnets($user->userId) as $magnet) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if ($magnet->public && |
|
||||||
$public['user'][$magnet->userId]) // After upgrade, some users have not updated their public status. |
|
||||||
// Remote node have warning on import, because user info still hidden to init new profile there. |
|
||||||
// Stop magnets export without public profile available, even magnet is public. |
|
||||||
{ |
|
||||||
// Info Hash |
|
||||||
$xt = []; |
|
||||||
foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
if ($infoHash = $db->getInfoHash($result->infoHashId)) |
|
||||||
{ |
|
||||||
$xt[] = (object) [ |
|
||||||
'version' => (float) $infoHash->version, |
|
||||||
'value' => (string) $infoHash->value, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Keyword Topic |
|
||||||
$kt = []; |
|
||||||
|
|
||||||
foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; |
|
||||||
} |
|
||||||
|
|
||||||
// Address Tracker |
|
||||||
$tr = []; |
|
||||||
foreach ($db->findAddressTrackerByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$addressTracker = $db->getAddressTracker($result->addressTrackerId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($addressTracker->schemeId); |
|
||||||
$host = $db->getHost($addressTracker->hostId); |
|
||||||
$port = $db->getPort($addressTracker->portId); |
|
||||||
$uri = $db->getUri($addressTracker->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$tr[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
// Acceptable Source |
|
||||||
$as = []; |
|
||||||
|
|
||||||
foreach ($db->findAcceptableSourceByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$acceptableSource = $db->getAcceptableSource($result->acceptableSourceId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($acceptableSource->schemeId); |
|
||||||
$host = $db->getHost($acceptableSource->hostId); |
|
||||||
$port = $db->getPort($acceptableSource->portId); |
|
||||||
$uri = $db->getUri($acceptableSource->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$as[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
// Exact Source |
|
||||||
$xs = []; |
|
||||||
|
|
||||||
foreach ($db->findExactSourceByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$eXactSource = $db->getExactSource($result->eXactSourceId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($eXactSource->schemeId); |
|
||||||
$host = $db->getHost($eXactSource->hostId); |
|
||||||
$port = $db->getPort($eXactSource->portId); |
|
||||||
$uri = $db->getUri($eXactSource->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$xs[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
$magnets[] = (object) |
|
||||||
[ |
|
||||||
'magnetId' => (int) $magnet->magnetId, |
|
||||||
'userId' => (int) $magnet->userId, |
|
||||||
'title' => (string) $magnet->title, |
|
||||||
'preview' => (string) $magnet->preview, |
|
||||||
'description' => (string) $magnet->description, |
|
||||||
'comments' => (bool) $magnet->comments, |
|
||||||
'sensitive' => (bool) $magnet->sensitive, |
|
||||||
'approved' => (bool) $magnet->approved, |
|
||||||
'timeAdded' => (int) $magnet->timeAdded, |
|
||||||
'timeUpdated' => (int) $magnet->timeUpdated, |
|
||||||
'dn' => (string) $magnet->dn, |
|
||||||
'xl' => (float) $magnet->xl, |
|
||||||
'xt' => (object) $xt, |
|
||||||
'kt' => (object) $kt, |
|
||||||
'tr' => (object) $tr, |
|
||||||
'as' => (object) $as, |
|
||||||
'xs' => (object) $xs, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// Cache public status |
|
||||||
if (!empty($public['user'][$magnet->userId])) |
|
||||||
{ |
|
||||||
$public['magnet'][$magnet->magnetId] = (bool) $magnet->public; |
|
||||||
} else { |
|
||||||
$public['magnet'][$magnet->magnetId] = false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump magnets feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnets.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($magnets)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/magnets.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet downloads |
|
||||||
if (API_EXPORT_MAGNET_DOWNLOADS_ENABLED) |
|
||||||
{ |
|
||||||
$magnetDownloads = []; |
|
||||||
|
|
||||||
foreach ($db->getMagnetDownloads() as $magnetDownload) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetDownload->magnetId]) && |
|
||||||
!empty($public['user'][$magnetDownload->userId])) |
|
||||||
{ |
|
||||||
$magnetDownloads[] = (object) |
|
||||||
[ |
|
||||||
'magnetDownloadId' => (int) $magnetDownload->magnetDownloadId, |
|
||||||
'userId' => (int) $magnetDownload->userId, |
|
||||||
'magnetId' => (int) $magnetDownload->magnetId, |
|
||||||
'timeAdded' => (int) $magnetDownload->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetDownloads.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($magnetDownloads)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/magnetDownloads.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet comments |
|
||||||
if (API_EXPORT_MAGNET_COMMENTS_ENABLED) |
|
||||||
{ |
|
||||||
$magnetComments = []; |
|
||||||
|
|
||||||
foreach ($db->getMagnetComments() as $magnetComment) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetComment->magnetId]) && |
|
||||||
!empty($public['user'][$magnetComment->userId])) |
|
||||||
{ |
|
||||||
$magnetComments[] = (object) |
|
||||||
[ |
|
||||||
'magnetCommentId' => (int) $magnetComment->magnetCommentId, |
|
||||||
'magnetCommentIdParent' => $magnetComment->magnetCommentIdParent, |
|
||||||
'userId' => (int) $magnetComment->userId, |
|
||||||
'magnetId' => (int) $magnetComment->magnetId, |
|
||||||
'timeAdded' => (int) $magnetComment->timeAdded, |
|
||||||
'approved' => (bool) $magnetComment->approved, |
|
||||||
'value' => (string) $magnetComment->value |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetComments.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($magnetComments)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/magnetComments.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet stars |
|
||||||
if (API_EXPORT_MAGNET_STARS_ENABLED) |
|
||||||
{ |
|
||||||
$magnetStars = []; |
|
||||||
|
|
||||||
foreach ($db->getMagnetStars() as $magnetStar) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetStar->magnetId]) && |
|
||||||
!empty($public['user'][$magnetStar->userId])) |
|
||||||
{ |
|
||||||
$magnetStars[] = (object) |
|
||||||
[ |
|
||||||
'magnetStarId' => (int) $magnetStar->magnetStarId, |
|
||||||
'userId' => (int) $magnetStar->userId, |
|
||||||
'magnetId' => (int) $magnetStar->magnetId, |
|
||||||
'value' => (bool) $magnetStar->value, |
|
||||||
'timeAdded' => (int) $magnetStar->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetStars.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($magnetStars)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/magnetStars.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet views |
|
||||||
if (API_EXPORT_MAGNET_VIEWS_ENABLED) |
|
||||||
{ |
|
||||||
$magnetViews = []; |
|
||||||
|
|
||||||
foreach ($db->getMagnetViews() as $magnetView) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetView->magnetId]) && |
|
||||||
!empty($public['user'][$magnetView->userId])) |
|
||||||
{ |
|
||||||
$magnetViews[] = (object) |
|
||||||
[ |
|
||||||
'magnetViewId' => (int) $magnetView->magnetViewId, |
|
||||||
'userId' => (int) $magnetView->userId, |
|
||||||
'magnetId' => (int) $magnetView->magnetId, |
|
||||||
'timeAdded' => (int) $magnetView->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/// Dump feed |
|
||||||
if ($handle = fopen(__DIR__ . '/../../public/api/magnetViews.json', 'w+')) |
|
||||||
{ |
|
||||||
fwrite($handle, json_encode($magnetViews)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(__DIR__ . '/../../public/api/magnetViews.json', 0774); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} catch (EXception $e) { |
|
||||||
|
|
||||||
var_dump($e); |
|
||||||
} |
|
||||||
|
|
||||||
// Debug output |
|
||||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
|
||||||
|
|
||||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
|
||||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
|
||||||
|
|
||||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
|
||||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
|
||||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
|
||||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
|
||||||
|
|
||||||
print_r($debug); |
|
||||||
|
|
||||||
// Debug log |
|
||||||
if (LOG_CRONTAB_EXPORT_FEED_ENABLED) |
|
||||||
{ |
|
||||||
@mkdir(LOG_DIRECTORY, 0774, true); |
|
||||||
|
|
||||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_FEED_FILENAME, 'a+')) |
|
||||||
{ |
|
||||||
fwrite($handle, print_r($debug, true)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_FEED_FILENAME, 0774); |
|
||||||
} |
|
||||||
} |
|
@ -1,506 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// Lock multi-thread execution |
|
||||||
$semaphore = sem_get(crc32('yggtracker.crontab.export.push'), 1); |
|
||||||
|
|
||||||
if (false === sem_acquire($semaphore, true)) |
|
||||||
{ |
|
||||||
exit (_('yggtracker.crontab.export.push process locked by another thread.')); |
|
||||||
} |
|
||||||
|
|
||||||
// Bootstrap |
|
||||||
require_once __DIR__ . '/../../config/bootstrap.php'; |
|
||||||
|
|
||||||
// Init Debug |
|
||||||
$debug = |
|
||||||
[ |
|
||||||
'dump' => [], |
|
||||||
'time' => [ |
|
||||||
'ISO8601' => date('c'), |
|
||||||
'total' => microtime(true), |
|
||||||
], |
|
||||||
'http' => |
|
||||||
[ |
|
||||||
'total' => 0, |
|
||||||
], |
|
||||||
'memory' => |
|
||||||
[ |
|
||||||
'start' => memory_get_usage(), |
|
||||||
'total' => 0, |
|
||||||
'peaks' => 0 |
|
||||||
], |
|
||||||
]; |
|
||||||
|
|
||||||
// Define public registry |
|
||||||
$public = [ |
|
||||||
'user' => [], |
|
||||||
'magnet' => [], |
|
||||||
]; |
|
||||||
|
|
||||||
// Push export enabled |
|
||||||
if (API_EXPORT_PUSH_ENABLED) |
|
||||||
{ |
|
||||||
// Get push queue from memory pool |
|
||||||
foreach((array) $memoryApiExportPush = $memory->get('api.export.push') as $id => $push) |
|
||||||
{ |
|
||||||
// Init request |
|
||||||
$request = []; |
|
||||||
|
|
||||||
// User request |
|
||||||
if (!empty($push->userId) && API_EXPORT_USERS_ENABLED) |
|
||||||
{ |
|
||||||
// Get user info |
|
||||||
if ($user = $db->getUser($push->userId)) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if ($user->public) |
|
||||||
{ |
|
||||||
$request['user'] = (object) |
|
||||||
[ |
|
||||||
'userId' => (int) $user->userId, |
|
||||||
'address' => (string) $user->address, |
|
||||||
'timeAdded' => (int) $user->timeAdded, |
|
||||||
'timeUpdated' => (int) $user->timeUpdated, |
|
||||||
'approved' => (bool) $user->approved, |
|
||||||
]; |
|
||||||
|
|
||||||
// Cache public status |
|
||||||
$public['user'][$user->userId] = (bool) $user->public; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet request |
|
||||||
if (!empty($push->magnetId) && API_EXPORT_MAGNETS_ENABLED) |
|
||||||
{ |
|
||||||
// Get magnet info |
|
||||||
if ($magnet = $db->getMagnet($push->magnetId)) |
|
||||||
{ |
|
||||||
if ($magnet->public && |
|
||||||
$public['user'][$magnet->userId]) // After upgrade, some users have not updated their public status. |
|
||||||
// Remote node have warning on import, because user info still hidden to init new profile there. |
|
||||||
// Stop magnets export without public profile available, even magnet is public. |
|
||||||
{ |
|
||||||
// Info Hash |
|
||||||
$xt = []; |
|
||||||
foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
if ($infoHash = $db->getInfoHash($result->infoHashId)) |
|
||||||
{ |
|
||||||
$xt[] = (object) [ |
|
||||||
'version' => (float) $infoHash->version, |
|
||||||
'value' => (string) $infoHash->value, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Keyword Topic |
|
||||||
$kt = []; |
|
||||||
|
|
||||||
foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; |
|
||||||
} |
|
||||||
|
|
||||||
// Address Tracker |
|
||||||
$tr = []; |
|
||||||
foreach ($db->findAddressTrackerByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$addressTracker = $db->getAddressTracker($result->addressTrackerId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($addressTracker->schemeId); |
|
||||||
$host = $db->getHost($addressTracker->hostId); |
|
||||||
$port = $db->getPort($addressTracker->portId); |
|
||||||
$uri = $db->getUri($addressTracker->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$tr[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
// Acceptable Source |
|
||||||
$as = []; |
|
||||||
|
|
||||||
foreach ($db->findAcceptableSourceByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$acceptableSource = $db->getAcceptableSource($result->acceptableSourceId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($acceptableSource->schemeId); |
|
||||||
$host = $db->getHost($acceptableSource->hostId); |
|
||||||
$port = $db->getPort($acceptableSource->portId); |
|
||||||
$uri = $db->getUri($acceptableSource->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$as[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
// Exact Source |
|
||||||
$xs = []; |
|
||||||
|
|
||||||
foreach ($db->findExactSourceByMagnetId($magnet->magnetId) as $result) |
|
||||||
{ |
|
||||||
$eXactSource = $db->getExactSource($result->eXactSourceId); |
|
||||||
|
|
||||||
$scheme = $db->getScheme($eXactSource->schemeId); |
|
||||||
$host = $db->getHost($eXactSource->hostId); |
|
||||||
$port = $db->getPort($eXactSource->portId); |
|
||||||
$uri = $db->getUri($eXactSource->uriId); |
|
||||||
|
|
||||||
// Yggdrasil host only |
|
||||||
if (!Valid::host($host->value)) |
|
||||||
{ |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$xs[] = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
} |
|
||||||
|
|
||||||
$request['magnet'] = (object) |
|
||||||
[ |
|
||||||
'magnetId' => (int) $magnet->magnetId, |
|
||||||
'userId' => (int) $magnet->userId, |
|
||||||
'title' => (string) $magnet->title, |
|
||||||
'preview' => (string) $magnet->preview, |
|
||||||
'description' => (string) $magnet->description, |
|
||||||
'comments' => (bool) $magnet->comments, |
|
||||||
'sensitive' => (bool) $magnet->sensitive, |
|
||||||
'approved' => (bool) $magnet->approved, |
|
||||||
'timeAdded' => (int) $magnet->timeAdded, |
|
||||||
'timeUpdated' => (int) $magnet->timeUpdated, |
|
||||||
'dn' => (string) $magnet->dn, |
|
||||||
'xl' => (float) $magnet->xl, |
|
||||||
'xt' => (object) $xt, |
|
||||||
'kt' => (object) $kt, |
|
||||||
'tr' => (object) $tr, |
|
||||||
'as' => (object) $as, |
|
||||||
'xs' => (object) $xs, |
|
||||||
]; |
|
||||||
} |
|
||||||
|
|
||||||
// Cache public status |
|
||||||
if (!empty($public['user'][$magnet->userId])) |
|
||||||
{ |
|
||||||
$public['magnet'][$magnet->magnetId] = (bool) $magnet->public; |
|
||||||
} else { |
|
||||||
$public['magnet'][$magnet->magnetId] = false; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet download request |
|
||||||
if (!empty($push->magnetDownloadId) && API_EXPORT_MAGNET_DOWNLOADS_ENABLED) |
|
||||||
{ |
|
||||||
// Get magnet download info |
|
||||||
if ($magnetDownload = $db->getMagnetDownload($push->magnetDownloadId)) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetDownload->magnetId]) && |
|
||||||
!empty($public['user'][$magnetDownload->userId])) |
|
||||||
{ |
|
||||||
$request['magnetDownload'] = (object) |
|
||||||
[ |
|
||||||
'magnetDownloadId' => (int) $magnetDownload->magnetDownloadId, |
|
||||||
'userId' => (int) $magnetDownload->userId, |
|
||||||
'magnetId' => (int) $magnetDownload->magnetId, |
|
||||||
'timeAdded' => (int) $magnetDownload->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet comment request |
|
||||||
if (!empty($push->magnetCommentId) && API_EXPORT_MAGNET_COMMENTS_ENABLED) |
|
||||||
{ |
|
||||||
// Get magnet comment info |
|
||||||
if ($magnetComment = $db->getMagnetComment($push->magnetCommentId)) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetComment->magnetId]) && |
|
||||||
!empty($public['user'][$magnetComment->userId])) |
|
||||||
{ |
|
||||||
$request['magnetComment'] = (object) |
|
||||||
[ |
|
||||||
'magnetCommentId' => (int) $magnetComment->magnetCommentId, |
|
||||||
'magnetCommentIdParent' => (int) $magnetComment->magnetCommentIdParent, |
|
||||||
'userId' => (int) $magnetComment->userId, |
|
||||||
'magnetId' => (int) $magnetComment->magnetId, |
|
||||||
'timeAdded' => (int) $magnetComment->timeAdded, |
|
||||||
'approved' => (bool) $magnetComment->approved, |
|
||||||
'value' => (string) $magnetComment->value |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet star request |
|
||||||
if (!empty($push->magnetStarId) && API_EXPORT_MAGNET_STARS_ENABLED) |
|
||||||
{ |
|
||||||
// Get magnet star info |
|
||||||
if ($magnetStar = $db->getMagnetStar($push->magnetStarId)) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetStar->magnetId]) && |
|
||||||
!empty($public['user'][$magnetStar->userId])) |
|
||||||
{ |
|
||||||
$request['magnetStar'] = (object) |
|
||||||
[ |
|
||||||
'magnetStarId' => (int) $magnetStar->magnetStarId, |
|
||||||
'userId' => (int) $magnetStar->userId, |
|
||||||
'magnetId' => (int) $magnetStar->magnetId, |
|
||||||
'value' => (bool) $magnetStar->value, |
|
||||||
'timeAdded' => (int) $magnetStar->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Magnet view request |
|
||||||
if (!empty($push->magnetViewId) && API_EXPORT_MAGNET_VIEWS_ENABLED) |
|
||||||
{ |
|
||||||
// Get magnet view info |
|
||||||
if ($magnetView = $db->getMagnetView($push->magnetViewId)) |
|
||||||
{ |
|
||||||
// Dump public data only |
|
||||||
if (!empty($public['magnet'][$magnetView->magnetId]) && |
|
||||||
!empty($public['user'][$magnetView->userId])) |
|
||||||
{ |
|
||||||
$request['magnetView'] = (object) |
|
||||||
[ |
|
||||||
'magnetViewId' => (int) $magnetView->magnetViewId, |
|
||||||
'userId' => (int) $magnetView->userId, |
|
||||||
'magnetId' => (int) $magnetView->magnetId, |
|
||||||
'timeAdded' => (int) $magnetView->timeAdded, |
|
||||||
]; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Check request |
|
||||||
if (empty($request)) |
|
||||||
{ |
|
||||||
// Amy request data match conditions, skip |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Send push data |
|
||||||
foreach (json_decode( |
|
||||||
file_get_contents(__DIR__ . '/../../config/nodes.json') |
|
||||||
) as $node) |
|
||||||
{ |
|
||||||
// Manifest exists |
|
||||||
if (empty($node->manifest)) |
|
||||||
{ |
|
||||||
$debug['dump']['warning'][] = sprintf( |
|
||||||
_('Manifest URL not provided: %s'), |
|
||||||
$node |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Skip non-condition addresses |
|
||||||
$error = []; |
|
||||||
|
|
||||||
if (!Valid::url($node->manifest, $error)) |
|
||||||
{ |
|
||||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
|
||||||
_('Manifest URL invalid: %s'), |
|
||||||
print_r( |
|
||||||
$error, |
|
||||||
true |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Skip current host |
|
||||||
$thisUrl = Yggverse\Parser\Url::parse(WEBSITE_URL); |
|
||||||
$manifestUrl = Yggverse\Parser\Url::parse($node->manifest); |
|
||||||
|
|
||||||
if (empty($thisUrl->host->name) || |
|
||||||
empty($manifestUrl->host->name) || |
|
||||||
$manifestUrl->host->name == $thisUrl->host->name) // @TODO some mirrors could be available, improve condition |
|
||||||
{ |
|
||||||
// No debug warnings in this case, continue next item |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Get node manifest |
|
||||||
// @TODO cache to prevent extra-queries as usually this script running every minute |
|
||||||
$curl = new Curl($node->manifest, API_USER_AGENT); |
|
||||||
|
|
||||||
$debug['http']['total']++; |
|
||||||
|
|
||||||
if (200 != $code = $curl->getCode()) |
|
||||||
{ |
|
||||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
|
||||||
_('Manifest unreachable with code: "%s"'), |
|
||||||
$code |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (!$manifest = $curl->getResponse()) |
|
||||||
{ |
|
||||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
|
||||||
_('Manifest URL "%s" has broken response'), |
|
||||||
$node->manifest |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// API channel not exists |
|
||||||
if (empty($manifest->import)) |
|
||||||
{ |
|
||||||
$debug['dump'][$node->manifest]['warning'][] = sprintf( |
|
||||||
_('Manifest import URL not provided: %s'), |
|
||||||
$node |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Push API channel not exists |
|
||||||
if (empty($manifest->import->push)) |
|
||||||
{ |
|
||||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
|
||||||
_('Manifest import push URL not provided: %s'), |
|
||||||
$node |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Skip sending to non-condition addresses |
|
||||||
$error = []; |
|
||||||
|
|
||||||
if (!Valid::url($manifest->import->push, $error)) |
|
||||||
{ |
|
||||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
|
||||||
_('Manifest import push URL invalid: %s'), |
|
||||||
print_r( |
|
||||||
$error, |
|
||||||
true |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// Skip current host |
|
||||||
$thisUrl = Yggverse\Parser\Url::parse(WEBSITE_URL); |
|
||||||
$pushUrl = Yggverse\Parser\Url::parse($manifest->import->push); |
|
||||||
|
|
||||||
if (empty($thisUrl->host->name) || |
|
||||||
empty($pushUrl->host->name) || |
|
||||||
$pushUrl->host->name == $thisUrl->host->name) // @TODO some mirrors could be available, improve condition |
|
||||||
{ |
|
||||||
// No debug warnings in this case, continue next item |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
// @TODO add recipient manifest conditions check to not disturb it API without needs |
|
||||||
|
|
||||||
// Send push request |
|
||||||
$debug['dump'][$manifest->import->push]['request'][] = $request; |
|
||||||
|
|
||||||
$curl = new Curl( |
|
||||||
$manifest->import->push, |
|
||||||
API_USER_AGENT, |
|
||||||
[ |
|
||||||
'data' => json_encode($request) |
|
||||||
] |
|
||||||
); |
|
||||||
|
|
||||||
$debug['http']['total']++; |
|
||||||
|
|
||||||
if (200 != $code = $curl->getCode()) |
|
||||||
{ |
|
||||||
$debug['dump'][$manifest->import->push]['warning'][] = sprintf( |
|
||||||
_('Server returned code "%s"'), |
|
||||||
$code |
|
||||||
); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (!$response = $curl->getResponse()) |
|
||||||
{ |
|
||||||
$debug['dump'][$manifest->import->push]['warning'][] = _('Could not receive server response'); |
|
||||||
|
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$debug['dump'][$manifest->import->push]['response'][] = $response; |
|
||||||
} |
|
||||||
|
|
||||||
// Drop processed item from queue |
|
||||||
unset($memoryApiExportPush[$id]); |
|
||||||
} |
|
||||||
|
|
||||||
// Update memory pool |
|
||||||
$memory->set('api.export.push', $memoryApiExportPush, 3600); |
|
||||||
} |
|
||||||
|
|
||||||
// Export push disabled, free api.export.push pool |
|
||||||
else |
|
||||||
{ |
|
||||||
$memory->delete('api.export.push'); |
|
||||||
} |
|
||||||
|
|
||||||
// Debug output |
|
||||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
|
||||||
|
|
||||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
|
||||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
|
||||||
|
|
||||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
|
||||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
|
||||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
|
||||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
|
||||||
|
|
||||||
print_r($debug); |
|
||||||
|
|
||||||
// Debug log |
|
||||||
if (LOG_CRONTAB_EXPORT_PUSH_ENABLED) |
|
||||||
{ |
|
||||||
@mkdir(LOG_DIRECTORY, 0770, true); |
|
||||||
|
|
||||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_PUSH_FILENAME, 'a+')) |
|
||||||
{ |
|
||||||
fwrite($handle, print_r($debug, true)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_EXPORT_PUSH_FILENAME, 0770); |
|
||||||
} |
|
||||||
} |
|
@ -1,158 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// Lock multi-thread execution |
|
||||||
$semaphore = sem_get(crc32('yggtracker.crontab.scrape'), 1); |
|
||||||
|
|
||||||
if (false === sem_acquire($semaphore, true)) { |
|
||||||
|
|
||||||
exit (PHP_EOL . 'yggtracker.crontab.scrape process locked by another thread.' . PHP_EOL); |
|
||||||
} |
|
||||||
|
|
||||||
// Bootstrap |
|
||||||
require_once __DIR__ . '/../config/bootstrap.php'; |
|
||||||
|
|
||||||
// Init Debug |
|
||||||
$debug = [ |
|
||||||
'time' => [ |
|
||||||
'ISO8601' => date('c'), |
|
||||||
'total' => microtime(true), |
|
||||||
], |
|
||||||
'http' => |
|
||||||
[ |
|
||||||
'total' => 0, |
|
||||||
], |
|
||||||
'memory' => |
|
||||||
[ |
|
||||||
'start' => memory_get_usage(), |
|
||||||
'total' => 0, |
|
||||||
'peaks' => 0 |
|
||||||
], |
|
||||||
]; |
|
||||||
|
|
||||||
// Init Scraper |
|
||||||
try { |
|
||||||
|
|
||||||
$scraper = new Scrapeer\Scraper(); |
|
||||||
|
|
||||||
} catch(Exception $e) { |
|
||||||
|
|
||||||
var_dump($e); |
|
||||||
|
|
||||||
exit; |
|
||||||
} |
|
||||||
|
|
||||||
// Begin |
|
||||||
try { |
|
||||||
|
|
||||||
$db->beginTransaction(); |
|
||||||
|
|
||||||
// Reset time offline by timeout |
|
||||||
$db->resetMagnetToAddressTrackerTimeOfflineByTimeout( |
|
||||||
CRAWLER_SCRAPE_TIME_OFFLINE_TIMEOUT |
|
||||||
); |
|
||||||
|
|
||||||
foreach ($db->getMagnetToAddressTrackerScrapeQueue(CRAWLER_SCRAPE_QUEUE_LIMIT) as $queue) |
|
||||||
{ |
|
||||||
$hashes = []; |
|
||||||
foreach ($db->findMagnetToInfoHashByMagnetId($queue->magnetId) as $result) |
|
||||||
{ |
|
||||||
$hashes[] = $db->getInfoHash($result->infoHashId)->value; |
|
||||||
} |
|
||||||
|
|
||||||
if ($addressTracker = $db->getAddressTracker($queue->addressTrackerId)) |
|
||||||
{ |
|
||||||
// Build url |
|
||||||
$scheme = $db->getScheme($addressTracker->schemeId); |
|
||||||
$host = $db->getHost($addressTracker->hostId); |
|
||||||
$port = $db->getPort($addressTracker->portId); |
|
||||||
$uri = $db->getUri($addressTracker->uriId); |
|
||||||
|
|
||||||
$url = $port->value ? sprintf('%s://%s:%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$port->value, |
|
||||||
$uri->value) : sprintf('%s://%s%s', $scheme->value, |
|
||||||
$host->value, |
|
||||||
$uri->value); |
|
||||||
|
|
||||||
foreach ($hashes as $hash) |
|
||||||
{ |
|
||||||
if ($scrape = $scraper->scrape([$hash], [$url], null, 1)) |
|
||||||
{ |
|
||||||
$db->updateMagnetToAddressTrackerTimeOffline( |
|
||||||
$queue->magnetToAddressTrackerId, |
|
||||||
null |
|
||||||
); |
|
||||||
|
|
||||||
if (isset($scrape[$hash]['seeders'])) |
|
||||||
{ |
|
||||||
$db->updateMagnetToAddressTrackerSeeders( |
|
||||||
$queue->magnetToAddressTrackerId, |
|
||||||
(int) $scrape[$hash]['seeders'], |
|
||||||
time() |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($scrape[$hash]['completed'])) |
|
||||||
{ |
|
||||||
$db->updateMagnetToAddressTrackerCompleted( |
|
||||||
$queue->magnetToAddressTrackerId, |
|
||||||
(int) $scrape[$hash]['completed'], |
|
||||||
time() |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
if (isset($scrape[$hash]['leechers'])) |
|
||||||
{ |
|
||||||
$db->updateMagnetToAddressTrackerLeechers( |
|
||||||
$queue->magnetToAddressTrackerId, |
|
||||||
(int) $scrape[$hash]['leechers'], |
|
||||||
time() |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
$db->updateMagnetToAddressTrackerTimeOffline( |
|
||||||
$queue->magnetToAddressTrackerId, |
|
||||||
time() |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$db->commit(); |
|
||||||
|
|
||||||
} catch (EXception $e) { |
|
||||||
|
|
||||||
$db->rollback(); |
|
||||||
|
|
||||||
var_dump($e); |
|
||||||
} |
|
||||||
|
|
||||||
// Debug output |
|
||||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
|
||||||
|
|
||||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
|
||||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
|
||||||
|
|
||||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
|
||||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
|
||||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
|
||||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
|
||||||
|
|
||||||
print_r($debug); |
|
||||||
|
|
||||||
// Debug log |
|
||||||
if (LOG_CRONTAB_SCRAPE_ENABLED) |
|
||||||
{ |
|
||||||
@mkdir(LOG_DIRECTORY, 0774, true); |
|
||||||
|
|
||||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_SCRAPE_FILENAME, 'a+')) |
|
||||||
{ |
|
||||||
fwrite($handle, print_r($debug, true)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_SCRAPE_FILENAME, 0774); |
|
||||||
} |
|
||||||
} |
|
@ -1,86 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
// Lock multi-thread execution |
|
||||||
$semaphore = sem_get(crc32('yggtracker.crontab.sitemap'), 1); |
|
||||||
|
|
||||||
if (false === sem_acquire($semaphore, true)) { |
|
||||||
|
|
||||||
exit (PHP_EOL . 'yggtracker.crontab.sitemap process locked by another thread.' . PHP_EOL); |
|
||||||
} |
|
||||||
|
|
||||||
// Bootstrap |
|
||||||
require_once __DIR__ . '/../config/bootstrap.php'; |
|
||||||
|
|
||||||
// Init Debug |
|
||||||
$debug = [ |
|
||||||
'time' => [ |
|
||||||
'ISO8601' => date('c'), |
|
||||||
'total' => microtime(true), |
|
||||||
], |
|
||||||
'http' => |
|
||||||
[ |
|
||||||
'total' => 0, |
|
||||||
], |
|
||||||
'memory' => |
|
||||||
[ |
|
||||||
'start' => memory_get_usage(), |
|
||||||
'total' => 0, |
|
||||||
'peaks' => 0 |
|
||||||
], |
|
||||||
]; |
|
||||||
|
|
||||||
// Begin |
|
||||||
try { |
|
||||||
|
|
||||||
// Delete cache |
|
||||||
@unlink(__DIR__ . '/../public/sitemap.xml'); |
|
||||||
|
|
||||||
if ($handle = fopen(__DIR__ . '/../public/sitemap.xml', 'w+')) |
|
||||||
{ |
|
||||||
|
|
||||||
fwrite($handle, '<?xml version="1.0" encoding="UTF-8"?>'); |
|
||||||
fwrite($handle, '<urlset>'); |
|
||||||
|
|
||||||
foreach ($db->getMagnets() as $magnet) |
|
||||||
{ |
|
||||||
if ($magnet->public && $magnet->approved) |
|
||||||
{ |
|
||||||
fwrite($handle, sprintf('<url><loc>%s/magnet.php?magnetId=%s</loc></url>', WEBSITE_URL, $magnet->magnetId)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fwrite($handle, '</urlset>'); |
|
||||||
fclose($handle); |
|
||||||
} |
|
||||||
|
|
||||||
} catch (EXception $e) { |
|
||||||
|
|
||||||
var_dump($e); |
|
||||||
} |
|
||||||
|
|
||||||
// Debug output |
|
||||||
$debug['time']['total'] = microtime(true) - $debug['time']['total']; |
|
||||||
|
|
||||||
$debug['memory']['total'] = memory_get_usage() - $debug['memory']['start']; |
|
||||||
$debug['memory']['peaks'] = memory_get_peak_usage(); |
|
||||||
|
|
||||||
$debug['db']['total']['select'] = $db->getDebug()->query->select->total; |
|
||||||
$debug['db']['total']['insert'] = $db->getDebug()->query->insert->total; |
|
||||||
$debug['db']['total']['update'] = $db->getDebug()->query->update->total; |
|
||||||
$debug['db']['total']['delete'] = $db->getDebug()->query->delete->total; |
|
||||||
|
|
||||||
print_r($debug); |
|
||||||
|
|
||||||
// Debug log |
|
||||||
if (LOG_CRONTAB_SITEMAP_ENABLED) |
|
||||||
{ |
|
||||||
@mkdir(LOG_DIRECTORY, 0774, true); |
|
||||||
|
|
||||||
if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_SITEMAP_FILENAME, 'a+')) |
|
||||||
{ |
|
||||||
fwrite($handle, print_r($debug, true)); |
|
||||||
fclose($handle); |
|
||||||
|
|
||||||
chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_SITEMAP_FILENAME, 0774); |
|
||||||
} |
|
||||||
} |
|
@ -1,97 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class Curl |
|
||||||
{ |
|
||||||
private $_connection; |
|
||||||
private $_response; |
|
||||||
|
|
||||||
public function __construct(string $url, |
|
||||||
string $userAgent = 'YGGtracker', |
|
||||||
array $post = [], |
|
||||||
int $connectTimeout = 10, |
|
||||||
bool $header = false, |
|
||||||
bool $followLocation = false, |
|
||||||
int $maxRedirects = 10, |
|
||||||
bool $sslVerifyHost = false, |
|
||||||
bool $sslVerifyPeer = false) |
|
||||||
{ |
|
||||||
$this->_connection = curl_init($url); |
|
||||||
|
|
||||||
if ($userAgent) |
|
||||||
{ |
|
||||||
curl_setopt($this->_connection, CURLOPT_USERAGENT, $userAgent); |
|
||||||
} |
|
||||||
|
|
||||||
if (!empty($post)) |
|
||||||
{ |
|
||||||
curl_setopt($this->_connection, CURLOPT_POST, true); |
|
||||||
curl_setopt($this->_connection, CURLOPT_POSTFIELDS, http_build_query($post)); |
|
||||||
} |
|
||||||
|
|
||||||
if ($header) { |
|
||||||
curl_setopt($this->_connection, CURLOPT_HEADER, true); |
|
||||||
} |
|
||||||
|
|
||||||
if ($followLocation) { |
|
||||||
curl_setopt($this->_connection, CURLOPT_FOLLOWLOCATION, true); |
|
||||||
curl_setopt($this->_connection, CURLOPT_MAXREDIRS, $maxRedirects); |
|
||||||
} |
|
||||||
|
|
||||||
curl_setopt($this->_connection, CURLOPT_FRESH_CONNECT, true); |
|
||||||
curl_setopt($this->_connection, CURLOPT_RETURNTRANSFER, true); |
|
||||||
curl_setopt($this->_connection, CURLOPT_CONNECTTIMEOUT, $connectTimeout); |
|
||||||
curl_setopt($this->_connection, CURLOPT_TIMEOUT, $connectTimeout); |
|
||||||
curl_setopt($this->_connection, CURLOPT_SSL_VERIFYHOST, $sslVerifyHost); |
|
||||||
curl_setopt($this->_connection, CURLOPT_SSL_VERIFYPEER, $sslVerifyPeer); |
|
||||||
|
|
||||||
$this->_response = curl_exec($this->_connection); |
|
||||||
} |
|
||||||
|
|
||||||
public function __destruct() |
|
||||||
{ |
|
||||||
curl_close($this->_connection); |
|
||||||
} |
|
||||||
|
|
||||||
public function getError() |
|
||||||
{ |
|
||||||
if (curl_errno($this->_connection)) |
|
||||||
{ |
|
||||||
return curl_errno($this->_connection); |
|
||||||
} |
|
||||||
|
|
||||||
else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public function getCode() |
|
||||||
{ |
|
||||||
return curl_getinfo($this->_connection, CURLINFO_HTTP_CODE); |
|
||||||
} |
|
||||||
|
|
||||||
public function getContentType() |
|
||||||
{ |
|
||||||
return curl_getinfo($this->_connection, CURLINFO_CONTENT_TYPE); |
|
||||||
} |
|
||||||
|
|
||||||
public function getSizeDownload() |
|
||||||
{ |
|
||||||
return curl_getinfo($this->_connection, CURLINFO_SIZE_DOWNLOAD); |
|
||||||
} |
|
||||||
|
|
||||||
public function getSizeRequest() |
|
||||||
{ |
|
||||||
return curl_getinfo($this->_connection, CURLINFO_REQUEST_SIZE); |
|
||||||
} |
|
||||||
|
|
||||||
public function getTotalTime() |
|
||||||
{ |
|
||||||
return curl_getinfo($this->_connection, CURLINFO_TOTAL_TIME_T); |
|
||||||
} |
|
||||||
|
|
||||||
public function getResponse(bool $json = true) |
|
||||||
{ |
|
||||||
return $json ? json_decode($this->_response) : $this->_response; |
|
||||||
} |
|
||||||
} |
|
@ -1,27 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class Environment |
|
||||||
{ |
|
||||||
public static function config(string $name) : object |
|
||||||
{ |
|
||||||
$config = __DIR__ . '/../config/' . $name . '.json'; |
|
||||||
|
|
||||||
if (file_exists(__DIR__ . '/../config/.env')) |
|
||||||
{ |
|
||||||
$environment = file_get_contents(__DIR__ . '/../config/.env'); |
|
||||||
|
|
||||||
$filename = __DIR__ . '/../config/' . $environment . '/' . $name . '.json'; |
|
||||||
|
|
||||||
if (file_exists($filename)) |
|
||||||
{ |
|
||||||
$config = $filename; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return (object) json_decode( |
|
||||||
file_get_contents( |
|
||||||
$config |
|
||||||
) |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
@ -1,48 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class Filter |
|
||||||
{ |
|
||||||
public static function magnetTitle(mixed $value) : string |
|
||||||
{ |
|
||||||
$value = trim( |
|
||||||
strip_tags( |
|
||||||
html_entity_decode($value) |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
return (string) $value; |
|
||||||
} |
|
||||||
|
|
||||||
public static function magnetPreview(mixed $value) : string |
|
||||||
{ |
|
||||||
$value = trim( |
|
||||||
strip_tags( |
|
||||||
html_entity_decode($value) |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
return (string) $value; |
|
||||||
} |
|
||||||
|
|
||||||
public static function magnetDescription(mixed $value) : string |
|
||||||
{ |
|
||||||
$value = trim( |
|
||||||
strip_tags( |
|
||||||
html_entity_decode($value) |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
return (string) $value; |
|
||||||
} |
|
||||||
|
|
||||||
public static function magnetDn(mixed $value) : string |
|
||||||
{ |
|
||||||
$value = trim( |
|
||||||
strip_tags( |
|
||||||
html_entity_decode($value) |
|
||||||
) |
|
||||||
); |
|
||||||
|
|
||||||
return (string) $value; |
|
||||||
} |
|
||||||
} |
|
@ -1,692 +0,0 @@ |
|||||||
<?php |
|
||||||
/** |
|
||||||
* Scrapeer, a tiny PHP library that lets you scrape |
|
||||||
* HTTP(S) and UDP trackers for torrent information. |
|
||||||
* |
|
||||||
* This file is extensively based on Johannes Zinnau's |
|
||||||
* work, which can be found at https://goo.gl/7hyjde |
|
||||||
* |
|
||||||
* Licensed under a Creative Commons |
|
||||||
* Attribution-ShareAlike 3.0 Unported License |
|
||||||
* http://creativecommons.org/licenses/by-sa/3.0 |
|
||||||
* |
|
||||||
* @package Scrapeer |
|
||||||
*/ |
|
||||||
|
|
||||||
namespace Scrapeer; |
|
||||||
|
|
||||||
/** |
|
||||||
* The one and only class you'll ever need. |
|
||||||
*/ |
|
||||||
class Scraper { |
|
||||||
|
|
||||||
/** |
|
||||||
* Current version of Scrapeer |
|
||||||
* |
|
||||||
* @var string |
|
||||||
*/ |
|
||||||
const VERSION = '0.5.4'; |
|
||||||
|
|
||||||
/** |
|
||||||
* Array of errors |
|
||||||
* |
|
||||||
* @var array |
|
||||||
*/ |
|
||||||
private $errors = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Array of infohashes to scrape |
|
||||||
* |
|
||||||
* @var array |
|
||||||
*/ |
|
||||||
private $infohashes = array(); |
|
||||||
|
|
||||||
/** |
|
||||||
* Timeout for a single tracker |
|
||||||
* |
|
||||||
* @var int |
|
||||||
*/ |
|
||||||
private $timeout; |
|
||||||
|
|
||||||
/** |
|
||||||
* Initiates the scraper |
|
||||||
* |
|
||||||
* @throws \RangeException In case of invalid amount of info-hashes. |
|
||||||
* |
|
||||||
* @param array|string $hashes List (>1) or string of infohash(es). |
|
||||||
* @param array|string $trackers List (>1) or string of tracker(s). |
|
||||||
* @param int|null $max_trackers Optional. Maximum number of trackers to be scraped, Default all. |
|
||||||
* @param int $timeout Optional. Maximum time for each tracker scrape in seconds, Default 2. |
|
||||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
|
||||||
* @return array List of results. |
|
||||||
*/ |
|
||||||
public function scrape( $hashes, $trackers, $max_trackers = null, $timeout = 2, $announce = false ) { |
|
||||||
$final_result = array(); |
|
||||||
|
|
||||||
if ( empty( $trackers ) ) { |
|
||||||
$this->errors[] = 'No tracker specified, aborting.'; |
|
||||||
return $final_result; |
|
||||||
} else if ( ! is_array( $trackers ) ) { |
|
||||||
$trackers = array( $trackers ); |
|
||||||
} |
|
||||||
|
|
||||||
if ( is_int( $timeout ) ) { |
|
||||||
$this->timeout = $timeout; |
|
||||||
} else { |
|
||||||
$this->timeout = 2; |
|
||||||
$this->errors[] = 'Timeout must be an integer. Using default value.'; |
|
||||||
} |
|
||||||
|
|
||||||
try { |
|
||||||
$this->infohashes = $this->normalize_infohashes( $hashes ); |
|
||||||
} catch ( \RangeException $e ) { |
|
||||||
$this->errors[] = $e->getMessage(); |
|
||||||
return $final_result; |
|
||||||
} |
|
||||||
|
|
||||||
$max_iterations = is_int( $max_trackers ) ? $max_trackers : count( $trackers ); |
|
||||||
foreach ( $trackers as $index => $tracker ) { |
|
||||||
if ( ! empty( $this->infohashes ) && $index < $max_iterations ) { |
|
||||||
$info = parse_url( $tracker ); |
|
||||||
$protocol = $info['scheme']; |
|
||||||
$host = $info['host']; |
|
||||||
if ( empty( $protocol ) || empty( $host ) ) { |
|
||||||
$this->errors[] = 'Skipping invalid tracker (' . $tracker . ').'; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$port = isset( $info['port'] ) ? $info['port'] : null; |
|
||||||
$path = isset( $info['path'] ) ? $info['path'] : null; |
|
||||||
$passkey = $this->get_passkey( $path ); |
|
||||||
$result = $this->try_scrape( $protocol, $host, $port, $passkey, $announce ); |
|
||||||
$final_result = array_merge( $final_result, $result ); |
|
||||||
continue; |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
return $final_result; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Normalizes the given hashes |
|
||||||
* |
|
||||||
* @throws \RangeException If amount of valid infohashes > 64 or < 1. |
|
||||||
* |
|
||||||
* @param array $infohashes List of infohash(es). |
|
||||||
* @return array Normalized infohash(es). |
|
||||||
*/ |
|
||||||
private function normalize_infohashes( $infohashes ) { |
|
||||||
if ( ! is_array( $infohashes ) ) { |
|
||||||
$infohashes = array( $infohashes ); |
|
||||||
} |
|
||||||
|
|
||||||
foreach ( $infohashes as $index => $infohash ) { |
|
||||||
if ( ! preg_match( '/^[a-f0-9]{40}$/i', $infohash ) ) { |
|
||||||
$this->errors[] = 'Invalid infohash skipped (' . $infohash . ').'; |
|
||||||
unset( $infohashes[ $index ] ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
$total_infohashes = count( $infohashes ); |
|
||||||
if ( $total_infohashes > 64 || $total_infohashes < 1 ) { |
|
||||||
throw new \RangeException( 'Invalid amount of valid infohashes (' . $total_infohashes . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
$infohashes = array_values( $infohashes ); |
|
||||||
|
|
||||||
return $infohashes; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the passkey found in the scrape request. |
|
||||||
* |
|
||||||
* @param string $path Path from the scrape request. |
|
||||||
* @return string Passkey or empty string. |
|
||||||
*/ |
|
||||||
private function get_passkey( $path ) { |
|
||||||
if ( ! is_null( $path ) && preg_match( '/[a-z0-9]{32}/i', $path, $matches ) ) { |
|
||||||
return '/' . $matches[0]; |
|
||||||
} |
|
||||||
|
|
||||||
return ''; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Tries to scrape with a single tracker. |
|
||||||
* |
|
||||||
* @throws \Exception In case of unsupported protocol. |
|
||||||
* |
|
||||||
* @param string $protocol Protocol of the tracker. |
|
||||||
* @param string $host Domain or address of the tracker. |
|
||||||
* @param int $port Optional. Port number of the tracker. |
|
||||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
|
||||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
|
||||||
* @return array List of results. |
|
||||||
*/ |
|
||||||
private function try_scrape( $protocol, $host, $port, $passkey, $announce ) { |
|
||||||
$infohashes = $this->infohashes; |
|
||||||
$this->infohashes = array(); |
|
||||||
$results = array(); |
|
||||||
try { |
|
||||||
switch ( $protocol ) { |
|
||||||
case 'udp': |
|
||||||
$port = isset( $port ) ? $port : 80; |
|
||||||
$results = $this->scrape_udp( $infohashes, $host, $port, $announce ); |
|
||||||
break; |
|
||||||
case 'http': |
|
||||||
$port = isset( $port ) ? $port : 80; |
|
||||||
$results = $this->scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ); |
|
||||||
break; |
|
||||||
case 'https': |
|
||||||
$port = isset( $port ) ? $port : 443; |
|
||||||
$results = $this->scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ); |
|
||||||
break; |
|
||||||
default: |
|
||||||
throw new \Exception( 'Unsupported protocol (' . $protocol . '://' . $host . ').' ); |
|
||||||
} |
|
||||||
} catch ( \Exception $e ) { |
|
||||||
$this->infohashes = $infohashes; |
|
||||||
$this->errors[] = $e->getMessage(); |
|
||||||
} |
|
||||||
return $results; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initiates the HTTP(S) scraping |
|
||||||
* |
|
||||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
|
||||||
* @param string $protocol Protocol to use for the scraping. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Optional. Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
|
||||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
|
||||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
|
||||||
* @return array List of results. |
|
||||||
*/ |
|
||||||
private function scrape_http( $infohashes, $protocol, $host, $port, $passkey, $announce ) { |
|
||||||
if ( true === $announce ) { |
|
||||||
$response = $this->http_announce( $infohashes, $protocol, $host, $port, $passkey ); |
|
||||||
} else { |
|
||||||
$query = $this->http_query( $infohashes, $protocol, $host, $port, $passkey ); |
|
||||||
$response = $this->http_request( $query, $host, $port ); |
|
||||||
} |
|
||||||
$results = $this->http_data( $response, $infohashes, $host ); |
|
||||||
|
|
||||||
return $results; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Builds the HTTP(S) query |
|
||||||
* |
|
||||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
|
||||||
* @param string $protocol Protocol to use for the scraping. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
|
||||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
|
||||||
* @return string Request query. |
|
||||||
*/ |
|
||||||
private function http_query( $infohashes, $protocol, $host, $port, $passkey ) { |
|
||||||
$tracker_url = $protocol . '://' . $host . ':' . $port . $passkey; |
|
||||||
$scrape_query = ''; |
|
||||||
|
|
||||||
foreach ( $infohashes as $index => $infohash ) { |
|
||||||
if ( $index > 0 ) { |
|
||||||
$scrape_query .= '&info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
|
||||||
} else { |
|
||||||
$scrape_query .= '/scrape?info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
|
||||||
} |
|
||||||
} |
|
||||||
$request_query = $tracker_url . $scrape_query; |
|
||||||
|
|
||||||
return $request_query; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Executes the query and returns the result |
|
||||||
* |
|
||||||
* @throws \Exception If the connection can't be established. |
|
||||||
* @throws \Exception If the response isn't valid. |
|
||||||
* |
|
||||||
* @param string $query The query that will be executed. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
|
||||||
* @return string Request response. |
|
||||||
*/ |
|
||||||
private function http_request( $query, $host, $port ) { |
|
||||||
$context = stream_context_create( array( |
|
||||||
'http' => array( |
|
||||||
'timeout' => $this->timeout, |
|
||||||
), |
|
||||||
)); |
|
||||||
|
|
||||||
if ( false === ( $response = @file_get_contents( $query, false, $context ) ) ) { |
|
||||||
throw new \Exception( 'Invalid scrape connection (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
if ( substr( $response, 0, 12 ) !== 'd5:filesd20:' ) { |
|
||||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
return $response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Builds the query, sends the announce request and returns the data |
|
||||||
* |
|
||||||
* @throws \Exception If the connection can't be established. |
|
||||||
* |
|
||||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
|
||||||
* @param string $protocol Protocol to use for the scraping. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80 (HTTP) or 443 (HTTPS). |
|
||||||
* @param string $passkey Optional. Passkey provided in the scrape request. |
|
||||||
* @return string Request response. |
|
||||||
*/ |
|
||||||
private function http_announce( $infohashes, $protocol, $host, $port, $passkey ) { |
|
||||||
$tracker_url = $protocol . '://' . $host . ':' . $port . $passkey; |
|
||||||
$context = stream_context_create( array( |
|
||||||
'http' => array( |
|
||||||
'timeout' => $this->timeout, |
|
||||||
), |
|
||||||
)); |
|
||||||
|
|
||||||
$response_data = ''; |
|
||||||
foreach ( $infohashes as $infohash ) { |
|
||||||
$query = $tracker_url . '/announce?info_hash=' . urlencode( pack( 'H*', $infohash ) ); |
|
||||||
if ( false === ( $response = @file_get_contents( $query, false, $context ) ) ) { |
|
||||||
throw new \Exception( 'Invalid announce connection (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
if ( substr( $response, 0, 12 ) !== 'd8:completei' || |
|
||||||
substr( $response, 0, 46 ) === 'd8:completei0e10:downloadedi0e10:incompletei1e' ) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$ben_hash = '20:' . pack( 'H*', $infohash ) . 'd'; |
|
||||||
$response_data .= $ben_hash . $response; |
|
||||||
} |
|
||||||
|
|
||||||
return $response_data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Parses the response and returns the data |
|
||||||
* |
|
||||||
* @param string $response The response that will be parsed. |
|
||||||
* @param array $infohashes List of infohash(es). |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @return array Parsed data. |
|
||||||
*/ |
|
||||||
private function http_data( $response, $infohashes, $host ) { |
|
||||||
$torrents_data = array(); |
|
||||||
|
|
||||||
foreach ( $infohashes as $infohash ) { |
|
||||||
$ben_hash = '20:' . pack( 'H*', $infohash ) . 'd'; |
|
||||||
$start_pos = strpos( $response, $ben_hash ); |
|
||||||
if ( false !== $start_pos ) { |
|
||||||
$start = $start_pos + 24; |
|
||||||
$head = substr( $response, $start ); |
|
||||||
$end = strpos( $head, 'ee' ) + 1; |
|
||||||
$data = substr( $response, $start, $end ); |
|
||||||
|
|
||||||
$seeders = '8:completei'; |
|
||||||
$torrent_info['seeders'] = $this->get_information( $data, $seeders, 'e' ); |
|
||||||
|
|
||||||
$completed = '10:downloadedi'; |
|
||||||
$torrent_info['completed'] = $this->get_information( $data, $completed, 'e' ); |
|
||||||
|
|
||||||
$leechers = '10:incompletei'; |
|
||||||
$torrent_info['leechers'] = $this->get_information( $data, $leechers, 'e' ); |
|
||||||
|
|
||||||
$torrents_data[ $infohash ] = $torrent_info; |
|
||||||
} else { |
|
||||||
$this->collect_infohash( $infohash ); |
|
||||||
$this->errors[] = 'Invalid infohash (' . $infohash . ') for tracker: ' . $host . '.'; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return $torrents_data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Parses a string and returns the data between $start and $end. |
|
||||||
* |
|
||||||
* @param string $data The data that will be parsed. |
|
||||||
* @param string $start Beginning part of the data. |
|
||||||
* @param string $end Ending part of the data. |
|
||||||
* @return int Parsed information or 0. |
|
||||||
*/ |
|
||||||
private function get_information( $data, $start, $end ) { |
|
||||||
$start_pos = strpos( $data, $start ); |
|
||||||
if ( false !== $start_pos ) { |
|
||||||
$start = $start_pos + strlen( $start ); |
|
||||||
$head = substr( $data, $start ); |
|
||||||
$end = strpos( $head, $end ); |
|
||||||
$information = substr( $data, $start, $end ); |
|
||||||
|
|
||||||
return (int) $information; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Initiates the UDP scraping |
|
||||||
* |
|
||||||
* @param array|string $infohashes List (>1) or string of infohash(es). |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Optional. Port number of the tracker, Default 80. |
|
||||||
* @param bool $announce Optional. Use announce instead of scrape, Default false. |
|
||||||
* @return array List of results. |
|
||||||
*/ |
|
||||||
private function scrape_udp( $infohashes, $host, $port, $announce ) { |
|
||||||
list( $socket, $transaction_id, $connection_id ) = $this->prepare_udp( $host, $port ); |
|
||||||
|
|
||||||
if ( true === $announce ) { |
|
||||||
$response = $this->udp_announce( $socket, $infohashes, $connection_id ); |
|
||||||
$keys = 'Nleechers/Nseeders'; |
|
||||||
$start = 12; |
|
||||||
$end = 16; |
|
||||||
$offset = 20; |
|
||||||
} else { |
|
||||||
$response = $this->udp_scrape( $socket, $infohashes, $connection_id, $transaction_id, $host, $port ); |
|
||||||
$keys = 'Nseeders/Ncompleted/Nleechers'; |
|
||||||
$start = 8; |
|
||||||
$end = $offset = 12; |
|
||||||
} |
|
||||||
$results = $this->udp_scrape_data( $response, $infohashes, $host, $keys, $start, $end, $offset ); |
|
||||||
|
|
||||||
return $results; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Prepares the UDP connection |
|
||||||
* |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Optional. Port number of the tracker, Default 80. |
|
||||||
* @return array Created socket, transaction ID and connection ID. |
|
||||||
*/ |
|
||||||
private function prepare_udp( $host, $port ) { |
|
||||||
$socket = $this->udp_create_connection( $host, $port ); |
|
||||||
$transaction_id = $this->udp_connection_request( $socket ); |
|
||||||
$connection_id = $this->udp_connection_response( $socket, $transaction_id, $host, $port ); |
|
||||||
|
|
||||||
return array( $socket, $transaction_id, $connection_id ); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates the UDP socket and establishes the connection |
|
||||||
* |
|
||||||
* @throws \Exception If the socket couldn't be created or connected to. |
|
||||||
* |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80. |
|
||||||
* @return resource $socket Created and connected socket. |
|
||||||
*/ |
|
||||||
private function udp_create_connection( $host, $port ) { |
|
||||||
if ( false === ( $socket = @socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ) ) ) { |
|
||||||
throw new \Exception( "Couldn't create socket." ); |
|
||||||
} |
|
||||||
|
|
||||||
$timeout = $this->timeout; |
|
||||||
socket_set_option( $socket, SOL_SOCKET, SO_RCVTIMEO, array( 'sec' => $timeout, 'usec' => 0 ) ); |
|
||||||
socket_set_option( $socket, SOL_SOCKET, SO_SNDTIMEO, array( 'sec' => $timeout, 'usec' => 0 ) ); |
|
||||||
if ( false === @socket_connect( $socket, $host, $port ) ) { |
|
||||||
throw new \Exception( "Couldn't connect to socket." ); |
|
||||||
} |
|
||||||
|
|
||||||
return $socket; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes to the connected socket and returns the transaction ID |
|
||||||
* |
|
||||||
* @throws \Exception If the socket couldn't be written to. |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @return int The transaction ID. |
|
||||||
*/ |
|
||||||
private function udp_connection_request( $socket ) { |
|
||||||
$connection_id = "\x00\x00\x04\x17\x27\x10\x19\x80"; |
|
||||||
$action = pack( 'N', 0 ); |
|
||||||
$transaction_id = mt_rand( 0, 2147483647 ); |
|
||||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ); |
|
||||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( "Couldn't write to socket." ); |
|
||||||
} |
|
||||||
|
|
||||||
return $transaction_id; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads the connection response and returns the connection ID |
|
||||||
* |
|
||||||
* @throws \Exception If anything fails with the scraping. |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @param int $transaction_id The transaction ID. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80. |
|
||||||
* @return string The connection ID. |
|
||||||
*/ |
|
||||||
private function udp_connection_response( $socket, $transaction_id, $host, $port ) { |
|
||||||
if ( false === ( $response = @socket_read( $socket, 16 ) ) ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( 'Invalid scrape connection! (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
if ( strlen( $response ) < 16 ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
|
||||||
if ( 0 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( 'Invalid scrape result (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
$connection_id = substr( $response, 8, 8 ); |
|
||||||
|
|
||||||
return $connection_id; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads the socket response and returns the torrent data |
|
||||||
* |
|
||||||
* @throws \Exception If anything fails while reading the response. |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @param array $hashes List (>1) or string of infohash(es). |
|
||||||
* @param string $connection_id The connection ID. |
|
||||||
* @param int $transaction_id The transaction ID. |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param int $port Port number of the tracker, Default 80. |
|
||||||
* @return string Response data. |
|
||||||
*/ |
|
||||||
private function udp_scrape( $socket, $hashes, $connection_id, $transaction_id, $host, $port ) { |
|
||||||
$this->udp_scrape_request( $socket, $hashes, $connection_id, $transaction_id ); |
|
||||||
|
|
||||||
$read_length = 8 + ( 12 * count( $hashes ) ); |
|
||||||
if ( false === ( $response = @socket_read( $socket, $read_length ) ) ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( 'Invalid scrape connection (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
socket_close( $socket ); |
|
||||||
|
|
||||||
if ( strlen( $response ) < $read_length ) { |
|
||||||
throw new \Exception( 'Invalid scrape response (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
|
||||||
if ( 2 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
|
||||||
throw new \Exception( 'Invalid scrape result (' . $host . ':' . $port . ').' ); |
|
||||||
} |
|
||||||
|
|
||||||
return $response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes to the connected socket |
|
||||||
* |
|
||||||
* @throws \Exception If the socket couldn't be written to. |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @param array $hashes List (>1) or string of infohash(es). |
|
||||||
* @param string $connection_id The connection ID. |
|
||||||
* @param int $transaction_id The transaction ID. |
|
||||||
*/ |
|
||||||
private function udp_scrape_request( $socket, $hashes, $connection_id, $transaction_id ) { |
|
||||||
$action = pack( 'N', 2 ); |
|
||||||
|
|
||||||
$infohashes = ''; |
|
||||||
foreach ( $hashes as $infohash ) { |
|
||||||
$infohashes .= pack( 'H*', $infohash ); |
|
||||||
} |
|
||||||
|
|
||||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ) . $infohashes; |
|
||||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( "Couldn't write to socket." ); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Writes the announce to the connected socket |
|
||||||
* |
|
||||||
* @throws \Exception If the socket couldn't be written to. |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @param array $hashes List (>1) or string of infohash(es). |
|
||||||
* @param string $connection_id The connection ID. |
|
||||||
* @return string Torrent(s) data. |
|
||||||
*/ |
|
||||||
private function udp_announce( $socket, $hashes, $connection_id ) { |
|
||||||
$action = pack( 'N', 1 ); |
|
||||||
$downloaded = $left = $uploaded = "\x30\x30\x30\x30\x30\x30\x30\x30"; |
|
||||||
$peer_id = $this->random_peer_id(); |
|
||||||
$event = pack( 'N', 3 ); |
|
||||||
$ip_addr = pack( 'N', 0 ); |
|
||||||
$key = pack( 'N', mt_rand( 0, 2147483647 ) ); |
|
||||||
$num_want = -1; |
|
||||||
$ann_port = pack( 'N', mt_rand( 0, 255 ) ); |
|
||||||
|
|
||||||
$response_data = ''; |
|
||||||
foreach ( $hashes as $infohash ) { |
|
||||||
$transaction_id = mt_rand( 0, 2147483647 ); |
|
||||||
$buffer = $connection_id . $action . pack( 'N', $transaction_id ) . pack( 'H*', $infohash ) . |
|
||||||
$peer_id . $downloaded . $left . $uploaded . $event . $ip_addr . $key . $num_want . $ann_port; |
|
||||||
|
|
||||||
if ( false === @socket_write( $socket, $buffer, strlen( $buffer ) ) ) { |
|
||||||
socket_close( $socket ); |
|
||||||
throw new \Exception( "Couldn't write announce to socket." ); |
|
||||||
} |
|
||||||
|
|
||||||
$response = $this->udp_verify_announce( $socket, $transaction_id ); |
|
||||||
if ( false === $response ) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$response_data .= $response; |
|
||||||
} |
|
||||||
socket_close( $socket ); |
|
||||||
|
|
||||||
return $response_data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Generates a random peer ID |
|
||||||
* |
|
||||||
* @return string Generated peer ID. |
|
||||||
*/ |
|
||||||
private function random_peer_id() { |
|
||||||
$identifier = '-SP0054-'; |
|
||||||
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; |
|
||||||
$peer_id = $identifier . substr( str_shuffle( $chars ), 0, 12 ); |
|
||||||
|
|
||||||
return $peer_id; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Verifies the correctness of the announce response |
|
||||||
* |
|
||||||
* @param resource $socket The socket resource. |
|
||||||
* @param int $transaction_id The transaction ID. |
|
||||||
* @return string Response data. |
|
||||||
*/ |
|
||||||
private function udp_verify_announce( $socket, $transaction_id ) { |
|
||||||
if ( false === ( $response = @socket_read( $socket, 20 ) ) ) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if ( strlen( $response ) < 20 ) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
$result = unpack( 'Naction/Ntransaction_id', $response ); |
|
||||||
if ( 1 !== $result['action'] || $result['transaction_id'] !== $transaction_id ) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
return $response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Reads the socket response and returns the torrent data |
|
||||||
* |
|
||||||
* @param string $response Data from the request response. |
|
||||||
* @param array $hashes List (>1) or string of infohash(es). |
|
||||||
* @param string $host Domain or IP address of the tracker. |
|
||||||
* @param string $keys Keys for the unpacked information. |
|
||||||
* @param int $start Start of the content we want to unpack. |
|
||||||
* @param int $end End of the content we want to unpack. |
|
||||||
* @param int $offset Offset to the next content part. |
|
||||||
* @return array Scraped torrent data. |
|
||||||
*/ |
|
||||||
private function udp_scrape_data( $response, $hashes, $host, $keys, $start, $end, $offset ) { |
|
||||||
$torrents_data = array(); |
|
||||||
|
|
||||||
foreach ( $hashes as $infohash ) { |
|
||||||
$byte_string = substr( $response, $start, $end ); |
|
||||||
$data = unpack( 'N', $byte_string ); |
|
||||||
$content = $data[1]; |
|
||||||
if ( ! empty( $content ) ) { |
|
||||||
$results = unpack( $keys, $byte_string ); |
|
||||||
$torrents_data[ $infohash ] = $results; |
|
||||||
} else { |
|
||||||
$this->collect_infohash( $infohash ); |
|
||||||
$this->errors[] = 'Invalid infohash (' . $infohash . ') for tracker: ' . $host . '.'; |
|
||||||
} |
|
||||||
$start += $offset; |
|
||||||
} |
|
||||||
|
|
||||||
return $torrents_data; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Collects info-hashes that couldn't be scraped. |
|
||||||
* |
|
||||||
* @param string $infohash Infohash that wasn't scraped. |
|
||||||
*/ |
|
||||||
private function collect_infohash( $infohash ) { |
|
||||||
$this->infohashes[] = $infohash; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Checks if there are any errors |
|
||||||
* |
|
||||||
* @return bool True or false, depending if errors are present or not. |
|
||||||
*/ |
|
||||||
public function has_errors() { |
|
||||||
return ! empty( $this->errors ); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns all the errors that were logged |
|
||||||
* |
|
||||||
* @return array All the logged errors. |
|
||||||
*/ |
|
||||||
public function get_errors() { |
|
||||||
return $this->errors; |
|
||||||
} |
|
||||||
} |
|
@ -1,45 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
class Time |
|
||||||
{ |
|
||||||
public static function ago(int $time) |
|
||||||
{ |
|
||||||
$diff = time() - $time; |
|
||||||
|
|
||||||
if ($diff < 1) |
|
||||||
{ |
|
||||||
return _('now'); |
|
||||||
} |
|
||||||
|
|
||||||
$values = |
|
||||||
[ |
|
||||||
365 * 24 * 60 * 60 => _('year'), |
|
||||||
30 * 24 * 60 * 60 => _('month'), |
|
||||||
24 * 60 * 60 => _('day'), |
|
||||||
60 * 60 => _('hour'), |
|
||||||
60 => _('minute'), |
|
||||||
1 => _('second') |
|
||||||
]; |
|
||||||
|
|
||||||
$plural = [ |
|
||||||
_('year') => _('years'), |
|
||||||
_('month') => _('months'), |
|
||||||
_('day') => _('days'), |
|
||||||
_('hour') => _('hours'), |
|
||||||
_('minute') => _('minutes'), |
|
||||||
_('second') => _('seconds') |
|
||||||
]; |
|
||||||
|
|
||||||
foreach ($values as $key => $value) |
|
||||||
{ |
|
||||||
$result = $diff / $key; |
|
||||||
|
|
||||||
if ($result >= 1) |
|
||||||
{ |
|
||||||
$round = round($result); |
|
||||||
|
|
||||||
return sprintf('%s %s ago', $round, $round > 1 ? $plural[$value] : $value); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue