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.
+
-#### 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
+
+[](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:
-
- + 
- + [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;
-}
-
-?>
-
-
-
-
-
-
-
- title ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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;
-}
-
-?>
-
-
-
-
-
-
-
- html->title ?>
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
html->h1 ?>
-
-
-
-
-
-
-
-
- html->link->direct as $direct) { ?>
-
-
-
-
-
-
-
-
-
- message ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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);
-}
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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)
- );
-}
-*/
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
open source community-driven BitTorrent registry for Yggdrasil ecosystem.') ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Node page.'), WEBSITE_URL) ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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.') ?>
-
-
-
-
-
message ?>
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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
-}
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- message ?>
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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 ?>
-
-
-
- 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) { ?>
- -
-
title, ENT_QUOTES, 'UTF-8') ?>
- preview), ENT_QUOTES, 'UTF-8') ?>
- magnetId) ?>
- magnetId) ?>
-
-
-
-
-
-
-
-
-
-
-
-
- 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) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- total > WEBSITE_PAGINATION_LIMIT) { ?>
-
-
- page, ceil($response->total / WEBSITE_PAGINATION_LIMIT)) ?>
- page > 1) { ?>
-
-
-
-
- page < ceil($response->total / WEBSITE_PAGINATION_LIMIT)) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
\ 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) ?>
- 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)) { ?>
- -
-
title, ENT_QUOTES, 'UTF-8'), $magnetComment->magnetCommentId) ?>
- value, ENT_QUOTES, 'UTF-8') ?>
- magnet->magnetId, $magnetComment->magnetCommentId) ?>
- magnet->magnetId, $magnetComment->magnetCommentId) ?>
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
- magnet->title), WEBSITE_NAME) ?>
-
-
-
- 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) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
-
\ 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)
- );
-}
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
-
-
-
-
-
-
-
-
-
- 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() ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
- -
-
-
-
- -
-
-
-
-
- -
- /
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
-
-
-
- $value) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $node) { ?>
-
-
-
- $value) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $peer) { ?>
-
-
-
- $value) { ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
message ?>
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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)
- );
- }
-}
-
-?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- success) { ?>
-
-
-
-
-
-
-
address ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
message ?>
-
-
-
-
-
-
-
-
-
-
- $tracker) { ?>
- announce) && !empty($tracker->stats)) { ?>
-
- /
-
- |
-
-
-
- |
-
- |
-
-
- |
-
-
- |
-
-
-
-
-
-
-
\ 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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ '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 %}
+
+ {{ 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/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 @@
+
+
+
+
+
+
+ {{ '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 %}
+
+ {{ 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/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 @@
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+
+ {{ '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 @@
+
+
+
+
+
+
+ {{ 'have approved' | 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 @@
+
+
+
+
+
+
+ {{ 'have disapproved' | 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 @@
+
+
+
+
+
+
+ {{ 'have granted moderator permissions to' | 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 @@
+
+
+
+
+
+
+ {{ 'have removed moderator permissions from' | 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 @@
+
+
+
+
+
+
+ {{ 'have added star for' | 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 @@
+
+
+
+
+
+
+ {{ 'have removed star from' | 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 @@
+
+
+
+
+
+
+ {{ 'have enabled' | 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 @@
+
+
+
+
+
+
+
+ {{ 'have disabled' | 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 %}
+
+
+
+
+ YGG tracker
+
+ {#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 }}
+
+
+
+ {{ 'Categories' | trans }}
+
+
+
+ {{ 'other' | trans | u.title }}
+
+
+ {% if sensitive.checked %}
+
+ {% else %}
+
+ {% endif %}
+
+ {{ 'Sensitive' | trans }}
+
+ ({{ sensitive.total }})
+
+
+
+
+ {% 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 %}
+
+
+
+
+
+ {{'Content category'|trans }}
+
+
+
+
+
+
+ {% for error in form.categories.error %}
+
+ {{ error }}
+
+ {% endfor %}
+
+
+
+
+
+
+ {% for edition in editions %}
+
+ {% if edition.active %}
+ {{ edition.added | format_ago }}
+ {% else %}
+
+ {{ edition.added | format_ago }}
+
+ {% endif %}
+ {{ 'by'|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 %}
+
+
+
+
+
+ {{'Content language'|trans }}
+
+
+
+
+
+
+ {% for error in form.locales.error %}
+
+ {{ error }}
+
+ {% endfor %}
+
+
+
+
+
+
+ {% for edition in editions %}
+
+ {% if edition.active %}
+ {{ edition.added | format_ago }}
+ {% else %}
+
+ {{ edition.added | format_ago }}
+
+ {% endif %}
+ {{ 'by'|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 %}
+
+ {% for edition in editions %}
+
+ {% if edition.active %}
+ {{ edition.added | format_ago }}
+ {% else %}
+
+ {{ edition.added | format_ago }}
+
+ {% endif %}
+ {{ 'by' | 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 %}
+
+
+
+
+
+ {{ 'Center' | trans }}
+
+
+
+ {{ 'Top' | trans }}
+
+
+
+ {{ 'Bottom' | trans }}
+
+
+
+
+
+
+
+ {% 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 %}
+
+
+
+
+
+
+ {{'Sensitive' | trans }}
+
+
+
+
+
+
+
+
+
+
+
+ {% for edition in editions %}
+
+ {% if edition.active %}
+ {{ edition.added | format_ago }}
+ {% else %}
+
+ {{ edition.added | format_ago }}
+
+ {% endif %}
+ {{ 'by' | 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 %}
+
+
+
+ {{ 'Moderation' | trans }}
+
+
+
+
+
+ {{ 'Enabled' | trans }}
+
+
+ {% if torrent.status %}
+ {{ 'Yes' | trans }}
+
+
+
+
+
+ {% else %}
+ {{ 'No' | trans }}
+
+
+
+
+
+ {% endif %}
+
+
+ {% if session.moderator %}
+
+
+ {{ 'Approved' | trans }}
+
+
+ {% if torrent.approved %}
+ {{ 'Yes' | trans }}
+
+
+
+
+
+ {% else %}
+ {{ 'No' | trans }}
+
+
+
+
+
+ {% endif %}
+
+
+ {% endif %}
+ {% endif %}
+
+
+
+ {{ 'Common' | trans }}
+
+
+
+
+
+ {{ 'ID' | trans }}
+
+
+ #{{ torrent.id }}
+
+
+
+
+ {{ 'MD5' | trans }}
+
+
+ {{ torrent.md5file }}
+
+
+ {% if file.hash.v1 %}
+
+
+ {{ 'Info hash v1' | trans }}
+
+
+ {{ file.hash.v1 }}
+
+
+ {% endif %}
+ {% if file.hash.v2 %}
+
+
+ {{ 'Info hash v2' | trans }}
+
+
+ {{ file.hash.v2 }}
+
+
+ {% endif %}
+ {# visible in H1
+
+
+ {{ 'Filename'|trans }}
+
+
+ {{ file.name }}
+
+
+ #}
+ {% if file.created %}
+
+
+ {{ 'Created' | trans }}
+
+
+ {{ file.created | format_date }}
+
+
+ {% endif %}
+
+ {% if file.size %}
+
+
+ {{ 'Size' | trans }}
+
+
+ {{ file.size | format_bytes }}
+
+
+ {% endif %}
+ {% if file.pieces %}
+
+
+ {{ 'Pieces' | trans }}
+
+
+ {{ file.pieces | format_number }}
+
+
+ {% endif %}
+
+
+ {{ 'Private' | trans }}
+
+
+ {% if file.private %}
+ {{ 'Yes' | trans }}
+ {% else %}
+ {{ 'No' | trans }}
+ {% endif %}
+
+
+ {% if file.source %}
+
+
+ {{ '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 }}
+
+
+ {% endif %}
+ {% if file.software %}
+
+
+ {{ '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 }}
+
+
+ {% endif %}
+ {% if file.comment %}
+
+
+ {{ '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 }}
+
+
+ {% endif %}
+
+
+ {{ 'Contributors' | trans }}
+
+
+ {% for id, identicon in torrent.contributors %}
+
+
+
+ {% 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 %}
+
+ {% 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 %}
+
+
+
+
+ {% 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 %}
+
+ {% 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 }}
+
+
+
+
+
+
+
+ {{ 'Submit' | 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 %}
+
+
+
+
+
+
+ {{'Content language' | trans }}
+
+
+
+
+
+
+ {% for error in form.locales.error %}
+
+ {{ error }}
+
+ {% endfor %}
+
+ {% for locale in locales | sort %}
+
+ {% if locale in form.locales.attribute.value %}
+
+ {% else %}
+
+ {% endif %}
+
+ {{ locale | locale_name(locale) | u.title }}
+
+
+ {% endfor %}
+ {#
+ {{'Other...'|trans }}
+ #}
+
+
+
+
+
+ {{'Content category' | trans }}
+
+
+
+
+
+
+ {% for error in form.categories.error %}
+
+ {{ error }}
+
+ {% endfor %}
+
+ {% for category in categories | sort %}
+
+ {% if category in form.categories.attribute.value %}
+
+ {% else %}
+
+ {% endif %}
+
+ {{ category | trans_category | u.title }}
+
+
+ {% endfor %}
+ {#
+ {{'Other...'|trans }}
+ #}
+
+
+
+
+
+ {{'Sensitive'|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 %}
+
+
+
+
+ {# @TODO username feature
+
+
+ {{ 'User'|trans }} #{{ user.id }}
+
+
+ #}
+
+
+
+
+ {{ 'Common'|trans }}
+
+ {% if session.owner or session.moderator %}
+
+ {{ 'Address' | trans }}
+
+ {{ user.address }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'Joined' | trans }}
+ {{ user.added | format_ago }}
+
+ {% else %}
+
+ {{ 'Joined' | trans }}
+ {{ user.added | format_ago }}
+
+ {% endif %}
+
+ {{ '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 %}
+
+ {% 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 }}
+
+
+
+
+
+
+{% 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
+ Приватний
+
+
+
+