Compare commits

...

81 commits
1.1.1 ... main

Author SHA1 Message Date
Thomas
ca9e903300 Release 1.3.0 2025-04-03 17:50:13 +02:00
Thomas
2861e104da :-\ 2025-04-03 17:48:05 +02:00
Thomas
5208512073 Fix custom emojis not displayed in messages and screen name 2025-04-03 17:36:43 +02:00
Thomas
ee02589df6 Change title for schedule/scheduled pages 2025-04-03 16:35:29 +02:00
Thomas
60a2895b1f Move logout item to the end of the menu 2025-04-03 16:34:58 +02:00
Thomas
4ffe74a73a Display date with browser timezone 2025-04-03 16:06:15 +02:00
Thomas
618da03e0e Add timezone to the date of scheduled messages 2025-04-03 15:24:56 +02:00
Thomas
60b919fd4a Fix issue #13 - Previous scheduled messages are not displayed when scrolling down the list 2025-04-03 11:02:23 +02:00
Thomas
f0881c2d51 Update libs 2025-04-03 10:09:21 +02:00
Thomas
16a398e852 add in autoscript 2024-05-19 10:14:12 +02:00
Thomas
53130908b5 1.2.4 2024-05-19 10:07:38 +02:00
Thomas
3f06bd8b08 Merge branch 'develop' 2024-05-19 10:06:46 +02:00
Thomas
ca3c8c5bd5 default assets 2024-05-19 10:05:48 +02:00
Thomas
7fee848433 Merge branch 'develop' 2024-05-19 08:44:57 +02:00
Thomas
4f5ac293ab add autoscript 2024-05-19 08:44:35 +02:00
Thomas
6f11a93268 Merge branch 'develop' 2024-05-19 08:04:22 +02:00
Thomas
e5f707ff73 Release 1.2.3 2024-05-19 08:04:10 +02:00
Thomas
82ac8422b4 Merge branch 'develop' 2024-05-19 08:03:07 +02:00
Thomas
493b790559 prod 2024-05-19 08:02:43 +02:00
Thomas
f3824010ee layout changes 2024-05-19 08:00:18 +02:00
Thomas
aadcb2e1a8 Merge branch 'develop' 2024-05-19 07:51:56 +02:00
Thomas
e25ac90340 Merge branch 'update_readme' into 'develop'
Update readme

See merge request tom79/fediplan!87
2024-05-18 18:03:40 +00:00
0xd9a
6e6d2cb3a1 Update readme 2024-05-18 18:03:39 +00:00
Thomas
34976a0252 Merge branch 'develop' into 'main'
Release 1.2.2

See merge request tom79/fediplan!86
2024-05-18 17:43:51 +00:00
Thomas
41bf64d7b0 Release 1.2.2 2024-05-18 17:43:51 +00:00
Thomas
2c773d7795 Merge branch 'main' into 'develop'
# Conflicts:
#   public/css/bootstrap-toggle.min.css
#   public/js/bootstrap-toggle.min.js
#   templates/fediplan/about.html.twig
#   templates/fediplan/index.html.twig
#   templates/fediplan/schedule.html.twig
2024-05-18 17:43:10 +00:00
Thomas
477dabc702 Fix layout 2024-05-18 19:40:04 +02:00
Thomas
524917e7ba Merge branch 'develop' into 'main'
Release 1.2.1

See merge request tom79/fediplan!85
2024-05-18 15:23:43 +00:00
Thomas
3a6fed30a6 Release 1.2.1 2024-05-18 15:23:43 +00:00
Thomas
7fa59fb29f -Release 1.2.1 2024-05-18 17:22:28 +02:00
Thomas
c81011b6ce - Fix column 2024-05-18 17:17:57 +02:00
Thomas
92eb140d69 Merge branch 'l10n_develop' into 'develop'
New Crowdin updates

See merge request tom79/fediplan!84
2024-05-16 13:41:50 +00:00
Thomas
c307ad0f10 New Crowdin updates 2024-05-16 13:41:50 +00:00
Thomas
7063de4dd2 Merge branch 'l10n_develop' into 'develop'
New Crowdin updates

See merge request tom79/fediplan!83
2024-05-16 09:12:23 +00:00
Thomas
4692d5430a New Crowdin updates 2024-05-16 09:12:23 +00:00
Thomas
defb279698 - migrate to boostrap 5.3
- Fix messages not displayed when missing media description
2024-05-16 10:55:16 +02:00
Thomas
244489c091 Merge branch 'develop' into 'main'
Release 1.2.0

See merge request tom79/fediplan!82
2024-05-15 07:32:35 +00:00
Thomas
23ff8dd75b README.md 2024-05-15 09:24:39 +02:00
Thomas
1d2583182f Merge branch 'update_readme' into 'main'
Update README.md

See merge request tom79/fediplan!81
2024-05-15 07:19:00 +00:00
0xd9a
dc1cc6fc24 Update README.md 2024-05-15 12:40:52 +05:30
Thomas
c0135847c3 Fix wrong version of Pleroma #2 2024-05-15 09:00:32 +02:00
Thomas
adcce820dd Fix Pleroma issue 2024-05-15 08:57:40 +02:00
Thomas
7a7c3e6974 Fix Pleroma issue 2024-05-15 08:34:35 +02:00
Thomas
40b3fc2ef2 Merge branch 'develop' into 'main'
Release 1.2.0

See merge request tom79/fediplan!80
2024-05-14 15:06:46 +00:00
Thomas
ac9d3824cf Release 1.2.0 2024-05-14 15:06:46 +00:00
Thomas
71c850b1f6 Merge branch 'symfony_7' into develop 2024-05-14 17:03:24 +02:00
Thomas
9ffa570229 Merge branch 'symfony_7' of https://framagit.org/tom79/fediplan into symfony_7 2024-05-14 17:02:56 +02:00
Thomas
d1f23f08ac Merge branch 'symfony_7' into 'symfony_7'
Add Docker configurations

See merge request tom79/fediplan!79
2024-05-14 15:01:50 +00:00
Thomas
b137385e30 Fix error 2024-05-14 17:00:24 +02:00
0xd9a
11468a7548 Add Docker configurations 2024-05-14 16:58:36 +05:30
Thomas
e4a199a88f new languages 2024-05-14 08:50:06 +02:00
Thomas
8a3d108af7 Merge branch 'l10n_develop' into 'develop'
New Crowdin updates

See merge request tom79/fediplan!78
2024-05-14 06:48:37 +00:00
Thomas
a784b7c615 New Crowdin updates 2024-05-14 06:48:36 +00:00
Thomas
a2457e2af2 Merge branch 'l10n_develop' into 'develop'
New Crowdin updates

See merge request tom79/fediplan!77
2024-05-14 06:38:43 +00:00
Thomas
86a405dc96 New Crowdin updates 2024-05-14 06:38:43 +00:00
Thomas
c2c24faa17 Merge branch 'l10n_develop' into 'develop'
New Crowdin updates

See merge request tom79/fediplan!75
2024-05-14 06:24:14 +00:00
Thomas
8ac9757372 New Crowdin updates 2024-05-14 06:24:14 +00:00
Thomas
2dfce02ef2 change version 2024-05-13 16:11:04 +02:00
Thomas
79f1ab4f66 Store timezone in session 2024-05-13 11:13:28 +02:00
Thomas
85e409a481 Fix poll not removed when hidden 2024-05-13 08:08:44 +02:00
Thomas
2cf43f4a50 Fix autocomplete 2024-05-11 16:56:11 +02:00
Thomas
b8873a277d Fix counters 2024-05-11 16:27:26 +02:00
Thomas
f3be249bef Use instance configuration 2024-05-11 10:31:12 +02:00
Thomas
86230adb87 Add some helpers 2024-05-10 17:39:43 +02:00
Thomas
cd6fad2e3b Fix some issues 2024-05-10 16:57:17 +02:00
Thomas
470dc8c714 Some fixes 2024-05-09 17:11:05 +02:00
Thomas
f9172b14f6 Type for variables and returns for methods 2024-05-09 16:41:04 +02:00
Thomas
daaa039f4c Missing files 2024-05-09 16:10:06 +02:00
Thomas
f10b894447 - migration to Symfony 7 2024-05-09 11:01:43 +02:00
Thomas
26a50e3ec5 - migrationi to Symfony 7 2024-05-07 17:15:33 +02:00
Thomas
668c86d97a - Update libs 2024-04-19 17:51:20 +02:00
Thomas
207946520b Merge branch 'legoktm-master-patch-60810' into 'master'
Correctly display newlines in scheduled messages

Closes #9

See merge request tom79/fediplan!74
2024-04-19 15:05:49 +00:00
Kunal Mehta
4675f65437 Correctly display newlines in scheduled messages 2024-04-19 15:05:49 +00:00
Thomas
c7646b47ee Merge branch 'l10n_master' into 'master'
New Crowdin updates

See merge request tom79/fediplan!73
2022-05-25 08:49:51 +00:00
Thomas
ecbb05c7b0 New Crowdin updates 2022-05-25 08:49:50 +00:00
Thomas
d61f3fe8e5 Merge branch 'l10n_master' into 'master'
New Crowdin updates

See merge request tom79/fediplan!72
2020-07-10 08:37:21 +02:00
Thomas
5f27eab47e New translations fediplan.en.yaml (Japanese) 2020-07-10 07:27:53 +02:00
Thomas
353ed22ea8 Merge branch 'l10n_master' into 'master'
New Crowdin translations

See merge request tom79/fediplan!71
2020-05-15 11:13:22 +02:00
Thomas
7948e8bacc New Crowdin translations 2020-05-15 11:13:22 +02:00
Thomas
ae993d549f Merge branch 'l10n_master' into 'master'
New Crowdin translations

See merge request tom79/fediplan!70
2020-05-04 10:15:49 +02:00
Thomas
cabd01b1c8 New Crowdin translations 2020-05-04 10:15:49 +02:00
131 changed files with 37127 additions and 21050 deletions

10
.env
View file

@ -1,5 +1,5 @@
# In all environments, the following files are loaded if they exist,
# the later taking precedence over the former:
# 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
@ -9,13 +9,13 @@
# 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/configuration.html#infrastructure-related-configuration
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_ENV=prod
APP_SECRET=7189792ca5da6b84aff72ec1c63d95ae
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ###

14
.gitignore vendored
View file

@ -12,3 +12,17 @@
###> symfony/web-server-bundle ###
/.web-server-pid
###< symfony/web-server-bundle ###
###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
/bin/phpunit
/tests/
/tests/bootstrap.php
/var/
/vendor/
/.env.test
/composer
/phpunit.xml.dist

56
Dockerfile Normal file
View file

@ -0,0 +1,56 @@
FROM composer as composer
COPY --chown=nobody . /app
RUN composer install --optimize-autoloader --no-interaction --no-progress
FROM alpine:3.19
# Install packages and remove default server definition
RUN apk add --no-cache \
curl \
nginx \
php83 \
php83-ctype \
php83-curl \
php83-dom \
php83-fpm \
php83-intl \
php83-mbstring \
php83-session \
php83-tokenizer \
php83-simplexml \
supervisor
# Configure nginx - http
COPY docker_config/nginx.conf /etc/nginx/nginx.conf
# Configure nginx - default server
COPY docker_config/conf.d /etc/nginx/conf.d/
# Configure PHP-FPM
ENV PHP_INI_DIR /etc/php83
COPY docker_config/fpm-pool.conf ${PHP_INI_DIR}/php-fpm.d/www.conf
COPY docker_config/php.ini ${PHP_INI_DIR}/conf.d/custom.ini
# Configure supervisord
COPY docker_config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Add application
COPY --chown=nobody --from=composer /app/ /var/www/fediplan/
# Make sure files/folders needed by the processes are accessable when they run under the nobody user
RUN chown -R nobody.nobody /var/www/fediplan /run /var/lib/nginx /var/log/nginx
# Create symlink for php
RUN ln -s /usr/bin/php83 /usr/bin/php
# Switch to use a non-root user from here on
USER nobody
# Expose the port nginx is reachable on
EXPOSE 8080
# Let supervisord start nginx & php-fpm
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
# Configure a healthcheck to validate that everything is up&running
HEALTHCHECK --timeout=10s CMD curl --silent --fail http://127.0.0.1:8080/fpm-ping || exit 1

View file

@ -1,25 +1,79 @@
**If you want to contribute to a _translation_:**[![Crowdin](https://badges.crowdin.net/fediplan/localized.svg)](https://crowdin.com/project/fediplan)
### How to install FediPlan
## How to install FediPlan
**1 - Clone the repository**
There are 2 methods to run Fediplan
1. Build and run Docker image
2. Manual install
**2 - Install vendors:**
### 1. Build and run Docker image
`composer install -o`
#### Required programs
- `git`
- `docker`
PS: You need to install `composer`, just use:
#### Steps
`apt install composer`
1. Install required programs
Or you can do that with the following commands:
2. Clone this repo<br>
`git clone https://framagit.org/tom79/fediplan.git fediplan`
```
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === '48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
```
3. Build the Docker image<br>
`docker build --tag fediplan fediplan`
4. Run the docker image<br>
`docker run --detach --restart unless-stopped --name fediplan fediplan:latest`
5. Find the IP<br>
`docker inspect fediplan | grep IPAddress`
6. Fediplan should be available at _<ip_address>:8080_
### 2. Manual install
#### Required programs
- `git`
- `php 8.3` with these extensions:
For Composer
- `openssl`
- `phar`
- `iconv`
- `xml`
- `simplexml`
- `xmlwriter`
For running
- `ctype`
- `curl`
- `dom`
- `fpm`
- `intl`
- `mbstring`
- `simplexml`
- `session`
- `tokenizer`
- `composer` ([getcomposer.org/download](https://getcomposer.org/download/))
#### Steps
1. Install required programs
2. Clone this repo<br>
`git clone https://framagit.org/tom79/fediplan.git fediplan`
3. Install vendors<br>
`php composer.phar install --optimize-autoloader --working-dir=fediplan`
4. Point your server software to `fediplan/public` folder
### Support My work
[fedilab.app/page/donations](https://fedilab.app/page/donations/)
### Credits
Docker configurations are based on [github.com/TrafeX/docker-php-nginx](https://github.com/TrafeX/docker-php-nginx)
See: [Download Composer](https://getcomposer.org/download/)

10
assets/app.js Normal file
View file

@ -0,0 +1,10 @@
import './bootstrap.js';
/*
* Welcome to your app's main JavaScript file!
*
* This file will be included onto the page via the importmap() Twig function,
* which should already be in your base.html.twig.
*/
import './styles/app.css';
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');

5
assets/bootstrap.js vendored Normal file
View file

@ -0,0 +1,5 @@
import { startStimulusApp } from '@symfony/stimulus-bundle';
const app = startStimulusApp();
// register any custom, 3rd party controllers here
// app.register('some_controller_name', SomeImportedController);

15
assets/controllers.json Normal file
View file

@ -0,0 +1,15 @@
{
"controllers": {
"@symfony/ux-turbo": {
"turbo-core": {
"enabled": true,
"fetch": "eager"
},
"mercure-turbo-stream": {
"enabled": false,
"fetch": "eager"
}
}
},
"entrypoints": []
}

View file

@ -0,0 +1,16 @@
import { Controller } from '@hotwired/stimulus';
/*
* This is an example Stimulus controller!
*
* Any element with a data-controller="hello" attribute will cause
* this controller to be executed. The name "hello" comes from the filename:
* hello_controller.js -> "hello"
*
* Delete this file or adapt it for your use!
*/
export default class extends Controller {
connect() {
this.element.textContent = 'Hello Stimulus! Edit me in assets/controllers/hello_controller.js';
}
}

1
assets/js/routes.json Normal file
View file

@ -0,0 +1 @@
{"base_url":"","routes":{"load_more":{"tokens":[["variable","\/","[^\/]++","max_id",true],["text","\/scheduled\/messages"],["variable","\/","[^\/]++","_locale",true]],"defaults":{"max_id":null,"_locale":""},"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"delete_message":{"tokens":[["variable","\/","[^\/]++","id",true],["text","\/scheduled\/delete\/messages"],["variable","\/","fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja|pl|ru|uk","_locale",true]],"defaults":{"_locale":"en","id":null},"requirements":{"_locale":"fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja|pl|ru|uk"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":""}

View file

@ -0,0 +1 @@
{"base_url":"","routes":{"load_more":{"tokens":[["variable","\/","[^\/]++","max_id",true],["text","\/scheduled\/messages"],["variable","\/","[^\/]++","_locale",true]],"defaults":{"max_id":null,"_locale":""},"requirements":[],"hosttokens":[],"methods":[],"schemes":[]},"delete_message":{"tokens":[["variable","\/","[^\/]++","id",true],["text","\/scheduled\/delete\/messages"],["variable","\/","fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja|pl|ru|uk","_locale",true]],"defaults":{"_locale":"en","id":null},"requirements":{"_locale":"fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja|pl|ru|uk"},"hosttokens":[],"methods":["POST"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":""}

3
assets/styles/app.css Normal file
View file

@ -0,0 +1,3 @@
body {
background-color: skyblue;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

22
assets/vendor/installed.php vendored Normal file
View file

@ -0,0 +1,22 @@
<?php return array (
'@hotwired/stimulus' =>
array (
'version' => '3.2.2',
'dependencies' =>
array (
),
'extraFiles' =>
array (
),
),
'@hotwired/turbo' =>
array (
'version' => '7.3.0',
'dependencies' =>
array (
),
'extraFiles' =>
array (
),
),
);

View file

@ -3,40 +3,19 @@
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
if (false === in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.\PHP_SAPI.' SAPI'.\PHP_EOL;
if (!is_dir(dirname(__DIR__).'/vendor')) {
throw new LogicException('Dependencies are missing. Try running "composer install".');
}
set_time_limit(0);
require dirname(__DIR__).'/vendor/autoload.php';
if (!class_exists(Application::class)) {
throw new RuntimeException('You need to add "symfony/framework-bundle" as a Composer dependency.');
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) {
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
}
$input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
if ($input->hasParameterOption('--no-debug', true)) {
putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
}
return function (array $context) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
require dirname(__DIR__).'/config/bootstrap.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
if (class_exists(Debug::class)) {
Debug::enable();
}
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new Application($kernel);
$application->run($input);
return new Application($kernel);
};

View file

@ -1,35 +1,54 @@
{
"type": "project",
"license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"php": "^7.1.3",
"php": ">=8.2",
"ext-ctype": "*",
"ext-curl": "*",
"ext-iconv": "*",
"craue/formflow-bundle": "^3.2",
"doctrine/collections": "^1.6",
"friendsofsymfony/jsrouting-bundle": "^2.4",
"sensio/framework-extra-bundle": "^5.4",
"symfony/asset": "4.3.*",
"symfony/console": "4.3.*",
"symfony/debug": "4.3.*",
"symfony/dotenv": "4.3.*",
"symfony/flex": "^1.3.1",
"symfony/framework-bundle": "4.3.*",
"symfony/polyfill-intl-messageformatter": "^1.15",
"symfony/security-bundle": "4.3.*",
"symfony/translation": "4.3.*",
"symfony/twig-bundle": "4.3.*",
"symfony/yaml": "4.3.*",
"twig/extensions": "^1.5",
"ext-curl": "*",
"ext-json": "*"
},
"require-dev": {
"symfony/web-server-bundle": "4.3.*"
"craue/formflow-bundle": "*",
"curl/curl": "^2.5",
"friendsofsymfony/jsrouting-bundle": "*",
"phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.29",
"symfony/asset": "7.0.*",
"symfony/asset-mapper": "7.0.*",
"symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*",
"symfony/expression-language": "7.0.*",
"symfony/flex": "^2",
"symfony/form": "7.0.*",
"symfony/framework-bundle": "7.0.*",
"symfony/http-client": "7.0.*",
"symfony/intl": "7.0.*",
"symfony/mime": "7.0.*",
"symfony/monolog-bundle": "^3.0",
"symfony/notifier": "7.0.*",
"symfony/process": "7.0.*",
"symfony/property-access": "7.0.*",
"symfony/property-info": "7.0.*",
"symfony/runtime": "7.0.*",
"symfony/security-bundle": "7.0.*",
"symfony/serializer": "7.0.*",
"symfony/stimulus-bundle": "^2.17",
"symfony/string": "7.0.*",
"symfony/translation": "7.0.*",
"symfony/twig-bundle": "7.0.*",
"symfony/ux-turbo": "^2.17",
"symfony/validator": "7.0.*",
"symfony/web-link": "7.0.*",
"symfony/yaml": "7.0.*",
"twig/extra-bundle": "^3.20",
"twig/intl-extra": "^3.20",
"twig/twig": "v3.15.0"
},
"config": {
"preferred-install": {
"*": "dist"
"allow-plugins": {
"php-http/discovery": true,
"symfony/flex": true,
"symfony/runtime": true
},
"sort-packages": true
},
@ -44,17 +63,22 @@
}
},
"replace": {
"paragonie/random_compat": "2.*",
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php71": "*",
"symfony/polyfill-php70": "*",
"symfony/polyfill-php56": "*"
"symfony/polyfill-php72": "*",
"symfony/polyfill-php73": "*",
"symfony/polyfill-php74": "*",
"symfony/polyfill-php80": "*",
"symfony/polyfill-php81": "*",
"symfony/polyfill-php82": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
"assets:install %PUBLIC_DIR%": "symfony-cmd",
"assets:install --symlink public": "symfony-cmd",
"importmap:install": "symfony-cmd",
"asset-map:compile": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
@ -69,7 +93,17 @@
"extra": {
"symfony": {
"allow-contrib": false,
"require": "4.3.*"
"require": "7.0.*"
}
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"symfony/browser-kit": "7.0.*",
"symfony/css-selector": "7.0.*",
"symfony/debug-bundle": "7.0.*",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^7.0",
"symfony/stopwatch": "7.0.*",
"symfony/web-profiler-bundle": "7.0.*"
}
}

7935
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,15 @@
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::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],
FOS\JsRoutingBundle\FOSJsRoutingBundle::class => ['all' => true],
Craue\FormFlowBundle\CraueFormFlowBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
Symfony\UX\Turbo\TurboBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
];

View file

@ -0,0 +1,5 @@
framework:
asset_mapper:
# The paths to make available to the asset mapper.
paths:
- assets/

View file

@ -0,0 +1,5 @@
when@dev:
debug:
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
# See the "server:dump" command to start a new server.
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"

View file

@ -1,3 +0,0 @@
framework:
router:
strict_requirements: true

View file

@ -1,16 +1,16 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
#http_method_override: 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
# Note that the session will be started ONLY if you read or write from it.
session: true
#esi: true
#fragments: true
php_errors:
log: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View file

@ -1,4 +1,10 @@
framework:
router:
strict_requirements: null
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

View file

@ -1,13 +1,19 @@
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#where-do-users-come-from-user-providers
providers:
in_memory: { memory: ~ }
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
id: App\Security\UserProvider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
lazy: true
provider: app_user_provider
logout:
path: logout
@ -23,3 +29,16 @@ security:
# - { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/(%languages%)?/schedule, roles: ROLE_USER }
- { path: ^/(%languages%)?/scheduled, 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

View file

@ -1,3 +0,0 @@
sensio_framework_extra:
router:
annotations: false

View file

@ -1,4 +0,0 @@
framework:
test: true
session:
storage_id: session.storage.mock_file

View file

@ -1,3 +0,0 @@
framework:
router:
strict_requirements: true

View file

@ -1,3 +0,0 @@
framework:
validation:
not_compromised_password: false

View file

@ -1,4 +1,6 @@
twig:
default_path: '%kernel.project_dir%/templates'
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
file_name_pattern: '*.twig'
when@test:
twig:
strict_variables: true

View file

@ -1,8 +1,11 @@
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\: []
# 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

View file

@ -0,0 +1,17 @@
when@dev:
web_profiler:
toolbar: true
intercept_redirects: false
framework:
profiler:
only_exceptions: false
collect_serializer_data: true
when@test:
web_profiler:
toolbar: false
intercept_redirects: false
framework:
profiler: { collect: false }

5
config/preload.php Normal file
View file

@ -0,0 +1,5 @@
<?php
if (file_exists(dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php')) {
require dirname(__DIR__).'/var/cache/prod/App_KernelProdContainer.preload.php';
}

View file

@ -1,3 +1,5 @@
#index:
# path: /
# controller: App\Controller\DefaultController::index
controllers:
resource:
path: ../src/Controller/
namespace: App\Controller
type: attribute

View file

@ -1,3 +0,0 @@
controllers:
resource: ../../src/Controller/
type: annotation

View file

@ -1,3 +0,0 @@
_errors:
resource: '@TwigBundle/Resources/config/routing/errors.xml'
prefix: /_error

View file

@ -0,0 +1,4 @@
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
prefix: /_error

View file

@ -0,0 +1,3 @@
_security_logout:
resource: security.route_loader.logout
type: service

View file

@ -0,0 +1,8 @@
when@dev:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
prefix: /_profiler

View file

@ -4,7 +4,7 @@
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
allowed_language: "fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja"
allowed_language: "fr|en|nl|pt-PT|pt-BR|de|ar|it|ca|ja|pl|ru|uk"
languages: "(%allowed_language%)?"
services:
# default configuration for services in *this* file
@ -16,7 +16,10 @@ services:
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
exclude:
- '../src/DependencyInjection/'
- '../src/Entity/'
- '../src/Kernel.php'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class

View file

@ -0,0 +1,56 @@
# Default server definition
server {
listen [::]:8080 default_server;
listen 8080 default_server;
server_name _;
sendfile off;
tcp_nodelay on;
absolute_redirect off;
root /var/www/fediplan/public;
index index.php index.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.php
try_files $uri $uri/ /index.php?q=$uri&$args;
}
# Redirect server error pages to the static page /50x.html
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/lib/nginx/html;
}
# Pass the PHP scripts to PHP-FPM listening on php-fpm.sock
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
# Set the cache-control headers on assets to cache for 5 days
location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
expires 5d;
}
# Deny access to . files, for security
location ~ /\. {
log_not_found off;
deny all;
}
# Allow fpm ping and status from localhost
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/run/php-fpm.sock;
}
}

View file

@ -0,0 +1,56 @@
[global]
; Log to stderr
error_log = /dev/stderr
[www]
; The address on which to accept FastCGI requests.
; Valid syntaxes are:
; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on
; a specific port;
; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on
; a specific port;
; 'port' - to listen on a TCP socket to all addresses
; (IPv6 and IPv4-mapped) on a specific port;
; '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = /run/php-fpm.sock
; Enable status page
pm.status_path = /fpm-status
; Ondemand process manager
pm = ondemand
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI. The below defaults are based on a server without much resources. Don't
; forget to tweak pm.* to fit your needs.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 100
; The number of seconds after which an idle process will be killed.
; Note: Used only when pm is set to 'ondemand'
; Default Value: 10s
pm.process_idle_timeout = 10s;
; The number of requests each child process should execute before respawning.
; This can be useful to work around memory leaks in 3rd party libraries. For
; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS.
; Default Value: 0
pm.max_requests = 1000
; Make sure the FPM workers can reach the environment variables for configuration
clear_env = no
; Catch output from PHP
catch_workers_output = yes
; Remove the 'child 10 said into stderr' prefix in the log and only show the actual message
decorate_workers_output = no
; Enable ping page to use in healthcheck
ping.path = /fpm-ping

47
docker_config/nginx.conf Normal file
View file

@ -0,0 +1,47 @@
worker_processes auto;
error_log stderr warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
# Threat files with a unknown filetype as binary
default_type application/octet-stream;
# Define custom log format to include reponse times
log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time $pipe $upstream_cache_status';
access_log /dev/stdout main_timed;
error_log /dev/stderr notice;
keepalive_timeout 65;
# Write temporary files to /tmp so they can be created as a non-privileged user
client_body_temp_path /tmp/client_temp;
proxy_temp_path /tmp/proxy_temp_path;
fastcgi_temp_path /tmp/fastcgi_temp;
uwsgi_temp_path /tmp/uwsgi_temp;
scgi_temp_path /tmp/scgi_temp;
# Hide headers that identify the server to prevent information leakage
proxy_hide_header X-Powered-By;
fastcgi_hide_header X-Powered-By;
server_tokens off;
# Enable gzip compression by default
gzip on;
gzip_proxied any;
# Based on CloudFlare's recommended settings
gzip_types text/richtext text/plain text/css text/x-script text/x-component text/x-java-source text/x-markdown application/javascript application/x-javascript text/javascript text/js image/x-icon image/vnd.microsoft.icon application/x-perl application/x-httpd-cgi text/xml application/xml application/rss+xml application/vnd.api+json application/x-protobuf application/json multipart/bag multipart/mixed application/xhtml+xml font/ttf font/otf font/x-woff image/svg+xml application/vnd.ms-fontobject application/ttf application/x-ttf application/otf application/x-otf application/truetype application/opentype application/x-opentype application/font-woff application/eot application/font application/font-sfnt application/wasm application/javascript-binast application/manifest+json application/ld+json application/graphql+json application/geo+json;
gzip_vary on;
gzip_disable "msie6";
# Include server configs
include /etc/nginx/conf.d/*.conf;
}

3
docker_config/php.ini Normal file
View file

@ -0,0 +1,3 @@
[Date]
date.timezone="UTC"
expose_php= Off

View file

@ -0,0 +1,23 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid
[program:php-fpm]
command=php-fpm83 -F
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0
[program:nginx]
command=nginx -g 'daemon off;'
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autorestart=false
startretries=0

28
importmap.php Normal file
View file

@ -0,0 +1,28 @@
<?php
/**
* Returns the importmap for this application.
*
* - "path" is a path inside the asset mapper system. Use the
* "debug:asset-map" command to see the full list of paths.
*
* - "entrypoint" (JavaScript only) set to true for any module that will
* be used as an "entrypoint" (and passed to the importmap() Twig function).
*
* The "importmap:require" command can be used to add new entries to this file.
*/
return [
'app' => [
'path' => './assets/app.js',
'entrypoint' => true,
],
'@hotwired/stimulus' => [
'version' => '3.2.2',
],
'@symfony/stimulus-bundle' => [
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
],
'@hotwired/turbo' => [
'version' => '7.3.0',
],
];

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,57 +1,268 @@
/*!
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2018 The Bootstrap Authors
* Copyright 2011-2018 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--bs-blue: #0d6efd;
--bs-indigo: #6610f2;
--bs-purple: #6f42c1;
--bs-pink: #d63384;
--bs-red: #dc3545;
--bs-orange: #fd7e14;
--bs-yellow: #ffc107;
--bs-green: #198754;
--bs-teal: #20c997;
--bs-cyan: #0dcaf0;
--bs-black: #000;
--bs-white: #fff;
--bs-gray: #6c757d;
--bs-gray-dark: #343a40;
--bs-gray-100: #f8f9fa;
--bs-gray-200: #e9ecef;
--bs-gray-300: #dee2e6;
--bs-gray-400: #ced4da;
--bs-gray-500: #adb5bd;
--bs-gray-600: #6c757d;
--bs-gray-700: #495057;
--bs-gray-800: #343a40;
--bs-gray-900: #212529;
--bs-primary: #0d6efd;
--bs-secondary: #6c757d;
--bs-success: #198754;
--bs-info: #0dcaf0;
--bs-warning: #ffc107;
--bs-danger: #dc3545;
--bs-light: #f8f9fa;
--bs-dark: #212529;
--bs-primary-rgb: 13, 110, 253;
--bs-secondary-rgb: 108, 117, 125;
--bs-success-rgb: 25, 135, 84;
--bs-info-rgb: 13, 202, 240;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
--bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 1rem;
--bs-body-font-weight: 400;
--bs-body-line-height: 1.5;
--bs-body-color: #212529;
--bs-body-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
font-family: sans-serif;
line-height: 1.15;
-webkit-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
-ms-overflow-style: scrollbar;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
@-ms-viewport {
width: device-width;
}
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
display: block;
@media (prefers-reduced-motion: no-preference) {
:root {
scroll-behavior: smooth;
}
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-size: 1rem;
font-weight: 400;
line-height: 1.5;
color: #212529;
text-align: left;
background-color: #fff;
}
[tabindex="-1"]:focus {
outline: 0 !important;
font-family: var(--bs-body-font-family);
font-size: var(--bs-body-font-size);
font-weight: var(--bs-body-font-weight);
line-height: var(--bs-body-line-height);
color: var(--bs-body-color);
text-align: var(--bs-body-text-align);
background-color: var(--bs-body-bg);
-webkit-text-size-adjust: 100%;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h1, h2, h3, h4, h5, h6 {
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
@ -59,13 +270,12 @@ p {
margin-bottom: 1rem;
}
abbr[title],
abbr[data-original-title] {
text-decoration: underline;
abbr[title] {
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted;
cursor: help;
border-bottom: 0;
-webkit-text-decoration-skip-ink: none;
text-decoration-skip-ink: none;
}
address {
@ -74,6 +284,11 @@ address {
line-height: inherit;
}
ol,
ul {
padding-left: 2rem;
}
ol,
ul,
dl {
@ -93,7 +308,7 @@ dt {
}
dd {
margin-bottom: .5rem;
margin-bottom: 0.5rem;
margin-left: 0;
}
@ -101,118 +316,139 @@ blockquote {
margin: 0 0 1rem;
}
dfn {
font-style: italic;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 80%;
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 75%;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -.25em;
bottom: -0.25em;
}
sup {
top: -.5em;
top: -0.5em;
}
a {
color: #007bff;
text-decoration: none;
background-color: transparent;
-webkit-text-decoration-skip: objects;
}
a:hover {
color: #0056b3;
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit;
text-decoration: none;
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre,
code,
kbd,
samp {
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
-ms-overflow-style: scrollbar;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img {
vertical-align: middle;
border-style: none;
}
img,
svg {
overflow: hidden;
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
color: #6c757d;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
caption-side: bottom;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
margin-bottom: 0.5rem;
}
button {
border-radius: 0;
}
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
button:focus:not(:focus-visible) {
outline: 0;
}
input,
@ -226,46 +462,45 @@ textarea {
line-height: inherit;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html [type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
[role=button] {
cursor: pointer;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
select {
word-wrap: normal;
}
select:disabled {
opacity: 1;
}
[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner {
padding: 0;
border-style: none;
}
input[type="radio"],
input[type="checkbox"] {
box-sizing: border-box;
padding: 0;
}
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"] {
-webkit-appearance: listbox;
}
textarea {
overflow: auto;
resize: vertical;
}
@ -277,34 +512,55 @@ fieldset {
}
legend {
display: block;
float: left;
width: 100%;
max-width: 100%;
padding: 0;
margin-bottom: .5rem;
font-size: 1.5rem;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
color: inherit;
white-space: normal;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
progress {
vertical-align: baseline;
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
::-webkit-inner-spin-button {
height: auto;
}
[type="search"] {
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
@ -312,20 +568,30 @@ progress {
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
template {
display: none;
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
public/css/bootstrap-toggle.min.css vendored Normal file
View file

@ -0,0 +1,14 @@
/* Copyright Notice
* bootstrap5-toggle v5.1.1
* https://palcarazm.github.io/bootstrap5-toggle/
* @author 2011-2014 Min Hur (https://github.com/minhur)
* @author 2018-2019 Brent Ely (https://github.com/gitbrent)
* @author 2022 Pablo Alcaraz Martínez (https://github.com/palcarazm)
* @funding GitHub Sponsors
* @see https://github.com/sponsors/palcarazm
* @license MIT
* @see https://github.com/palcarazm/bootstrap5-toggle/blob/master/LICENSE
*/
.btn-group-xs>.btn,.btn-xs{padding:.35rem .4rem .25rem .4rem;font-size:.875rem;line-height:.5;border-radius:.2rem}.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-1.25rem;margin-right:.35rem}.toggle{position:relative;overflow:hidden}.toggle:focus>.toggle-group>.btn,.toggle:hover>.toggle-group>.btn{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.toggle:focus>.toggle-group>.toggle-handle,.toggle:hover>.toggle-group>.toggle-handle{background-color:var(--bs-light);opacity:.5}.toggle>input[type=checkbox]{display:none}.toggle>.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;user-select:none;-moz-user-select:none;-webkit-user-select:none}.toggle>.toggle-group>span{cursor:pointer}.toggle.off>.toggle-group{left:-100%}.toggle.indeterminate>.toggle-group{left:-50%}.toggle>.toggle-group>.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}.toggle>.toggle-group>.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0;box-shadow:none}.toggle>.toggle-group>.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px;background-color:var(--bs-light);border-color:var(--bs-light)}.input-group .toggle>.toggle-group>.toggle-off,.input-group .toggle>.toggle-group>.toggle-on{position:absolute}.toggle:not(:hover):not(:focus).btn-outline-primary>.toggle-group>.toggle-handle{background-color:var(--bs-primary);border-color:var(--bs-primary)}.toggle:not(:hover):not(:focus).btn-outline-secondary>.toggle-group>.toggle-handle{background-color:var(--bs-secondary);border-color:var(--bs-secondary)}.toggle:not(:hover):not(:focus).btn-outline-success>.toggle-group>.toggle-handle{background-color:var(--bs-success);border-color:var(--bs-success)}.toggle:not(:hover):not(:focus).btn-outline-danger>.toggle-group>.toggle-handle{background-color:var(--bs-danger);border-color:var(--bs-danger)}.toggle:not(:hover):not(:focus).btn-outline-warning>.toggle-group>.toggle-handle{background-color:var(--bs-warning);border-color:var(--bs-warning)}.toggle:not(:hover):not(:focus).btn-outline-info>.toggle-group>.toggle-handle{background-color:var(--bs-info);border-color:var(--bs-info)}.toggle:not(:hover):not(:focus).btn-outline-light>.toggle-group>.toggle-handle{background-color:var(--bs-light);border-color:var(--bs-light)}.toggle:not(:hover):not(:focus).btn-outline-dark>.toggle-group>.toggle-handle{background-color:var(--bs-dark);border-color:var(--bs-dark)}.toggle.btn{min-width:3.7rem;min-height:2.15rem}.toggle>.toggle-group>.toggle-on.btn{padding-right:1.5rem}.toggle>.toggle-group>.toggle-off.btn{padding-left:1.5rem}.toggle.btn-lg{min-width:5rem;min-height:2.815rem}.toggle>.toggle-group>.toggle-on.btn-lg{padding-right:2rem}.toggle>.toggle-group>.toggle-off.btn-lg{padding-left:2rem}.toggle>.toggle-group>.toggle-handle.btn-lg{width:2.5rem}.toggle.btn-sm{min-width:3.125rem;min-height:1.938rem}.toggle>.toggle-group>.toggle-on.btn-sm{padding-right:1rem}.toggle>.toggle-group>.toggle-off.btn-sm{padding-left:1rem}.toggle.btn-xs{min-width:2.19rem;min-height:1.375rem}.toggle>.toggle-group>.toggle-on.btn-xs{padding-right:.8rem}.toggle>.toggle-group>.toggle-off.btn-xs{padding-left:.8rem}
/*# sourceMappingURL=bootstrap5-toggle.min.css.map */

16439
public/css/bootstrap.css vendored

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,42 +0,0 @@
/*\
|*| ========================================================================
|*| Bootstrap Toggle: bootstrap4-toggle.css v3.5.0
|*| https://gitbrent.github.io/bootstrap-toggle/
|*| ========================================================================
|*| Copyright 2018-2019 Brent Ely
|*| Licensed under MIT
|*| ========================================================================
\*/
.btn-group-xs>.btn,.btn-xs{padding:.35rem .4rem .25rem;font-size:.875rem;line-height:.5;border-radius:.2rem}
.checkbox label .toggle,.checkbox-inline .toggle{margin-left:-1.25rem;margin-right:.35rem}
.toggle{position:relative;overflow:hidden}
.toggle.btn.btn-light,.toggle.btn.btn-outline-light{border-color:rgba(0,0,0,.15)}
.toggle input[type=checkbox]{display:none}
.toggle-group{position:absolute;width:200%;top:0;bottom:0;left:0;transition:left .35s;-webkit-transition:left .35s;-moz-user-select:none;-webkit-user-select:none}
.toggle-group label,.toggle-group span{cursor:pointer}
.toggle.off .toggle-group{left:-100%}
.toggle-on{position:absolute;top:0;bottom:0;left:0;right:50%;margin:0;border:0;border-radius:0}
.toggle-off{position:absolute;top:0;bottom:0;left:50%;right:0;margin:0;border:0;border-radius:0;box-shadow:none}
.toggle-handle{position:relative;margin:0 auto;padding-top:0;padding-bottom:0;height:100%;width:0;border-width:0 1px;background-color:#fff}
.toggle.btn-outline-primary .toggle-handle{background-color:var(--primary);border-color:var(--primary)}
.toggle.btn-outline-secondary .toggle-handle{background-color:var(--secondary);border-color:var(--secondary)}
.toggle.btn-outline-success .toggle-handle{background-color:var(--success);border-color:var(--success)}
.toggle.btn-outline-danger .toggle-handle{background-color:var(--danger);border-color:var(--danger)}
.toggle.btn-outline-warning .toggle-handle{background-color:var(--warning);border-color:var(--warning)}
.toggle.btn-outline-info .toggle-handle{background-color:var(--info);border-color:var(--info)}
.toggle.btn-outline-light .toggle-handle{background-color:var(--light);border-color:var(--light)}
.toggle.btn-outline-dark .toggle-handle{background-color:var(--dark);border-color:var(--dark)}
.toggle[class*=btn-outline]:hover .toggle-handle{background-color:var(--light);opacity:.5}
.toggle.btn{min-width:3.7rem;min-height:2.15rem}
.toggle-on.btn{padding-right:1.5rem}
.toggle-off.btn{padding-left:1.5rem}
.toggle.btn-lg{min-width:5rem;min-height:2.815rem}
.toggle-on.btn-lg{padding-right:2rem}
.toggle-off.btn-lg{padding-left:2rem}
.toggle-handle.btn-lg{width:2.5rem}
.toggle.btn-sm{min-width:3.125rem;min-height:1.938rem}
.toggle-on.btn-sm{padding-right:1rem}
.toggle-off.btn-sm{padding-left:1rem}
.toggle.btn-xs{min-width:2.19rem;min-height:1.375rem}
.toggle-on.btn-xs{padding-right:.8rem}
.toggle-off.btn-xs{padding-left:.8rem}

View file

@ -1,27 +1,9 @@
<?php
use App\Kernel;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
require dirname(__DIR__).'/config/bootstrap.php';
require_once dirname(__DIR__).'/vendor/autoload_runtime.php';
if ($_SERVER['APP_DEBUG']) {
umask(0000);
Debug::enable();
}
if ($trustedProxies = $_SERVER['TRUSTED_PROXIES'] ?? $_ENV['TRUSTED_PROXIES'] ?? false) {
Request::setTrustedProxies(explode(',', $trustedProxies), Request::HEADER_X_FORWARDED_ALL ^ Request::HEADER_X_FORWARDED_HOST);
}
if ($trustedHosts = $_SERVER['TRUSTED_HOSTS'] ?? $_ENV['TRUSTED_HOSTS'] ?? false) {
Request::setTrustedHosts([$trustedHosts]);
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

14
public/js/bootstrap-toggle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4447
public/js/bootstrap.esm.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

7
public/js/bootstrap.esm.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

8020
public/js/bootstrap.js vendored

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,11 +0,0 @@
/*\
|*| ========================================================================
|*| Bootstrap Toggle: bootstrap4-toggle.js v3.5.0
|*| https://gitbrent.github.io/bootstrap-toggle/
|*| ========================================================================
|*| Copyright 2018-2019 Brent Ely
|*| Licensed under MIT
|*| ========================================================================
\*/
+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.toggle"),f="object"==typeof b&&b;e||d.data("bs.toggle",e=new c(this,f)),"string"==typeof b&&e[b]&&e[b]()})}var c=function(b,c){this.$element=a(b),this.options=a.extend({},this.defaults(),c),this.render()};c.VERSION="3.5.0",c.DEFAULTS={on:"On",off:"Off",onstyle:"primary",offstyle:"light",size:"normal",style:"",width:null,height:null},c.prototype.defaults=function(){return{on:this.$element.attr("data-on")||c.DEFAULTS.on,off:this.$element.attr("data-off")||c.DEFAULTS.off,onstyle:this.$element.attr("data-onstyle")||c.DEFAULTS.onstyle,offstyle:this.$element.attr("data-offstyle")||c.DEFAULTS.offstyle,size:this.$element.attr("data-size")||c.DEFAULTS.size,style:this.$element.attr("data-style")||c.DEFAULTS.style,width:this.$element.attr("data-width")||c.DEFAULTS.width,height:this.$element.attr("data-height")||c.DEFAULTS.height}},c.prototype.render=function(){this._onstyle="btn-"+this.options.onstyle,this._offstyle="btn-"+this.options.offstyle;var b="large"===this.options.size||"lg"===this.options.size?"btn-lg":"small"===this.options.size||"sm"===this.options.size?"btn-sm":"mini"===this.options.size||"xs"===this.options.size?"btn-xs":"",c=a('<label class="btn">').html(this.options.on).addClass(this._onstyle+" "+b),d=a('<label class="btn">').html(this.options.off).addClass(this._offstyle+" "+b),e=a('<span class="toggle-handle btn btn-light">').addClass(b),f=a('<div class="toggle-group">').append(c,d,e),g=a('<div class="toggle btn" data-toggle="toggle" role="button">').addClass(this.$element.prop("checked")?this._onstyle:this._offstyle+" off").addClass(b).addClass(this.options.style);this.$element.wrap(g),a.extend(this,{$toggle:this.$element.parent(),$toggleOn:c,$toggleOff:d,$toggleGroup:f}),this.$toggle.append(f);var h=this.options.width||Math.max(c.outerWidth(),d.outerWidth())+e.outerWidth()/2,i=this.options.height||Math.max(c.outerHeight(),d.outerHeight());c.addClass("toggle-on"),d.addClass("toggle-off"),this.$toggle.css({width:h,height:i}),this.options.height&&(c.css("line-height",c.height()+"px"),d.css("line-height",d.height()+"px")),this.update(!0),this.trigger(!0)},c.prototype.toggle=function(){this.$element.prop("checked")?this.off():this.on()},c.prototype.on=function(a){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._offstyle+" off").addClass(this._onstyle),this.$element.prop("checked",!0),a||this.trigger()},c.prototype.off=function(a){if(this.$element.prop("disabled"))return!1;this.$toggle.removeClass(this._onstyle).addClass(this._offstyle+" off"),this.$element.prop("checked",!1),a||this.trigger()},c.prototype.enable=function(){this.$toggle.removeAttr("disabled"),this.$element.prop("disabled",!1)},c.prototype.disable=function(){this.$toggle.attr("disabled","disabled"),this.$element.prop("disabled",!0)},c.prototype.update=function(a){this.$element.prop("disabled")?this.disable():this.enable(),this.$element.prop("checked")?this.on(a):this.off(a)},c.prototype.trigger=function(b){this.$element.off("change.bs.toggle"),b||this.$element.change(),this.$element.on("change.bs.toggle",a.proxy(function(){this.update()},this))},c.prototype.destroy=function(){this.$element.off("change.bs.toggle"),this.$toggleGroup.remove(),this.$element.removeData("bs.toggle"),this.$element.unwrap()};var d=a.fn.bootstrapToggle;a.fn.bootstrapToggle=b,a.fn.bootstrapToggle.Constructor=c,a.fn.toggle.noConflict=function(){return a.fn.bootstrapToggle=d,this},a(function(){a("input[type=checkbox][data-toggle^=toggle]").bootstrapToggle()}),a(document).on("click.bs.toggle","div[data-toggle^=toggle]",function(b){a(this).find("input[type=checkbox]").bootstrapToggle("toggle"),b.preventDefault()})}(jQuery);
//# sourceMappingURL=bootstrap4-toggle.min.js.map

View file

@ -1400,13 +1400,14 @@ document = window.document || {};
self.editor.html(self.content = '');
}
source[sourceValFunc](self.getText());
let inputText;
inputText = self.getText();
inputText = inputText
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3')
.replace(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/g, 'xxxxxxxxxxxxxxxxxxxxxxx');
$("#count").text(inputText.length);
var count = 0;
$('.emojionearea-editor').each(function() {
var currentElement = $(this);
count += currentElement.text()
.replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3')
.replace(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/g, 'xxxxxxxxxxxxxxxxxxxxxxx').length;
});
$("#count").text(count);
});
if (options.shortcuts) {
self.on("@keydown", function(_, e) {
@ -1456,12 +1457,11 @@ document = window.document || {};
id: css_class,
match: /\B((:[\-+\w]*)|(@[\-+\w]*)|(#[\-+\w]*))$/,
search: function (term, callback) {
if (term.startsWith(":")) {
callback($.map(map, function (emoji) {
return emoji.indexOf(term) === 0 ? emoji : null;
}));
} else if (term.startsWith("@")){
} else if (term.startsWith("@") && term.substring(1).length > 1){
$.ajax({
url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=accounts&q="+term.substring(1),
headers: {"Authorization": $('#data_api').attr('data-token')},
@ -1471,7 +1471,7 @@ document = window.document || {};
return value;
}));
});
}else if (term.startsWith("#")){
}else if (term.startsWith("#") && term.substring(1).length > 1){
$.ajax({
url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=hashtags&q="+term.substring(1),
headers: {"Authorization": $('#data_api').attr('data-token')},
@ -1481,6 +1481,10 @@ document = window.document || {};
return value;
}));
});
} else {
callback($.map(map, function () {
return null;
}));
}
},
template: function (value) {
@ -1500,7 +1504,6 @@ document = window.document || {};
}else if (typeof value.name != 'undefined') {
return "#"+value.name+ "&nbsp;";
}else{
return shortnameTo(value, self.emojiTemplate);
}
},

View file

@ -1,8 +1,4 @@
<?php /** @noinspection PhpUndefinedClassInspection */
/** @noinspection PhpDocSignatureInspection */
/** @noinspection PhpUnused */
/** @noinspection DuplicatedCode */
/** @noinspection PhpTranslationKeyInspection */
<?php
/**
* Created by fediplan.
@ -15,34 +11,39 @@ namespace App\Controller;
use App\Form\ComposeType;
use App\Form\ConnectMastodonAccountFlow;
use App\Security\MastodonAccount;
use App\Services\Mastodon_api;
use App\SocialEntity\Client;
use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use App\SocialEntity\PollOption;
use DateTime;
use DateTimeZone;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use \Symfony\Component\HttpFoundation\RedirectResponse;
use \Symfony\Component\HttpFoundation\Response;
class FediPlanController extends AbstractController
{
/**
* @Route("/{_locale}",name="index", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
*/
public function indexAction(Request $request, AuthorizationCheckerInterface $authorizationChecker, ConnectMastodonAccountFlow $flow, Mastodon_api $mastodon_api, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher)
#[Route(
'/{_locale}',
name: 'index',
requirements: ['_locale' => '%allowed_language%'],
defaults: ['_locale'=>'en']
)]
public function index(Request $request, AuthorizationCheckerInterface $authorizationChecker, ConnectMastodonAccountFlow $flow, Mastodon_api $mastodon_api, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher): RedirectResponse|Response
{
if ($authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')) {
@ -72,15 +73,11 @@ class FediPlanController extends AbstractController
// form for the next step
$mastodon_api->set_client($createApp['response']['client_id'], $createApp['response']['client_secret']);
$urlToMastodon = $mastodon_api->getAuthorizationUrl();
if (isset($createApp['error'])) {
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_oauth_url', [], 'fediplan', 'en')));
} else {
$flow->saveCurrentStepData($form);
$client_id = $createApp['response']['client_id'];
$client_secret = $createApp['response']['client_secret'];
$flow->nextStep();
$form = $flow->createForm();
}
$flow->saveCurrentStepData($form);
$client_id = $createApp['response']['client_id'];
$client_secret = $createApp['response']['client_secret'];
$flow->nextStep();
$form = $flow->createForm();
}
}
@ -102,14 +99,23 @@ class FediPlanController extends AbstractController
if (isset($accountReply['error'])) {
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_account', [], 'fediplan', 'en')));
} else {
$Account = $mastodon_api->getSingleAccount($accountReply['response']);
$Account->setInstance($host);
$Account->setToken($token_type . " " . $access_token);
$token = new UsernamePasswordToken($Account, null, 'main', array('ROLE_USER'));
$this->get('security.token_storage')->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
$eventDispatcher->dispatch($event, "security.interactive_login");
return $this->redirectToRoute('schedule');
$account = $mastodon_api->getSingleAccount($accountReply['response']);
$instanceReply = $mastodon_api->get_instance();
$instance = $mastodon_api->getInstanceConfiguration($instanceReply['response']);
$session = $request->getSession();
$session->set("instance",$instance);
$account->setInstance($host);
$account->setToken($token_type . " " . $access_token);
$token = new UsernamePasswordToken($account, 'main', array('ROLE_USER'));
try {
$this->container->get('security.token_storage')->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
$eventDispatcher->dispatch($event, "security.interactive_login");
return $this->redirectToRoute('schedule');
} catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) {
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_account', [], 'fediplan', 'en')));
}
}
}
}
@ -128,27 +134,31 @@ class FediPlanController extends AbstractController
}
/**
* @Route("/{_locale}/schedule", name="schedule", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
*/
public function schedule(Request $request, Mastodon_api $mastodon_api, TranslatorInterface $translator)
#[Route(
'/{_locale}/schedule',
name: 'schedule',
requirements: ['_locale' => '%allowed_language%'],
defaults: ['_locale'=>'en']
)]
public function schedule(Request $request, Mastodon_api $mastodon_api, TranslatorInterface $translator): Response
{
$compose = new Compose();
$pollOption1 = new PollOption();
$pollOption1->setTitle("");
$compose->getPollOptions()->add($pollOption1);
$options = $compose->getPollOptions();
$options[] = $pollOption1;
$pollOption2 = new PollOption();
$pollOption2->setTitle("");
$compose->getPollOptions()->add($pollOption2);
$form = $this->createForm(ComposeType::class, $compose, ['user' => $this->getUser()]);
$options[] = $pollOption2;
$compose->setPollOptions($options);
$user = $this->getUser();
$form = $this->createForm(ComposeType::class, $compose, ['user' => $user]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var $data Compose */
$data = $form->getData();
/* @var $user MastodonAccount */
$user = $this->getUser();
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" ", $user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
@ -157,8 +167,7 @@ class FediPlanController extends AbstractController
//Update media description and store their id
foreach ($_POST as $key => $value) {
if ($key != "compose") {
if (strpos($key, 'media_id_') !== false) {
if (str_contains($key, 'media_id_')) {
$mediaId = $value;
$description = $_POST['media_description_' . $mediaId];
@ -182,7 +191,12 @@ class FediPlanController extends AbstractController
}
$params['sensitive'] = ($data->getSensitive() == null || !$data->getSensitive()) ? false : true;
$pollOptions = $data->getPollOptions();
if($data->getAttachPoll() > 0) {
$pollOptions = $data->getPollOptions();
} else{
$pollOptions = array();
}
$pollExpiresAt = $data->getPollExpiresAt();
$isPollMultiple = $data->isPollMultiple();
if (count($pollOptions) > 0) {
@ -227,10 +241,12 @@ class FediPlanController extends AbstractController
$compose = new Compose();
$pollOption1 = new PollOption();
$pollOption1->setTitle("");
$compose->getPollOptions()->add($pollOption1);
$options = $compose->getPollOptions();
$options[] = $pollOption1;
$pollOption2 = new PollOption();
$pollOption2->setTitle("");
$compose->getPollOptions()->add($pollOption2);
$options[] = $pollOption2;
$compose->setPollOptions($options);
$session->getFlashBag()->add(
'Success',
$translator->trans('common.schedule_success', [], 'fediplan', 'en')
@ -238,8 +254,9 @@ class FediPlanController extends AbstractController
$form = $this->createForm(ComposeType::class, $compose, ['user' => $this->getUser()]);
}
}
$user = $this->getUser();
/** @var $user MastodonAccount */
$user = $this->getUser();
return $this->render("fediplan/schedule.html.twig", [
'form' => $form->createView(),
@ -249,42 +266,53 @@ class FediPlanController extends AbstractController
}
/**
* @Route("/{_locale}/scheduled", name="scheduled", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
*/
public function scheduled()
#[Route(
'/{_locale}/scheduled',
name: 'scheduled',
requirements: ['_locale' => '%allowed_language%'],
defaults: ['_locale'=>'en']
)]
public function scheduled(): Response
{
return $this->render("fediplan/scheduled.html.twig");
$user = $this->getUser();
return $this->render("fediplan/scheduled.html.twig", [ 'instance' => $user->getInstance(),]);
}
/**
* @Route("/{_locale}/scheduled/messages/{max_id}", options={"expose"=true}, name="load_more")
*/
public function loadMoreAction(Mastodon_api $mastodon_api, string $max_id = null)
#[Route(
'/{_locale}/scheduled/messages/{max_id}',
name: 'load_more',
options: ['expose' => true]
)]
public function loadMoreAction(Mastodon_api $mastodon_api, ?string $max_id = null , int $limit = 10): JsonResponse
{
/** @var $user MastodonAccount */
$user = $this->getUser();
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" ", $user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$params = [];
$params['limit'] = $limit;
if ($max_id != null) {
$params['max_id'] = $max_id;
}
$scheduled_reply = $mastodon_api->get_scheduled($params);
$statuses = $mastodon_api->getScheduledStatuses($scheduled_reply['response'], $this->getUser());
$data['max_id'] = $scheduled_reply['max_id'];
$statuses = $mastodon_api->getScheduledStatuses($scheduled_reply['response'], $user);
$data['max_id'] = $statuses[count($statuses)-1]->getId();
$data['html'] = $this->renderView('fediplan/Ajax/layout.html.twig', ['statuses' => $statuses]);
return new JsonResponse($data);
}
/**
* @Route("/{_locale}/scheduled/delete/messages/{id}", options={"expose"=true}, name="delete_message", methods={"POST"}, defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
*/
public function deleteMessage(Mastodon_api $mastodon_api, string $id = null)
#[Route(
'/{_locale}/scheduled/delete/messages/{id}',
name: 'delete_message',
requirements: ['_locale' => '%allowed_language%'],
options: ['expose' => true],
defaults: ['_locale'=>'en'],
methods: ['POST']
)]
public function deleteMessage(Mastodon_api $mastodon_api, ?string $id = null): JsonResponse
{
$user = $this->getUser();
$mastodon_api->set_url("https://" . $user->getInstance());
@ -295,19 +323,23 @@ class FediPlanController extends AbstractController
return new JsonResponse($response);
}
/**
* @Route("/about",defaults={"_locale"="en"})
* @Route("/{_locale}/about", name="about", defaults={"_locale":"en"}, requirements={"_locale": "%allowed_language%"})
*/
public function about()
#[Route(
'/{_locale}/about',
name: 'about',
requirements: ['_locale' => '%allowed_language%'],
defaults: ['_locale'=>'en']
)]
public function about(): Response
{
return $this->render("fediplan/about.html.twig");
}
/**
* @Route("/logout", name="logout")
*/
public function logout()
#[Route(
'/logout',
name: 'logout'
)]
public function logout(): Response
{
return $this->render("fediplan/index.html.twig");
}

View file

@ -15,14 +15,14 @@ use Symfony\Component\HttpKernel\KernelEvents;
class LocaleSubscriber implements EventSubscriberInterface
{
private $defaultLocale;
private string $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public static function getSubscribedEvents()
public static function getSubscribedEvents(): array
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
@ -30,7 +30,7 @@ class LocaleSubscriber implements EventSubscriberInterface
];
}
public function onKernelRequest(RequestEvent $event)
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {

View file

@ -10,28 +10,29 @@
namespace App\Form;
use App\Security\MastodonAccount;
use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use DateTime;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\Translator;
class ComposeType extends AbstractType
{
private $securityContext;
private Security $securityContext;
private $translator;
public function __construct(Security $securityContext, Translator $translator)
@ -40,7 +41,7 @@ class ComposeType extends AbstractType
$this->translator = $translator;
}
public function buildForm(FormBuilderInterface $builder, array $options)
public function buildForm(FormBuilderInterface $builder, array $options): void
{
/**@var $user MastodonAccount */
$user = $options['user'];
@ -76,6 +77,8 @@ class ComposeType extends AbstractType
'data' => $user->getDefaultVisibility(),
'label' => 'page.schedule.form.visibility',
'translation_domain' => 'fediplan']);
$builder->add('attach_poll', HiddenType::class, ['required' => true, 'empty_data' => 0]);
$builder->add('timeZone', TimezoneType::class,
[
'label' => 'page.schedule.form.timeZone',
@ -123,7 +126,7 @@ class ComposeType extends AbstractType
}
public function configureOptions(OptionsResolver $resolver)
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Compose::class,

View file

@ -14,7 +14,7 @@ use Craue\FormFlowBundle\Form\FormFlow;
class ConnectMastodonAccountFlow extends FormFlow
{
protected function loadStepsConfig()
protected function loadStepsConfig(): array
{
return [
[

View file

@ -17,7 +17,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
class ConnectMastodonAccountType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
public function buildForm(FormBuilderInterface $builder, array $options): void
{
switch ($options['flow_step']) {
case 1:
@ -38,12 +38,12 @@ class ConnectMastodonAccountType extends AbstractType
}
}
public function getBlockPrefix()
public function getBlockPrefix(): string
{
return 'addMastodonAccount';
}
public function configureOptions(OptionsResolver $resolver)
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'validation_groups' => ['registration'],

View file

@ -7,36 +7,42 @@
namespace App\Form;
use App\SocialEntity\Instance;
use App\SocialEntity\PollOption;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
class PollOptionType extends AbstractType
{
private $securityContext;
private Security $securityContext;
private Instance $instance;
public function __construct(Security $securityContext)
public function __construct(Security $securityContext, RequestStack $requestStack)
{
$this->securityContext = $securityContext;
$this->instance = $requestStack->getSession()->get('instance');
}
public function buildForm(FormBuilderInterface $builder, array $options)
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$max_char = $this->instance->getConfiguration()->getPolls()->getMaxCharactersPerOption();
$builder->add('title', TextType::class,
[
'required' => false,
'attr' => ['class' => 'form-control'],
'attr' => ['class' => 'form-control', 'maxlength' => $max_char],
'label' => 'page.schedule.form.poll_item',
]);
}
public function configureOptions(OptionsResolver $resolver)
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => PollOption::class,

View file

@ -3,52 +3,9 @@
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use function dirname;
class Kernel extends BaseKernel
{
use MicroKernelTrait;
private const CONFIG_EXTS = '.{php,xml,yaml,yml}';
public function registerBundles(): iterable
{
$contents = require $this->getProjectDir() . '/config/bundles.php';
foreach ($contents as $class => $envs) {
if ($envs[$this->environment] ?? $envs['all'] ?? false) {
yield new $class();
}
}
}
public function getProjectDir(): string
{
return dirname(__DIR__);
}
protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader): void
{
$container->addResource(new FileResource($this->getProjectDir() . '/config/bundles.php'));
$container->setParameter('container.dumper.inline_class_loader', true);
$confDir = $this->getProjectDir() . '/config';
$loader->load($confDir . '/{packages}/*' . self::CONFIG_EXTS, 'glob');
$loader->load($confDir . '/{packages}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, 'glob');
$loader->load($confDir . '/{services}' . self::CONFIG_EXTS, 'glob');
$loader->load($confDir . '/{services}_' . $this->environment . self::CONFIG_EXTS, 'glob');
}
protected function configureRoutes(RouteCollectionBuilder $routes): void
{
$confDir = $this->getProjectDir() . '/config';
$routes->import($confDir . '/{routes}/' . $this->environment . '/**/*' . self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir . '/{routes}/*' . self::CONFIG_EXTS, '/', 'glob');
$routes->import($confDir . '/{routes}' . self::CONFIG_EXTS, '/', 'glob');
}
}

View file

@ -1,70 +1,68 @@
<?php
namespace App\SocialEntity;
namespace App\Security;
use App\SocialEntity\Client;
use App\SocialEntity\CustomField;
use App\SocialEntity\Emoji;
use Symfony\Component\Security\Core\User\UserInterface;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
class MastodonAccount
class MastodonAccount implements UserInterface
{
private string $acct;
private string $id;
private $id;
private string $account_id;
private $account_id;
private string $username;
private $username;
private string $display_name;
private $acct;
private bool $locked;
private $display_name;
private \DateTime $created_at;
private $locked;
private int $followers_count;
private $created_at;
private int $following_count;
private $followers_count;
private int $statuses_count;
private $following_count;
private string $note;
private $statuses_count;
private string $url;
private $note;
private string $avatar;
private $url;
private string $avatar_static;
private $avatar;
private string $header;
private $avatar_static;
private string $header_static;
private $header;
private MastodonAccount $moved;
private $header_static;
private bool $bot;
private $moved;
private string $instance;
private $bot;
private Client $client;
private $instance;
private string $token;
private $client;
private array $Fields;
/** @var Emoji[] */
private array $Emojis;
private $token;
private string $default_sensitivity;
private $Fields;
private $Emojis;
private $default_sensitivity;
private $default_visibility;
private string $default_visibility;
public function __construct()
{
$this->Fields = new ArrayCollection();
$this->Emojis = new ArrayCollection();
$this->Fields = array();
$this->Emojis = array();
}
@ -121,12 +119,12 @@ class MastodonAccount
return $this;
}
public function getCreatedAt(): ?DateTimeInterface
public function getCreatedAt(): ?\DateTime
{
return $this->created_at;
}
public function setCreatedAt(DateTimeInterface $created_at): self
public function setCreatedAt(\DateTime $created_at): self
{
$this->created_at = $created_at;
@ -324,59 +322,52 @@ class MastodonAccount
return $this;
}
/**
* @return Collection|CustomField[]
*/
public function getFields(): Collection
public function getFields(): array
{
return $this->Fields;
}
public function addField(CustomField $field): self
{
if (!$this->Fields->contains($field)) {
if (in_array($field, $this->Fields) === false) {
$this->Fields[] = $field;
$field->setMastodonAccount($this);
}
return $this;
}
public function removeField(CustomField $field): self
{
if ($this->Fields->contains($field)) {
$this->Fields->removeElement($field);
if (($key = array_search($field, $this->Fields)) !== false) {
unset($this->Fields[$key]);
// set the owning side to null (unless already changed)
if ($field->getMastodonAccount() === $this) {
$field->setMastodonAccount(null);
}
}
return $this;
}
/**
* @return Collection|Emoji[]
*/
public function getEmojis(): Collection
public function getEmojis(): array
{
return $this->Emojis;
}
public function addEmoji(Emoji $emoji): self
{
if (!$this->Emojis->contains($emoji)) {
if (in_array($emoji, $this->Emojis) === false) {
$this->Emojis[] = $emoji;
$emoji->setMastodonAccount($this);
}
return $this;
}
public function removeEmoji(Emoji $emoji): self
{
if ($this->Emojis->contains($emoji)) {
$this->Emojis->removeElement($emoji);
if (($key = array_search($emoji, $this->Emojis)) !== false) {
unset($this->Emojis[$key]);
// set the owning side to null (unless already changed)
if ($emoji->getMastodonAccount() === $this) {
$emoji->setMastodonAccount(null);
@ -390,7 +381,7 @@ class MastodonAccount
/**
* @return mixed
*/
public function getDefaultSensitivity()
public function getDefaultSensitivity(): mixed
{
return $this->default_sensitivity;
}
@ -418,6 +409,52 @@ class MastodonAccount
{
$this->default_visibility = $default_visibility;
}
/**
* @var list<string> The user roles
*/
private $roles = [];
/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->acct;
}
/**
* @see UserInterface
*
* @return list<string>
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
/**
* @param list<string> $roles
*/
public function setRoles(array $roles): static
{
$this->roles = $roles;
return $this;
}
/**
* @see UserInterface
*/
public function eraseCredentials(): void
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace App\Security;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UserNotFoundException;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class UserProvider implements UserProviderInterface, PasswordUpgraderInterface
{
/**
* Symfony calls this method if you use features like switch_user
* or remember_me.
*
* If you're not using these features, you do not need to implement
* this method.
*
* @throws UserNotFoundException if the user is not found
*/
public function loadUserByIdentifier($identifier): UserInterface
{
// Load a User object from your data source or throw UserNotFoundException.
// The $identifier argument may not actually be a username:
// it is whatever value is being returned by the getUserIdentifier()
// method in your User class.
throw new \Exception('TODO: fill in loadUserByIdentifier() inside '.__FILE__);
}
/**
* @deprecated since Symfony 5.3, loadUserByIdentifier() is used instead
*/
public function loadUserByUsername($username): UserInterface
{
return $this->loadUserByIdentifier($username);
}
/**
* Refreshes the user after being reloaded from the session.
*
* When a user is logged in, at the beginning of each request, the
* User object is loaded from the session and then this method is
* called. Your job is to make sure the user's data is still fresh by,
* for example, re-querying for fresh User data.
*
* If your firewall is "stateless: true" (for a pure API), this
* method is not called.
*/
public function refreshUser(UserInterface $user): UserInterface
{
if (!$user instanceof MastodonAccount) {
throw new UnsupportedUserException(sprintf('Invalid user class "%s".', $user::class));
}
// Return a User object after making sure its data is "fresh".
// Or throw a UsernameNotFoundException if the user no longer exists.
return $user;
}
/**
* Tells Symfony to use this provider for this User class.
*/
public function supportsClass(string $class): bool
{
return MastodonAccount::class === $class || is_subclass_of($class, MastodonAccount::class);
}
/**
* Upgrades the hashed password of a user, typically for using a better hash algorithm.
*/
public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void
{
// TODO: when hashed passwords are in use, this method should:
// 1. persist the new password in the user storage
// 2. update the $user object with $user->setPassword($newHashedPassword);
}
}

View file

@ -286,52 +286,21 @@ class Curl
*
* @param string $url The url to make the post request
* @param array $data Post data to pass to the url
* @param bool $payload
* @return self
*/
public function post($url, $data = array(), $payload = false)
public function post($url, $data = array())
{
if (!empty($data)) {
if ($payload === false) {
// Check if the url has not already been modified
$url .= strpos($url, '?') !== false ? '&' : '?';
$url .= http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$fields_string = http_build_query($data);
$payload = json_encode( $data );
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_RETURNTRANSFER, true);
$this->setOpt(CURLOPT_POST, true);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
$this->setOpt(CURLOPT_POSTFIELDS, $payload);
$this->exec();
return $this;
}
/**
* @param array|object|string $data
*/
protected function preparePayload($data)
{
$this->setOpt(CURLOPT_POST, true);
if (is_array($data) || is_object($data)) {
$skip = false;
foreach ($data as $key => $value) {
// If a value is an instance of CurlFile skip the http_build_query
// see issue https://github.com/php-mod/curl/issues/46
// suggestion from: https://stackoverflow.com/a/36603038/4611030
if ($value instanceof CurlFile) {
$skip = true;
}
}
if (!$skip) {
$data = http_build_query($data);
}
}
$this->setOpt(CURLOPT_POSTFIELDS, $data);
}
/**
* Make a put request with optional data.
@ -340,21 +309,16 @@ class Curl
*
* @param string $url The url to make the put request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function put($url, $data = array(), $payload = false)
public function put($url, $data = array())
{
if (!empty($data)) {
if ($payload === false) {
$url .= '?' . http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$fields_string = http_build_query($data);
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_POST, 1);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
$this->setOpt(CURLOPT_POSTFIELDS, $fields_string);
$this->exec();
return $this;
}
@ -366,21 +330,16 @@ class Curl
*
* @param string $url The url to make the patch request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function patch($url, $data = array(), $payload = false)
public function patch($url, $data = array())
{
if (!empty($data)) {
if ($payload === false) {
$url .= '?' . http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$fields_string = http_build_query($data);
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_POST, 1);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
$this->setOpt(CURLOPT_POSTFIELDS, $fields_string);
$this->exec();
return $this;
}
@ -392,21 +351,16 @@ class Curl
*
* @param string $url The url to make the delete request
* @param array $data Optional data to pass to the $url
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
* @return self
*/
public function delete($url, $data = array(), $payload = false)
public function delete($url, $data = array())
{
if (!empty($data)) {
if ($payload === false) {
$url .= '?' . http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$fields_string = http_build_query($data);
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_POST, 1);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
$this->setOpt(CURLOPT_POSTFIELDS, $fields_string);
$this->exec();
return $this;
}

View file

@ -11,17 +11,22 @@
namespace App\Services;
use App\Security\MastodonAccount;
use App\Services\Curl as Curl;
use App\SocialEntity\Application;
use App\SocialEntity\Attachment;
use App\SocialEntity\Configuration;
use App\SocialEntity\CustomField;
use App\SocialEntity\Emoji;
use App\SocialEntity\MastodonAccount;
use App\SocialEntity\Instance;
use App\SocialEntity\MediaAttachments;
use App\SocialEntity\Mention;
use App\SocialEntity\Notification;
use App\SocialEntity\Poll;
use App\SocialEntity\PollOption;
use App\SocialEntity\Polls;
use App\SocialEntity\Status;
use App\SocialEntity\Statuses;
use App\SocialEntity\Tag;
use CURLFile;
use DateTime;
@ -66,7 +71,7 @@ class Mastodon_api
*
* @param string $path
*/
public function set_url($path)
public function set_url(string $path): void
{
$this->mastodon_url = $path;
}
@ -77,7 +82,7 @@ class Mastodon_api
* @param string $id
* @param string $secret
*/
public function set_client($id, $secret)
public function set_client(string $id, string $secret): void
{
$this->client_id = $id;
$this->client_secret = $secret;
@ -89,7 +94,7 @@ class Mastodon_api
* @param string $token
* @param string $type
*/
public function set_token($token, $type)
public function set_token(string $token, string $type): void
{
$this->token['access_token'] = $token;
$this->token['token_type'] = $type;
@ -100,7 +105,7 @@ class Mastodon_api
*
* @param array $scopes read / write / follow
*/
public function set_scopes($scopes)
public function set_scopes(array $scopes): void
{
$this->scopes = $scopes;
}
@ -119,7 +124,7 @@ class Mastodon_api
* string $response['client_id']
* string $response['client_secret']
*/
public function create_app($client_name, $scopes = array(), $redirect_uris = '', $website = '')
public function create_app(string $client_name, array $scopes = array(), string $redirect_uris = '', string $website = ''): array
{
$parameters = array();
@ -162,7 +167,7 @@ class Mastodon_api
*
* @return array $response
*/
private function _post($url, $parameters = array())
private function _post(string $url, array $parameters = array()): array
{
$params["method"] = "POST";
@ -185,70 +190,32 @@ class Mastodon_api
*
* @return array $data
*/
public function get_content_remote($url, $parameters = array())
public function get_content_remote(string $url, array $parameters = array()): array
{
$data = array();
// set USERAGENT
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$parameters['headers']['User-Agent'] = $_SERVER['HTTP_USER_AGENT'];
} else {
// default IE11
$parameters['headers']['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko';
}
$parameters['headers']['User-Agent'] = 'Mozilla/5.0 (X11; Linux i686; rv:126.0) Gecko/20100101 Firefox/126.0';
$curl = new Curl();
$response = null;
if (isset($parameters["method"]) && $parameters['method'] == "POST") {
$parameters['headers']['content-type'] = 'application/json';
}
foreach ($parameters['headers'] as $key => $value) {
$curl->setHeader($key, $value);
}
//Special treatment for sending media when editing profile
if (isset($parameters['body']['media'])) {
$fields = [$parameters['body']['media']['name'] => new CURLFile($parameters['body']['media']['path'], $parameters['body']['media']['mimetype'], $parameters['body']['media']['filename'])];
$curl->setOpt(CURLOPT_POSTFIELDS, $fields);
}
// Since Curl does not let us upload media_ids as we wish, we had to pass it through the url, directly.
if (isset($parameters['body']['media_ids'])) {
$url .= strpos($url, '?') !== false ? '' : '?';
foreach ($parameters['body']['media_ids'] as $key => $value) {
$url .= 'media_ids[]=' . (int)$value . '&';
}
$url = substr($url, 0, -1);
unset($parameters['body']['media_ids']);
}
if (isset($parameters['body']['poll']['options'])) {
$url .= strpos($url, '?') !== false ? '' : '?';
foreach ($parameters['body']['poll']['options'] as $key => $value) {
$url .= 'poll[options][]=' . urlencode($value) . '&';
}
$url = substr($url, 0, -1);
unset($parameters['body']['poll']['options']);
}
//Special treatment for filtering notifications
if (isset($parameters['body']['exclude_types[]']) && $parameters['method'] == "GET") {
$url .= "?";
foreach ($parameters['body']['exclude_types[]'] as $key => $value) {
$url .= "exclude_types[]=" . $value . "&";
}
if (isset($parameters['body']['max_id']))
$url .= "&max_id=" . $parameters['body']['max_id'];
$parameters['body'] = [];
}
if (isset($parameters["method"]) && $parameters['method'] == "POST")
if (isset($parameters["method"]) && $parameters['method'] == "POST") {
$response = $curl->post($url, $parameters['body']);
else if (isset($parameters["method"]) && $parameters['method'] == "GET")
}else if (isset($parameters["method"]) && $parameters['method'] == "GET") {
$response = $curl->get($url, $parameters['body']);
else if (isset($parameters["method"]) && $parameters['method'] == "PUT")
}else if (isset($parameters["method"]) && $parameters['method'] == "PUT") {
$response = $curl->put($url, $parameters['body']);
else if (isset($parameters["method"]) && $parameters['method'] == "PATCH")
}else if (isset($parameters["method"]) && $parameters['method'] == "PATCH") {
$response = $curl->patch($url, $parameters['body']);
else if (isset($parameters["method"]) && $parameters['method'] == "DELETE")
}else if (isset($parameters["method"]) && $parameters['method'] == "DELETE") {
$response = $curl->delete($url);
}
$min_id = null;
$max_id = null;
@ -283,7 +250,7 @@ class Mastodon_api
$data['error'] = $response->error;
$data['error_code'] = $response->error_code;
$data['error_message'] = $response->error_message;
if ($response->response)
if ($response->response && isset(json_decode($response->response, true)['error']))
$data['error_message'] = json_decode($response->response, true)['error'];
} else {
$data['response_headers'] = $response->response_headers;
@ -304,7 +271,7 @@ class Mastodon_api
* string $response['scope'] read
* int $response['created_at'] time
*/
public function login($id, $password)
public function login(string $id, string $password): array
{
$parameters = array();
$parameters['client_id'] = $this->client_id;
@ -340,7 +307,7 @@ class Mastodon_api
* string $response['scope'] read
* int $response['created_at'] time
*/
public function loginAuthorization($code, $redirect_uri = '')
public function loginAuthorization(string $code, string $redirect_uri = ''): array
{
$parameters = array();
$parameters['client_id'] = $this->client_id;
@ -367,7 +334,7 @@ class Mastodon_api
*
* @return string $response Authorization code
*/
public function getAuthorizationUrl($redirect_uri = '')
public function getAuthorizationUrl(string $redirect_uri = ''): string
{
if (empty($redirect_uri))
$redirect_uri = 'urn:ietf:wg:oauth:2.0:oob';
@ -389,7 +356,7 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
* int $response['id']
@ -408,7 +375,7 @@ class Mastodon_api
* string $response['header'] A base64 encoded image to display as the user's header image
* string $response['header_static']
*/
public function accounts($id)
public function accounts(string $id): array
{
return $this->_get('/api/v1/accounts/' . $id);
}
@ -421,7 +388,7 @@ class Mastodon_api
*
* @return array $response
*/
private function _get($url, $parameters = array())
private function _get(string $url, array $parameters = array()): array
{
$params["method"] = "GET";
@ -433,7 +400,6 @@ class Mastodon_api
}
$params['body'] = $parameters;
$url = $this->mastodon_url . $url;
return $this->get_content_remote($url, $params);
}
@ -444,7 +410,7 @@ class Mastodon_api
*
* @return array $response
*/
public function accounts_verify_credentials()
public function accounts_verify_credentials(): array
{
return $this->_get('/api/v1/accounts/verify_credentials');
}
@ -462,7 +428,7 @@ class Mastodon_api
*
* @return array $response
*/
public function accounts_update_credentials($parameters)
public function accounts_update_credentials($parameters): array
{
return $this->_patch('/api/v1/accounts/update_credentials', $parameters);
}
@ -475,7 +441,7 @@ class Mastodon_api
*
* @return array $parameters
*/
private function _patch($url, $parameters = array())
private function _patch(string $url, array $parameters = array()): array
{
$params["method"] = "PATCH";
@ -497,11 +463,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_followers($id)
public function accounts_followers(string $id): array
{
return $this->_get('/api/v1/accounts/' . $id . '/followers');
}
@ -511,11 +477,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_following($id)
public function accounts_following(string $id): array
{
return $this->_get('/api/v1/accounts/' . $id . '/following');
}
@ -525,11 +491,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_statuses($id)
public function accounts_statuses(string $id): array
{
return $this->_get('/api/v1/accounts/' . $id . '/statuses');
}
@ -539,11 +505,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_own_statuses($id)
public function accounts_own_statuses(string $id): array
{
$response = $this->_get('/api/v1/accounts/' . $id . '/statuses?exclude_replies=1');
$result = [];
@ -563,11 +529,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_follow($id)
public function accounts_follow(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/follow');
}
@ -577,11 +543,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_unfollow($id)
public function accounts_unfollow(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/unfollow');
}
@ -591,11 +557,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_block($id)
public function accounts_block(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/block');
}
@ -605,11 +571,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_unblock($id)
public function accounts_unblock(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/unblock');
}
@ -619,11 +585,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_mute($id)
public function accounts_mute(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/mute');
}
@ -633,11 +599,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function accounts_unmute($id)
public function accounts_unmute(string $id): array
{
return $this->_post('/api/v1/accounts/' . $id . '/unmute');
}
@ -658,7 +624,7 @@ class Mastodon_api
* bool $response['muting']
* bool $response['requested']
*/
public function accounts_relationships($parameters)
public function accounts_relationships(array $parameters): array
{
return $this->_get('/api/v1/accounts/relationships', $parameters);
}
@ -672,7 +638,7 @@ class Mastodon_api
*
* @return array $response
*/
public function accounts_search($parameters)
public function accounts_search(array $parameters): array
{
return $this->_get('/api/v1/accounts/search', $parameters);
}
@ -682,7 +648,7 @@ class Mastodon_api
*
* @return array $response
*/
public function blocks()
public function blocks(): array
{
return $this->_get('/api/v1/blocks');
}
@ -692,7 +658,7 @@ class Mastodon_api
*
* @return array $response
*/
public function favourites()
public function favourites(): array
{
return $this->_get('/api/v1/favourites');
}
@ -702,7 +668,7 @@ class Mastodon_api
*
* @return array $response
*/
public function follow_requests()
public function follow_requests(): array
{
return $this->_get('/api/v1/follow_requests');
}
@ -712,11 +678,11 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function follow_requests_authorize($id)
public function follow_requests_authorize(string $id): array
{
return $this->_post('/api/v1/follow_requests/authorize', array('id' => $id));
}
@ -726,10 +692,10 @@ class Mastodon_api
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
* @param string $id
* @return array $response
*/
public function follow_requests_reject($id)
public function follow_requests_reject(string $id): array
{
return $this->_post('/api/v1/follow_requests/reject', array('id' => $id));
}
@ -742,27 +708,11 @@ class Mastodon_api
* @param string $uri username@domain of the person you want to follow
* @return array $response
*/
public function follows($uri)
public function follows($uri): array
{
return $this->_post('/api/v1/follows', array('uri' => $uri));
}
/**
* instance
*
* Getting instance information
*
* @return array $response
* string $response['uri']
* string $response['title']
* string $response['description']
* string $response['email']
*/
public function instance()
{
return $this->_get('/api/v1/instance');
}
/**
* mutes
*
@ -770,7 +720,7 @@ class Mastodon_api
*
* @return array $response
*/
public function mutes()
public function mutes(): array
{
return $this->_get('/api/v1/mutes');
}
@ -783,7 +733,7 @@ class Mastodon_api
*
* @return array $response
*/
public function notifications($parameters)
public function notifications($parameters): array
{
$url = '/api/v1/notifications';
@ -797,7 +747,7 @@ class Mastodon_api
*
* @return array $response
*/
public function notifications_clear()
public function notifications_clear(): array
{
return $this->_post('/api/v1/notifications/clear');
}
@ -809,7 +759,7 @@ class Mastodon_api
*
* @return array $response
*/
public function get_reports()
public function get_reports(): array
{
return $this->_get('/api/v1/reports');
}
@ -826,7 +776,7 @@ class Mastodon_api
*
* @return array $response
*/
public function post_reports($parameters)
public function post_reports(array $parameters): array
{
return $this->_post('/api/v1/reports', $parameters);
}
@ -842,7 +792,7 @@ class Mastodon_api
*
* @return array $response
*/
public function search($parameters)
public function search(array $parameters): array
{
return $this->_get('/api/v1/search', $parameters);
}
@ -852,11 +802,11 @@ class Mastodon_api
*
* Fetching a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses($id)
public function statuses(string $id): array
{
return $this->_get('/api/v1/statuses/' . $id);
}
@ -866,11 +816,11 @@ class Mastodon_api
*
* Getting status context
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_context($id)
public function statuses_context(string $id): array
{
return $this->_get('/api/v1/statuses/' . $id . '/context');
}
@ -880,11 +830,11 @@ class Mastodon_api
*
* Getting a card associated with a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_card($id)
public function statuses_card(string $id): array
{
return $this->_get('/api/v1/statuses/' . $id . '/card');
}
@ -894,11 +844,11 @@ class Mastodon_api
*
* Getting who reblogged a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_reblogged_by($id)
public function statuses_reblogged_by(string $id): array
{
return $this->_get('/api/v1/statuses/' . $id . '/reblogged_by');
}
@ -908,11 +858,11 @@ class Mastodon_api
*
* Getting who favourited a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_favourited_by($id)
public function statuses_favourited_by(string $id): array
{
return $this->_get('/api/v1/statuses/' . $id . '/favourited_by');
}
@ -927,7 +877,7 @@ class Mastodon_api
*
* @return array $response
*/
public function post_media($parameters)
public function post_media(array $parameters): array
{
return $this->_post('/api/v1/media', $parameters);
}
@ -943,7 +893,7 @@ class Mastodon_api
* @param $parameters
* @return array $response
*/
public function update_media($id, $parameters)
public function update_media(string $id, array $parameters): array
{
return $this->_put('/api/v1/media/' . $id, $parameters);
}
@ -958,7 +908,7 @@ class Mastodon_api
*
* @return array $response
*/
private function _put($url, $parameters = array())
private function _put(string $url, array $parameters = array()): array
{
$params["method"] = "PUT";
@ -986,7 +936,7 @@ class Mastodon_api
*
* @return array $response
*/
public function post_statuses($parameters)
public function post_statuses(array $parameters): array
{
return $this->_post('/api/v1/statuses', $parameters);
}
@ -996,11 +946,11 @@ class Mastodon_api
*
* Deleting a status
*
* @param int $id
* @param string $id
*
* @return array $response empty
*/
public function delete_statuses($id)
public function delete_statuses(string $id): array
{
return $this->_delete('/api/v1/statuses/' . $id);
}
@ -1014,7 +964,7 @@ class Mastodon_api
*
*/
private function _delete($url)
private function _delete(string $url): array
{
$parameters = array();
$parameters["method"] = "DELETE";
@ -1034,11 +984,11 @@ class Mastodon_api
*
* Deleting a scheduled status
*
* @param int $id
* @param string $id
*
* @return array $response empty
*/
public function delete_scheduled($id)
public function delete_scheduled(string $id): array
{
return $this->_delete('/api/v1/scheduled_statuses/' . $id);
}
@ -1048,11 +998,11 @@ class Mastodon_api
*
* Reblogging a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_reblog($id)
public function statuses_reblog(string $id): array
{
return $this->_post('/api/v1/statuses/' . $id . '/reblog');
}
@ -1062,11 +1012,11 @@ class Mastodon_api
*
* Unreblogging a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_unreblog($id)
public function statuses_unreblog(string $id): array
{
return $this->_post('/api/v1/statuses/' . $id . '/unreblog');
}
@ -1076,11 +1026,11 @@ class Mastodon_api
*
* Favouriting a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_favourite($id)
public function statuses_favourite(string $id): array
{
return $this->_post('/api/v1/statuses/' . $id . '/favourite');
}
@ -1090,16 +1040,28 @@ class Mastodon_api
*
* Unfavouriting a status
*
* @param int $id
* @param string $id
*
* @return array $response
*/
public function statuses_unfavourite($id)
public function statuses_unfavourite(string $id): array
{
return $this->_post('/api/v1/statuses/' . $id . '/unfavourite');
}
/**
* scheduled_statuses
*
*
* @return array $response
*/
public function get_instance(): array
{
return $this->_get('/api/v1/instance');
}
/**
* scheduled_statuses
*
@ -1107,7 +1069,7 @@ class Mastodon_api
*
* @return array $response
*/
public function get_scheduled($parameters = array())
public function get_scheduled($parameters = array()): array
{
return $this->_get('/api/v1/scheduled_statuses/', $parameters);
}
@ -1117,7 +1079,7 @@ class Mastodon_api
*
* @return array $response
*/
public function timelines_home()
public function timelines_home(): array
{
return $this->_get('/api/v1/timelines/home');
}
@ -1130,7 +1092,7 @@ class Mastodon_api
*
* @return array $response
*/
public function timelines_public($parameters = array())
public function timelines_public(array $parameters = array()): array
{
return $this->_get('/api/v1/timelines/public', $parameters);
}
@ -1144,7 +1106,7 @@ class Mastodon_api
*
* @return array $response
*/
public function timelines_tag($hashtag, $parameters = array())
public function timelines_tag(string $hashtag, array $parameters = array()): array
{
return $this->_get('/api/v1/timelines/tag/' . $hashtag, $parameters);
}
@ -1160,13 +1122,12 @@ class Mastodon_api
* @param $host
* @return string|null
*/
public function getInstanceNodeInfo($host)
public function getInstanceNodeInfo(string $host): ?string
{
$curl = new Curl();
$url = "https://" . $host . "/.well-known/nodeinfo";
$reply = $curl->get($url);
$responseArray = json_decode($reply->response, true);
if (empty($responseArray)) {
$curl = new Curl();
@ -1193,7 +1154,7 @@ class Mastodon_api
* @param $accountParams array
* @return MastodonAccount
*/
public function updateAccount(MastodonAccount $MastodonAccount, $accountParams)
public function updateAccount(MastodonAccount $MastodonAccount, array $accountParams): MastodonAccount
{
$MastodonAccount->setUsername($accountParams['username']);
@ -1242,21 +1203,21 @@ class Mastodon_api
return $MastodonAccount;
}
public function stringToDate($string_date)
public function stringToDate(?string $string_date): DateTime
{
try {
return new DateTime($string_date);
} catch (Exception $e) {
}
return "";
return new DateTime();
}
/**
* getNotifications Hydrate an array of Notification from API reply
* @param $notificationParams
* @param $notificationParams array
* @return array
*/
public function getNotifications($notificationParams)
public function getNotifications(array $notificationParams): array
{
$notifications = [];
foreach ($notificationParams as $notificationParam)
@ -1269,7 +1230,7 @@ class Mastodon_api
* @param $notificationParams
* @return Notification
*/
public function getSingleNotification($notificationParams)
public function getSingleNotification($notificationParams): Notification
{
$notification = new Notification();
$notification->setId($notificationParams['id']);
@ -1281,12 +1242,62 @@ class Mastodon_api
return $notification;
}
/**
* get instance configuration from API reply
* @param $instantParams array
* @return Instance
*/
public function getInstanceConfiguration(array $instantParams): Instance
{
$Instance = new Instance();
$Configuration = new Configuration();
$Statuses = new Statuses();
$MediaAttachments = new MediaAttachments();
$Polls = new Polls();
if(isset($instantParams['configuration'])) {
//Dealing with statuses configuration
if(isset($instantParams['configuration']['statuses'])) {
$Statuses->setMaxCharacters($instantParams['configuration']['statuses']['max_characters']);
$Statuses->setMaxMediaAttachments($instantParams['configuration']['statuses']['max_media_attachments']);
$Statuses->setCharactersReservedPerUrl($instantParams['configuration']['statuses']['characters_reserved_per_url']);
}
if(isset($instantParams['configuration']['media_attachments'])) {
$MediaAttachments->setSupportedMimeTypes($instantParams['configuration']['media_attachments']['supported_mime_types']);
$MediaAttachments->setImageSizeLimit($instantParams['configuration']['media_attachments']['image_size_limit']);
$MediaAttachments->setImageMatrixLimit($instantParams['configuration']['media_attachments']['image_matrix_limit']);
$MediaAttachments->setVideoSizeLimit($instantParams['configuration']['media_attachments']['video_size_limit']);
$MediaAttachments->setVideoFrameRateLimit($instantParams['configuration']['media_attachments']['video_frame_rate_limit']);
$MediaAttachments->setVideoMatrixLimit($instantParams['configuration']['media_attachments']['video_matrix_limit']);
}
if(isset($instantParams['configuration']['polls'])) {
$Polls->setMaxOptions($instantParams['configuration']['polls']['max_options']);
$Polls->setMaxCharactersPerOption($instantParams['configuration']['polls']['max_characters_per_option']);
$Polls->setMinExpiration($instantParams['configuration']['polls']['min_expiration']);
$Polls->setMaxExpiration($instantParams['configuration']['polls']['max_expiration']);
}
} else if (isset($instantParams['pleroma'])) {
if (isset($instantParams['poll_limits'])) {
$Polls->setMaxOptions($instantParams['poll_limits']['max_options']);
$Polls->setMaxCharactersPerOption($instantParams['poll_limits']['max_option_chars']);
$Polls->setMinExpiration($instantParams['poll_limits']['min_expiration']);
$Polls->setMaxExpiration($instantParams['poll_limits']['max_expiration']);
}
if(isset($instantParams['max_toot_chars'])) {
$Statuses->setMaxCharacters($instantParams['max_toot_chars']);
}
}
$Configuration->setStatuses($Statuses);
$Configuration->setMediaAttachments($MediaAttachments);
$Configuration->setPolls($Polls);
$Instance->setConfiguration($Configuration);
return $Instance;
}
/**
* getSingleAccount Hydrate a MastodonAccount from API reply
* @param $accountParams
* @param $accountParams array
* @return MastodonAccount
*/
public function getSingleAccount($accountParams)
public function getSingleAccount(array $accountParams): MastodonAccount
{
$MastodonAccount = new MastodonAccount();
@ -1343,10 +1354,10 @@ class Mastodon_api
/**
* getSingleStatus Hydrate a Status from API reply
* @param $statusParams
* @param $statusParams array
* @return Status
*/
public function getSingleStatus($statusParams)
public function getSingleStatus(array $statusParams): Status
{
$status = new Status();
@ -1477,7 +1488,7 @@ class Mastodon_api
* @param $statusParams
* @return array
*/
public function getStatuses($statusParams)
public function getStatuses($statusParams): array
{
$statuses = [];
foreach ($statusParams as $statusParam)
@ -1487,11 +1498,11 @@ class Mastodon_api
/**
* getScheduledStatuses Hydrate an array of Scheduled Status from API reply
* @param $statusParams
* @param $account
* @param $statusParams array
* @param $account MastodonAccount
* @return array
*/
public function getScheduledStatuses($statusParams, $account)
public function getScheduledStatuses(array $statusParams, MastodonAccount $account): array
{
$statuses = [];
foreach ($statusParams as $statusParam)
@ -1502,11 +1513,11 @@ class Mastodon_api
/**
* getSingleScheduledStatus Hydrate a scheduled Status from API reply
* @param $statusParams
* @param $account
* @param $statusParams array
* @param $account MastodonAccount
* @return Status
*/
public function getSingleScheduledStatus($statusParams, $account)
public function getSingleScheduledStatus(array $statusParams, MastodonAccount $account): Status
{
$status = new Status();
@ -1545,6 +1556,8 @@ class Mastodon_api
$attachment->setMeta(serialize($_m['meta']));
if ($_m['description'])
$attachment->setDescription($_m['description']);
else
$attachment->setDescription("");
$media_attachments[] = $attachment;
}
$status->setMediaAttachments($media_attachments);
@ -1578,10 +1591,10 @@ class Mastodon_api
/**
* getSingleAttachment Hydrate an Attachment from API reply
* @param $mediaParams
* @param $mediaParams array
* @return Attachment
*/
public function getSingleAttachment($mediaParams)
public function getSingleAttachment(array $mediaParams): Attachment
{
$attachment = new Attachment();

View file

@ -6,10 +6,8 @@ namespace App\SocialEntity;
class Application
{
/** @var string */
private $name;
/** @var string */
private $website;
private string $name;
private string $website;
/**
* @return string

View file

@ -5,22 +5,15 @@ namespace App\SocialEntity;
class Attachment
{
/** @var string */
private $id;
/** @var string */
private $type;
/** @var string */
private $url;
/** @var string */
private $remote_url;
/** @var string */
private $preview_url;
/** @var string */
private $text_url;
/** @var string */
private $meta;
/** @var string */
private $description;
private string $id;
private string $type;
private string $url;
private string $remote_url;
private string $preview_url;
private string $text_url;
private string $meta;
private string $description;
/**
* @return string

View file

@ -5,30 +5,18 @@ namespace App\SocialEntity;
class Card
{
/** @var string */
private $url;
/** @var string */
private $title;
/** @var string */
private $description;
/** @var string */
private $image;
/** @var string */
private $type;
/** @var string */
private $author_name;
/** @var string */
private $author_url;
/** @var string */
private $provider_name;
/** @var string */
private $provider_url;
/** @var string */
private $html;
/** @var int */
private $width;
/** @var int */
private $height;
private string $url;
private string $title;
private string $description;
private string $image;
private string $type;
private string $author_name;
private string $author_url;
private string $provider_name;
private string $provider_url;
private string $html;
private int $width;
private int $height;
/**
* @return string
@ -222,5 +210,4 @@ class Card
$this->height = $height;
}
}

View file

@ -3,20 +3,17 @@
namespace App\SocialEntity;
use App\Security\MastodonAccount;
class Client
{
private $id;
private $host;
private $client_id;
private $client_secret;
private $account;
private $code;
private string $id;
private string $host;
private string $client_id;
private string $client_secret;
private MastodonAccount $account;
private string $code;
public function getId(): ?int
{

View file

@ -3,55 +3,52 @@
namespace App\SocialEntity;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
class Compose
{
private $id;
private string $id;
private ?string $content_warning = null;
private ?string $content = null;
private $content_warning;
private string $visibility;
private DateTime $created_at;
private DateTime $scheduled_at;
private DateTime $sent_at;
private bool $sensitive;
private ?string $in_reply_to_id = null;
private $content;
private string $timeZone;
/** @var PollOption[] */
private ?array $poll_options = null;
private ?int $poll_expires_at = null;
private ?bool $poll_multiple = null;
private $visibility;
public function getAttachPoll(): ?bool
{
return $this->attach_poll;
}
private $created_at;
private $scheduled_at;
private $sent_at;
private $sensitive;
private $in_reply_to_id;
private $timeZone;
private $poll_options;
/** @var int */
private $poll_expires_at;
/** @var bool */
private $poll_multiple;
public function setAttachPoll(?bool $attach_poll): void
{
$this->attach_poll = $attach_poll;
}
private ?bool $attach_poll = null;
public function __construct()
{
$this->poll_options = new ArrayCollection();
$this->poll_options = array();
}
/**
* @return mixed
*/
public function getTimeZone()
public function getTimeZone(): string
{
return $this->timeZone;
}
/**
* @param mixed $timeZone
*/
public function setTimeZone($timeZone): void
{
$this->timeZone = $timeZone;
@ -63,7 +60,7 @@ class Compose
public function getSent()
{
return ($this->sent_at != null);
return ($this->sent_at != null && !empty($this->sent_at));
}
public function getId(): ?int
@ -108,29 +105,23 @@ class Compose
}
/**
* @return boolean
*/
public function getSensitive()
public function getSensitive(): bool
{
return $this->sensitive;
}
/**
* @param mixed $sensitive
*/
public function setSensitive(bool $sensitive): void
{
$this->sensitive = $sensitive;
}
public function getCreatedAt(): ?DateTimeInterface
public function getCreatedAt(): ?DateTime
{
return $this->created_at;
}
public function setCreatedAt(DateTimeInterface $created_at): self
public function setCreatedAt(DateTime $created_at): self
{
$this->created_at = $created_at;
@ -161,41 +152,29 @@ class Compose
return $this;
}
/**
* @return ArrayCollection|null
*/
public function getPollOptions(): ?ArrayCollection
public function getPollOptions(): ?array
{
return $this->poll_options;
}
/**
* @param ArrayCollection $poll_options
*/
public function setPollOptions(?ArrayCollection $poll_options): void
public function setPollOptions(?array $poll_options): void
{
$this->poll_options = $poll_options;
}
/**
* @return int
*/
public function getPollExpiresAt(): ?int
{
return $this->poll_expires_at;
}
/**
* @param int $poll_expires_at
*/
public function setPollExpiresAt(?int $poll_expires_at): void
{
$this->poll_expires_at = $poll_expires_at;
}
/**
* @return bool
*/
public function isPollMultiple(): ?bool
{
return $this->poll_multiple;

View file

@ -3,20 +3,21 @@
namespace App\SocialEntity;
use DateTimeInterface;
use App\Security\MastodonAccount;
use DateTime;
class CustomField
{
private $id;
private string $id;
private $name;
private string $name;
private $value;
private string $value;
private $verified_at;
private \DateTime $verified_at;
private $mastodonAccount;
private MastodonAccount $mastodonAccount;
public function __construct()
@ -40,12 +41,12 @@ class CustomField
return $this;
}
public function getVerifiedAt(): ?DateTimeInterface
public function getVerifiedAt(): ?DateTime
{
return $this->verified_at;
}
public function setVerifiedAt(?DateTimeInterface $verified_at): self
public function setVerifiedAt(?DateTime $verified_at): self
{
$this->verified_at = $verified_at;

View file

@ -3,19 +3,21 @@
namespace App\SocialEntity;
use App\Security\MastodonAccount;
class Emoji
{
private $id;
private string $id;
private $shortcode;
private string $shortcode;
private $static_url;
private string $static_url;
private $url;
private string $url;
private $visible_in_picker;
private bool $visible_in_picker;
private $mastodonAccount;
private MastodonAccount $mastodonAccount;
public function __construct()

View file

@ -0,0 +1,225 @@
<?php
namespace App\SocialEntity;
class Statuses {
private int $max_characters = 500;
private int $max_media_attachments = 4;
private int $characters_reserved_per_url = 23;
public function getMaxCharacters(): int
{
return $this->max_characters;
}
public function setMaxCharacters(int $max_characters): void
{
$this->max_characters = $max_characters;
}
public function getMaxMediaAttachments(): int
{
return $this->max_media_attachments;
}
public function setMaxMediaAttachments(int $max_media_attachments): void
{
$this->max_media_attachments = $max_media_attachments;
}
public function getCharactersReservedPerUrl(): int
{
return $this->characters_reserved_per_url;
}
public function setCharactersReservedPerUrl(int $characters_reserved_per_url): void
{
$this->characters_reserved_per_url = $characters_reserved_per_url;
}
}
class MediaAttachments {
private array $supported_mime_types = ["image/jpeg","image/png","image/gif","image/heic","image/heif","image/webp","image/avif","video/webm","video/mp4","video/quicktime","video/ogg","audio/wave","audio/wav","audio/x-wav","audio/x-pn-wave","audio/vnd.wave","audio/ogg","audio/vorbis","audio/mpeg","audio/mp3","audio/webm","audio/flac","audio/aac","audio/m4a","audio/x-m4a","audio/mp4","audio/3gpp","video/x-ms-asf"];
private int $image_size_limit = 16777216;
private int $image_matrix_limit = 33177600;
private int $video_size_limit = 103809024;
private int $video_frame_rate_limit = 120;
private int $video_matrix_limit = 8294400;
public function getSupportedMimeTypes(): array
{
return $this->supported_mime_types;
}
public function setSupportedMimeTypes(array $supported_mime_types): void
{
$this->supported_mime_types = $supported_mime_types;
}
public function getSupportedFiles() : string {
$values = "/(\.|\/)(gif|jpe?g|apng|png|mp4|mp3|avi|mov|webm|wmv|flv|wav|ogg)$/i";
if(isset($this->supported_mime_types) && count($this->supported_mime_types) >0) {
$values = "/(\.|\/)(";
foreach ($this->supported_mime_types as $value) {
$cleanedValue = preg_replace("#(image/)|(video/)|(audio/)#","",$value,);
if(!str_contains($cleanedValue, '.') && !str_contains($cleanedValue, '-')) {
$values .= $cleanedValue.'|';
}
}
$values .= "jpg)$/i";
}
return $values;
}
public function getImageSizeLimit(): int
{
return $this->image_size_limit;
}
public function setImageSizeLimit(int $image_size_limit): void
{
$this->image_size_limit = $image_size_limit;
}
public function getImageMatrixLimit(): int
{
return $this->image_matrix_limit;
}
public function setImageMatrixLimit(int $image_matrix_limit): void
{
$this->image_matrix_limit = $image_matrix_limit;
}
public function getVideoSizeLimit(): int
{
return $this->video_size_limit;
}
public function setVideoSizeLimit(int $video_size_limit): void
{
$this->video_size_limit = $video_size_limit;
}
public function getVideoFrameRateLimit(): int
{
return $this->video_frame_rate_limit;
}
public function setVideoFrameRateLimit(int $video_frame_rate_limit): void
{
$this->video_frame_rate_limit = $video_frame_rate_limit;
}
public function getVideoMatrixLimit(): int
{
return $this->video_matrix_limit;
}
public function setVideoMatrixLimit(int $video_matrix_limit): void
{
$this->video_matrix_limit = $video_matrix_limit;
}
}
class Polls {
private int $max_options = 4;
private int $max_characters_per_option = 50;
private int $min_expiration = 300;
private int $max_expiration = 2629746;
public function getMaxOptions(): int
{
return $this->max_options;
}
public function setMaxOptions(int $max_options): void
{
$this->max_options = $max_options;
}
public function getMaxCharactersPerOption(): int
{
return $this->max_characters_per_option;
}
public function setMaxCharactersPerOption(int $max_characters_per_option): void
{
$this->max_characters_per_option = $max_characters_per_option;
}
public function getMinExpiration(): int
{
return $this->min_expiration;
}
public function setMinExpiration(int $min_expiration): void
{
$this->min_expiration = $min_expiration;
}
public function getMaxExpiration(): int
{
return $this->max_expiration;
}
public function setMaxExpiration(int $max_expiration): void
{
$this->max_expiration = $max_expiration;
}
}
class Configuration {
private Statuses $statuses;
private MediaAttachments $mediaAttachments;
public function getStatuses(): Statuses
{
return $this->statuses;
}
public function setStatuses(Statuses $statuses): void
{
$this->statuses = $statuses;
}
public function getMediaAttachments(): MediaAttachments
{
return $this->mediaAttachments;
}
public function setMediaAttachments(MediaAttachments $mediaAttachments): void
{
$this->mediaAttachments = $mediaAttachments;
}
public function getPolls(): Polls
{
return $this->polls;
}
public function setPolls(Polls $polls): void
{
$this->polls = $polls;
}
private Polls $polls;
}
class Instance
{
private Configuration $configuration;
public function getConfiguration(): Configuration
{
return $this->configuration;
}
public function setConfiguration(Configuration $configuration): void
{
$this->configuration = $configuration;
}
}

View file

@ -4,14 +4,10 @@ namespace App\SocialEntity;
class Mention
{
/** @var string */
private $url;
/** @var string */
private $username;
/** @var string */
private $acct;
/** @var string */
private $id;
private string $url;
private string $username;
private string $acct;
private string $id;
/**

View file

@ -3,20 +3,16 @@
namespace App\SocialEntity;
use App\Security\MastodonAccount;
use DateTime;
class Notification
{
/** @var string */
private $id;
/** @var string */
private $type;
/** @var DateTime */
private $created_at;
/** @var MastodonAccount */
private $account;
/** @var Status */
private $status;
private string $id;
private string $type;
private DateTime $created_at;
private MastodonAccount $account;
private Status $status;
/**

View file

@ -8,26 +8,19 @@ use DateTime;
class Poll
{
/** @var string */
private $id;
/** @var DateTime */
private $expires_at;
/** @var bool */
private $expired;
/** @var bool */
private $multiple;
/** @var int */
private $votes_count;
/** @var int */
private $voters_count;
/** @var bool */
private $voted;
private string $id;
private DateTime $expires_at;
private bool $expired;
private bool $multiple;
private int $votes_count;
private int $voters_count;
private bool $voted;
/** @var int[] */
private $own_votes;
private array $own_votes;
/** @var PollOption[] */
private $options;
private array $options;
/** @var Emoji[] */
private $emojis;
private array $emojis;
/**
* @return string

View file

@ -6,38 +6,27 @@ namespace App\SocialEntity;
class PollOption
{
/** @var string */
private $title;
/** @var int */
private $votes_count;
private ?string $title = null;
private ?int $votes_count = null;
/**
* @return string
*/
public function getTitle(): ?string
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle(?string $title): void
{
$this->title = $title;
}
/**
* @return int
*/
public function getVotesCount(): ?int
{
return $this->votes_count;
}
/**
* @param int $votes_count
*/
public function setVotesCount(?int $votes_count): void
{
$this->votes_count = $votes_count;

View file

@ -3,66 +3,43 @@
namespace App\SocialEntity;
use App\Security\MastodonAccount;
use DateTime;
class Status
{
/** @var string */
private $id;
/** @var string */
private $uri;
/** @var string */
private $url;
/** @var MastodonAccount */
private $account;
/** @var string */
private $in_reply_to_id;
/** @var string */
private $in_reply_to_account_id;
/** @var string */
private $content;
/** @var DateTime */
private $created_at;
/** @var DateTime */
private $scheduled_at;
private string $id;
private string $uri;
private string $url;
private MastodonAccount $account;
private ?string $in_reply_to_id;
private ?string $in_reply_to_account_id;
private ?string $content;
private DateTime $created_at;
private DateTime $scheduled_at;
/** @var Emoji[] */
private $emojis = [];
/** @var int */
private $replies_count;
/** @var int */
private $reblogs_count;
/** @var int */
private $favourites_count;
/** @var boolean */
private $reblogged;
/** @var boolean */
private $favourited;
/** @var boolean */
private $muted;
/** @var boolean */
private $sensitive_;
/** @var string */
private $spoiler_text;
/** @var string */
private $visibility;
private array $emojis = [];
private int $replies_count;
private int $reblogs_count;
private int $favourites_count;
private bool $reblogged;
private bool $favourited;
private bool $muted;
private bool $sensitive_;
private ?string $spoiler_text;
private string $visibility;
/** @var Attachment[] */
private $media_attachments = [];
private array $media_attachments = [];
/** @var Mention[] */
private $mentions = [];
private array $mentions = [];
/** @var Tag[] */
private $tags = [];
/** @var Card */
private $card;
/** @var Application */
private $application;
/** @var string */
private $language;
/** @var boolean */
private $pinned;
/** @var Status */
private $reblog;
/** @var Poll */
private $poll;
private array $tags = [];
private Card $card;
private Application $application;
private string $language;
private bool $pinned;
private Status $reblog;
private Poll $poll;
/**
* @return string
@ -129,15 +106,15 @@ class Status
}
/**
* @return string
* @return string|null
*/
public function getInReplyToId(): string
public function getInReplyToId(): ?string
{
return $this->in_reply_to_id;
}
/**
* @param string $in_reply_to_id
* @param mixed $in_reply_to_id
*/
public function setInReplyToId(?string $in_reply_to_id): void
{
@ -153,7 +130,7 @@ class Status
}
/**
* @param string $in_reply_to_account_id
* @param mixed $in_reply_to_account_id
*/
public function setInReplyToAccountId(?string $in_reply_to_account_id): void
{
@ -161,17 +138,17 @@ class Status
}
/**
* @return string
* @return string|null
*/
public function getContent(): string
public function getContent(): ?string
{
return $this->content;
}
/**
* @param string $content
* @param mixed $content
*/
public function setContent(string $content): void
public function setContent(?string $content): void
{
$this->content = $content;
}
@ -185,7 +162,7 @@ class Status
}
/**
* @param DateTime $created_at
* @param mixed $created_at
*/
public function setCreatedAt(?DateTime $created_at): void
{
@ -346,7 +323,7 @@ class Status
}
/**
* @param string $spoiler_text
* @param mixed $spoiler_text
*/
public function setSpoilerText(?string $spoiler_text): void
{

View file

@ -6,14 +6,10 @@ namespace App\SocialEntity;
class Tag
{
/** @var string */
private $name;
/** @var string */
private $url;
/** @var array */
private $history = [];
/** @var Status */
private $status;
private string $name;
private string $url;
private array $history = [];
private Status $status;
/**
* @return string

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