diff --git a/.env b/.env new file mode 100644 index 0000000..a59ee2e --- /dev/null +++ b/.env @@ -0,0 +1,126 @@ +# 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 +APP_KEY=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 + +# Application version, used for API and media cache +APP_VERSION=2.6.2 + +# Application name +APP_NAME=YGGtracker + +# Default locale +APP_LOCALE=en + +# Supported locales for interface and content filters +APP_LOCALES=en|cs|nl|eo|fr|ja|ka|de|he|it|lv|pl|pt|ru|es|uk + +# Content categories, lowercase, enabled by default for new users +# src/Twig/AppExtension.php:transCategory +APP_CATEGORIES=movie|series|tv|animation|music|game|audiobook|podcast|book|archive|picture|software|other + +# Items per page on pagination +APP_PAGINATION=10 + +# Default application theme +APP_THEME=default + +# Additional themes, stored in /src/templates, /public/asset +APP_THEMES=default + +# Default sensitive status for new users +APP_SENSITIVE=1 + +# Default approved status for new users +APP_APPROVED=1 + +# Default Yggdrasil filters status for new users +APP_YGGDRASIL=1 + +# Default posters status for new users +APP_POSTERS=1 + +# Build-in trackers append to downloads +APP_TRACKERS=http://[201:23b4:991a:634d:8359:4521:5576:15b7]:2023/announce|http://[200:1e2f:e608:eb3a:2bf:1e62:87ba:e2f7]/announce|http://[316:c51a:62a3:8b9::5]/announce + +# List of crawlers where ignored in actions and activity features +APP_CRAWLERS=201:23b4:991a:634d:8359:4521:5576:15b7|30a:5fad::e|202:f2bc:f800:7cc4:c109:7857:5cae:6630|200:1554:e730:4030:605b:47be:6fb6:7b11 + +# Max torrent filesize for uploads (check upload_max_filesize in the php.ini) +APP_TORRENT_FILE_SIZE_MAX=1024000 + +# Max torrent poster filesize for uploads (check upload_max_filesize in the php.ini) +APP_TORRENT_POSTER_FILE_SIZE_MAX=10240000 + +# Store wanted torrent files in /app/var/ftp by /app/crontab/torrent/scrape/{key} +APP_TORRENT_WANTED_FTP_ENABLED=1 +APP_TORRENT_WANTED_FTP_FOLDER=/yggtracker +APP_TORRENT_WANTED_FTP_APPROVED_ONLY=1 + +# Enable search index for torrent name +APP_INDEX_TORRENT_NAME_ENABLED=1 + +# Enable search index for torrent info hash v1 +APP_INDEX_TORRENT_HASH_V1_ENABLED=1 + +# Enable search index for torrent info hash v2 +APP_INDEX_TORRENT_HASH_V2_ENABLED=1 + +# Enable search index for torrent filenames +APP_INDEX_TORRENT_FILENAMES_ENABLED=1 + +# Enable search index for torrent source +APP_INDEX_TORRENT_SOURCE_ENABLED=1 + +# Enable search index for torrent comment +APP_INDEX_TORRENT_COMMENT_ENABLED=1 + +# Enable search index for words length greater than N chars +APP_INDEX_WORD_LENGTH_MIN=3 + +# Enable search index for words length not greater than N chars +APP_INDEX_WORD_LENGTH_MAX=255 \ No newline at end of file diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..9e7162f --- /dev/null +++ b/.env.test @@ -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 diff --git a/.gitignore b/.gitignore index 9a6ca43..79ced3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,22 @@ -/.vscode/ +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/public/bundles/ +/var/ /vendor/ +###< symfony/framework-bundle ### -/database/yggtracker.mwb.bak +###> phpunit/phpunit ### +/phpunit.xml +.phpunit.result.cache +###< phpunit/phpunit ### -/src/public/api/*.json +###> symfony/phpunit-bridge ### +.phpunit.result.cache +/phpunit.xml +###< symfony/phpunit-bridge ### -/src/config/* -!/src/config/bootstrap.json -!/src/config/nodes.json -!/src/config/trackers.json -!/src/config/peers.json - -/src/public/sitemap.xml - -/src/storage/log/*.log - -/composer.lock - -*test* \ No newline at end of file +.vscode \ No newline at end of file diff --git a/README.md b/README.md index e8eefd7..6ab362a 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,76 @@ # YGGtracker -Distributed BitTorrent Registry for Yggdrasil +> [!NOTE] +> Take a look at [βtracker](https://github.com/yggverse/btracker) - the modern aggregation alternative written in Rust! -YGGtracker uses [Yggdrasil](https://github.com/yggdrasil-network/yggdrasil-go) IPv6 addresses to identify users without registration. +A social-oriented BitTorrent catalog for the [Yggdrasil](https://github.com/yggdrasil-network) network, written in the Symfony framework. -#### Nodes online +YGGtracker is a manually operated catalog and social network that allows users to share their torrents in the local network. Engine uses IPv6 `0200::/7` addresses to identify users without registration. -YGGtracker is distributed index engine, default nodes list defined in [nodes.json](https://github.com/YGGverse/YGGtracker/blob/main/src/config/nodes.json) +#### [Showcase](https://github.com/YGGverse/YGGtracker/wiki/Showcase) -If you have launched new one, feel free to participate by PR. +![Pasted image 1](https://github.com/YGGverse/YGGtracker/assets/108541346/962f7850-01e1-4add-9dbe-c11b80108a75) -#### Trackers - -Open trackers defined in [trackers.json](https://github.com/YGGverse/YGGtracker/blob/main/src/config/trackers.json) - -* Application appends initial trackers to all download links and magnet forms -* Trackers not in list will be cropped by the application filter -* Feel free to PR new yggdrasil tracker! - -#### Requirements - -``` -php8^ -php-pdo -php-mysql -php-curl -php-memcached -sphinxsearch -memcached -``` #### Installation +``` +symfony check:requirements +``` + ##### Production -* `composer create-project yggverse/yggtracker` +Install stable release + +``` +composer create-project yggverse/yggtracker +``` ##### Development -* `git clone https://github.com/YGGverse/YGGtracker.git` -* `cd YGGtracker` -* `composer update` +Latest codebase available in repository -#### Setup -* Server configuration `/example/environment` -* The web root dir is `/src/public` -* Deploy the database using [MySQL Workbench](https://www.mysql.com/products/workbench) project presented in the `/database` folder -* Install [Sphinx Search Server](https://sphinxsearch.com) -* Configuration examples presented at `/example/environment` folder. On first app launch, configuration file will be auto-generated in `/src/config` -* Make sure `/src/api` folder writable +``` +git clone https://github.com/YGGverse/YGGtracker.git +cd YGGtracker +composer update +symfony server:start +``` -#### Contribute +##### Database + +New installation + +``` +php bin/console doctrine:schema:update --force +``` + +Existing DB upgrade + +``` +php bin/console doctrine:migrations:migrate +``` + +##### Crontab + +* `* * * * * /crontab/torrent/scrape/{%app.key%}` - update seeding stats + +##### FTP + +Setup anonymous read-only access to `/var/ftp` catalog ([read more](https://github.com/YGGverse/YGGtracker/wiki/Features#the-wanted)) + +##### App settings + +Custom settings could be provided in the `/.env.local` file by overwriting default `/.env` values + +#### Localization + +[![Crowdin](https://badges.crowdin.net/yggtracker/localized.svg)](https://crowdin.com/project/yggtracker) + +#### API + +[Wiki reference](https://github.com/YGGverse/YGGtracker/wiki/API) + +#### Contribution Please make new branch for each PR @@ -58,129 +79,40 @@ git checkout main git checkout -b my-pr-branch-name ``` -#### Roadmap - -* [ ] BitTorrent protocol - + [ ] Protocol - + [ ] announce - + [ ] announce-list - + [ ] comment - + [ ] created by - + [ ] creation date - + [ ] info - + [ ] file-duration - + [ ] file-media - + [ ] files - + [ ] name - + [ ] piece length - + [ ] pieces - + [ ] private - + [ ] profiles - -* [ ] Magnet protocol - + [x] Exact Topic / xt - + [x] Display Name / dn - + [x] eXact Length / xl - + [x] Address Tracker / rt - + [x] Web Seed / ws - + [x] Acceptable Source / as - + [x] eXact Source / xs - + [x] Keyword Topic / kt - + [ ] Manifest Topic / mt - + [ ] Select Only / so - + [ ] PEer / x.pe - -* [ ] Catalog - + [x] Public levels - + [x] Sensitive filter - + [x] Comments - + [x] Scrape trackers - + [x] Peers - + [x] Completed - + [x] Leechers - + [x] Stars - + [x] Views - + [x] Downloads - + [x] Wanted - + [x] Threading comments - + [ ] Forks - -* [ ] Profile - + [ ] Listing - + [ ] Uploads - + [ ] Downloads - + [ ] Stars - + [ ] Following - + [ ] Followers - + [ ] Comments - + [ ] Settings - + [ ] Public name - + [ ] Downloads customization - + [ ] Address Tracker - + [ ] Web Seed - + [ ] Acceptable Source - + [ ] eXact Source - + [ ] Content filters - -* [x] API - + [x] Active (push) - + [x] Magnet - + [x] Edit - + [x] Download - + [x] Comment - + [x] Star - + [x] View - + [x] Passive (feed) - + [x] Manifest - + [x] Users - + [x] Magnets - + [x] Downloads - + [x] Comments - + [x] Stars - + [x] Views - -* [x] Export - + [x] Sitemap - + [x] RSS - + [x] Magnets - + [x] Comments - -* [x] Other - + [x] Moderation - + [x] UI - + [ ] CLI - + [ ] Installation tools - - -#### Donate to contributors - -* @d47081: - - + ![wakatime](https://wakatime.com/badge/user/0b7fe6c1-b091-4c98-b930-75cfee17c7a5/project/059ec567-2c65-4c65-a48e-51dcc366f1a0.svg) - + [BTC](https://www.blockchain.com/explorer/addresses/btc/bc1qngdf2kwty6djjqpk0ynkpq9wmlrmtm7e0c534y) | [LTC](https://live.blockcypher.com/ltc/address/LUSiqzKsfB1vBLvpu515DZktG9ioKqLyj7) | [XMR](835gSR1Uvka19gnWPkU2pyRozZugRZSPHDuFL6YajaAqjEtMwSPr4jafM8idRuBWo7AWD3pwFQSYRMRW9XezqrK4BEXBgXE) | [ZEPH](ZEPHsADHXqnhfWhXrRcXnyBQMucE3NM7Ng5ZVB99XwA38PTnbjLKpCwcQVgoie8EJuWozKgBiTmDFW4iY7fNEgSEWyAy4dotqtX) - + Support our server by order [Linux VPS](https://www.yourserver.se/portal/aff.php?aff=610) - + Inspiration by [SomaFM Deep Space One](https://somafm.com/deepspaceone/) - #### License * Engine sources [MIT License](https://github.com/YGGverse/YGGtracker/blob/main/LICENSE) +#### Versioning + +[Semantic Versioning 2.0.0](https://semver.org/#semantic-versioning-200) + #### Components +* [Symfony Framework](https://symfony.com) * [SVG icons](https://icons.getbootstrap.com) -* [PHP Scrapper](https://github.com/medariox/scrapeer) +* [Scrapper](https://github.com/medariox/scrapeer) / [Composer Edition](https://github.com/YGGverse/scrapeer) +* [Bencode](https://github.com/Rhilip/Bencode) +* [Transliteration](https://github.com/ashtokalo/php-translit) * [Identicons](https://github.com/dmester/jdenticon-php) -#### Feedback +#### Support -[https://github.com/YGGverse/YGGtracker/issues](https://github.com/YGGverse/YGGtracker/issues) +* [Issues](https://github.com/YGGverse/YGGtracker/issues) +* [Documentation](https://github.com/YGGverse/YGGtracker/wiki) +* [HowTo Yggdrasil](https://ygg.work.gd/yggdrasil:bittorrent:yggtracker) -#### Community +#### Blog * [Mastodon](https://mastodon.social/@YGGverse) +#### Integrations + +* [YGGtracker Search Plugin for qBittorrent](https://github.com/YGGverse/qbittorrent-yggtracker-search-plugin) +* [Crontab script that allows to receive wanted torrents from multiple YGGtracker nodes](https://github.com/YGGverse/yggtracker-wanted-torrents-receiver) + #### See also * [YGGo - YGGo! Distributed Web Search Engine ](https://github.com/YGGverse/YGGo) * [YGGwave ~ The Radio Catalog](https://github.com/YGGverse/YGGwave) -* [YGGstate - Yggdrasil Network Explorer](https://github.com/YGGverse/YGGstate) \ No newline at end of file +* [YGGstate - Yggdrasil Network Explorer](https://github.com/YGGverse/YGGstate) diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..c933dc5 --- /dev/null +++ b/bin/console @@ -0,0 +1,17 @@ +#!/usr/bin/env php +=0.3.0", - "yggverse/parser": ">=0.4.0", - "jdenticon/jdenticon": "^1.0", - "christeredvartsen/php-bittorrent": "^2.0" - }, "license": "MIT", + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "php": ">=8.1", + "ext-ctype": "*", + "ext-iconv": "*", + "ashtokalo/php-translit": "^0.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", + "league/commonmark": "^2.4", + "phpdocumentor/reflection-docblock": "^5.3", + "phpstan/phpdoc-parser": "^1.24", + "rhilip/bencode": "^2.3", + "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": "^3.7", + "twig/intl-extra": "^3.7", + "twig/markdown-extra": "^3.7", + "twig/string-extra": "^3.7", + "twig/twig": "^2.12|^3.0", + "yggverse/scrapeer": "^0.5.4" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, "autoload": { "psr-4": { - "Yggverse\\Yggtracker\\": "src/" + "App\\": "src/" } }, - "authors": [ - { - "name": "YGGverse" + "autoload-dev": { + "psr-4": { + "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.*" + } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..efca9a6 --- /dev/null +++ b/composer.lock @@ -0,0 +1,10456 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "99ad25219a6bff24126d6f83ebd1756a", + "packages": [ + { + "name": "ashtokalo/php-translit", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/ashtokalo/php-translit.git", + "reference": "8ced36cbcd0ed8befb1388ef51935eb53bcfe5b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ashtokalo/php-translit/zipball/8ced36cbcd0ed8befb1388ef51935eb53bcfe5b3", + "reference": "8ced36cbcd0ed8befb1388ef51935eb53bcfe5b3", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "~7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "ashtokalo\\translit\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP library to convert text from one script to another.", + "keywords": [ + "latinization", + "romanization", + "translit", + "transliteration" + ], + "support": { + "issues": "https://github.com/ashtokalo/php-translit/issues", + "source": "https://github.com/ashtokalo/php-translit/tree/0.2.0" + }, + "time": "2022-09-26T09:05:24+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "f41715465d65213d644d3141a6a93081be5d3549" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/f41715465d65213d644d3141a6a93081be5d3549", + "reference": "f41715465d65213d644d3141a6a93081be5d3549", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.2" + }, + "time": "2022-10-27T11:44:00+00:00" + }, + { + "name": "doctrine/annotations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" + }, + "time": "2023-02-02T22:02:53+00:00" + }, + { + "name": "doctrine/cache", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/1ca8f21980e770095a31456042471a57bc4c68fb", + "reference": "1ca8f21980e770095a31456042471a57bc4c68fb", + "shasum": "" + }, + "require": { + "php": "~7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/coding-standard": "^9", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psr/cache": "^1.0 || ^2.0 || ^3.0", + "symfony/cache": "^4.4 || ^5.4 || ^6", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.", + "homepage": "https://www.doctrine-project.org/projects/cache.html", + "keywords": [ + "abstraction", + "apcu", + "cache", + "caching", + "couchdb", + "memcached", + "php", + "redis", + "xcache" + ], + "support": { + "issues": "https://github.com/doctrine/cache/issues", + "source": "https://github.com/doctrine/cache/tree/2.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache", + "type": "tidelift" + } + ], + "time": "2022-05-20T20:07:39+00:00" + }, + { + "name": "doctrine/collections", + "version": "2.1.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "72328a11443a0de79967104ad36ba7b30bded134" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/72328a11443a0de79967104ad36ba7b30bded134", + "reference": "72328a11443a0de79967104ad36ba7b30bded134", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1", + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "ext-json": "*", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^5.11" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Collections\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.", + "homepage": "https://www.doctrine-project.org/projects/collections.html", + "keywords": [ + "array", + "collections", + "iterators", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/collections/issues", + "source": "https://github.com/doctrine/collections/tree/2.1.4" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcollections", + "type": "tidelift" + } + ], + "time": "2023-10-03T09:22:33+00:00" + }, + { + "name": "doctrine/common", + "version": "3.4.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/8b5e5650391f851ed58910b3e3d48a71062eeced", + "reference": "8b5e5650391f851ed58910b3e3d48a71062eeced", + "shasum": "" + }, + "require": { + "doctrine/persistence": "^2.0 || ^3.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9.0 || ^10.0", + "doctrine/collections": "^1", + "phpstan/phpstan": "^1.4.1", + "phpstan/phpstan-phpunit": "^1", + "phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0", + "squizlabs/php_codesniffer": "^3.0", + "symfony/phpunit-bridge": "^6.1", + "vimeo/psalm": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and much more.", + "homepage": "https://www.doctrine-project.org/projects/common.html", + "keywords": [ + "common", + "doctrine", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/common/issues", + "source": "https://github.com/doctrine/common/tree/3.4.3" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcommon", + "type": "tidelift" + } + ], + "time": "2022-10-09T11:47:59+00:00" + }, + { + "name": "doctrine/dbal", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/0ac3c270590e54910715e9a1a044cc368df282b2", + "reference": "0ac3c270590e54910715e9a1a044cc368df282b2", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/cache": "^1.11|^2.0", + "doctrine/deprecations": "^0.5.3|^1", + "doctrine/event-manager": "^1|^2", + "php": "^7.4 || ^8.0", + "psr/cache": "^1|^2|^3", + "psr/log": "^1|^2|^3" + }, + "require-dev": { + "doctrine/coding-standard": "12.0.0", + "fig/log-test": "^1", + "jetbrains/phpstorm-stubs": "2023.1", + "phpstan/phpstan": "1.10.42", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "9.6.13", + "psalm/plugin-phpunit": "0.18.4", + "slevomat/coding-standard": "8.13.1", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^5.4|^6.0", + "symfony/console": "^4.4|^5.4|^6.0", + "vimeo/psalm": "4.30.0" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\DBAL\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.", + "homepage": "https://www.doctrine-project.org/projects/dbal.html", + "keywords": [ + "abstraction", + "database", + "db2", + "dbal", + "mariadb", + "mssql", + "mysql", + "oci8", + "oracle", + "pdo", + "pgsql", + "postgresql", + "queryobject", + "sasql", + "sql", + "sqlite", + "sqlserver", + "sqlsrv" + ], + "support": { + "issues": "https://github.com/doctrine/dbal/issues", + "source": "https://github.com/doctrine/dbal/tree/3.7.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal", + "type": "tidelift" + } + ], + "time": "2023-11-19T08:06:58+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "2.11.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "4089f1424b724786c062aea50aae5f773449b94b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/4089f1424b724786c062aea50aae5f773449b94b", + "reference": "4089f1424b724786c062aea50aae5f773449b94b", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/dbal": "^3.7.0 || ^4.0", + "doctrine/persistence": "^2.2 || ^3", + "doctrine/sql-formatter": "^1.0.1", + "php": "^7.4 || ^8.0", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/doctrine-bridge": "^5.4.19 || ^6.0.7 || ^7.0", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/polyfill-php80": "^1.15", + "symfony/service-contracts": "^1.1.1 || ^2.0 || ^3" + }, + "conflict": { + "doctrine/annotations": ">=3.0", + "doctrine/orm": "<2.14 || >=4.0", + "twig/twig": "<1.34 || >=2.0 <2.4" + }, + "require-dev": { + "doctrine/annotations": "^1 || ^2", + "doctrine/coding-standard": "^12", + "doctrine/deprecations": "^1.0", + "doctrine/orm": "^2.14 || ^3.0", + "friendsofphp/proxy-manager-lts": "^1.0", + "phpunit/phpunit": "^9.5.26 || ^10.0", + "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-symfony": "^4", + "psr/log": "^1.1.4 || ^2.0 || ^3.0", + "symfony/phpunit-bridge": "^6.1 || ^7.0", + "symfony/property-info": "^5.4 || ^6.0 || ^7.0", + "symfony/proxy-manager-bridge": "^5.4 || ^6.0 || ^7.0", + "symfony/security-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/string": "^5.4 || ^6.0 || ^7.0", + "symfony/twig-bridge": "^5.4 || ^6.0 || ^7.0", + "symfony/validator": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^5.4 || ^6.2 || ^7.0", + "symfony/web-profiler-bundle": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0", + "twig/twig": "^1.34 || ^2.12 || ^3.0", + "vimeo/psalm": "^4.30" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "ext-pdo": "*", + "symfony/web-profiler-bundle": "To use the data collector." + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org/" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineBundle/issues", + "source": "https://github.com/doctrine/DoctrineBundle/tree/2.11.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-bundle", + "type": "tidelift" + } + ], + "time": "2023-11-15T20:01:50+00:00" + }, + { + "name": "doctrine/doctrine-migrations-bundle", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1dd42906a5fb9c5960723e2ebb45c68006493835", + "reference": "1dd42906a5fb9c5960723e2ebb45c68006493835", + "shasum": "" + }, + "require": { + "doctrine/doctrine-bundle": "^2.4", + "doctrine/migrations": "^3.2", + "php": "^7.2|^8.0", + "symfony/deprecation-contracts": "^2.1 || ^3", + "symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.6 || ^3", + "doctrine/persistence": "^2.0 || ^3 ", + "phpstan/phpstan": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^8.5|^9.5", + "psalm/plugin-phpunit": "^0.18.4", + "psalm/plugin-symfony": "^3 || ^5", + "symfony/phpunit-bridge": "^6.3 || ^7", + "symfony/var-exporter": "^5.4 || ^6 || ^7", + "vimeo/psalm": "^4.30 || ^5.15" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\MigrationsBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "https://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineMigrationsBundle", + "homepage": "https://www.doctrine-project.org", + "keywords": [ + "dbal", + "migrations", + "schema" + ], + "support": { + "issues": "https://github.com/doctrine/DoctrineMigrationsBundle/issues", + "source": "https://github.com/doctrine/DoctrineMigrationsBundle/tree/3.3.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdoctrine-migrations-bundle", + "type": "tidelift" + } + ], + "time": "2023-11-13T19:44:41+00:00" + }, + { + "name": "doctrine/event-manager", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/750671534e0241a7c50ea5b43f67e23eb5c96f32", + "reference": "750671534e0241a7c50ea5b43f67e23eb5c96f32", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.8", + "phpunit/phpunit": "^9.5", + "vimeo/psalm": "^4.28" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2022-10-12T20:59:15+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.8" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2023-06-16T13:40:37+00:00" + }, + { + "name": "doctrine/instantiator", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^11", + "ext-pdo": "*", + "ext-phar": "*", + "phpbench/phpbench": "^1.2", + "phpstan/phpstan": "^1.9.4", + "phpstan/phpstan-phpunit": "^1.3", + "phpunit/phpunit": "^9.5.27", + "vimeo/psalm": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "https://ocramius.github.io/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://www.doctrine-project.org/projects/instantiator.html", + "keywords": [ + "constructor", + "instantiate" + ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/2.0.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", + "type": "tidelift" + } + ], + "time": "2022-12-30T00:23:10+00:00" + }, + { + "name": "doctrine/lexer", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^10", + "phpstan/phpstan": "^1.3", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^4.11 || ^5.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2022-12-14T08:49:07+00:00" + }, + { + "name": "doctrine/migrations", + "version": "3.7.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/migrations.git", + "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/47af29eef49f29ebee545947e8b2a4b3be318c8a", + "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/dbal": "^3.5.1 || ^4", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2.0", + "php": "^8.1", + "psr/log": "^1.1.3 || ^2 || ^3", + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0", + "symfony/var-exporter": "^6.2 || ^7.0" + }, + "conflict": { + "doctrine/orm": "<2.12 || >=4" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "doctrine/orm": "^2.13 || ^3", + "doctrine/persistence": "^2 || ^3", + "doctrine/sql-formatter": "^1.0", + "ext-pdo_sqlite": "*", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-deprecation-rules": "^1.1", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.4", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^10.3", + "symfony/cache": "^5.4 || ^6.0 || ^7.0", + "symfony/process": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "suggest": { + "doctrine/sql-formatter": "Allows to generate formatted SQL with the diff command.", + "symfony/yaml": "Allows the use of yaml for migration configuration files." + }, + "bin": [ + "bin/doctrine-migrations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Migrations\\": "lib/Doctrine/Migrations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Michael Simonson", + "email": "contact@mikesimonson.com" + } + ], + "description": "PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying changes to it. It is a very easy to use and a powerful tool.", + "homepage": "https://www.doctrine-project.org/projects/migrations.html", + "keywords": [ + "database", + "dbal", + "migrations" + ], + "support": { + "issues": "https://github.com/doctrine/migrations/issues", + "source": "https://github.com/doctrine/migrations/tree/3.7.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fmigrations", + "type": "tidelift" + } + ], + "time": "2023-12-05T11:35:05+00:00" + }, + { + "name": "doctrine/orm", + "version": "2.17.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/orm.git", + "reference": "1a4fe6e0bb67762370937a7e6cee3da40a9122d1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/orm/zipball/1a4fe6e0bb67762370937a7e6cee3da40a9122d1", + "reference": "1a4fe6e0bb67762370937a7e6cee3da40a9122d1", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2", + "doctrine/cache": "^1.12.1 || ^2.1.1", + "doctrine/collections": "^1.5 || ^2.1", + "doctrine/common": "^3.0.3", + "doctrine/dbal": "^2.13.1 || ^3.2", + "doctrine/deprecations": "^0.5.3 || ^1", + "doctrine/event-manager": "^1.2 || ^2", + "doctrine/inflector": "^1.4 || ^2.0", + "doctrine/instantiator": "^1.3 || ^2", + "doctrine/lexer": "^2", + "doctrine/persistence": "^2.4 || ^3", + "ext-ctype": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3", + "symfony/console": "^4.2 || ^5.0 || ^6.0 || ^7.0", + "symfony/polyfill-php72": "^1.23", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "doctrine/annotations": "<1.13 || >= 3.0" + }, + "require-dev": { + "doctrine/annotations": "^1.13 || ^2", + "doctrine/coding-standard": "^9.0.2 || ^12.0", + "phpbench/phpbench": "^0.16.10 || ^1.0", + "phpstan/phpstan": "~1.4.10 || 1.10.35", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", + "psr/log": "^1 || ^2 || ^3", + "squizlabs/php_codesniffer": "3.7.2", + "symfony/cache": "^4.4 || ^5.4 || ^6.0", + "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2", + "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0", + "vimeo/psalm": "4.30.0 || 5.15.0" + }, + "suggest": { + "ext-dom": "Provides support for XSD validation for XML mapping files", + "symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0", + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "bin": [ + "bin/doctrine" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\ORM\\": "lib/Doctrine/ORM" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "https://www.doctrine-project.org/projects/orm.html", + "keywords": [ + "database", + "orm" + ], + "support": { + "issues": "https://github.com/doctrine/orm/issues", + "source": "https://github.com/doctrine/orm/tree/2.17.1" + }, + "time": "2023-11-17T06:25:40+00:00" + }, + { + "name": "doctrine/persistence", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/persistence.git", + "reference": "63fee8c33bef740db6730eb2a750cd3da6495603" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603", + "reference": "63fee8c33bef740db6730eb2a750cd3da6495603", + "shasum": "" + }, + "require": { + "doctrine/event-manager": "^1 || ^2", + "php": "^7.2 || ^8.0", + "psr/cache": "^1.0 || ^2.0 || ^3.0" + }, + "conflict": { + "doctrine/common": "<2.10" + }, + "require-dev": { + "composer/package-versions-deprecated": "^1.11", + "doctrine/coding-standard": "^11", + "doctrine/common": "^3.0", + "phpstan/phpstan": "1.9.4", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.5", + "symfony/cache": "^4.4 || ^5.4 || ^6.0", + "vimeo/psalm": "4.30.0 || 5.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Persistence\\": "src/Persistence" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.", + "homepage": "https://www.doctrine-project.org/projects/persistence.html", + "keywords": [ + "mapper", + "object", + "odm", + "orm", + "persistence" + ], + "support": { + "issues": "https://github.com/doctrine/persistence/issues", + "source": "https://github.com/doctrine/persistence/tree/3.2.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fpersistence", + "type": "tidelift" + } + ], + "time": "2023-05-17T18:32:04+00:00" + }, + { + "name": "doctrine/sql-formatter", + "version": "1.1.3", + "source": { + "type": "git", + "url": "https://github.com/doctrine/sql-formatter.git", + "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5", + "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4" + }, + "bin": [ + "bin/sql-formatter" + ], + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\SqlFormatter\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "https://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/doctrine/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "support": { + "issues": "https://github.com/doctrine/sql-formatter/issues", + "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3" + }, + "time": "2022-05-23T21:33:49+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", + "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2023-10-06T06:47:41+00:00" + }, + { + "name": "jdenticon/jdenticon", + "version": "1.0.2", + "source": { + "type": "git", + "url": "https://github.com/dmester/jdenticon-php.git", + "reference": "cabb7a44c413c318392a341c5d3ca30fcdd57a6f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dmester/jdenticon-php/zipball/cabb7a44c413c318392a341c5d3ca30fcdd57a6f", + "reference": "cabb7a44c413c318392a341c5d3ca30fcdd57a6f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jdenticon\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Mester Pirttijärvi" + } + ], + "description": "Render PNG and SVG identicons.", + "homepage": "https://jdenticon.com/", + "keywords": [ + "avatar", + "identicon", + "jdenticon" + ], + "support": { + "docs": "https://jdenticon.com/php-api.html", + "issues": "https://github.com/dmester/jdenticon-php/issues", + "source": "https://github.com/dmester/jdenticon-php" + }, + "time": "2022-10-30T17:15:02+00:00" + }, + { + "name": "league/commonmark", + "version": "2.4.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "reference": "3669d6d5f7a47a93c08ddff335e6d945481a1dd5", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.30.0", + "commonmark/commonmark.js": "0.30.0", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2023-08-30T16:55:00+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.5.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "phpstan/phpstan": "^1.9", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-strict-rules": "^1.4", + "phpunit/phpunit": "^10.1", + "predis/predis": "^1.1 || ^2", + "ruflin/elastica": "^7", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.5.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2023-10-27T15:32:31+00:00" + }, + { + "name": "nette/schema", + "version": "v1.2.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/0462f0166e823aad657c9224d0f849ecac1ba10a", + "reference": "0462f0166e823aad657c9224d0f849ecac1ba10a", + "shasum": "" + }, + "require": { + "nette/utils": "^2.5.7 || ^3.1.5 || ^4.0", + "php": "7.1 - 8.3" + }, + "require-dev": { + "nette/tester": "^2.3 || ^2.4", + "phpstan/phpstan-nette": "^1.0", + "tracy/tracy": "^2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.2.5" + }, + "time": "2023-10-05T20:37:59+00:00" + }, + { + "name": "nette/utils", + "version": "v4.0.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/a9d127dd6a203ce6d255b2e2db49759f7506e015", + "reference": "a9d127dd6a203ce6d255b2e2db49759f7506e015", + "shasum": "" + }, + "require": { + "php": ">=8.0 <8.4" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "dev-master", + "nette/tester": "^2.5", + "phpstan/phpstan": "^1.0", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.0.3" + }, + "time": "2023-10-29T21:02:13+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.4", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6bd0c26f3786cd9b7c359675cb789e35a8e07496", + "reference": "6bd0c26f3786cd9b7c359675cb789e35a8e07496", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.4" + }, + "time": "2023-11-26T18:29:22+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/link", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/link.git", + "reference": "84b159194ecfd7eaa472280213976e96415433f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/link/zipball/84b159194ecfd7eaa472280213976e96415433f7", + "reference": "84b159194ecfd7eaa472280213976e96415433f7", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "suggest": { + "fig/link-util": "Provides some useful PSR-13 utilities" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Link\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for HTTP links", + "homepage": "https://github.com/php-fig/link", + "keywords": [ + "http", + "http-link", + "link", + "psr", + "psr-13", + "rest" + ], + "support": { + "source": "https://github.com/php-fig/link/tree/2.0.1" + }, + "time": "2021-03-11T23:00:27+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "rhilip/bencode", + "version": "v2.4.2", + "source": { + "type": "git", + "url": "https://github.com/Rhilip/Bencode.git", + "reference": "d5b51d02f73017834c53aa76aff2403fb753ac97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Rhilip/Bencode/zipball/d5b51d02f73017834c53aa76aff2403fb753ac97", + "reference": "d5b51d02f73017834c53aa76aff2403fb753ac97", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "ext-json": "*", + "phpunit/phpunit": "^9.0" + }, + "suggest": { + "php-64bit": "Running 64 bit is recommended to prevent integer overflow" + }, + "type": "library", + "autoload": { + "psr-4": { + "Rhilip\\Bencode\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rhilip", + "email": "rhilipruan@gmail.com" + } + ], + "description": "A pure and simple PHP library for encoding and decoding Bencode data", + "keywords": [ + "bencode", + "bittorrent", + "torrent" + ], + "support": { + "issues": "https://github.com/Rhilip/Bencode/issues", + "source": "https://github.com/Rhilip/Bencode/tree/v2.4.2" + }, + "time": "2023-11-05T11:24:41+00:00" + }, + { + "name": "symfony/asset", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "b2382a403f2111836301623d89e9af3d84989525" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/b2382a403f2111836301623d89e9af3d84989525", + "reference": "b2382a403f2111836301623d89e9af3d84989525", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/http-foundation": "<5.4" + }, + "require-dev": { + "symfony/http-client": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/asset/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, + { + "name": "symfony/cache", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache.git", + "reference": "8c6e84272e4febbb1fed3c5b9f3c722537c2bd55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache/zipball/8c6e84272e4febbb1fed3c5b9f3c722537c2bd55", + "reference": "8c6e84272e4febbb1fed3c5b9f3c722537c2bd55", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.3.6" + }, + "conflict": { + "doctrine/dbal": "<2.13.1", + "symfony/dependency-injection": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/var-dumper": "<5.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", + "homepage": "https://symfony.com", + "keywords": [ + "caching", + "psr6" + ], + "support": { + "source": "https://github.com/symfony/cache/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-24T13:24:35+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/cache-contracts.git", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/1d74b127da04ffa87aa940abe15446fa89653778", + "reference": "1d74b127da04ffa87aa940abe15446fa89653778", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-25T12:52:38+00:00" + }, + { + "name": "symfony/clock", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "a74086d3db70d0f06ffd84480daa556248706e98" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/a74086d3db70d0f06ffd84480daa556248706e98", + "reference": "a74086d3db70d0f06ffd84480daa556248706e98", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-31T11:35:03+00:00" + }, + { + "name": "symfony/config", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/config.git", + "reference": "b7a63887960359e5b59b15826fa9f9be10acbe88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/config/zipball/b7a63887960359e5b59b15826fa9f9be10acbe88", + "reference": "b7a63887960359e5b59b15826fa9f9be10acbe88", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/finder": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "require-dev": { + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Config\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-09T08:28:21+00:00" + }, + { + "name": "symfony/console", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "0566dbd051f8648d980592c7849f5d90d2c7c60c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/0566dbd051f8648d980592c7849f5d90d2c7c60c", + "reference": "0566dbd051f8648d980592c7849f5d90d2c7c60c", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-20T16:36:29+00:00" + }, + { + "name": "symfony/crowdin-translation-provider", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/crowdin-translation-provider.git", + "reference": "dea8d5a664780e9c36efc075fa758d435e4ef3a7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/crowdin-translation-provider/zipball/dea8d5a664780e9c36efc075fa758d435e4ef3a7", + "reference": "dea8d5a664780e9c36efc075fa758d435e4ef3a7", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/config": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation": "^5.4.21|^6.2.7" + }, + "type": "symfony-translation-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Translation\\Bridge\\Crowdin\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andrii Bodnar", + "homepage": "https://github.com/andrii-bodnar" + }, + { + "name": "Mathieu Santostefano", + "homepage": "https://github.com/welcomattic" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Crowdin Translation Provider Bridge", + "homepage": "https://symfony.com", + "keywords": [ + "crowdin", + "provider", + "translation" + ], + "support": { + "source": "https://github.com/symfony/crowdin-translation-provider/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-20T13:15:31+00:00" + }, + { + "name": "symfony/dependency-injection", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/dependency-injection.git", + "reference": "51383a1d9d7e93d5c3c76ddc32672de1b3e82c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/51383a1d9d7e93d5c3c76ddc32672de1b3e82c77", + "reference": "51383a1d9d7e93d5c3c76ddc32672de1b3e82c77", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^6.2.10" + }, + "conflict": { + "ext-psr": "<1.1|>=2", + "symfony/config": "<6.1", + "symfony/finder": "<5.4", + "symfony/proxy-manager-bridge": "<6.3", + "symfony/yaml": "<5.4" + }, + "provide": { + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "symfony/config": "^6.1", + "symfony/expression-language": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DependencyInjection\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows you to standardize and centralize the way objects are constructed in your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T14:25:58+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/doctrine-bridge", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/doctrine-bridge.git", + "reference": "8842d289d41320a0f725e996b4e58d84af398a9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/8842d289d41320a0f725e996b4e58d84af398a9e", + "reference": "8842d289d41320a0f725e996b4e58d84af398a9e", + "shasum": "" + }, + "require": { + "doctrine/event-manager": "^1.2|^2", + "doctrine/persistence": "^2|^3", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.13.1", + "doctrine/dbal": "<2.13.1", + "doctrine/lexer": "<1.1", + "doctrine/orm": "<2.12", + "symfony/cache": "<5.4", + "symfony/dependency-injection": "<6.2", + "symfony/form": "<5.4.21|>=6,<6.2.7", + "symfony/http-foundation": "<6.3", + "symfony/http-kernel": "<6.2", + "symfony/lock": "<6.3", + "symfony/messenger": "<5.4", + "symfony/property-info": "<5.4", + "symfony/security-bundle": "<5.4", + "symfony/security-core": "<6.0", + "symfony/validator": "<5.4.25|>=6,<6.2.12|>=6.3,<6.3.1" + }, + "require-dev": { + "doctrine/annotations": "^1.13.1|^2", + "doctrine/collections": "^1.0|^2.0", + "doctrine/data-fixtures": "^1.1", + "doctrine/dbal": "^2.13.1|^3|^4", + "doctrine/orm": "^2.12|^3", + "psr/log": "^1|^2|^3", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^6.2", + "symfony/doctrine-messenger": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/form": "^5.4.21|^6.2.7", + "symfony/http-kernel": "^6.3", + "symfony/lock": "^6.3", + "symfony/messenger": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/proxy-manager-bridge": "^5.4|^6.0", + "symfony/security-core": "^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^5.4.25|~6.2.12|^6.3.1", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Doctrine with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, + { + "name": "symfony/doctrine-messenger", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/doctrine-messenger.git", + "reference": "b225f860450a34291bc5983f87d57fb7b9ef18b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/b225f860450a34291bc5983f87d57fb7b9ef18b8", + "reference": "b225f860450a34291bc5983f87d57fb7b9ef18b8", + "shasum": "" + }, + "require": { + "doctrine/dbal": "^2.13|^3|^4", + "php": ">=8.1", + "symfony/messenger": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/persistence": "<1.3" + }, + "require-dev": { + "doctrine/persistence": "^1.3|^2|^3", + "symfony/property-access": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "type": "symfony-messenger-bridge", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\Bridge\\Doctrine\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Doctrine Messenger Bridge", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/doctrine-messenger/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T09:24:52+00:00" + }, + { + "name": "symfony/dotenv", + "version": "v6.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/dotenv.git", + "reference": "7dfbe2976f3c1b7cfa8fac2212a050bfa9bd7d9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/7dfbe2976f3c1b7cfa8fac2212a050bfa9bd7d9e", + "reference": "7dfbe2976f3c1b7cfa8fac2212a050bfa9bd7d9e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/console": "<5.4", + "symfony/process": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Dotenv\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Registers environment variables from a .env file", + "homepage": "https://symfony.com", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "source": "https://github.com/symfony/dotenv/tree/v6.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-26T18:15:14+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "1f69476b64fb47105c06beef757766c376b548c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/1f69476b64fb47105c06beef757766c376b548c4", + "reference": "1f69476b64fb47105c06beef757766c376b548c4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/var-dumper": "^5.4|^6.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-12T06:57:20+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-06T06:56:43+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/expression-language", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/expression-language.git", + "reference": "6d560c4c80e7e328708efd923f93ad67e6a0c1c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/expression-language/zipball/6d560c4c80e7e328708efd923f93ad67e6a0c1c0", + "reference": "6d560c4c80e7e328708efd923f93ad67e6a0c1c0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ExpressionLanguage\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an engine that can compile and evaluate expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/expression-language/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-28T16:05:33+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-26T12:56:25+00:00" + }, + { + "name": "symfony/flex", + "version": "v2.4.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/flex.git", + "reference": "67ee785f1aedada76461de7a7ec10cd7f8ff8d36" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/flex/zipball/67ee785f1aedada76461de7a7ec10cd7f8ff8d36", + "reference": "67ee785f1aedada76461de7a7ec10cd7f8ff8d36", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.1", + "php": ">=8.0" + }, + "require-dev": { + "composer/composer": "^2.1", + "symfony/dotenv": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/phpunit-bridge": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Symfony\\Flex\\Flex" + }, + "autoload": { + "psr-4": { + "Symfony\\Flex\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien.potencier@gmail.com" + } + ], + "description": "Composer plugin for Symfony", + "support": { + "issues": "https://github.com/symfony/flex/issues", + "source": "https://github.com/symfony/flex/tree/v2.4.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-05T14:09:35+00:00" + }, + { + "name": "symfony/form", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/form.git", + "reference": "5afc7334b9d60dd0799612faf3d103b25bc60ae4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/form/zipball/5afc7334b9d60dd0799612faf3d103b25bc60ae4", + "reference": "5afc7334b9d60dd0799612faf3d103b25bc60ae4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/options-resolver": "^5.4|^6.0", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/doctrine-bridge": "<5.4.21|>=6,<6.2.7", + "symfony/error-handler": "<5.4", + "symfony/framework-bundle": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<6.3" + }, + "require-dev": { + "doctrine/collections": "^1.0|^2.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/html-sanitizer": "^6.1", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/security-core": "^6.2", + "symfony/security-csrf": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Form\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows to easily create, process and reuse HTML forms", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/form/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-30T11:08:22+00:00" + }, + { + "name": "symfony/framework-bundle", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/framework-bundle.git", + "reference": "f83d20092e98c3ae8b5874b8f0787546c5c61cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/f83d20092e98c3ae8b5874b8f0787546c5c61cda", + "reference": "f83d20092e98c3ae8b5874b8f0787546c5c61cda", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.1", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^6.1", + "symfony/dependency-injection": "^6.3.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.1", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-foundation": "^6.3", + "symfony/http-kernel": "^6.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/routing": "^5.4|^6.0" + }, + "conflict": { + "doctrine/annotations": "<1.13.1", + "doctrine/persistence": "<1.3", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/asset": "<5.4", + "symfony/clock": "<6.3", + "symfony/console": "<5.4", + "symfony/dom-crawler": "<6.3", + "symfony/dotenv": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<6.3", + "symfony/lock": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<6.3", + "symfony/mime": "<6.2", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4", + "symfony/security-core": "<5.4", + "symfony/security-csrf": "<5.4", + "symfony/serializer": "<6.3", + "symfony/stopwatch": "<5.4", + "symfony/translation": "<6.2.8", + "symfony/twig-bridge": "<5.4", + "symfony/twig-bundle": "<5.4", + "symfony/validator": "<6.3", + "symfony/web-profiler-bundle": "<5.4", + "symfony/workflow": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.13.1|^2", + "doctrine/persistence": "^1.3|^2|^3", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^5.4|^6.0", + "symfony/asset-mapper": "^6.3", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/clock": "^6.2", + "symfony/console": "^5.4.9|^6.0.9", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dom-crawler": "^6.3", + "symfony/dotenv": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/html-sanitizer": "^6.1", + "symfony/http-client": "^6.3", + "symfony/lock": "^5.4|^6.0", + "symfony/mailer": "^5.4|^6.0", + "symfony/messenger": "^6.3", + "symfony/mime": "^6.2", + "symfony/notifier": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/process": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/rate-limiter": "^5.4|^6.0", + "symfony/scheduler": "^6.3", + "symfony/security-bundle": "^5.4|^6.0", + "symfony/semaphore": "^5.4|^6.0", + "symfony/serializer": "^6.3", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/string": "^5.4|^6.0", + "symfony/translation": "^6.2.8", + "symfony/twig-bundle": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^6.3", + "symfony/web-link": "^5.4|^6.0", + "symfony/workflow": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0", + "twig/twig": "^2.10|^3.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\FrameworkBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/framework-bundle/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-24T10:25:33+00:00" + }, + { + "name": "symfony/http-client", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "0314e2d49939a9831929d6fc81c01c6df137fd0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/0314e2d49939a9831929d6fc81c01c6df137fd0a", + "reference": "0314e2d49939a9831929d6fc81c01c6df137fd0a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^3", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "php-http/discovery": "<1.15", + "symfony/http-foundation": "<6.3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "keywords": [ + "http" + ], + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-06T18:31:59+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "1ee70e699b41909c209a0c930f11034b93578654" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/1ee70e699b41909c209a0c930f11034b93578654", + "reference": "1ee70e699b41909c209a0c930f11034b93578654", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-30T20:28:31+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "49a04fd3a21edc9ce503ab78e9f342805fefe780" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/49a04fd3a21edc9ce503ab78e9f342805fefe780", + "reference": "49a04fd3a21edc9ce503ab78e9f342805fefe780", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.1", + "symfony/polyfill-php83": "^1.27" + }, + "conflict": { + "symfony/cache": "<6.3" + }, + "require-dev": { + "doctrine/dbal": "^2.13.1|^3|^4", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^6.3", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-kernel": "^5.4.12|^6.0.12|^6.1.4", + "symfony/mime": "^5.4|^6.0", + "symfony/rate-limiter": "^5.2|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-20T16:36:29+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "8d8e7aa60593fd0a2e3c1cea08cc687314841b61" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/8d8e7aa60593fd0a2e3c1cea08cc687314841b61", + "reference": "8d8e7aa60593fd0a2e3c1cea08cc687314841b61", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/error-handler": "^6.3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-foundation": "^6.3.4", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/cache": "<5.4", + "symfony/config": "<6.1", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<6.3.4", + "symfony/doctrine-bridge": "<5.4", + "symfony/form": "<5.4", + "symfony/http-client": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4", + "symfony/translation": "<5.4", + "symfony/translation-contracts": "<2.5", + "symfony/twig-bridge": "<5.4", + "symfony/validator": "<5.4", + "symfony/var-dumper": "<6.3", + "twig/twig": "<2.13" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/clock": "^6.2", + "symfony/config": "^6.1", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dependency-injection": "^6.3.4", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^5.4|^6.0", + "symfony/property-access": "^5.4.5|^6.0.5", + "symfony/routing": "^5.4|^6.0", + "symfony/serializer": "^6.3", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^6.3", + "symfony/var-exporter": "^6.2", + "twig/twig": "^2.13|^3.0.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T16:57:27+00:00" + }, + { + "name": "symfony/intl", + "version": "v6.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/intl.git", + "reference": "4cc98c05f2c55150a6aa5b3e20667f7a6d06cca9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/intl/zipball/4cc98c05f2c55150a6aa5b3e20667f7a6d06cca9", + "reference": "4cc98c05f2c55150a6aa5b3e20667f7a6d06cca9", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Eriksen Costa", + "email": "eriksen.costa@infranology.com.br" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides access to the localization data of the ICU library", + "homepage": "https://symfony.com", + "keywords": [ + "i18n", + "icu", + "internationalization", + "intl", + "l10n", + "localization" + ], + "support": { + "source": "https://github.com/symfony/intl/tree/v6.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-28T23:11:45+00:00" + }, + { + "name": "symfony/mailer", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/d89611a7830d51b5e118bca38e390dea92f9ea06", + "reference": "d89611a7830d51b5e118bca38e390dea92f9ea06", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.1", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/mime": "^6.2", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/messenger": "<6.2", + "symfony/mime": "<6.2", + "symfony/twig-bridge": "<6.2.1" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/messenger": "^6.2", + "symfony/twig-bridge": "^6.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-06T09:47:15+00:00" + }, + { + "name": "symfony/messenger", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/messenger.git", + "reference": "096b151f955d5ac56cc61a3ca0e16b38ff093805" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/messenger/zipball/096b151f955d5ac56cc61a3ca0e16b38ff093805", + "reference": "096b151f955d5ac56cc61a3ca0e16b38ff093805", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/clock": "^6.3", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<6.3", + "symfony/event-dispatcher": "<5.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/framework-bundle": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/serializer": "<5.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/console": "^6.3", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/rate-limiter": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Messenger\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Samuel Roze", + "email": "samuel.roze@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps applications send and receive messages to/from other applications or via message queues", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/messenger/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-22T19:13:10+00:00" + }, + { + "name": "symfony/mime", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "reference": "d5179eedf1cb2946dbd760475ebf05c251ef6a6e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<5.4", + "symfony/serializer": "<6.2.13|>=6.3,<6.3.2" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/serializer": "~6.2.13|^6.3.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-29T06:59:36+00:00" + }, + { + "name": "symfony/monolog-bridge", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bridge.git", + "reference": "2bbfc8bd9d6f966b69eda20c66762580a0410c78" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/2bbfc8bd9d6f966b69eda20c66762580a0410c78", + "reference": "2bbfc8bd9d6f966b69eda20c66762580a0410c78", + "shasum": "" + }, + "require": { + "monolog/monolog": "^1.25.1|^2|^3", + "php": ">=8.1", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/console": "<5.4", + "symfony/http-foundation": "<5.4", + "symfony/security-core": "<6.0" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/mailer": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/security-core": "^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Monolog\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Monolog with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/monolog-bridge/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, + { + "name": "symfony/monolog-bundle", + "version": "v3.10.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "reference": "414f951743f4aa1fd0f5bf6a0e9c16af3fe7f181", + "shasum": "" + }, + "require": { + "monolog/monolog": "^1.25.1 || ^2.0 || ^3.0", + "php": ">=7.2.5", + "symfony/config": "^5.4 || ^6.0 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/http-kernel": "^5.4 || ^6.0 || ^7.0", + "symfony/monolog-bridge": "^5.4 || ^6.0 || ^7.0" + }, + "require-dev": { + "symfony/console": "^5.4 || ^6.0 || ^7.0", + "symfony/phpunit-bridge": "^6.3 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony MonologBundle", + "homepage": "https://symfony.com", + "keywords": [ + "log", + "logging" + ], + "support": { + "issues": "https://github.com/symfony/monolog-bundle/issues", + "source": "https://github.com/symfony/monolog-bundle/tree/v3.10.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-06T17:08:13+00:00" + }, + { + "name": "symfony/notifier", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/notifier.git", + "reference": "2e39d4a9a96e25d127ffe30171127f6ef73934df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/notifier/zipball/2e39d4a9a96e25d127ffe30171127f6ef73934df", + "reference": "2e39d4a9a96e25d127ffe30171127f6ef73934df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3" + }, + "conflict": { + "symfony/event-dispatcher": "<5.4", + "symfony/event-dispatcher-contracts": "<2.5", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4" + }, + "require-dev": { + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/messenger": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Notifier\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Sends notifications via one or more channels (email, SMS, ...)", + "homepage": "https://symfony.com", + "keywords": [ + "notification", + "notifier" + ], + "support": { + "source": "https://github.com/symfony/notifier/tree/v6.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-07T07:32:05+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-12T14:21:09+00:00" + }, + { + "name": "symfony/password-hasher", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/password-hasher.git", + "reference": "82161c4bebf77900372083ec6e484b5f055b0cba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/82161c4bebf77900372083ec6e484b5f055b0cba", + "reference": "82161c4bebf77900372083ec6e484b5f055b0cba", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "conflict": { + "symfony/security-core": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0", + "symfony/security-core": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PasswordHasher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Robin Chalas", + "email": "robin.chalas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides password hashing utilities", + "homepage": "https://symfony.com", + "keywords": [ + "hashing", + "password" + ], + "support": { + "source": "https://github.com/symfony/password-hasher/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-06T10:58:05+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "e46b4da57951a16053cd751f63f4a24292788157" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/e46b4da57951a16053cd751f63f4a24292788157", + "reference": "e46b4da57951a16053cd751f63f4a24292788157", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance and support of other locales than \"en\"" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Icu\\": "" + }, + "classmap": [ + "Resources/stubs" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-icu/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-03-21T17:27:24+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d", + "reference": "ecaafce9f77234a6a449d29e49267ba10499116d", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-intl-normalizer": "^1.10", + "symfony/polyfill-php72": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:30:37+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/polyfill-php83", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php83.git", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "symfony/polyfill-php80": "^1.14" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php83\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-16T06:22:46+00:00" + }, + { + "name": "symfony/process", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-07T10:39:22+00:00" + }, + { + "name": "symfony/property-access", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "2dc4f9da444b8f8ff592e95d570caad67924f1d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/2dc4f9da444b8f8ff592e95d570caad67924f1d0", + "reference": "2dc4f9da444b8f8ff592e95d570caad67924f1d0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/property-info": "^5.4|^6.0" + }, + "require-dev": { + "symfony/cache": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides functions to read and write from/to an object or array using a simple string notation", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property-path", + "reflection" + ], + "support": { + "source": "https://github.com/symfony/property-access/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-13T15:26:11+00:00" + }, + { + "name": "symfony/property-info", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-info.git", + "reference": "664ae7ad443d7cc591ff3e15496b954e4cefe729" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-info/zipball/664ae7ad443d7cc591ff3e15496b954e4cefe729", + "reference": "664ae7ad443d7cc591ff3e15496b954e4cefe729", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<5.2", + "phpdocumentor/type-resolver": "<1.5.1", + "symfony/dependency-injection": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4|^2", + "phpdocumentor/reflection-docblock": "^5.2", + "phpstan/phpdoc-parser": "^1.0", + "symfony/cache": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyInfo\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Extracts information about PHP class' properties using metadata of popular sources", + "homepage": "https://symfony.com", + "keywords": [ + "doctrine", + "phpdoc", + "property", + "symfony", + "type", + "validator" + ], + "support": { + "source": "https://github.com/symfony/property-info/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-24T11:57:32+00:00" + }, + { + "name": "symfony/routing", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "cb7404232d49dd11cc971b832fcbd49e7c22b049" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/cb7404232d49dd11cc971b832fcbd49e7c22b049", + "reference": "cb7404232d49dd11cc971b832fcbd49e7c22b049", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "symfony/config": "<6.2", + "symfony/dependency-injection": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "psr/log": "^1|^2|^3", + "symfony/config": "^6.2", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T14:25:58+00:00" + }, + { + "name": "symfony/runtime", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/runtime.git", + "reference": "d5c09493647a0c1a16e6c8da308098e840d1164f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/runtime/zipball/d5c09493647a0c1a16e6c8da308098e840d1164f", + "reference": "d5c09493647a0c1a16e6c8da308098e840d1164f", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0|^2.0", + "php": ">=8.1" + }, + "conflict": { + "symfony/dotenv": "<5.4" + }, + "require-dev": { + "composer/composer": "^1.0.2|^2.0", + "symfony/console": "^5.4.9|^6.0.9", + "symfony/dotenv": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Symfony\\Component\\Runtime\\Internal\\ComposerPlugin" + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Runtime\\": "", + "Symfony\\Runtime\\Symfony\\Component\\": "Internal/" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Enables decoupling PHP applications from global state", + "homepage": "https://symfony.com", + "keywords": [ + "runtime" + ], + "support": { + "source": "https://github.com/symfony/runtime/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-16T17:05:46+00:00" + }, + { + "name": "symfony/security-bundle", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-bundle.git", + "reference": "57889ebb1ac3403d550c787c4fde127261abacb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/57889ebb1ac3403d550c787c4fde127261abacb6", + "reference": "57889ebb1ac3403d550c787c4fde127261abacb6", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.1", + "symfony/clock": "^6.3", + "symfony/config": "^6.1", + "symfony/dependency-injection": "^6.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/http-foundation": "^6.2", + "symfony/http-kernel": "^6.2", + "symfony/password-hasher": "^5.4|^6.0", + "symfony/security-core": "^6.2", + "symfony/security-csrf": "^5.4|^6.0", + "symfony/security-http": "^6.3.6", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/browser-kit": "<5.4", + "symfony/console": "<5.4", + "symfony/framework-bundle": "<6.3", + "symfony/http-client": "<5.4", + "symfony/ldap": "<5.4", + "symfony/twig-bundle": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4|^2", + "symfony/asset": "^5.4|^6.0", + "symfony/browser-kit": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/dom-crawler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/framework-bundle": "^6.3", + "symfony/http-client": "^5.4|^6.0", + "symfony/ldap": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/rate-limiter": "^5.4|^6.0", + "symfony/serializer": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/twig-bridge": "^5.4|^6.0", + "symfony/twig-bundle": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1", + "web-token/jwt-signature-algorithm-eddsa": "^3.1", + "web-token/jwt-signature-algorithm-hmac": "^3.1", + "web-token/jwt-signature-algorithm-none": "^3.1", + "web-token/jwt-signature-algorithm-rsa": "^3.1" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SecurityBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-bundle/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-09T09:33:10+00:00" + }, + { + "name": "symfony/security-core", + "version": "v6.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-core.git", + "reference": "7ceb30fed93f5ea40ccde3173d1f7712527c0d62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-core/zipball/7ceb30fed93f5ea40ccde3173d1f7712527c0d62", + "reference": "7ceb30fed93f5ea40ccde3173d1f7712527c0d62", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher-contracts": "^2.5|^3", + "symfony/password-hasher": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/event-dispatcher": "<5.4", + "symfony/http-foundation": "<5.4", + "symfony/ldap": "<5.4", + "symfony/security-guard": "<5.4", + "symfony/validator": "<5.4" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "psr/container": "^1.1|^2.0", + "psr/log": "^1|^2|^3", + "symfony/cache": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/ldap": "^5.4|^6.0", + "symfony/string": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Core\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - Core Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-core/tree/v6.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-28T23:11:45+00:00" + }, + { + "name": "symfony/security-csrf", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-csrf.git", + "reference": "63d7b098c448cbddb46ea5eda33b68c1ece6eb5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/63d7b098c448cbddb46ea5eda33b68c1ece6eb5b", + "reference": "63d7b098c448cbddb46ea5eda33b68c1ece6eb5b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/security-core": "^5.4|^6.0" + }, + "conflict": { + "symfony/http-foundation": "<5.4" + }, + "require-dev": { + "symfony/http-foundation": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Csrf\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - CSRF Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-csrf/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-05T08:41:27+00:00" + }, + { + "name": "symfony/security-http", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-http.git", + "reference": "19f7b5f5d20879a976d6d376e359bc975dfc6002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-http/zipball/19f7b5f5d20879a976d6d376e359bc975dfc6002", + "reference": "19f7b5f5d20879a976d6d376e359bc975dfc6002", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^6.3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/security-core": "^6.3", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/clock": "<6.3", + "symfony/event-dispatcher": "<5.4.9|>=6,<6.0.9", + "symfony/http-client-contracts": "<3.0", + "symfony/security-bundle": "<5.4", + "symfony/security-csrf": "<5.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/cache": "^5.4|^6.0", + "symfony/clock": "^6.3", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-client-contracts": "^3.0", + "symfony/rate-limiter": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/security-csrf": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "web-token/jwt-checker": "^3.1", + "web-token/jwt-signature-algorithm-ecdsa": "^3.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Http\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - HTTP Integration", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-http/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-09T21:20:12+00:00" + }, + { + "name": "symfony/serializer", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/serializer.git", + "reference": "6eee0fd95f5caa1e77cab29552620ebf8e5b1a5f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/serializer/zipball/6eee0fd95f5caa1e77cab29552620ebf8e5b1a5f", + "reference": "6eee0fd95f5caa1e77cab29552620ebf8e5b1a5f", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "doctrine/annotations": "<1.12", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/dependency-injection": "<5.4", + "symfony/property-access": "<5.4", + "symfony/property-info": "<5.4.24|>=6,<6.2.11", + "symfony/uid": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "phpdocumentor/reflection-docblock": "^3.2|^4.0|^5.0", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/filesystem": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4.24|^6.2.11", + "symfony/uid": "^5.4|^6.0", + "symfony/validator": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0", + "symfony/var-exporter": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Serializer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/serializer/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T14:25:58+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "reference": "b3313c2dbffaf71c8de2934e2ea56ed2291a3838", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-30T20:28:31+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-16T10:14:28+00:00" + }, + { + "name": "symfony/string", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "56427887aeaf540e9bbd121ad6c43f14ad3ce136" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/56427887aeaf540e9bbd121ad6c43f14ad3ce136", + "reference": "56427887aeaf540e9bbd121ad6c43f14ad3ce136", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-28T20:40:29+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.3.7", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/30212e7c87dcb79c83f6362b00bde0e0b1213499", + "reference": "30212e7c87dcb79c83f6362b00bde0e0b1213499", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.13", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.3.7" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-28T23:11:45+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.4.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "reference": "dee0c6e5b4c07ce851b462530088e64b255ac9c5", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-25T15:08:44+00:00" + }, + { + "name": "symfony/twig-bridge", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bridge.git", + "reference": "c51407623959a626784ff302419026f56dc4e1ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/c51407623959a626784ff302419026f56dc4e1ba", + "reference": "c51407623959a626784ff302419026f56dc4e1ba", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/translation-contracts": "^2.5|^3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/console": "<5.4", + "symfony/form": "<6.3", + "symfony/http-foundation": "<5.4", + "symfony/http-kernel": "<6.2", + "symfony/mime": "<6.2", + "symfony/translation": "<5.4", + "symfony/workflow": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.12|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/asset": "^5.4|^6.0", + "symfony/asset-mapper": "^6.3", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/form": "^6.3", + "symfony/html-sanitizer": "^6.1", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^6.2", + "symfony/intl": "^5.4|^6.0", + "symfony/mime": "^6.2", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/security-acl": "^2.8|^3.0", + "symfony/security-core": "^5.4|^6.0", + "symfony/security-csrf": "^5.4|^6.0", + "symfony/security-http": "^5.4|^6.0", + "symfony/serializer": "^6.2", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^6.1", + "symfony/web-link": "^5.4|^6.0", + "symfony/workflow": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0", + "twig/cssinliner-extra": "^2.12|^3", + "twig/inky-extra": "^2.12|^3", + "twig/markdown-extra": "^2.12|^3" + }, + "type": "symfony-bridge", + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Twig\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides integration for Twig with various Symfony components", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bridge/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-09T21:20:12+00:00" + }, + { + "name": "symfony/twig-bundle", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/twig-bundle.git", + "reference": "82429320fe931dd50825ec08140c54b3a315bf79" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/82429320fe931dd50825ec08140c54b3a315bf79", + "reference": "82429320fe931dd50825ec08140c54b3a315bf79", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "php": ">=8.1", + "symfony/config": "^6.1", + "symfony/dependency-injection": "^6.1", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^6.2", + "symfony/twig-bridge": "^6.3", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "symfony/framework-bundle": "<5.4", + "symfony/translation": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.10.4|^2", + "symfony/asset": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/form": "^5.4|^6.0", + "symfony/framework-bundle": "^5.4|^6.0", + "symfony/routing": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/web-link": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\TwigBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of Twig into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/twig-bundle/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, + { + "name": "symfony/validator", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/validator.git", + "reference": "c118889931856af47b0732b609f3ac2ddccd1da6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/validator/zipball/c118889931856af47b0732b609f3ac2ddccd1da6", + "reference": "c118889931856af47b0732b609f3ac2ddccd1da6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php83": "^1.27", + "symfony/translation-contracts": "^2.5|^3" + }, + "conflict": { + "doctrine/annotations": "<1.13", + "doctrine/lexer": "<1.1", + "symfony/dependency-injection": "<5.4", + "symfony/expression-language": "<5.4", + "symfony/http-kernel": "<5.4", + "symfony/intl": "<5.4", + "symfony/property-info": "<5.4", + "symfony/translation": "<5.4", + "symfony/yaml": "<5.4" + }, + "require-dev": { + "doctrine/annotations": "^1.13|^2", + "egulias/email-validator": "^2.1.10|^3|^4", + "symfony/cache": "^5.4|^6.0", + "symfony/config": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/intl": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/property-access": "^5.4|^6.0", + "symfony/property-info": "^5.4|^6.0", + "symfony/translation": "^5.4|^6.0", + "symfony/yaml": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Validator\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to validate values", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/validator/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-29T07:44:47+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/81acabba9046550e89634876ca64bfcd3c06aa0a", + "reference": "81acabba9046550e89634876ca64bfcd3c06aa0a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-08T10:42:36+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v6.3.10", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "7bfcf232a9c7e4acad00e96774e340eb86d10bf0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7bfcf232a9c7e4acad00e96774e340eb86d10bf0", + "reference": "7bfcf232a9c7e4acad00e96774e340eb86d10bf0", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v6.3.10" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-29T16:00:50+00:00" + }, + { + "name": "symfony/web-link", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/web-link.git", + "reference": "0989ca617d0703cdca501a245f10e194ff22315b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/web-link/zipball/0989ca617d0703cdca501a245f10e194ff22315b", + "reference": "0989ca617d0703cdca501a245f10e194ff22315b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/link": "^1.1|^2.0" + }, + "conflict": { + "symfony/http-kernel": "<5.4" + }, + "provide": { + "psr/link-implementation": "1.0|2.0" + }, + "require-dev": { + "symfony/http-kernel": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\WebLink\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kévin Dunglas", + "email": "dunglas@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages links between resources", + "homepage": "https://symfony.com", + "keywords": [ + "dns-prefetch", + "http", + "http2", + "link", + "performance", + "prefetch", + "preload", + "prerender", + "psr13", + "push" + ], + "support": { + "source": "https://github.com/symfony/web-link/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-04-21T14:41:17+00:00" + }, + { + "name": "symfony/yaml", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "3493af8a8dad7fa91c77fa473ba23ecd95334a92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/3493af8a8dad7fa91c77fa473ba23ecd95334a92", + "reference": "3493af8a8dad7fa91c77fa473ba23ecd95334a92", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "symfony/console": "^5.4|^6.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-06T10:58:05+00:00" + }, + { + "name": "twig/extra-bundle", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/twig-extra-bundle.git", + "reference": "32807183753de0388c8e59f7ac2d13bb47311140" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/twig-extra-bundle/zipball/32807183753de0388c8e59f7ac2d13bb47311140", + "reference": "32807183753de0388c8e59f7ac2d13bb47311140", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/framework-bundle": "^5.4|^6.0|^7.0", + "symfony/twig-bundle": "^5.4|^6.0|^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "league/commonmark": "^1.0|^2.0", + "symfony/phpunit-bridge": "^6.4|^7.0", + "twig/cache-extra": "^3.0", + "twig/cssinliner-extra": "^2.12|^3.0", + "twig/html-extra": "^2.12|^3.0", + "twig/inky-extra": "^2.12|^3.0", + "twig/intl-extra": "^2.12|^3.0", + "twig/markdown-extra": "^2.12|^3.0", + "twig/string-extra": "^2.12|^3.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Twig\\Extra\\TwigExtraBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Symfony bundle for extra Twig extensions", + "homepage": "https://twig.symfony.com", + "keywords": [ + "bundle", + "extra", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/twig-extra-bundle/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T14:02:01+00:00" + }, + { + "name": "twig/intl-extra", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/intl-extra.git", + "reference": "7b3db67c700735f473a265a97e1adaeba3e6ca0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/7b3db67c700735f473a265a97e1adaeba3e6ca0c", + "reference": "7b3db67c700735f473a265a97e1adaeba3e6ca0c", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/intl": "^5.4|^6.0|^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Intl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Intl", + "homepage": "https://twig.symfony.com", + "keywords": [ + "intl", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/intl-extra/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T17:27:48+00:00" + }, + { + "name": "twig/markdown-extra", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/markdown-extra.git", + "reference": "b6e4954ab60030233df5d293886b5404558daac8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/markdown-extra/zipball/b6e4954ab60030233df5d293886b5404558daac8", + "reference": "b6e4954ab60030233df5d293886b5404558daac8", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "twig/twig": "^3.0" + }, + "require-dev": { + "erusev/parsedown": "^1.7", + "league/commonmark": "^1.0|^2.0", + "league/html-to-markdown": "^4.8|^5.0", + "michelf/php-markdown": "^1.8|^2.0", + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\Markdown\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Markdown", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "markdown", + "twig" + ], + "support": { + "source": "https://github.com/twigphp/markdown-extra/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T14:02:01+00:00" + }, + { + "name": "twig/string-extra", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/string-extra.git", + "reference": "b0c9037d96baff79abe368dc092a59b726517548" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/string-extra/zipball/b0c9037d96baff79abe368dc092a59b726517548", + "reference": "b0c9037d96baff79abe368dc092a59b726517548", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/string": "^5.4|^6.0|^7.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\Extra\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "description": "A Twig extension for Symfony String", + "homepage": "https://twig.symfony.com", + "keywords": [ + "html", + "string", + "twig", + "unicode" + ], + "support": { + "source": "https://github.com/twigphp/string-extra/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T14:02:01+00:00" + }, + { + "name": "twig/twig", + "version": "v3.8.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3", + "symfony/polyfill-php80": "^1.22" + }, + "require-dev": { + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2023-11-21T18:54:41+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + }, + { + "name": "yggverse/scrapeer", + "version": "0.5.4", + "source": { + "type": "git", + "url": "https://github.com/YGGverse/scrapeer.git", + "reference": "3b9d15c9706a844bf1428812bb938370e907b4f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/YGGverse/scrapeer/zipball/3b9d15c9706a844bf1428812bb938370e907b4f3", + "reference": "3b9d15c9706a844bf1428812bb938370e907b4f3", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Yggverse\\Scrapeer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "CC-BY-SA-3.0" + ], + "authors": [ + { + "name": "medariox", + "homepage": "https://github.com/medariox/scrapeer" + }, + { + "name": "YGGverse", + "homepage": "https://github.com/YGGverse/scrapeer" + } + ], + "description": "Essential PHP library that scrapes HTTP(S) and UDP trackers for torrent information", + "support": { + "issues": "https://github.com/YGGverse/scrapeer/issues", + "source": "https://github.com/YGGverse/scrapeer/tree/0.5.4" + }, + "time": "2023-10-09T01:47:30+00:00" + } + ], + "packages-dev": [ + { + "name": "masterminds/html5", + "version": "2.8.1", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f47dcf3c70c584de14f21143c55d9939631bc6cf", + "reference": "f47dcf3c70c584de14f21143c55d9939631bc6cf", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.8.1" + }, + "time": "2023-05-10T11:58:31+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "9.2.29", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=7.3", + "phpunit/php-file-iterator": "^3.0.3", + "phpunit/php-text-template": "^2.0.2", + "sebastian/code-unit-reverse-lookup": "^2.0.2", + "sebastian/complexity": "^2.0", + "sebastian/environment": "^5.1.2", + "sebastian/lines-of-code": "^1.0.3", + "sebastian/version": "^3.0.1", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.29" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-19T04:57:46+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "3.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2021-12-02T12:48:52+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:58:55+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T05:33:50+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:16:10+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "9.6.15", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", + "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.3.1 || ^2", + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=7.3", + "phpunit/php-code-coverage": "^9.2.28", + "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-invoker": "^3.1.1", + "phpunit/php-text-template": "^2.0.3", + "phpunit/php-timer": "^5.0.2", + "sebastian/cli-parser": "^1.0.1", + "sebastian/code-unit": "^1.0.6", + "sebastian/comparator": "^4.0.8", + "sebastian/diff": "^4.0.3", + "sebastian/environment": "^5.1.3", + "sebastian/exporter": "^4.0.5", + "sebastian/global-state": "^5.0.1", + "sebastian/object-enumerator": "^4.0.3", + "sebastian/resource-operations": "^3.0.3", + "sebastian/type": "^3.2", + "sebastian/version": "^3.0.2" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "9.6-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-12-01T16:55:19+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:08:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "1.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", + "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:08:54+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T05:30:19+00:00" + }, + { + "name": "sebastian/comparator", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", + "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/diff": "^4.0", + "sebastian/exporter": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T12:41:17+00:00" + }, + { + "name": "sebastian/complexity", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/739b35e53379900cc9ac327b2147867b8b6efd88", + "reference": "739b35e53379900cc9ac327b2147867b8b6efd88", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.7", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T15:52:27+00:00" + }, + { + "name": "sebastian/diff", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-07T05:35:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "5.1.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:03:51+00:00" + }, + { + "name": "sebastian/exporter", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2022-09-14T06:03:37+00:00" + }, + { + "name": "sebastian/global-state", + "version": "5.0.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bde739e7565280bda77be70044ac1047bc007e34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", + "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^9.3" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-02T09:26:13+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "reference": "c1c2e997aa3146983ed888ad08b15470a2e22ecc", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.6", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-11-28T06:42:11+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", + "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "shasum": "" + }, + "require": { + "php": ">=7.3", + "sebastian/object-reflector": "^2.0", + "sebastian/recursion-context": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:12:34+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-10-26T13:14:26+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "4.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:07:39+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "support": { + "issues": "https://github.com/sebastianbergmann/resource-operations/issues", + "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:45:17+00:00" + }, + { + "name": "sebastian/type", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:13:03+00:00" + }, + { + "name": "sebastian/version", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c6c1022351a901512170118436c764e473f6de8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", + "reference": "c6c1022351a901512170118436c764e473f6de8c", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2020-09-28T06:39:44+00:00" + }, + { + "name": "symfony/browser-kit", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/browser-kit.git", + "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/e270297dbee59168274c2b535ab1bccd593e6ffe", + "reference": "e270297dbee59168274c2b535ab1bccd593e6ffe", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/dom-crawler": "^5.4|^6.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/mime": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\BrowserKit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/browser-kit/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T08:07:48+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/883d961421ab1709877c10ac99451632a3d6fa57", + "reference": "883d961421ab1709877c10ac99451632a3d6fa57", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-12T16:00:22+00:00" + }, + { + "name": "symfony/debug-bundle", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/debug-bundle.git", + "reference": "3f04a578e1a9f1d7da84a87b690c03123e5d8c31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/debug-bundle/zipball/3f04a578e1a9f1d7da84a87b690c03123e5d8c31", + "reference": "3f04a578e1a9f1d7da84a87b690c03123e5d8c31", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "php": ">=8.1", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/twig-bridge": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/dependency-injection": "<5.4" + }, + "require-dev": { + "symfony/config": "^5.4|^6.0", + "symfony/web-profiler-bundle": "^5.4|^6.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\DebugBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Symfony VarDumper component and the ServerLogCommand from MonologBridge into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug-bundle/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-13T14:29:38+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v6.3.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "b7065c123ae977a008568a3d016a17a110df7a8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b7065c123ae977a008568a3d016a17a110df7a8e", + "reference": "b7065c123ae977a008568a3d016a17a110df7a8e", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v6.3.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-20T16:36:29+00:00" + }, + { + "name": "symfony/maker-bundle", + "version": "v1.52.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/maker-bundle.git", + "reference": "112f9466c94a46ca33dc441eee59a12cd1790757" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/maker-bundle/zipball/112f9466c94a46ca33dc441eee59a12cd1790757", + "reference": "112f9466c94a46ca33dc441eee59a12cd1790757", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "nikic/php-parser": "^4.11", + "php": ">=8.1", + "symfony/config": "^6.3|^7.0", + "symfony/console": "^6.3|^7.0", + "symfony/dependency-injection": "^6.3|^7.0", + "symfony/deprecation-contracts": "^2.2|^3", + "symfony/filesystem": "^6.3|^7.0", + "symfony/finder": "^6.3|^7.0", + "symfony/framework-bundle": "^6.3|^7.0", + "symfony/http-kernel": "^6.3|^7.0", + "symfony/process": "^6.3|^7.0" + }, + "conflict": { + "doctrine/doctrine-bundle": "<2.4", + "doctrine/orm": "<2.10" + }, + "require-dev": { + "composer/semver": "^3.0", + "doctrine/doctrine-bundle": "^2.5.0", + "doctrine/orm": "^2.10.0", + "symfony/http-client": "^6.3|^7.0", + "symfony/phpunit-bridge": "^6.3|^7.0", + "symfony/security-core": "^6.3|^7.0", + "symfony/yaml": "^6.3|^7.0", + "twig/twig": "^2.0|^3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MakerBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Maker helps you create empty commands, controllers, form classes, tests and more so you can forget about writing boilerplate code.", + "homepage": "https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html", + "keywords": [ + "code generator", + "dev", + "generator", + "scaffold", + "scaffolding" + ], + "support": { + "issues": "https://github.com/symfony/maker-bundle/issues", + "source": "https://github.com/symfony/maker-bundle/tree/v1.52.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T18:23:49+00:00" + }, + { + "name": "symfony/phpunit-bridge", + "version": "v6.4.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/phpunit-bridge.git", + "reference": "cca5373a41d45edbeaf38b7b67f376da2205ff95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/cca5373a41d45edbeaf38b7b67f376da2205ff95", + "reference": "cca5373a41d45edbeaf38b7b67f376da2205ff95", + "shasum": "" + }, + "require": { + "php": ">=7.1.3" + }, + "conflict": { + "phpunit/phpunit": "<7.5|9.1.2" + }, + "require-dev": { + "symfony/deprecation-contracts": "^2.5|^3.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/polyfill-php81": "^1.27" + }, + "bin": [ + "bin/simple-phpunit" + ], + "type": "symfony-bridge", + "extra": { + "thanks": { + "name": "phpunit/phpunit", + "url": "https://github.com/sebastianbergmann/phpunit" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Bridge\\PhpUnit\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides utilities for PHPUnit, especially user deprecation notices management", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-12-01T09:25:07+00:00" + }, + { + "name": "symfony/web-profiler-bundle", + "version": "v6.3.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/web-profiler-bundle.git", + "reference": "4167c20cbdbb1152007fa731718c8c0362f28617" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/4167c20cbdbb1152007fa731718c8c0362f28617", + "reference": "4167c20cbdbb1152007fa731718c8c0362f28617", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/config": "^5.4|^6.0", + "symfony/framework-bundle": "^5.4|^6.0,<6.4", + "symfony/http-kernel": "^6.3", + "symfony/routing": "^5.4|^6.0", + "symfony/twig-bundle": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "conflict": { + "symfony/form": "<5.4", + "symfony/mailer": "<5.4", + "symfony/messenger": "<5.4" + }, + "require-dev": { + "symfony/browser-kit": "^5.4|^6.0", + "symfony/console": "^5.4|^6.0", + "symfony/css-selector": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\WebProfilerBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a development tool that gives detailed information about the execution of any request", + "homepage": "https://symfony.com", + "keywords": [ + "dev" + ], + "support": { + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.3.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-31T14:41:59+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2023-11-20T00:12:19+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.1", + "ext-ctype": "*", + "ext-iconv": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.3.0" +} diff --git a/config/bundles.php b/config/bundles.php new file mode 100644 index 0000000..0457f99 --- /dev/null +++ b/config/bundles.php @@ -0,0 +1,14 @@ + ['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], +]; diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml new file mode 100644 index 0000000..6899b72 --- /dev/null +++ b/config/packages/cache.yaml @@ -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 diff --git a/config/packages/debug.yaml b/config/packages/debug.yaml new file mode 100644 index 0000000..ad874af --- /dev/null +++ b/config/packages/debug.yaml @@ -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)%" diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml new file mode 100644 index 0000000..ec0f77e --- /dev/null +++ b/config/packages/doctrine.yaml @@ -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 diff --git a/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations.yaml new file mode 100644 index 0000000..29231d9 --- /dev/null +++ b/config/packages/doctrine_migrations.yaml @@ -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 diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml new file mode 100644 index 0000000..6d85c29 --- /dev/null +++ b/config/packages/framework.yaml @@ -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 diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml new file mode 100644 index 0000000..56a650d --- /dev/null +++ b/config/packages/mailer.yaml @@ -0,0 +1,3 @@ +framework: + mailer: + dsn: '%env(MAILER_DSN)%' diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml new file mode 100644 index 0000000..587083a --- /dev/null +++ b/config/packages/messenger.yaml @@ -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 diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml new file mode 100644 index 0000000..2b3726f --- /dev/null +++ b/config/packages/monolog.yaml @@ -0,0 +1,62 @@ +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 + path: "%kernel.logs_dir%/%kernel.environment%.log" + 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 diff --git a/config/packages/notifier.yaml b/config/packages/notifier.yaml new file mode 100644 index 0000000..7f4bed1 --- /dev/null +++ b/config/packages/notifier.yaml @@ -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 } diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml new file mode 100644 index 0000000..4b766ce --- /dev/null +++ b/config/packages/routing.yaml @@ -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 diff --git a/config/packages/security.yaml b/config/packages/security.yaml new file mode 100644 index 0000000..367af25 --- /dev/null +++ b/config/packages/security.yaml @@ -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 diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml new file mode 100644 index 0000000..210ba05 --- /dev/null +++ b/config/packages/translation.yaml @@ -0,0 +1,17 @@ +framework: + default_locale: en + translator: + default_path: '%kernel.project_dir%/translations' + fallbacks: + - en + providers: + crowdin: + dsn: '%env(CROWDIN_DSN)%' + domains: ['messages'] + locales: ['en','cs','nl','eo','fr', 'ja', 'ka','de','he','it','lv','pl','pt','ru','es','uk'] +# loco: +# dsn: '%env(LOCO_DSN)%' +# lokalise: +# dsn: '%env(LOKALISE_DSN)%' +# phrase: +# dsn: '%env(PHRASE_DSN)%' diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml new file mode 100644 index 0000000..236f109 --- /dev/null +++ b/config/packages/twig.yaml @@ -0,0 +1,10 @@ +twig: + default_path: '%kernel.project_dir%/templates' + globals: + version: '%app.version%' + name: '%app.name%' + theme: '%app.theme%' + +when@test: + twig: + strict_variables: true diff --git a/config/packages/validator.yaml b/config/packages/validator.yaml new file mode 100644 index 0000000..0201281 --- /dev/null +++ b/config/packages/validator.yaml @@ -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 diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml new file mode 100644 index 0000000..b946111 --- /dev/null +++ b/config/packages/web_profiler.yaml @@ -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 } diff --git a/config/preload.php b/config/preload.php new file mode 100644 index 0000000..5ebcdb2 --- /dev/null +++ b/config/preload.php @@ -0,0 +1,5 @@ + doctrine/doctrine-bundle ### + database: + ports: + - "5432" +###< doctrine/doctrine-bundle ### + +###> symfony/mailer ### + mailer: + image: schickling/mailcatcher + ports: ["1025", "1080"] +###< symfony/mailer ### diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1067b9c --- /dev/null +++ b/docker-compose.yml @@ -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 ### diff --git a/example/environment/crontab b/example/environment/crontab deleted file mode 100644 index 136ec32..0000000 --- a/example/environment/crontab +++ /dev/null @@ -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 \ No newline at end of file diff --git a/example/environment/env.example.php b/example/environment/env.example.php deleted file mode 100644 index 134afc5..0000000 --- a/example/environment/env.example.php +++ /dev/null @@ -1,218 +0,0 @@ -addSql('ALTER TABLE torrent ADD COLUMN status BOOLEAN DEFAULT 1'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE TEMPORARY TABLE __temp__torrent AS SELECT id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers FROM torrent'); + $this->addSql('DROP TABLE torrent'); + $this->addSql('CREATE TABLE torrent (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, scraped INTEGER DEFAULT NULL, locales CLOB NOT NULL --(DC2Type:simple_array) + , sensitive BOOLEAN NOT NULL, approved BOOLEAN NOT NULL, md5file VARCHAR(32) NOT NULL, keywords CLOB DEFAULT NULL --(DC2Type:simple_array) + , seeders INTEGER DEFAULT NULL, peers INTEGER DEFAULT NULL, leechers INTEGER DEFAULT NULL)'); + $this->addSql('INSERT INTO torrent (id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers) SELECT id, user_id, added, scraped, locales, sensitive, approved, md5file, keywords, seeders, peers, leechers FROM __temp__torrent'); + $this->addSql('DROP TABLE __temp__torrent'); + } +} diff --git a/migrations/Version20231029184600.php b/migrations/Version20231029184600.php new file mode 100644 index 0000000..634e2a1 --- /dev/null +++ b/migrations/Version20231029184600.php @@ -0,0 +1,35 @@ +addSql('CREATE TABLE torrent_poster (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, torrent_id INTEGER NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, approved BOOLEAN NOT NULL, md5file VARCHAR(32) NOT NULL)'); + $this->addSql('ALTER TABLE user ADD COLUMN posters BOOLEAN NOT NULL DEFAULT 1'); + $this->addSql('ALTER TABLE torrent ADD COLUMN torrent_poster_id BOOLEAN NULL'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE torrent_poster'); + $this->addSql('ALTER TABLE user DROP COLUMN posters'); + $this->addSql('ALTER TABLE torrent DROP COLUMN torrent_poster_id'); + } +} diff --git a/migrations/Version20231030225418.php b/migrations/Version20231030225418.php new file mode 100644 index 0000000..e43e71d --- /dev/null +++ b/migrations/Version20231030225418.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE torrent_poster ADD COLUMN position BOOLEAN NOT NULL DEFAULT "center"'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE torrent_poster DROP COLUMN position'); + } +} diff --git a/migrations/Version20231103235504.php b/migrations/Version20231103235504.php new file mode 100644 index 0000000..1ec3213 --- /dev/null +++ b/migrations/Version20231103235504.php @@ -0,0 +1,37 @@ +addSql('CREATE TABLE torrent_categories (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, torrent_id INTEGER NOT NULL, user_id INTEGER NOT NULL, added INTEGER NOT NULL, value CLOB NOT NULL --(DC2Type:simple_array) + , approved BOOLEAN NOT NULL)'); + $this->addSql('ALTER TABLE user ADD COLUMN categories CLOB DEFAULT "other"'); + $this->addSql('ALTER TABLE torrent ADD COLUMN categories CLOB DEFAULT "other"'); + $this->addSql('UPDATE user SET categories = "movie,series,tv,animation,music,game,audiobook,podcast,book,archive,picture,software,other"'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('DROP TABLE torrent_categories'); + $this->addSql('ALTER TABLE user DROP COLUMN categories'); + $this->addSql('ALTER TABLE torrent DROP COLUMN categories'); + } +} diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..6c4bfed --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + tests + + + + + + src + + + + + + + + + + diff --git a/public/asset/default/css/common.css b/public/asset/default/css/common.css new file mode 100644 index 0000000..71856f1 --- /dev/null +++ b/public/asset/default/css/common.css @@ -0,0 +1,128 @@ +* { + border: 0; + margin: 0; + padding: 0; + box-sizing: border-box; + outline: none; +} + +:focus, +:focus-within, +:focus-visible, +:active, +:target, +:hover { + opacity: 1; + transition: opacity .2s ease-in-out; + +} + +body { + background: #282b3c; + color: #ccc; + font-family: Sans-serif; + font-size: 13px; +} + +a, +a:visited, +a:active { + color: #96d9a1; + text-decoration: none; + opacity: .9; +} + +h1, h2, h3, h4, h5 { + display: inline-block; + font-weight: normal; +} + +h1 { + font-size: 16px; +} + +h2 { + color: #ccc; + font-size: 14px; +} + +input, +button, +select, +textarea { + accent-color: #65916d; + background: #5d627d; + border: #5d627d 1px solid; + color: #ccc; + border-radius: 3px; + padding: 6px 8px; + opacity: .96; +} + +input[type="file"] { + padding: 4px 8px; +} + +/* +main input, +main button, +main select, +main textarea { + padding: 8px; +} +*/ +textarea:focus, +input:focus { + border: #65916d 1px solid; + color: #fff; +} + +select[multiple="multiple"] > option { + border-top: 1px #5d627d solid; + border-bottom: 1px #5d627d solid; + color: #fff; +} + +select[multiple="multiple"] > option:active, +select[multiple="multiple"] > option:focus, +select[multiple="multiple"] > option:focus-within, +select[multiple="multiple"] > option:checked { + border-top: 1px #65916d solid; + border-bottom: 1px #65916d solid; + background: linear-gradient(#65916d, #65916d); +} + +button, +input[type="submit"] { + cursor: pointer; +} + +textarea, +select[multiple="multiple"] { + min-height: 180px; +} + +textarea::placeholder, +input::placeholder { + color: #9698a5; + opacity: 1; +} + +input[type="text"]:hover, +textarea:hover { + background: #636884; +} + +td { + padding: 2px 0; + vertical-align: top; +} + +header a.logo { + color: #ccc; + font-size: 20px; +} + +header a.logo > span { + color: #96d9a1; +} \ No newline at end of file diff --git a/public/asset/default/css/framework.css b/public/asset/default/css/framework.css new file mode 100644 index 0000000..0ca71e6 --- /dev/null +++ b/public/asset/default/css/framework.css @@ -0,0 +1,553 @@ +.container { + position: relative; + overflow: hidden; + max-width: 748px; + margin: 0 auto; +} + +.row { + position: relative; + overflow: hidden; +} + +.column { + position: relative; + float: left; +} + +.word-break { + word-break: break-word; +} + +.overflow-auto { + overflow: auto; +} + +.float-left { + float: left; +} + +.float-right { + float: right; +} + +.text-center { + text-align: center; +} + +.text-left { + text-align: left; +} + +.text-right { + text-align: right; +} + +.text-color-green, +a.text-color-green, +a.text-color-green:active, +a.text-color-green:visited, +a.text-color-green:hover { + color: #96d9a1; +} + +.text-color-red, +a.text-color-red, +a.text-color-red:active, +a.text-color-red:visited, +a.text-color-red:hover { + color: #d77575; +} + +.text-color-pink, +a.text-color-pink, +a.text-color-pink:active, +a.text-color-pink:visited, +a.text-color-pink:hover { + color: #b55cab; +} + +.text-color-default, +a.text-color-default, +a.text-color-default:active, +a.text-color-default:visited, +a.text-color-default:hover { + color: #ccc; +} + +.text-color-white, +a.text-color-white, +a.text-color-white:active, +a.text-color-white:visited, +a.text-color-white:hover { + color: #fff; +} + +/* +.text-color-pink { + color: #a44399; +} +*/ + +.text-color-blue { + color: #5785b7; +} + +.text-color-night, +a.text-color-night, +a.text-color-night:active, +a.text-color-night:visited, +a.text-color-night:hover { + color: #838695; +} + +.label { + padding: 4px 8px; + border-radius: 3px; +} + +.label-green, +a.label-green, +a.label-green:active, +a.label-green:visited, +a.label-green:hover { + color: #fff; + background-color: #65916d; +} + +.button, +a.button, +a.button:active, +a.button:visited, +a.button:hover { + background: #5d627d; + border: #5d627d 1px solid; + color: #ccc; + padding: 6px 8px; + border-radius: 3px; + opacity: .96; + display: inline-block; +} + +.button-green, +a.button-green, +a.button-green:active, +a.button-green:visited, +a.button-green:hover { + color: #fff; + background-color: #65916d; + border: #65916d 1px solid; +} + +.button-green:hover { + color: #fff; + background-color: #709e79; +} + +.position-relative { + position: relative; +} + +.position-fixed { + position: fixed; +} + +.position-absolute { + position: absolute; +} + +.vertical-align-middle { + vertical-align: middle; +} + +.top-2-px { + top: 2px; +} + +.line-height-20-px { + line-height: 20px; +} + +.line-height-26-px { + line-height: 26px; +} + +.border-radius-3-px { + border-radius: 3px; +} + +.border-radius-50 { + border-radius: 50%; +} + +.border-color-pink-light { + border: 1px #9b6895 solid; +} + +.border-color-pink { + border: 1px #a44399 solid; +} + +.border-color-green { + border: 1px #65916d solid; +} + +.border-color-pink { + border-bottom: 1px #a44399 solid; +} + +.border-color-default { + border: 1px rgba(93, 98, 125, .6) solid; +} + +.border-bottom-default { + border-bottom: 1px rgba(93, 98, 125, .6) solid; +} + +.border-top-default { + border-top: 1px rgba(93, 98, 125, .6) solid; +} + +.border-bottom-dashed { + border-bottom: 1px rgba(93, 98, 125, .6) dashed; +} + +.border-top-dashed { + border-top: 1px rgba(93, 98, 125, .6) dashed; +} + +.border-width-2-px { + border-width: 2px; +} + +.background-poster { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + background-blend-mode: soft-light; +} + +.background-color-night { + background-color: #34384f; +} + +.background-color-night-light { + background-color: #3d4159; +} + +.background-color-green { + background-color: #65916d; +} + +.background-color-hover-night-light:hover, +a.background-color-hover-night-light:hover, +a:active.background-color-hover-night-light:hover, +a:visited.background-color-hover-night-light:hover { + /*color: #fff;*/ + background-color: #3d4159; +} + +.background-color-red { + background-color: #9b4a4a; +} + +.cursor-default { + cursor: default; +} + +.cursor-help { + cursor: help; +} + +.font-weight-normal { + font-weight: normal +} + +.font-weight-200 { + font-weight: 200 +} + +.font-size-10-px { + font-size: 10px; +} + +.font-size-12-px { + font-size: 12px; +} + +.font-size-22-px { + font-size: 22px; +} + +.padding-0 { + padding: 0; +} + +.padding-x-0 { + padding-left: 0; + padding-right: 0; +} + +.padding-4-px { + padding: 4px; +} + +.padding-l-4-px { + padding-left: 4px; +} + +.padding-t-4-px { + padding-top: 4px; +} + +.padding-y-4-px { + padding-top: 4px; + padding-bottom: 4px; +} + +.padding-x-4-px { + padding-left: 4px; + padding-right: 4px; +} + +.padding-x-8-px { + padding-left: 8px; + padding-right: 8px; +} + +.padding-y-6-px { + padding-top: 6px; + padding-bottom: 6px; +} + +.padding-8-px { + padding: 8px; +} + +.padding-l-8-px { + padding-left: 8px; +} + +.padding-t-8-px { + padding-top: 8px; +} + +.padding-b-8-px { + padding-bottom: 8px; +} + +.padding-y-8-px { + padding-top: 8px; + padding-bottom: 8px; +} + +.padding-y-12-px { + padding-top: 12px; + padding-bottom: 12px; +} + +.padding-b-16-px { + padding-bottom: 16px; +} + +.padding-t-16-px { + padding-top: 16px; +} + +.padding-y-16-px { + padding-top: 16px; + padding-bottom: 16px; +} + +.padding-x-16-px { + padding-left: 16px; + padding-right: 16px; +} + +.padding-16-px { + padding: 16px; +} + +.padding-24-px { + padding: 24px; +} + +.padding-x-24-px { + padding-left: 24px; + padding-right: 24px; +} + +.padding-y-24-px { + padding-top: 24px; + padding-bottom: 24px; +} + +.margin-t-4-px { + margin-top: 4px; +} + +.margin-l-4-px { + margin-left: 4px; +} + +.margin-8-px { + margin: 8px; +} + +.margin-x-8-px { + margin-left: 8px; + margin-right: 8px; +} + +.margin-l-8-px { + margin-left: 8px; +} + +.margin-16-px { + margin: 16px; +} + +.margin-l-16-px { + margin-left: 16px; +} + +.margin-x-4-px { + margin-left: 4px; + margin-right: 4px; +} + +.margin-b-4-px { + margin-bottom: 4px; +} + +.margin-r-4-px { + margin-right: 4px; +} + +.margin-r-8-px { + margin-right: 8px; +} + +.margin-l-12-px { + margin-left: 12px; +} + +.margin-l-96-px { + margin-left: 96px; +} + +.margin-l--48-px { + margin-left: -48px; +} + +.margin-y-8-px { + margin-top: 8px; + margin-bottom: 8px; +} + +.margin-t-8-px { + margin-top: 8px; +} + +.margin-b-8-px { + margin-bottom: 8px; +} + +.margin-y-16-px { + margin-top: 16px; + margin-bottom: 16px; +} + +.margin-t-16-px { + margin-top: 16px; +} + +.margin-b-16-px { + margin-bottom: 16px; +} + +.margin-b-24-px { + margin-bottom: 24px; +} + +.display-block { + display: block; +} + +.display-inline-block { + display: inline-block; +} + +.display-flex { + display: flex; +} + +.opacity-0 { + opacity: 0; +} + +.opacity-06 { + opacity: .6; +} + +.opacity-hover-1:hover { + opacity: 1; + transition: opacity .2s; +} + +*:hover > .parent-hover-opacity-09 { + opacity: .9; + transition: opacity .2s; +} + +.blur-2 { + filter: blur(2px); +} + +.blur-hover-0:hover { + filter: blur(0); +} + +/* responsive rules */ + +.width-100 { + width: 100%; +} + +.width-50 { + width: 50%; +} + +.width-20 { + width: 20%; +} + +.width-80 { + width: 80%; +} + +.width-80-px { + width: 80px; +} + +.min-width-120-px { + min-width: 120px; +} + +.min-width-200-px { + min-width: 200px; +} + +@media (max-width: 1220px) { + + .width-tablet-100 { + width: 100%; + } +} + +@media (max-width: 512px) { + + .width-mobile-100 { + width: 100%; + } +} diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..9982c21 --- /dev/null +++ b/public/index.php @@ -0,0 +1,9 @@ + '%app.locales%' + ], + methods: + [ + 'GET' + ] + )] + public function all( + Request $request, + UserService $userService, + ActivityService $activityService + ): Response + { + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + $total = $activityService->findActivitiesTotal( + $user->getEvents() + ); + + $page = $request->get('page') ? (int) $request->get('page') : 1; + + return $this->render( + 'default/activity/list.html.twig', + [ + 'session' => [ + 'user' => $user + ], + 'activities' => $activityService->findLastActivities( + $user->getEvents(), + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ), + 'pagination' => + [ + 'page' => $page, + 'pages' => ceil($total / $this->getParameter('app.pagination')), + 'total' => $total + ] + ] + ); + } + + #[Route( + '/{_locale}/rss/activity', + name: 'rss_activity', + requirements: [ + '_locale' => '%app.locales%' + ], + methods: + [ + 'GET' + ] + )] + public function rssAll( + Request $request, + UserService $userService, + ActivityService $activityService + ): Response + { + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + $total = $activityService->findActivitiesTotal( + $user->getEvents() + ); + + $response = new Response(); + $response->headers->set('Content-Type', 'text/xml'); + + return $this->render( + 'default/activity/list.rss.twig', + [ + 'session' => [ + 'user' => $user + ], + 'activities' => $activityService->findLastActivities( + $user->getEvents() + ) + ], + $response + ); + } + + #[Route( + '/{_locale}/rss/activity/user/{userId}', + name: 'rss_activity_user', + defaults: [ + 'userId' => 0 + ], + requirements: [ + '_locale' => '%app.locales%', + 'userId' => '\d+' + ], + methods: + [ + 'GET' + ] + )] + public function rssUser( + Request $request, + UserService $userService, + ActivityService $activityService + ): Response + { + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init target user + if (!$userTarget = $userService->getUser( + $request->get('userId') ? $request->get('userId') : $user->getId() + )) + { + throw $this->createNotFoundException(); + } + + $total = $activityService->findActivitiesTotalByUserId( + $userTarget->getId(), + $user->getEvents() + ); + + $response = new Response(); + $response->headers->set('Content-Type', 'text/xml'); + + return $this->render( + 'default/activity/list.rss.twig', + [ + 'session' => [ + 'user' => $user + ], + 'activities' => $activityService->findLastActivitiesByUserId( + $userTarget->getId(), + $userTarget->getEvents() + ) + ], + $response + ); + } + + #[Route( + '/{_locale}/rss/activity/torrent/{torrentId}', + name: 'rss_activity_torrent', + requirements: [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+' + ], + methods: + [ + 'GET' + ] + )] + public function rssTorrent( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Get total activities + $total = $activityService->findActivitiesTotalByTorrentId( + $torrent->getId(), + $user->getEvents() + ); + + $response = new Response(); + $response->headers->set('Content-Type', 'text/xml'); + + return $this->render( + 'default/activity/list.rss.twig', + [ + 'session' => [ + 'user' => $user + ], + 'activities' => $activityService->findLastActivitiesByTorrentId( + $torrent->getId(), + $user->getEvents() + ) + ], + $response + ); + } + + public function event( + \App\Entity\User $user, + \App\Entity\Activity $activity, + ActivityService $activityService, + UserService $userService, + TorrentService $torrentService, + ?string $format = null, + ): Response + { + switch ($format) + { + case 'rss': + + $extension = '.rss.twig'; + + break; + + default: + + $extension = '.html.twig'; + } + + switch ($activity->getEvent()) + { + // User + case $activity::EVENT_USER_ADD: + + return $this->render( + 'default/activity/event/user/add' . $extension, + [ + 'id' => $activity->getId(), + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ] + ] + ); + + break; + + case $activity::EVENT_USER_APPROVE_ADD: + + return $this->render( + 'default/activity/event/user/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_APPROVE_DELETE: + + return $this->render( + 'default/activity/event/user/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_MODERATOR_ADD: + + return $this->render( + 'default/activity/event/user/moderator/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_MODERATOR_DELETE: + + return $this->render( + 'default/activity/event/user/moderator/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_STATUS_ADD: + + return $this->render( + 'default/activity/event/user/status/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_STATUS_DELETE: + + return $this->render( + 'default/activity/event/user/status/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_STAR_ADD: + + return $this->render( + 'default/activity/event/user/star/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + case $activity::EVENT_USER_STAR_DELETE: + + return $this->render( + 'default/activity/event/user/star/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'to' => + [ + 'user' => + [ + 'id' => $activity->getData()['userId'], + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getData()['userId'] + )->getAddress() + ) + ] + ] + ] + ); + + break; + + // Torrent + case $activity::EVENT_TORRENT_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_APPROVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_APPROVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + // Torrent Download + case $activity::EVENT_TORRENT_DOWNLOAD_FILE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/download/file/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_DOWNLOAD_MAGNET_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/download/magnet/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + // Torrent Wanted + case $activity::EVENT_TORRENT_WANTED_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/wanted/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + // Torrent Status + case $activity::EVENT_TORRENT_STATUS_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/status/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_STATUS_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/status/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + /// Torrent Locales + case $activity::EVENT_TORRENT_LOCALES_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/locales/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'locales' => [ + 'id' => $activity->getData()['torrentLocalesId'], + 'exist' => $torrentService->getTorrentLocales( + $activity->getData()['torrentLocalesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_LOCALES_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/locales/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'locales' => [ + 'id' => $activity->getData()['torrentLocalesId'], + 'exist' => $torrentService->getTorrentLocales( + $activity->getData()['torrentLocalesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_LOCALES_APPROVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/locales/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'locales' => [ + 'id' => $activity->getData()['torrentLocalesId'], + 'exist' => $torrentService->getTorrentLocales( + $activity->getData()['torrentLocalesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_LOCALES_APPROVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/locales/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'locales' => [ + 'id' => $activity->getData()['torrentLocalesId'], + 'exist' => $torrentService->getTorrentLocales( + $activity->getData()['torrentLocalesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + /// Torrent Categories + case $activity::EVENT_TORRENT_CATEGORIES_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/categories/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'categories' => [ + 'id' => $activity->getData()['torrentCategoriesId'], + 'exist' => $torrentService->getTorrentCategories( + $activity->getData()['torrentCategoriesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_CATEGORIES_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/categories/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'categories' => [ + 'id' => $activity->getData()['torrentCategoriesId'], + 'exist' => $torrentService->getTorrentCategories( + $activity->getData()['torrentCategoriesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_CATEGORIES_APPROVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/categories/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'categories' => [ + 'id' => $activity->getData()['torrentCategoriesId'], + 'exist' => $torrentService->getTorrentCategories( + $activity->getData()['torrentCategoriesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_CATEGORIES_APPROVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/categories/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'categories' => [ + 'id' => $activity->getData()['torrentCategoriesId'], + 'exist' => $torrentService->getTorrentCategories( + $activity->getData()['torrentCategoriesId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + /// Torrent Sensitive + case $activity::EVENT_TORRENT_SENSITIVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/sensitive/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'sensitive' => [ + 'id' => $activity->getData()['torrentSensitiveId'], + 'exist' => $torrentService->getTorrentSensitive( + $activity->getData()['torrentSensitiveId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_SENSITIVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/sensitive/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'sensitive' => [ + 'id' => $activity->getData()['torrentSensitiveId'], + 'exist' => $torrentService->getTorrentSensitive( + $activity->getData()['torrentSensitiveId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_SENSITIVE_APPROVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/sensitive/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'sensitive' => [ + 'id' => $activity->getData()['torrentSensitiveId'], + 'exist' => $torrentService->getTorrentSensitive( + $activity->getData()['torrentSensitiveId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_SENSITIVE_APPROVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/sensitive/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'sensitive' => [ + 'id' => $activity->getData()['torrentSensitiveId'], + 'exist' => $torrentService->getTorrentSensitive( + $activity->getData()['torrentSensitiveId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + /// Torrent Poster + case $activity::EVENT_TORRENT_POSTER_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/poster/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'poster' => [ + 'id' => $activity->getData()['torrentPosterId'], + 'exist' => $torrentService->getTorrentPoster( + $activity->getData()['torrentPosterId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_POSTER_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/poster/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'poster' => [ + 'id' => $activity->getData()['torrentPosterId'], + 'exist' => $torrentService->getTorrentPoster( + $activity->getData()['torrentPosterId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_POSTER_APPROVE_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/poster/approve/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'poster' => [ + 'id' => $activity->getData()['torrentPosterId'], + 'exist' => $torrentService->getTorrentPoster( + $activity->getData()['torrentPosterId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_POSTER_APPROVE_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/poster/approve/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName(), + 'poster' => [ + 'id' => $activity->getData()['torrentPosterId'], + 'exist' => $torrentService->getTorrentPoster( + $activity->getData()['torrentPosterId'] // could be deleted by moderator, remove links + ) + ] + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + /// Torrent star + case $activity::EVENT_TORRENT_STAR_ADD: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/star/add' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + case $activity::EVENT_TORRENT_STAR_DELETE: + + // Init torrent + if (!$torrent = $torrentService->getTorrent($activity->getTorrentId())) + { + throw $this->createNotFoundException(); + } + + return $this->render( + 'default/activity/event/torrent/star/delete' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'name' => $torrentService->readTorrentFileByTorrentId( + $torrent->getId() + )->getName() + ], + 'session' => + [ + 'user' => + [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ] + ] + ] + ); + + break; + + default: + + return $this->render( + 'default/activity/event/undefined' . $extension, + [ + 'id' => $activity->getId(), + 'added' => $activity->getAdded(), + 'user' => + [ + 'id' => $activity->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $activity->getUserId() + )->getAddress() + ) + ] + ] + ); + } + } + + private function initUser( + Request $request, + UserService $userService, + ActivityService $activityService + ): ?\App\Entity\User + { + // Init user + if (!$user = $userService->findUserByAddress($request->getClientIp())) + { + $user = $userService->addUser( + $request->getClientIp(), + time(), + $this->getParameter('app.locale'), + explode('|', $this->getParameter('app.locales')), + $activityService->getEventCodes(), + $this->getParameter('app.theme'), + $this->getParameter('app.sensitive'), + $this->getParameter('app.yggdrasil'), + $this->getParameter('app.posters'), + $this->getParameter('app.approved') + ); + + // Add user join event + $activityService->addEventUserAdd( + $user->getId(), + time() + ); + } + + return $user; + } +} \ No newline at end of file diff --git a/src/Controller/SearchController.php b/src/Controller/SearchController.php new file mode 100644 index 0000000..8cb63bc --- /dev/null +++ b/src/Controller/SearchController.php @@ -0,0 +1,164 @@ +get('query') ? urldecode($request->get('query')) : ''; + $filter = $request->get('filter') ? true : false; + + // Extended search + if ($filter) + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Keywords + $keywords = explode(' ', $query); + + // Locales + foreach (explode('|', $this->getParameter('app.locales')) as $locale) + { + + if ($request->get('locales')) + { + $checked = in_array($locale, (array) $request->get('locales')); + } + + else + { + $checked = in_array($locale, $user->getLocales()); + } + + $locales[] = + [ + 'value' => $locale, + 'checked' => $checked, + 'total' => $torrentService->findTorrentsTotal( + 0, + $keywords, + [$locale], + $request->get('categories') ? $request->get('categories') : $user->getCategories(), + $request->get('sensitive') ? null : false, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ) + ]; + } + + // Categories + foreach (explode('|', $this->getParameter('app.categories')) as $category) + { + if ($request->get('categories')) + { + $checked = in_array($category, (array) $request->get('categories')); + } + + else + { + $checked = in_array($category, $user->getCategories()); + } + + $categories[] = + [ + 'value' => $category, + 'checked' => $checked, + 'total' => $torrentService->findTorrentsTotal( + 0, + $keywords, + $request->get('locales') ? $request->get('locales') : $user->getLocales(), + [$category], + $request->get('sensitive') ? null : false, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ) + ]; + } + + // Sensitive + $sensitive = + [ + 'checked' => $request->get('sensitive'), + 'total' => $torrentService->findTorrentsTotal( + 0, + $keywords, + $request->get('locales') ? $request->get('locales') : $user->getLocales(), + $request->get('categories') ? $request->get('categories') : $user->getCategories(), + true, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ) + ]; + } + + return $this->render( + 'default/search/module.html.twig', + [ + 'query' => $query, + 'filter' => $filter, + 'sensitive' => $sensitive, + 'locales' => $locales, + 'categories' => $categories, + ] + ); + } + + private function initUser( + Request $request, + UserService $userService, + ActivityService $activityService + ): ?\App\Entity\User + { + // Init user + if (!$user = $userService->findUserByAddress($request->getClientIp())) + { + $user = $userService->addUser( + $request->getClientIp(), + time(), + $this->getParameter('app.locale'), + explode('|', $this->getParameter('app.locales')), + $activityService->getEventCodes(), + $this->getParameter('app.theme'), + $this->getParameter('app.sensitive'), + $this->getParameter('app.yggdrasil'), + $this->getParameter('app.posters'), + $this->getParameter('app.approved') + ); + + // Add user join event + $activityService->addEventUserAdd( + $user->getId(), + time() + ); + } + + return $user; + } +} \ No newline at end of file diff --git a/src/Controller/TorrentController.php b/src/Controller/TorrentController.php new file mode 100644 index 0000000..38205db --- /dev/null +++ b/src/Controller/TorrentController.php @@ -0,0 +1,3486 @@ + '%app.locales%', + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function info( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Read file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); + } + + // Sensitive filter + // @TODO add blur effect or sensitive notice instead of 404 on direct request #37 + /* + if (!$user->isModerator() && $user->isSensitive() && $torrent->isSensitive()) + { + throw $this->createNotFoundException(); + } + */ + + // Access filter + if (!$user->isModerator() && $user->getId() != $torrent->getUserId() && + (!$torrent->isStatus() || !$torrent->isApproved())) + { + throw $this->createNotFoundException(); + } + + // Get contributors + $contributors = []; + foreach ($torrentService->getTorrentContributors($torrent) as $userId) + { + $contributors[$userId] = $userService->identicon( + $userService->getUser( + $userId + )->getAddress() + ); + } + + // Get total activities + $total = $activityService->findActivitiesTotalByTorrentId( + $torrent->getId(), + $user->getEvents() + ); + + // Create trackers list + $appTrackers = explode('|', $this->getParameter('app.trackers')); + $allTrackers = []; + + foreach ($appTrackers as $tracker) + { + $allTrackers[$tracker] = true; + } + + foreach ($file->getAnnounceList() as $announce) + { + foreach ($announce as $tracker) + { + $allTrackers[$tracker] = !($user->isYggdrasil() && !in_array($tracker, $appTrackers)); + } + } + + // Init page + $page = $request->get('page') ? (int) $request->get('page') : 1; + + // Poster + if ($user->isPosters() && $torrent->getTorrentPosterId()) + { + $torrentPoster = $torrentService->getTorrentPoster( + $torrent->getTorrentPosterId() + ); + + $poster = [ + 'position' => $torrentPoster->getPosition(), + 'url' => $request->getScheme() . '://' . + $request->getHttpHost() . + $request->getBasePath() . + $torrentService->getImageUriByTorrentPosterId( + $torrentPoster->getId() + ) + ]; + } + + else + { + $poster = false; + } + + // Render template + return $this->render('default/torrent/info.html.twig', + [ + 'session' => + [ + 'user' => $user, + 'id' => $user->getId(), + 'moderator' => $user->isModerator(), + 'owner' => $user->getId() === $torrent->getUserId(), + ], + 'torrent' => + [ + 'id' => $torrent->getId(), + 'md5file' => $torrent->getMd5File(), + 'added' => $torrent->getAdded(), + 'scrape' => + [ + 'seeders' => (int) $torrent->getSeeders(), + 'peers' => (int) $torrent->getPeers(), + 'leechers' => (int) $torrent->getLeechers(), + ], + 'keywords' => $torrent->getKeywords(), + 'locales' => $torrent->getLocales(), + 'categories' => $torrent->getCategories(), + 'sensitive' => $torrent->isSensitive(), + 'approved' => $torrent->isApproved(), + 'status' => $torrent->isStatus(), + 'download' => + [ + 'file' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadFile( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadFilesTotalByTorrentId( + $torrent->getId() + ) + ], + 'magnet' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadMagnet( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadMagnetsTotalByTorrentId( + $torrent->getId() + ) + ] + ], + 'star' => + [ + 'exist' => (bool) $torrentService->findTorrentStar( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentStarsTotalByTorrentId( + $torrent->getId() + ) + ], + 'contributors' => $contributors, + 'poster' => $poster + ], + 'file' => + [ + 'name' => $file->getName(), + 'size' => $file->getSize(), + 'count' => $file->getFileCount(), + 'pieces' => $file->getPieceLength(), + 'created' => $file->getCreationDate(), + 'software' => $file->getCreatedBy(), + 'protocol' => $file->getProtocol(), + 'private' => $file->isPrivate(), + 'source' => $file->getSource(), + 'comment' => $file->getComment(), + 'tree' => $file->getFileTree( + \Rhilip\Bencode\TorrentFile::FILETREE_SORT_NATURAL // #34 @TODO cyrillic + ), + //'trackers' => $file->getAnnounceList(), + 'hash' => + [ + 'v1' => $file->getInfoHashV1(false), + 'v2' => $file->getInfoHashV2(false) + ], + ], + 'trackers' => $allTrackers, + 'activities' => $activityService->findLastActivitiesByTorrentId( + $torrent->getId(), + $user->getEvents(), + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ), + 'pagination' => + [ + 'page' => $page, + 'pages' => ceil($total / $this->getParameter('app.pagination')), + 'total' => $total + ] + ]); + } + + #[Route( + '/{_locale}/search', + name: 'torrent_search', + requirements: + [ + '_locale' => '%app.locales%' + ], + methods: + [ + 'GET' + ] + )] + public function search( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init request + $query = $request->get('query') ? explode(' ', urldecode($request->get('query'))) : []; + $page = $request->get('page') ? (int) $request->get('page') : 1; + + // Get total torrents + $total = $torrentService->findTorrentsTotal( + $request->get('filter') ? 0 : $user->getId(), + $query, + $request->get('filter') ? (array) $request->get('locales') : $user->getLocales(), + $request->get('filter') ? (array) $request->get('categories') : $user->getCategories(), + $request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null), + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ); + + $torrents = []; + foreach ($torrentService->findTorrents( + $request->get('filter') ? 0 : $user->getId(), + $query, + $request->get('filter') ? (array) $request->get('locales') : $user->getLocales(), + $request->get('filter') ? (array) $request->get('categories') : $user->getCategories(), + $request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null), + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ) as $torrent) + { + // Read file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); // @TODO exception + } + + // Generate keywords by extension + $keywords = []; + + foreach ($file->getFileList() as $item) + { + if ($keyword = pathinfo($item['path'], PATHINFO_EXTENSION)) + { + $keyword = mb_strtolower($keyword); + + if (isset($keywords[$keyword])) + { + $keywords[$keyword] = $keywords[$keyword] + (int) $item['size']; + } + + else + { + $keywords[$keyword] = 0; + } + } + } + + arsort($keywords); + + // Poster + if ($user->isPosters() && $torrent->getTorrentPosterId()) + { + $torrentPoster = $torrentService->getTorrentPoster( + $torrent->getTorrentPosterId() + ); + + $poster = [ + 'position' => $torrentPoster->getPosition(), + 'url' => $request->getScheme() . '://' . + $request->getHttpHost() . + $request->getBasePath() . + $torrentService->getImageUriByTorrentPosterId( + $torrentPoster->getId() + ) + ]; + } + + else + { + $poster = false; + } + + // Push torrent + $torrents[] = + [ + 'id' => $torrent->getId(), + 'added' => $torrent->getAdded(), + 'approved' => $torrent->isApproved(), + 'sensitive' => $torrent->isSensitive(), + 'status' => $torrent->isStatus(), + 'file' => + [ + 'name' => $file->getName(), + 'size' => $file->getSize(), + 'hash' => + [ + 'v1' => $file->getInfoHashV1(false), + 'v2' => $file->getInfoHashV2(false) + ], + ], + 'scrape' => + [ + 'seeders' => (int) $torrent->getSeeders(), + 'peers' => (int) $torrent->getPeers(), + 'leechers' => (int) $torrent->getLeechers(), + ], + 'user' => + [ + 'id' => $torrent->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrent->getUserId() + )->getAddress() + ) + ], + 'keywords' => $keywords, + 'download' => + [ + 'file' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadFile( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadFilesTotalByTorrentId( + $torrent->getId() + ) + ], + 'magnet' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadMagnet( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadMagnetsTotalByTorrentId( + $torrent->getId() + ) + ] + ], + 'star' => + [ + 'exist' => (bool) $torrentService->findTorrentStar( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentStarsTotalByTorrentId( + $torrent->getId() + ) + ], + 'poster' => $poster + ]; + } + + return $this->render('default/torrent/list.html.twig', [ + 'query' => $request->get('query') ? urldecode($request->get('query')) : '', + 'torrents' => $torrents, + 'pagination' => + [ + 'page' => $page, + 'pages' => ceil($total / $this->getParameter('app.pagination')), + 'total' => $total + ] + ]); + } + + #[Route( + '/{_locale}', + name: 'torrent_recent', + requirements: + [ + '_locale' => '%app.locales%' + ], + methods: + [ + 'GET' + ] + )] + public function recent( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init page + $page = $request->get('page') ? (int) $request->get('page') : 1; + + // Get total torrents + $total = $torrentService->findTorrentsTotal( + $user->getId(), + [], + $user->getLocales(), + $user->getCategories(), + $user->isSensitive() ? false : null, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ); + + // Create torrents list + $torrents = []; + foreach ($torrentService->findTorrents( + $user->getId(), + [], + $user->getLocales(), + $user->getCategories(), + $user->isSensitive() ? false : null, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ) as $torrent) + { + // Read file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); // @TODO exception + } + + // Generate keywords by extension + $keywords = []; + + foreach ($file->getFileList() as $item) + { + if ($keyword = pathinfo($item['path'], PATHINFO_EXTENSION)) + { + $keyword = mb_strtolower($keyword); + + if (isset($keywords[$keyword])) + { + $keywords[$keyword] = $keywords[$keyword] + (int) $item['size']; + } + + else + { + $keywords[$keyword] = 0; + } + } + } + + arsort($keywords); + + // Poster + if ($user->isPosters() && $torrent->getTorrentPosterId()) + { + $torrentPoster = $torrentService->getTorrentPoster( + $torrent->getTorrentPosterId() + ); + + $poster = [ + 'position' => $torrentPoster->getPosition(), + 'url' => $request->getScheme() . '://' . + $request->getHttpHost() . + $request->getBasePath() . + $torrentService->getImageUriByTorrentPosterId( + $torrentPoster->getId() + ) + ]; + } + + else + { + $poster = false; + } + + // Push torrent + $torrents[] = + [ + 'id' => $torrent->getId(), + 'added' => $torrent->getAdded(), + 'approved' => $torrent->isApproved(), + 'sensitive' => $torrent->isSensitive(), + 'status' => $torrent->isStatus(), + 'file' => + [ + 'name' => $file->getName(), + 'size' => $file->getSize(), + 'hash' => + [ + 'v1' => $file->getInfoHashV1(false), + 'v2' => $file->getInfoHashV2(false) + ], + ], + 'scrape' => + [ + 'seeders' => (int) $torrent->getSeeders(), + 'peers' => (int) $torrent->getPeers(), + 'leechers' => (int) $torrent->getLeechers(), + ], + 'keywords' => $keywords, + 'download' => + [ + 'file' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadFile( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadFilesTotalByTorrentId( + $torrent->getId() + ) + ], + 'magnet' => + [ + 'exist' => (bool) $torrentService->findTorrentDownloadMagnet( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentDownloadMagnetsTotalByTorrentId( + $torrent->getId() + ) + ] + ], + 'star' => + [ + 'exist' => (bool) $torrentService->findTorrentStar( + $torrent->getId(), + $user->getId() + ), + 'total' => $torrentService->findTorrentStarsTotalByTorrentId( + $torrent->getId() + ) + ], + 'poster' => $poster + ]; + } + + return $this->render('default/torrent/list.html.twig', [ + 'torrents' => $torrents, + 'pagination' => + [ + 'page' => $page, + 'pages' => ceil($total / $this->getParameter('app.pagination')), + 'total' => $total + ] + ]); + } + + #[Route( + '/{_locale}/rss/torrents', + name: 'rss_torrents_recent', + requirements: [ + '_locale' => '%app.locales%' + ], + methods: + [ + 'GET' + ] + )] + public function rssRecent( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init request + $query = $request->get('query') ? explode(' ', urldecode($request->get('query'))) : []; + $page = $request->get('page') ? (int) $request->get('page') : 1; + + // Get total torrents + $total = $torrentService->findTorrentsTotal( + $request->get('filter') ? 0 : $user->getId(), + $query, + $request->get('filter') ? (array) $request->get('locales') : $user->getLocales(), + $request->get('filter') ? (array) $request->get('categories') : $user->getCategories(), + $request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null), + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ); + + // Create torrents list + $torrents = []; + foreach ($torrentService->findTorrents( + $request->get('filter') ? 0 : $user->getId(), + $query, + $request->get('filter') ? (array) $request->get('locales') : $user->getLocales(), + $request->get('filter') ? (array) $request->get('categories') : $user->getCategories(), + $request->get('filter') ? ($request->get('sensitive') ? null : false) : ($user->isSensitive() ? false : null), + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ) as $torrent) + { + // Read file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); // @TODO exception + } + + $torrents[] = + [ + 'id' => $torrent->getId(), + 'added' => $torrent->getAdded(), + 'file' => + [ + 'name' => $file->getName(), + ], + 'user' => + [ + 'id' => $torrent->getUserId(), + ], + ]; + } + + $response = new Response(); + $response->headers->set('Content-Type', 'text/xml'); + + return $this->render( + 'default/torrent/list.rss.twig', + [ + 'torrents' => $torrents + ], + $response + ); + } + + // #25 + // https://github.com/YGGverse/YGGtracker/issues/25 + #[Route( + '/api/torrents', + methods: + [ + 'GET' + ] + )] + public function jsonRecent( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init request + $query = $request->get('query') ? + explode(' ', urldecode($request->get('query'))) : []; + + $page = $request->get('page') ? + (int) $request->get('page') : 1; + + $filter = $request->get('filter') ? + true : false; + + if ($request->get('locales')) + { + $locales = explode('|', $request->get('locales')); + } + + else + { + $locales = $user->getLocales(); + } + + if ($request->get('categories')) + { + $categories = explode('|', $request->get('categories')); + } + + else + { + $categories = $user->getCategories(); + } + + switch ($request->get('sensitive')) + { + case 'true': + $sensitive = true; + break; + case 'false': + $sensitive = false; + break; + default: + $sensitive = $user->isSensitive() ? false : null; + } + + switch ($request->get('yggdrasil')) + { + case 'true': + $yggdrasil = true; + break; + case 'false': + $yggdrasil = false; + break; + default: + $yggdrasil = $user->isYggdrasil(); + } + + // Init trackers + $trackers = explode('|', $this->getParameter('app.trackers')); + + // Get total torrents + $total = $torrentService->findTorrentsTotal( + $filter ? 0 : $user->getId(), + $query, + $locales, + $categories, + $sensitive, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + ); + + // Create torrents list + $torrents = []; + foreach ($torrentService->findTorrents( + $filter ? 0 : $user->getId(), + $query, + $locales, + $categories, + $sensitive, + !$user->isModerator() ? true : null, + !$user->isModerator() ? true : null, + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ) as $torrent) + { + // Read file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); // @TODO exception + } + + // Apply yggdrasil filters + $file = $this->filterYggdrasil($file, $yggdrasil); + + $torrents[] = + [ + 'torrent' => + [ + 'id' => $torrent->getId(), + 'added' => $torrent->getAdded(), + 'locales' => $torrent->getLocales(), + 'categories' => $torrent->getCategories(), + 'sensitive' => $torrent->isSensitive(), + 'file' => + [ + 'name' => $file->getName(), + 'size' => $file->getSize(), + 'url' => $this->generateUrl( + 'torrent_file', + [ + 'torrentId' => $torrent->getId() + ], + false + ) + ], + 'magnet' => + [ + 'url' => $this->generateUrl( + 'torrent_magnet', + [ + 'torrentId' => $torrent->getId() + ], + false + ), + //'urn' => $file->getMagnetLink() + ], + 'scrape' => + [ + 'seeders' => (int) $torrent->getSeeders(), + 'peers' => (int) $torrent->getPeers(), + 'leechers' => (int) $torrent->getLeechers(), + ], + 'url' => $this->generateUrl( + 'torrent_info', + [ + '_locale' => $user->getLocale(), + 'torrentId' => $torrent->getId(), + ], + false + ) + ], + ]; + } + + return $this->json( + [ + 'version' => time(), + 'tracker' => + [ + 'name' => $this->getParameter('app.name'), + 'version' => $this->getParameter('app.version'), + 'url' => $this->generateUrl( + 'torrent_recent', + [ + '_locale' => $user->getLocale() + ], + false + ) + ], + 'torrents' => $torrents + ] + ); + } + + // Forms + #[Route( + '/{_locale}/submit', + name: 'torrent_submit', + requirements: + [ + '_locale' => '%app.locales%' + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function submit( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init form + $form = + [ + 'locales' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('locales') ? $request->get('locales') : [$request->get('_locale')], + ] + ], + 'categories' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('categories') ? $request->get('categories') : [], + ] + ], + 'torrent' => + [ + 'error' => [], + ], + 'sensitive' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('sensitive'), + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + /// Locales + $locales = []; + if ($request->get('locales')) + { + foreach ((array) $request->get('locales') as $locale) + { + if (in_array($locale, explode('|', $this->getParameter('app.locales')))) + { + $locales[] = $locale; + } + } + } + + //// At least one valid locale required + if (!$locales) + { + $form['locales']['error'][] = $translator->trans('At least one locale required'); + } + + /// Categories + $categories = []; + if ($request->get('categories')) + { + foreach ((array) $request->get('categories') as $locale) + { + if (in_array($locale, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $locale; + } + } + } + + //// At least one valid locale required + if (!$categories) + { + $form['categories']['error'][] = $translator->trans('At least one category required'); + } + + /// Torrent + if ($file = $request->files->get('torrent')) + { + //// Validate torrent file + if (filesize($file->getPathName()) > $this->getParameter('app.torrent.size.max')) + { + $form['torrent']['error'][] = $translator->trans('Torrent file out of size limit'); + } + + //// Check for duplicates + if ($torrentService->findTorrentByMd5File(md5_file($file->getPathName()))) + { + $form['torrent']['error'][] = $translator->trans('Torrent file already exists'); + } + + //// Validate torrent format + if (!$torrentService->readTorrentFileByFilepath($file->getPathName())) + { + $form['torrent']['error'][] = $translator->trans('Could not parse torrent file'); + } + } + + else + { + $form['torrent']['error'][] = $translator->trans('Torrent file required'); + } + + // Request is valid + if (empty($form['torrent']['error']) && empty($form['locales']['error']) && empty($form['categories']['error'])) + { + // Save data + $torrent = $torrentService->add( + + $file->getPathName(), + + (bool) $this->getParameter('app.index.torrent.name.enabled'), + (bool) $this->getParameter('app.index.torrent.filenames.enabled'), + (bool) $this->getParameter('app.index.torrent.hash.v1.enabled'), + (bool) $this->getParameter('app.index.torrent.hash.v2.enabled'), + (bool) $this->getParameter('app.index.torrent.source.enabled'), + (bool) $this->getParameter('app.index.torrent.comment.enabled'), + (int) $this->getParameter('app.index.word.length.min'), + (int) $this->getParameter('app.index.word.length.max'), + + $user->getId(), + time(), + (array) $locales, + (array) $categories, + (bool) $request->get('sensitive'), + $user->isApproved(), + $user->isStatus() + ); + + // Add activity event + $activityService->addEventTorrentAdd( + $user->getId(), + time(), + $torrent->getId() + ); + + // Redirect to info page + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + } + + // Render form template + return $this->render( + 'default/torrent/submit.html.twig', + [ + 'locales' => explode('|', $this->getParameter('app.locales')), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'form' => $form, + ] + ); + } + + // Torrent moderation + #[Route( + '/{_locale}/torrent/{torrentId}/approve/toggle', + name: 'torrent_approve_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approve( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Register activity event + if (!$torrent->isApproved()) + { + $activityService->addEventTorrentApproveAdd( + $user->getId(), + $torrent->getId(), + time() + ); + } + + else + { + $activityService->addEventTorrentApproveDelete( + $user->getId(), + $torrent->getId(), + time() + ); + } + + // Update approved + $torrentService->toggleTorrentApproved( + $torrent->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/status/toggle', + name: 'torrent_status_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function status( + Request $request, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() == $torrent->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Register activity event + if (!$torrent->isStatus()) + { + $activityService->addEventTorrentStatusAdd( + $user->getId(), + $torrent->getId(), + time() + ); + } + + else + { + $activityService->addEventTorrentStatusDelete( + $user->getId(), + $torrent->getId(), + time() + ); + } + + // Update status + $torrentService->toggleTorrentStatus( + $torrent->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + + // Torrent locales + #[Route( + '/{_locale}/torrent/{torrentId}/edit/locales/{torrentLocalesId}', + name: 'torrent_locales_edit', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentLocalesId' => '\d+', + ], + defaults: + [ + 'torrentLocalesId' => null, + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function editLocales( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent locales + $torrentLocalesCurrent = [ + 'userId' => null, + 'value' => [] + ]; + + // Get from edition version requested + if ($request->get('torrentLocalesId')) + { + if ($torrentLocales = $torrentService->getTorrentLocales($request->get('torrentLocalesId'))) + { + $torrentLocalesCurrent['userId'] = $torrentLocales->getUserId(); + + foreach ($torrentLocales->getValue() as $value) + { + $torrentLocalesCurrent['value'][] = $value; + } + } + + else + { + throw $this->createNotFoundException(); + } + } + + // Otherwise, get latest available + else + { + if ($torrentLocales = $torrentService->findLastTorrentLocalesByTorrentId($torrent->getId())) + { + $torrentLocalesCurrent['userId'] = $torrentLocales->getUserId(); + + foreach ($torrentLocales->getValue() as $value) + { + $torrentLocalesCurrent['value'][] = $value; + } + + // Update active locale + $request->attributes->set('torrentLocalesId', $torrentLocales->getId()); + } + + else + { + $torrentLocalesCurrent['value'][] = $request->get('_locale'); + } + } + + // Init edition history + $editions = []; + foreach ($torrentService->findTorrentLocalesByTorrentId($torrent->getId()) as $torrentLocalesEdition) + { + $editions[] = + [ + 'id' => $torrentLocalesEdition->getId(), + 'added' => $torrentLocalesEdition->getAdded(), + 'approved' => $torrentLocalesEdition->isApproved(), + 'active' => $torrentLocalesEdition->getId() == $request->get('torrentLocalesId'), + 'user' => + [ + 'id' => $torrentLocalesEdition->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrentLocalesEdition->getUserId() + )->getAddress() + ), + ] + ]; + } + + // Init form + $form = + [ + 'locales' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('locales') ? $request->get('locales') : $torrentLocalesCurrent['value'], + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + /// Locales + $locales = []; + if ($request->get('locales')) + { + foreach ((array) $request->get('locales') as $locale) + { + if (in_array($locale, explode('|', $this->getParameter('app.locales')))) + { + $locales[] = $locale; + } + } + } + + //// At least one valid locale required + if (!$locales) + { + $form['locales']['error'][] = $translator->trans('At least one locale required'); + } + + // Request is valid + if (empty($form['locales']['error'])) + { + // Save data + $torrentLocales = $torrentService->addTorrentLocales( + $torrent->getId(), + $user->getId(), + time(), + $locales, + $user->isApproved() + ); + + // Register activity event + $activityService->addEventTorrentLocalesAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentLocales->getId() + ); + + // Redirect to info page + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + } + + // Render form template + return $this->render( + 'default/torrent/edit/locales.html.twig', + [ + 'torrentId' => $torrent->getId(), + 'locales' => explode('|', $this->getParameter('app.locales')), + 'editions' => $editions, + 'form' => $form, + 'session' => + [ + 'moderator' => $user->isModerator(), + 'owner' => $torrentLocalesCurrent['userId'] === $user->getId(), + ] + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/approve/locales/{torrentLocalesId}', + name: 'torrent_locales_approve', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentLocalesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approveLocales( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent locales + if (!$torrentLocales = $torrentService->getTorrentLocales($request->get('torrentLocalesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Register activity event + if (!$torrentLocales->isApproved()) + { + $activityService->addEventTorrentLocalesApproveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentLocales->getId() + ); + } + + else + { + $activityService->addEventTorrentLocalesApproveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentLocales->getId() + ); + } + + // Update approved + $torrentService->toggleTorrentLocalesApproved( + $torrentLocales->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_locales_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentLocalesId' => $torrentLocales->getId(), + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/delete/locales/{torrentLocalesId}', + name: 'torrent_locales_delete', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentLocalesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function deleteLocales( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent locales + if (!$torrentLocales = $torrentService->getTorrentLocales($request->get('torrentLocalesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() === $torrentLocales->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + $activityService->addEventTorrentLocalesDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentLocales->getId() + ); + + // Update approved + $torrentService->deleteTorrentLocales( + $torrentLocales->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_locales_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentLocalesId' => $torrentLocales->getId(), + ] + ); + } + + // Torrent categories + #[Route( + '/{_locale}/torrent/{torrentId}/edit/categories/{torrentCategoriesId}', + name: 'torrent_categories_edit', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + defaults: + [ + 'torrentCategoriesId' => null, + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function editCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + $torrentCategoriesCurrent = [ + 'userId' => null, + 'value' => [] + ]; + + // Get from edition version requested + if ($request->get('torrentCategoriesId')) + { + if ($torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + $torrentCategoriesCurrent['userId'] = $torrentCategories->getUserId(); + + foreach ($torrentCategories->getValue() as $value) + { + $torrentCategoriesCurrent['value'][] = $value; + } + } + + else + { + throw $this->createNotFoundException(); + } + } + + // Otherwise, get latest available + else + { + if ($torrentCategories = $torrentService->findLastTorrentCategoriesByTorrentId($torrent->getId())) + { + $torrentCategoriesCurrent['userId'] = $torrentCategories->getUserId(); + + foreach ($torrentCategories->getValue() as $value) + { + $torrentCategoriesCurrent['value'][] = $value; + } + + // Update active categories + $request->attributes->set('torrentCategoriesId', $torrentCategories->getId()); + } + } + + // Init edition history + $editions = []; + foreach ($torrentService->findTorrentCategoriesByTorrentId($torrent->getId()) as $torrentCategoriesEdition) + { + $editions[] = + [ + 'id' => $torrentCategoriesEdition->getId(), + 'added' => $torrentCategoriesEdition->getAdded(), + 'approved' => $torrentCategoriesEdition->isApproved(), + 'active' => $torrentCategoriesEdition->getId() == $request->get('torrentCategoriesId'), + 'user' => + [ + 'id' => $torrentCategoriesEdition->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrentCategoriesEdition->getUserId() + )->getAddress() + ), + ] + ]; + } + + // Init form + $form = + [ + 'categories' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $request->get('categories') ? $request->get('categories') : $torrentCategoriesCurrent['value'], + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + /// Categories + $categories = []; + if ($request->get('categories')) + { + foreach ((array) $request->get('categories') as $category) + { + if (in_array($category, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $category; + } + } + } + + //// At least one valid category required + if (!$categories) + { + $form['categories']['error'][] = $translator->trans('At least one category required'); + } + + // Request is valid + if (empty($form['categories']['error'])) + { + // Save data + $torrentCategories = $torrentService->addTorrentCategories( + $torrent->getId(), + $user->getId(), + time(), + $categories, + $user->isApproved() + ); + + // Register activity event + $activityService->addEventTorrentCategoriesAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + + // Redirect to info page + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + } + + // Render form template + return $this->render( + 'default/torrent/edit/categories.html.twig', + [ + 'torrentId' => $torrent->getId(), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'editions' => $editions, + 'form' => $form, + 'session' => + [ + 'moderator' => $user->isModerator(), + 'owner' => $torrentCategoriesCurrent['userId'] === $user->getId(), + ] + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/approve/categories/{torrentCategoriesId}', + name: 'torrent_categories_approve', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approveCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + if (!$torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Register activity event + if (!$torrentCategories->isApproved()) + { + $activityService->addEventTorrentCategoriesApproveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + } + + else + { + $activityService->addEventTorrentCategoriesApproveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + } + + // Update approved + $torrentService->toggleTorrentCategoriesApproved( + $torrentCategories->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_categories_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentCategoriesId' => $torrentCategories->getId(), + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/delete/categories/{torrentCategoriesId}', + name: 'torrent_categories_delete', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentCategoriesId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function deleteCategories( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent categories + if (!$torrentCategories = $torrentService->getTorrentCategories($request->get('torrentCategoriesId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() === $torrentCategories->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + $activityService->addEventTorrentCategoriesDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentCategories->getId() + ); + + // Update approved + $torrentService->deleteTorrentCategories( + $torrentCategories->getId() + ); + + // Redirect back to form + return $this->redirectToRoute( + 'torrent_categories_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentCategoriesId' => $torrentCategories->getId(), + ] + ); + } + + // Torrent sensitive + #[Route( + '/{_locale}/torrent/{torrentId}/edit/sensitive/{torrentSensitiveId}', + name: 'torrent_sensitive_edit', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentSensitiveId' => '\d+', + ], + defaults: + [ + 'torrentSensitiveId' => null, + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function editSensitive( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init sensitive value + if ($request->get('torrentSensitiveId')) + { + if ($torrentSensitive = $torrentService->getTorrentSensitive($request->get('torrentSensitiveId'))) + { + $torrentSensitiveCurrent = + [ + 'id' => $torrentSensitive->getId(), + 'userId' => $torrentSensitive->getUserId(), + 'value' => $torrentSensitive->isValue(), + ]; + } + + else + { + throw $this->createNotFoundException(); + } + } + else + { + if ($torrentSensitive = $torrentService->findLastTorrentSensitiveByTorrentId($torrent->getId())) + { + $torrentSensitiveCurrent = + [ + 'id' => $torrentSensitive->getId(), + 'userId' => $torrentSensitive->getUserId(), + 'value' => $torrentSensitive->isValue(), + ]; + } + + else + { + $torrentSensitiveCurrent = + [ + 'id' => null, + 'userId' => null, + 'value' => false, + ]; + } + } + + // Init edition history + $editions = []; + foreach ($torrentService->findTorrentSensitiveByTorrentId($torrent->getId()) as $torrentSensitiveEdition) + { + $editions[] = + [ + 'id' => $torrentSensitiveEdition->getId(), + 'added' => $torrentSensitiveEdition->getAdded(), + 'approved' => $torrentSensitiveEdition->isApproved(), + 'active' => $torrentSensitiveEdition->getId() == $torrentSensitiveCurrent['id'], + 'user' => + [ + 'id' => $torrentSensitiveEdition->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrentSensitiveEdition->getUserId() + )->getAddress() + ), + ] + ]; + } + + // Init form + $form = + [ + 'sensitive' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $torrentSensitiveCurrent['value'], + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + // Save data + $torrentSensitive = $torrentService->addTorrentSensitive( + $torrent->getId(), + $user->getId(), + time(), + $request->get('sensitive') === 'true', + $user->isApproved() + ); + + // Add activity event + $activityService->addEventTorrentSensitiveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentSensitive->getId() + ); + + // Redirect to info page created + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + + // Render form template + return $this->render( + 'default/torrent/edit/sensitive.html.twig', + [ + 'torrentId' => $torrent->getId(), + 'editions' => $editions, + 'form' => $form, + 'session' => + [ + 'moderator' => $user->isModerator(), + 'owner' => $torrentSensitiveCurrent['userId'] === $user->getId(), + ] + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/approve/sensitive/{torrentSensitiveId}', + name: 'torrent_sensitive_approve', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentSensitiveId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approveSensitive( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent sensitive + if (!$torrentSensitive = $torrentService->getTorrentSensitive($request->get('torrentSensitiveId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + if (!$torrentSensitive->isApproved()) + { + $activityService->addEventTorrentSensitiveApproveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentSensitive->getId() + ); + } + + else + { + $activityService->addEventTorrentSensitiveApproveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentSensitive->getId() + ); + } + + // Update approved + $torrentService->toggleTorrentSensitiveApproved( + $torrentSensitive->getId() + ); + + // Redirect + return $this->redirectToRoute( + 'torrent_sensitive_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentSensitiveId' => $torrentSensitive->getId(), + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/delete/sensitive/{torrentSensitiveId}', + name: 'torrent_sensitive_delete', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentSensitiveId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function deleteSensitive( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent sensitive + if (!$torrentSensitive = $torrentService->getTorrentSensitive($request->get('torrentSensitiveId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() === $torrentSensitive->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + $activityService->addEventTorrentSensitiveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentSensitive->getId() + ); + + // Update approved + $torrentService->deleteTorrentSensitive( + $torrentSensitive->getId() + ); + + // Redirect + return $this->redirectToRoute( + 'torrent_sensitive_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentSensitiveId' => $torrentSensitive->getId(), + ] + ); + } + + // Torrent poster + #[Route( + '/{_locale}/torrent/{torrentId}/edit/poster/{torrentPosterId}', + name: 'torrent_poster_edit', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentPosterId' => '\d+', + ], + defaults: + [ + 'torrentPosterId' => null, + ], + methods: + [ + 'GET', + 'POST' + ] + )] + public function editPoster( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init poster value + if ($request->get('torrentPosterId')) + { + if ($torrentPoster = $torrentService->getTorrentPoster($request->get('torrentPosterId'))) + { + $torrentPosterCurrent = + [ + 'id' => $torrentPoster->getId(), + 'userId' => $torrentPoster->getUserId(), + 'value' => 'src' // @TODO + ]; + } + + else + { + throw $this->createNotFoundException(); + } + } + else + { + if ($torrentPoster = $torrentService->findLastTorrentPosterByTorrentId($torrent->getId())) + { + $torrentPosterCurrent = + [ + 'id' => $torrentPoster->getId(), + 'userId' => $torrentPoster->getUserId(), + 'value' => 'src' // @TODO + ]; + } + + else + { + $torrentPosterCurrent = + [ + 'id' => null, + 'userId' => null, + 'value' => false, + ]; + } + } + + // Init position + $position = in_array( + $request->get('position'), + [ + 'center', + 'top', + 'bottom' + ] + ) ? $request->get('position') : 'center'; + + // Init edition history + $editions = []; + foreach ($torrentService->findTorrentPosterByTorrentId($torrent->getId()) as $torrentPosterEdition) + { + $editions[] = + [ + 'id' => $torrentPosterEdition->getId(), + 'added' => $torrentPosterEdition->getAdded(), + 'position' => $torrentPosterEdition->getPosition(), + 'approved' => $torrentPosterEdition->isApproved(), + 'active' => $torrentPosterEdition->getId() == $torrentPosterCurrent['id'], + 'user' => + [ + 'id' => $torrentPosterEdition->getUserId(), + 'identicon' => $userService->identicon( + $userService->getUser( + $torrentPosterEdition->getUserId() + )->getAddress() + ), + ], + 'poster' => + $request->getScheme() . '://' . + $request->getHttpHost() . + $request->getBasePath() . + $torrentService->getImageUriByTorrentPosterId( + $torrentPosterEdition->getId() + ) + ]; + } + + // Init form + $form = + [ + 'poster' => + [ + 'error' => [] + ], + 'position' => + [ + 'error' => [], + 'attribute' => + [ + 'value' => $position + ] + ] + ]; + + // Process request + if ($request->isMethod('post')) + { + if ($request->get('id') && $torrentService->getTorrentPoster($request->get('id'))) + { + $filename = $torrentService->getStorageFilepathByTorrentPosterId( + $request->get('id') + ); + } + + else if ($file = $request->files->get('poster')) + { + //// Validate poster file + if (filesize($file->getPathName()) > $this->getParameter('app.torrent.poster.size.max')) + { + $form['poster']['error'][] = $translator->trans('Poster file out of size limit'); + } + + //// Validate image format + if (!@getimagesize($file->getPathName())) + { + $form['poster']['error'][] = $translator->trans('Image file not supported'); + } + + $filename = $file->getPathName(); + } + + else + { + $form['poster']['error'][] = $translator->trans('Poster file required'); + + $filename = false; + } + + // Request is valid + if (empty($form['poster']['error'])) + { + // Save data + $torrentPoster = $torrentService->addTorrentPoster( + $filename, + $position, + $torrent->getId(), + $user->getId(), + time(), + $user->isApproved() + ); + + // Add activity event + $activityService->addEventTorrentPosterAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentPoster->getId() + ); + + // Redirect to info page created + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + } + + // Render form template + return $this->render( + 'default/torrent/edit/poster.html.twig', + [ + 'torrentId' => $torrent->getId(), + 'editions' => $editions, + 'form' => $form, + 'session' => + [ + 'moderator' => $user->isModerator(), + 'owner' => $torrentPosterCurrent['userId'] === $user->getId(), + ] + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/approve/poster/{torrentPosterId}', + name: 'torrent_poster_approve', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentPosterId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function approvePoster( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent poster + if (!$torrentPoster = $torrentService->getTorrentPoster($request->get('torrentPosterId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + if (!$torrentPoster->isApproved()) + { + $activityService->addEventTorrentPosterApproveAdd( + $user->getId(), + $torrent->getId(), + time(), + $torrentPoster->getId() + ); + } + + else + { + $activityService->addEventTorrentPosterApproveDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentPoster->getId() + ); + } + + // Update approved + $torrentService->toggleTorrentPosterApproved( + $torrentPoster->getId() + ); + + // Redirect + return $this->redirectToRoute( + 'torrent_poster_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentPosterId' => $torrentPoster->getId(), + ] + ); + } + + #[Route( + '/{_locale}/torrent/{torrentId}/delete/poster/{torrentPosterId}', + name: 'torrent_poster_delete', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + 'torrentPosterId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function deletePoster( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Init torrent poster + if (!$torrentPoster = $torrentService->getTorrentPoster($request->get('torrentPosterId'))) + { + throw $this->createNotFoundException(); + } + + // Check permissions + if (!($user->isModerator() || $user->getId() === $torrentPoster->getUserId())) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Add activity event + $activityService->addEventTorrentPosterDelete( + $user->getId(), + $torrent->getId(), + time(), + $torrentPoster->getId() + ); + + // Update approved + $torrentService->deleteTorrentPoster( + $torrentPoster->getId() + ); + + // Redirect + return $this->redirectToRoute( + 'torrent_poster_edit', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId(), + 'torrentPosterId' => $torrentPoster->getId(), + ] + ); + } + + // Torrent star + #[Route( + '/{_locale}/torrent/{torrentId}/star/toggle', + name: 'torrent_star_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'torrentId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleStar( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Block crawler requests + if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers')))) + { + throw $this->createNotFoundException(); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + // Update + $value = $torrentService->toggleTorrentStar( + $torrent->getId(), + $user->getId(), + time() + ); + + // Register activity event + if ($value) + { + $activityService->addEventTorrentStarAdd( + $user->getId(), + time(), + $torrent->getId() + ); + } + + else + { + $activityService->addEventTorrentStarDelete( + $user->getId(), + time(), + $torrent->getId() + ); + } + + // Redirect + return $this->redirectToRoute( + 'torrent_info', + [ + '_locale' => $request->get('_locale'), + 'torrentId' => $torrent->getId() + ] + ); + } + + // Torrent download file + #[Route( + '/torrent/{torrentId}/file', + name: 'torrent_file', + requirements: + [ + 'torrentId' => '\d+' + ], + methods: + [ + 'GET' + ] + )] + public function downloadFile( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Block crawler requests + if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers')))) + { + throw $this->createNotFoundException(); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); + } + + // Access filter + if (!$user->isModerator() && $user->getId() != $torrent->getUserId() && + (!$torrent->isStatus() || !$torrent->isApproved())) + { + throw $this->createNotFoundException(); + } + + // Register download + $torrentService->addTorrentDownloadFile( + $torrent->getId(), + $user->getId(), + time() + ); + + // Request scrape + $torrentService->updateTorrentScraped( + $torrent->getId(), + 0 + ); + + // Register download event + $activityService->addEventTorrentDownloadFileAdd( + $user->getId(), + time(), + $torrent->getId() + ); + + // Apply filters + $file = $this->filterYggdrasil( + $file, + $user->isYggdrasil() + ); + + // Get data + $data = $file->dumpToString(); + + // Set headers + $response = new Response(); + + $response->headers->set( + 'Content-type', + 'application/x-bittorrent' + ); + + $response->headers->set( + 'Content-length', + strlen($data) + ); + + $response->headers->set( + 'Content-Disposition', + sprintf( + 'attachment; filename="%s [%s#%s].torrent";', + $file->getName(), + $this->getParameter('app.name'), + $torrent->getId() + ) + ); + + $response->sendHeaders(); + + // Return file content + return $response->setContent($data); + } + + // Torrent download wanted file + #[Route( + '/torrent/{torrentId}/file/wanted', + name: 'torrent_file_wanted', + requirements: + [ + 'torrentId' => '\d+' + ], + methods: + [ + 'GET' + ] + )] + public function downloadFileWanted( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Block crawler requests + if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers')))) + { + throw $this->createNotFoundException(); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); + } + + // Access filter + if (!$user->isModerator() && $user->getId() != $torrent->getUserId() && + (!$torrent->isStatus() || !$torrent->isApproved())) + { + throw $this->createNotFoundException(); + } + + // Register download + $torrentService->addTorrentDownloadFile( + $torrent->getId(), + $user->getId(), + time() + ); + + // Request scrape + $torrentService->updateTorrentScraped( + $torrent->getId(), + 0 + ); + + // Register download event + $activityService->addEventTorrentDownloadFileAdd( + $user->getId(), + time(), + $torrent->getId() + ); + + // Apply filters + $file = $this->filterYggdrasil( + $file, + false // wanted file downloading with original trackers + ); + + // Get data + $data = $file->dumpToString(); + + // Set headers + $response = new Response(); + + $response->headers->set( + 'Content-type', + 'application/x-bittorrent' + ); + + $response->headers->set( + 'Content-length', + strlen($data) + ); + + $response->headers->set( + 'Content-Disposition', + sprintf( + 'attachment; filename="%s [wanted#%s].torrent";', + $file->getName(), + $torrent->getId() + ) + ); + + $response->sendHeaders(); + + // Return file content + return $response->setContent($data); + } + + // Torrent download magnet + #[Route( + '/torrent/{torrentId}/magnet', + name: 'torrent_magnet', + requirements: + [ + 'torrentId' => '\d+' + ], + methods: + [ + 'GET' + ] + )] + public function getMagnet( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Block crawler requests + if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers')))) + { + throw $this->createNotFoundException(); + } + + // Init torrent + if (!$torrent = $torrentService->getTorrent($request->get('torrentId'))) + { + throw $this->createNotFoundException(); + } + + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); + } + + // Access filter + if (!$user->isModerator() && $user->getId() != $torrent->getUserId() && + (!$torrent->isStatus() || !$torrent->isApproved())) + { + throw $this->createNotFoundException(); + } + + // Register download + $torrentService->addTorrentDownloadMagnet( + $torrent->getId(), + $user->getId(), + time() + ); + + // Request scrape + $torrentService->updateTorrentScraped( + $torrent->getId(), + 0 + ); + + // Register download event + $activityService->addEventTorrentDownloadMagnetAdd( + $user->getId(), + time(), + $torrent->getId() + ); + + // Apply filters + $file = $this->filterYggdrasil( + $file, + $user->isYggdrasil() + ); + + // Return magnet link + return $this->redirect( + $file->getMagnetLink() + ); + } + + // Tools + #[Route( + '/crontab/torrent/scrape/{key}', + requirements: [ + 'key' => '%app.key%' + ], + methods: + [ + 'GET' + ] + )] + public function scrape( + Request $request, + TranslatorInterface $translator, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init Scraper + $scraper = new \Yggverse\Scrapeer\Scraper(); + + // Get next torrent in scrape queue + if (!$torrent = $torrentService->getTorrentScrapeQueue()) + { + throw $this->createNotFoundException(); + } + + // Get file + if (!$file = $torrentService->readTorrentFileByTorrentId($torrent->getId())) + { + throw $this->createNotFoundException(); + } + + // Filter yggdrasil trackers + $file = $this->filterYggdrasil($file, true); + + // Get trackers list + $trackers = []; + + if ($announce = $file->getAnnounce()) + { + $trackers[] = $announce; + } + + if ($announceList = $file->getAnnounceList()) + { + if (isset($announceList[0])) + { + foreach ($announceList[0] as $value) + { + $trackers[] = $value; + } + } + + if (isset($announceList[1])) + { + foreach ($announceList[1] as $value) + { + $trackers[] = $value; + } + } + } + + $trackers = array_unique($trackers); + + // Get info hashes + $hashes = []; + + if ($hash = $file->getInfoHashV1(false)) + { + $hashes[] = $hash; + } + + if ($hash = $file->getInfoHashV2(false)) + { + $hashes[] = $hash; + } + + // Get scrape + $seeders = 0; + $peers = 0; + $leechers = 0; + + if ($hashes && $trackers) + { + // Update scrape info + if ($results = $scraper->scrape($hashes, $trackers, null, 1)) + { + foreach ($results as $result) + { + if (isset($result['seeders'])) + { + $seeders = $seeders + (int) $result['seeders']; + } + + if (isset($result['completed'])) + { + $peers = $peers + (int) $result['completed']; + } + + if (isset($result['leechers'])) + { + $leechers = $leechers + (int) $result['leechers']; + } + } + } + } + + // Register activity event only on previous status changed + if ($leechers && !$seeders && + $leechers != (int) $torrent->getLeechers() && $seeders != (int) $torrent->getSeeders()) + { + $activityService->addEventTorrentWantedAdd( + $torrent->getUserId(), // just required field, let's relate with author, because we don't know which exactly user requires for seeders from crontab @TODO + time(), + $torrent->getId() + ); + } + + // Update DB + $torrentService->updateTorrentScrape( + $torrent->getId(), + $seeders, + $peers, + $leechers + ); + + // Update torrent wanted storage if enabled + if ($this->getParameter('app.torrent.wanted.ftp.enabled') === '1') + { + // Add wanted file + if ($leechers && !$seeders) + { + if ($this->getParameter('app.torrent.wanted.ftp.approved') === '0' || + ($this->getParameter('app.torrent.wanted.ftp.approved') === '1' && $torrent->isApproved())) + { + /// All + $torrentService->copyToFtpStorage( + $torrent->getId(), + sprintf( + '%s/torrents/wanted/all/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + + /// Sensitive + if ($torrent->isSensitive()) + { + $torrentService->copyToFtpStorage( + $torrent->getId(), + sprintf( + '%s/torrents/wanted/sensitive/yes/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + } + + else + { + $torrentService->copyToFtpStorage( + $torrent->getId(), + sprintf( + '%s/torrents/wanted/sensitive/no/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + } + + /// Locals + foreach ($torrent->getLocales() as $locale) + { + $torrentService->copyToFtpStorage( + $torrent->getId(), + sprintf( + '%s/torrents/wanted/locale/%s/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $locale, + $torrent->getId() + ) + ); + } + } + } + + // Remove not wanted files + else + { + /// All + $torrentService->removeFromFtpStorage( + sprintf( + '%s/torrents/wanted/all/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + + /// Sensitive + $torrentService->removeFromFtpStorage( + sprintf( + '%s/torrents/wanted/sensitive/yes/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + + $torrentService->removeFromFtpStorage( + sprintf( + '%s/torrents/wanted/sensitive/no/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $torrent->getId() + ) + ); + + /// Locals + foreach (explode('|', $this->getParameter('app.locales')) as $locale) + { + $torrentService->removeFromFtpStorage( + sprintf( + '%s/torrents/wanted/locale/%s/wanted#%s.torrent', + $this->getParameter('app.torrent.wanted.ftp.folder'), + $locale, + $torrent->getId() + ) + ); + } + } + } + + // Render response + return new Response(); // @TODO + } + + #[Route( + '/tool/torrent/reindex/{key}', + requirements: [ + 'key' => '%app.key%' + ], + methods: + [ + 'GET' + ] + )] + public function reindex( + TorrentService $torrentService + ): Response + { + // Reindex keywords + $torrentService->reindexTorrentKeywordsAll( + (bool) $this->getParameter('app.index.torrent.name.enabled'), + (bool) $this->getParameter('app.index.torrent.filenames.enabled'), + (bool) $this->getParameter('app.index.torrent.hash.v1.enabled'), + (bool) $this->getParameter('app.index.torrent.hash.v2.enabled'), + (bool) $this->getParameter('app.index.torrent.source.enabled'), + (bool) $this->getParameter('app.index.torrent.comment.enabled'), + (int) $this->getParameter('app.index.word.length.min'), + (int) $this->getParameter('app.index.word.length.max') + ); + + // Render response + return new Response(); // @TODO + } + + #[Route( + '/sitemap.xml', + methods: + [ + 'GET' + ] + )] + public function sitemap( + TorrentService $torrentService + ): Response + { + $locale = $this->getParameter('app.locale'); + $locales = explode('|', $this->getParameter('app.locales')); + + $response = new Response(); + $response->headers->set('Content-Type', 'text/xml'); + + return $this->render( + 'default/torrent/sitemap.xml.twig', + [ + 'locale' => $locale, + 'locales' => $locales, + 'torrents' => $torrentService->findTorrents( + 0, // no user session init, pass 0 + [], // without keywords filter + $locales, // all system locales + $categories, // all system locales + null, // all sensitive levels + true, // approved only + true, // enabled only + 1000, // @TODO limit + 0 // offset + ) + ], + $response + ); + } + + private function initUser( + Request $request, + UserService $userService, + ActivityService $activityService + ): ?\App\Entity\User + { + // Init user + if (!$user = $userService->findUserByAddress($request->getClientIp())) + { + $user = $userService->addUser( + $request->getClientIp(), + time(), + $this->getParameter('app.locale'), + explode('|', $this->getParameter('app.locales')), + $activityService->getEventCodes(), + $this->getParameter('app.theme'), + $this->getParameter('app.sensitive'), + $this->getParameter('app.yggdrasil'), + $this->getParameter('app.posters'), + $this->getParameter('app.approved') + ); + + // Add user join event + $activityService->addEventUserAdd( + $user->getId(), + time() + ); + } + + return $user; + } + + private function filterYggdrasil( + ?\Rhilip\Bencode\TorrentFile $file, bool $yggdrasil, string $regex = '/^0{0,1}[2-3][a-f0-9]{0,2}:/' + ): ?\Rhilip\Bencode\TorrentFile + { + // Init trackers registry + $allTrackers = []; + + // Get app trackers + $appTrackers = explode('|', $this->getParameter('app.trackers')); + + // Append app trackers + foreach ($appTrackers as $appTracker) + { + $allTrackers[] = $appTracker; + } + + // Get original file announcements + $announceList = $file->getAnnounceList(); + + // Append original file announcements + foreach ($announceList as $announce) + { + if (is_array($announce)) + { + foreach ($announce as $value) + { + $allTrackers[] = $value; + } + } + + else + { + $allTrackers[] = $value; + } + } + + // Remove duplicates + $allTrackers = array_unique($allTrackers); + + // Yggdrasil-only mode + if ($yggdrasil) + { + // Replace announce URL with first application tracker if original does not match Yggdrasil condition + if (!preg_match($regex, str_replace(['[',']'], false, parse_url($value, PHP_URL_HOST)))) + { + $file->setAnnounce( + $appTrackers[0] + ); + } + + // Remove non-Yggdrasil trackers from announcement list + foreach ($allTrackers as $key => $value) + { + // trackers + if (!preg_match($regex, str_replace(['[',']'], false, parse_url($value, PHP_URL_HOST)))) + { + unset($allTrackers[$key]); + } + } + } + + // Format announce list + $trackers = []; + + foreach ($allTrackers as $value) + { + $trackers[] = [$value]; + } + + // Update announce list + $file->setAnnounceList( + $trackers + ); + + // Return filtered file + return $file; + } +} diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php new file mode 100644 index 0000000..538af56 --- /dev/null +++ b/src/Controller/UserController.php @@ -0,0 +1,672 @@ +initUser( + $request, + $userService, + $activityService + ); + + return $this->redirectToRoute( + 'torrent_recent', + [ + '_locale' => $user->getLocale() + ] + ); + } + + #[Route( + '/{_locale}/settings', + name: 'user_settings', + requirements: [ + '_locale' => '%app.locales%', + ], + )] + public function settings( + Request $request, + UserService $userService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + // Process post request + if ($request->isMethod('post')) + { + // Update locale + if (in_array($request->get('locale'), explode('|', $this->getParameter('app.locales')))) + { + $user->setLocale( + $request->get('locale') + ); + } + + // Update locales + if ($request->get('locales')) + { + $locales = []; + foreach ((array) $request->get('locales') as $locale) + { + if (in_array($locale, explode('|', $this->getParameter('app.locales')))) + { + $locales[] = $locale; + } + } + + $user->setLocales( + $locales + ); + } + + // Update categories + if ($request->get('categories')) + { + $categories = []; + foreach ((array) $request->get('categories') as $category) + { + if (in_array($category, explode('|', $this->getParameter('app.categories')))) + { + $categories[] = $category; + } + } + + $user->setCategories( + $categories + ); + } + + // Update theme + if (in_array($request->get('theme'), explode('|', $this->getParameter('app.themes')))) + { + $user->setTheme( + $request->get('theme') + ); + } + + // Update events + $events = []; + foreach ((array) $request->get('events') as $event) + { + if (in_array($event, $activityService->getEventCodes())) + { + $events[] = $event; + } + } + + $user->setEvents( + $events + ); + + // Update sensitive + $user->setSensitive( + $request->get('sensitive') === 'true' + ); + + // Update yggdrasil + $user->setYggdrasil( + $request->get('yggdrasil') === 'true' + ); + + // Update posters + $user->setPosters( + $request->get('posters') === 'true' + ); + + // Save changes to DB + $userService->save($user); + + // Redirect user to new locale + return $this->redirectToRoute( + 'user_settings', + [ + '_locale' => $user->getLocale() + ] + ); + } + + // Render template + return $this->render( + 'default/user/settings.html.twig', + [ + 'user' => [ + 'id' => $user->getId(), + 'sensitive' => $user->isSensitive(), + 'yggdrasil' => $user->isYggdrasil(), + 'posters' => $user->isPosters(), + 'locale' => $user->getLocale(), + 'locales' => $user->getLocales(), + 'categories' => $user->getCategories(), + 'events' => $user->getEvents(), + 'theme' => $user->getTheme(), + 'added' => $user->getAdded() + ], + 'locales' => explode('|', $this->getParameter('app.locales')), + 'categories' => explode('|', $this->getParameter('app.categories')), + 'themes' => explode('|', $this->getParameter('app.themes')), + 'events' => $activityService->getEventsTree() + ] + ); + } + + #[Route( + '/{_locale}/profile/{userId}', + name: 'user_info', + defaults: [ + 'userId' => 0, + ], + requirements: [ + '_locale' => '%app.locales%', + 'userId' => '\d+', + ], + )] + public function info( + Request $request, + TranslatorInterface $translator, + UserService $userService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init target user + if (!$userTarget = $userService->getUser( + $request->get('userId') ? $request->get('userId') : $user->getId() + )) + { + throw $this->createNotFoundException(); + } + + // Get total activities + $total = $activityService->findActivitiesTotalByUserId( + $userTarget->getId(), + $user->getEvents() + ); + + // Init page + $page = $request->get('page') ? (int) $request->get('page') : 1; + + // Render template + return $this->render( + 'default/user/info.html.twig', + [ + 'session' => + [ + 'user' => $user, + 'owner' => $user->getId() === $userTarget->getId(), + 'moderator' => $user->isModerator() + ], + 'user' => [ + 'id' => $userTarget->getId(), + 'address' => $userTarget->getAddress(), + 'moderator' => $userTarget->isModerator(), + 'approved' => $userTarget->isApproved(), + 'status' => $userTarget->isStatus(), + 'posters' => $userTarget->isPosters(), + 'sensitive' => $userTarget->isSensitive(), + 'yggdrasil' => $userTarget->isYggdrasil(), + 'locale' => $userTarget->getLocale(), + 'locales' => $userTarget->getLocales(), + 'categories' => $user->getCategories(), + 'events' => $userTarget->getEvents(), + 'theme' => $userTarget->getTheme(), + 'added' => $userTarget->getAdded(), + 'identicon' => $userService->identicon( + $userTarget->getAddress(), + 48 + ), + 'owner' => $user->getId() === $userTarget->getId(), + 'star' => + [ + 'exist' => (bool) $userService->findUserStar( + $user->getId(), + $userTarget->getId() + ), + 'total' => $userService->findUserStarsTotalByUserIdTarget( + $userTarget->getId() + ) + ], + 'activities' => $activityService->findLastActivitiesByUserId( + $userTarget->getId(), + $userTarget->getEvents(), + $this->getParameter('app.pagination'), + ($page - 1) * $this->getParameter('app.pagination') + ) + ], + 'events' => $activityService->getEventsTree(), + 'pagination' => + [ + 'page' => $page, + 'pages' => ceil($total / $this->getParameter('app.pagination')), + 'total' => $total + ] + ] + ); + } + + #[Route( + '/{_locale}/user/star/toggle/{userId}', + name: 'user_star_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'userId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleStar( + Request $request, + TranslatorInterface $translator, + UserService $userService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isStatus()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Block crawler requests + if (in_array($request->getClientIp(), explode('|', $this->getParameter('app.crawlers')))) + { + throw $this->createNotFoundException(); + } + + // Init target user + if (!$userTarget = $userService->getUser($request->get('userId'))) + { + throw $this->createNotFoundException(); + } + + // Update + $value = $userService->toggleUserStar( + $user->getId(), + $userTarget->getId(), + time() + ); + + // Add activity event + if ($value) + { + $activityService->addEventUserStarAdd( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + else + { + $activityService->addEventUserStarDelete( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + // Redirect + return $this->redirectToRoute( + 'user_info', + [ + '_locale' => $request->get('_locale'), + 'userId' => $userTarget->getId() + ] + ); + } + + #[Route( + '/{_locale}/user/{userId}/moderator/toggle', + name: 'user_moderator_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'userId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleModerator( + Request $request, + TranslatorInterface $translator, + UserService $userService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init target user + if (!$userTarget = $userService->getUser($request->get('userId'))) + { + throw $this->createNotFoundException(); + } + + // Update user moderator + $value = $userService->toggleUserModerator( + $userTarget->getId() + )->isModerator(); + + // Add activity event + if ($value) + { + $activityService->addEventUserModeratorAdd( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + else + { + $activityService->addEventUserModeratorDelete( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + // Redirect + return $this->redirectToRoute( + 'user_info', + [ + '_locale' => $request->get('_locale'), + 'userId' => $userTarget->getId() + ] + ); + } + + #[Route( + '/{_locale}/user/{userId}/status/toggle', + name: 'user_status_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'userId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleStatus( + Request $request, + TranslatorInterface $translator, + UserService $userService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init target user + if (!$userTarget = $userService->getUser($request->get('userId'))) + { + throw $this->createNotFoundException(); + } + + // Update user status + $value = $userService->toggleUserStatus( + $userTarget->getId() + )->isStatus(); + + // Add activity event + if ($value) + { + $activityService->addEventUserStatusAdd( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + else + { + $activityService->addEventUserStatusDelete( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + // Redirect + return $this->redirectToRoute( + 'user_info', + [ + '_locale' => $request->get('_locale'), + 'userId' => $userTarget->getId() + ] + ); + } + + #[Route( + '/{_locale}/user/{userId}/approved/toggle', + name: 'user_approved_toggle', + requirements: + [ + '_locale' => '%app.locales%', + 'userId' => '\d+', + ], + methods: + [ + 'GET' + ] + )] + public function toggleApproved( + Request $request, + TranslatorInterface $translator, + UserService $userService, + TorrentService $torrentService, + ActivityService $activityService + ): Response + { + // Init user + $user = $this->initUser( + $request, + $userService, + $activityService + ); + + if (!$user->isModerator()) + { + // @TODO + throw new \Exception( + $translator->trans('Access denied') + ); + } + + // Init target user + if (!$userTarget = $userService->getUser($request->get('userId'))) + { + throw $this->createNotFoundException(); + } + + // Auto-approve all related content on user approve + if (!$userTarget->isApproved()) + { + $torrentService->setTorrentsApprovedByUserId( + $userTarget->getId(), + true + ); + + $torrentService->setTorrentLocalesApprovedByUserId( + $userTarget->getId(), + true + ); + + $torrentService->setTorrentCategoriesApprovedByUserId( + $userTarget->getId(), + true + ); + + $torrentService->setTorrentSensitivesApprovedByUserId( + $userTarget->getId(), + true + ); + + $torrentService->setTorrentPostersApprovedByUserId( + $userTarget->getId(), + true + ); + + // @TODO make event for each item + } + + // Update user approved + $value = $userService->toggleUserApproved( + $userTarget->getId() + )->isApproved(); + + // Add activity event + if ($value) + { + $activityService->addEventUserApproveAdd( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + else + { + $activityService->addEventUserApproveDelete( + $user->getId(), + time(), + $userTarget->getId() + ); + } + + // Redirect + return $this->redirectToRoute( + 'user_info', + [ + '_locale' => $request->get('_locale'), + 'userId' => $userTarget->getId() + ] + ); + } + + public function module(?string $route): Response + { + return $this->render( + 'default/user/module.html.twig', + [ + 'route' => $route, + 'stars' => 0, + 'views' => 0, + 'comments' => 0, + 'downloads' => 0, + 'editions' => 0, + ] + ); + } + + private function initUser( + Request $request, + UserService $userService, + ActivityService $activityService + ): ?\App\Entity\User + { + // Init user + if (!$user = $userService->findUserByAddress($request->getClientIp())) + { + $user = $userService->addUser( + $request->getClientIp(), + time(), + $this->getParameter('app.locale'), + explode('|', $this->getParameter('app.locales')), + $activityService->getEventCodes(), + $this->getParameter('app.theme'), + $this->getParameter('app.sensitive'), + $this->getParameter('app.yggdrasil'), + $this->getParameter('app.posters'), + $this->getParameter('app.approved') + ); + + // Add user join event + $activityService->addEventUserAdd( + $user->getId(), + time() + ); + } + + return $user; + } +} \ No newline at end of file diff --git a/src/Entity/.gitignore b/src/Entity/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Entity/Activity.php b/src/Entity/Activity.php new file mode 100644 index 0000000..1be1097 --- /dev/null +++ b/src/Entity/Activity.php @@ -0,0 +1,160 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getEvent(): ?int + { + return $this->event; + } + + public function setEvent(int $event): static + { + $this->event = $event; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(?int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(?int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function getData(): array + { + return $this->data; + } + + public function setData(array $data): static + { + $this->data = $data; + + return $this; + } +} diff --git a/src/Entity/Torrent.php b/src/Entity/Torrent.php new file mode 100644 index 0000000..515a62e --- /dev/null +++ b/src/Entity/Torrent.php @@ -0,0 +1,239 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function getScraped(): ?int + { + return $this->scraped; + } + + public function setScraped(int $scraped): static + { + $this->scraped = $scraped; + + return $this; + } + + public function getMd5file(): ?string + { + return $this->md5file; + } + + public function setMd5file(string $md5file): static + { + $this->md5file = $md5file; + + return $this; + } + + public function getKeywords(): ?array + { + return $this->keywords; + } + + public function setKeywords(?array $keywords): static + { + $this->keywords = $keywords; + + return $this; + } + + public function getLocales(): array + { + return $this->locales; + } + + public function setLocales(array $locales): static + { + $this->locales = $locales; + + return $this; + } + + public function isSensitive(): ?bool + { + return $this->sensitive; + } + + public function setSensitive(bool $sensitive): static + { + $this->sensitive = $sensitive; + + 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; + } + + public function getSeeders(): ?int + { + return $this->seeders; + } + + public function setSeeders(?int $seeders): static + { + $this->seeders = $seeders; + + return $this; + } + + public function getPeers(): ?int + { + return $this->peers; + } + + public function setPeers(?int $peers): static + { + $this->peers = $peers; + + return $this; + } + + public function getLeechers(): ?int + { + return $this->leechers; + } + + public function setLeechers(?int $leechers): static + { + $this->leechers = $leechers; + + return $this; + } + + public function getTorrentPosterId(): ?int + { + return $this->torrentPosterId; + } + + public function setTorrentPosterId(?int $torrentPosterId): static + { + $this->torrentPosterId = $torrentPosterId; + + return $this; + } + + public function getCategories(): ?array + { + return $this->categories; + } + + public function setCategories(?array $categories): static + { + $this->categories = $categories; + + return $this; + } +} diff --git a/src/Entity/TorrentCategories.php b/src/Entity/TorrentCategories.php new file mode 100644 index 0000000..9d73809 --- /dev/null +++ b/src/Entity/TorrentCategories.php @@ -0,0 +1,103 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function getValue(): array + { + return $this->value; + } + + public function setValue(array $value): static + { + $this->value = $value; + + return $this; + } + + public function isApproved(): ?bool + { + return $this->approved; + } + + public function setApproved(bool $approved): static + { + $this->approved = $approved; + + return $this; + } +} diff --git a/src/Entity/TorrentDownloadFile.php b/src/Entity/TorrentDownloadFile.php new file mode 100644 index 0000000..f05b416 --- /dev/null +++ b/src/Entity/TorrentDownloadFile.php @@ -0,0 +1,72 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Entity/TorrentDownloadMagnet.php b/src/Entity/TorrentDownloadMagnet.php new file mode 100644 index 0000000..1b56cad --- /dev/null +++ b/src/Entity/TorrentDownloadMagnet.php @@ -0,0 +1,72 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Entity/TorrentLocales.php b/src/Entity/TorrentLocales.php new file mode 100644 index 0000000..f5322d9 --- /dev/null +++ b/src/Entity/TorrentLocales.php @@ -0,0 +1,103 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function getValue(): array + { + return $this->value; + } + + public function setValue(array $value): static + { + $this->value = $value; + + return $this; + } + + public function isApproved(): ?bool + { + return $this->approved; + } + + public function setApproved(bool $approved): static + { + $this->approved = $approved; + + return $this; + } +} diff --git a/src/Entity/TorrentPoster.php b/src/Entity/TorrentPoster.php new file mode 100644 index 0000000..b25c481 --- /dev/null +++ b/src/Entity/TorrentPoster.php @@ -0,0 +1,117 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function isApproved(): ?bool + { + return $this->approved; + } + + public function setApproved(bool $approved): static + { + $this->approved = $approved; + + return $this; + } + + public function getMd5file(): ?string + { + return $this->md5file; + } + + public function setMd5file(string $md5file): static + { + $this->md5file = $md5file; + + return $this; + } + + public function getPosition(): ?string + { + return $this->position; + } + + public function setPosition(string $position): static + { + $this->position = $position; + + return $this; + } +} diff --git a/src/Entity/TorrentSensitive.php b/src/Entity/TorrentSensitive.php new file mode 100644 index 0000000..9c1560f --- /dev/null +++ b/src/Entity/TorrentSensitive.php @@ -0,0 +1,103 @@ +id; + } + + public function setId(string $id): static + { + $this->id = $id; + + return $this; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } + + public function isValue(): ?bool + { + return $this->value; + } + + public function setValue(bool $value): static + { + $this->value = $value; + + return $this; + } + + public function isApproved(): ?bool + { + return $this->approved; + } + + public function setApproved(bool $approved): static + { + $this->approved = $approved; + + return $this; + } +} diff --git a/src/Entity/TorrentStar.php b/src/Entity/TorrentStar.php new file mode 100644 index 0000000..e5fd435 --- /dev/null +++ b/src/Entity/TorrentStar.php @@ -0,0 +1,65 @@ +id; + } + + public function getTorrentId(): ?int + { + return $this->torrentId; + } + + public function setTorrentId(int $torrentId): static + { + $this->torrentId = $torrentId; + + return $this; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..330a97a --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,223 @@ +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 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; + } + + public function getLocale(): ?string + { + return $this->locale; + } + + public function setLocale(string $locale): static + { + $this->locale = $locale; + + return $this; + } + + public function getLocales(): array + { + return $this->locales; + } + + public function setLocales(array $locales): static + { + $this->locales = $locales; + + return $this; + } + + public function getEvents(): array + { + return $this->events; + } + + public function setEvents(array $events): static + { + $this->events = $events; + + return $this; + } + + public function getTheme(): ?string + { + return $this->theme; + } + + public function setTheme(string $theme): static + { + $this->theme = $theme; + + return $this; + } + + public function isSensitive(): ?bool + { + return $this->sensitive; + } + + public function setSensitive(bool $sensitive): static + { + $this->sensitive = $sensitive; + + return $this; + } + + public function isYggdrasil(): ?bool + { + return $this->yggdrasil; + } + + public function setYggdrasil(bool $yggdrasil): static + { + $this->yggdrasil = $yggdrasil; + + return $this; + } + + public function isPosters(): ?bool + { + return $this->posters; + } + + public function setPosters(bool $posters): static + { + $this->posters = $posters; + + return $this; + } + + public function getCategories(): ?array + { + return $this->categories; + } + + public function setCategories(?array $categories): static + { + $this->categories = $categories; + + return $this; + } +} diff --git a/src/Entity/UserStar.php b/src/Entity/UserStar.php new file mode 100644 index 0000000..2fd755e --- /dev/null +++ b/src/Entity/UserStar.php @@ -0,0 +1,65 @@ +id; + } + + public function getUserId(): ?int + { + return $this->userId; + } + + public function setUserId(int $userId): static + { + $this->userId = $userId; + + return $this; + } + + public function getUserIdTarget(): ?int + { + return $this->userIdTarget; + } + + public function setUserIdTarget(int $userIdTarget): static + { + $this->userIdTarget = $userIdTarget; + + return $this; + } + + public function getAdded(): ?int + { + return $this->added; + } + + public function setAdded(int $added): static + { + $this->added = $added; + + return $this; + } +} diff --git a/src/Kernel.php b/src/Kernel.php new file mode 100644 index 0000000..779cd1f --- /dev/null +++ b/src/Kernel.php @@ -0,0 +1,11 @@ + + * + * @method Activity|null find($id, $lockMode = null, $lockVersion = null) + * @method Activity|null findOneBy(array $criteria, array $orderBy = null) + * @method Activity[] findAll() + * @method Activity[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class ActivityRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Activity::class); + } + + public function findActivitiesTotal( + array $whitelist + ): int + { + return $this->createQueryBuilder('a') + ->select('count(a.id)') + ->where('a.event IN (:event)') + ->setParameter(':event', $whitelist) + ->getQuery() + ->getSingleScalarResult() + ; + } + + public function findActivitiesTotalByUserId( + int $userId, + array $whitelist + ): int + { + return $this->createQueryBuilder('a') + ->select('count(a.id)') + ->where('a.userId = :userId') + ->andWhere('a.event IN (:event)') + ->setParameter(':userId', $userId) + ->setParameter(':event', $whitelist) + ->getQuery() + ->getSingleScalarResult() + ; + } + + public function findActivitiesTotalByTorrentId( + int $torrentId, + array $whitelist + ): int + { + return $this->createQueryBuilder('a') + ->select('count(a.id)') + ->where('a.torrentId = :torrentId') + ->andWhere('a.event IN (:event)') + ->setParameter(':torrentId', $torrentId) + ->setParameter(':event', $whitelist) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Repository/TorrentCategoriesRepository.php b/src/Repository/TorrentCategoriesRepository.php new file mode 100644 index 0000000..030f69c --- /dev/null +++ b/src/Repository/TorrentCategoriesRepository.php @@ -0,0 +1,23 @@ + + * + * @method TorrentCategories|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentCategories|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentCategories[] findAll() + * @method TorrentCategories[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentCategoriesRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentCategories::class); + } +} diff --git a/src/Repository/TorrentDownloadFileRepository.php b/src/Repository/TorrentDownloadFileRepository.php new file mode 100644 index 0000000..4052176 --- /dev/null +++ b/src/Repository/TorrentDownloadFileRepository.php @@ -0,0 +1,36 @@ + + * + * @method TorrentDownloadFile|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentDownloadFile|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentDownloadFile[] findAll() + * @method TorrentDownloadFile[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentDownloadFileRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentDownloadFile::class); + } + + public function findTorrentDownloadFilesTotalByTorrentId( + int $torrentId + ): int + { + return $this->createQueryBuilder('tdf') + ->select('count(tdf.id)') + ->where('tdf.torrentId = :torrentId') + ->setParameter('torrentId', $torrentId) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Repository/TorrentDownloadMagnetRepository.php b/src/Repository/TorrentDownloadMagnetRepository.php new file mode 100644 index 0000000..ef4e706 --- /dev/null +++ b/src/Repository/TorrentDownloadMagnetRepository.php @@ -0,0 +1,36 @@ + + * + * @method TorrentDownloadMagnet|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentDownloadMagnet|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentDownloadMagnet[] findAll() + * @method TorrentDownloadMagnet[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentDownloadMagnetRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentDownloadMagnet::class); + } + + public function findTorrentDownloadMagnetsTotalByTorrentId( + int $torrentId + ): int + { + return $this->createQueryBuilder('tdm') + ->select('count(tdm.id)') + ->where('tdm.torrentId = :torrentId') + ->setParameter('torrentId', $torrentId) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Repository/TorrentLocalesRepository.php b/src/Repository/TorrentLocalesRepository.php new file mode 100644 index 0000000..8ba7b73 --- /dev/null +++ b/src/Repository/TorrentLocalesRepository.php @@ -0,0 +1,23 @@ + + * + * @method TorrentLocales|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentLocales|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentLocales[] findAll() + * @method TorrentLocales[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentLocalesRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentLocales::class); + } +} diff --git a/src/Repository/TorrentPosterRepository.php b/src/Repository/TorrentPosterRepository.php new file mode 100644 index 0000000..235231c --- /dev/null +++ b/src/Repository/TorrentPosterRepository.php @@ -0,0 +1,23 @@ + + * + * @method TorrentPoster|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentPoster|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentPoster[] findAll() + * @method TorrentPoster[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentPosterRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentPoster::class); + } +} diff --git a/src/Repository/TorrentRepository.php b/src/Repository/TorrentRepository.php new file mode 100644 index 0000000..536a72f --- /dev/null +++ b/src/Repository/TorrentRepository.php @@ -0,0 +1,241 @@ + + * + * @method Torrent|null find($id, $lockMode = null, $lockVersion = null) + * @method Torrent|null findOneBy(array $criteria, array $orderBy = null) + * @method Torrent[] findAll() + * @method Torrent[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Torrent::class); + } + + public function findTorrentsTotal( + int $userId, + array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive = null, + ?bool $approved = null, + ?bool $status = null, + int $limit = 10, + int $offset = 0 + ): int + { + return $this->getTorrentsQueryByFilter( + $userId, + $keywords, + $locales, + $categories, + $sensitive, + $approved, + $status, + )->select('count(t.id)') + ->getQuery() + ->getSingleScalarResult(); + } + + public function findTorrents( + int $userId, + array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive = null, + ?bool $approved = null, + ?bool $status = null, + int $limit = 10, + int $offset = 0 + ): array + { + return $this->getTorrentsQueryByFilter( + $userId, + $keywords, + $locales, + $categories, + $sensitive, + $approved, + $status, + )->setMaxResults($limit) + ->setFirstResult($offset) + ->orderBy('t.id', 'DESC') // same as t.added + ->getQuery() + ->getResult(); + } + + private function getTorrentsQueryByFilter( + int $userId, + ?array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive = null, + ?bool $approved = null, + ?bool $status = null + ): \Doctrine\ORM\QueryBuilder + { + $query = $this->createQueryBuilder('t'); + + if (is_array($keywords)) + { + foreach ($keywords as $i => $keyword) + { + // Make query to the index case insensitive + $keyword = mb_strtolower($keyword); + + // Init OR condition for each word form + $orKeywords = $query->expr()->orX(); + + $orKeywords->add("t.keywords LIKE :keyword{$i}"); + $query->setParameter(":keyword{$i}", "%{$keyword}%"); + + // Generate word forms for each transliteration locale #33 + foreach ($this->generateWordForms($keyword) as $j => $wordForm) + { + $orKeywords->add("t.keywords LIKE :keyword{$i}{$j}"); + $query->setParameter(":keyword{$i}{$j}", "%{$wordForm}%"); + } + + // Append AND condition + $query->andWhere($orKeywords); + } + } + + if (is_array($locales)) + { + $orLocales = $query->expr()->orX(); + + foreach ($locales as $i => $locale) + { + $orLocales->add("t.locales LIKE :locale{$i}"); + $orLocales->add("t.userId = :userId"); + + $query->setParameter(":locale{$i}", "%{$locale}%"); + $query->setParameter('userId', $userId); + } + + $query->andWhere($orLocales); + } + + if (is_array($categories)) + { + $orCategories = $query->expr()->orX(); + + foreach ($categories as $i => $category) + { + $orCategories->add("t.categories LIKE :category{$i}"); + $orCategories->add("t.userId = :userId"); + + $query->setParameter(":category{$i}", "%{$category}%"); + $query->setParameter('userId', $userId); + } + + $query->andWhere($orCategories); + } + + if (is_bool($sensitive)) + { + $orSensitive = $query->expr()->orX(); + + $orSensitive->add("t.sensitive = :sensitive"); + $orSensitive->add("t.userId = :userId"); + + $query->setParameter('sensitive', $sensitive); + $query->setParameter('userId', $userId); + + $query->andWhere($orSensitive); + } + + if (is_bool($approved)) + { + $orApproved = $query->expr()->orX(); + + $orApproved->add("t.approved = :approved"); + $orApproved->add("t.userId = :userId"); + + $query->setParameter('approved', $approved); + $query->setParameter('userId', $userId); + + $query->andWhere($orApproved); + } + + if (is_bool($status)) + { + $orStatus = $query->expr()->orX(); + + $orStatus->add("t.status = :status"); + $orStatus->add("t.userId = :userId"); + + $query->setParameter('status', $status); + $query->setParameter('userId', $userId); + + $query->andWhere($orStatus); + } + + return $query; + } + + // Word forms generator to improve search results + // e.g. transliteration rules for latin filenames + private function generateWordForms( + string $keyword, + // #33 supported locales: + // https://github.com/ashtokalo/php-translit + array $transliteration = [ + 'be', + 'bg', + 'el', + 'hy', + 'kk', + 'mk', + 'ru', + 'ka', + 'uk' + ], + // Additional char forms + array $charForms = + [ + 'c' => 'k', + 'k' => 'c', + ] + ): array + { + $wordForms = []; + + // Apply transliteration + foreach ($transliteration as $locale) + { + $wordForms[] = \ashtokalo\translit\Translit::object()->convert( + $keyword, + $locale + ); + } + + // Apply char forms + foreach ($wordForms as $wordForm) + { + foreach ($charForms as $from => $to) + { + $wordForms[] = str_replace( + $from, + $to, + $wordForm + ); + } + } + + // Remove duplicates + return array_unique( + $wordForms + ); + } +} diff --git a/src/Repository/TorrentSensitiveRepository.php b/src/Repository/TorrentSensitiveRepository.php new file mode 100644 index 0000000..e21c7e6 --- /dev/null +++ b/src/Repository/TorrentSensitiveRepository.php @@ -0,0 +1,23 @@ + + * + * @method TorrentSensitive|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentSensitive|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentSensitive[] findAll() + * @method TorrentSensitive[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentSensitiveRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentSensitive::class); + } +} diff --git a/src/Repository/TorrentStarRepository.php b/src/Repository/TorrentStarRepository.php new file mode 100644 index 0000000..978aa95 --- /dev/null +++ b/src/Repository/TorrentStarRepository.php @@ -0,0 +1,36 @@ + + * + * @method TorrentStar|null find($id, $lockMode = null, $lockVersion = null) + * @method TorrentStar|null findOneBy(array $criteria, array $orderBy = null) + * @method TorrentStar[] findAll() + * @method TorrentStar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class TorrentStarRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, TorrentStar::class); + } + + public function findTorrentStarsTotalByTorrentId( + int $torrentId + ): int + { + return $this->createQueryBuilder('ts') + ->select('count(ts.id)') + ->where('ts.torrentId = :torrentId') + ->setParameter('torrentId', $torrentId) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..86a987a --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,23 @@ + + * + * @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); + } +} diff --git a/src/Repository/UserStarRepository.php b/src/Repository/UserStarRepository.php new file mode 100644 index 0000000..2a4f295 --- /dev/null +++ b/src/Repository/UserStarRepository.php @@ -0,0 +1,36 @@ + + * + * @method UserStar|null find($id, $lockMode = null, $lockVersion = null) + * @method UserStar|null findOneBy(array $criteria, array $orderBy = null) + * @method UserStar[] findAll() + * @method UserStar[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) + */ +class UserStarRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, UserStar::class); + } + + public function findUserStarsTotalByUserIdTarget( + int $userIdTarget + ): int + { + return $this->createQueryBuilder('us') + ->select('count(us.userId)') + ->where('us.userIdTarget = :userIdTarget') + ->setParameter('userIdTarget', $userIdTarget) + ->getQuery() + ->getSingleScalarResult() + ; + } +} diff --git a/src/Service/ActivityService.php b/src/Service/ActivityService.php new file mode 100644 index 0000000..dd785d1 --- /dev/null +++ b/src/Service/ActivityService.php @@ -0,0 +1,1806 @@ +entityManagerInterface = $entityManagerInterface; + $this->translatorInterface = $translatorInterface; + } + + public function getEventCodes(): array + { + return + [ + // User + Activity::EVENT_USER_ADD, + + Activity::EVENT_USER_APPROVE_ADD, + Activity::EVENT_USER_APPROVE_DELETE, + + Activity::EVENT_USER_MODERATOR_ADD, + Activity::EVENT_USER_MODERATOR_DELETE, + + Activity::EVENT_USER_STATUS_ADD, + Activity::EVENT_USER_STATUS_DELETE, + + Activity::EVENT_USER_STAR_ADD, + Activity::EVENT_USER_STAR_DELETE, + + // Torrents + Activity::EVENT_TORRENT_ADD, + + Activity::EVENT_TORRENT_APPROVE_ADD, + Activity::EVENT_TORRENT_APPROVE_DELETE, + + Activity::EVENT_TORRENT_LOCALES_ADD, + Activity::EVENT_TORRENT_LOCALES_DELETE, + Activity::EVENT_TORRENT_LOCALES_APPROVE_ADD, + Activity::EVENT_TORRENT_LOCALES_APPROVE_DELETE, + + Activity::EVENT_TORRENT_CATEGORIES_ADD, + Activity::EVENT_TORRENT_CATEGORIES_DELETE, + Activity::EVENT_TORRENT_CATEGORIES_APPROVE_ADD, + Activity::EVENT_TORRENT_CATEGORIES_APPROVE_DELETE, + + Activity::EVENT_TORRENT_SENSITIVE_ADD, + Activity::EVENT_TORRENT_SENSITIVE_DELETE, + Activity::EVENT_TORRENT_SENSITIVE_APPROVE_ADD, + Activity::EVENT_TORRENT_SENSITIVE_APPROVE_DELETE, + + Activity::EVENT_TORRENT_POSTER_ADD, + Activity::EVENT_TORRENT_POSTER_DELETE, + Activity::EVENT_TORRENT_POSTER_APPROVE_ADD, + Activity::EVENT_TORRENT_POSTER_APPROVE_DELETE, + + Activity::EVENT_TORRENT_STAR_ADD, + Activity::EVENT_TORRENT_STAR_DELETE, + + Activity::EVENT_TORRENT_DOWNLOAD_FILE_ADD, + Activity::EVENT_TORRENT_DOWNLOAD_MAGNET_ADD, + + Activity::EVENT_TORRENT_STATUS_ADD, + Activity::EVENT_TORRENT_STATUS_DELETE, + + Activity::EVENT_TORRENT_WANTED_ADD, + ]; + } + + public function getEventsTree(): array + { + $events = []; + + foreach ($this->getEventCodes() as $code) + { + switch ($code) + { + // User + case Activity::EVENT_USER_ADD: + + $events + [ + $this->translatorInterface->trans('Users') + ] + [ + $this->translatorInterface->trans('Joined') + ] = $code; + + break; + + /// User approve + case Activity::EVENT_USER_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Users') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_USER_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Users') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + break; + + /// User status + case Activity::EVENT_USER_STATUS_ADD: + + $events + [ + $this->translatorInterface->trans('User statuses') + ] + [ + $this->translatorInterface->trans('Enabled') + ] = $code; + + break; + + case Activity::EVENT_USER_STATUS_DELETE: + + $events + [ + $this->translatorInterface->trans('User statuses') + ] + [ + $this->translatorInterface->trans('Disabled') + ] = $code; + break; + + /// User moderator + case Activity::EVENT_USER_MODERATOR_ADD: + + $events + [ + $this->translatorInterface->trans('User moderators') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_USER_MODERATOR_DELETE: + + $events + [ + $this->translatorInterface->trans('User moderators') + ] + [ + $this->translatorInterface->trans('Removed') + ] = $code; + break; + + /// User star + case Activity::EVENT_USER_STAR_ADD: + + $events + [ + $this->translatorInterface->trans('User stars') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_USER_STAR_DELETE: + + $events + [ + $this->translatorInterface->trans('User stars') + ] + [ + $this->translatorInterface->trans('Removed') + ] = $code; + break; + + // Torrent + case Activity::EVENT_TORRENT_ADD: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + + break; + + /// Torrent locales + case Activity::EVENT_TORRENT_LOCALES_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent locales') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_LOCALES_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent locales') + ] + [ + $this->translatorInterface->trans('Deleted') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_LOCALES_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent locales') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_LOCALES_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent locales') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + + break; + + /// Torrent categories + case Activity::EVENT_TORRENT_CATEGORIES_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent categories') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_CATEGORIES_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent categories') + ] + [ + $this->translatorInterface->trans('Deleted') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_CATEGORIES_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent categories') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_CATEGORIES_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent categories') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + + break; + + /// Torrent sensitive + case Activity::EVENT_TORRENT_SENSITIVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent sensitive') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_SENSITIVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent sensitive') + ] + [ + $this->translatorInterface->trans('Deleted') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_SENSITIVE_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent sensitive') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_SENSITIVE_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent sensitive') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + + break; + + /// Torrent poster + case Activity::EVENT_TORRENT_POSTER_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent posters') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_POSTER_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent posters') + ] + [ + $this->translatorInterface->trans('Deleted') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_POSTER_APPROVE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent posters') + ] + [ + $this->translatorInterface->trans('Approved') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_POSTER_APPROVE_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent posters') + ] + [ + $this->translatorInterface->trans('Disapproved') + ] = $code; + + break; + + /// Torrent stars + case Activity::EVENT_TORRENT_STAR_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent stars') + ] + [ + $this->translatorInterface->trans('Added') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_STAR_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrent stars') + ] + [ + $this->translatorInterface->trans('Removed') + ] = $code; + + break; + + /// Torrent downloads + case Activity::EVENT_TORRENT_DOWNLOAD_FILE_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent downloads') + ] + [ + $this->translatorInterface->trans('Files') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_DOWNLOAD_MAGNET_ADD: + + $events + [ + $this->translatorInterface->trans('Torrent downloads') + ] + [ + $this->translatorInterface->trans('Magnet links') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_WANTED_ADD: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Wanted') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_STATUS_ADD: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Enabled') + ] = $code; + + break; + + case Activity::EVENT_TORRENT_STATUS_DELETE: + + $events + [ + $this->translatorInterface->trans('Torrents') + ] + [ + $this->translatorInterface->trans('Disabled') + ] = $code; + + break; + } + } + + return $events; + } + + public function findLastActivities( + array $whitelist, + int $limit = 10, + int $offset = 0 + ): array + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findBy( + [ + 'event' => $whitelist + ], + [ + 'id' => 'DESC' + ], + $limit, + $offset + ); + } + + public function findLastActivitiesByUserId( + int $userId, + array $whitelist, + int $limit = 10, + int $offset = 0 + ): array + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findBy( + [ + 'userId' => $userId, + 'event' => $whitelist, + ], + [ + 'id' => 'DESC' + ], + $limit, + $offset + ); + } + + public function findLastActivitiesByTorrentId( + int $torrentId, + array $whitelist, + int $limit = 10, + int $offset = 0 + ): array + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findBy( + [ + 'torrentId' => $torrentId, + 'event' => $whitelist, + ], + [ + 'id' => 'DESC' + ], + $limit, + $offset + ); + } + + public function findActivitiesTotal( + array $whitelist + ): int + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findActivitiesTotal($whitelist); + } + + public function findActivitiesTotalByUserId( + int $userId, + array $whitelist + ): int + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findActivitiesTotalByUserId( + $userId, + $whitelist + ); + } + + public function findActivitiesTotalByTorrentId( + int $torrentId, + array $whitelist + ): int + { + return $this->entityManagerInterface + ->getRepository(Activity::class) + ->findActivitiesTotalByTorrentId( + $torrentId, + $whitelist + ); + } + + // User + public function addEventUserAdd( + int $userId, + int $added + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// User approved + public function addEventUserApproveAdd( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventUserApproveDelete( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// User status + public function addEventUserStatusAdd( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_STATUS_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventUserStatusDelete( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_STATUS_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// User moderator + public function addEventUserModeratorAdd( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_MODERATOR_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventUserModeratorDelete( + int $userId, + int $added, + int $userIdTarget, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_MODERATOR_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// User star + public function addEventUserStarAdd( + int $userId, + int $added, + int $userIdTarget + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_STAR_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventUserStarDelete( + int $userId, + int $added, + int $userIdTarget + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_USER_STAR_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'userId' => $userIdTarget + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + // Torrent + public function addEventTorrentAdd( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setTorrentId( + $torrentId + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentApproveAdd( + int $userId, + int $torrentId, + int $added + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentApproveDelete( + int $userId, + int $torrentId, + int $added + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentStatusAdd( + int $userId, + int $torrentId, + int $added + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_STATUS_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentStatusDelete( + int $userId, + int $torrentId, + int $added + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_STATUS_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentWantedAdd( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_WANTED_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setTorrentId( + $torrentId + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent Download + public function addEventTorrentDownloadFileAdd( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_DOWNLOAD_FILE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setTorrentId( + $torrentId + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentDownloadMagnetAdd( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_DOWNLOAD_MAGNET_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setAdded( + $added + ); + + $activity->setTorrentId( + $torrentId + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent star + public function addEventTorrentStarAdd( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_STAR_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentStarDelete( + int $userId, + int $added, + int $torrentId + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_STAR_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent locales + public function addEventTorrentLocalesAdd( + int $userId, + int $torrentId, + int $added, + int $torrentLocalesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_LOCALES_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentLocalesId' => $torrentLocalesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentLocalesDelete( + int $userId, + int $torrentId, + int $added, + int $torrentLocalesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_LOCALES_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentLocalesId' => $torrentLocalesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentLocalesApproveAdd( + int $userId, + int $torrentId, + int $added, + int $torrentLocalesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_LOCALES_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentLocalesId' => $torrentLocalesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentLocalesApproveDelete( + int $userId, + int $torrentId, + int $added, + int $torrentLocalesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_LOCALES_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentLocalesId' => $torrentLocalesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent categories + public function addEventTorrentCategoriesAdd( + int $userId, + int $torrentId, + int $added, + int $torrentCategoriesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_CATEGORIES_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentCategoriesId' => $torrentCategoriesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentCategoriesDelete( + int $userId, + int $torrentId, + int $added, + int $torrentCategoriesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_CATEGORIES_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentCategoriesId' => $torrentCategoriesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentCategoriesApproveAdd( + int $userId, + int $torrentId, + int $added, + int $torrentCategoriesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_CATEGORIES_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentCategoriesId' => $torrentCategoriesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentCategoriesApproveDelete( + int $userId, + int $torrentId, + int $added, + int $torrentCategoriesId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_CATEGORIES_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentCategoriesId' => $torrentCategoriesId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent sensitive + public function addEventTorrentSensitiveAdd( + int $userId, + int $torrentId, + int $added, + int $torrentSensitiveId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_SENSITIVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentSensitiveId' => $torrentSensitiveId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentSensitiveDelete( + int $userId, + int $torrentId, + int $added, + int $torrentSensitiveId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_SENSITIVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentSensitiveId' => $torrentSensitiveId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentSensitiveApproveAdd( + int $userId, + int $torrentId, + int $added, + int $torrentSensitiveId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_SENSITIVE_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentSensitiveId' => $torrentSensitiveId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentSensitiveApproveDelete( + int $userId, + int $torrentId, + int $added, + int $torrentSensitiveId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_SENSITIVE_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentSensitiveId' => $torrentSensitiveId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + /// Torrent poster + public function addEventTorrentPosterAdd( + int $userId, + int $torrentId, + int $added, + int $torrentPosterId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_POSTER_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentPosterId' => $torrentPosterId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentPosterDelete( + int $userId, + int $torrentId, + int $added, + int $torrentPosterId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_POSTER_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentPosterId' => $torrentPosterId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentPosterApproveAdd( + int $userId, + int $torrentId, + int $added, + int $torrentPosterId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_POSTER_APPROVE_ADD + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentPosterId' => $torrentPosterId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } + + public function addEventTorrentPosterApproveDelete( + int $userId, + int $torrentId, + int $added, + int $torrentPosterId, + ): ?Activity + { + $activity = new Activity(); + + $activity->setEvent( + Activity::EVENT_TORRENT_POSTER_APPROVE_DELETE + ); + + $activity->setUserId( + $userId + ); + + $activity->setTorrentId( + $torrentId + ); + + $activity->setAdded( + $added + ); + + $activity->setData( + [ + 'torrentPosterId' => $torrentPosterId + ] + ); + + $this->entityManagerInterface->persist($activity); + $this->entityManagerInterface->flush(); + + return $activity; + } +} \ No newline at end of file diff --git a/src/Service/TorrentService.php b/src/Service/TorrentService.php new file mode 100644 index 0000000..c0c3d52 --- /dev/null +++ b/src/Service/TorrentService.php @@ -0,0 +1,1548 @@ +kernelInterface = $kernelInterface; + $this->entityManagerInterface = $entityManagerInterface; + } + + // Tools + public function readTorrentFileByFilepath( + string $filepath + ): ?\Rhilip\Bencode\TorrentFile + { + try + { + return \Rhilip\Bencode\TorrentFile::load( + $filepath + ); + } + + catch (\Rhilip\Bencode\ParseException $error) + { + return null; + } + } + + public function readTorrentFileByTorrentId( + int $torrentId + ): ?\Rhilip\Bencode\TorrentFile + { + return $this->readTorrentFileByFilepath( + $this->getStorageFilepathByTorrentId($torrentId) + ); + } + + public function generateTorrentKeywordsByString( + string $string, + int $wordLengthMin, + int $wordLengthMax, + ): array + { + $words = explode( + ' ', + preg_replace( + '/[\s]+/', + ' ', + preg_replace( + '/[\W_]+/u', + ' ', + $string + ) + ) + ); + + // Apply words filter + foreach ((array) $words as $key => $value) + { + // Apply word length filter + $length = mb_strlen($value); + + if ($length < $wordLengthMin || $length > $wordLengthMax) + { + unset($words[$key]); + } + + else + { + // Apply case insensitive search conversion + $words[$key] = mb_strtolower($value); + } + } + + // Build simple array + $keywords = []; + foreach ((array) $words as $word) + { + $keywords[] = $word; + } + + // Return unique keywords + return array_unique( + $keywords + ); + } + + public function generateTorrentKeywordsByTorrentFilepath( + + string $filepath, + + bool $extractName, + bool $extractFilenames, + bool $extractInfoHashV1, + bool $extractInfoHashV2, + bool $extractSource, + bool $extractComment, + + int $wordLengthMin, + int $wordLengthMax + + ): array + { + $keywords = []; + + if ($file = $this->readTorrentFileByFilepath($filepath)) + { + if ($extractName) + { + if ($name = $file->getName(false)) + { + $keywords = array_merge( + $keywords, + $this->generateTorrentKeywordsByString( + $name, + $wordLengthMin, + $wordLengthMax + ) + ); + } + } + + if ($extractFilenames) + { + foreach ($file->getFileList() as $list) + { + $keywords = array_merge( + $keywords, + $this->generateTorrentKeywordsByString( + $list['path'], + $wordLengthMin, + $wordLengthMax + ) + ); + } + } + + if ($extractSource) + { + if ($source = $file->getSource(false)) + { + $keywords = array_merge( + $keywords, + $this->generateTorrentKeywordsByString( + $source, + $wordLengthMin, + $wordLengthMax + ) + ); + } + } + + if ($extractComment) + { + if ($comment = $file->getComment(false)) + { + $keywords = array_merge( + $keywords, + $this->generateTorrentKeywordsByString( + $comment, + $wordLengthMin, + $wordLengthMax + ) + ); + } + } + + if ($extractInfoHashV1) + { + if ($hash = $file->getInfoHashV1(false)) + { + $keywords[] = $hash; + } + } + + if ($extractInfoHashV2) + { + if ($hash = $file->getInfoHashV2(false)) + { + $keywords[] = $hash; + } + } + } + + return array_unique( + $keywords + ); + } + + public function getImageUriByTorrentPosterId( + int $torrentPosterId, + int $quality = 100, + int $width = 748, + int $height = 0, + float $opacity = 1, + bool $grayscale = false, + string $format = 'webp' + ): string + { + $uri = sprintf( + '/posters/%s.%s', + implode('/', str_split($torrentPosterId)), + $format + ); + + $filename = sprintf( + '%s/public/posters/%s.%s', + $this->kernelInterface->getProjectDir(), + implode('/', str_split($torrentPosterId)), + $format + ); + + if (file_exists($filename)) + { + return $uri; + } + + $path = explode('/', $filename); + + array_pop($path); + + @mkdir(implode('/', $path), 0755, true); + + $image = new \Imagick(); + + $image->readImage( + $this->getStorageFilepathByTorrentPosterId( + $torrentPosterId + ) + ); + + $image->setImageFormat($format); + $image->setImageCompressionQuality($quality); + + if ($width || $height) + { + $image->adaptiveResizeImage( + $width, + $height + ); + } + + if ($grayscale) + { + $image->setImageType( + \Imagick::IMGTYPE_GRAYSCALE + ); + } + + if ($opacity) + { + $image->setImageOpacity( + $opacity + ); + } + + $image->writeImage( + $filename + ); + + return $uri; + } + + public function getStorageFilepathByTorrentPosterId(int $torrentPosterId): string + { + return sprintf( + '%s/var/posters/%s.original', + $this->kernelInterface->getProjectDir(), + implode('/', str_split($torrentPosterId)) + ); + } + + public function getStorageFilepathByTorrentId(int $torrentId): string + { + return sprintf( + '%s/var/torrents/%s.torrent', + $this->kernelInterface->getProjectDir(), + implode('/', str_split($torrentId)) + ); + } + + public function getFtpFilepathByFilename(string $filename): string + { + return sprintf( + '%s/var/ftp/%s', + $this->kernelInterface->getProjectDir(), + $filename + ); + } + + public function getTorrentContributors(Torrent $torrent): array + { + $contributors = []; + + foreach ($this->findTorrentCategoriesByTorrentId($torrent->getId()) as $torrentCategory) + { + $contributors[] = $torrentCategory->getUserId(); + } + + foreach ($this->findTorrentLocalesByTorrentId($torrent->getId()) as $torrentLocale) + { + $contributors[] = $torrentLocale->getUserId(); + } + + foreach ($this->findTorrentSensitiveByTorrentId($torrent->getId()) as $torrentSensitive) + { + $contributors[] = $torrentSensitive->getUserId(); + } + + foreach ($this->findTorrentPosterByTorrentId($torrent->getId()) as $torrentPoster) + { + $contributors[] = $torrentPoster->getUserId(); + } + + $contributors[] = $torrent->getUserId(); + + return array_unique($contributors); + } + + public function copyToFtpStorage( + int $torrentId, + string $filename + ): void + { + $filesystem = new Filesystem(); + $filesystem->copy( + $this->getStorageFilepathByTorrentId( + $torrentId + ), + $this->getFtpFilepathByFilename( + $filename + ) + ); + } + + public function removeFromFtpStorage( + string $filename + ): void + { + $filesystem = new Filesystem(); + $filesystem->remove( + $this->getFtpFilepathByFilename( + $filename + ) + ); + } + + public function add( + + string $filepath, + + bool $extractName, + bool $extractFilenames, + bool $extractInfoHashV1, + bool $extractInfoHashV2, + bool $extractSource, + bool $extractComment, + + int $wordLengthMin, + int $wordLengthMax, + + int $userId, + int $added, + array $locales, + array $categories, + bool $sensitive, + bool $approved, + bool $status + + ): ?Torrent + { + $torrent = $this->addTorrent( + $userId, + $added, + md5_file($filepath), + $this->generateTorrentKeywordsByTorrentFilepath( + $filepath, + $extractName, + $extractFilenames, + $extractInfoHashV1, + $extractInfoHashV2, + $extractSource, + $extractComment, + $wordLengthMin, + $wordLengthMax + ), + $locales, + $categories, + $sensitive, + $approved, + $status + ); + + $filesystem = new Filesystem(); + $filesystem->copy( + $filepath, + $this->getStorageFilepathByTorrentId( + $torrent->getId() + ) + ); + + $this->addTorrentLocales( + $torrent->getId(), + $userId, + $added, + $locales, + $approved + ); + + $this->addTorrentCategories( + $torrent->getId(), + $userId, + $added, + $categories, + $approved + ); + + $this->addTorrentSensitive( + $torrent->getId(), + $userId, + $added, + $sensitive, + $approved + ); + + return $torrent; + } + + // Torrent + public function getTorrent(int $torrentId): ?Torrent + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->find($torrentId); + } + + public function addTorrent( + int $userId, + int $added, + string $md5file, + array $keywords, + array $locales, + array $categories, + bool $sensitive, + bool $approved, + bool $status + ): ?Torrent + { + $torrent = new Torrent(); + + $torrent->setUserId($userId); + $torrent->setAdded($added); + $torrent->setMd5File($md5file); + $torrent->setKeywords($keywords); + $torrent->setLocales($locales); + $torrent->setCategories($categories); + $torrent->setSensitive($sensitive); + $torrent->setApproved($approved); + $torrent->setStatus($status); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + + return $torrent; + } + + public function toggleTorrentApproved( + int $torrentId + ): ?Torrent + { + $torrent = $this->getTorrent($torrentId); + + $torrent->setApproved( + !$torrent->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrent->getId() + ); + + $this->updateTorrentSensitive( + $torrent->getId() + ); + + return $torrent; + } + + public function toggleTorrentStatus( + int $torrentId + ): ?Torrent + { + $torrent = $this->getTorrent($torrentId); + + $torrent->setStatus( + !$torrent->isStatus() // toggle current value + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrent->getId() + ); + + $this->updateTorrentSensitive( + $torrent->getId() + ); + + return $torrent; + } + + public function getTorrentScrapeQueue(): ?Torrent + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->findOneBy( + [], + [ + 'scraped' => 'ASC' + ] + ); + } + + public function findTorrents( + int $userId, + array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive, + ?bool $approved, + ?bool $status, + int $limit, + int $offset + ) : array + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->findTorrents( + $userId, + $keywords, + $locales, + $categories, + $sensitive, + $approved, + $status, + $limit, + $offset + ); + } + + public function findTorrentsTotal( + int $userId, + array $keywords, + ?array $locales, + ?array $categories, + ?bool $sensitive, + ?bool $approved, + ?bool $status + ) : int + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->findTorrentsTotal( + $userId, + $keywords, + $locales, + $categories, + $sensitive, + $approved, + $status + ); + } + + public function findTorrentByMd5File(string $md5file) : ?Torrent + { + return $this->entityManagerInterface + ->getRepository(Torrent::class) + ->findOneBy( + [ + 'md5file' => $md5file + ] + ); + } + + public function updateTorrentSensitive( + int $torrentId, + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + if ($torrentSensitive = $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'approved' => true, + ], + [ + 'id' => 'DESC' + ] + )) + { + $torrent->setSensitive( + $torrentSensitive->isValue() + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + } + + public function updateTorrentLocales( + int $torrentId + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + if ($torrentLocales = $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'approved' => true, + ], + [ + 'id' => 'DESC' + ] + )) + { + $torrent->setLocales($torrentLocales->getValue()); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + } + + public function updateTorrentCategories( + int $torrentId + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + if ($torrentCategories = $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'approved' => true, + ], + [ + 'id' => 'DESC' + ] + )) + { + $torrent->setCategories($torrentCategories->getValue()); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + } + + public function updateTorrentScraped( + int $torrentId, + int $time + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + $torrent->setScraped( + $time + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + public function updateTorrentScrape( + int $torrentId, + int $seeders, + int $peers, + int $leechers + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + $torrent->setSeeders( + $seeders + ); + + $torrent->setPeers( + $peers + ); + + $torrent->setLeechers( + $leechers + ); + + $torrent->setScraped( + time() + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + public function reindexTorrentKeywordsAll( + bool $extractName, + bool $extractFilenames, + bool $extractInfoHashV1, + bool $extractInfoHashV2, + bool $extractSource, + bool $extractComment, + int $wordLengthMin, + int $wordLengthMax + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(Torrent::class) + ->findAll() as $torrent) + { + $torrent->setKeywords( + $this->generateTorrentKeywordsByTorrentFilepath( + $this->getStorageFilepathByTorrentId( + $torrent->getId() + ), + $extractName, + $extractFilenames, + $extractInfoHashV1, + $extractInfoHashV2, + $extractSource, + $extractComment, + $wordLengthMin, + $wordLengthMax + ) + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + public function setTorrentApprovedByTorrentId( + int $torrentId, + bool $value + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + $torrent->setApproved($value); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + public function setTorrentsApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(Torrent::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrent) + { + $torrent->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + // Torrent locale + public function getTorrentLocales( + int $torrentLocaleId + ): ?TorrentLocales + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->find($torrentLocaleId); + } + + public function findLastTorrentLocalesByTorrentId( + int $torrentId + ): ?TorrentLocales + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findOneBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function findTorrentLocalesByTorrentId(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findBy( + [ + 'torrentId' => $torrentId, + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function toggleTorrentLocalesApproved( + int $torrentLocalesId + ): ?TorrentLocales + { + $torrentLocales = $this->getTorrentLocales($torrentLocalesId); + + $torrentLocales->setApproved( + !$torrentLocales->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrentLocales->getTorrentId() + ); + + return $torrentLocales; + } + + public function deleteTorrentLocales( + int $torrentLocalesId + ): ?TorrentLocales + { + $torrentLocales = $this->getTorrentLocales($torrentLocalesId); + + $this->entityManagerInterface->remove($torrentLocales); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrentLocales->getTorrentId() + ); + + return $torrentLocales; + } + + public function addTorrentLocales( + int $torrentId, + int $userId, + int $added, + array $value, + bool $approved + ): ?TorrentLocales + { + $torrentLocales = new TorrentLocales(); + + $torrentLocales->setTorrentId($torrentId); + $torrentLocales->setUserId($userId); + $torrentLocales->setAdded($added); + $torrentLocales->setValue($value); + $torrentLocales->setApproved($approved); + + $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrentId + ); + + return $torrentLocales; + } + + public function setTorrentLocalesApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(TorrentLocales::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrentLocales) + { + $torrentLocales->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrentLocales); + $this->entityManagerInterface->flush(); + + $this->updateTorrentLocales( + $torrentLocales->getTorrentId(), + ); + } + } + + // Torrent category + public function getTorrentCategories( + int $torrentCategoryId + ): ?TorrentCategories + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->find($torrentCategoryId); + } + + public function findLastTorrentCategoriesByTorrentId( + int $torrentId + ): ?TorrentCategories + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findOneBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function findTorrentCategoriesByTorrentId(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findBy( + [ + 'torrentId' => $torrentId, + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function toggleTorrentCategoriesApproved( + int $torrentCategoriesId + ): ?TorrentCategories + { + $torrentCategories = $this->getTorrentCategories($torrentCategoriesId); + + $torrentCategories->setApproved( + !$torrentCategories->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId() + ); + + return $torrentCategories; + } + + public function deleteTorrentCategories( + int $torrentCategoriesId + ): ?TorrentCategories + { + $torrentCategories = $this->getTorrentCategories($torrentCategoriesId); + + $this->entityManagerInterface->remove($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId() + ); + + return $torrentCategories; + } + + public function addTorrentCategories( + int $torrentId, + int $userId, + int $added, + array $value, + bool $approved + ): ?TorrentCategories + { + $torrentCategories = new TorrentCategories(); + + $torrentCategories->setTorrentId($torrentId); + $torrentCategories->setUserId($userId); + $torrentCategories->setAdded($added); + $torrentCategories->setValue($value); + $torrentCategories->setApproved($approved); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentId + ); + + return $torrentCategories; + } + + public function setTorrentCategoriesApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(TorrentCategories::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrentCategories) + { + $torrentCategories->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrentCategories); + $this->entityManagerInterface->flush(); + + $this->updateTorrentCategories( + $torrentCategories->getTorrentId(), + ); + } + } + + // Torrent sensitive + public function getTorrentSensitive( + int $torrentSensitiveId + ): ?TorrentSensitive + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->find( + $torrentSensitiveId + ); + } + + public function findLastTorrentSensitiveByTorrentId(int $torrentId): ?TorrentSensitive + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findOneBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function findTorrentSensitiveByTorrentId(int $torrentId): array + { + return $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function toggleTorrentSensitiveApproved( + int $torrentSensitiveId + ): ?TorrentSensitive + { + $torrentSensitive = $this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->find($torrentSensitiveId); + + $torrentSensitive->setApproved( + !$torrentSensitive->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentSensitive); + $this->entityManagerInterface->flush(); + + $this->updateTorrentSensitive( + $torrentSensitive->getTorrentId() + ); + + return $torrentSensitive; + } + + public function deleteTorrentSensitive( + int $torrentSensitiveId + ): ?TorrentSensitive + { + $torrentSensitive = $this->getTorrentSensitive( + $torrentSensitiveId + ); + + $this->entityManagerInterface->remove($torrentSensitive); + $this->entityManagerInterface->flush(); + + $this->updateTorrentSensitive( + $torrentSensitive->getTorrentId() + ); + + return $torrentSensitive; + } + + public function addTorrentSensitive( + int $torrentId, + int $userId, + int $added, + bool $value, + bool $approved + ): ?TorrentSensitive + { + $torrentSensitive = new TorrentSensitive(); + + $torrentSensitive->setTorrentId($torrentId); + $torrentSensitive->setUserId($userId); + $torrentSensitive->setAdded($added); + $torrentSensitive->setValue($value); + $torrentSensitive->setApproved($approved); + + $this->entityManagerInterface->persist($torrentSensitive); + $this->entityManagerInterface->flush(); + + $this->updateTorrentSensitive( + $torrentId + ); + + return $torrentSensitive; + } + + public function setTorrentSensitivesApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(TorrentSensitive::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrentSensitive) + { + $torrentSensitive->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrentSensitive); + $this->entityManagerInterface->flush(); + + $this->updateTorrentSensitive( + $torrentSensitive->getTorrentId(), + ); + } + } + + // Torrent poster + public function getTorrentPoster( + int $torrentPosterId + ): ?TorrentPoster + { + return $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->find( + $torrentPosterId + ); + } + + public function findTorrentPosterByMd5File( + string $md5file + ): ?Torrent + { + return $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->findOneBy( + [ + 'md5file' => $md5file + ] + ); + } + + public function findLastTorrentPosterByTorrentId( + int $torrentId + ): ?TorrentPoster + { + return $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->findOneBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function findTorrentPosterByTorrentId( + int $torrentId + ): array + { + return $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->findBy( + [ + 'torrentId' => $torrentId + ], + [ + 'id' => 'DESC' + ] + ); + } + + public function toggleTorrentPosterApproved( + int $torrentPosterId + ): ?TorrentPoster + { + $torrentPoster = $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->find($torrentPosterId); + + $torrentPoster->setApproved( + !$torrentPoster->isApproved() // toggle current value + ); + + $this->entityManagerInterface->persist($torrentPoster); + $this->entityManagerInterface->flush(); + + $this->updateTorrentPoster( + $torrentPoster->getTorrentId() + ); + + return $torrentSensitive; + } + + public function deleteTorrentPoster( + int $torrentPosterId + ): ?TorrentPoster + { + // Remove torrent file from permanent storage + $filesystem = new Filesystem(); + $filesystem->remove( + $this->getStorageFilepathByTorrentPosterId( + $torrentPosterId + ) + ); + + // Remove from DB + $torrentPoster = $this->getTorrentPoster( + $torrentPosterId + ); + + $this->entityManagerInterface->remove($torrentPoster); + $this->entityManagerInterface->flush(); + + // Update torrent + $this->updateTorrentPoster( + $torrentPoster->getTorrentId() + ); + + return $torrentSensitive; + } + + public function addTorrentPoster( + string $filename, + string $position, + int $torrentId, + int $userId, + int $added, + bool $approved + ): ?TorrentPoster + { + // Add new DB record + $torrentPoster = new TorrentPoster(); + + $torrentPoster->setTorrentId($torrentId); + $torrentPoster->setUserId($userId); + $torrentPoster->setAdded($added); + $torrentPoster->setApproved($approved); + $torrentPoster->setPosition($position); + $torrentPoster->setMd5file( + md5_file($filename) + ); + + $this->entityManagerInterface->persist($torrentPoster); + $this->entityManagerInterface->flush(); + + // Save file in permanent storage + $filesystem = new Filesystem(); + $filesystem->copy( + $filename, + $this->getStorageFilepathByTorrentPosterId( + $torrentPoster->getId() + ) + ); + + // Update torrent info + $this->updateTorrentPoster( + $torrentId + ); + + return $torrentPoster; + } + + public function setTorrentPostersApprovedByUserId( + int $userId, + bool $value + ): void + { + foreach ($this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->findBy( + [ + 'userId' => $userId + ]) as $torrentPoster) + { + $torrentPoster->setApproved( + $value + ); + + $this->entityManagerInterface->persist($torrentPoster); + $this->entityManagerInterface->flush(); + + $this->updateTorrentPoster( + $torrentPoster->getTorrentId(), + ); + } + } + + public function updateTorrentPoster( + int $torrentId, + ): void + { + if ($torrent = $this->getTorrent($torrentId)) + { + if ($torrentPoster = $this->entityManagerInterface + ->getRepository(TorrentPoster::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'approved' => true, + ], + [ + 'id' => 'DESC' + ] + )) + { + $torrent->setTorrentPosterId( + $torrentPoster->getId() + ); + } + + else + { + $torrent->setTorrentPosterId( + null + ); + } + + $this->entityManagerInterface->persist($torrent); + $this->entityManagerInterface->flush(); + } + } + + + + + + // Torrent star + public function findTorrentStar( + int $torrentId, + int $userId + ): ?TorrentStar + { + return $this->entityManagerInterface + ->getRepository(TorrentStar::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'userId' => $userId, + ] + ); + } + + public function findTorrentStarsTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentStar::class) + ->findTorrentStarsTotalByTorrentId($torrentId); + } + + public function toggleTorrentStar( + int $torrentId, + int $userId, + int $added + ): bool + { + if ($torrentStar = $this->findTorrentStar($torrentId, $userId)) + { + $this->entityManagerInterface->remove($torrentStar); + $this->entityManagerInterface->flush(); + + return false; + } + + else + { + $torrentStar = new TorrentStar(); + + $torrentStar->setTorrentId($torrentId); + $torrentStar->setUserId($userId); + $torrentStar->setAdded($added); + + $this->entityManagerInterface->persist($torrentStar); + $this->entityManagerInterface->flush(); + + return true; + } + } + + // Torrent download file + public function findTorrentDownloadFile( + int $torrentId, + int $userId + ): ?TorrentDownloadFile + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'userId' => $userId + ] + ); + } + + public function findTorrentDownloadFilesTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadFile::class) + ->findTorrentDownloadFilesTotalByTorrentId($torrentId); + } + + public function addTorrentDownloadFile( + int $torrentId, + int $userId, + int $added + ): void + { + if (!$this->findTorrentDownloadFile($torrentId, $userId)) + { + $torrentDownloadFile = new TorrentDownloadFile(); + + $torrentDownloadFile->setTorrentId($torrentId); + $torrentDownloadFile->setUserId($userId); + $torrentDownloadFile->setAdded($added); + + $this->entityManagerInterface->persist($torrentDownloadFile); + $this->entityManagerInterface->flush(); + } + } + + // Torrent download magnet + public function findTorrentDownloadMagnet( + int $torrentId, + int $userId + ): ?TorrentDownloadMagnet + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findOneBy( + [ + 'torrentId' => $torrentId, + 'userId' => $userId + ] + ); + } + + public function findTorrentDownloadMagnetsTotalByTorrentId(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(TorrentDownloadMagnet::class) + ->findTorrentDownloadMagnetsTotalByTorrentId($torrentId); + } + + public function addTorrentDownloadMagnet( + int $torrentId, + int $userId, + int $added + ): void + { + if (!$this->findTorrentDownloadMagnet($torrentId, $userId)) + { + $torrentDownloadMagnet = new TorrentDownloadMagnet(); + + $torrentDownloadMagnet->setTorrentId($torrentId); + $torrentDownloadMagnet->setUserId($userId); + $torrentDownloadMagnet->setAdded($added); + + $this->entityManagerInterface->persist($torrentDownloadMagnet); + $this->entityManagerInterface->flush(); + } + } +} \ No newline at end of file diff --git a/src/Service/UserService.php b/src/Service/UserService.php new file mode 100644 index 0000000..eeba420 --- /dev/null +++ b/src/Service/UserService.php @@ -0,0 +1,256 @@ +entityManagerInterface = $entityManagerInterface; + $this->parameterBagInterface = $parameterBagInterface; + } + + public function addUser( + string $address, + string $added, + string $locale, + array $locales, + array $events, + string $theme, + bool $sensitive = true, + bool $yggdrasil = true, + bool $posters = true, + bool $approved = false, + bool $moderator = false, + bool $status = true + ): ?User + { + // Create new user + $user = new User(); + + $user->setAddress( + $address + ); + + $user->setAdded( + $added + ); + + $user->setApproved( + $approved + ); + + $user->setModerator( + $moderator + ); + + $user->setStatus( + $status + ); + + $user->setLocale( + $locale + ); + + $user->setLocales( + $locales + ); + + $user->setTheme( + $theme + ); + + $user->setEvents( + $events + ); + + $user->setSensitive( + $sensitive + ); + + $user->setYggdrasil( + $yggdrasil + ); + + $user->setPosters( + $posters + ); + + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + + // Set initial user as approved & moderator + if (1 === $user->getId()) + { + $user->setApproved(true); + $user->setModerator(true); + $user->setSensitive(false); + + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + } + + // Return user data + return $user; + } + + public function getUser(int $userId): ?User + { + return $this->entityManagerInterface + ->getRepository(User::class) + ->find($userId); + } + + public function findUserByAddress(string $address): ?User + { + return $this->entityManagerInterface + ->getRepository(User::class) + ->findOneBy( + [ + 'address' => $address + ] + ); + } + + public function identicon( + mixed $value, + int $size = 16, + array $style = + [ + 'backgroundColor' => 'rgba(255, 255, 255, 0)', + 'padding' => 0 + ], + string $format = 'webp' + ): string + { + $identicon = new \Jdenticon\Identicon(); + + $identicon->setValue($value); + $identicon->setSize($size); + $identicon->setStyle($style); + + return $identicon->getImageDataUri($format); + } + + public function save(User $user) : void // @TODO delete + { + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + } + + // User star + public function findUserStar( + int $userId, + int $userIdTarget + ): ?UserStar + { + return $this->entityManagerInterface + ->getRepository(UserStar::class) + ->findOneBy( + [ + 'userId' => $userId, + 'userIdTarget' => $userIdTarget + ] + ); + } + + public function findUserStarsTotalByUserIdTarget(int $torrentId): int + { + return $this->entityManagerInterface + ->getRepository(UserStar::class) + ->findUserStarsTotalByUserIdTarget($torrentId); + } + + public function toggleUserStar( + int $userId, + int $userIdTarget, + int $added + ): bool + { + if ($userStar = $this->findUserStar($userId, $userIdTarget)) + { + $this->entityManagerInterface->remove($userStar); + $this->entityManagerInterface->flush(); + + return false; + } + + else + { + $userStar = new UserStar(); + + $userStar->setUserId($userId); + $userStar->setUserIdTarget($userIdTarget); + $userStar->setAdded($added); + + $this->entityManagerInterface->persist($userStar); + $this->entityManagerInterface->flush(); + + return true; + } + } + + public function toggleUserModerator( + int $userId + ): ?User + { + if ($user = $this->getUser($userId)) + { + $user->setModerator( + !$user->isModerator() + ); + + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + } + + return $user; + } + + public function toggleUserStatus( + int $userId + ): ?User + { + if ($user = $this->getUser($userId)) + { + $user->setStatus( + !$user->isStatus() + ); + + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + } + + return $user; + } + + public function toggleUserApproved( + int $userId + ): ?User + { + if ($user = $this->getUser($userId)) + { + $user->setApproved( + !$user->isApproved() + ); + + $this->entityManagerInterface->persist($user); + $this->entityManagerInterface->flush(); + } + + return $user; + } +} \ No newline at end of file diff --git a/src/Twig/AppExtension.php b/src/Twig/AppExtension.php new file mode 100644 index 0000000..440ca6c --- /dev/null +++ b/src/Twig/AppExtension.php @@ -0,0 +1,192 @@ +container = $container; + $this->translator = $translator; + } + + public function getFilters() + { + return + [ + new TwigFilter( + 'format_bytes', + [ + $this, + 'formatBytes' + ] + ), + new TwigFilter( + 'format_ago', + [ + $this, + 'formatAgo' + ] + ), + new TwigFilter( + 'url_to_markdown', + [ + $this, + 'urlToMarkdown' + ] + ), + new TwigFilter( + 'trans_category', + [ + $this, + 'transCategory' + ] + ), + ]; + } + + public function formatBytes( + int $bytes, + int $precision = 2 + ): string + { + $size = [ + $this->translator->trans('B'), + $this->translator->trans('Kb'), + $this->translator->trans('Mb'), + $this->translator->trans('Gb'), + $this->translator->trans('Tb'), + $this->translator->trans('Pb'), + $this->translator->trans('Eb'), + $this->translator->trans('Zb'), + $this->translator->trans('Yb') + ]; + + $factor = floor((strlen($bytes) - 1) / 3); + + return sprintf("%.{$precision}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor]; + } + + public function formatAgo( + int $time, + ): string + { + $diff = time() - $time; + + if ($diff < 1) + { + return $this->translator->trans('now'); + } + + $values = + [ + 365 * 24 * 60 * 60 => + [ + $this->translator->trans('year ago'), + $this->translator->trans('years ago'), + $this->translator->trans(' years ago') + ], + 30 * 24 * 60 * 60 => + [ + $this->translator->trans('month ago'), + $this->translator->trans('months ago'), + $this->translator->trans(' months ago') + ], + 24 * 60 * 60 => + [ + $this->translator->trans('day ago'), + $this->translator->trans('days ago'), + $this->translator->trans(' days ago') + ], + 60 * 60 => + [ + $this->translator->trans('hour ago'), + $this->translator->trans('hours ago'), + $this->translator->trans(' hours ago') + ], + 60 => + [ + $this->translator->trans('minute ago'), + $this->translator->trans('minutes ago'), + $this->translator->trans(' minutes ago') + ], + 1 => + [ + $this->translator->trans('second ago'), + $this->translator->trans('seconds ago'), + $this->translator->trans(' seconds ago') + ] + ]; + + foreach ($values as $key => $value) + { + $result = $diff / $key; + + if ($result >= 1) + { + $round = round($result); + + return sprintf( + '%s %s', + $round, + $this->plural( + $round, + $value + ) + ); + } + } + } + + public function urlToMarkdown( + string $text + ): string + { + return preg_replace( + '~(https?://(?:www\.)?[^\(\s\)]+)~i', + '[$1]($1)', + $text + ); + } + + public function transCategory( + string $name + ): string + { + switch ($name) + { + case 'movie': return $this->translator->trans('movie'); + case 'series': return $this->translator->trans('series'); + case 'tv': return $this->translator->trans('tv'); + case 'animation': return $this->translator->trans('animation'); + case 'music': return $this->translator->trans('music'); + case 'game': return $this->translator->trans('game'); + case 'audiobook': return $this->translator->trans('audiobook'); + case 'podcast': return $this->translator->trans('podcast'); + case 'book': return $this->translator->trans('book'); + case 'archive': return $this->translator->trans('archive'); + case 'picture': return $this->translator->trans('picture'); + case 'software': return $this->translator->trans('software'); + case 'other': return $this->translator->trans('other'); + default: return $name; + } + } + + private function plural(int $number, array $texts) + { + $cases = [2, 0, 1, 1, 1, 2]; + + return $texts[(($number % 100) > 4 && ($number % 100) < 20) ? 2 : $cases[min($number % 10, 5)]]; + } +} \ No newline at end of file diff --git a/src/config/bootstrap.php b/src/config/bootstrap.php deleted file mode 100644 index 906adf0..0000000 --- a/src/config/bootstrap.php +++ /dev/null @@ -1,83 +0,0 @@ - [], - '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(), - - '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_VERSION' => (string) API_VERSION, - '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); - } -} \ No newline at end of file diff --git a/src/crontab/export/push.php b/src/crontab/export/push.php deleted file mode 100644 index 18ae4e2..0000000 --- a/src/crontab/export/push.php +++ /dev/null @@ -1,506 +0,0 @@ - [], - '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); - } -} \ No newline at end of file diff --git a/src/crontab/import/feed.php b/src/crontab/import/feed.php deleted file mode 100644 index a1c657e..0000000 --- a/src/crontab/import/feed.php +++ /dev/null @@ -1,1180 +0,0 @@ - [], - 'time' => - [ - 'ISO8601' => date('c'), - 'total' => microtime(true), - ], - 'http' => - [ - 'total' => 0, - ], - 'memory' => - [ - 'start' => memory_get_usage(), - 'total' => 0, - 'peaks' => 0 - ], - 'db' => - [ - 'total' => [ - 'select' => 0, - 'insert' => 0, - 'update' => 0, - 'delete' => 0, - ] - ], -]; - -// Begin import -try -{ - // Transaction begin - $db->beginTransaction(); - - foreach (json_decode( - file_get_contents(__DIR__ . '/../../config/nodes.json') - ) as $node) - { - // Manifest exists - if (empty($node->manifest)) - { - array_push( - $debug['dump'], - sprintf( - _('Manifest URL not provided for this node: %s'), - $node - ) - ); - - continue; - } - - // Skip non-condition addresses - $error = []; - - if (!Valid::url($node->manifest, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Manifest URL "%s" invalid: %s'), - $node->manifest, - 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 - $curl = new Curl($node->manifest, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Manifest URL "%s" unreachable with code: "%s"'), - $node->manifest, - $code - ) - ); - - continue; - } - - if (!$manifest = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Manifest URL "%s" has broken response'), - $node->manifest - ) - ); - - continue; - } - - if (empty($manifest->export)) - { - array_push( - $debug['dump'], - sprintf( - _('Manifest URL "%s" has broken protocol'), - $node->manifest - ) - ); - - continue; - } - - // Users - if (API_IMPORT_USERS_ENABLED) - { - $error = []; - - if (!Valid::url($manifest->export->users, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Users feed URL "%s" invalid: %s'), - $manifest->export->users, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->users, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Users feed URL "%s" unreachable with code: "%s"'), - $manifest->export->users, - $code - ) - ); - - continue; - } - - if (!$remoteUsers = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Users feed URL "%s" has broken response'), - $manifest->export->users - ) - ); - - continue; - } - - // Init alias registry for this host - $aliasUserId = []; - - foreach ((object) $remoteUsers as $remoteUser) - { - // Validate required fields - $error = []; - - if (!Valid::user($remoteUser, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Users feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->users, - print_r( - $remoteUser, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Skip import on user approved required - if (API_IMPORT_USERS_APPROVED_ONLY && !$remoteUser->approved) - { - // No debug warnings in this case, continue next item - - continue; - } - - // Init session - else if (!$localUser = $db->getUser( - $db->initUserId( - $remoteUser->address, - USER_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remoteUser->approved : USER_DEFAULT_APPROVED, - $remoteUser->timeAdded - ) - )) - { - array_push( - $debug['dump'], - sprintf( - _('Could not init user with address "%s" using feed URL "%s"'), - $remoteUser->address, - $manifest->export->users - ) - ); - - continue; - } - - // Update time added if newer - if ($localUser->timeAdded < $remoteUser->timeAdded) - { - $db->updateUserTimeAdded( - $localUser->userId, - $remoteUser->timeAdded - ); - } - - // Update user info if newer - if ($localUser->timeUpdated < $remoteUser->timeUpdated) - { - // Update time updated - $db->updateUserTimeUpdated( - $localUser->userId, - $remoteUser->timeUpdated - ); - - // Update approved for existing user - if (USER_AUTO_APPROVE_ON_IMPORT_APPROVED && $localUser->approved !== $remoteUser->approved && $remoteUser->approved) - { - $db->updateUserApproved( - $localUser->userId, - $remoteUser->approved, - $remoteUser->timeUpdated - ); - } - - // Set public as received remotely - if (!$localUser->public) - { - $db->updateUserPublic( - $localUser->userId, - true, - $remoteUser->timeUpdated - ); - } - } - - // Register userId alias - $aliasUserId[$remoteUser->userId] = $localUser->userId; - } - - // Magnets - if (API_IMPORT_MAGNETS_ENABLED) - { - $error = []; - - if (!Valid::url($manifest->export->magnets, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnets feed URL "%s" invalid: %s'), - $manifest->export->magnets, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->magnets, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnets feed URL "%s" unreachable with code: "%s"'), - $manifest->export->magnets, - $code - ) - ); - - continue; - } - - if (!$remoteMagnets = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnets feed URL "%s" has broken response'), - $manifest->export->magnets - ) - ); - - continue; - } - - // Init alias registry for this host - $aliasMagnetId = []; - - foreach ((object) $remoteMagnets as $remoteMagnet) - { - // Validate required fields by protocol - $error = []; - - if (!Valid::magnet($remoteMagnet, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnets feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->magnets, - print_r( - $remoteMagnet, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Aliases check - if (!isset($aliasUserId[$remoteMagnet->userId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote userId "%s" not found in URL "%s" %s'), - $remoteMagnet->userId, - $manifest->export->magnets, - print_r( - $remoteMagnet, - true - ), - ) - ); - - continue; - } - - // Skip import on magnet approved required - if (API_IMPORT_MAGNETS_APPROVED_ONLY && !$remoteMagnet->approved) - { - // No debug warnings in this case, continue next item - - continue; - } - - // Add new magnet if not exist by timestamp added for this user - if (!$localMagnet = $db->findMagnet($aliasUserId[$remoteMagnet->userId], $remoteMagnet->timeAdded)) - { - $localMagnet = $db->getMagnet( - $db->addMagnet( - $aliasUserId[$remoteMagnet->userId], - $remoteMagnet->xl, - $remoteMagnet->dn, - '', // @TODO linkSource used for debug only, will be deleted later - true, - $remoteMagnet->comments, - $remoteMagnet->sensitive, - MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remoteMagnet->approved : MAGNET_DEFAULT_APPROVED, - $remoteMagnet->timeAdded - ) - ); - } - - // Update time added if newer - if ($localMagnet->timeAdded < $remoteMagnet->timeAdded) - { - $db->updateMagnetTimeAdded( - $localMagnet->magnetId, - $remoteMagnet->timeAdded - ); - } - - // Update info if remote newer - if ($localMagnet->timeUpdated < $remoteMagnet->timeUpdated) - { - // Magnet fields - $db->updateMagnetXl($localMagnet->magnetId, $remoteMagnet->xl, $remoteMagnet->timeUpdated); - $db->updateMagnetDn($localMagnet->magnetId, $remoteMagnet->dn, $remoteMagnet->timeUpdated); - $db->updateMagnetTitle($localMagnet->magnetId, $remoteMagnet->title, $remoteMagnet->timeUpdated); - $db->updateMagnetPreview($localMagnet->magnetId, $remoteMagnet->preview, $remoteMagnet->timeUpdated); - $db->updateMagnetDescription($localMagnet->magnetId, $remoteMagnet->description, $remoteMagnet->timeUpdated); - $db->updateMagnetComments($localMagnet->magnetId, $remoteMagnet->comments, $remoteMagnet->timeUpdated); - $db->updateMagnetSensitive($localMagnet->magnetId, $remoteMagnet->sensitive, $remoteMagnet->timeUpdated); - - if (MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED && $localMagnet->approved !== $remoteMagnet->approved && $remoteMagnet->approved) - { - $db->updateMagnetApproved($localMagnet->magnetId, $remoteMagnet->approved, $remoteMagnet->timeUpdated); - } - - // xt - foreach ((array) $remoteMagnet->xt as $xt) - { - switch ($xt->version) - { - case 1: - - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($localMagnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 1) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $localMagnet->magnetId, - $db->initInfoHashId( - $xt->value, 1 - ) - ); - } - - break; - - case 2: - - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($localMagnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 2) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $localMagnet->magnetId, - $db->initInfoHashId( - $xt->value, 2 - ) - ); - } - - break; - } - } - - // kt - $db->deleteMagnetToKeywordTopicByMagnetId($localMagnet->magnetId); - - foreach ($remoteMagnet->kt as $kt) - { - $db->initMagnetToKeywordTopicId( - $localMagnet->magnetId, - $db->initKeywordTopicId(trim(mb_strtolower($kt))) // @TODO - ); - } - - // tr - $db->deleteMagnetToAddressTrackerByMagnetId($localMagnet->magnetId); - - foreach ($remoteMagnet->tr as $tr) - { - if ($url = Yggverse\Parser\Url::parse($tr)) - { - $db->initMagnetToAddressTrackerId( - $localMagnet->magnetId, - $db->initAddressTrackerId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - - // as - $db->deleteMagnetToAcceptableSourceByMagnetId($localMagnet->magnetId); - - foreach ($remoteMagnet->as as $as) - { - if ($url = Yggverse\Parser\Url::parse($as)) - { - $db->initMagnetToAcceptableSourceId( - $localMagnet->magnetId, - $db->initAcceptableSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - - // xs - $db->deleteMagnetToExactSourceByMagnetId($localMagnet->magnetId); - - foreach ($remoteMagnet->xs as $xs) - { - if ($url = Yggverse\Parser\Url::parse($xs)) - { - $db->initMagnetToExactSourceId( - $localMagnet->magnetId, - $db->initExactSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - - // Add magnet alias for this host - $aliasMagnetId[$remoteMagnet->magnetId] = $localMagnet->magnetId; - } - - // Magnet comments - if (API_IMPORT_MAGNET_COMMENTS_ENABLED) - { - $error = []; - - if (!Valid::url($manifest->export->magnetComments, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet comments feed URL "%s" invalid: %s'), - $manifest->export->magnetComments, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->magnetComments, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet comments feed URL "%s" unreachable with code: "%s"'), - $manifest->export->magnetComments, - $code - ) - ); - - continue; - } - - if (!$remoteMagnetComments = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet comments feed URL "%s" has broken response'), - $manifest->export->magnetComments - ) - ); - - continue; - } - - foreach ((object) $remoteMagnetComments as $remoteMagnetComment) - { - // Validate - $error = []; - - if (!Valid::magnetComment($remoteMagnetComment, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet comments feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->magnetComments, - print_r( - $remoteMagnetComment, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Aliases check - if (!isset($aliasUserId[$remoteMagnetComment->userId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote userId "%s" not found in URL "%s" %s'), - $remoteMagnetComment->userId, - $manifest->export->magnetComments, - print_r( - $remoteMagnetComment, - true - ), - ) - ); - - continue; - } - - if (!isset($aliasMagnetId[$remoteMagnetComment->magnetId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote magnetId "%s" not found in URL "%s" %s'), - $remoteMagnetComment->magnetId, - $manifest->export->magnetComments, - print_r( - $remoteMagnetComment, - true - ), - ) - ); - - continue; - } - - // Skip import on magnet comment approved required - if (API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY && !$remoteMagnetComment->approved) - { - // No debug warnings in this case, continue next item - - continue; - } - - // Add new magnet comment if not exist by timestamp added for this user - if (!$db->findMagnetComment($aliasMagnetId[$remoteMagnetComment->magnetId], - $aliasUserId[$remoteMagnetComment->userId], - $remoteMagnetComment->timeAdded)) - { - // Parent comment provided - if (is_int($remoteMagnetComment->magnetCommentIdParent)) - { - $localMagnetCommentIdParent = null; // @TODO feature not in use yet - } - - else - { - $localMagnetCommentIdParent = null; - } - - $db->addMagnetComment( - $aliasMagnetId[$remoteMagnetComment->magnetId], - $aliasUserId[$remoteMagnetComment->userId], - $localMagnetCommentIdParent, - $remoteMagnetComment->value, - $remoteMagnetComment->approved, - true, - $remoteMagnetComment->timeAdded - ); - } - } - } - - // Magnet downloads - if (API_IMPORT_MAGNET_DOWNLOADS_ENABLED) - { - // Skip non-condition addresses - $error = []; - - if (!Valid::url($manifest->export->magnetDownloads, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet downloads feed URL "%s" invalid: %s'), - $manifest->export->magnetDownloads, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->magnetDownloads, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet downloads feed URL "%s" unreachable with code: "%s"'), - $manifest->export->magnetDownloads, - $code - ) - ); - - continue; - } - - if (!$remoteMagnetDownloads = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet downloads feed URL "%s" has broken response'), - $manifest->export->magnetDownloads - ) - ); - - continue; - } - - foreach ((object) $remoteMagnetDownloads as $remoteMagnetDownload) - { - // Validate - $error = []; - - if (!Valid::magnetDownload($remoteMagnetDownload, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet downloads feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->magnetDownloads, - print_r( - $remoteMagnetDownload, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Aliases check - if (!isset($aliasUserId[$remoteMagnetDownload->userId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote userId "%s" not found in URL "%s" %s'), - $remoteMagnetDownload->userId, - $manifest->export->magnetDownloads, - print_r( - $remoteMagnetDownload, - true - ), - ) - ); - - continue; - } - - if (!isset($aliasMagnetId[$remoteMagnetDownload->magnetId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote magnetId "%s" not found in URL "%s" %s'), - $remoteMagnetDownload->magnetId, - $manifest->export->magnetDownloads, - print_r( - $remoteMagnetDownload, - true - ), - ) - ); - - continue; - } - - // Add new magnet download if not exist by timestamp added for this user - if (!$db->findMagnetDownload($aliasMagnetId[$remoteMagnetDownload->magnetId], - $aliasUserId[$remoteMagnetDownload->userId], - $remoteMagnetDownload->timeAdded)) - { - $db->addMagnetDownload( - $aliasMagnetId[$remoteMagnetDownload->magnetId], - $aliasUserId[$remoteMagnetDownload->userId], - $remoteMagnetDownload->timeAdded - ); - } - } - } - - // Magnet views - if (API_IMPORT_MAGNET_VIEWS_ENABLED) - { - $error = []; - - if (!Valid::url($manifest->export->magnetViews, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet views feed URL "%s" invalid: %s'), - $manifest->export->magnetViews, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->magnetViews, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet views feed URL "%s" unreachable with code: "%s"'), - $manifest->export->magnetViews, - $code - ) - ); - - continue; - } - - if (!$remoteMagnetViews = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet views feed URL "%s" has broken response'), - $manifest->export->magnetViews - ) - ); - - continue; - } - - foreach ((object) $remoteMagnetViews as $remoteMagnetView) - { - // Validate - $error = []; - - if (!Valid::magnetView($remoteMagnetView, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet views feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->magnetViews, - print_r( - $remoteMagnetView, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Aliases check - if (!isset($aliasUserId[$remoteMagnetView->userId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote userId "%s" not found in URL "%s" %s'), - $remoteMagnetView->userId, - $manifest->export->magnetViews, - print_r( - $remoteMagnetView, - true - ), - ) - ); - - continue; - } - - if (!isset($aliasMagnetId[$remoteMagnetView->magnetId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote magnetId "%s" not found in URL "%s" %s'), - $remoteMagnetView->magnetId, - $manifest->export->magnetViews, - print_r( - $remoteMagnetView, - true - ), - ) - ); - - continue; - } - - // Add new magnet view if not exist by timestamp added for this user - if (!$db->findMagnetView($aliasMagnetId[$remoteMagnetView->magnetId], - $aliasUserId[$remoteMagnetView->userId], - $remoteMagnetView->timeAdded)) - { - $db->addMagnetView( - $aliasMagnetId[$remoteMagnetView->magnetId], - $aliasUserId[$remoteMagnetView->userId], - $remoteMagnetView->timeAdded - ); - } - } - } - - // Magnet stars - if (API_IMPORT_MAGNET_STARS_ENABLED) - { - $error = []; - - if (!Valid::url($manifest->export->magnetStars, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet stars feed URL "%s" invalid: %s'), - $manifest->export->magnetStars, - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Call feed - $curl = new Curl($manifest->export->magnetStars, API_USER_AGENT); - - $debug['http']['total']++; - - if (200 != $code = $curl->getCode()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet stars feed URL "%s" unreachable with code: "%s"'), - $manifest->export->magnetStars, - $code - ) - ); - - continue; - } - - if (!$remoteMagnetStars = $curl->getResponse()) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet stars feed URL "%s" has broken response'), - $manifest->export->magnetStars - ) - ); - - continue; - } - - foreach ((object) $remoteMagnetStars as $remoteMagnetStar) - { - // Validate - $error = []; - - if (!Valid::magnetStar($remoteMagnetStar, $error)) - { - array_push( - $debug['dump'], - sprintf( - _('Magnet stars feed URL "%s" has invalid protocol: "%s" error: "%s"'), - $manifest->export->magnetStars, - print_r( - $remoteMagnetStar, - true - ), - print_r( - $error, - true - ) - ) - ); - - continue; - } - - // Aliases check - if (!isset($aliasUserId[$remoteMagnetStar->userId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote userId "%s" not found in URL "%s" %s'), - $remoteMagnetStar->userId, - $manifest->export->magnetStars, - print_r( - $remoteMagnetStar, - true - ), - ) - ); - - continue; - } - - if (!isset($aliasMagnetId[$remoteMagnetStar->magnetId])) - { - array_push( - $debug['dump'], - sprintf( - _('Local alias for remote magnetId "%s" not found in URL "%s" %s'), - $remoteMagnetStar->magnetId, - $manifest->export->magnetStars, - print_r( - $remoteMagnetStar, - true - ), - ) - ); - - continue; - } - - // Add new magnet star if not exist by timestamp added for this user - if (!$db->findMagnetStar($aliasMagnetId[$remoteMagnetStar->magnetId], - $aliasUserId[$remoteMagnetStar->userId], - $remoteMagnetStar->timeAdded)) - { - $db->addMagnetStar( - $aliasMagnetId[$remoteMagnetStar->magnetId], - $aliasUserId[$remoteMagnetStar->userId], - $remoteMagnetStar->value, - $remoteMagnetStar->timeAdded - ); - } - } - } - } - } - } - - $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_IMPORT_FEED_ENABLED) -{ - @mkdir(LOG_DIRECTORY, 0770, true); - - if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_CRONTAB_IMPORT_FEED_FILENAME, 'a+')) - { - fwrite($handle, print_r($debug, true)); - fclose($handle); - - chmod(LOG_DIRECTORY . '/' . LOG_CRONTAB_IMPORT_FEED_FILENAME, 0770); - } -} \ No newline at end of file diff --git a/src/crontab/scrape.php b/src/crontab/scrape.php deleted file mode 100644 index f974165..0000000 --- a/src/crontab/scrape.php +++ /dev/null @@ -1,158 +0,0 @@ - [ - '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); - } -} \ No newline at end of file diff --git a/src/crontab/sitemap.php b/src/crontab/sitemap.php deleted file mode 100644 index 1c65e87..0000000 --- a/src/crontab/sitemap.php +++ /dev/null @@ -1,86 +0,0 @@ - [ - '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, ''); - fwrite($handle, ''); - - foreach ($db->getMagnets() as $magnet) - { - if ($magnet->public && $magnet->approved) - { - fwrite($handle, sprintf('%s/magnet.php?magnetId=%s', WEBSITE_URL, $magnet->magnetId)); - } - } - - fwrite($handle, ''); - 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); - } -} \ No newline at end of file diff --git a/src/library/curl.php b/src/library/curl.php deleted file mode 100644 index 24c4021..0000000 --- a/src/library/curl.php +++ /dev/null @@ -1,97 +0,0 @@ -_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; - } -} \ No newline at end of file diff --git a/src/library/database.php b/src/library/database.php deleted file mode 100644 index ccb6c0e..0000000 --- a/src/library/database.php +++ /dev/null @@ -1,1708 +0,0 @@ -_db = new PDO('mysql:dbname=' . $database . ';host=' . $host . ';port=' . $port . ';charset=utf8', $username, $password, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); - $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - $this->_db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); - $this->_db->setAttribute(PDO::ATTR_TIMEOUT, 600); - - $this->_debug = (object) - [ - 'query' => (object) - [ - 'select' => (object) - [ - 'total' => 0 - ], - 'insert' => (object) - [ - 'total' => 0 - ], - 'update' => (object) - [ - 'total' => 0 - ], - 'delete' => (object) - [ - 'total' => 0 - ], - ] - ]; - } - - // Tools - public function beginTransaction() { - - $this->_db->beginTransaction(); - } - - public function commit() { - - $this->_db->commit(); - } - - public function rollBack() { - - $this->_db->rollBack(); - } - - public function getDebug() { - - return $this->_debug; - } - - // Scheme - public function addScheme(string $value) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `scheme` SET `value` = ?'); - - $query->execute([$value]); - - return $this->_db->lastInsertId(); - } - - public function getScheme(int $schemeId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `scheme` WHERE `schemeId` = ?'); - - $query->execute([$schemeId]); - - return $query->fetch(); - } - - public function findScheme(string $value) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `scheme` WHERE `value` = ?'); - - $query->execute([$value]); - - return $query->fetch(); - } - - public function initSchemeId(string $value) : int { - - if ($result = $this->findScheme($value)) { - - return $result->schemeId; - } - - return $this->addScheme($value); - } - - // Host - public function addHost(string $value) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `host` SET `value` = ?'); - - $query->execute([$value]); - - return $this->_db->lastInsertId(); - } - - public function getHost(int $hostId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `host` WHERE `hostId` = ?'); - - $query->execute([$hostId]); - - return $query->fetch(); - } - - public function findHost(string $value) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `host` WHERE `value` = ?'); - - $query->execute([$value]); - - return $query->fetch(); - } - - public function initHostId(string $value) : int { - - if ($result = $this->findHost($value)) { - - return $result->hostId; - } - - return $this->addHost($value); - } - - // Port - public function addPort(mixed $value) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `port` SET `value` = ?'); - - $query->execute([$value]); - - return $this->_db->lastInsertId(); - } - - public function getPort(int $portId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `port` WHERE `portId` = ?'); - - $query->execute([$portId]); - - return $query->fetch(); - } - - public function findPort(mixed $value) { - - $this->_debug->query->select->total++; - - if ($value) { - - $query = $this->_db->prepare('SELECT * FROM `port` WHERE `value` = ?'); - - $query->execute([$value]); - - } else { - - $query = $this->_db->query('SELECT * FROM `port` WHERE `value` IS NULL'); - } - - return $query->fetch(); - } - - public function initPortId(mixed $value) : int { - - if ($result = $this->findPort($value)) { - - return $result->portId; - } - - return $this->addPort($value); - } - - // URI - public function addUri(mixed $value) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `uri` SET `value` = ?'); - - $query->execute([$value]); - - return $this->_db->lastInsertId(); - } - - public function getUri(int $uriId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `uri` WHERE `uriId` = ?'); - - $query->execute([$uriId]); - - return $query->fetch(); - } - - public function findUri(mixed $value) { - - $this->_debug->query->select->total++; - - if ($value) { - - $query = $this->_db->prepare('SELECT * FROM `uri` WHERE `value` = ?'); - - $query->execute([$value]); - - } else { - - $query = $this->_db->query('SELECT * FROM `uri` WHERE `value` IS NULL'); - } - - return $query->fetch(); - } - - public function initUriId(mixed $value) : int { - - if ($result = $this->findUri($value)) { - - return $result->uriId; - } - - return $this->addUri($value); - } - - // Info Hash - public function addInfoHash(mixed $value, int $version) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `infoHash` SET `value` = ?, `version` = ?'); - - $query->execute([$value, $version]); - - return $this->_db->lastInsertId(); - } - - public function getInfoHash(int $infoHashId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `infoHash` WHERE `infoHashId` = ?'); - - $query->execute([$infoHashId]); - - return $query->fetch(); - } - - public function findInfoHash(string $value, int $version) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `infoHash` WHERE `value` = ? AND `version` = ?'); - - $query->execute([$value, $version]); - - return $query->fetch(); - } - - public function initInfoHashId(mixed $value, int $version) : int { - - if ($result = $this->findInfoHash($value, $version)) { - - return $result->infoHashId; - } - - return $this->addInfoHash($value, $version); - } - - // Address Tracker - public function addAddressTracker(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `addressTracker` SET `schemeId` = ?, `hostId` = ?, `portId` = ?, `uriId` = ?'); - - $query->execute([$schemeId, $hostId, $portId, $uriId]); - - return $this->_db->lastInsertId(); - } - - public function getAddressTracker(int $addressTrackerId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `addressTracker` WHERE `addressTrackerId` = ?'); - - $query->execute([$addressTrackerId]); - - return $query->fetch(); - } - - public function findAddressTracker(int $schemeId, int $hostId, mixed $portId, mixed $uriId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `addressTracker` WHERE `schemeId` = ' . (int) $schemeId . ' - AND `hostId` = ' . (int) $hostId . ' - AND `portId` ' . ($portId ? ' = ' . (int) $portId : ' IS NULL ') . ' - AND `uriId` ' . ($uriId ? ' = ' . (int) $uriId : ' IS NULL ')); - - return $query->fetch(); - } - - public function initAddressTrackerId(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - if ($result = $this->findAddressTracker($schemeId, $hostId, $portId, $uriId)) { - - return $result->addressTrackerId; - } - - return $this->addAddressTracker($schemeId, $hostId, $portId, $uriId); - } - - // Acceptable Source - public function addAcceptableSource(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `acceptableSource` SET `schemeId` = ?, `hostId` = ?, `portId` = ?, `uriId` = ?'); - - $query->execute([$schemeId, $hostId, $portId, $uriId]); - - return $this->_db->lastInsertId(); - } - - public function getAcceptableSource(int $acceptableSourceId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `acceptableSource` WHERE `acceptableSourceId` = ?'); - - $query->execute([$acceptableSourceId]); - - return $query->fetch(); - } - - public function findAcceptableSource(int $schemeId, int $hostId, mixed $portId, mixed $uriId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `acceptableSource` WHERE `schemeId` = ' . (int) $schemeId . ' - AND `hostId` = ' . (int) $hostId . ' - AND `portId` ' . ($portId ? ' = ' . (int) $portId : ' IS NULL ') . ' - AND `uriId` ' . ($uriId ? ' = ' . (int) $uriId : ' IS NULL ')); - - return $query->fetch(); - } - - public function initAcceptableSourceId(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - if ($result = $this->findAcceptableSource($schemeId, $hostId, $portId, $uriId)) { - - return $result->acceptableSourceId; - } - - return $this->addAcceptableSource($schemeId, $hostId, $portId, $uriId); - } - - // eXact Source - public function addExactSource(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `eXactSource` SET `schemeId` = ?, `hostId` = ?, `portId` = ?, `uriId` = ?'); - - $query->execute([$schemeId, $hostId, $portId, $uriId]); - - return $this->_db->lastInsertId(); - } - - public function getExactSource(int $eXactSourceId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `eXactSource` WHERE `eXactSourceId` = ?'); - - $query->execute([$eXactSourceId]); - - return $query->fetch(); - } - - public function findExactSource(int $schemeId, int $hostId, mixed $portId, mixed $uriId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `eXactSource` WHERE `schemeId` = ' . (int) $schemeId . ' - AND `hostId` = ' . (int) $hostId . ' - AND `portId` ' . ($portId ? ' = ' . (int) $portId : ' IS NULL ') . ' - AND `uriId` ' . ($uriId ? ' = ' . (int) $uriId : ' IS NULL ')); - - return $query->fetch(); - } - - public function initExactSourceId(int $schemeId, int $hostId, mixed $portId, mixed $uriId) : int { - - if ($result = $this->findExactSource($schemeId, $hostId, $portId, $uriId)) { - - return $result->eXactSourceId; - } - - return $this->addExactSource($schemeId, $hostId, $portId, $uriId); - } - - // Keyword Topic - public function addKeywordTopic(string $value) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `keywordTopic` SET `value` = ?'); - - $query->execute([$value]); - - return $this->_db->lastInsertId(); - } - - public function findKeywordTopic(string $value) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `keywordTopic` WHERE `value` = ?'); - - $query->execute([$value]); - - return $query->fetch(); - } - - public function getKeywordTopic(int $keywordTopicId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `keywordTopic` WHERE `keywordTopicId` = ?'); - - $query->execute([$keywordTopicId]); - - return $query->fetch(); - } - - public function initKeywordTopicId(string $value) : int { - - if ($result = $this->findKeywordTopic($value)) { - - return $result->keywordTopicId; - } - - return $this->addKeywordTopic($value); - } - - // User - public function addUser(string $address, bool $approved, $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `user` SET `address` = ?, `approved` = ?, `timeAdded` = ?'); - - $query->execute([$address, (int) $approved, $timeAdded]); - - return $this->_db->lastInsertId(); - } - - public function getUser(int $userId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `user` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch(); - } - - public function getUsers() { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `user`'); - - return $query->fetchAll(); - } - - public function getUsersTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `user`'); - - $query->execute(); - - return $query->fetch()->result; - } - - public function getUsersTotalByPublic(mixed $public) : int { - - $this->_debug->query->select->total++; - - if (is_null($public)) - { - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `user` WHERE `public` IS NULL'); - $query->execute(); - } - else - { - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `user` WHERE `public` = ?'); - $query->execute([(int) $public]); - } - - return $query->fetch()->result; - } - - public function findUserByAddress(string $address) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `user` WHERE `address` = ?'); - - $query->execute([$address]); - - return $query->fetch(); - } - - public function initUserId(string $address, bool $approved, int $timeAdded) : int { - - if ($result = $this->findUserByAddress($address)) { - - return $result->userId; - } - - return $this->addUser($address, $approved, $timeAdded); - } - - public function updateUserApproved(int $userId, mixed $approved, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `user` SET `approved` = ?, `timeUpdated` = ? WHERE `userId` = ?'); - - $query->execute([(int) $approved, $timeUpdated, $userId]); - - return $query->rowCount(); - } - - public function updateUserPublic(int $userId, bool $public, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `user` SET `public` = ?, `timeUpdated` = ? WHERE `userId` = ?'); - - $query->execute([(int) $public, $timeUpdated, $userId]); - - return $query->rowCount(); - } - - public function updateUserTimeAdded(int $userId, int $timeAdded) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `user` SET `timeAdded` = ? WHERE `userId` = ?'); - - $query->execute([$timeAdded, $userId]); - - return $query->rowCount(); - } - - public function updateUserTimeUpdated(int $userId, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `user` SET `timeUpdated` = ? WHERE `userId` = ?'); - - $query->execute([$timeUpdated, $userId]); - - return $query->rowCount(); - } - - // Magnet - public function addMagnet(int $userId, - int $xl, - string $dn, - string $linkSource, - bool $public, - bool $comments, - bool $sensitive, - bool $approved, - int $timeAdded, - mixed $timeUpdated = null) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnet` SET `userId` = ?, - `xl` = ?, - `dn` = ?, - `linkSource` = ?, - `public` = ?, - `comments` = ?, - `sensitive` = ?, - `approved` = ?, - `timeAdded` = ?, - `timeUpdated` = ?'); - - $query->execute( - [ - $userId, - $xl, - $dn, - $linkSource, - $public ? 1 : 0, - $comments ? 1 : 0, - $sensitive ? 1 : 0, - $approved ? 1 : 0, - $timeAdded, - $timeUpdated - ] - ); - - return $this->_db->lastInsertId(); - } - - public function getMagnet(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnet` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetch(); - } - - public function getMagnets() { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnet`'); - - $query->execute(); - - return $query->fetchAll(); - } - - public function getMagnetsTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnet`'); - - $query->execute(); - - return $query->fetch()->result; - } - - public function findMagnet(int $userId, int $timeAdded) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnet` WHERE `userId` = ? AND `timeAdded` = ?'); - - $query->execute([$userId, $timeAdded]); - - return $query->fetch(); - } - - public function findMagnetsByUserId(int $userId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnet` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetchAll(); - } - - public function findMagnetsTotalByUserId(int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnet` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch()->result; - } - - public function getMagnetsTotalByUsersPublic(bool $public) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnet` - JOIN `user` ON (`user`.`userId` = `magnet`.`userId`) - WHERE `user`.`public` = ?'); - - $query->execute([(int) $public]); - - return $query->fetch()->result; - } - - public function updateMagnetXl(int $magnetId, int $xl, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `xl` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$xl, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetDn(int $magnetId, string $dn, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `dn` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$dn, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetTitle(int $magnetId, string $title, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `title` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$title, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetPreview(int $magnetId, string $preview, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `preview` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$preview, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetDescription(int $magnetId, string $description, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `description` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$description, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetPublic(int $magnetId, bool $public, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `public` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([(int) $public, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetComments(int $magnetId, bool $comments, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `comments` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([(int) $comments, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetSensitive(int $magnetId, bool $sensitive, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `sensitive` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([(int) $sensitive, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetApproved(int $magnetId, bool $approved, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `approved` = ?, `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([(int) $approved, $timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetTimeUpdated(int $magnetId, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `timeUpdated` = ? WHERE `magnetId` = ?'); - - $query->execute([$timeUpdated, $magnetId]); - - return $query->rowCount(); - } - - public function updateMagnetTimeAdded(int $magnetId, int $timeAdded) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnet` SET `timeAdded` = ? WHERE `magnetId` = ?'); - - $query->execute([$timeAdded, $magnetId]); - - return $query->rowCount(); - } - - // Magnet to Info Hash - public function addMagnetToInfoHash(int $magnetId, int $infoHashId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetToInfoHash` SET `magnetId` = ?, `infoHashId` = ?'); - - $query->execute([$magnetId, $infoHashId]); - - return $this->_db->lastInsertId(); - } - - public function findMagnetToInfoHashByMagnetId(int $magnetId) - { - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToInfoHash` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetchAll(); - } - - // Magnet to AddressTracker - public function addMagnetToAddressTracker(int $magnetId, int $addressTrackerId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetToAddressTracker` SET `magnetId` = ?, `addressTrackerId` = ?'); - - $query->execute([$magnetId, $addressTrackerId]); - - return $this->_db->lastInsertId(); - } - - public function updateMagnetToAddressTrackerSeeders(int $magnetToAddressTrackerId, int $seeders, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetToAddressTracker` SET `seeders` = ?, `timeUpdated` = ? WHERE `magnetToAddressTrackerId` = ?'); - - $query->execute([$seeders, $timeUpdated, $magnetToAddressTrackerId]); - - return $query->rowCount(); - } - - public function updateMagnetToAddressTrackerCompleted(int $magnetToAddressTrackerId, int $completed, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetToAddressTracker` SET `completed` = ?, `timeUpdated` = ? WHERE `magnetToAddressTrackerId` = ?'); - - $query->execute([$completed, $timeUpdated, $magnetToAddressTrackerId]); - - return $query->rowCount(); - } - - public function updateMagnetToAddressTrackerLeechers(int $magnetToAddressTrackerId, int $leechers, int $timeUpdated) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetToAddressTracker` SET `leechers` = ?, `timeUpdated` = ? WHERE `magnetToAddressTrackerId` = ?'); - - $query->execute([$leechers, $timeUpdated, $magnetToAddressTrackerId]); - - return $query->rowCount(); - } - - public function updateMagnetToAddressTrackerTimeOffline(int $magnetToAddressTrackerId, mixed $timeOffline) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetToAddressTracker` SET `timeOffline` = ? WHERE `magnetToAddressTrackerId` = ?'); - - $query->execute([$timeOffline, $magnetToAddressTrackerId]); - - return $query->rowCount(); - } - - public function deleteMagnetToAddressTrackerByMagnetId(int $magnetId) : int { - - $this->_debug->query->delete->total++; - - $query = $this->_db->prepare('DELETE FROM `magnetToAddressTracker` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->rowCount(); - } - - public function findMagnetToAddressTracker(int $magnetId, int $addressTrackerId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToAddressTracker` WHERE `magnetId` = ? AND `addressTrackerId` = ?'); - - $query->execute([$magnetId, $addressTrackerId]); - - return $query->fetch(); - } - - public function findAddressTrackerByMagnetId(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToAddressTracker` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetchAll(); - } - - public function getMagnetToAddressTrackerScrapeQueue(int $limit) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToAddressTracker` - - WHERE `timeOffline` IS NULL - - ORDER BY `timeUpdated` ASC, RAND() - - LIMIT ' . (int) $limit); - - $query->execute(); - - return $query->fetchAll(); - } - - public function resetMagnetToAddressTrackerTimeOfflineByTimeout(int $timeOffline) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetToAddressTracker` SET `timeOffline` = NULL WHERE `timeOffline` < ?'); - - $query->execute( - [ - time() - $timeOffline - ] - ); - - return $query->rowCount(); - } - - public function initMagnetToAddressTrackerId(int $magnetId, int $addressTrackerId) : int { - - if ($result = $this->findMagnetToAddressTracker($magnetId, $addressTrackerId)) { - - return $result->magnetToAddressTrackerId; - } - - return $this->addMagnetToAddressTracker($magnetId, $addressTrackerId); - } - - public function getMagnetToAddressTrackerSeedersSumByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`seeders`) AS `result` FROM `magnetToAddressTracker` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAddressTrackerCompletedSumByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`completed`) AS `result` FROM `magnetToAddressTracker` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAddressTrackerLeechersSumByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`leechers`) AS `result` FROM `magnetToAddressTracker` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAcceptableSourceTotalByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetToAcceptableSource` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAddressTrackerSeedersSum() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`seeders`) AS `result` FROM `magnetToAddressTracker`'); - - $query->execute(); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAddressTrackerCompletedSum() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`completed`) AS `result` FROM `magnetToAddressTracker`'); - - $query->execute(); - - return (int) $query->fetch()->result; - } - - public function getMagnetToAddressTrackerLeechersSum() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT SUM(`leechers`) AS `result` FROM `magnetToAddressTracker`'); - - $query->execute(); - - return (int) $query->fetch()->result; - } - - // Magnet to AcceptableSource - public function addMagnetToAcceptableSource(int $magnetId, int $acceptableSourceId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetToAcceptableSource` SET `magnetId` = ?, `acceptableSourceId` = ?'); - - $query->execute([$magnetId, $acceptableSourceId]); - - return $this->_db->lastInsertId(); - } - - public function deleteMagnetToAcceptableSourceByMagnetId(int $magnetId) : int { - - $this->_debug->query->delete->total++; - - $query = $this->_db->prepare('DELETE FROM `magnetToAcceptableSource` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->rowCount(); - } - - public function findMagnetToAcceptableSource(int $magnetId, int $acceptableSourceId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToAcceptableSource` WHERE `magnetId` = ? AND `acceptableSourceId` = ?'); - - $query->execute([$magnetId, $acceptableSourceId]); - - return $query->fetch(); - } - - public function findAcceptableSourceByMagnetId(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToAcceptableSource` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetchAll(); - } - - public function initMagnetToAcceptableSourceId(int $magnetId, int $acceptableSourceId) : int { - - if ($result = $this->findMagnetToAcceptableSource($magnetId, $acceptableSourceId)) { - - return $result->magnetToAcceptableSourceId; - } - - return $this->addMagnetToAcceptableSource($magnetId, $acceptableSourceId); - } - - // Magnet to eXactSource - public function addMagnetToExactSource(int $magnetId, int $eXactSourceId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetToExactSource` SET `magnetId` = ?, `eXactSourceId` = ?'); - - $query->execute([$magnetId, $eXactSourceId]); - - return $this->_db->lastInsertId(); - } - - public function deleteMagnetToExactSourceByMagnetId(int $magnetId) : int { - - $this->_debug->query->delete->total++; - - $query = $this->_db->prepare('DELETE FROM `magnetToExactSource` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $this->_db->lastInsertId(); - } - - public function findMagnetToExactSource(int $magnetId, int $eXactSourceId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToExactSource` WHERE `magnetId` = ? AND `eXactSourceId` = ?'); - - $query->execute([$magnetId, $eXactSourceId]); - - return $query->fetch(); - } - - public function findExactSourceByMagnetId(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToExactSource` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetchAll(); - } - - public function initMagnetToExactSourceId(int $magnetId, int $eXactSourceId) : int { - - if ($result = $this->findMagnetToEXactSource($magnetId, $eXactSourceId)) { - - return $result->magnetToExactSourceId; - } - - return $this->addMagnetToEXactSource($magnetId, $eXactSourceId); - } - - // Magnet to KeywordTopic - public function addMagnetToKeywordTopic(int $magnetId, int $keywordTopicId) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetToKeywordTopic` SET `magnetId` = ?, `keywordTopicId` = ?'); - - $query->execute([$magnetId, $keywordTopicId]); - - return $this->_db->lastInsertId(); - } - - public function deleteMagnetToKeywordTopicByMagnetId(int $magnetId) : int { - - $this->_debug->query->delete->total++; - - $query = $this->_db->prepare('DELETE FROM `magnetToKeywordTopic` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->rowCount(); - } - - public function findMagnetToKeywordTopic(int $magnetId, int $keywordTopicId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToKeywordTopic` WHERE `magnetId` = ? AND `keywordTopicId` = ?'); - - $query->execute([$magnetId, $keywordTopicId]); - - return $query->fetch(); - } - - public function findKeywordTopicByMagnetId(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetToKeywordTopic` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetchAll(); - } - - public function initMagnetToKeywordTopicId(int $magnetId, int $keywordTopicId) : int { - - if ($result = $this->findMagnetToKeywordTopic($magnetId, $keywordTopicId)) { - - return $result->magnetToKeywordTopicId; - } - - return $this->addMagnetToKeywordTopic($magnetId, $keywordTopicId); - } - - // Magnet lock - public function addMagnetLock(int $magnetId, int $userId, int $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetLock` SET `magnetId` = ?, `userId` = ?, `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $this->_db->lastInsertId(); - } - - public function flushMagnetLock(int $magnetId) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('DELETE FROM `magnetLock` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->rowCount(); - } - - public function findLastMagnetLock(int $magnetId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetLock` WHERE `magnetId` = ? ORDER BY `magnetLockId` DESC LIMIT 1'); - - $query->execute([$magnetId]); - - return $query->fetch(); - } - - // Magnet comment - public function addMagnetComment( int $magnetId, - int $userId, - mixed $magnetCommentIdParent, - string $value, - bool $approved, - bool $public, - int $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetComment` SET `magnetId` = ?, - `userId` = ?, - `magnetCommentIdParent` = ?, - `value` = ?, - `approved` = ?, - `public` = ?, - `timeAdded` = ?'); - - $query->execute( - [ - $magnetId, - $userId, - $magnetCommentIdParent, - $value, - $approved, - $public, - $timeAdded - ] - ); - - return $this->_db->lastInsertId(); - } - - public function updateMagnetCommentPublic(int $magnetCommentId, mixed $public) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetComment` SET `public` = ? WHERE `magnetCommentId` = ?'); - - $query->execute([(int) $public, $magnetCommentId]); - - return $query->rowCount(); - } - - public function updateMagnetCommentApproved(int $magnetCommentId, mixed $approved) : int { - - $this->_debug->query->update->total++; - - $query = $this->_db->prepare('UPDATE `magnetComment` SET `approved` = ? WHERE `magnetCommentId` = ?'); - - $query->execute([(int) $approved, $magnetCommentId]); - - return $query->rowCount(); - } - - public function getMagnetCommentsTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetComment`'); - - return $query->fetch()->result; - } - - public function getMagnetComments() { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `magnetComment`'); - - return $query->fetchAll(); - } - - public function findMagnetCommentsTotalByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `userId`) AS `result` FROM `magnetComment` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetch()->result; - } - - public function findMagnetCommentsTotalByUserId(int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `magnetId`) AS `result` FROM `magnetComment` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch()->result; - } - - public function findMagnetCommentsTotal(int $magnetId, int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetComment` WHERE `magnetId` = ? AND `userId` = ?'); - - $query->execute([$magnetId, $userId]); - - return $query->fetch()->result; - } - - public function findMagnetCommentsTotalByUsersPublic(bool $public) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetComment` - JOIN `user` ON (`user`.`userId` = `magnetComment`.`userId`) - WHERE `user`.`public` = ?'); - - $query->execute([(int) $public]); - - return $query->fetch()->result; - } - - public function findMagnetComments(int $magnetId, mixed $magnetCommentIdParent = null) { - - $this->_debug->query->select->total++; - - if ($magnetCommentIdParent) - { - $query = $this->_db->prepare('SELECT * FROM `magnetComment` WHERE `magnetId` = ? AND `magnetCommentIdParent` = ?'); - - $query->execute([$magnetId, $magnetCommentIdParent]); - } - else - { - $query = $this->_db->prepare('SELECT * FROM `magnetComment` WHERE `magnetId` = ? AND `magnetCommentIdParent` IS NULL'); - - $query->execute([$magnetId]); - } - - return $query->fetchAll(); - } - - public function findMagnetComment(int $magnetId, int $userId, int $timeAdded) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetComment` WHERE `magnetId` = ? AND `userId` = ? AND `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $query->fetch(); - } - - public function getMagnetComment(int $magnetCommentId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetComment` WHERE `magnetCommentId` = ?'); - - $query->execute([$magnetCommentId]); - - return $query->fetch(); - } - - // Magnet star - public function addMagnetStar(int $magnetId, int $userId, bool $value, int $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetStar` SET `magnetId` = ?, `userId` = ?, `value` = ?, `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, (int) $value, $timeAdded]); - - return $this->_db->lastInsertId(); - } - - public function getMagnetStar(int $magnetStarId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetStar` WHERE `magnetStarId` = ?'); - - $query->execute([$magnetStarId]); - - return $query->fetch(); - } - - public function getMagnetStars() { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `magnetStar`'); - - return $query->fetchAll(); - } - - public function getMagnetStarsTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetStar`'); - - return $query->fetch()->result; - } - - public function findMagnetStarsTotalByMagnetId(int $magnetId, bool $value) : int { - - $this->_debug->query->select->total++; - - $total = 0; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `userId`) AS `result` FROM `magnetStar` WHERE `magnetId` = ? AND `value` = ?'); - - $query->execute([$magnetId, (int) $value]); - - return $query->fetch()->result; - } - - public function findMagnetStarsTotalByUserId(int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `magnetId`) AS `result` FROM `magnetStar` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch()->result; - } - - public function findLastMagnetStarValue(int $magnetId, int $userId) : bool { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetStar` WHERE `magnetId` = ? AND `userId` = ? ORDER BY `magnetStarId` DESC'); - - $query->execute([$magnetId, $userId]); - - return $query->rowCount() ? (bool) $query->fetch()->value : false; - } - - public function findMagnetStarsTotalByUsersPublic(bool $public) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetStar` - JOIN `user` ON (`user`.`userId` = `magnetStar`.`userId`) - WHERE `user`.`public` = ?'); - - $query->execute([(int) $public]); - - return $query->fetch()->result; - } - - public function findMagnetStar(int $magnetId, int $userId, int $timeAdded) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetStar` WHERE `magnetId` = ? AND `userId` = ? AND `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $query->fetch(); - } - - // Magnet download - public function addMagnetDownload(int $magnetId, int $userId, int $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetDownload` SET `magnetId` = ?, `userId` = ?, `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $this->_db->lastInsertId(); - } - - public function getMagnetDownload(int $magnetDownloadId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetDownload` WHERE `magnetDownloadId` = ?'); - - $query->execute([$magnetDownloadId]); - - return $query->fetch(); - } - - public function getMagnetDownloads() { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `magnetDownload`'); - - return $query->fetchAll(); - } - - public function getMagnetDownloadsTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetDownload`'); - - return $query->fetch()->result; - } - - public function findMagnetDownloadsTotal(int $magnetId, int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetDownload` WHERE `magnetId` = ? AND `userId` = ?'); - - $query->execute([$magnetId, $userId]); - - return $query->fetch()->result; - } - - public function findMagnetDownload(int $magnetId, int $userId, int $timeAdded) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetDownload` WHERE `magnetId` = ? AND `userId` = ? AND `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $query->fetch(); - } - - public function findMagnetDownloadsTotalByMagnetId(int $magnetId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `userId`) AS `result` FROM `magnetDownload` WHERE `magnetId` = ?'); - - $query->execute([$magnetId]); - - return $query->fetch()->result; - } - - public function findMagnetDownloadsTotalByUserId(int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(DISTINCT `magnetId`) AS `result` FROM `magnetDownload` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch()->result; - } - - public function findMagnetDownloadsTotalByUsersPublic(bool $public) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetDownload` - JOIN `user` ON (`user`.`userId` = `magnetDownload`.`userId`) - WHERE `user`.`public` = ?'); - - $query->execute([(int) $public]); - - return $query->fetch()->result; - } - - // Magnet view - public function addMagnetView(int $magnetId, int $userId, int $timeAdded) : int { - - $this->_debug->query->insert->total++; - - $query = $this->_db->prepare('INSERT INTO `magnetView` SET `magnetId` = ?, `userId` = ?, `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $this->_db->lastInsertId(); - } - - public function getMagnetViews() { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT * FROM `magnetView`'); - - return $query->fetchAll(); - } - - public function getMagnetView(int $magnetViewId) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetView` WHERE `magnetViewId` = ?'); - - $query->execute([$magnetViewId]); - - return $query->fetch(); - } - - public function getMagnetViewsTotal() : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->query('SELECT COUNT(*) AS `result` FROM `magnetView`'); - - return $query->fetch()->result; - } - - public function findMagnetViewsTotalByUserId(int $userId) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetView` WHERE `userId` = ?'); - - $query->execute([$userId]); - - return $query->fetch()->result; - } - - public function findMagnetViewsTotalByUsersPublic(bool $public) : int { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT COUNT(*) AS `result` FROM `magnetView` - JOIN `user` ON (`user`.`userId` = `magnetView`.`userId`) - WHERE `user`.`public` = ?'); - - $query->execute([(int) $public]); - - return $query->fetch()->result; - } - - public function findMagnetView(int $magnetId, int $userId, int $timeAdded) { - - $this->_debug->query->select->total++; - - $query = $this->_db->prepare('SELECT * FROM `magnetView` WHERE `magnetId` = ? AND `userId` = ? AND `timeAdded` = ?'); - - $query->execute([$magnetId, $userId, $timeAdded]); - - return $query->fetch(); - } -} \ No newline at end of file diff --git a/src/library/filter.php b/src/library/filter.php deleted file mode 100644 index 72d9e28..0000000 --- a/src/library/filter.php +++ /dev/null @@ -1,48 +0,0 @@ -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; - } -} diff --git a/src/library/sphinx.php b/src/library/sphinx.php deleted file mode 100644 index 6cb15ae..0000000 --- a/src/library/sphinx.php +++ /dev/null @@ -1,111 +0,0 @@ -_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); - } - } -} diff --git a/src/library/time.php b/src/library/time.php deleted file mode 100644 index 9199d3c..0000000 --- a/src/library/time.php +++ /dev/null @@ -1,45 +0,0 @@ - _('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); - } - } - } -} diff --git a/src/library/valid.php b/src/library/valid.php deleted file mode 100644 index 0c3b843..0000000 --- a/src/library/valid.php +++ /dev/null @@ -1,1898 +0,0 @@ -userId) || !self::userId($value->userId, $error) || - !isset($value->address) || !self::userAddress($value->address, $error) || - !isset($value->timeAdded) || !self::userTimeAdded($value->timeAdded, $error) || - !isset($value->timeUpdated) || !self::userTimeUpdated($value->timeUpdated, $error) || - !isset($value->approved) || !self::userApproved($value->approved, $error) || - - (isset($value->public) && !self::userPublic($value->public, $error))) - { - array_push( - $error, - _('Invalid user data protocol') - ); - - return false; - } - - return true; - } - - public static function userId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid userId data type') - ); - - return false; - } - - return true; - } - - public static function userAddress(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid user address data type') - ); - - return false; - } - - if (!self::host($value, $error)) - { - array_push( - $error, - sprintf( - _('User address "%s" not supported'), - $value - ) - ); - - return false; - } - - return true; - } - - public static function userTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid user timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('User timeAdded out of range') - ); - - return false; - } - - return true; - } - - public static function userTimeUpdated(mixed $value, array &$error = []) : bool - { - if (!(is_int($value) || is_bool($value))) - { - array_push( - $error, - _('Invalid user timeUpdated data type') - ); - - return false; - } - - if (is_int($value) && ($value > time() || $value < 0)) - { - array_push( - $error, - _('User timeUpdated out of range') - ); - - return false; - } - - return true; - } - - public static function userApproved(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid user approved data type') - ); - - return false; - } - - return true; - } - - public static function userPublic(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid user public data type') - ); - - return false; - } - - return true; - } - - // Magnet - public static function magnet(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet data type') - ); - - return false; - } - - // Validate required fields by protocol - if (!isset($value->userId) || !self::userId($value->userId, $error) || - - !isset($value->magnetId) || !self::magnetId($value->magnetId, $error) || - - - !isset($value->title) || !self::magnetTitle($value->title, $error) || - !isset($value->preview) || !self::magnetPreview($value->preview, $error) || - !isset($value->description) || !self::magnetDescription($value->description, $error) || - - !isset($value->comments) || !self::magnetComments($value->comments, $error) || - !isset($value->sensitive) || !self::magnetSensitive($value->sensitive, $error) || - !isset($value->approved) || !self::magnetApproved($value->approved, $error) || - - !isset($value->timeAdded) || !self::magnetTimeAdded($value->timeAdded, $error) || - !isset($value->timeUpdated) || !self::magnetTimeUpdated($value->timeUpdated, $error) || - - !isset($value->dn) || !self::magnetDn($value->dn, $error) || - !isset($value->xt) || !self::magnetXt($value->xt, $error) || - - !isset($value->xl) || !self::magnetXl($value->xl, $error) || - - !isset($value->kt) || !self::magnetKt($value->kt, $error) || - !isset($value->tr) || !self::magnetTr($value->tr, $error) || - !isset($value->as) || !self::magnetAs($value->as, $error) || - !isset($value->xs) || !self::magnetWs($value->xs, $error) || - - (isset($value->public) && !self::magnetPublic($value->public, $error))) - { - array_push( - $error, - _('Invalid magnet data protocol') - ); - - return false; - } - - return true; - } - - public static function magnetId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnetId data type') - ); - - return false; - } - - return true; - } - - public static function magnetTitle(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid magnet title data type') - ); - - return false; - } - - if (!preg_match(MAGNET_TITLE_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Magnet title format does not match condition "%s"'), - MAGNET_TITLE_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < MAGNET_TITLE_MIN_LENGTH || - mb_strlen($value) > MAGNET_TITLE_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet title out of %s-%s chars range'), - MAGNET_TITLE_MIN_LENGTH, - MAGNET_TITLE_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function magnetPreview(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid magnet preview data type') - ); - - return false; - } - - if (!preg_match(MAGNET_PREVIEW_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Magnet preview format does not match condition "%s"'), - MAGNET_PREVIEW_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < MAGNET_PREVIEW_MIN_LENGTH || - mb_strlen($value) > MAGNET_PREVIEW_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet preview out of %s-%s chars range'), - MAGNET_PREVIEW_MIN_LENGTH, - MAGNET_PREVIEW_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function magnetDescription(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid magnet description data type') - ); - - return false; - } - - if (!preg_match(MAGNET_DESCRIPTION_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Magnet description format does not match condition "%s"'), - MAGNET_DESCRIPTION_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < MAGNET_DESCRIPTION_MIN_LENGTH || - mb_strlen($value) > MAGNET_DESCRIPTION_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet description out of %s-%s chars range'), - MAGNET_DESCRIPTION_MIN_LENGTH, - MAGNET_DESCRIPTION_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function magnetComments(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet comments data type') - ); - - return false; - } - - return true; - } - - public static function magnetPublic(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet public data type') - ); - - return false; - } - - return true; - } - - public static function magnetApproved(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet approved data type') - ); - - return false; - } - - return true; - } - - public static function magnetSensitive(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet sensitive data type') - ); - - return false; - } - - return true; - } - - public static function magnetTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnet timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Magnet timeAdded out of range') - ); - - return false; - } - - return true; - } - - public static function magnetTimeUpdated(mixed $value, array &$error = []) : bool - { - if (!(is_int($value) || is_bool($value))) - { - array_push( - $error, - _('Invalid magnet timeUpdated data type') - ); - - return false; - } - - if (is_int($value) && ($value > time() || $value < 0)) - { - array_push( - $error, - _('Magnet timeUpdated out of range') - ); - - return false; - } - - return true; - } - - public static function magnetDn(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid magnet display name data type') - ); - - return false; - } - - if (!preg_match(MAGNET_DN_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Magnet display name format does not match condition "%s"'), - MAGNET_DN_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < MAGNET_DN_MIN_LENGTH || - mb_strlen($value) > MAGNET_DN_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet display name out of %s-%s chars range'), - MAGNET_DN_MIN_LENGTH, - MAGNET_DN_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function magnetXl(mixed $value, array &$error = []) : bool - { - if (!(is_int($value) || is_float($value))) - { - array_push( - $error, - _('Invalid magnet exact length data type') - ); - - return false; - } - - return true; - } - - public static function magnetKt(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet keyword data type') - ); - - return false; - } - - $total = 0; - - foreach ($value as $kt) - { - if (!is_string($kt)) - { - array_push( - $error, - _('Invalid magnet keyword value data type') - ); - - return false; - } - - if (!preg_match(MAGNET_KT_REGEX, $kt)) - { - array_push( - $error, - sprintf( - _('Magnet keyword format does not match condition "%s"'), - MAGNET_KT_REGEX - ) - ); - - return false; - } - - if (mb_strlen($kt) < MAGNET_KT_MIN_LENGTH || - mb_strlen($kt) > MAGNET_KT_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet keyword out of %s-%s chars range'), - MAGNET_KT_MIN_LENGTH, - MAGNET_KT_MAX_LENGTH - ) - ); - - return false; - } - - $total++; - } - - if ($total < MAGNET_KT_MIN_QUANTITY || - $total > MAGNET_KT_MAX_QUANTITY) - { - array_push( - $error, - sprintf( - _('Magnet keywords quantity out of %s-%s range'), - MAGNET_KT_MIN_QUANTITY, - MAGNET_KT_MAX_QUANTITY - ) - ); - - return false; - } - - return true; - } - - public static function magnetXt(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet info hash data type') - ); - - return false; - } - - foreach ($value as $xt) - { - if (empty($xt->version)) - { - array_push( - $error, - _('Magnet info hash version required') - ); - - return false; - } - - if (!(is_int($xt->version) || is_float($xt->version))) - { - array_push( - $error, - _('Invalid magnet info hash version data type') - ); - - return false; - } - - if (empty($xt->value)) - { - array_push( - $error, - _('Magnet info hash value required') - ); - - return false; - } - - if (!is_string($xt->value)) - { - array_push( - $error, - _('Invalid magnet info hash value data type') - ); - - return false; - } - - switch ($xt->version) - { - case 1: - - if (!preg_match('/^([A-z0-9]{40})$/i', $xt->value)) - { - array_push( - $error, - _('Invalid magnet info hash v1 value') - ); - - return false; - } - - break; - - case 2: - - if (!preg_match('/^([A-z0-9]{64})$/i', $xt->value)) - { - array_push( - $error, - _('Invalid magnet info hash v2 value') - ); - - return false; - } - - break; - - default: - - array_push( - $error, - _('Magnet info hash version not supported') - ); - - return false; - } - } - - return true; - } - - public static function magnetTr(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet address tracker data type') - ); - - return false; - } - - $total = 0; - - foreach ($value as $tr) - { - if (!self::url($tr, $error)) - { - array_push( - $error, - sprintf( - _('Invalid magnet address tracker URL "%s"'), - $tr - ) - ); - - return false; - } - - $total++; - } - - if ($total < MAGNET_TR_MIN_QUANTITY || - $total > MAGNET_TR_MAX_QUANTITY) - { - array_push( - $error, - sprintf( - _('Magnet address trackers quantity out of %s-%s range'), - MAGNET_TR_MIN_QUANTITY, - MAGNET_TR_MAX_QUANTITY - ) - ); - - return false; - } - - return true; - } - - public static function magnetAs(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet acceptable source data type') - ); - - return false; - } - - $total = 0; - - foreach ($value as $as) - { - if (!self::url($as, $error)) - { - array_push( - $error, - sprintf( - _('Invalid magnet acceptable source URL "%s"'), - $as - ) - ); - - return false; - } - - $total++; - } - - if ($total < MAGNET_AS_MIN_QUANTITY || - $total > MAGNET_AS_MAX_QUANTITY) - { - array_push( - $error, - sprintf( - _('Magnet acceptable sources quantity out of %s-%s range'), - MAGNET_AS_MIN_QUANTITY, - MAGNET_AS_MAX_QUANTITY - ) - ); - - return false; - } - - return true; - } - - public static function magnetWs(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet web seed data type') - ); - - return false; - } - - $total = 0; - - foreach ($value as $ws) - { - if (!self::url($ws, $error)) - { - array_push( - $error, - sprintf( - _('Invalid magnet web seed URL "%s"'), - $ws - ) - ); - - return false; - } - - $total++; - } - - if ($total < MAGNET_WS_MIN_QUANTITY || - $total > MAGNET_WS_MAX_QUANTITY) - { - array_push( - $error, - sprintf( - _('Magnet web seeds quantity out of %s-%s range'), - MAGNET_WS_MIN_QUANTITY, - MAGNET_WS_MAX_QUANTITY - ) - ); - - return false; - } - - return true; - } - - // Magnet comment - public static function magnetComment(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet comment data type') - ); - - return false; - } - - if (!isset($value->magnetCommentId) || !self::magnetCommentId($value->magnetCommentId, $error) || - !isset($value->magnetId) || !self::magnetId($value->magnetId, $error) || - !isset($value->userId) || !self::userId($value->userId, $error) || - !isset($value->timeAdded) || !self::magnetCommentTimeAdded($value->timeAdded, $error) || - !isset($value->approved) || !self::magnetCommentApproved($value->approved, $error) || - !isset($value->value) || !self::magnetCommentValue($value->value, $error) || - - (isset($value->magnetCommentIdParent) && !self::magnetCommentIdParent($value->magnetCommentIdParent, $error)) || - - (isset($value->public) && !self::magnetCommentPublic($value->public, $error))) - { - array_push( - $error, - _('Invalid magnet comment data protocol') - ); - - return false; - } - - return true; - } - - public static function magnetCommentId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnetCommentId data type') - ); - - return false; - } - - return true; - } - - public static function magnetCommentIdParent(mixed $value, array &$error = []) : bool - { - if (!(is_null($value) || is_int($value))) - { - array_push( - $error, - _('Invalid magnet magnetCommentIdParent data type') - ); - - return false; - } - - if (is_int($value) && !self::magnetCommentId($value, $error)) - { - return false; - } - - return true; - } - - public static function magnetCommentTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnet comment timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Magnet comment timeAdded out of range') - ); - - return false; - } - - return true; - } - - public static function magnetCommentApproved(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet comment approved data type') - ); - - return false; - } - - return true; - } - - public static function magnetCommentPublic(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet comment public data type') - ); - - return false; - } - - return true; - } - - public static function magnetCommentValue(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid magnet comment value data type') - ); - - return false; - } - - if (mb_strlen($value) < MAGNET_COMMENT_MIN_LENGTH || - mb_strlen($value) > MAGNET_COMMENT_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Magnet comment value out of %s-%s chars range'), - MAGNET_COMMENT_MIN_LENGTH, - MAGNET_COMMENT_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - // Magnet download - public static function magnetDownload(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet download data type') - ); - - return false; - } - - if (!isset($value->magnetDownloadId) || !self::magnetDownloadId($value->magnetDownloadId, $error) || - !isset($value->magnetId) || !self::magnetId($value->magnetId, $error) || - !isset($value->userId) || !self::userId($value->userId, $error) || - !isset($value->timeAdded) || !self::magnetDownloadTimeAdded($value->timeAdded, $error) - ) - { - array_push( - $error, - _('Invalid magnet download data protocol') - ); - - return false; - } - - return true; - } - - public static function magnetDownloadId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnetDownloadId data type') - ); - - return false; - } - - return true; - } - - public static function magnetDownloadTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnet download timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Magnet download timeAdded out of range') - ); - - return false; - } - - return true; - } - - // Magnet star - public static function magnetStar(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet star data type') - ); - - return false; - } - - if (!isset($value->magnetStarId) || !self::magnetViewId($value->magnetStarId, $error) || - !isset($value->magnetId) || !self::magnetId($value->magnetId, $error) || - !isset($value->userId) || !self::userId($value->userId, $error) || - !isset($value->timeAdded) || !self::magnetStarTimeAdded($value->timeAdded, $error) || - !isset($value->value) || !self::magnetStarValue($value->value, $error) - ) - { - array_push( - $error, - _('Invalid magnet star data protocol') - ); - - return false; - } - - return true; - } - - public static function magnetStarId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnetStarId data type') - ); - - return false; - } - - return true; - } - - public static function magnetStarValue(mixed $value, array &$error = []) : bool - { - if (!is_bool($value)) - { - array_push( - $error, - _('Invalid magnet star value data type') - ); - - return false; - } - - return true; - } - - public static function magnetStarTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnet star timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Magnet star timeAdded out of range') - ); - - return false; - } - - return true; - } - - // Magnet view - public static function magnetView(mixed $value, array &$error = []) : bool - { - if (!is_object($value)) - { - array_push( - $error, - _('Invalid magnet view data type') - ); - - return false; - } - - if (!isset($value->magnetViewId) || !self::magnetViewId($value->magnetViewId, $error) || - !isset($value->magnetId) || !self::magnetId($value->magnetId, $error) || - !isset($value->userId) || !self::userId($value->userId, $error) || - !isset($value->timeAdded) || !self::magnetViewTimeAdded($value->timeAdded, $error) - ) - { - array_push( - $error, - _('Invalid magnet view data protocol') - ); - - return false; - } - - return true; - } - - public static function magnetViewId(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnetViewId data type') - ); - - return false; - } - - return true; - } - - public static function magnetViewTimeAdded(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid magnet view timeAdded data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Magnet view timeAdded out of range') - ); - - return false; - } - - return true; - } - - // Torrent - public static function torrentAnnounce(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid torrent announce data type') - ); - - return false; - } - - if (!self::url($tr, $error)) - { - array_push( - $error, - sprintf( - _('Invalid torrent announce URL "%s"'), - $tr - ) - ); - - return false; - } - - return true; - } - - public static function torrentAnnounceList(mixed $value, array &$error = []) : bool - { - if (!is_array($value)) - { - array_push( - $error, - _('Invalid torrent announce data type') - ); - - return false; - } - - $total = 0; - - foreach ($value as $list) - { - if (!is_array($list)) - { - array_push( - $error, - _('Invalid torrent announce list') - ); - - return false; - } - - foreach ($list as $announce) - { - if (!self::torrentAnnounce($announce, $error)) - { - array_push( - $error, - sprintf( - _('Invalid torrent announce list URL "%s"'), - $announce - ) - ); - - return false; - } - - $total++; - } - } - - if ($total < TORRENT_ANNOUNCE_MIN_QUANTITY || - $total > TORRENT_ANNOUNCE_MAX_QUANTITY) - { - array_push( - $error, - sprintf( - _('Torrent announces quantity out of %s-%s range'), - TORRENT_ANNOUNCE_MIN_QUANTITY, - TORRENT_ANNOUNCE_MAX_QUANTITY - ) - ); - - return false; - } - - return true; - } - - public static function torrentComment(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid torrent comment data type') - ); - - return false; - } - - if (!preg_match(TORRENT_COMMENT_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Torrent comment format does not match condition "%s"'), - TORRENT_COMMENT_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < TORRENT_COMMENT_MIN_LENGTH || - mb_strlen($value) > TORRENT_COMMENT_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Torrent comment out of %s-%s chars range'), - TORRENT_COMMENT_MIN_LENGTH, - TORRENT_COMMENT_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function torrentCreatedBy(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid torrent created by data type') - ); - - return false; - } - - if (!preg_match(TORRENT_CREATED_BY_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Torrent created by format does not match condition "%s"'), - TORRENT_CREATED_BY_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < TORRENT_CREATED_BY_MIN_LENGTH || - mb_strlen($value) > TORRENT_CREATED_BY_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Torrent created by out of %s-%s chars range'), - TORRENT_CREATED_BY_MIN_LENGTH, - TORRENT_CREATED_BY_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function torrentCreationDate(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid torrent creation date data type') - ); - - return false; - } - - if ($value > time() || $value < 0) - { - array_push( - $error, - _('Torrent creation date out of range') - ); - - return false; - } - - return true; - } - - public static function torrentInfo(mixed $value, array &$error = []) : bool - { - if (!is_array($value)) - { - array_push( - $error, - _('Invalid torrent info data type') - ); - - return false; - } - - if (empty($value)) - { - array_push( - $error, - _('Torrent info has no keys') - ); - - return false; - } - - foreach ($value as $info) - { - if (!is_array($info)) - { - array_push( - $error, - _('Invalid torrent info protocol') - ); - - return false; - } - - if (empty($info)) - { - array_push( - $error, - _('Torrent info has no values') - ); - - return false; - } - - foreach ($info as $key => $data) - { - switch ($key) - { - case 'file-duration': - - if (!self::torrentInfoFileDuration($data, $error)) - { - array_push( - $error, - _('Invalid torrent info file-duration') - ); - - return false; - } - - break; - case 'file-media': - - if (!self::torrentInfoFileMedia($data, $error)) - { - array_push( - $error, - _('Invalid torrent info file-media') - ); - - return false; - } - - break; - case 'files': - - if (!self::torrentInfoFiles($data, $error)) - { - array_push( - $error, - _('Invalid torrent info files') - ); - - return false; - } - - break; - case 'name': - - if (!self::torrentInfoName($data, $error)) - { - array_push( - $error, - _('Invalid torrent info name') - ); - - return false; - } - - break; - case 'piece length': - - if (!self::torrentInfoPieceLength($data, $error)) - { - array_push( - $error, - _('Invalid torrent info piece length') - ); - - return false; - } - - break; - case 'pieces': - - if (!self::torrentInfoPieces($data, $error)) - { - array_push( - $error, - _('Invalid torrent info pieces') - ); - - return false; - } - - break; - case 'private': - - if (!self::torrentInfoPrivate($data, $error)) - { - array_push( - $error, - _('Invalid torrent info private') - ); - - return false; - } - - break; - case 'profiles': - - if (!self::torrentInfoProfiles($data, $error)) - { - array_push( - $error, - _('Invalid torrent info profiles') - ); - - return false; - } - - break; - case 'source': - - if (!self::torrentInfoSource($data, $error)) - { - array_push( - $error, - _('Invalid torrent info source') - ); - - return false; - } - - break; - default: - array_push( - $error, - _('Not supported torrent info key') - ); - } - } - } - - return true; - } - - public static function torrentInfoName(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid torrent info name data type') - ); - - return false; - } - - if (!preg_match(TORRENT_INFO_NAME_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Torrent info name format does not match condition "%s"'), - TORRENT_INFO_NAME_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < TORRENT_INFO_NAME_MIN_LENGTH || - mb_strlen($value) > TORRENT_INFO_NAME_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Torrent info name out of %s-%s chars range'), - TORRENT_INFO_NAME_MIN_LENGTH, - TORRENT_INFO_NAME_MAX_LENGTH - ) - ); - - return false; - } - - return true; - } - - public static function torrentInfoSource(mixed $value, array &$error = []) : bool - { - if (!is_string($value)) - { - array_push( - $error, - _('Invalid torrent info source data type') - ); - - return false; - } - - if (!preg_match(TORRENT_INFO_SOURCE_REGEX, $value)) - { - array_push( - $error, - sprintf( - _('Torrent info source format does not match condition "%s"'), - TORRENT_INFO_SOURCE_REGEX - ) - ); - - return false; - } - - if (mb_strlen($value) < TORRENT_INFO_SOURCE_MIN_LENGTH || - mb_strlen($value) > TORRENT_INFO_SOURCE_MAX_LENGTH) - { - array_push( - $error, - sprintf( - _('Torrent info source out of %s-%s chars range'), - TORRENT_INFO_SOURCE_MIN_LENGTH, - TORRENT_INFO_SOURCE_MAX_LENGTH - ) - ); - - return false; - } - - return true; - - return true; - } - - public static function torrentInfoFileDuration(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid torrent file-duration data type') - ); - - return false; - } - - if ($value < 0) - { - array_push( - $error, - _('Torrent file-duration out of range') - ); - - return false; - } - - return true; - } - - public static function torrentInfoPieceLength(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid torrent info piece length data type') - ); - - return false; - } - - if ($value < 0) - { - array_push( - $error, - _('Torrent torrent info piece length out of range') - ); - - return false; - } - - return true; - } - - public static function torrentInfoPieces(mixed $value, array &$error = []) : bool - { - // @TODO - - return true; - } - - public static function torrentInfoPrivate(mixed $value, array &$error = []) : bool - { - if (!is_int($value)) - { - array_push( - $error, - _('Invalid torrent info private data type') - ); - - return false; - } - - if (!in_array($value, [0, 1])) - { - array_push( - $error, - _('Invalid torrent info private value') - ); - - return false; - } - - return true; - } - - public static function torrentInfoProfiles(mixed $value, array &$error = []) : bool - { - // @TODO - - return true; - } - - public static function torrentInfoFileMedia(mixed $value, array &$error = []) : bool - { - // @TODO - - return true; - } - - public static function torrentInfoFiles(mixed $value, array &$error = []) : bool - { - // @TODO - - return true; - } -} \ No newline at end of file diff --git a/src/public/action.php b/src/public/action.php deleted file mode 100644 index b4b1bdc..0000000 --- a/src/public/action.php +++ /dev/null @@ -1,670 +0,0 @@ - true, - 'message' => _('Internal server error'), - 'title' => sprintf(_('Oops - %s'), WEBSITE_NAME) -]; - -// Begin action request -switch (isset($_GET['target']) ? urldecode($_GET['target']) : false) -{ - case 'profile': - - switch (isset($_GET['toggle']) ? $_GET['toggle'] : false) - { - case 'jidenticon': - - // Yggdrasil connections only - if (!Valid::host($_SERVER['REMOTE_ADDR'])) - { - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); - } - - // Init session - else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) - { - $response->success = false; - $response->message = _('Could not init user session'); - } - - // Get user - else if (!$user = $db->getUser($userId)) - { - $response->success = false; - $response->message = _('Could not init user info'); - } - - // On first visit, redirect user to the welcome page with access level question - else if (is_null($user->public)) - { - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); - } - - // Render icon - else - { - header('Cache-Control: max-age=604800'); - - - $icon = new Jdenticon\Identicon(); - - $icon->setValue($user->{USER_IDENTICON_FIELD}); - $icon->setSize(empty($_GET['size']) ? 100 : (int) $_GET['size']); - $icon->setStyle( - [ - 'backgroundColor' => 'rgba(255, 255, 255, 0)', - ] - ); - $icon->displayImage('webp'); - } - - break; - } - - break; - - case 'comment': - - switch (isset($_GET['toggle']) ? $_GET['toggle'] : false) - { - case 'approved': - - // Yggdrasil connections only - if (!Valid::host($_SERVER['REMOTE_ADDR'])) - { - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); - } - - // Init session - else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) - { - $response->success = false; - $response->message = _('Could not init user session'); - } - - // Get user - else if (!$user = $db->getUser($userId)) - { - $response->success = false; - $response->message = _('Could not init user info'); - } - - // On first visit, redirect user to the welcome page with access level question - else if (is_null($user->public)) - { - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); - } - - // Magnet comment exists - else if (!$magnetComment = $db->getMagnetComment(isset($_GET['magnetCommentId']) && $_GET['magnetCommentId'] > 0 ? (int) $_GET['magnetCommentId'] : 0)) - { - $response->success = false; - $response->message = _('Requested magnet comment not found'); - } - - // Access allowed - else if (!in_array($user->address, MODERATOR_IP_LIST)) { - - $response->success = false; - $response->message = _('Access denied'); - } - - // Validate callback - else if (empty($_GET['callback'])) - { - $response->success = false; - $response->message = _('Callback required'); - } - - // Validate base64 - else if (!$callback = (string) @base64_decode($_GET['callback'])) - { - $response->success = false; - $response->message = _('Invalid callback encoding'); - } - - // Request valid - else - { - if ($magnetComment->approved) - { - $db->updateMagnetCommentApproved($magnetComment->magnetCommentId, false); - - if (USER_AUTO_APPROVE_ON_COMMENT_APPROVE) - { - $db->updateUserApproved($magnetComment->userId, false, time()); - } - } - else - { - $db->updateMagnetCommentApproved($magnetComment->magnetCommentId, true); - - if (USER_AUTO_APPROVE_ON_COMMENT_APPROVE) - { - $db->updateUserApproved($magnetComment->userId, true, time()); - } - } - - // Redirect to edit page - header( - sprintf('Location: %s', $callback) - ); - } - - break; - - case 'new': - - // Yggdrasil connections only - if (!Valid::host($_SERVER['REMOTE_ADDR'])) - { - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); - } - - // Init session - else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) - { - $response->success = false; - $response->message = _('Could not init user session'); - } - - // Get user - else if (!$user = $db->getUser($userId)) - { - $response->success = false; - $response->message = _('Could not init user info'); - } - - // On first visit, redirect user to the welcome page with access level question - else if (is_null($user->public)) - { - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); - } - - // Magnet exists - else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0)) - { - $response->success = false; - $response->message = _('Requested magnet not found'); - } - - // Access allowed - else if (!($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) { - - $response->success = false; - $response->message = _('Magnet not available for this action'); - } - - // Validate callback - else if (empty($_GET['callback'])) - { - $response->success = false; - $response->message = _('Callback required'); - } - - // Validate base64 - else if (!$callback = (string) @base64_decode($_GET['callback'])) - { - $response->success = false; - $response->message = _('Invalid callback encoding'); - } - - // Validate comment value - else if (empty($_POST['comment']) || - mb_strlen($_POST['comment']) < MAGNET_COMMENT_MIN_LENGTH || - mb_strlen($_POST['comment']) > MAGNET_COMMENT_MAX_LENGTH) - { - $response->success = false; - $response->message = sprintf(_('Valid comment value required, %s-%s chars allowed'), MAGNET_COMMENT_MIN_LENGTH, MAGNET_COMMENT_MAX_LENGTH); - } - - // Request valid - else - { - if ($magnetCommentId = $db->addMagnetComment($magnet->magnetId, - $user->userId, - null, // @TODO implement threads - trim($_POST['comment']), - $user->approved || in_array($user->address, MODERATOR_IP_LIST) ? true : MAGNET_COMMENT_DEFAULT_APPROVED, - MAGNET_COMMENT_DEFAULT_PUBLIC, - time())) - { - - // Push event to other nodes - if (API_EXPORT_ENABLED && - API_EXPORT_PUSH_ENABLED && - API_EXPORT_USERS_ENABLED && - API_EXPORT_MAGNETS_ENABLED && - API_EXPORT_MAGNET_COMMENTS_ENABLED) - { - if (!$memoryApiExportPush = $memory->get('api.export.push')) - { - $memoryApiExportPush = []; - } - - $memoryApiExportPush[] = (object) - [ - 'time' => time(), - 'userId' => $user->userId, - 'magnetId' => $magnet->magnetId, - 'magnetCommentId' => $magnetCommentId - ]; - - $memory->set('api.export.push', $memoryApiExportPush, 3600); - } - - // Redirect to referrer page - header( - sprintf('Location: %s#comment-%s', $callback, $magnetCommentId) - ); - } - } - - break; - - default: - - header( - sprintf('Location: %s', WEBSITE_URL) - ); - } - - break; - - case 'magnet': - - switch (isset($_GET['toggle']) ? $_GET['toggle'] : false) - { - case 'star': - - // Yggdrasil connections only - if (!Valid::host($_SERVER['REMOTE_ADDR'])) - { - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); - } - - // Init session - else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) - { - $response->success = false; - $response->message = _('Could not init user session'); - } - - // Get user - else if (!$user = $db->getUser($userId)) - { - $response->success = false; - $response->message = _('Could not init user info'); - } - - // On first visit, redirect user to the welcome page with access level question - else if (is_null($user->public)) - { - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); - } - - // Magnet exists - else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0)) - { - $response->success = false; - $response->message = _('Requested magnet not found'); - } - - // Access allowed - else if (!($_SERVER['REMOTE_ADDR'] == $db->getUser($magnet->userId)->address || in_array($_SERVER['REMOTE_ADDR'], MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) { - - $response->success = false; - $response->message = _('Magnet not available for this action'); - } - - // Validate callback - else if (empty($_GET['callback'])) - { - $response->success = false; - $response->message = _('Callback required'); - } - - // Validate base64 - else if (!$callback = (string) @base64_decode($_GET['callback'])) - { - $response->success = false; - $response->message = _('Invalid callback encoding'); - } - - // Request valid - else - { - // Save star - if ($magnetStarId = $db->addMagnetStar( $magnet->magnetId, - $user->userId, - !$db->findLastMagnetStarValue($magnet->magnetId, $user->userId), - time())) - { - // Push event to other nodes - if (API_EXPORT_ENABLED && - API_EXPORT_PUSH_ENABLED && - API_EXPORT_USERS_ENABLED && - API_EXPORT_MAGNETS_ENABLED && - API_EXPORT_MAGNET_STARS_ENABLED) - { - if (!$memoryApiExportPush = $memory->get('api.export.push')) - { - $memoryApiExportPush = []; - } - - $memoryApiExportPush[] = (object) - [ - 'time' => time(), - 'userId' => $user->userId, - 'magnetId' => $magnet->magnetId, - 'magnetStarId' => $magnetStarId - ]; - - $memory->set('api.export.push', $memoryApiExportPush, 3600); - } - - // Redirect to edit page - header( - sprintf('Location: %s', $callback) - ); - } - } - - break; - - case 'new': - - // Yggdrasil connections only - if (!Valid::host($_SERVER['REMOTE_ADDR'])) - { - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); - } - - // Init session - else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) - { - $response->success = false; - $response->message = _('Could not init user session'); - } - - // Get user - else if (!$user = $db->getUser($userId)) - { - $response->success = false; - $response->message = _('Could not init user info'); - } - - // On first visit, redirect user to the welcome page with access level question - else if (is_null($user->public)) - { - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); - } - - // Validate link - if (empty($_GET['magnet'])) - { - $response->success = false; - $response->message = _('Link required'); - } - - // Validate magnet - else if (!$magnet = Yggverse\Parser\Magnet::parse($_GET['magnet'])) - { - $response->success = false; - $response->message = _('Invalid magnet link'); - } - - // Request valid - else - { - // Begin magnet registration - try - { - $db->beginTransaction(); - - // Init magnet - if ($magnetId = $db->addMagnet( $user->userId, - $magnet->xl, - $magnet->dn, - '', // @TODO deprecated, remove - MAGNET_DEFAULT_PUBLIC, - MAGNET_DEFAULT_COMMENTS, - MAGNET_DEFAULT_SENSITIVE, - $user->approved ? true : MAGNET_DEFAULT_APPROVED, - time())) - { - foreach ($magnet as $key => $value) - { - switch ($key) - { - case 'xt': - foreach ($value as $xt) - { - if (Yggverse\Parser\Magnet::isXTv1($xt)) - { - $db->addMagnetToInfoHash( - $magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($xt), 1 - ) - ); - } - if (Yggverse\Parser\Magnet::isXTv2($xt)) - { - $db->addMagnetToInfoHash( - $magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($xt), 2 - ) - ); - } - } - break; - case 'tr': - foreach ($value as $tr) - { - if (Valid::url($tr)) - { - if ($url = Yggverse\Parser\Url::parse($tr)) - { - $db->initMagnetToAddressTrackerId( - $magnetId, - $db->initAddressTrackerId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'ws': - foreach ($value as $ws) - { - // @TODO - } - break; - case 'as': - foreach ($value as $as) - { - if (Valid::url($as)) - { - if ($url = Yggverse\Parser\Url::parse($as)) - { - $db->initMagnetToAcceptableSourceId( - $magnetId, - $db->initAcceptableSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'xs': - foreach ($value as $xs) - { - if (Valid::url($xs)) - { - if ($url = Yggverse\Parser\Url::parse($xs)) - { - $db->initMagnetToExactSourceId( - $magnetId, - $db->initExactSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'mt': - foreach ($value as $mt) - { - // @TODO - } - break; - case 'x.pe': - foreach ($value as $xPe) - { - // @TODO - } - break; - case 'kt': - foreach ($value as $kt) - { - $db->initMagnetToKeywordTopicId( - $magnetId, - $db->initKeywordTopicId(trim(mb_strtolower(strip_tags(html_entity_decode($kt))))) - ); - } - break; - } - } - - $db->commit(); - - // Redirect to edit page - header(sprintf('Location: %s/edit.php?magnetId=%s', trim(WEBSITE_URL, '/'), $magnetId)); - } - - } catch (Exception $e) { - - var_dump($e); - - $db->rollBack(); - } - } - - break; - } - - break; -} - -?> - - - - - - - - <?php echo $response->title ?> - - - - - - -
-
- -
-
-
-
-
-
-
-
message ?>
-
-
-
- -
-
- - - -
-
- -
-
- - - \ No newline at end of file diff --git a/src/public/api/push.php b/src/public/api/push.php deleted file mode 100644 index ad63354..0000000 --- a/src/public/api/push.php +++ /dev/null @@ -1,938 +0,0 @@ - [ - 'ISO8601' => date('c'), - 'total' => microtime(true), - ], - 'memory' => - [ - 'start' => memory_get_usage(), - 'total' => 0, - 'peaks' => 0 - ], - 'exception' => [] -]; - -// Define response -$response = -[ - 'status' => false, - 'message' => _('Internal server error'), - 'data' => [ - 'user' => [], - 'magnet' => [], - 'magnetDownload' => [], - 'magnetComment' => [], - 'magnetView' => [], - 'magnetStar' => [], - ] -]; - -// Init connections whitelist -$connectionWhiteList = []; - -foreach (json_decode(file_get_contents(__DIR__ . '/../../config/nodes.json')) as $node) -{ - // Skip non-condition addresses - if (!Valid::url($node->manifest)) - { - 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 on same host sub-folders, improve condition - { - continue; - } - - $connectionWhiteList[] = str_replace(['[',']'], false, $manifestUrl->host->name); -} - -// API import enabled -$error = []; - -if (!API_IMPORT_ENABLED) -{ - $response = - [ - 'status' => false, - 'message' => _('Import API disabled') - ]; -} - -// Push API import enabled -else if (!API_IMPORT_PUSH_ENABLED) -{ - $response = - [ - 'status' => false, - 'message' => _('Push API import disabled') - ]; -} - -// Yggdrasil connections only -else if (!Valid::host($_SERVER['REMOTE_ADDR'], $error)) -{ - $response = - [ - 'status' => false, - 'message' => $error - ]; -} - -// Init session -else if (!in_array($_SERVER['REMOTE_ADDR'], $connectionWhiteList)) -{ - $response = - [ - 'status' => false, - 'message' => sprintf( - _('Push API access denied for host "%s"'), - $_SERVER['REMOTE_ADDR'] - ) - ]; -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response = - [ - 'status' => false, - 'message' => _('Could not init user session for this connection') - ]; -} - -// Validate required fields -else if (empty($_POST['data'])) -{ - $response = - [ - 'status' => false, - 'message' => _('Request protocol invalid') - ]; -} - -// Validate required fields -else if (false === $data = json_decode($_POST['data'])) -{ - $response = - [ - 'status' => false, - 'message' => _('Could not decode data requested') - ]; -} - -// Import begin -else -{ - $response = - [ - 'status' => true, - 'message' => sprintf( - _('Connection for "%s" established'), - $_SERVER['REMOTE_ADDR'] - ) - ]; - - // Init alias registry - $aliasUserId = []; - $aliasMagnetId = []; - - try { - - // Transaction begin - $db->beginTransaction(); - - // Process request - foreach ((object) $data as $field => $remote) - { - // Process alias fields - switch ($field) - { - case 'user': - - if (!API_IMPORT_USERS_ENABLED) - { - $response['user'][] = [ - 'status' => false, - 'message' => _('Users import disabled on this node. Related content skipped.') - ]; - - continue 2; - } - - // Validate remote fields - $error = []; - - if (!Valid::user($remote, $error)) - { - $response['user'][] = [ - 'status' => false, - 'message' => sprintf( - _('User data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // Skip import on user approved required - if (API_IMPORT_USERS_APPROVED_ONLY && !$remote->approved) - { - $response['user'][] = [ - 'status' => false, - 'message' => _('Node accepting approved users only') - ]; - - continue 2; - } - - // Init local user by remote address - if (!$local = $db->getUser($db->initUserId($remote->address, - USER_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remote->approved : USER_DEFAULT_APPROVED, - $remote->timeAdded))) - { - $response['user'][] = [ - 'status' => false, - 'message' => _('Could not init user profile') - ]; - - continue 2; - } - - else - { - $response['user'][] = [ - 'status' => true, - 'message' => sprintf( - _('User profile successfully associated with ID "%s"'), - $local->userId - ) - ]; - } - - // Register user alias - $aliasUserId[$remote->userId] = $local->userId; - - // Update time added if newer - if ($local->timeAdded < $remote->timeAdded) - { - $db->updateUserTimeAdded( - $local->userId, - $remote->timeAdded - ); - - $response['user'][] = [ - 'status' => true, - 'message' => sprintf( - _('Field "timeAdded" changed to newer value for user ID "%s"'), - $local->userId - ) - ]; - } - - // Update user info if newer - if ($local->timeUpdated < $remote->timeUpdated) - { - // Update time updated - $db->updateUserTimeUpdated( - $local->userId, - $remote->timeUpdated - ); - - $response['user'][] = [ - 'status' => true, - 'message' => sprintf( - _('Field "timeUpdated" changed to newer value for user ID "%s"'), - $local->userId - ) - ]; - - // Update approved for existing user - if (USER_AUTO_APPROVE_ON_IMPORT_APPROVED && $local->approved !== $remote->approved && $remote->approved) - { - $db->updateUserApproved( - $local->userId, - $remote->approved, - $remote->timeUpdated - ); - - $response['user'][] = [ - 'status' => true, - 'message' => sprintf( - _('Field "approved" changed to newer value for user ID "%s"'), - $local->userId - ) - ]; - } - - // Set public as received remotely - if (!$local->public) - { - $db->updateUserPublic( - $local->userId, - true, - $remote->timeUpdated - ); - - $response['user'][] = [ - 'status' => true, - 'message' => sprintf( - _('Field "public" changed to newer value for user ID "%s"'), - $local->userId - ) - ]; - } - } - - break; - case 'magnet': - - if (!API_IMPORT_MAGNETS_ENABLED) - { - $response['magnet'][] = [ - 'status' => false, - 'message' => _('Magnets import disabled on this node. Related content skipped.') - ]; - - continue 2; - } - - // Validate remote fields - $error = []; - - if (!Valid::magnet($remote, $error)) - { - $response['magnet'][] = [ - 'status' => false, - 'message' => sprintf( - _('Magnet data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // User local alias required - if (!isset($aliasUserId[$remote->userId])) - { - $response['magnet'][] = [ - 'status' => false, - 'message' => _('User data relation not found for magnet'), - ]; - - continue 2; - } - - // Skip import on magnet approved required - if (API_IMPORT_MAGNETS_APPROVED_ONLY && !$remote->approved) - { - $response['magnet'][] = [ - 'status' => false, - 'message' => _('Node accepting approved magnets only') - ]; - - continue 2; - } - - /// Add new magnet if not exist by timestamp added for this user - if ($local = $db->findMagnet($aliasUserId[$remote->userId], $remote->timeAdded)) - { - $response['magnet'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet successfully associated with ID "%s"'), - $local->magnetId - ) - ]; - } - - /// Add and init new magnet if not exist - else if ($local = $db->getMagnet( - $db->addMagnet( - $aliasUserId[$remote->userId], - $remote->xl, - $remote->dn, - '', // @TODO linkSource used for debug only, will be deleted later - true, - $remote->comments, - $remote->sensitive, - MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED ? $remote->approved : MAGNET_DEFAULT_APPROVED, - $remote->timeAdded - ) - ) - ) - { - $response['magnet'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet successfully synced with ID "%s"'), - $local->magnetId - ) - ]; - } - - else - { - $response['magnet'][] = [ - 'status' => false, - 'message' => sprintf( - _('Could not init magnet: %s'), - $remote - ) - ]; - - continue 2; - } - - /// Add magnet alias for this host - $aliasMagnetId[$remote->magnetId] = $local->magnetId; - - /// Update time added if newer - if ($local->timeAdded < $remote->timeAdded) - { - $db->updateMagnetTimeAdded( - $local->magnetId, - $remote->timeAdded - ); - - $response['magnet'][] = [ - 'status' => true, - 'message' => sprintf( - _('Field "timeAdded" changed to newer value for magnet ID "%s"'), - $local->magnetId - ) - ]; - } - - /// Update info if remote newer - if ($local->timeUpdated < $remote->timeUpdated) - { - // Magnet fields - $db->updateMagnetXl($local->magnetId, $remote->xl, $remote->timeUpdated); - $db->updateMagnetDn($local->magnetId, $remote->dn, $remote->timeUpdated); - $db->updateMagnetTitle($local->magnetId, $remote->title, $remote->timeUpdated); - $db->updateMagnetPreview($local->magnetId, $remote->preview, $remote->timeUpdated); - $db->updateMagnetDescription($local->magnetId, $remote->description, $remote->timeUpdated); - $db->updateMagnetComments($local->magnetId, $remote->comments, $remote->timeUpdated); - $db->updateMagnetSensitive($local->magnetId, $remote->sensitive, $remote->timeUpdated); - - if (MAGNET_AUTO_APPROVE_ON_IMPORT_APPROVED && $local->approved !== $remote->approved && $remote->approved) - { - $db->updateMagnetApproved($local->magnetId, $remote->approved, $remote->timeUpdated); - } - - // xt - foreach ((array) $remote->xt as $xt) - { - switch ($xt->version) - { - case 1: - - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($local->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 1) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $local->magnetId, - $db->initInfoHashId( - $xt->value, 1 - ) - ); - } - - break; - - case 2: - - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($local->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 2) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $local->magnetId, - $db->initInfoHashId( - $xt->value, 2 - ) - ); - } - - break; - } - } - - // kt - $db->deleteMagnetToKeywordTopicByMagnetId($local->magnetId); - - foreach ($remote->kt as $kt) - { - $db->initMagnetToKeywordTopicId( - $local->magnetId, - $db->initKeywordTopicId(trim(mb_strtolower($kt))) - ); - } - - // tr - $db->deleteMagnetToAddressTrackerByMagnetId($local->magnetId); - - foreach ($remote->tr as $tr) - { - if ($url = Yggverse\Parser\Url::parse($tr)) - { - $db->initMagnetToAddressTrackerId( - $local->magnetId, - $db->initAddressTrackerId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - - // as - $db->deleteMagnetToAcceptableSourceByMagnetId($local->magnetId); - - foreach ($remote->as as $as) - { - if ($url = Yggverse\Parser\Url::parse($as)) - { - $db->initMagnetToAcceptableSourceId( - $local->magnetId, - $db->initAcceptableSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - - // xs - $db->deleteMagnetToExactSourceByMagnetId($local->magnetId); - - foreach ($remote->xs as $xs) - { - if ($url = Yggverse\Parser\Url::parse($xs)) - { - $db->initMagnetToExactSourceId( - $local->magnetId, - $db->initExactSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - - $response['magnet'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet fields updated to newer version for magnet ID "%s"'), - $local->magnetId - ) - ]; - } - - break; - case 'magnetComment': - - if (!API_IMPORT_MAGNET_COMMENTS_ENABLED) - { - $response['magnetComment'][] = [ - 'status' => false, - 'message' => _('Magnet comments import disabled on this node') - ]; - - continue 2; - } - - // Validate - $error = []; - - if (!Valid::magnetComment($remote, $error)) - { - $response['magnetComment'][] = [ - 'status' => false, - 'message' => sprintf( - _('Magnet comment data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // Skip import on magnet approved required - if (API_IMPORT_MAGNET_COMMENTS_APPROVED_ONLY && !$remote->approved) - { - $response['magnetComment'][] = [ - 'status' => false, - 'message' => _('Node accepting approved magnet comments only: %s') - ]; - - continue 2; - } - - // User local alias required - if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId])) - { - $response['magnetComment'][] = [ - 'status' => false, - 'message' => _('Magnet comment data relation not found for: %s') - ]; - - continue 2; - } - - // Parent comment provided - if (is_int($remote->magnetCommentIdParent)) - { - $localMagnetCommentIdParent = null; // @TODO feature not in use yet - } - - else - { - $localMagnetCommentIdParent = null; - } - - // Magnet comment exists by timestamp added for this user - if ($local = $db->findMagnetComment($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetComment'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet comment successfully associated with ID "%s"'), - $local->magnetCommentId - ) - ]; - } - - // Magnet comment exists by timestamp added for this user, register new one - else if ($magnetCommentId = $db->addMagnetComment($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $localMagnetCommentIdParent, - $remote->value, - $remote->approved, - true, - $remote->timeAdded)) - { - $response['magnetComment'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet comment successfully synced with ID "%s"'), - $magnetCommentId - ) - ]; - } - - break; - case 'magnetDownload': - - // Magnet downloads - if (!API_IMPORT_MAGNET_DOWNLOADS_ENABLED) - { - $response['magnetDownload'][] = [ - 'status' => false, - 'message' => _('Magnet downloads import disabled on this node') - ]; - - continue 2; - } - - // Validate - $error = []; - - if (!Valid::magnetDownload($remote, $error)) - { - $response['magnetDownload'][] = [ - 'status' => false, - 'message' => sprintf( - _('Magnet download data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // User local alias required - if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId])) - { - $response['magnetDownload'][] = [ - 'status' => false, - 'message' => _('Magnet download data relation not found') - ]; - - continue 2; - } - - // Magnet download exists by timestamp added for this user - if ($local = $db->findMagnetDownload($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetDownload'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet download successfully associated with ID "%s"'), - $local->magnetDownloadId - ) - ]; - } - - // Magnet download exists by timestamp added for this user, register new one - else if ($magnetDownloadId = $db->addMagnetDownload($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetDownload'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet download successfully synced with ID "%s"'), - $magnetDownloadId - ) - ]; - } - - break; - case 'magnetStar': - - if (!API_IMPORT_MAGNET_STARS_ENABLED) - { - $response['magnetStar'][] = [ - 'status' => false, - 'message' => _('Magnet stars import disabled on this node') - ]; - - continue 2; - } - - // Validate - $error = []; - - if (!Valid::magnetStar($remote, $error)) - { - $response['magnetStar'][] = [ - 'status' => false, - 'message' => sprintf( - _('Magnet star data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // User local alias required - if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId])) - { - $response['magnetStar'][] = [ - 'status' => false, - 'message' => _('Magnet star data relation not found') - ]; - - continue 2; - } - - // Magnet star exists by timestamp added for this user - if ($local = $db->findMagnetStar($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetStar'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet star successfully associated with ID "%s"'), - $local->magnetStarId - ) - ]; - } - - // Magnet star exists by timestamp added for this user, register new one - else if ($magnetStarId = $db->addMagnetStar($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->value, - $remote->timeAdded)) - { - $response['magnetStar'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet star successfully synced with ID "%s"'), - $magnetStarId - ) - ]; - } - - break; - case 'magnetView': - - if (!API_IMPORT_MAGNET_VIEWS_ENABLED) - { - $response['magnetView'][] = [ - 'status' => false, - 'message' => _('Magnet views import disabled on this node') - ]; - - continue 2; - } - - // Validate - $error = []; - - if (!Valid::magnetView($remote, $error)) - { - $response['magnetView'][] = [ - 'status' => false, - 'message' => sprintf( - _('Magnet view data mismatch protocol with error: %s'), - print_r($error, true) - ), - ]; - - continue 2; - } - - // User local alias required - if (!isset($aliasUserId[$remote->userId]) || !isset($aliasMagnetId[$remote->magnetId])) - { - $response['magnetView'][] = [ - 'status' => false, - 'message' => _('Magnet view data relation not found for: %s') - ]; - - continue 2; - } - - // Magnet view exists by timestamp added for this user - if ($local = $db->findMagnetView($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetView'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet view successfully associated with ID "%s"'), - $local->magnetViewId - ) - ]; - } - - // Magnet view exists by timestamp added for this user, register new one - else if ($magnetViewId = $db->addMagnetView($aliasMagnetId[$remote->magnetId], - $aliasUserId[$remote->userId], - $remote->timeAdded)) - { - $response['magnetView'][] = [ - 'status' => true, - 'message' => sprintf( - _('Magnet view successfully synced with ID "%s"'), - $magnetViewId - ) - ]; - } - - break; - default: - - $response[$field][] = - [ - 'status' => false, - 'message' => _('Field "%s" not supported by protocol') - ]; - - continue 2; - } - } - - $db->commit(); - } - - catch (Exception $error) - { - $debug['exception'][] = print_r($error, true); - - $db->rollBack(); - } -} - -// Debug log -if (LOG_API_PUSH_ENABLED) -{ - @mkdir(LOG_DIRECTORY, 0770, true); - - if ($handle = fopen(LOG_DIRECTORY . '/' . LOG_API_PUSH_FILENAME, 'a+')) - { - $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; - - fwrite( - $handle, - print_r( - [ - 'response' => $response, - 'debug' => $debug - ], - true - ) - ); - - fclose($handle); - - chmod(LOG_DIRECTORY . '/' . LOG_API_PUSH_FILENAME, 0770); - } -} - -// Output -header('Content-Type: application/json; charset=utf-8'); - -echo json_encode($response); \ No newline at end of file diff --git a/src/public/assets/theme/default/css/common.css b/src/public/assets/theme/default/css/common.css deleted file mode 100644 index c5f9fb2..0000000 --- a/src/public/assets/theme/default/css/common.css +++ /dev/null @@ -1,115 +0,0 @@ -* { - border: 0; - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - background: #282b3c; - color: #ccc; - font-family: Sans-serif; - font-size: 13px; -} - -a, -a:visited, -a:active { - color: #96d9a1; - text-decoration: none; - opacity: .9; -} - -a:hover { - opacity: 1; - transition: opacity .5s ease-in-out; -} - -h1, h2, h3, h4, h5 { - display: inline-block; - font-weight: normal; -} - -h1 { - font-size: 16px; -} - -h2 { - color: #ccc; - font-size: 16px; -} - -a h2, -a:visited h2, -a:active h2 { - /* @TODO doubts - color: #a4d4ff; - */ -} - -input, -textarea { - background: #5d627d; - color: #ccc; - border: 0; - border-radius: 3px; - padding: 6px 8px; - font-size: 13px; -} - -textarea:focus, -input:focus { - outline: none; - color: #fff; -} - -textarea { - min-height: 86px; -} - -/* @TODO improve focus out -textarea:focus { - min-height: 120px; -} -*/ - -textarea::placeholder, -input::placeholder { - color: #9698a5; - opacity: 1; -} - -input:hover, -textarea:hover { - background: #636884; -} - -input[type="submit"] { - cursor: pointer; -} - -td { - padding: 2px 0; -} - -header a.logo { - color: #ccc; - font-size: 22px; -} - -header a.logo > span { - color: #96d9a1; -} - -a.button, -a.button:visited, -a.button:active, -a.button:hover, -.button { - background: #5d627d; - color: #ccc; - border: 0; - border-radius: 3px; - padding: 6px 8px; - font-size: 13px; -} \ No newline at end of file diff --git a/src/public/assets/theme/default/css/framework.css b/src/public/assets/theme/default/css/framework.css deleted file mode 100644 index 9b3ebff..0000000 --- a/src/public/assets/theme/default/css/framework.css +++ /dev/null @@ -1,338 +0,0 @@ -.container { - position: relative; - overflow: hidden; - max-width: 748px; - margin: 0 auto; -} - -.row { - position: relative; - overflow: hidden; - padding: 8px; -} - -.column { - position: relative; - float: left; -} - -.float-right { - float: right; -} - -.text-center { - text-align: center; -} - -.text-left { - text-align: left; -} - -.text-right { - text-align: right; -} - -.text-color-green { - color: #96d9a1; -} - -.text-color-red { - color: #d77575; -} - -.text-color-pink { - color: #b55cab; -} - -.text-color-default { - color: #ccc; -} - -/* -.text-color-pink { - color: #a44399; -} -*/ - -.text-color-blue { - color: #5785b7; -} - -.text-color-night { - color: #838695; -} - -.label { - padding: 4px 8px; - border-radius: 3px; -} - -.label-green { - color: #fff; - background-color: #65916d; -} - -.position-relative { - position: relative; -} - -.top--2 { - top: -2px; -} - -.top-2 { - top: 2px; -} - -.line-height-26 { - line-height: 26px; -} - -.border-radius-3 { - border-radius: 3px; -} - -.border-pink-light { - border: 1px #9b6895 solid; -} - -.border-pink { - border: 1px #a44399 solid; -} - -.border-bottom-pink { - border-bottom: 1px #a44399 solid; -} - -.border-default { - border: 1px #5d627d solid; -} - -.border-bottom-default { - border-bottom: 1px #5d627d solid; -} - -.border-top-default { - border-top: 1px #5d627d solid; -} - -.background-color-night { - background-color: #34384f; -} - -/* -.background-color-hover-night-light:hover { - background-color: #363a51; -} -*/ - -.background-color-red { - background-color: #9b4a4a; -} - -.cursor-default { - cursor: default; -} - -.cursor-help { - cursor: help; -} - -.font-width-normal { - font-weight: normal; -} - -.font-size-10 { - font-size: 10px; -} - -.font-size-12 { - font-size: 12px; -} - -.font-size-22 { - font-size: 22px; -} - -.padding-0 { - padding: 0; -} - -.padding-x-0 { - padding-left: 0; - padding-right: 0; -} - -.padding-4 { - padding: 4px; -} - -.padding-t-4 { - padding-top: 4px; -} - -.padding-y-4 { - padding-top: 4px; - padding-bottom: 4px; -} - -.padding-x-4 { - padding-left: 4px; - padding-right: 4px; -} - -.padding-x-8 { - padding-left: 8px; - padding-right: 8px; -} - -.padding-8 { - padding: 8px; -} - -.padding-t-8 { - padding-top: 8px; -} - -.padding-b-8 { - padding-bottom: 8px; -} - -.padding-y-8 { - padding-top: 8px; - padding-bottom: 8px; -} - -.padding-b-16 { - padding-bottom: 16px; -} - -.padding-t-16 { - padding-top: 16px; -} - -.padding-y-16 { - padding-top: 16px; - padding-bottom: 16px; -} - -.padding-x-16 { - padding-left: 16px; - padding-right: 16px; -} - -.padding-16 { - padding: 16px; -} - -.margin-l-4 { - margin-left: 4px; -} - -.margin-l-8 { - margin-left: 8px; -} - -.margin-l-16 { - margin-left: 16px; -} - -.margin-x-4 { - margin-left: 4px; - margin-right: 4px; -} - -.margin-r-4 { - margin-right: 4px; -} - -.margin-r-8 { - margin-right: 8px; -} - -.margin-l-12 { - margin-left: 12px; -} - -.margin-y-8 { - margin-top: 8px; - margin-bottom: 8px; -} - -.margin-t-8 { - margin-top: 8px; -} - -.margin-b-8 { - margin-bottom: 8px; -} - -.margin-t-16 { - margin-top: 16px; -} - -.margin-b-16 { - margin-bottom: 16px; -} - -.margin-b-24 { - margin-bottom: 24px; -} - -.display-block { - display: block; -} - -.opacity-0 { - opacity: 0; -} - -.opacity-06 { - opacity: .6; -} - -.opacity-hover-1:hover { - opacity: 1; - transition: opacity .2s; -} - -*:hover > .parent-hover-opacity-09 { - opacity: .9; - transition: opacity .2s; -} - -.blur-2 { - filter: blur(2px); -} - -.blur-hover-0:hover { - filter: blur(0); -} - -/* responsive rules */ - -.width-100 { - width: 100%; -} - -.width-50 { - width: 50%; -} - -.width-13px { - width: 13px; -} - -@media (max-width: 1220px) { - - .width-tablet-100 { - width: 100%; - } -} - -@media (max-width: 512px) { - - .width-mobile-100 { - width: 100%; - } -} diff --git a/src/public/download.php b/src/public/download.php deleted file mode 100644 index ddadb96..0000000 --- a/src/public/download.php +++ /dev/null @@ -1,335 +0,0 @@ - true, - 'message' => _('Internal server error'), - 'html' => (object) - [ - 'title' => sprintf(_('Oops - %s'), WEBSITE_NAME), - 'h1' => false, - 'link' => (object) [], - ] -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Magnet exists -else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) && $_GET['magnetId'] > 0 ? (int) $_GET['magnetId'] : 0)) -{ - $response->success = false; - $response->message = _('Requested magnet not found'); -} - -// Access allowed -else if (!($_SERVER['REMOTE_ADDR'] == $db->getUser($magnet->userId)->address || in_array($_SERVER['REMOTE_ADDR'], MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved))) { - - $response->success = false; - $response->message = _('Magnet not available for this action'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public)) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -// Request valid -else -{ - // Register magnet download - if ($magnetDownloadId = $db->addMagnetDownload($magnet->magnetId, $user->userId, time())) - { - // Push event to other nodes - if (API_EXPORT_ENABLED && - API_EXPORT_PUSH_ENABLED && - API_EXPORT_USERS_ENABLED && - API_EXPORT_MAGNETS_ENABLED && - API_EXPORT_MAGNET_DOWNLOADS_ENABLED) - { - if (!$memoryApiExportPush = $memory->get('api.export.push')) - { - $memoryApiExportPush = []; - } - - $memoryApiExportPush[] = (object) - [ - 'time' => time(), - 'userId' => $user->userId, - 'magnetId' => $magnet->magnetId, - 'magnetDownloadId' => $magnetDownloadId - ]; - - $memory->set('api.export.push', $memoryApiExportPush, 3600); - } - } - - // Build magnet link - $link = (object) - [ - 'magnet' => [], - 'direct' => [], - ]; - - /// Exact Topic - $xt = []; - - foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - switch ($infoHash->version) - { - case 1: - - $xt[] = sprintf('xt=urn:btih:%s', $infoHash->value); - - break; - - case 2: - - $xt[] = sprintf('xt=urn:btmh:1220%s', $infoHash->value); - - break; - } - } - } - - $link->magnet[] = sprintf('magnet:?%s', implode('&', $xt)); - - /// Display Name - $link->magnet[] = sprintf('dn=%s', urlencode($magnet->dn)); - - // Keyword Topic - $kt = []; - - foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) - { - $kt[] = urlencode($db->getKeywordTopic($result->keywordTopicId)->value); - } - - $link->magnet[] = sprintf('kt=%s', implode('+', $kt)); - - /// Address Tracker - 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; - } - - $link->magnet[] = sprintf('tr=%s', urlencode($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))); - } - - // Append trackers.json - foreach (json_decode(file_get_contents(__DIR__ . '/../config/trackers.json')) as $tracker) - { - $link->magnet[] = sprintf('tr=%s', urlencode($tracker->announce)); - } - - /// Acceptable Source - 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; - } - - $link->magnet[] = sprintf('as=%s', urlencode($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))); - $link->direct[] = $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 - 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; - } - - $link->magnet[] = sprintf('xs=%s', urlencode($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))); - } - - // Return html - $response->html->title = sprintf( - _('%s - Download - %s'), - htmlentities($magnet->title), - WEBSITE_NAME - ); - - $response->html->h1 = htmlentities($magnet->title); - - // @TODO implement .bittorrent, separated v1/v2 magnet links - $response->html->link->magnet = implode('&', array_unique($link->magnet)); - $response->html->link->direct = $link->direct; -} - -?> - - - - - - - - <?php echo $response->html->title ?> - - - - - - -
-
- -
-
-
-
-
-
-
- success) { ?> -
-

html->h1 ?>

-
- -
- - - - - - html->link->direct as $direct) { ?> - - - - - - -
- -
- message ?> -
- -
-
-
- -
-
- - - -
-
- -
-
- - - \ No newline at end of file diff --git a/src/public/edit.php b/src/public/edit.php deleted file mode 100644 index e8bf3e9..0000000 --- a/src/public/edit.php +++ /dev/null @@ -1,914 +0,0 @@ - true, - 'message' => false, - 'form' => (object) - [ - 'title' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'preview' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'description' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'dn' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'xt' => (object) - [ - 'value' => [], - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'kt' => (object) - [ - 'value' => [], - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'tr' => (object) - [ - 'value' => [], - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'as' => (object) - [ - 'value' => [], - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'xs' => (object) - [ - 'value' => [], - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'public' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'comments' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'sensitive' => (object) - [ - 'value' => false, - 'valid' => (object) - [ - 'success' => true, - 'message' => false, - ] - ], - 'approved' => (object) - [ - 'value' => false, - ], - ] -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required to enable resource features'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// Init magnet -else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) ? (int) $_GET['magnetId'] : 0)) { - - $response->success = false; - $response->message = _('Magnet not found!'); -} - -// Validate access -else if (!($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST))) { - - $response->success = false; - $response->message = _('You have no permissions to edit this magnet!'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public)) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -// Process form -else { - - // Validate magnet lock - if ($lastMagnetLock = $db->findLastMagnetLock($magnet->magnetId)) - { - if ($lastMagnetLock->userId != $user->userId && - $lastMagnetLock->timeAdded > time() - MAGNET_EDITOR_LOCK_TIMEOUT) - { - $response->success = false; - $response->message = _('This form have opened by owner or moderator, to prevent overwriting, try attempt later!'); - } - } - - // Lock form for moderators - $db->addMagnetLock($magnet->magnetId, $user->userId, time()); - - // Update form - if (!empty($_POST)) { - - // Push event to other nodes - if (API_EXPORT_ENABLED && - API_EXPORT_PUSH_ENABLED && - API_EXPORT_USERS_ENABLED && - API_EXPORT_MAGNETS_ENABLED) - { - if (!$memoryApiExportPush = $memory->get('api.export.push')) - { - $memoryApiExportPush = []; - } - - $memoryApiExportPush[] = (object) - [ - 'time' => time(), - 'userId' => $user->userId, - 'magnetId' => $magnet->magnetId, - ]; - - $memory->set('api.export.push', $memoryApiExportPush, 3600); - } - - // Approve by moderation request - if (in_array($user->address, MODERATOR_IP_LIST)) - { - $db->updateMagnetApproved($magnet->magnetId, isset($_POST['approved']), time()); - - // Auto-approve user on magnet approve - if (USER_AUTO_APPROVE_ON_MAGNET_APPROVE) - { - $db->updateUserApproved($magnet->userId, isset($_POST['approved']), time()); - } - } - - // Approve by user approved status - else - { - $db->updateMagnetApproved($magnet->magnetId, (bool) $user->approved, time()); - } - - // Social - $db->updateMagnetComments($magnet->magnetId, isset($_POST['comments']) ? true : false, time()); - $db->updateMagnetSensitive($magnet->magnetId, isset($_POST['sensitive']) ? true : false, time()); - - if (isset($_POST['public'])) // could be enabled once only because of distributed database model #1 - { - $db->updateMagnetPublic($magnet->magnetId, true, time()); - } - - // Title - $response->form->title->valid->success = true; - $response->form->title->valid->message = []; - - if (!Valid::magnetTitle($_POST['title'], $response->form->title->valid->message)) - { - $response->form->title->valid->success = false; - } - - else - { - $db->updateMagnetTitle( - $magnet->magnetId, - Filter::magnetTitle($_POST['title']), - time() - ); - } - - // Preview - $response->form->preview->valid->success = true; - $response->form->preview->valid->message = []; - - if (!Valid::magnetPreview($_POST['preview'], $response->form->preview->valid->message)) - { - $response->form->preview->valid->success = false; - } - - else - { - $db->updateMagnetPreview( - $magnet->magnetId, - Filter::magnetPreview($_POST['preview']), - time() - ); - } - - // Description - $response->form->description->valid->success = true; - $response->form->description->valid->message = []; - - if (!Valid::magnetDescription($_POST['description'], $response->form->description->valid->message)) - { - $response->form->description->valid->success = false; - } - - else - { - $db->updateMagnetDescription( - $magnet->magnetId, - Filter::magnetDescription($_POST['description']), - time() - ); - } - - // Display Name - $response->form->dn->valid->success = true; - $response->form->dn->valid->message = []; - - if (!Valid::magnetDn($_POST['dn'], $response->form->dn->valid->message)) - { - $response->form->dn->valid->success = false; - } - - else - { - $db->updateMagnetDn( - $magnet->magnetId, - Filter::magnetDn($_POST['dn']), - time() - ); - } - - // Exact Topic - if (isset($_POST['xt'])) - { - foreach ((array) $_POST['xt'] as $version => $value) - { - switch ($version) - { - case 1: - - if (!empty($value) && Yggverse\Parser\Magnet::isXTv1($value)) - { - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 1) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $magnet->magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($value), 1 - ) - ); - } - } - - break; - - case 2: - - if (!empty($value) && Yggverse\Parser\Magnet::isXTv2($value)) - { - $exist = false; - - foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - if ($infoHash->version == 2) - { - $exist = true; - } - } - } - - if (!$exist) - { - $db->addMagnetToInfoHash( - $magnet->magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($value), 2 - ) - ); - } - } - - break; - } - } - } - - // Keyword Topic - $db->deleteMagnetToKeywordTopicByMagnetId($magnet->magnetId); - - if (!empty($_POST['kt'])) - { - foreach (explode(PHP_EOL, str_replace(['#', ',', ' '], PHP_EOL, $_POST['kt'])) as $kt) - { - $kt = trim($kt); - - if (!empty(trim($kt))) - { - $db->initMagnetToKeywordTopicId( - $magnet->magnetId, - $db->initKeywordTopicId(trim(mb_strtolower(strip_tags(html_entity_decode($kt))))) - ); - } - } - } - - // Address Tracker - $db->deleteMagnetToAddressTrackerByMagnetId($magnet->magnetId); - - if (!empty($_POST['tr'])) - { - $response->form->tr->valid->success = false; - $response->form->tr->valid->message = _('* please, provide at least one Yggdrasil address'); - - foreach (explode(PHP_EOL, str_replace(['#', ',', ' '], PHP_EOL, $_POST['tr'])) as $tr) - { - $tr = trim($tr); - - if (Valid::url($tr)) - { - if ($url = Yggverse\Parser\Url::parse($tr)) - { - $db->initMagnetToAddressTrackerId( - $magnet->magnetId, - $db->initAddressTrackerId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - - $response->form->tr->valid->success = true; - $response->form->tr->valid->message = false; - } - } - } - } - - // Acceptable Source - $db->deleteMagnetToAcceptableSourceByMagnetId($magnet->magnetId); - - if (!empty($_POST['as'])) - { - $response->form->as->valid->success = false; - $response->form->as->valid->message = _('* please, provide at least one Yggdrasil address'); - - foreach (explode(PHP_EOL, str_replace(['#', ',', ' '], PHP_EOL, $_POST['as'])) as $as) - { - $xs = trim($as); - - if (Valid::url($as)) - { - if ($url = Yggverse\Parser\Url::parse($as)) - { - $db->initMagnetToAcceptableSourceId( - $magnet->magnetId, - $db->initAcceptableSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - - $response->form->as->valid->success = true; - $response->form->as->valid->message = false; - } - } - } - } - - // Exact Source - $db->deleteMagnetToExactSourceByMagnetId($magnet->magnetId); - - if (!empty($_POST['xs'])) - { - $response->form->xs->valid->success = false; - $response->form->xs->valid->message = _('* please, provide at least one Yggdrasil address'); - - foreach (explode(PHP_EOL, str_replace(['#', ',', ' '], PHP_EOL, $_POST['xs'])) as $xs) - { - $xs = trim($xs); - - if (Valid::url($xs)) - { - if ($url = Yggverse\Parser\Url::parse($xs)) - { - $db->initMagnetToExactSourceId( - $magnet->magnetId, - $db->initExactSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - - $response->form->xs->valid->success = true; - $response->form->xs->valid->message = false; - } - } - } - } - - // Is valid - if ($response->success && - $response->form->title->valid->success && - $response->form->preview->valid->success && - $response->form->description->valid->success && - $response->form->dn->valid->success && - $response->form->tr->valid->success && - $response->form->as->valid->success && - $response->form->xs->valid->success) - { - // Unlock form - $db->flushMagnetLock($magnet->magnetId); - - // Return redirect to the magnet page - header( - sprintf('Location: %s/magnet.php?magnetId=%s', WEBSITE_URL, $magnet->magnetId) - ); - } - else - { - // Refresh magnet data - $magnet = $db->getMagnet($magnet->magnetId); - - // Replace fields by last POST data - foreach ($_POST as $key => $value) - { - $magnet->{$key} = $value; - } - } - } - - // Meta Title, auto-replace with Display Name on empty value - $response->form->title->value = $magnet->title ? $magnet->title : $magnet->dn; - - // Meta Description - $response->form->preview->value = $magnet->preview; - - // Description - $response->form->description->value = $magnet->description; - - // Magnet settings - $response->form->public->value = (bool) $magnet->public; - $response->form->comments->value = (bool) $magnet->comments; - $response->form->sensitive->value = (bool) $magnet->sensitive; - $response->form->approved->value = (bool) $magnet->approved; - - // Display Name - $response->form->dn->value = $magnet->dn; - - // Exact Topic - foreach ($db->findMagnetToInfoHashByMagnetId($magnet->magnetId) as $result) - { - if ($infoHash = $db->getInfoHash($result->infoHashId)) - { - $response->form->xt->value[$infoHash->version] = $infoHash->value; - } - } - - // Keyword Topic - $kt = []; - foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $result) - { - $kt[] = $db->getKeywordTopic($result->keywordTopicId)->value; - } - - $response->form->kt->value = implode(', ', $kt); - - // 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); - - $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); - } - - $response->form->tr->value = implode(PHP_EOL, $tr); - - // 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); - - $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); - } - - $response->form->as->value = implode(PHP_EOL, $as); - - // 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); - - $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); - } - - $response->form->xs->value = implode(PHP_EOL, $xs); -} - -?> - - - - - - - - <?php echo sprintf(_('Edit - %s'), WEBSITE_NAME) ?> - - - - - - - -
-
- -
-
-
-
-
-
- success) { ?> -
- - -
-
-

-
-
- - - - -
-
- - - - form->xt->value[1]) && empty($response->form->xt->value[2])) { ?> -
- - - - - - -
-
- -
- -
-
- -
-
- -
- address, MODERATOR_IP_LIST)) { ?> -
- -
- -
-
- -
-
-
- -
-
message ?>
-
- -
-
-
-
- - - \ No newline at end of file diff --git a/src/public/faq.php b/src/public/faq.php deleted file mode 100644 index 06000d9..0000000 --- a/src/public/faq.php +++ /dev/null @@ -1,228 +0,0 @@ - - true, - 'message' => _('Internal server error'), -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// On first visit, redirect user to the welcome page with access level question -/* Allow to users read this page before accepting data access type in welcome form -else if (is_null($user->public)) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} -*/ - -?> - - - - - - - <?php echo sprintf(_('F.A.Q. - %s'), WEBSITE_NAME) ?> - - - - - -
-
- -
-
-
-
-
-
-
- success) { ?> -

-
- -
-
-

- - - - - - -
-

open source community-driven BitTorrent registry for Yggdrasil ecosystem.') ?>

-

-
-
-
-

- - - - - - -
-

-

-

-
-
-
-

- - - - - - -
-

Node page.'), WEBSITE_URL) ?>

-
-
-
- -
-
-

- - - - - - -
-

Submit.') ?>

-

-

-
-
-
-

- - - - - - -
-

-

qBittorrent supports all required features, just check Preferences - Advanced - Optional IP address to bind and set to All addresses or Yggdrasil address only.') ?>

-

') ?>

-
-
-
-

- - - - - - -
-

-

-

-
-
-
- -
-
-

- - - - - - -
-

-

Install section.') ?>

-

trackers.json registry to participate shared model testing.') ?>

-
-
-
-

- - - - - - -
-

Issues page!') ?>

-
-
- -
message ?>
- -
-
-
-
-
- - - \ No newline at end of file diff --git a/src/public/import.php b/src/public/import.php deleted file mode 100644 index a233cd9..0000000 --- a/src/public/import.php +++ /dev/null @@ -1,303 +0,0 @@ - - true, - 'message' => _('Internal server error'), -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Init user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not get user session'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public)) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -// Import form magnet link request -else if (!empty($_POST['magnet'])) -{ - // Validate magnet - if (!$magnet = Yggverse\Parser\Magnet::parse($_POST['magnet'])) - { - $response->success = false; - $response->message = _('Could not parse magnet link'); - } - - // Request valid - else - { - // Begin magnet registration - try - { - $db->beginTransaction(); - - // Init magnet - if ($magnetId = $db->addMagnet( $user->userId, - $magnet->xl, - $magnet->dn, - '', // @TODO deprecated, remove - MAGNET_DEFAULT_PUBLIC, - MAGNET_DEFAULT_COMMENTS, - MAGNET_DEFAULT_SENSITIVE, - $user->approved ? true : MAGNET_DEFAULT_APPROVED, - time())) - { - foreach ($magnet as $key => $value) - { - switch ($key) - { - case 'xt': - foreach ($value as $xt) - { - if (Yggverse\Parser\Magnet::isXTv1($xt)) - { - $db->addMagnetToInfoHash( - $magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($xt), 1 - ) - ); - } - if (Yggverse\Parser\Magnet::isXTv2($xt)) - { - $db->addMagnetToInfoHash( - $magnetId, - $db->initInfoHashId( - Yggverse\Parser\Magnet::filterInfoHash($xt), 2 - ) - ); - } - } - break; - case 'tr': - foreach ($value as $tr) - { - if (Valid::url($tr)) - { - if ($url = Yggverse\Parser\Url::parse($tr)) - { - $db->initMagnetToAddressTrackerId( - $magnetId, - $db->initAddressTrackerId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'ws': - foreach ($value as $ws) - { - // @TODO - } - break; - case 'as': - foreach ($value as $as) - { - if (Valid::url($as)) - { - if ($url = Yggverse\Parser\Url::parse($as)) - { - $db->initMagnetToAcceptableSourceId( - $magnetId, - $db->initAcceptableSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'xs': - foreach ($value as $xs) - { - if (Valid::url($xs)) - { - if ($url = Yggverse\Parser\Url::parse($xs)) - { - $db->initMagnetToExactSourceId( - $magnetId, - $db->initExactSourceId( - $db->initSchemeId($url->host->scheme), - $db->initHostId($url->host->name), - $db->initPortId($url->host->port), - $db->initUriId($url->page->uri) - ) - ); - } - } - } - break; - case 'mt': - foreach ($value as $mt) - { - // @TODO - } - break; - case 'x.pe': - foreach ($value as $xPe) - { - // @TODO - } - break; - case 'kt': - foreach ($value as $kt) - { - $db->initMagnetToKeywordTopicId( - $magnetId, - $db->initKeywordTopicId(trim(mb_strtolower(strip_tags(html_entity_decode($kt))))) - ); - } - break; - } - } - - $db->commit(); - } - } - - catch (Exception $error) - { - $response->success = false; - $response->message = sprintf( - _('Internal server error: %s'), - print_r($error, true) - ); - - $db->rollBack(); - } - } - - // Redirect to edit page on success - if ($response->success) - { - header(sprintf('Location: %s/edit.php?magnetId=%s', trim(WEBSITE_URL, '/'), $magnetId)); - } -} - -// Import form torrent file request -else if (!empty($_FILE['torrent'])) -{ - // @TODO -} - -?> - - - - - - - <?php echo sprintf(_('Add - %s'), WEBSITE_NAME) ?> - - - - - - -
-
- -
-
-
-
-
-
-
- success) { ?> -
-

-
-
-
- - -
-
- - -
-
- -
-
- -
- message ?> -
- -
-
-
-
-
- - - \ No newline at end of file diff --git a/src/public/index.php b/src/public/index.php deleted file mode 100644 index 13cf940..0000000 --- a/src/public/index.php +++ /dev/null @@ -1,431 +0,0 @@ - false, - 'page' => 1, -]; - -// Prepare request -$request->query = isset($_GET['query']) ? urldecode((string) $_GET['query']) : ''; -$request->page = isset($_GET['page']) && $_GET['page'] > 0 ? (int) $_GET['page'] : 1; - -// Define response -$response = (object) -[ - 'success' => true, - 'message' => false, - 'magnets' => [], -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required to enable resource features'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public) && !isset($_GET['rss'])) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -// Request valid -else -{ - // Query is magnet link - if ($magnet = Yggverse\Parser\Magnet::is($request->query)) - { - header( - sprintf('Location: %s/action.php?target=magnet&toggle=new&magnet=%s', WEBSITE_URL, urlencode($request->query)) - ); - } - - // Get index - $response->total = $sphinx->searchMagnetsTotal($request->query); - $results = $sphinx->searchMagnets( - $request->query, - $request->page * WEBSITE_PAGINATION_LIMIT - WEBSITE_PAGINATION_LIMIT, - WEBSITE_PAGINATION_LIMIT, - $response->total - ); - - foreach ($results as $result) - { - if ($magnet = $db->getMagnet($result->magnetid)) - { - // Get access info - $accessRead = ($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved)); - $accessEdit = ($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST)); - - // Keywords - $keywords = []; - - foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $keyword) - { - $keywords[] = $db->getKeywordTopic($keyword->keywordTopicId)->value; - } - - $response->magnets[] = (object) - [ - 'magnetId' => $magnet->magnetId, - 'title' => $magnet->title ? htmlentities($magnet->title) : ($magnet->dn ? htmlentities($magnet->dn): false), - 'preview' => $magnet->preview ? nl2br( - htmlentities( - $magnet->preview - ) - ) : false, - 'approved' => (bool) $magnet->approved, - 'public' => (bool) $magnet->public, - 'sensitive' => (bool) $magnet->sensitive, - 'comments' => (bool) $magnet->comments, - 'timeAdded' => $magnet->timeAdded ? Time::ago((int) $magnet->timeAdded) : false, - 'timeUpdated' => $magnet->timeUpdated ? Time::ago((int) $magnet->timeUpdated) : false, - 'keywords' => $keywords, - 'comment' => (object) - [ - 'total' => $db->findMagnetCommentsTotalByMagnetId($magnet->magnetId), - 'status' => $db->findMagnetCommentsTotal($magnet->magnetId, $userId), - ], - 'download' => (object) - [ - 'total' => $db->findMagnetDownloadsTotalByMagnetId($magnet->magnetId), - 'status' => $db->findMagnetDownloadsTotal($magnet->magnetId, $userId), - ], - 'star' => (object) - [ - 'total' => $db->findMagnetStarsTotalByMagnetId($magnet->magnetId, true), - 'status' => $db->findLastMagnetStarValue($magnet->magnetId, $userId), - ], - 'access' => (object) - [ - 'read' => $accessRead, - 'edit' => $accessEdit, - ], - 'seeders' => $db->getMagnetToAddressTrackerSeedersSumByMagnetId($magnet->magnetId), - 'completed' => $db->getMagnetToAddressTrackerCompletedSumByMagnetId($magnet->magnetId), - 'leechers' => $db->getMagnetToAddressTrackerLeechersSumByMagnetId($magnet->magnetId), - 'directs' => $db->getMagnetToAcceptableSourceTotalByMagnetId($magnet->magnetId) - ]; - } - } -} - -if (isset($_GET['rss']) && $response->success) { ?>' . PHP_EOL ?> - - - - <?php echo !empty($request->query) ? sprintf(_('%s - Search - %s'), htmlspecialchars($request->query, ENT_QUOTES, 'UTF-8'), WEBSITE_NAME) - : WEBSITE_NAME ?> - - query ? sprintf('?query=%s', urlencode($request->query)) : false) ?> - magnets as $magnet) { ?> - access->read) { ?> - - <?php echo htmlspecialchars($magnet->title, ENT_QUOTES, 'UTF-8') ?> - preview), ENT_QUOTES, 'UTF-8') ?> - magnetId) ?> - magnetId) ?> - - - - - - - - - - - - - <?php echo !empty($request->query) ? sprintf(_('%s - Search - %s'), htmlspecialchars($request->query, ENT_QUOTES, 'UTF-8'), WEBSITE_NAME) - : sprintf(_('%s - BitTorrent Registry for Yggdrasil'), WEBSITE_NAME) ?> - - - - - - - -
-
- -
-
-
-
-
-
- success) { ?> - magnets) { ?> - magnets as $magnet) { ?> - access->read) { ?> - -
-
- -

title ?>

- leechers && !$magnet->seeders) { ?> - - - - -
-
- public) { ?> - - - - - - - - approved) { ?> - - - - - - - access->edit) { ?> - - - - - - - -
- preview) { ?> -
preview ?>
- - keywords) { ?> -
- keywords as $keyword) { ?> - - # - - -
- -
- - - - timeUpdated ? _('Updated') : _('Added') ?> - timeUpdated ? $magnet->timeUpdated : $magnet->timeAdded ?> - - - - - - - seeders ?> - - - - - - completed ?> - - - - - - - leechers ?> - - directs) { ?> - - - - - directs ?> - - - - - - star->status) { ?> - - - - - - - - - - star->total ?> - - comments) { ?> - - - comment->status) { ?> - - - - - - - - - - comment->total ?> - - - - - download->status) { ?> - - - - - - - - - - download->total ?> - -
-
- - - - - -
-

- -

-
-
- - -
-
message ?>
-
- -
-
- total > WEBSITE_PAGINATION_LIMIT) { ?> -
-
- page, ceil($response->total / WEBSITE_PAGINATION_LIMIT)) ?> - page > 1) { ?> - - - - - page < ceil($response->total / WEBSITE_PAGINATION_LIMIT)) { ?> - - - - -
-
- -
-
- - - - \ No newline at end of file diff --git a/src/public/magnet.php b/src/public/magnet.php deleted file mode 100644 index 2c3f1e0..0000000 --- a/src/public/magnet.php +++ /dev/null @@ -1,508 +0,0 @@ - true, - 'message' => false, - 'magnet' => [], - 'comments' => [], -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required to enable resource features'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// Init magnet -else if (!$magnet = $db->getMagnet(isset($_GET['magnetId']) ? (int) $_GET['magnetId'] : 0)) -{ - $response->success = false; - $response->message = _('Magnet not found! Submit new magnet link by sending address to the search field.'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public) && !isset($_GET['rss'])) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -// Request valid -else -{ - // Get access info - $accessRead = ($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST) || ($magnet->public && $magnet->approved)); - $accessEdit = ($user->address == $db->getUser($magnet->userId)->address || in_array($user->address, MODERATOR_IP_LIST)); - - // Update magnet viewed - if ($accessRead) - { - if ($magnetViewId = $db->addMagnetView($magnet->magnetId, $userId, time())) - { - // Push event to other nodes - if (API_EXPORT_ENABLED && - API_EXPORT_PUSH_ENABLED && - API_EXPORT_USERS_ENABLED && - API_EXPORT_MAGNETS_ENABLED && - API_EXPORT_MAGNET_VIEWS_ENABLED) - { - if (!$memoryApiExportPush = $memory->get('api.export.push')) - { - $memoryApiExportPush = []; - } - - $memoryApiExportPush[] = (object) - [ - 'time' => time(), - 'userId' => $user->userId, - 'magnetId' => $magnet->magnetId, - 'magnetViewId' => $magnetViewId - ]; - - $memory->set('api.export.push', $memoryApiExportPush, 3600); - } - } - } - - // Keywords - $keywords = []; - - foreach ($db->findKeywordTopicByMagnetId($magnet->magnetId) as $keyword) - { - $keywords[] = $db->getKeywordTopic($keyword->keywordTopicId)->value; - } - - $response->user = $user; - $response->magnet = (object) - [ - 'magnetId' => $magnet->magnetId, - 'title' => $magnet->title ? htmlentities($magnet->title) : ($magnet->dn ? htmlentities($magnet->dn): false), - 'preview' => $magnet->preview ? nl2br( - htmlentities( - $magnet->preview - ) - ) : false, - 'description' => $magnet->description ? nl2br( - htmlentities( - $magnet->description - ) - ) : false, - 'approved' => (bool) $magnet->approved, - 'public' => (bool) $magnet->public, - 'sensitive' => (bool) $magnet->sensitive, - 'comments' => (bool) $magnet->comments, - 'timeAdded' => $magnet->timeAdded ? Time::ago((int) $magnet->timeAdded) : false, - 'timeUpdated' => $magnet->timeUpdated ? Time::ago((int) $magnet->timeUpdated) : false, - 'keywords' => $keywords, - 'comment' => (object) - [ - 'total' => $db->findMagnetCommentsTotalByMagnetId($magnet->magnetId), - 'status' => $db->findMagnetCommentsTotal($magnet->magnetId, $userId), - ], - 'download' => (object) - [ - 'total' => $db->findMagnetDownloadsTotalByMagnetId($magnet->magnetId), - 'status' => $db->findMagnetDownloadsTotal($magnet->magnetId, $userId), - ], - 'star' => (object) - [ - 'total' => $db->findMagnetStarsTotalByMagnetId($magnet->magnetId, true), - 'status' => $db->findLastMagnetStarValue($magnet->magnetId, $userId), - ], - 'access' => (object) - [ - 'read' => $accessRead, - 'edit' => $accessEdit, - ], - 'seeders' => $db->getMagnetToAddressTrackerSeedersSumByMagnetId($magnet->magnetId), - 'completed' => $db->getMagnetToAddressTrackerCompletedSumByMagnetId($magnet->magnetId), - 'leechers' => $db->getMagnetToAddressTrackerLeechersSumByMagnetId($magnet->magnetId), - 'directs' => $db->getMagnetToAcceptableSourceTotalByMagnetId($magnet->magnetId), - ]; -} - -if (isset($_GET['rss']) && isset($_GET['target']) && $_GET['target'] == 'comment' && $response->success) { ?>' . PHP_EOL ?> - - - - magnet->magnetId) ?> - <?php echo sprintf(_('%s - Comments - %s'), htmlentities($response->magnet->title), WEBSITE_NAME) ?> - - findMagnetComments($response->magnet->magnetId) as $magnetComment) { ?> - user->address == $db->getUser($magnetComment->userId)->address || in_array($response->user->address, MODERATOR_IP_LIST)) { ?> - - <?php echo sprintf('%s - comment #%s', htmlspecialchars($magnet->title, ENT_QUOTES, 'UTF-8'), $magnetComment->magnetCommentId) ?> - value, ENT_QUOTES, 'UTF-8') ?> - magnet->magnetId, $magnetComment->magnetCommentId) ?> - magnet->magnetId, $magnetComment->magnetCommentId) ?> - - - - - - - - - - - - success) { ?> - <?php echo sprintf(_('%s - %s'), htmlentities($response->magnet->title), WEBSITE_NAME) ?> - - - - <?php echo $response->message ?> - - - - - -
-
- -
-
-
-
-
-
- success) { ?> - magnet->access->read) { ?> -
-
- -

magnet->title ?>

- magnet->leechers && !$response->magnet->seeders) { ?> - - - - -
- magnet->public) { ?> - - - - - - - - magnet->approved) { ?> - - - - - - - magnet->access->edit) { ?> - - - - - - - - - -
- magnet->preview) { ?> -
magnet->preview ?>
- - magnet->description) { ?> -
magnet->description ?>
- - magnet->keywords) { ?> -
- magnet->keywords as $keyword) { ?> - - # - - -
- -
- - - - magnet->timeUpdated ? _('Updated') : _('Added') ?> - magnet->timeUpdated ? $response->magnet->timeUpdated : $response->magnet->timeAdded ?> - - - - - - - magnet->seeders ?> - - - - - - magnet->completed ?> - - - - - - - magnet->leechers ?> - - magnet->directs) { ?> - - - - - magnet->directs ?> - - - - - magnet->star->status) { ?> - - - - - - - - - - magnet->star->total ?> - - magnet->comments) { ?> - - - magnet->comment->status) { ?> - - - - - - - - - - magnet->comment->total ?> - - - - - magnet->download->status) { ?> - - - - - - - - - - magnet->download->total ?> - -
-
- searchMagnetsTotal($magnet->title ? $magnet->title : $magnet->dn, 'similar', MAGNET_STOP_WORDS_SIMILAR)) { ?> - 1) { // skip current magnet ?> -
- -

-
-
-
- searchMagnets( - $magnet->title ? $magnet->title : $magnet->dn, - 0, - 10, - $similarMagnetsTotal, - 'similar', - MAGNET_STOP_WORDS_SIMILAR - ) as $result) { ?> - getMagnet($result->magnetid)) { ?> - magnetid != $response->magnet->magnetId && // skip current magnet - ($response->user->address == $db->getUser($magnet->userId)->address || - in_array($response->user->address, MODERATOR_IP_LIST) || ($magnet->approved && $magnet->public))) { ?> - - - - -
-
- - - magnet->comments) { ?> -
- -

- -
-
- findMagnetComments($response->magnet->magnetId) as $magnetComment) { ?> -
- - user->address == $db->getUser($magnetComment->userId)->address || - in_array($response->user->address, MODERATOR_IP_LIST) || - ($magnetComment->approved && $magnetComment->public)) { ?> -
- value)) ?> -
- - - - - timeAdded) ?> - - - public) { ?> - - - - - - - - approved) { ?> - - - - - - - - user->address, MODERATOR_IP_LIST)) { ?> - - approved) { ?> - - - - - - - - - -
- -
- -
-
- -
-
- -
-
-
- - -
-
-
- - -
-
message ?>
-
- -
-
-
-
- - - - \ No newline at end of file diff --git a/src/public/node.php b/src/public/node.php deleted file mode 100644 index 08df253..0000000 --- a/src/public/node.php +++ /dev/null @@ -1,534 +0,0 @@ - - true, - 'message' => _('Internal server error'), -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Get user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not init user info'); -} - -// On first visit, redirect user to the welcome page with access level question -else if (is_null($user->public)) -{ - header( - sprintf('Location: %s/welcome.php', WEBSITE_URL) - ); -} - -?> - - - - - - - <?php echo sprintf(_('%s instance info'), WEBSITE_NAME) ?> - - - - - - -
-
- -
-
-
-
-
-
-
- success) { ?> -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $tracker) { ?> - - - - $value) { ?> - - - - - - - - - - - - - - - - $node) { ?> - - - - $value) { ?> - - - - - - - - - - - - - - - - $peer) { ?> - - - - $value) { ?> - - - - - - - - - - - - - - -
- -
- public ? _('Distributed') : _('Local') ?> - - - - - -
- address ?> -
- timeUpdated) ?> -
- -
- -
- getUsersTotal() ?> - getUsersTotalByPublic(true)) { ?> - / - - - - - getUsersTotalByPublic(false)) { ?> - / - - - - - - - - - -
- getMagnetsTotal() ?> - getMagnetsTotalByUsersPublic(true)) { ?> - / - - - - - getMagnetsTotalByUsersPublic(false)) { ?> - / - - - - - - - - - -
- getMagnetDownloadsTotal() ?> - findMagnetDownloadsTotalByUsersPublic(true)) { ?> - / - - - - - findMagnetDownloadsTotalByUsersPublic(false)) { ?> - / - - - - - - - - - -
- getMagnetCommentsTotal() ?> - findMagnetCommentsTotalByUsersPublic(true)) { ?> - / - - - - - findMagnetCommentsTotalByUsersPublic(false)) { ?> - / - - - - - - - - - -
- getMagnetStarsTotal() ?> - findMagnetStarsTotalByUsersPublic(true)) { ?> - / - - - - - findMagnetStarsTotalByUsersPublic(false)) { ?> - / - - - - - - - - - -
- getMagnetViewsTotal() ?> - findMagnetViewsTotalByUsersPublic(true)) { ?> - / - - - - - findMagnetViewsTotalByUsersPublic(false)) { ?> - / - - - - - - - - - -
getMagnetToAddressTrackerSeedersSum() ?>
getMagnetToAddressTrackerCompletedSum() ?>
getMagnetToAddressTrackerLeechersSum() ?>
- -
- -
-
-
-
- - - / - - -
- - -
- - -
- - -
- -
-
- - - - - - - - - -
- - - - - -
- - - - - - - - - -
- - - - - -
- - - - - - - - - -
- - - - - -
- -
message ?>
- -
-
-
-
-
- - - \ No newline at end of file diff --git a/src/public/welcome.php b/src/public/welcome.php deleted file mode 100644 index aaac011..0000000 --- a/src/public/welcome.php +++ /dev/null @@ -1,146 +0,0 @@ - - true, - 'message' => _('Internal server error'), -]; - -// Yggdrasil connections only -if (!Valid::host($_SERVER['REMOTE_ADDR'])) -{ - $response->success = false; - $response->message = _('Yggdrasil connection required for this action'); -} - -// Init session -else if (!$userId = $db->initUserId($_SERVER['REMOTE_ADDR'], USER_DEFAULT_APPROVED, time())) -{ - $response->success = false; - $response->message = _('Could not init user session'); -} - -// Init user -else if (!$user = $db->getUser($userId)) -{ - $response->success = false; - $response->message = _('Could not get user session'); -} - -// User can change public level once, because by agreement data could be already sent -// Otherwise, local access level could be changed to public on settings page later -// Redirect to website features -else if (!is_null($user->public)) -{ - header( - sprintf('Location: %s', WEBSITE_URL) - ); -} - -// Apply answer on form submit -else if (isset($_POST['public'])) -{ - if ($db->updateUserPublic($user->userId, (bool) $_POST['public'], time())) - { - header( - sprintf('Location: %s', WEBSITE_URL) - ); - } -} - -?> - - - - - - - <?php echo sprintf(_('Welcome to %s'), WEBSITE_NAME) ?> - - - - - - -
-
- -
-
-
-
-
-
-
- success) { ?> -
-
-

-
-

-

-

address ?>

-
-
- - -
-
- -
-
-
- -
message ?>
- -
-
-
-
-
- - - \ No newline at end of file diff --git a/symfony.lock b/symfony.lock new file mode 100644 index 0000000..c7eb7ca --- /dev/null +++ b/symfony.lock @@ -0,0 +1,279 @@ +{ + "doctrine/annotations": { + "version": "2.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.10", + "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05" + } + }, + "doctrine/doctrine-bundle": { + "version": "2.10", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "2.10", + "ref": "e025a6cb69b195970543820b2f18ad21724473fa" + }, + "files": [ + "config/packages/doctrine.yaml", + "src/Entity/.gitignore", + "src/Repository/.gitignore" + ] + }, + "doctrine/doctrine-migrations-bundle": { + "version": "3.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.1", + "ref": "1d01ec03c6ecbd67c3375c5478c9a423ae5d6a33" + }, + "files": [ + "config/packages/doctrine_migrations.yaml", + "migrations/.gitignore" + ] + }, + "phpunit/phpunit": { + "version": "9.6", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "9.6", + "ref": "7364a21d87e658eb363c5020c072ecfdc12e2326" + }, + "files": [ + ".env.test", + "phpunit.xml.dist", + "tests/bootstrap.php" + ] + }, + "symfony/console": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "da0c8be8157600ad34f10ff0c9cc91232522e047" + }, + "files": [ + "bin/console" + ] + }, + "symfony/crowdin-translation-provider": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "9f5f1508bc80ed56c8a3ae7febc53a8aa982e424" + } + }, + "symfony/debug-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b" + }, + "files": [ + "config/packages/debug.yaml" + ] + }, + "symfony/flex": { + "version": "2.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.0", + "ref": "146251ae39e06a95be0fe3d13c807bcf3938b172" + }, + "files": [ + ".env" + ] + }, + "symfony/framework-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.2", + "ref": "af47254c5e4cd543e6af3e4508298ffebbdaddd3" + }, + "files": [ + "config/packages/cache.yaml", + "config/packages/framework.yaml", + "config/preload.php", + "config/routes/framework.yaml", + "config/services.yaml", + "public/index.php", + "src/Controller/.gitignore", + "src/Kernel.php" + ] + }, + "symfony/mailer": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "4.3", + "ref": "2bf89438209656b85b9a49238c4467bff1b1f939" + }, + "files": [ + "config/packages/mailer.yaml" + ] + }, + "symfony/maker-bundle": { + "version": "1.51", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.0", + "ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f" + } + }, + "symfony/messenger": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.0", + "ref": "ba1ac4e919baba5644d31b57a3284d6ba12d52ee" + }, + "files": [ + "config/packages/messenger.yaml" + ] + }, + "symfony/monolog-bundle": { + "version": "3.8", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "3.7", + "ref": "213676c4ec929f046dfde5ea8e97625b81bc0578" + }, + "files": [ + "config/packages/monolog.yaml" + ] + }, + "symfony/notifier": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.0", + "ref": "178877daf79d2dbd62129dd03612cb1a2cb407cc" + }, + "files": [ + "config/packages/notifier.yaml" + ] + }, + "symfony/phpunit-bridge": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "01dfaa98c58f7a7b5a9b30e6edb7074af7ed9819" + }, + "files": [ + ".env.test", + "bin/phpunit", + "phpunit.xml.dist", + "tests/bootstrap.php" + ] + }, + "symfony/routing": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.2", + "ref": "e0a11b4ccb8c9e70b574ff5ad3dfdcd41dec5aa6" + }, + "files": [ + "config/packages/routing.yaml", + "config/routes.yaml" + ] + }, + "symfony/security-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.0", + "ref": "8a5b112826f7d3d5b07027f93786ae11a1c7de48" + }, + "files": [ + "config/packages/security.yaml" + ] + }, + "symfony/translation": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "64fe617084223633e1dedf9112935d8c95410d3e" + }, + "files": [ + "config/packages/translation.yaml", + "translations/.gitignore" + ] + }, + "symfony/twig-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.3", + "ref": "b7772eb20e92f3fb4d4fe756e7505b4ba2ca1a2c" + }, + "files": [ + "config/packages/twig.yaml", + "templates/base.html.twig" + ] + }, + "symfony/validator": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.3", + "ref": "c32cfd98f714894c4f128bb99aa2530c1227603c" + }, + "files": [ + "config/packages/validator.yaml" + ] + }, + "symfony/web-profiler-bundle": { + "version": "6.3", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "6.1", + "ref": "e42b3f0177df239add25373083a564e5ead4e13a" + }, + "files": [ + "config/packages/web_profiler.yaml", + "config/routes/web_profiler.yaml" + ] + }, + "symfony/webapp-pack": { + "version": "1.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.0", + "ref": "aece95c8a188f6e6d04f01ccb8678d1764fd2642" + }, + "files": [ + "config/packages/messenger.yaml" + ] + }, + "twig/extra-bundle": { + "version": "v3.7.1" + } +} diff --git a/templates/bundles/TwigBundle/Exception/error404.html.twig b/templates/bundles/TwigBundle/Exception/error404.html.twig new file mode 100644 index 0000000..4509a25 --- /dev/null +++ b/templates/bundles/TwigBundle/Exception/error404.html.twig @@ -0,0 +1,14 @@ +{% extends theme ~ '/layout.html.twig' %} +{% block title %}{{ 'Not found' | trans }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ 404 +

+
+ {{ 'Page not found!' | trans }} +
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/bundles/TwigBundle/Exception/error500.html.twig b/templates/bundles/TwigBundle/Exception/error500.html.twig new file mode 100644 index 0000000..8567d21 --- /dev/null +++ b/templates/bundles/TwigBundle/Exception/error500.html.twig @@ -0,0 +1,17 @@ +{% extends theme ~ '/layout.html.twig' %} +{% block title %}{{ 'Error' | trans }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{ 'Oops!' | trans }} +

+
+ {{ 'Internal server error' | trans }} +
+ + {{ 'Report' | trans }} + +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/default/activity/event/torrent/add.html.twig b/templates/default/activity/event/torrent/add.html.twig new file mode 100644 index 0000000..b7e2610 --- /dev/null +++ b/templates/default/activity/event/torrent/add.html.twig @@ -0,0 +1,28 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/add.rss.twig b/templates/default/activity/event/torrent/add.rss.twig new file mode 100644 index 0000000..db55c4c --- /dev/null +++ b/templates/default/activity/event/torrent/add.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/approve/add.html.twig b/templates/default/activity/event/torrent/approve/add.html.twig new file mode 100644 index 0000000..e394ecb --- /dev/null +++ b/templates/default/activity/event/torrent/approve/add.html.twig @@ -0,0 +1,30 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have approved torrent' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/approve/add.rss.twig b/templates/default/activity/event/torrent/approve/add.rss.twig new file mode 100644 index 0000000..e378199 --- /dev/null +++ b/templates/default/activity/event/torrent/approve/add.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/approve/delete.html.twig b/templates/default/activity/event/torrent/approve/delete.html.twig new file mode 100644 index 0000000..50d6079 --- /dev/null +++ b/templates/default/activity/event/torrent/approve/delete.html.twig @@ -0,0 +1,30 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disapproved torrent' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/approve/delete.rss.twig b/templates/default/activity/event/torrent/approve/delete.rss.twig new file mode 100644 index 0000000..7252d9f --- /dev/null +++ b/templates/default/activity/event/torrent/approve/delete.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/add.html.twig b/templates/default/activity/event/torrent/categories/add.html.twig new file mode 100644 index 0000000..1d081c7 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added categories edition' | trans }} + {% if torrent.categories.exist %} + + #{{ torrent.categories.id }} + + {% else %} + #{{ torrent.categories.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/add.rss.twig b/templates/default/activity/event/torrent/categories/add.rss.twig new file mode 100644 index 0000000..2262f72 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added categories edition' | trans }} + #{{ torrent.categories.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity-{{ id }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/approve/add.html.twig b/templates/default/activity/event/torrent/categories/approve/add.html.twig new file mode 100644 index 0000000..19c4836 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/approve/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have approved categories edition' | trans }} + {% if torrent.categories.exist %} + + #{{ torrent.categories.id }} + + {% else %} + #{{ torrent.categories.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/approve/add.rss.twig b/templates/default/activity/event/torrent/categories/approve/add.rss.twig new file mode 100644 index 0000000..9395f46 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/approve/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved categories edition' | trans }} + #{{ torrent.categories.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity-{{ id }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/approve/delete.html.twig b/templates/default/activity/event/torrent/categories/approve/delete.html.twig new file mode 100644 index 0000000..2d9b682 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/approve/delete.html.twig @@ -0,0 +1,41 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disapproved categories edition' | trans }} + + {% if torrent.categories.exist %} + + #{{ torrent.categories.id }} + + {% else %} + #{{ torrent.categories.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/approve/delete.rss.twig b/templates/default/activity/event/torrent/categories/approve/delete.rss.twig new file mode 100644 index 0000000..a6a6970 --- /dev/null +++ b/templates/default/activity/event/torrent/categories/approve/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved categories edition' | trans }} + #{{ torrent.categories.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity-{{ id }} + {{ url('torrent_categories_edit', { torrentId : torrent.id, torrentCategoriesId : torrent.categories.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/delete.html.twig b/templates/default/activity/event/torrent/categories/delete.html.twig new file mode 100644 index 0000000..06b972b --- /dev/null +++ b/templates/default/activity/event/torrent/categories/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have deleted categories edition' | trans }} + {% if torrent.categories.exist %} + + #{{ torrent.categories.id }} + + {% else %} + #{{ torrent.categories.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/categories/delete.rss.twig b/templates/default/activity/event/torrent/categories/delete.rss.twig new file mode 100644 index 0000000..063750b --- /dev/null +++ b/templates/default/activity/event/torrent/categories/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have deleted categories edition' | trans }} + #{{ torrent.categories.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_categories_edit', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_categories_edit', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/download/file/add.html.twig b/templates/default/activity/event/torrent/download/file/add.html.twig new file mode 100644 index 0000000..1e0e5e3 --- /dev/null +++ b/templates/default/activity/event/torrent/download/file/add.html.twig @@ -0,0 +1,33 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have downloaded torrent file' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/download/file/add.rss.twig b/templates/default/activity/event/torrent/download/file/add.rss.twig new file mode 100644 index 0000000..d9bee5b --- /dev/null +++ b/templates/default/activity/event/torrent/download/file/add.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have downloaded torrent file' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/download/magnet/add.html.twig b/templates/default/activity/event/torrent/download/magnet/add.html.twig new file mode 100644 index 0000000..edfc68c --- /dev/null +++ b/templates/default/activity/event/torrent/download/magnet/add.html.twig @@ -0,0 +1,33 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have downloaded magnet link' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/download/magnet/add.rss.twig b/templates/default/activity/event/torrent/download/magnet/add.rss.twig new file mode 100644 index 0000000..e29157f --- /dev/null +++ b/templates/default/activity/event/torrent/download/magnet/add.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have downloaded magnet link' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/add.html.twig b/templates/default/activity/event/torrent/locales/add.html.twig new file mode 100644 index 0000000..facd581 --- /dev/null +++ b/templates/default/activity/event/torrent/locales/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added locales edition' | trans }} + {% if torrent.locales.exist %} + + #{{ torrent.locales.id }} + + {% else %} + #{{ torrent.locales.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/add.rss.twig b/templates/default/activity/event/torrent/locales/add.rss.twig new file mode 100644 index 0000000..81ec578 --- /dev/null +++ b/templates/default/activity/event/torrent/locales/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added locales edition' | trans }} + #{{ torrent.locales.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity-{{ id }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/approve/add.html.twig b/templates/default/activity/event/torrent/locales/approve/add.html.twig new file mode 100644 index 0000000..27ed85b --- /dev/null +++ b/templates/default/activity/event/torrent/locales/approve/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have approved locales edition' | trans }} + {% if torrent.locales.exist %} + + #{{ torrent.locales.id }} + + {% else %} + #{{ torrent.locales.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/approve/add.rss.twig b/templates/default/activity/event/torrent/locales/approve/add.rss.twig new file mode 100644 index 0000000..64dbcdd --- /dev/null +++ b/templates/default/activity/event/torrent/locales/approve/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved locales edition' | trans }} + #{{ torrent.locales.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity-{{ id }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/approve/delete.html.twig b/templates/default/activity/event/torrent/locales/approve/delete.html.twig new file mode 100644 index 0000000..d49dcb1 --- /dev/null +++ b/templates/default/activity/event/torrent/locales/approve/delete.html.twig @@ -0,0 +1,41 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disapproved locales edition' | trans }} + + {% if torrent.locales.exist %} + + #{{ torrent.locales.id }} + + {% else %} + #{{ torrent.locales.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/approve/delete.rss.twig b/templates/default/activity/event/torrent/locales/approve/delete.rss.twig new file mode 100644 index 0000000..b0cbada --- /dev/null +++ b/templates/default/activity/event/torrent/locales/approve/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved locales edition' | trans }} + #{{ torrent.locales.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity-{{ id }} + {{ url('torrent_locales_edit', { torrentId : torrent.id, torrentLocalesId : torrent.locales.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/delete.html.twig b/templates/default/activity/event/torrent/locales/delete.html.twig new file mode 100644 index 0000000..4327e41 --- /dev/null +++ b/templates/default/activity/event/torrent/locales/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have deleted locales edition' | trans }} + {% if torrent.locales.exist %} + + #{{ torrent.locales.id }} + + {% else %} + #{{ torrent.locales.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/locales/delete.rss.twig b/templates/default/activity/event/torrent/locales/delete.rss.twig new file mode 100644 index 0000000..1bb311d --- /dev/null +++ b/templates/default/activity/event/torrent/locales/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have deleted locales edition' | trans }} + #{{ torrent.locales.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_locales_edit', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_locales_edit', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/add.html.twig b/templates/default/activity/event/torrent/poster/add.html.twig new file mode 100644 index 0000000..519a6f0 --- /dev/null +++ b/templates/default/activity/event/torrent/poster/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added poster edition' | trans }} + {% if torrent.poster.exist %} + + #{{ torrent.poster.id }} + + {% else %} + #{{ torrent.poster.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/add.rss.twig b/templates/default/activity/event/torrent/poster/add.rss.twig new file mode 100644 index 0000000..348e851 --- /dev/null +++ b/templates/default/activity/event/torrent/poster/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added poster edition' | trans }} + #{{ torrent.poster.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity-{{ id }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/approve/add.html.twig b/templates/default/activity/event/torrent/poster/approve/add.html.twig new file mode 100644 index 0000000..35685c0 --- /dev/null +++ b/templates/default/activity/event/torrent/poster/approve/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have approved poster edition' | trans }} + {% if torrent.poster.exist %} + + #{{ torrent.poster.id }} + + {% else %} + #{{ torrent.poster.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/approve/add.rss.twig b/templates/default/activity/event/torrent/poster/approve/add.rss.twig new file mode 100644 index 0000000..3a75b3d --- /dev/null +++ b/templates/default/activity/event/torrent/poster/approve/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved poster edition' | trans }} + #{{ torrent.poster.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity-{{ id }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/approve/delete.html.twig b/templates/default/activity/event/torrent/poster/approve/delete.html.twig new file mode 100644 index 0000000..483a79f --- /dev/null +++ b/templates/default/activity/event/torrent/poster/approve/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have disapproved poster edition' | trans }} + {% if torrent.poster.exist %} + + #{{ torrent.poster.id }} + + {% else %} + #{{ torrent.poster.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/approve/delete.rss.twig b/templates/default/activity/event/torrent/poster/approve/delete.rss.twig new file mode 100644 index 0000000..42d6527 --- /dev/null +++ b/templates/default/activity/event/torrent/poster/approve/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved poster edition' | trans }} + #{{ torrent.poster.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity-{{ id }} + {{ url('torrent_poster_edit', { torrentId : torrent.id, torrentPosterId : torrent.poster.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/delete.html.twig b/templates/default/activity/event/torrent/poster/delete.html.twig new file mode 100644 index 0000000..c3c7293 --- /dev/null +++ b/templates/default/activity/event/torrent/poster/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have deleted poster edition' | trans }} + {% if torrent.poster.exist %} + + #{{ torrent.poster.id }} + + {% else %} + #{{ torrent.poster.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/poster/delete.rss.twig b/templates/default/activity/event/torrent/poster/delete.rss.twig new file mode 100644 index 0000000..4b7bf6a --- /dev/null +++ b/templates/default/activity/event/torrent/poster/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have deleted poster edition' | trans }} + #{{ torrent.poster.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_poster_edit', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_poster_edit', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/add.html.twig b/templates/default/activity/event/torrent/sensitive/add.html.twig new file mode 100644 index 0000000..e21705a --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added sensitive edition' | trans }} + {% if torrent.sensitive.exist %} + + #{{ torrent.sensitive.id }} + + {% else %} + #{{ torrent.sensitive.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/add.rss.twig b/templates/default/activity/event/torrent/sensitive/add.rss.twig new file mode 100644 index 0000000..e1036bb --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added sensitive edition' | trans }} + #{{ torrent.sensitive.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity-{{ id }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/approve/add.html.twig b/templates/default/activity/event/torrent/sensitive/approve/add.html.twig new file mode 100644 index 0000000..4bad7b0 --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/approve/add.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have approved sensitive edition' | trans }} + {% if torrent.sensitive.exist %} + + #{{ torrent.sensitive.id }} + + {% else %} + #{{ torrent.sensitive.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/approve/add.rss.twig b/templates/default/activity/event/torrent/sensitive/approve/add.rss.twig new file mode 100644 index 0000000..583a42f --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/approve/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved sensitive edition' | trans }} + #{{ torrent.sensitive.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity-{{ id }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/approve/delete.html.twig b/templates/default/activity/event/torrent/sensitive/approve/delete.html.twig new file mode 100644 index 0000000..c02a804 --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/approve/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have disapproved sensitive edition' | trans }} + {% if torrent.sensitive.exist %} + + #{{ torrent.sensitive.id }} + + {% else %} + #{{ torrent.sensitive.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/approve/delete.rss.twig b/templates/default/activity/event/torrent/sensitive/approve/delete.rss.twig new file mode 100644 index 0000000..f1a697f --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/approve/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved sensitive edition' | trans }} + #{{ torrent.sensitive.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity-{{ id }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id, torrentSensitiveId : torrent.sensitive.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/delete.html.twig b/templates/default/activity/event/torrent/sensitive/delete.html.twig new file mode 100644 index 0000000..a2640dc --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/delete.html.twig @@ -0,0 +1,39 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have deleted sensitive edition' | trans }} + {% if torrent.sensitive.exist %} + + #{{ torrent.sensitive.id }} + + {% else %} + #{{ torrent.sensitive.id }} + {% endif %} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/sensitive/delete.rss.twig b/templates/default/activity/event/torrent/sensitive/delete.rss.twig new file mode 100644 index 0000000..3c13424 --- /dev/null +++ b/templates/default/activity/event/torrent/sensitive/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have deleted sensitive edition' | trans }} + #{{ torrent.sensitive.id }} + {{ 'for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_sensitive_edit', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/star/add.html.twig b/templates/default/activity/event/torrent/star/add.html.twig new file mode 100644 index 0000000..9d27547 --- /dev/null +++ b/templates/default/activity/event/torrent/star/add.html.twig @@ -0,0 +1,31 @@ +
+
+ + {{ 'identicon' | trans }} + + {{ 'have added star for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/star/add.rss.twig b/templates/default/activity/event/torrent/star/add.rss.twig new file mode 100644 index 0000000..a5fb324 --- /dev/null +++ b/templates/default/activity/event/torrent/star/add.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added star for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/star/delete.html.twig b/templates/default/activity/event/torrent/star/delete.html.twig new file mode 100644 index 0000000..a160792 --- /dev/null +++ b/templates/default/activity/event/torrent/star/delete.html.twig @@ -0,0 +1,32 @@ + +
+
+ + {{ 'identicon' | trans }} + + {{ 'have removed star from torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% if torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% endif %} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/star/delete.rss.twig b/templates/default/activity/event/torrent/star/delete.rss.twig new file mode 100644 index 0000000..6c4c02c --- /dev/null +++ b/templates/default/activity/event/torrent/star/delete.rss.twig @@ -0,0 +1,24 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have removed star from torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/status/add.html.twig b/templates/default/activity/event/torrent/status/add.html.twig new file mode 100644 index 0000000..0466adc --- /dev/null +++ b/templates/default/activity/event/torrent/status/add.html.twig @@ -0,0 +1,30 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have enabled torrent' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/status/add.rss.twig b/templates/default/activity/event/torrent/status/add.rss.twig new file mode 100644 index 0000000..1f21d67 --- /dev/null +++ b/templates/default/activity/event/torrent/status/add.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have enabled torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + <a href="{{ path('torrent_info', { torrentId : torrent.id }) }}"> + {{ torrent.name }} + </a> + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/status/delete.html.twig b/templates/default/activity/event/torrent/status/delete.html.twig new file mode 100644 index 0000000..3c5dea3 --- /dev/null +++ b/templates/default/activity/event/torrent/status/delete.html.twig @@ -0,0 +1,30 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disabled torrent' | trans }} + + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/status/delete.rss.twig b/templates/default/activity/event/torrent/status/delete.rss.twig new file mode 100644 index 0000000..f1ef108 --- /dev/null +++ b/templates/default/activity/event/torrent/status/delete.rss.twig @@ -0,0 +1,26 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disabled torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + <a href="{{ path('torrent_info', { torrentId : torrent.id }) }}"> + {{ torrent.name }} + </a> + {% endif %} + {% endif %} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/torrent/wanted/add.html.twig b/templates/default/activity/event/torrent/wanted/add.html.twig new file mode 100644 index 0000000..86a8744 --- /dev/null +++ b/templates/default/activity/event/torrent/wanted/add.html.twig @@ -0,0 +1,25 @@ +
+
+ {{ 'Seeders wanted for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + + {{ torrent.name }} + + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + + {{ torrent.name }} + + {% endif %} + {% endif %} +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/torrent/wanted/add.rss.twig b/templates/default/activity/event/torrent/wanted/add.rss.twig new file mode 100644 index 0000000..9459b57 --- /dev/null +++ b/templates/default/activity/event/torrent/wanted/add.rss.twig @@ -0,0 +1,21 @@ + + + {{ 'Seeders wanted for torrent' | trans }} + {% if session.user.moderator or session.user.owner %} + {{ torrent.name }} + {% else %} + {% if torrent.status == false %} + #{{ torrent.id }} ({{ 'disabled' | trans }}) + {% elseif torrent.approved == false %} + #{{ torrent.id }} ({{ 'waiting for approve' | trans }}) + {% elseif torrent.sensitive == true and session.user.sensitive == true %} + #{{ torrent.id }} ({{ 'sensitive' | trans }}) + {% else %} + {{ torrent.name }} + {% endif %} + {% endif %} + + {{ added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity-{{ id }} + {{ url('torrent_info', { torrentId : torrent.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/undefined.html.twig b/templates/default/activity/event/undefined.html.twig new file mode 100644 index 0000000..ba40c72 --- /dev/null +++ b/templates/default/activity/event/undefined.html.twig @@ -0,0 +1,13 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'undefined event' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/undefined.rss.twig b/templates/default/activity/event/undefined.rss.twig new file mode 100644 index 0000000..34ab004 --- /dev/null +++ b/templates/default/activity/event/undefined.rss.twig @@ -0,0 +1,7 @@ + + {{ 'undefined event' | trans }} {{ 'by' | trans }} #{{ user.id }} + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/add.html.twig b/templates/default/activity/event/user/add.html.twig new file mode 100644 index 0000000..974a8d1 --- /dev/null +++ b/templates/default/activity/event/user/add.html.twig @@ -0,0 +1,14 @@ + +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have joined' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/add.rss.twig b/templates/default/activity/event/user/add.rss.twig new file mode 100644 index 0000000..4f4fdd1 --- /dev/null +++ b/templates/default/activity/event/user/add.rss.twig @@ -0,0 +1,7 @@ + + {{ 'User' | trans }} #{{ user.id }} {{ 'have joined' | trans }} + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/approve/add.html.twig b/templates/default/activity/event/user/approve/add.html.twig new file mode 100644 index 0000000..2866bdf --- /dev/null +++ b/templates/default/activity/event/user/approve/add.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have approved' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/approve/add.rss.twig b/templates/default/activity/event/user/approve/add.rss.twig new file mode 100644 index 0000000..ad0c2e8 --- /dev/null +++ b/templates/default/activity/event/user/approve/add.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have approved user' | trans }} + #{{ to.user.id }} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/approve/delete.html.twig b/templates/default/activity/event/user/approve/delete.html.twig new file mode 100644 index 0000000..1283a04 --- /dev/null +++ b/templates/default/activity/event/user/approve/delete.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disapproved' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/approve/delete.rss.twig b/templates/default/activity/event/user/approve/delete.rss.twig new file mode 100644 index 0000000..0008063 --- /dev/null +++ b/templates/default/activity/event/user/approve/delete.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disapproved user' | trans }} + #{{ to.user.id }} + + #{{ user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/moderator/add.html.twig b/templates/default/activity/event/user/moderator/add.html.twig new file mode 100644 index 0000000..68cfd16 --- /dev/null +++ b/templates/default/activity/event/user/moderator/add.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have granted moderator permissions to' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/moderator/add.rss.twig b/templates/default/activity/event/user/moderator/add.rss.twig new file mode 100644 index 0000000..ed7513f --- /dev/null +++ b/templates/default/activity/event/user/moderator/add.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have granted moderator permissions to user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/moderator/delete.html.twig b/templates/default/activity/event/user/moderator/delete.html.twig new file mode 100644 index 0000000..f3e577c --- /dev/null +++ b/templates/default/activity/event/user/moderator/delete.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have removed moderator permissions from' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/moderator/delete.rss.twig b/templates/default/activity/event/user/moderator/delete.rss.twig new file mode 100644 index 0000000..9ef9978 --- /dev/null +++ b/templates/default/activity/event/user/moderator/delete.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have removed moderator permissions from user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/star/add.html.twig b/templates/default/activity/event/user/star/add.html.twig new file mode 100644 index 0000000..138be52 --- /dev/null +++ b/templates/default/activity/event/user/star/add.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have added star for' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/star/add.rss.twig b/templates/default/activity/event/user/star/add.rss.twig new file mode 100644 index 0000000..a7b1470 --- /dev/null +++ b/templates/default/activity/event/user/star/add.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have added star for user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/star/delete.html.twig b/templates/default/activity/event/user/star/delete.html.twig new file mode 100644 index 0000000..f42b206 --- /dev/null +++ b/templates/default/activity/event/user/star/delete.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have removed star from' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/star/delete.rss.twig b/templates/default/activity/event/user/star/delete.rss.twig new file mode 100644 index 0000000..f46562e --- /dev/null +++ b/templates/default/activity/event/user/star/delete.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have removed star from user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/status/add.html.twig b/templates/default/activity/event/user/status/add.html.twig new file mode 100644 index 0000000..a68fd52 --- /dev/null +++ b/templates/default/activity/event/user/status/add.html.twig @@ -0,0 +1,16 @@ +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have enabled' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/status/add.rss.twig b/templates/default/activity/event/user/status/add.rss.twig new file mode 100644 index 0000000..d07d6c5 --- /dev/null +++ b/templates/default/activity/event/user/status/add.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have enabled user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/event/user/status/delete.html.twig b/templates/default/activity/event/user/status/delete.html.twig new file mode 100644 index 0000000..be723d9 --- /dev/null +++ b/templates/default/activity/event/user/status/delete.html.twig @@ -0,0 +1,17 @@ + +
+
+ + {{ 'identicon' | trans }} + + + {{ 'have disabled' | trans }} + + + {{ 'identicon' | trans }} + +
+
+ {{ added | format_ago }} +
+
\ No newline at end of file diff --git a/templates/default/activity/event/user/status/delete.rss.twig b/templates/default/activity/event/user/status/delete.rss.twig new file mode 100644 index 0000000..6542d28 --- /dev/null +++ b/templates/default/activity/event/user/status/delete.rss.twig @@ -0,0 +1,12 @@ + + + {{ 'User' | trans }} + #{{ user.id }} + {{ 'have disabled user' | trans }} + #{{ to.user.id }} + + #{{ to.user.id }} + {{ added | date('D, d M Y h:i:s O') }} + {{ url('user_info', { userId : to.user.id }) }}#activity-{{ id }} + {{ url('user_info', { userId : to.user.id }) }}#activity + \ No newline at end of file diff --git a/templates/default/activity/list.html.twig b/templates/default/activity/list.html.twig new file mode 100644 index 0000000..5a1c883 --- /dev/null +++ b/templates/default/activity/list.html.twig @@ -0,0 +1,43 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{ 'Activity' | trans }}{% if pagination.page > 1 %} - {{ 'Page' | trans }} {{ pagination.page }}{% endif %} - {{ name }}{% endblock %} +{% block main_content %} +
+

{{ 'Recent activity' | trans }}

+ + + + + + + +
+ {% for activity in activities %} +
+ {{ render(controller( + 'App\\Controller\\ActivityController::event', + { user : session.user, activity : activity } + )) }} +
+ {% endfor %} +
+ {% if pagination.pages > 1 %} + {{ 'Page' | trans | lower }} {{ pagination.page }} / {{ pagination.pages }} + {% if pagination.page > 1 %} + {% if pagination.page == 2 %} + + {{ 'Back' | trans | lower }} + + {% else %} + + {{ 'Back' | trans | lower }} + + {% endif %} + {% endif %} + {% if pagination.page < pagination.pages %} + + {{ 'Next' | trans | lower }} + + {% endif %} + {% endif %} +
+{% endblock %} \ No newline at end of file diff --git a/templates/default/activity/list.rss.twig b/templates/default/activity/list.rss.twig new file mode 100644 index 0000000..2fabaaa --- /dev/null +++ b/templates/default/activity/list.rss.twig @@ -0,0 +1,14 @@ + + + + + {{ name }} - {{ 'Activity' | trans }} + {{ url('torrent_recent') }} + {% for activity in activities %} + {{ render(controller( + 'App\\Controller\\ActivityController::event', + { user : session.user, activity : activity, format : 'rss' } + )) }} + {% endfor %} + + diff --git a/templates/default/layout.html.twig b/templates/default/layout.html.twig new file mode 100644 index 0000000..99c0eeb --- /dev/null +++ b/templates/default/layout.html.twig @@ -0,0 +1,69 @@ + + + + + {% block title %}{{ name }}{% endblock %} + {% block keywords %} + + {% endblock %} + {% block stylesheets %} + + + {% endblock %} + + + {% block header %} +
+
+
+ + {#32 +
+ {{ 'Upload any torrent - download with Yggdrasil' | trans | format(path('torrent_submit')) | raw }} +
+ #} + {% block header_search %} + {{ render(controller( + 'App\\Controller\\SearchController::module', + { + request: app.request + } + )) }} + {% endblock %} +
+
+
+ {% endblock %} + {% block main %} +
+
+
+
+ {% block main_profile %} + {{ render(controller( + 'App\\Controller\\UserController::module', + { route : app.request.get('_route') } + )) }} + {% endblock %} + {% block main_content %}{% endblock %} +
+
+
+
+ {% endblock %} + {% block footer %} +
+
+
+
+ {% block footer_trackers %}{% endblock %} + GitHub +
+
+
+
+ {% endblock %} + + diff --git a/templates/default/search/module.html.twig b/templates/default/search/module.html.twig new file mode 100644 index 0000000..8b1b99b --- /dev/null +++ b/templates/default/search/module.html.twig @@ -0,0 +1,69 @@ +
+ + + {% if filter %} + +
+
+ {{ 'Locales' | trans }} +
+
+ {% for locale in locales | sort %} + {#{% if locale.total %}#} +
+ {% if locale.checked %} + + {% else %} + + {% endif %} + +
+ {#{% endif %}#} + {% endfor %} +
+
+ {{ 'Categories' | trans }} +
+
+ {% for category in categories | sort %} + {#{% if category.total %}#} +
+ {% if category.checked %} + + {% else %} + + {% endif %} + +
+ {#{% endif %}#} + {% endfor %} +
+
+ {{ 'other' | trans | u.title }} +
+
+ {% if sensitive.checked %} + + {% else %} + + {% endif %} + +
+
+ {% endif %} +
\ No newline at end of file diff --git a/templates/default/torrent/edit/categories.html.twig b/templates/default/torrent/edit/categories.html.twig new file mode 100644 index 0000000..417828d --- /dev/null +++ b/templates/default/torrent/edit/categories.html.twig @@ -0,0 +1,109 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{'Edit categories'|trans }} - {{'Torrent'|trans }} #{{ torrentId }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{'Edit categories for torrent' | trans }} + #{{ torrentId }} +

+
+
+
+ + + + + + + {% for error in form.categories.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for category in categories | sort %} +
+ {% if category in form.categories.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + + #} +
+
+
+ +
+
+ {% for edition in editions %} +
+ {% if edition.active %} + {{ edition.added | format_ago }} + {% else %} + + {{ edition.added | format_ago }} + + {% endif %} + {{ 'by'|trans }} + + {{'identicon'|trans }} + +
+ {% if session.moderator or session.owner %} + + + + + + + {% endif %} + {% if edition.approved %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% else %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% endif %} +
+
+ {% endfor %} +{% endblock %} diff --git a/templates/default/torrent/edit/locales.html.twig b/templates/default/torrent/edit/locales.html.twig new file mode 100644 index 0000000..dcab8db --- /dev/null +++ b/templates/default/torrent/edit/locales.html.twig @@ -0,0 +1,109 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{'Edit locales'|trans }} - {{'Torrent'|trans }} #{{ torrentId }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{'Edit locales for torrent' | trans }} + #{{ torrentId }} +

+
+
+
+ + + + + + + {% for error in form.locales.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for locale in locales | sort %} +
+ {% if locale in form.locales.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + + #} +
+
+
+ +
+
+ {% for edition in editions %} +
+ {% if edition.active %} + {{ edition.added | format_ago }} + {% else %} + + {{ edition.added | format_ago }} + + {% endif %} + {{ 'by'|trans }} + + {{'identicon'|trans }} + +
+ {% if session.moderator or session.owner %} + + + + + + + {% endif %} + {% if edition.approved %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% else %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% endif %} +
+
+ {% endfor %} +{% endblock %} diff --git a/templates/default/torrent/edit/poster.html.twig b/templates/default/torrent/edit/poster.html.twig new file mode 100644 index 0000000..e4ed5e9 --- /dev/null +++ b/templates/default/torrent/edit/poster.html.twig @@ -0,0 +1,121 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{ 'Edit torrent poster' | trans }} - {{ 'Torrent' | trans }} #{{ torrentId }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{'Edit poster for torrent' | trans }} + #{{ torrentId }} +

+
+
+
+ {% for error in form.poster.error %} +
+ {{ error }} +
+ {% endfor %} + +
+
+ + + + + + +
+
+ +
+
+ {% for edition in editions %} +
+ {% if edition.active %} + {{ edition.added | format_ago }} + {% else %} + + {{ edition.added | format_ago }} + + {% endif %} + {{ 'by' | trans }} + + {{'identicon'|trans }} + +
+ {% if session.moderator or session.owner %} + + + + + + + {% endif %} + {% if edition.approved %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% else %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% endif %} +
+ {% if edition.active %} +
+ +
+ + + + + + +
+
+
+ +
+
+ {% endif %} +
+ {% endfor %} +{% endblock %} diff --git a/templates/default/torrent/edit/sensitive.html.twig b/templates/default/torrent/edit/sensitive.html.twig new file mode 100644 index 0000000..cadd2a2 --- /dev/null +++ b/templates/default/torrent/edit/sensitive.html.twig @@ -0,0 +1,86 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{ 'Edit sensitive status' | trans }} - {{ 'Torrent' | trans }} #{{ torrentId }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

+ {{'Edit sensitive status for torrent' | trans }} + #{{ torrentId }} +

+
+
+
+ + + + + + + +
+
+ +
+
+ {% for edition in editions %} +
+ {% if edition.active %} + {{ edition.added | format_ago }} + {% else %} + + {{ edition.added | format_ago }} + + {% endif %} + {{ 'by' | trans }} + + {{'identicon'|trans }} + +
+ {% if session.moderator or session.owner %} + + + + + + + {% endif %} + {% if edition.approved %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% else %} + {% if session.moderator %} + + + + + + {% else %} + + + + + + {% endif %} + {% endif %} +
+
+ {% endfor %} +{% endblock %} diff --git a/templates/default/torrent/info.html.twig b/templates/default/torrent/info.html.twig new file mode 100644 index 0000000..dda2a72 --- /dev/null +++ b/templates/default/torrent/info.html.twig @@ -0,0 +1,467 @@ +{% macro recursive_file_tree(tree) %} + {% import _self as self %} + {% for key, value in tree %} + {% if value is iterable %} +
+ {{ key }} +
+
+ {{ self.recursive_file_tree(value) }} +
+ {% else %} +
+
+ {{ key }} +
+
+ {{ value | format_bytes }} +
+
+ {% endif %} + {% endfor %} +{% endmacro %} +{% from _self import recursive_file_tree %} +{% extends 'default/layout.html.twig' %} +{% block title %}{{ file.name }} - {{ 'Torrent' | trans }} #{{ torrent.id }}{% if pagination.page > 1 %} - {{ 'Page' | trans }} {{ pagination.page }}{% endif %} - {{ name }}{% endblock %} +{% block keywords %} + {% if torrent.keywords %} + + {% endif %} +{% endblock %} +{% block main_content %} +
+

+ {{ file.name }} + {#{{ 'Torrent' | trans }} #{{ torrent.id }}#} +

+ + + + {% if session.moderator or session.owner %} + + + + + + + + {% if session.moderator %} + + + + + {% endif %} + {% endif %} + + + + + + + + + + + + {% if file.hash.v1 %} + + + + + {% endif %} + {% if file.hash.v2 %} + + + + + {% endif %} + {# visible in H1 + + + + + #} + {% if file.created %} + + + + + {% endif %} + + {% if file.size %} + + + + + {% endif %} + {% if file.pieces %} + + + + + {% endif %} + + + + + {% if file.source %} + + + + + {% endif %} + {% if file.software %} + + + + + {% endif %} + {% if file.comment %} + + + + + {% endif %} + + + + + + + + + + + + + + + + + + + + +
+
+ {{ 'Moderation' | trans }} +
+
+ {{ 'Enabled' | trans }} + + {% if torrent.status %} + {{ 'Yes' | trans }} + + + + + + {% else %} + {{ 'No' | trans }} + + + + + + {% endif %} +
+ {{ 'Approved' | trans }} + + {% if torrent.approved %} + {{ 'Yes' | trans }} + + + + + + {% else %} + {{ 'No' | trans }} + + + + + + {% endif %} +
+
+ {{ 'Common' | trans }} +
+
+ {{ 'ID' | trans }} + + #{{ torrent.id }} +
+ {{ 'MD5' | trans }} + + {{ torrent.md5file }} +
+ {{ 'Info hash v1' | trans }} + + {{ file.hash.v1 }} +
+ {{ 'Info hash v2' | trans }} + + {{ file.hash.v2 }} +
+ {{ 'Filename'|trans }} + + {{ file.name }} +
+ {{ 'Created' | trans }} + + {{ file.created | format_date }} +
+ {{ 'Size' | trans }} + + {{ file.size | format_bytes }} +
+ {{ 'Pieces' | trans }} + + {{ file.pieces | format_number }} +
+ {{ 'Private' | trans }} + + {% if file.private %} + {{ 'Yes' | trans }} + {% else %} + {{ 'No' | trans }} + {% endif %} +
+ {{ 'Source' | trans }} + + {# strip all tags then apply whitelist markdown filters to prevent ping from remote #} + {{ file.source | trim | striptags | url_to_markdown | markdown_to_html | nl2br }} +
+ {{ 'Software' | trans }} + + {# strip all tags then apply whitelist markdown filters to prevent ping from remote #} + {{ file.software | trim | striptags | url_to_markdown | markdown_to_html | nl2br }} +
+ {{ 'Comment' | trans }} + + {# strip all tags then apply whitelist markdown filters to prevent ping from remote #} + {{ file.comment | trim | striptags | url_to_markdown | markdown_to_html | nl2br }} +
+ {{ 'Contributors' | trans }} + + {% for id, identicon in torrent.contributors %} + + {{'identicon'|trans }} + + {% endfor %} +
+
+ {{ 'Scrape' | trans }} +
+
+ {{ 'Seeders' | trans }} + + {{ torrent.scrape.seeders }} +
+ {{ 'Peers' | trans }} + + {{ torrent.scrape.peers }} +
+ {{ 'Leechers' | trans }} + + {{ torrent.scrape.leechers }} +
+
+ {{ 'Files' | trans }} +
+
+ {% for key, value in file.tree %} + {% if value is iterable %} + {{ recursive_file_tree(value) }} + {% else %} +
+ {{ key }} +
+ {{ value | format_bytes }} +
+
+ {% endif %} + {% endfor %} +
+
+ {{ 'Trackers' | trans }} +
+
+ {% for tracker, status in trackers %} +
+ {% if status %} + {{ tracker }} + {% else %} + + {{ tracker }} + + + + + + + {% endif %} +
+ {% endfor %} +
+
+ + + + + + {{ 'Locale' | trans }} +
+
+ {% if torrent.locales %} +
+ {% set i = 0 %} + {% for locale in torrent.locales | sort %}{% if i > 0 %},{% endif %} {{ locale | locale_name(locale) | u.title }}{% set i = i + 1 %}{% endfor %} +
+ {% endif %} +
+
+
+ + + + + + {{ 'Category' | trans }} +
+
+ {% if torrent.categories %} +
+ {% set i = 0 %} + {% for category in torrent.categories | sort %}{% if i > 0 %},{% endif %} {{ category | trans_category | u.title }}{% set i = i + 1 %}{% endfor %} +
+ {% endif %} +
+
+
+ + + + + + {{ 'Sensitive' | trans }} +
+
+ {% if torrent.sensitive %} + {{ 'Yes' | trans }} + {% else %} + {{ 'No' | trans }} + {% endif %} +
+
+
+ + + + + + {{ 'Poster' | trans }} +
+
+ {% if torrent.poster %} + {{ 'Yes' | trans }} + {% else %} + {{ 'No' | trans }} + {% endif %} +
+
+
+ +
+

{{ 'Recent activity' | trans }}

+ + + + + + + +
+ {% if activities %} + {% for activity in activities %} +
+ {{ render(controller( + 'App\\Controller\\ActivityController::event', + { user : session.user, activity : activity } + )) }} +
+ {% endfor %} + {% if pagination.pages > 1 %} +
+
+   +
+
+ {% if pagination.pages > 1 %} + {{ 'Page' | trans | lower }} {{ pagination.page }} / {{ pagination.pages }} + {% if pagination.page > 1 %} + {% if pagination.page == 2 %} + + {{ 'Back' | trans | lower }} + + {% else %} + + {{ 'Back' | trans | lower }} + + {% endif %} + {% endif %} + {% if pagination.page < pagination.pages %} + + {{ 'Next' | trans | lower }} + + {% endif %} + {% endif %} +
+
+ {% endif %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/default/torrent/list.html.twig b/templates/default/torrent/list.html.twig new file mode 100644 index 0000000..93e27d1 --- /dev/null +++ b/templates/default/torrent/list.html.twig @@ -0,0 +1,269 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{% if query %}{{ query }} - {{ 'Search' | trans }} - {% endif %}{{ name }}{% endblock %} +{% block main_content %} +
+ {% if query %} +

{{ 'Search results' | trans }}

+ + {% if app.request.get('filter') %} + + + + + + + + + + + {% else %} + + + + + + + + + + + {% endif %} + + {% else %} +

{{ 'Recent uploads' | trans }}

+ + + + + + + + {% endif %} +
+ {% if torrents %} + {% for torrent in torrents %} +
+ +
+

+ + {{ torrent.file.name }} + +

+ {% if torrent.scrape.leechers > 0 and torrent.scrape.seeders == 0 %} + + {{ 'wanted' | trans }} + + {% endif %} + {% if torrent.status == false %} + + + + + + + {% endif %} + {% if torrent.approved == false %} + + + + + + {% endif %} + {% if torrent.sensitive == true %} + + + + + + {% endif %} + {% if torrent.file.hash.v2 %} + + + + + + {% endif %} + {% if torrent.file.hash.v1 and torrent.file.hash.v2 %} + + + + + + {% endif %} +
+ {% if torrent.keywords %} +
+ {% for keyword, quantity in torrent.keywords %} + + #{{ keyword }} + + {% endfor %} +
+ {% endif %} +
+ + + {{ torrent.file.size | format_bytes }} + + + + + + + + {{ torrent.scrape.seeders }} + + + + + + + + {{ torrent.scrape.peers }} + + + + + + + + + {{ torrent.scrape.leechers }} + + + +
+ {% endfor %} + {% if pagination.pages > 1 %} +
+ {{ 'Page' | trans | lower }} {{ pagination.page }} / {{ pagination.pages }} + {% if query %} + {% if pagination.page > 1 %} + {% if pagination.page == 2 %} + + {{ 'Back' | trans | lower }} + + {% else %} + + {{ 'Back' | trans | lower }} + + {% endif %} + {% endif %} + {% if pagination.page < pagination.pages %} + + {{ 'Next' | trans | lower }} + + {% endif %} + {% else %} + {% if pagination.page > 1 %} + {% if pagination.page == 2 %} + + {{ 'Back' | trans | lower }} + + {% else %} + + {{ 'Back' | trans | lower }} + + {% endif %} + {% endif %} + {% if pagination.page < pagination.pages %} + + {{ 'Next' | trans | lower }} + + {% endif %} + {% endif %} +
+ {% endif %} + {% else %} +
+
+

+ {{ 'Nothing found' | trans }} +

+
+ {{ '* share new torrent file to change it' | trans }} +
+
+ +
+
+
+ {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/default/torrent/list.rss.twig b/templates/default/torrent/list.rss.twig new file mode 100644 index 0000000..31fa74b --- /dev/null +++ b/templates/default/torrent/list.rss.twig @@ -0,0 +1,17 @@ + + + + + {{ name }} - {{ 'Torrents' | trans }} + {{ url('torrent_recent') }} + {% for torrent in torrents %} + + {{ torrent.file.name }} + #{{ torrent.user.id }} + {{ torrent.added | date('D, d M Y h:i:s O') }} + {{ url('torrent_info', { torrentId : torrent.id }) }} + {{ url('torrent_info', { torrentId : torrent.id }) }} + + {% endfor %} + + diff --git a/templates/default/torrent/sitemap.xml.twig b/templates/default/torrent/sitemap.xml.twig new file mode 100644 index 0000000..99d77e5 --- /dev/null +++ b/templates/default/torrent/sitemap.xml.twig @@ -0,0 +1,15 @@ + + + {% for torrent in torrents %} + + {{ torrent.added | date('Y-m-d') }} + {% for code in locales %} + {% if code == locale %} + {{ url('torrent_info', { '_locale': code , torrentId : torrent.id }) }} + {% else %} + + {% endif %} + {% endfor %} + + {% endfor %} + diff --git a/templates/default/torrent/submit.html.twig b/templates/default/torrent/submit.html.twig new file mode 100644 index 0000000..c36da9a --- /dev/null +++ b/templates/default/torrent/submit.html.twig @@ -0,0 +1,100 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{'Submit torrent' | trans }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

{{'Submit torrent' | trans }}

+
+
+
+ {% for error in form.torrent.error %} +
+ {{ error }} +
+ {% endfor %} + +
+
+
+
+ + + + + + + {% for error in form.locales.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for locale in locales | sort %} +
+ {% if locale in form.locales.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + {{'Other...'|trans }} + #} +
+
+
+
+ + + + + + + {% for error in form.categories.error %} +
+ {{ error }} +
+ {% endfor %} +
+ {% for category in categories | sort %} +
+ {% if category in form.categories.attribute.value %} + + {% else %} + + {% endif %} + +
+ {% endfor %} + {# + {{'Other...'|trans }} + #} +
+
+
+ + + + + + + +
+
+ +
+
+
+{% endblock %} diff --git a/templates/default/user/info.html.twig b/templates/default/user/info.html.twig new file mode 100644 index 0000000..a22da9e --- /dev/null +++ b/templates/default/user/info.html.twig @@ -0,0 +1,299 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{ 'User'|trans }} #{{ user.id }}{% if pagination.page > 1 %} - {{ 'Page' | trans }} {{ pagination.page }}{% endif %} - {{ name }}{% endblock %} +{% block main_content %} +
+
+ {{ 'identicon'|trans }} +
+ {# @TODO username feature +
+

+ {{ 'User'|trans }} #{{ user.id }} +

+
+ #} + + + + + + + {% if session.owner or session.moderator %} + + + + + + + + + {% else %} + + + + + {% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'Common'|trans }}
{{ 'Address' | trans }} + {{ user.address }} + + + + + + + + +
{{ 'Joined' | trans }}{{ user.added | format_ago }}
{{ 'Joined' | trans }}{{ user.added | format_ago }}
{{ 'Access' | trans }}
+ {{ 'Status' | trans }} + + {% if user.status %} + {{ 'Active' | trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% else %} + {{ 'Disabled' | trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% endif %} +
+ {{ 'Approved' | trans }} + + {% if user.approved %} + {{ 'Yes'| trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% else %} + {{ 'No'| trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% endif %} +
+ {{ 'Moderator' | trans }} + + {% if user.moderator %} + {{ 'Yes'| trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% else %} + {{ 'No'| trans }} + {% if session.moderator %} + + + + + + {% endif %} + {% endif %} +
+ {% if user.owner %} + + + + + + {% endif %} + {{ 'Settings' | trans }} +
+ {{ 'Theme' | trans }} + + {{ user.theme | u.title }} +
+ {{ 'Interface' | trans }} + + {{ user.locale | locale_name(user.locale) | u.title }} +
+ {{ 'Languages' | trans }} + + {% set i = 0 %} + {% for locale in user.locales | sort %}{% if i > 0 %},{% endif %} {{ locale | locale_name(locale) | u.title }}{% set i = i + 1 %}{% endfor %} +
+ {{ 'Categories' | trans }} + + {% set i = 0 %} + {% for category in user.categories | sort %}{% if i > 0 %},{% endif %} {{ category | trans_category | u.title }}{% set i = i + 1 %}{% endfor %} +
+ {{ 'Posters' | trans }} + + {% if user.posters %} + {{ 'Yes' | trans }} + {% else %} + {{ 'No' | trans }} + {% endif %} +
+ {{ 'Sensitive' | trans }} + + {% if user.sensitive %} + {{ 'Yes' | trans }} + {% else %} + {{ 'No' | trans }} + {% endif %} +
+ {{ 'Events subscribed' | trans }} + + {% for group, event in events %} +
+ + {{ group }}: + + {% set i = 0 %} + {% for key, value in event %}{% if value in user.events %}{% if i > 0 %}, {% endif %}{{ key | lower }}{% set i = i + 1 %}{% endif %}{% endfor %} +
+ {% endfor %} +
+ {{ 'Yggdrasil only' | trans }} + + {% if user.yggdrasil %} + {{ 'Yes'|trans }} + {% else %} + {{ 'No'|trans }} + {% endif %} +
+
+ +
+

{{ 'Recent activity' | trans }}

+ + + + + + + +
+ {% if user.activities %} + {% for activity in user.activities %} +
+ {{ render(controller( + 'App\\Controller\\ActivityController::event', + { user: session.user, activity : activity } + )) }} +
+ {% endfor %} + {% if pagination.pages > 1 %} +
+
+   + {# @TODO + + + + + + #} +
+
+ {% if pagination.pages > 1 %} + {{ 'Page' | trans | lower }} {{ pagination.page }} / {{ pagination.pages }} + {% if pagination.page > 1 %} + {% if pagination.page == 2 %} + + {{ 'Back' | trans | lower }} + + {% else %} + + {{ 'Back' | trans | lower }} + + {% endif %} + {% endif %} + {% if pagination.page < pagination.pages %} + + {{ 'Next' | trans | lower }} + + {% endif %} + {% endif %} +
+
+ {% endif %} + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/templates/default/user/module.html.twig b/templates/default/user/module.html.twig new file mode 100644 index 0000000..5dc7e51 --- /dev/null +++ b/templates/default/user/module.html.twig @@ -0,0 +1,67 @@ +
+ {% if route == 'torrent_recent' %} + + + + + + {% else %} + + + + + + {% endif %} + {% if route == 'activity_all' %} + + + + + + {% else %} + + + + + + {% endif %} + {% if route == 'user_info' %} + + + + + + {% else %} + + + + + + {% endif %} + {% if route == 'user_settings' %} + + + + + + {% else %} + + + + + + {% endif %} + {% if route == 'torrent_submit' or route == 'torrent_submit' %} + + + + + + {% else %} + + + + + + {% endif %} +
\ No newline at end of file diff --git a/templates/default/user/settings.html.twig b/templates/default/user/settings.html.twig new file mode 100644 index 0000000..49fcbed --- /dev/null +++ b/templates/default/user/settings.html.twig @@ -0,0 +1,200 @@ +{% extends 'default/layout.html.twig' %} +{% block title %}{{ 'Settings' | trans }} - {{ name }}{% endblock %} +{% block main_content %} +
+
+

{{ 'Settings' | trans }}

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% set i = 0 %} + {% for group, event in events %} + {% set i = i + 1 %} + + + + + {% endfor %} + + + + + + + + +
+ {{ 'Interface' | trans }} +
+ {{ 'Theme' | trans }} + + +
{{ 'Language' | trans }} + + +
+ {{ 'Posters' | trans }} + +
+ {% if user.posters %} + + {% else %} + + {% endif %} + +
+
+ {{ 'Search' | trans }} +
+
+ {{ 'Locales' | trans }} +
+
+ {% for locale in locales | sort %} +
+ {% if locale in user.locales %} + + {% else %} + + {% endif %} + +
+ {% endfor %} +
+
+ {{ 'Categories' | trans }} +
+
+ {% for category in categories | sort %} +
+ {% if category in user.categories %} + + {% else %} + + {% endif %} + +
+ {% endfor %} +
+ {{ 'Sensitive' | trans }} + +
+ {% if user.sensitive %} + + {% else %} + + {% endif %} + +
+
+ {{ 'Activity' | trans }} +
+ {{ group }} + + {% for key, value in event %} + + {% if value in user.events %} + + {% else %} + + {% endif %} + + + {% endfor %} +
+ {{ 'Downloads' | trans }} +
+ {{ 'Yggdrasil only' | trans }} + + {% if user.yggdrasil %} + + {% else %} + + {% endif %} + +
+
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..469dcce --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,11 @@ +bootEnv(dirname(__DIR__).'/.env'); +} diff --git a/translations/.gitignore b/translations/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/translations/messages+intl-icu.cs.xlf b/translations/messages+intl-icu.cs.xlf new file mode 100644 index 0000000..e661df7 --- /dev/null +++ b/translations/messages+intl-icu.cs.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.de.xlf b/translations/messages+intl-icu.de.xlf new file mode 100644 index 0000000..754006d --- /dev/null +++ b/translations/messages+intl-icu.de.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.en.xlf b/translations/messages+intl-icu.en.xlf new file mode 100644 index 0000000..cbd0dc2 --- /dev/null +++ b/translations/messages+intl-icu.en.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.eo.xlf b/translations/messages+intl-icu.eo.xlf new file mode 100644 index 0000000..5e611d1 --- /dev/null +++ b/translations/messages+intl-icu.eo.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.es.xlf b/translations/messages+intl-icu.es.xlf new file mode 100644 index 0000000..9638ae5 --- /dev/null +++ b/translations/messages+intl-icu.es.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.fr.xlf b/translations/messages+intl-icu.fr.xlf new file mode 100644 index 0000000..daae954 --- /dev/null +++ b/translations/messages+intl-icu.fr.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.he.xlf b/translations/messages+intl-icu.he.xlf new file mode 100644 index 0000000..a324b84 --- /dev/null +++ b/translations/messages+intl-icu.he.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.it.xlf b/translations/messages+intl-icu.it.xlf new file mode 100644 index 0000000..e31236c --- /dev/null +++ b/translations/messages+intl-icu.it.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.ja.xlf b/translations/messages+intl-icu.ja.xlf new file mode 100644 index 0000000..56c715b --- /dev/null +++ b/translations/messages+intl-icu.ja.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.ka.xlf b/translations/messages+intl-icu.ka.xlf new file mode 100644 index 0000000..944c10a --- /dev/null +++ b/translations/messages+intl-icu.ka.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.lv.xlf b/translations/messages+intl-icu.lv.xlf new file mode 100644 index 0000000..814cec1 --- /dev/null +++ b/translations/messages+intl-icu.lv.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.nl.xlf b/translations/messages+intl-icu.nl.xlf new file mode 100644 index 0000000..cb827de --- /dev/null +++ b/translations/messages+intl-icu.nl.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.pl.xlf b/translations/messages+intl-icu.pl.xlf new file mode 100644 index 0000000..b9debc7 --- /dev/null +++ b/translations/messages+intl-icu.pl.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.pt.xlf b/translations/messages+intl-icu.pt.xlf new file mode 100644 index 0000000..bc8dc0f --- /dev/null +++ b/translations/messages+intl-icu.pt.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Users + + + Joined + Joined + + + Approved + Approved + + + Disapproved + Disapproved + + + User statuses + User statuses + + + Enabled + Enabled + + + Disabled + Disabled + + + User moderators + User moderators + + + Added + Added + + + Removed + Removed + + + User stars + User stars + + + Torrents + Torrents + + + Torrent locales + Torrent locales + + + Deleted + Deleted + + + Torrent sensitive + Torrent sensitive + + + Torrent stars + Torrent stars + + + Torrent downloads + Torrent downloads + + + Files + Files + + + Magnet links + Magnet links + + + Access denied + Access denied + + + At least one locale required + At least one locale required + + + Torrent file out of size limit + Torrent file out of size limit + + + Torrent file already exists + Torrent file already exists + + + Could not parse torrent file + Could not parse torrent file + + + Torrent file required + Torrent file required + + + B + B + + + Kb + Kb + + + Mb + Mb + + + Gb + Gb + + + Tb + Tb + + + Pb + Pb + + + Eb + Eb + + + Zb + Zb + + + Yb + Yb + + + now + now + + + Keyword, file, hash... + Keyword, file, hash... + + + Search + Search + + + Recent uploads + Recent uploads + + + Active leechers waiting for seeders + Active leechers waiting for seeders + + + wanted + wanted + + + Waiting for approve + Waiting for approve + + + Size + Size + + + Seeders + Seeders + + + Peers + Peers + + + Leechers + Leechers + + + Open magnet link + Open magnet link + + + Total + Total + + + Download torrent file + Download torrent file + + + Star + Star + + + Nothing found + Nothing found + + + * share new torrent file to change it + * share new torrent file to change it + + + Submit + Submit + + + Submit torrent + Submit torrent + + + Content language + Content language + + + This torrent contains selected languages + This torrent contains selected languages + + + Sensitive + Sensitive + + + Mark torrent content as sensitive + Mark torrent content as sensitive + + + Edit locales + Edit locales + + + Torrent + Torrent + + + Edit locales for torrent + Edit locales for torrent + + + cancel + cancel + + + by + by + + + identicon + identicon + + + Delete + Delete + + + Disapprove + Disapprove + + + Approve + Approve + + + Edit sensitive status + Edit sensitive status + + + Edit sensitive status for torrent + Edit sensitive status for torrent + + + Page + Page + + + Moderation + Moderation + + + Yes + Yes + + + Toggle + Toggle + + + No + No + + + Common + Common + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Info hash v1 + + + Info hash v2 + Info hash v2 + + + Created + Created + + + Pieces + Pieces + + + Source + Source + + + Software + Software + + + Comment + Comment + + + Contributors + Contributors + + + Scrape + Scrape + + + Trackers + Trackers + + + Filtered by settings + Filtered by settings + + + Edit + Edit + + + Locales + Locales + + + Recent activity + Recent activity + + + Back + Back + + + Next + Next + + + Settings + Settings + + + Interface + Interface + + + Theme + Theme + + + Language + Language + + + Join translation + Join translation + + + Activity + Activity + + + Downloads + Downloads + + + Yggdrasil only + Yggdrasil only + + + Save + Save + + + Home + Home + + + Profile + Profile + + + User + User + + + Bookmark + Bookmark + + + Address + Address + + + Address hidden for others + Address hidden for others + + + Access + Access + + + Status + Status + + + Active + Active + + + Moderator + Moderator + + + Languages + Languages + + + Events subscribed + Events subscribed + + + undefined event + undefined event + + + have downloaded torrent file + have downloaded torrent file + + + waiting for approve + waiting for approve + + + sensitive + sensitive + + + have downloaded magnet link + have downloaded magnet link + + + have disapproved torrent + have disapproved torrent + + + have approved torrent + have approved torrent + + + have added torrent + have added torrent + + + have deleted locales edition + have deleted locales edition + + + for torrent + for torrent + + + have disapproved locales edition + have disapproved locales edition + + + have approved locales edition + have approved locales edition + + + have added locales edition + have added locales edition + + + have removed star from torrent + have removed star from torrent + + + have added star for torrent + have added star for torrent + + + have deleted sensitive edition + have deleted sensitive edition + + + have disapproved sensitive edition + have disapproved sensitive edition + + + have approved sensitive edition + have approved sensitive edition + + + have added sensitive edition + have added sensitive edition + + + have disabled + have disabled + + + have disabled user + have disabled user + + + have enabled + have enabled + + + have enabled user + have enabled user + + + have disapproved + have disapproved + + + have disapproved user + have disapproved user + + + have approved + have approved + + + have approved user + have approved user + + + have joined + have joined + + + have removed moderator permissions from + have removed moderator permissions from + + + have removed moderator permissions from user + have removed moderator permissions from user + + + have granted moderator permissions to + have granted moderator permissions to + + + have granted moderator permissions to user + have granted moderator permissions to user + + + have removed star from + have removed star from + + + have removed star from user + have removed star from user + + + have added star for + have added star for + + + have added star for user + have added star for user + + + Error + Error + + + Oops! + Oops! + + + Internal server error + Internal server error + + + Report + Report + + + Not found + Not found + + + Page not found! + Page not found! + + + BitTorrent protocol version 2 + BitTorrent protocol version 2 + + + BitTorrent protocol version 1 + BitTorrent protocol version 1 + + + year ago + year ago + + + month ago + month ago + + + day ago + day ago + + + hour ago + hour ago + + + minute ago + minute ago + + + second ago + second ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + years ago + years ago + + + months ago + months ago + + + days ago + days ago + + + hours ago + hours ago + + + minutes ago + minutes ago + + + seconds ago + seconds ago + + + Search results + Search results + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Upload any torrent - download with Yggdrasil]]> + + + Wanted + Wanted + + + Seeders wanted for torrent + Seeders wanted for torrent + + + Hide + Hide + + + have disabled torrent + have disabled torrent + + + have enabled torrent + have enabled torrent + + + disabled + disabled + + + Poster file out of size limit + Poster file out of size limit + + + Image file not supported + Image file not supported + + + Poster file required + Poster file required + + + Edit torrent poster + Edit torrent poster + + + Edit poster for torrent + Edit poster for torrent + + + Poster + Poster + + + Posters + Posters + + + have deleted poster edition + have deleted poster edition + + + have disapproved poster edition + have disapproved poster edition + + + have approved poster edition + have approved poster edition + + + have added poster edition + have added poster edition + + + Torrent posters + Torrent posters + + + Center + Center + + + Top + Top + + + Bottom + Bottom + + + Categories + Categories + + + At least one category required + At least one category required + + + Content category + Content category + + + This torrent have selected categories + This torrent have selected categories + + + Edit categories + Edit categories + + + Edit categories for torrent + Edit categories for torrent + + + Locale + Locale + + + Category + Category + + + Torrent categories + Torrent categories + + + have deleted categories edition + have deleted categories edition + + + have disapproved categories edition + have disapproved categories edition + + + have approved categories edition + have approved categories edition + + + have added categories edition + have added categories edition + + + movie + movie + + + series + series + + + tv + tv + + + animation + animation + + + music + music + + + game + game + + + audiobook + audiobook + + + podcast + podcast + + + book + book + + + archive + archive + + + picture + picture + + + software + software + + + other + other + + + Hide filter + Hide filter + + + Show filter + Show filter + + + Private + Private + + +
+
diff --git a/translations/messages+intl-icu.ru.xlf b/translations/messages+intl-icu.ru.xlf new file mode 100644 index 0000000..a452819 --- /dev/null +++ b/translations/messages+intl-icu.ru.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Пользователи + + + Joined + Присоединился + + + Approved + Утвержден + + + Disapproved + Отклонен + + + User statuses + Статусы пользователей + + + Enabled + Включено + + + Disabled + Отключено + + + User moderators + Модерация пользователей + + + Added + Добавлено + + + Removed + Удалено + + + User stars + Звезды пользователей + + + Torrents + Торренты + + + Torrent locales + Локали торрента + + + Deleted + Удалено + + + Torrent sensitive + Чувствительное содержимое + + + Torrent stars + Звезды торрента + + + Torrent downloads + Загрузки торрентов + + + Files + Файлы + + + Magnet links + Magnet-ссылки + + + Access denied + Доступ запрещен + + + At least one locale required + Требуется хотя бы одна локаль + + + Torrent file out of size limit + Превышен размер торрент-файла + + + Torrent file already exists + Торрент-файл уже существует + + + Could not parse torrent file + Не удалось проанализировать торрент-файл + + + Torrent file required + Требуется указать торрент-файл + + + B + Б + + + Kb + Кб + + + Mb + Мб + + + Gb + Гб + + + Tb + Тб + + + Pb + Пб + + + Eb + Эб + + + Zb + Зб + + + Yb + Иб + + + now + сейчас + + + Keyword, file, hash... + Ключевое слово, файл, хэш... + + + Search + Поиск + + + Recent uploads + Последние загрузки + + + Active leechers waiting for seeders + Активные пиры ожидают раздачи + + + wanted + розыск + + + Waiting for approve + На рассмотрении + + + Size + Размер + + + Seeders + Сиды + + + Peers + Пиры + + + Leechers + Личеры + + + Open magnet link + Открыть magnet-ссылку + + + Total + Всего + + + Download torrent file + Скачать торрент-файл + + + Star + Звезда + + + Nothing found + Ничего не найдено + + + * share new torrent file to change it + * поделитесь новым торрент-файлом, чтобы исправить это + + + Submit + Отправить + + + Submit torrent + Отправить торрент + + + Content language + Язык контента + + + This torrent contains selected languages + Этот торрент содержит выбранные языки + + + Sensitive + Чувствительность + + + Mark torrent content as sensitive + Отметить содержимое торрента как чувствительное + + + Edit locales + Изменить локали + + + Torrent + Торрент + + + Edit locales for torrent + Изменить локали для торрента + + + cancel + отменить + + + by + от + + + identicon + пиктограмма + + + Delete + Удалить + + + Disapprove + Отклонить + + + Approve + Утвердить + + + Edit sensitive status + Изменить статус чувствительности + + + Edit sensitive status for torrent + Редактировать статус чувствительности для торрента + + + Page + Страница + + + Moderation + Модерация + + + Yes + Да + + + Toggle + Переключить + + + No + Нет + + + Common + Общее + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Инфо-хэш v1 + + + Info hash v2 + Инфо-хэш v2 + + + Created + Создано + + + Pieces + Фрагменты + + + Source + Источник + + + Software + Программа + + + Comment + Комментарий + + + Contributors + Участники + + + Scrape + Статус + + + Trackers + Трекеры + + + Filtered by settings + Отфильтровано согласно настройкам + + + Edit + Редактировать + + + Locales + Локали + + + Recent activity + Недавняя активность + + + Back + Назад + + + Next + Далее + + + Settings + Настройки + + + Interface + Интерфейс + + + Theme + Тема + + + Language + Язык + + + Join translation + Присоединиться к переводам + + + Activity + Активность + + + Downloads + Загрузки + + + Yggdrasil only + Только Yggdrasil + + + Save + Сохранить + + + Home + Главная + + + Profile + Профиль + + + User + Пользователь + + + Bookmark + Закладка + + + Address + Адрес + + + Address hidden for others + Адрес скрыт для других + + + Access + Доступ + + + Status + Статус + + + Active + Активный + + + Moderator + Модератор + + + Languages + Языки + + + Events subscribed + Подписка на события + + + undefined event + неизвестное событие + + + have downloaded torrent file + скачал торрент-файл + + + waiting for approve + на рассмотрении + + + sensitive + чувствительность + + + have downloaded magnet link + скачал magnet-ссылку + + + have disapproved torrent + отменил утверждение торрента + + + have approved torrent + утвердил торрент + + + have added torrent + добавил торрент + + + have deleted locales edition + удалил редакцию локалей + + + for torrent + для торрента + + + have disapproved locales edition + отклонил редакцию локалей + + + have approved locales edition + утвердил редакцию локалей + + + have added locales edition + добавил редакцию локалей + + + have removed star from torrent + удалил звезду с торрента + + + have added star for torrent + добавил звезду для торрента + + + have deleted sensitive edition + удалил редакцию чувствительного контента + + + have disapproved sensitive edition + отклонил редакцию чувствительного контента + + + have approved sensitive edition + утвердил редакцию чувствительного контента + + + have added sensitive edition + добавил редакцию чувствительного контента + + + have disabled + отключил + + + have disabled user + отключил пользователя + + + have enabled + включил + + + have enabled user + включил пользователя + + + have disapproved + отклонил + + + have disapproved user + отключил пользователя + + + have approved + утвердил + + + have approved user + утвердил пользователя + + + have joined + присоединился + + + have removed moderator permissions from + удалил права модератора от + + + have removed moderator permissions from user + удалил права модератора от пользователя + + + have granted moderator permissions to + предоставил права модератора для + + + have granted moderator permissions to user + предоставил права модератора для пользователя + + + have removed star from + удалил звезду от + + + have removed star from user + удалили звезду от пользователя + + + have added star for + добавил звезду для + + + have added star for user + добавил звезду для пользователя + + + Error + Ошибка + + + Oops! + Ой! + + + Internal server error + Внутренняя ошибка сервера + + + Report + Сообщить + + + Not found + Не найдено + + + Page not found! + Страница не найдена! + + + BitTorrent protocol version 2 + BitTorrent протокол версии 2 + + + BitTorrent protocol version 1 + BitTorrent протокол версии 1 + + + year ago + год назад + + + month ago + месяц назад + + + day ago + день назад + + + hour ago + час назад + + + minute ago + минуту назад + + + second ago + секунду назад + + + years ago + год назад + + + months ago + месяца назад + + + days ago + дня назад + + + hours ago + часа назад + + + minutes ago + минуты назад + + + seconds ago + секунды назад + + + years ago + лет назад + + + months ago + месяцев назад + + + days ago + дней назад + + + hours ago + часов назад + + + minutes ago + минут назад + + + seconds ago + секунд назад + + + Search results + Результаты поиска + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Загрузить любой торрент - скачать с Yggdrasil]]> + + + Wanted + Розыск + + + Seeders wanted for torrent + Требуется раздача для торрента + + + Hide + Скрыть + + + have disabled torrent + отключил торрент + + + have enabled torrent + включил торрент + + + disabled + отключено + + + Poster file out of size limit + Размер файла постера превышает максимальный размер + + + Image file not supported + Формат изображения не поддерживается + + + Poster file required + Требуется указать файл постера + + + Edit torrent poster + Редактировать постер торрента + + + Edit poster for torrent + Редактировать постер для торрента + + + Poster + Постер + + + Posters + Постеры + + + have deleted poster edition + удалил редакцию постера + + + have disapproved poster edition + отклонил редакцию постера + + + have approved poster edition + утвердил редакцию постера + + + have added poster edition + добавил редакцию постера + + + Torrent posters + Постеры торрентов + + + Center + Центр + + + Top + Верх + + + Bottom + Низ + + + Categories + Категории + + + At least one category required + Необходимо указать минимум одну категорию + + + Content category + Категория содержимого + + + This torrent have selected categories + Этот торрент содержит выбранные категории + + + Edit categories + Редактировать категории + + + Edit categories for torrent + Редактировать категории для торрента + + + Locale + Локаль + + + Category + Категория + + + Torrent categories + Категории торрента + + + have deleted categories edition + удалил редакцию категорий + + + have disapproved categories edition + отклонил редакцию категорий + + + have approved categories edition + утвердил редакцию категорий + + + have added categories edition + добавил редакцию категорий + + + movie + фильм + + + series + сериал + + + tv + ТВ шоу + + + animation + анимация + + + music + музыка + + + game + игра + + + audiobook + аудиокнига + + + podcast + подкаст + + + book + книга + + + archive + архив + + + picture + изображение + + + software + программа + + + other + другое + + + Hide filter + Скрыть фильтр + + + Show filter + Показать фильтр + + + Private + Приватный + + +
+
diff --git a/translations/messages+intl-icu.uk.xlf b/translations/messages+intl-icu.uk.xlf new file mode 100644 index 0000000..ffea2ef --- /dev/null +++ b/translations/messages+intl-icu.uk.xlf @@ -0,0 +1,918 @@ + + + +
+ +
+ + + Users + Користувачі + + + Joined + Приєднався + + + Approved + Затверджено + + + Disapproved + Відхилено + + + User statuses + Статуси користувача + + + Enabled + Увімкнено + + + Disabled + Вимкнено + + + User moderators + Модератори + + + Added + Додано + + + Removed + Видалено + + + User stars + Зірки користувача + + + Torrents + Торенти + + + Torrent locales + Файли торентів + + + Deleted + Видалено + + + Torrent sensitive + Чутливий вміст + + + Torrent stars + Зірки торенту + + + Torrent downloads + Завантаження торенту + + + Files + Файли + + + Magnet links + Magnet-посилання + + + Access denied + Відмовлено у доступі + + + At least one locale required + Необхідна принаймні одна локаль + + + Torrent file out of size limit + Торент-файл поза розміром + + + Torrent file already exists + Торент-файл вже існує + + + Could not parse torrent file + Не вдалося проаналізувати торент-файл + + + Torrent file required + Необхідно надати торент файл + + + B + Б + + + Kb + Кб + + + Mb + Мб + + + Gb + Гб + + + Tb + Тб + + + Pb + Пб + + + Eb + Еб + + + Zb + Зб + + + Yb + Іб + + + now + щойно + + + Keyword, file, hash... + Ключове слово, файл, хеш... + + + Search + Пошук + + + Recent uploads + Останні завантаження + + + Active leechers waiting for seeders + Активні лічери очікують сідів + + + wanted + розшук + + + Waiting for approve + Очікує на підтвердження + + + Size + Розмір + + + Seeders + Сідери + + + Peers + Піри + + + Leechers + Лічери + + + Open magnet link + Відкрити посилання на magnet + + + Total + Всього + + + Download torrent file + Завантажити торрент файл + + + Star + Зірка + + + Nothing found + Нічого не знайдено + + + * share new torrent file to change it + * поширте новий торент-файл, щоб змінити це + + + Submit + Надіслати + + + Submit torrent + Надіслати торент + + + Content language + Мова контенту + + + This torrent contains selected languages + Цей торрент містить обрані мови + + + Sensitive + Чутливий вміст + + + Mark torrent content as sensitive + Помітити вміст торенту як чутливий + + + Edit locales + Редагувати локалі + + + Torrent + Торент + + + Edit locales for torrent + Редагування локалей для торенту + + + cancel + скасувати + + + by + за участі + + + identicon + піктограма + + + Delete + Видалити + + + Disapprove + Відхилити + + + Approve + Затвердити + + + Edit sensitive status + Редагувати статус чутливості + + + Edit sensitive status for torrent + Редагувати статус чутливості для торенту + + + Page + Сторінка + + + Moderation + Модерація + + + Yes + Так + + + Toggle + Перемкнути + + + No + Ні + + + Common + Загальні + + + ID + ID + + + MD5 + MD5 + + + Info hash v1 + Інфо-хеш версії 1 + + + Info hash v2 + Інфо-хеш версії 2 + + + Created + Створено + + + Pieces + Частини + + + Source + Джерело + + + Software + Програма + + + Comment + Коментар + + + Contributors + Учасники + + + Scrape + Стан + + + Trackers + Трекери + + + Filtered by settings + Відфільтровано згідно з налаштуваннями + + + Edit + Редагувати + + + Locales + Локалі + + + Recent activity + Остання активність + + + Back + Назад + + + Next + Далі + + + Settings + Налаштування + + + Interface + Інтерфейс + + + Theme + Тема + + + Language + Мова + + + Join translation + Приєднатись до перекладу + + + Activity + Активність + + + Downloads + Завантаження + + + Yggdrasil only + Тільки Іґдрасіль + + + Save + Зберегти + + + Home + Головна + + + Profile + Профіль + + + User + Користувач + + + Bookmark + Додати до обраного + + + Address + Адреса + + + Address hidden for others + Адресу приховано для інших + + + Access + Доступ + + + Status + Статус + + + Active + Активний + + + Moderator + Модератор + + + Languages + Мови + + + Events subscribed + Підписка на події + + + undefined event + невідома подія + + + have downloaded torrent file + завантажив торрент файл + + + waiting for approve + очікує на підтвердження + + + sensitive + чутливий вміст + + + have downloaded magnet link + завантажив торрент файл + + + have disapproved torrent + відхилив торент + + + have approved torrent + затвердив торент + + + have added torrent + додав торент + + + have deleted locales edition + видалено версію локалей + + + for torrent + для торенту + + + have disapproved locales edition + відхилив версію локалей + + + have approved locales edition + затвердив версію локалей + + + have added locales edition + додав редакцію локалей + + + have removed star from torrent + видалив зірку з торента + + + have added star for torrent + додав зірку для торента + + + have deleted sensitive edition + видалив редакцію чутливого вмісту + + + have disapproved sensitive edition + відхилив редакцію чутливого вмісту + + + have approved sensitive edition + затвердив редакцію чутливого вмісту + + + have added sensitive edition + додав редакцію чутливого вмісту + + + have disabled + вимкнув + + + have disabled user + вимкнув користувача + + + have enabled + увімкнув + + + have enabled user + увімкнув користувача + + + have disapproved + відхилив + + + have disapproved user + відхилив користувача + + + have approved + затвердив + + + have approved user + затвердив користувача + + + have joined + приєднався + + + have removed moderator permissions from + видалив права модератора від + + + have removed moderator permissions from user + видалив права модератора від користувача + + + have granted moderator permissions to + надано права модератора + + + have granted moderator permissions to user + надано права модератора користувачеві + + + have removed star from + видалив зірку від + + + have removed star from user + видалив зірку від користувача + + + have added star for + додав зірку до + + + have added star for user + додав зірку користувачеві + + + Error + Помилка + + + Oops! + Ой! + + + Internal server error + Внутрішня помилка сервера + + + Report + Звіт + + + Not found + Не знайдено + + + Page not found! + Сторінку не знайдено! + + + BitTorrent protocol version 2 + BitTorrent протокол версії 2 + + + BitTorrent protocol version 1 + BitTorrent протокол версії 1 + + + year ago + рік тому + + + month ago + місяць тому + + + day ago + день тому + + + hour ago + годину тому + + + minute ago + хвилину тому + + + second ago + секунду тому + + + years ago + роки тому + + + months ago + місяці тому + + + days ago + дні тому + + + hours ago + години тому + + + minutes ago + хвилини тому + + + seconds ago + секунди тому + + + years ago + років тому + + + months ago + місяців тому + + + days ago + днів тому + + + hours ago + годин тому + + + minutes ago + хвилин тому + + + seconds ago + секунд тому + + + Search results + Результати пошуку + + + <a href="%s">Upload</a> any torrent - download with Yggdrasil + Надіслати будь-який торент - завантажити з Yggdrasil]]> + + + Wanted + Розшук + + + Seeders wanted for torrent + Розшукуються роздачі для торенту + + + Hide + Приховати + + + have disabled torrent + вимкнув торент + + + have enabled torrent + увімкнув торент + + + disabled + вимкнено + + + Poster file out of size limit + Розмір файлу постера перевищує ліміти + + + Image file not supported + Формат зображення не підтримується + + + Poster file required + Необхідно вказати файл постера + + + Edit torrent poster + Редагувати постер торента + + + Edit poster for torrent + Редагувати постер для торента + + + Poster + Постер + + + Posters + Постери + + + have deleted poster edition + видалив редакцію постера + + + have disapproved poster edition + відхилив редакцію постера + + + have approved poster edition + додав редакцію постера + + + have added poster edition + додав редакцію постера + + + Torrent posters + Постери торентів + + + Center + Центр + + + Top + Верх + + + Bottom + Низ + + + Categories + Категорії + + + At least one category required + Потрібно вказати щонайменше одну категорію + + + Content category + Категорія вмісту + + + This torrent have selected categories + Цей торрент має виділені категорії + + + Edit categories + Змінити категорії + + + Edit categories for torrent + Редагувати категорії для торенту + + + Locale + Локаль + + + Category + Категорія + + + Torrent categories + Категорії торенту + + + have deleted categories edition + видалив редакцію категорій + + + have disapproved categories edition + відхилив редакцію категорій + + + have approved categories edition + затвердив редакцію категорій + + + have added categories edition + додав редакцію категорій + + + movie + кіно + + + series + серіал + + + tv + ТБ шоу + + + animation + анімація + + + music + музика + + + game + гра + + + audiobook + аудіокнига + + + podcast + подкаст + + + book + книга + + + archive + архів + + + picture + зображення + + + software + програма + + + other + інше + + + Hide filter + Приховати фільтр + + + Show filter + Показати фільтр + + + Private + Приватний + + +
+