mirror of
https://github.com/YGGverse/YGGtracker.git
synced 2026-03-31 09:05:43 +00:00
init symfony framework #14
This commit is contained in:
parent
3c233fcfad
commit
380377b27c
114 changed files with 11525 additions and 10998 deletions
50
.env
Normal file
50
.env
Normal file
|
|
@ -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
|
||||||
6
.env.test
Normal file
6
.env.test
Normal file
|
|
@ -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
|
||||||
42
.gitignore
vendored
42
.gitignore
vendored
|
|
@ -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*
|
|
||||||
17
bin/console
Executable file
17
bin/console
Executable file
|
|
@ -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);
|
||||||
|
};
|
||||||
19
bin/phpunit
Executable file
19
bin/phpunit
Executable file
|
|
@ -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';
|
||||||
|
}
|
||||||
112
composer.json
112
composer.json
|
|
@ -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",
|
||||||
"require": {
|
|
||||||
"php": "^8.1",
|
|
||||||
"yggverse/cache": ">=0.3.0",
|
|
||||||
"yggverse/parser": ">=0.4.0",
|
|
||||||
"jdenticon/jdenticon": "^1.0",
|
|
||||||
"christeredvartsen/php-bittorrent": "^2.0"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"prefer-stable": true,
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"ext-ctype": "*",
|
||||||
|
"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",
|
||||||
|
"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
|
||||||
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Yggverse\\Yggtracker\\": "src/"
|
"App\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"authors": [
|
"autoload-dev": {
|
||||||
{
|
"psr-4": {
|
||||||
"name": "YGGverse"
|
"App\\Tests\\": "tests/"
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
"minimum-stability": "alpha"
|
"replace": {
|
||||||
|
"symfony/polyfill-ctype": "*",
|
||||||
|
"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.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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.*"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9748
composer.lock
generated
Normal file
9748
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
config/bundles.php
Normal file
14
config/bundles.php
Normal file
|
|
@ -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],
|
||||||
|
];
|
||||||
19
config/packages/cache.yaml
Normal file
19
config/packages/cache.yaml
Normal file
|
|
@ -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
|
||||||
5
config/packages/debug.yaml
Normal file
5
config/packages/debug.yaml
Normal file
|
|
@ -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)%"
|
||||||
48
config/packages/doctrine.yaml
Normal file
48
config/packages/doctrine.yaml
Normal file
|
|
@ -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
|
||||||
6
config/packages/doctrine_migrations.yaml
Normal file
6
config/packages/doctrine_migrations.yaml
Normal file
|
|
@ -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
|
||||||
25
config/packages/framework.yaml
Normal file
25
config/packages/framework.yaml
Normal file
|
|
@ -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
|
||||||
3
config/packages/mailer.yaml
Normal file
3
config/packages/mailer.yaml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
framework:
|
||||||
|
mailer:
|
||||||
|
dsn: '%env(MAILER_DSN)%'
|
||||||
24
config/packages/messenger.yaml
Normal file
24
config/packages/messenger.yaml
Normal file
|
|
@ -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
|
||||||
61
config/packages/monolog.yaml
Normal file
61
config/packages/monolog.yaml
Normal file
|
|
@ -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
|
||||||
13
config/packages/notifier.yaml
Normal file
13
config/packages/notifier.yaml
Normal file
|
|
@ -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 }
|
||||||
12
config/packages/routing.yaml
Normal file
12
config/packages/routing.yaml
Normal file
|
|
@ -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
|
||||||
39
config/packages/security.yaml
Normal file
39
config/packages/security.yaml
Normal file
|
|
@ -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
|
||||||
15
config/packages/translation.yaml
Normal file
15
config/packages/translation.yaml
Normal file
|
|
@ -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)%'
|
||||||
9
config/packages/twig.yaml
Normal file
9
config/packages/twig.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
twig:
|
||||||
|
default_path: '%kernel.project_dir%/templates'
|
||||||
|
globals:
|
||||||
|
version: '%app.version%'
|
||||||
|
name: '%app.name%'
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
twig:
|
||||||
|
strict_variables: true
|
||||||
13
config/packages/validator.yaml
Normal file
13
config/packages/validator.yaml
Normal file
|
|
@ -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
|
||||||
17
config/packages/web_profiler.yaml
Normal file
17
config/packages/web_profiler.yaml
Normal file
|
|
@ -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 }
|
||||||
5
config/preload.php
Normal file
5
config/preload.php
Normal file
|
|
@ -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';
|
||||||
|
}
|
||||||
5
config/routes.yaml
Normal file
5
config/routes.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
controllers:
|
||||||
|
resource:
|
||||||
|
path: ../src/Controller/
|
||||||
|
namespace: App\Controller
|
||||||
|
type: attribute
|
||||||
4
config/routes/framework.yaml
Normal file
4
config/routes/framework.yaml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
when@dev:
|
||||||
|
_errors:
|
||||||
|
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
|
||||||
|
prefix: /_error
|
||||||
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
|
|
@ -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
|
||||||
26
config/services.yaml
Normal file
26
config/services.yaml
Normal file
|
|
@ -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.
14
docker-compose.override.yml
Normal file
14
docker-compose.override.yml
Normal file
|
|
@ -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 ###
|
||||||
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
|
|
@ -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
|
|
||||||
}
|
|
||||||
38
phpunit.xml.dist
Normal file
38
phpunit.xml.dist
Normal file
|
|
@ -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>
|
||||||
|
|
@ -122,19 +122,19 @@ a.label-green:hover {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top--2 {
|
.top--2-px {
|
||||||
top: -2px;
|
top: -2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-2 {
|
.top-2-px {
|
||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.line-height-26 {
|
.line-height-26-px {
|
||||||
line-height: 26px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-radius-3 {
|
.border-radius-3-px {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,138 +227,138 @@ a:visited.background-color-hover-night-light:hover {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-4 {
|
.padding-4-px {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-t-4 {
|
.padding-t-4-px {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-y-4 {
|
.padding-y-4-px {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-x-4 {
|
.padding-x-4-px {
|
||||||
padding-left: 4px;
|
padding-left: 4px;
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-x-8 {
|
.padding-x-8-px {
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-y-6 {
|
.padding-y-6-px {
|
||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
padding-bottom: 6px;
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-8 {
|
.padding-8-px {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-t-8 {
|
.padding-t-8-px {
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-b-8 {
|
.padding-b-8-px {
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-y-8 {
|
.padding-y-8-px {
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-y-12 {
|
.padding-y-12-px {
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-b-16 {
|
.padding-b-16-px {
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-t-16 {
|
.padding-t-16-px {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-y-16 {
|
.padding-y-16-px {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-x-16 {
|
.padding-x-16-px {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.padding-16 {
|
.padding-16-px {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-l-4 {
|
.margin-l-4-px {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-l-8 {
|
.margin-l-8-px {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-l-16 {
|
.margin-l-16-px {
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-x-4 {
|
.margin-x-4-px {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-r-4 {
|
.margin-r-4-px {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-r-8 {
|
.margin-r-8-px {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-l-12 {
|
.margin-l-12-px {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-l--196 {
|
.margin-l--196-px {
|
||||||
margin-left: -196px;
|
margin-left: -196px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-y-8 {
|
.margin-y-8-px {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t-8 {
|
.margin-t-8-px {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-b-8 {
|
.margin-b-8-px {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-y-16 {
|
.margin-y-16-px {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-t-16 {
|
.margin-t-16-px {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-b-16 {
|
.margin-b-16-px {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.margin-b-24 {
|
.margin-b-24-px {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -410,10 +410,6 @@ a:visited.background-color-hover-night-light:hover {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.width-13px {
|
|
||||||
width: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.width-180-px {
|
.width-180-px {
|
||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
9
public/index.php
Normal file
9
public/index.php
Normal file
|
|
@ -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
src/Controller/.gitignore
vendored
Normal file
0
src/Controller/.gitignore
vendored
Normal file
20
src/Controller/HomeController.php
Normal file
20
src/Controller/HomeController.php
Normal file
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/Controller/PageController.php
Normal file
67
src/Controller/PageController.php
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/Controller/ProfileController.php
Normal file
76
src/Controller/ProfileController.php
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/Controller/SearchController.php
Normal file
31
src/Controller/SearchController.php
Normal file
|
|
@ -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
src/Entity/.gitignore
vendored
Normal file
0
src/Entity/.gitignore
vendored
Normal file
27
src/Entity/Page.php
Normal file
27
src/Entity/Page.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
147
src/Entity/User.php
Normal file
147
src/Entity/User.php
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/Kernel.php
Normal file
11
src/Kernel.php
Normal file
|
|
@ -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
src/Repository/.gitignore
vendored
Normal file
0
src/Repository/.gitignore
vendored
Normal file
48
src/Repository/PageRepository.php
Normal file
48
src/Repository/PageRepository.php
Normal file
|
|
@ -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()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
||||||
48
src/Repository/UserRepository.php
Normal file
48
src/Repository/UserRepository.php
Normal file
|
|
@ -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()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
||||||
12
src/Service/User.php
Normal file
12
src/Service/User.php
Normal file
|
|
@ -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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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,277 +0,0 @@
|
||||||
{
|
|
||||||
"af-ZA":
|
|
||||||
[
|
|
||||||
"Afrikaans",
|
|
||||||
"Afrikaans"
|
|
||||||
],
|
|
||||||
"ar":
|
|
||||||
[
|
|
||||||
"العربية",
|
|
||||||
"Arabic"
|
|
||||||
],
|
|
||||||
"bg-BG":
|
|
||||||
[
|
|
||||||
"Български",
|
|
||||||
"Bulgarian"
|
|
||||||
],
|
|
||||||
"ca-AD":
|
|
||||||
[
|
|
||||||
"Català",
|
|
||||||
"Catalan"
|
|
||||||
],
|
|
||||||
"cs-CZ":
|
|
||||||
[
|
|
||||||
"Čeština",
|
|
||||||
"Czech"
|
|
||||||
],
|
|
||||||
"cy-GB":
|
|
||||||
[
|
|
||||||
"Cymraeg",
|
|
||||||
"Welsh"
|
|
||||||
],
|
|
||||||
"da-DK":
|
|
||||||
[
|
|
||||||
"Dansk",
|
|
||||||
"Danish"
|
|
||||||
],
|
|
||||||
"de-AT":
|
|
||||||
[
|
|
||||||
"Deutsch (Österreich)",
|
|
||||||
"German (Austria)"
|
|
||||||
],
|
|
||||||
"de-CH":
|
|
||||||
[
|
|
||||||
"Deutsch (Schweiz)",
|
|
||||||
"German (Switzerland)"
|
|
||||||
],
|
|
||||||
"de-DE":
|
|
||||||
[
|
|
||||||
"Deutsch (Deutschland)",
|
|
||||||
"German (Germany)"
|
|
||||||
],
|
|
||||||
"el-GR":
|
|
||||||
[
|
|
||||||
"Ελληνικά",
|
|
||||||
"Greek"
|
|
||||||
],
|
|
||||||
"en-GB":
|
|
||||||
[
|
|
||||||
"English (UK)",
|
|
||||||
"English (UK)"
|
|
||||||
],
|
|
||||||
"en-US":
|
|
||||||
[
|
|
||||||
"English (US)",
|
|
||||||
"English (US)"
|
|
||||||
],
|
|
||||||
"es-CL":
|
|
||||||
[
|
|
||||||
"Español (Chile)",
|
|
||||||
"Spanish (Chile)"
|
|
||||||
],
|
|
||||||
"es-ES":
|
|
||||||
[
|
|
||||||
"Español (España)",
|
|
||||||
"Spanish (Spain)"
|
|
||||||
],
|
|
||||||
"es-MX":
|
|
||||||
[
|
|
||||||
"Español (México)",
|
|
||||||
"Spanish (Mexico)"
|
|
||||||
],
|
|
||||||
"et-EE":
|
|
||||||
[
|
|
||||||
"Eesti keel",
|
|
||||||
"Estonian"
|
|
||||||
],
|
|
||||||
"eu":
|
|
||||||
[
|
|
||||||
"Euskara",
|
|
||||||
"Basque"
|
|
||||||
],
|
|
||||||
"fa-IR":
|
|
||||||
[
|
|
||||||
"فارسی",
|
|
||||||
"Persian"
|
|
||||||
],
|
|
||||||
"fi-FI":
|
|
||||||
[
|
|
||||||
"Suomi",
|
|
||||||
"Finnish"
|
|
||||||
],
|
|
||||||
"fr-CA":
|
|
||||||
[
|
|
||||||
"Français (Canada)",
|
|
||||||
"French (Canada)"
|
|
||||||
],
|
|
||||||
"fr-FR":
|
|
||||||
[
|
|
||||||
"Français (France)",
|
|
||||||
"French (France)"
|
|
||||||
],
|
|
||||||
"gl-ES":
|
|
||||||
[
|
|
||||||
"Galego (Spain)",
|
|
||||||
"Galician (Spain)"
|
|
||||||
],
|
|
||||||
"he-IL":
|
|
||||||
[
|
|
||||||
"עברית",
|
|
||||||
"Hebrew"
|
|
||||||
],
|
|
||||||
"hi-IN":
|
|
||||||
[
|
|
||||||
"हिंदी",
|
|
||||||
"Hindi"
|
|
||||||
],
|
|
||||||
"hr-HR":
|
|
||||||
[
|
|
||||||
"Hrvatski",
|
|
||||||
"Croatian"
|
|
||||||
],
|
|
||||||
"hu-HU":
|
|
||||||
[
|
|
||||||
"Magyar",
|
|
||||||
"Hungarian"
|
|
||||||
],
|
|
||||||
"id-ID":
|
|
||||||
[
|
|
||||||
"Bahasa Indonesia",
|
|
||||||
"Indonesian"
|
|
||||||
],
|
|
||||||
"is-IS":
|
|
||||||
[
|
|
||||||
"Íslenska",
|
|
||||||
"Icelandic"
|
|
||||||
],
|
|
||||||
"it-IT":
|
|
||||||
[
|
|
||||||
"Italiano",
|
|
||||||
"Italian"
|
|
||||||
],
|
|
||||||
"ja-JP":
|
|
||||||
[
|
|
||||||
"日本語",
|
|
||||||
"Japanese"
|
|
||||||
],
|
|
||||||
"km-KH":
|
|
||||||
[
|
|
||||||
"ភាសាខ្មែរ",
|
|
||||||
"Khmer"
|
|
||||||
],
|
|
||||||
"ko-KR":
|
|
||||||
[
|
|
||||||
"한국어",
|
|
||||||
"Korean"
|
|
||||||
],
|
|
||||||
"la":
|
|
||||||
[
|
|
||||||
"Latina",
|
|
||||||
"Latin"
|
|
||||||
],
|
|
||||||
"lt-LT":
|
|
||||||
[
|
|
||||||
"Lietuvių kalba",
|
|
||||||
"Lithuanian"
|
|
||||||
],
|
|
||||||
"lv-LV":
|
|
||||||
[
|
|
||||||
"Latviešu",
|
|
||||||
"Latvian"
|
|
||||||
],
|
|
||||||
"mn-MN":
|
|
||||||
[
|
|
||||||
"Монгол",
|
|
||||||
"Mongolian"
|
|
||||||
],
|
|
||||||
"nb-NO":
|
|
||||||
[
|
|
||||||
"Norsk bokmål",
|
|
||||||
"Norwegian (Bokmål)"
|
|
||||||
],
|
|
||||||
"nl-NL":
|
|
||||||
[
|
|
||||||
"Nederlands",
|
|
||||||
"Dutch"
|
|
||||||
],
|
|
||||||
"nn-NO":
|
|
||||||
[
|
|
||||||
"Norsk nynorsk",
|
|
||||||
"Norwegian (Nynorsk)"
|
|
||||||
],
|
|
||||||
"pl-PL":
|
|
||||||
[
|
|
||||||
"Polski",
|
|
||||||
"Polish"
|
|
||||||
],
|
|
||||||
"pt-BR":
|
|
||||||
[
|
|
||||||
"Português (Brasil)",
|
|
||||||
"Portuguese (Brazil)"
|
|
||||||
],
|
|
||||||
"pt-PT":
|
|
||||||
[
|
|
||||||
"Português (Portugal)",
|
|
||||||
"Portuguese (Portugal)"
|
|
||||||
],
|
|
||||||
"ro-RO":
|
|
||||||
[
|
|
||||||
"Română",
|
|
||||||
"Romanian"
|
|
||||||
],
|
|
||||||
"ru-RU":
|
|
||||||
[
|
|
||||||
"Русский",
|
|
||||||
"Russian"
|
|
||||||
],
|
|
||||||
"sk-SK":
|
|
||||||
[
|
|
||||||
"Slovenčina",
|
|
||||||
"Slovak"
|
|
||||||
],
|
|
||||||
"sl-SI":
|
|
||||||
[
|
|
||||||
"Slovenščina",
|
|
||||||
"Slovenian"
|
|
||||||
],
|
|
||||||
"sr-RS":
|
|
||||||
[
|
|
||||||
"Српски / Srpski",
|
|
||||||
"Serbian"
|
|
||||||
],
|
|
||||||
"sv-SE":
|
|
||||||
[
|
|
||||||
"Svenska",
|
|
||||||
"Swedish"
|
|
||||||
],
|
|
||||||
"th-TH":
|
|
||||||
[
|
|
||||||
"ไทย",
|
|
||||||
"Thai"
|
|
||||||
],
|
|
||||||
"tr-TR":
|
|
||||||
[
|
|
||||||
"Türkçe",
|
|
||||||
"Turkish"
|
|
||||||
],
|
|
||||||
"uk-UA":
|
|
||||||
[
|
|
||||||
"Українська",
|
|
||||||
"Ukrainian"
|
|
||||||
],
|
|
||||||
"vi-VN":
|
|
||||||
[
|
|
||||||
"Tiếng Việt",
|
|
||||||
"Vietnamese"
|
|
||||||
],
|
|
||||||
"zh-CN":
|
|
||||||
[
|
|
||||||
"中文 (中国大陆)",
|
|
||||||
"Chinese (PRC)"
|
|
||||||
],
|
|
||||||
"zh-TW":
|
|
||||||
[
|
|
||||||
"中文 (台灣)",
|
|
||||||
"Chinese (Taiwan)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"port": 11211,
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"namespace": "",
|
|
||||||
"timeout": 3600
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[
|
|
||||||
""
|
|
||||||
]
|
|
||||||
|
|
@ -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,4 +0,0 @@
|
||||||
{
|
|
||||||
"port":9306,
|
|
||||||
"host":"127.0.0.1"
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
[
|
|
||||||
"default"
|
|
||||||
]
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -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…
Add table
Add a link
Reference in a new issue