Compare commits

...

90 commits
1.1.0 ... 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
Thomas
4abc42e094 Some fixes/improvements 2020-05-03 19:25:06 +02:00
Thomas
493f1966d9 custom emoji in picker 2020-05-03 15:16:32 +02:00
Thomas
cc2fc34ec9 Some fixes 2020-05-03 14:00:07 +02:00
Thomas
2a9e99b3bf Merge branch 'master' of framagit.org:tom79/fediplan 2020-05-03 11:54:53 +02:00
Thomas
1d9cabc893 Update info for the api 2020-05-03 11:54:47 +02:00
Thomas
b2c511008d Merge branch 'l10n_master' into 'master'
New Crowdin translations

See merge request tom79/fediplan!69
2020-05-03 11:51:42 +02:00
Thomas
19f961a1c0 New Crowdin translations 2020-05-03 11:51:42 +02:00
Thomas
6a443adf08 Fix poll issue 2020-05-03 11:37:40 +02:00
Thomas
6817607955 some code cleaning 2020-05-03 11:13:49 +02:00
131 changed files with 38005 additions and 21957 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,33 +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"
},
"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
},
@ -42,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"
@ -67,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

@ -269,6 +269,11 @@ document = window.document || {};
emoji: ""
},
custom_emoji: {
icon: "grinning",
title: "Custom emoji",
emoji: "",
},
smileys_people: {
icon: "yum",
title: "Smileys & People",
@ -968,7 +973,7 @@ document = window.document || {};
var mainBlock = div('category-block').attr({"data-tone": 0}).prependTo(emojisList);
$.each(options.filters, function(filter, params) {
var skin = 0;
let skin = 0;
if (filter === 'recent' && !self.recentEmojis) {
return;
}
@ -981,13 +986,13 @@ document = window.document || {};
.wrapInner(shortnameTo(params.icon, self.emojiTemplateAlt))
.appendTo(filters);
} else if (options.tones) {
skin = 5;
skin = 6;
} else {
return;
}
do {
var category,
let category,
items = params.emoji.replace(/[\s,;]+/g, '|');
if (skin === 0) {
@ -1001,27 +1006,46 @@ document = window.document || {};
"data-tone": skin
}).appendTo(emojisList);
}
if( filter === "custom_emoji"){
let titleTab = params.title;
$.ajax({
url: "https://"+$('#data_api').attr('data-instance')+"/api/v1/custom_emojis",
context: document.body
}).done(function(customEmoji) {
for(var i = 0 ; i < customEmoji.length ; i++) {
var element = $('<i class="emojibtn" role="button" data-name=":'+customEmoji[i].shortcode+':" title="'+customEmoji[i].shortcode+'"><img class="emojioneemoji" src="'+customEmoji[i].url+'" data-src="'+customEmoji[i].url+'"/></i>)');
element.on('click', function () {
pasteHtmlAtCaret(shortnameTo($(this).attr('data-name'), self.emojiTemplate));
})
category.append(element);
}
if (skin > 0) {
category.hide();
items = items.split('|').join('_tone' + skin + '|') + '_tone' + skin;
$('<div class="emojionearea-category-title"/>').text(titleTab).prependTo(category);
});
}else {
if (skin > 0) {
category.hide();
items = items.split('|').join('_tone' + skin + '|') + '_tone' + skin;
}
if (filter === 'recent') {
items = getRecent();
}
items = shortnameTo(items,
self.sprite ?
'<i class="emojibtn" role="button" data-name="{name}" title="{friendlyName}"><i class="emojione-{uni}"></i></i>' :
'<i class="emojibtn" role="button" data-name="{name}" title="{friendlyName}"><img class="emojioneemoji lazy-emoji" data-src="{img}"/></i>',
true).split('|').join('');
category.html(items);
$('<div class="emojionearea-category-title"/>').text(params.title).prependTo(category);
}
if (filter === 'recent') {
items = getRecent();
}
items = shortnameTo(items,
self.sprite ?
'<i class="emojibtn" role="button" data-name="{name}" title="{friendlyName}"><i class="emojione-{uni}"></i></i>' :
'<i class="emojibtn" role="button" data-name="{name}" title="{friendlyName}"><img class="emojioneemoji lazy-emoji" data-src="{img}"/></i>',
true).split('|').join('');
category.html(items);
$('<div class="emojionearea-category-title"/>').text(params.title).prependTo(category);
} while (--skin > 0);
});
options.filters = null;
if (!self.sprite) {
self.lasyEmoji = emojisList.find(".lazy-emoji");
@ -1376,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) {
@ -1432,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')},
@ -1447,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')},
@ -1457,6 +1481,10 @@ document = window.document || {};
return value;
}));
});
} else {
callback($.map(map, function () {
return null;
}));
}
},
template: function (value) {
@ -1476,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,4 +1,5 @@
<?php
/**
* Created by fediplan.
* User: tom79
@ -10,38 +11,44 @@ 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\Security\Core\Authentication\Token\UsernamePasswordToken;
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')){
if ($authorizationChecker->isGranted('IS_AUTHENTICATED_FULLY')) {
$local = $request->getSession()->get('_locale');
return $this->redirect($this->generateUrl('schedule',['_locale' => $local]));
return $this->redirect($this->generateUrl('schedule', ['_locale' => $local]));
}
$client = new Client();
$flow->bind($client);
@ -50,31 +57,27 @@ class FediPlanController extends AbstractController
$client_id = null;
$client_secret = null;
if ($flow->isValid($form)) {
if( $flow->getCurrentStep() == 1){
if ($flow->getCurrentStep() == 1) {
$host = $client->getHost();
$result = $mastodon_api->getInstanceNodeInfo($host);
//We currently only support Mastodon accounts
if( $result != "MASTODON" && $result != "PLEROMA"){
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_only',[],'fediplan','en')));
}else{
if ($result != "MASTODON" && $result != "PLEROMA") {
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_only', [], 'fediplan', 'en')));
} else {
$mastodon_api->set_url("https://" . $host);
$mastodon_api->set_scopes([]);
$createApp = $mastodon_api->create_app("FediPlan", [], '', "https://plan.fedilab.app");
if( isset($createApp['error']) ){
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_client_id',[],'fediplan','en')));
}else{
if (isset($createApp['error'])) {
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_client_id', [], 'fediplan', 'en')));
} else {
// 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();
}
}
@ -85,49 +88,34 @@ class FediPlanController extends AbstractController
$mastodon_api->set_scopes([]);
$mastodon_api->set_client($client->getClientId(), $client->getClientSecret());
$reply = $mastodon_api->loginAuthorization($code);
if( isset($reply['error']) ){
/* $access_token = $code;
$token_type = "Bearer";
$mastodon_api->set_url("https://" . $client->getHost());
$mastodon_api->set_token($access_token, $token_type);
try {
$accountReply = $mastodon_api->accounts_verify_credentials();
} catch (\ErrorException $e) {
}
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("security.interactive_login", $event);
return $this->redirectToRoute('schedule');
}*/
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_token',[],'fediplan','en')));
}else{
if (isset($reply['error'])) {
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_token', [], 'fediplan', 'en')));
} else {
$access_token = $reply['response']['access_token'];
$token_type = $reply['response']['token_type'];
$mastodon_api->set_url("https://" . $client->getHost());
$mastodon_api->set_token($access_token, $token_type);
try {
$accountReply = $mastodon_api->accounts_verify_credentials();
} catch (\ErrorException $e) {
}
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("security.interactive_login", $event);
return $this->redirectToRoute('schedule');
$accountReply = $mastodon_api->accounts_verify_credentials();
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']);
$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')));
}
}
}
}
@ -146,80 +134,85 @@ 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)
#[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];
$token = explode(" ", $user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$params = [];
//Update media description and store their id
foreach ($_POST as $key => $value){
if( $key != "compose"){
if (strpos($key, 'media_id_') !== false){
foreach ($_POST as $key => $value) {
if ($key != "compose") {
if (str_contains($key, 'media_id_')) {
$mediaId = $value;
$description = $_POST['media_description_'.$mediaId];
$description = $_POST['media_description_' . $mediaId];
//update description if needed
if( $description != null && trim($description) != ""){
try {
$res = $mastodon_api->update_media($mediaId, ['description' => $description]);
} catch (\ErrorException $e) {}
if ($description != null && trim($description) != "") {
$mastodon_api->update_media($mediaId, ['description' => $description]);
}
$params['media_ids'][] = $mediaId;
}
}
}
//Schedule status
if( $data->getContentWarning() ){
if ($data->getContentWarning()) {
$params['spoiler_text'] = $data->getContentWarning();
}
if( $data->getContent() ){
if ($data->getContent()) {
$params['status'] = $data->getContent();
}
if( $data->getVisibility() ){
if ($data->getVisibility()) {
$params['visibility'] = $data->getVisibility();
}
$params['sensitive'] = ($data->getSensitive() == null || !$data->getSensitive())?false:true;
$params['sensitive'] = ($data->getSensitive() == null || !$data->getSensitive()) ? false : true;
if($data->getAttachPoll() > 0) {
$pollOptions = $data->getPollOptions();
} else{
$pollOptions = array();
}
$pollOptions = $data->getPollOptions();
$pollExpiresAt = $data->getPollExpiresAt();
$isPollMultiple = $data->isPollMultiple();
if( count($pollOptions) > 0){
if (count($pollOptions) > 0) {
$count_correct_values = 0;
foreach($pollOptions as $po) {
foreach ($pollOptions as $po) {
/** @var $po PollOption */
if( $po->getTitle() != null && strlen(trim($po->getTitle())) > 0){
if ($po->getTitle() != null && strlen(trim($po->getTitle())) > 0) {
$count_correct_values++;
}
}
if( $count_correct_values > 1 ){
if ($count_correct_values > 1) {
$params['poll']['options'] = [];
foreach($pollOptions as $po) {
foreach ($pollOptions as $po) {
/** @var $po PollOption */
if( $po->getTitle() != null && strlen(trim($po->getTitle())) > 0){
if ($po->getTitle() != null && strlen(trim($po->getTitle())) > 0) {
$params['poll']['options'][] = trim($po->getTitle());
}
}
@ -228,121 +221,125 @@ class FediPlanController extends AbstractController
}
}
try {
$date = new DateTime( $data->getScheduledAt()->format("Y-m-d H:i"), new DateTimeZone($data->getTimeZone()) );
$date->setTimezone( new DateTimeZone("UTC"));
$date = new DateTime($data->getScheduledAt()->format("Y-m-d H:i"), new DateTimeZone($data->getTimeZone()));
$date->setTimezone(new DateTimeZone("UTC"));
$params['scheduled_at'] = $date->format(DateTime::ISO8601);
} catch (\Exception $e) {}
try {
$response = $mastodon_api->post_statuses($params);
} catch (\ErrorException $e) {}
} catch (Exception $e) {
}
$response = $mastodon_api->post_statuses($params);
$session = $request->getSession();
if( isset($response['error']) ){
if (isset($response['error'])) {
$session->getFlashBag()->add(
'Error',
$response['error_message']
);
}else{
} else {
unset($compose);
unset($form);
$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',
'The message has been scheduled'
$translator->trans('common.schedule_success', [], 'fediplan', 'en')
);
$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",[
return $this->render("fediplan/schedule.html.twig", [
'form' => $form->createView(),
'instance' => $user->getInstance(),
'token' => $user->getToken(),
]);
]);
}
/**
* @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();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" " ,$user->getToken())[1];
$token = explode(" ", $user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$params = [];
if( $max_id != null){
$params['limit'] = $limit;
if ($max_id != null) {
$params['max_id'] = $max_id;
}
$scheduled_reply = [];
try {
$scheduled_reply = $mastodon_api->get_scheduled($params);
} catch (\ErrorException $e) {
}
$statuses = $mastodon_api->getScheduledStatuses($scheduled_reply['response'], $this->getUser());
$data['max_id'] = $scheduled_reply['max_id'];
$scheduled_reply = $mastodon_api->get_scheduled($params);
$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();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" " ,$user->getToken())[1];
$token = explode(" ", $user->getToken())[1];
$type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type);
$response = [];
try {
$response = $mastodon_api->delete_scheduled($id);
} catch (\ErrorException $e) {}
$response = $mastodon_api->delete_scheduled($id);
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,22 @@ use Symfony\Component\HttpKernel\KernelEvents;
class LocaleSubscriber implements EventSubscriberInterface
{
private $defaultLocale;
private string $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(RequestEvent $event)
public static function getSubscribedEvents(): array
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
@ -37,12 +45,4 @@ class LocaleSubscriber implements EventSubscriberInterface
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
public static function getSubscribedEvents()
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
}

View file

@ -1,4 +1,5 @@
<?php
<?php /** @noinspection PhpTranslationKeyInspection */
/**
* Created by fediplan.
* User: tom79
@ -9,27 +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\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\Translator;
class ComposeType extends AbstractType {
class ComposeType extends AbstractType
{
private $securityContext;
private Security $securityContext;
private $translator;
public function __construct(Security $securityContext, Translator $translator)
@ -38,19 +41,19 @@ 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*/
/**@var $user MastodonAccount */
$user = $options['user'];
if( $user->getDefaultSensitivity()) {
if ($user->getDefaultSensitivity()) {
$checkbox = [
'required' => false,
'attr' => ['checked' => 'checked'],
'label' => 'page.schedule.form.sensitive',
'translation_domain' => 'fediplan'
];
}else {
} else {
$checkbox = ['required' => false, 'label' => 'page.schedule.form.sensitive',
'translation_domain' => 'fediplan'];
}
@ -74,12 +77,14 @@ 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',
'translation_domain' => 'fediplan']);
$builder->add('sensitive', CheckboxType::class, $checkbox);
$builder->add('scheduled_at', DateTimeType::class,[
$builder->add('scheduled_at', DateTimeType::class, [
'widget' => 'single_text',
"data" => new DateTime(),
'label' => 'page.schedule.form.scheduled_at',
@ -87,7 +92,7 @@ class ComposeType extends AbstractType {
$builder->add('poll_options', CollectionType::class,
[
'entry_type' => PollOptionType::class,
'entry_type' => PollOptionType::class,
'by_reference' => false,
'allow_add' => true,
'prototype' => true,
@ -101,16 +106,16 @@ class ComposeType extends AbstractType {
$builder->add('poll_expires_at', ChoiceType::class,
[
'choices' => [
$this->translator->trans('poll.duration_m', ['minutes' => 5], 'fediplan') => 5*60,
$this->translator->trans('poll.duration_m', ['minutes' => 30], 'fediplan') => 30*60,
$this->translator->trans('poll.duration_h', ['hours' => 1], 'fediplan') => 60*60,
$this->translator->trans('poll.duration_h', ['hours' => 6], 'fediplan') => 6*60*60,
$this->translator->trans('poll.duration_d', ['days' => 1], 'fediplan') => 24*60*60,
$this->translator->trans('poll.duration_d', ['days' => 3], 'fediplan') => 3*24*60*60,
$this->translator->trans('poll.duration_d', ['days' => 7], 'fediplan') => 7*24*60*60,
$this->translator->trans('poll.duration_m', ['minutes' => 5], 'fediplan') => 5 * 60,
$this->translator->trans('poll.duration_m', ['minutes' => 30], 'fediplan') => 30 * 60,
$this->translator->trans('poll.duration_h', ['hours' => 1], 'fediplan') => 60 * 60,
$this->translator->trans('poll.duration_h', ['hours' => 6], 'fediplan') => 6 * 60 * 60,
$this->translator->trans('poll.duration_d', ['days' => 1], 'fediplan') => 24 * 60 * 60,
$this->translator->trans('poll.duration_d', ['days' => 3], 'fediplan') => 3 * 24 * 60 * 60,
$this->translator->trans('poll.duration_d', ['days' => 7], 'fediplan') => 7 * 24 * 60 * 60,
],
'data' => 24*60*60,
'data' => 24 * 60 * 60,
'required' => false,
'label' => 'page.schedule.form.end_in',
'translation_domain' => 'fediplan']);
@ -121,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

@ -11,9 +11,11 @@ namespace App\Form;
use Craue\FormFlowBundle\Form\FormFlow;
class ConnectMastodonAccountFlow extends FormFlow {
class ConnectMastodonAccountFlow extends FormFlow
{
protected function loadStepsConfig() {
protected function loadStepsConfig(): array
{
return [
[
'form_type' => ConnectMastodonAccountType::class,

View file

@ -7,19 +7,22 @@
*/
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ConnectMastodonAccountType extends AbstractType {
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:
$builder->add('host', TextType::class, [
'label' => 'page.index.form.instance',
'label' => 'page.index.form.instance',
'translation_domain' => 'fediplan'
]);
break;
@ -35,11 +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,35 +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 {
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,51 +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;
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

@ -1,7 +1,10 @@
<?php
<?php /** @noinspection PhpUnused */
namespace App\Services;
use CurlFile;
use ErrorException;
/**
* An object-oriented wrapper of the PHP cURL extension.
*
@ -67,91 +70,75 @@ class Curl
* @var string The user agent name which is set when making a request
*/
const USER_AGENT = 'PHP Curl/1.9 (+https://github.com/php-mod/curl)';
private $_cookies = array();
private $_headers = array();
/**
* @var resource Contains the curl resource created by `curl_init()` function
*/
public $curl;
/**
* @var bool Whether an error occured or not
*/
public $error = false;
/**
* @var int Contains the error code of the curren request, 0 means no error happend
*/
public $error_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $error_message = null;
/**
* @var bool Whether an error occured or not
*/
public $curl_error = false;
/**
* @var int Contains the error code of the curren request, 0 means no error happend.
* @see https://curl.haxx.se/libcurl/c/libcurl-errors.html
*/
public $curl_error_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $curl_error_message = null;
/**
* @var bool Whether an error occured or not
*/
public $http_error = false;
/**
* @var int Contains the status code of the current processed request.
*/
public $http_status_code = 0;
/**
* @var string If the curl request failed, the error message is contained
*/
public $http_error_message = null;
/**
* @var string|array TBD (ensure type) Contains the request header informations
*/
public $request_headers = null;
/**
* @var string|array TBD (ensure type) Contains the response header informations
*/
public $response_headers = array();
/**
* @var string Contains the response from the curl request
*/
public $response = null;
/**
* @var bool Whether the current section of response headers is after 'HTTP/1.1 100 Continue'
*/
protected $response_header_continue = false;
private $_cookies = array();
private $_headers = array();
/**
* Constructor ensures the available curl extension is loaded.
*
* @throws \ErrorException
* @throws ErrorException
*/
public function __construct()
{
if (!extension_loaded('curl')) {
throw new \ErrorException('The cURL extensions is not loaded, make sure you have installed the cURL extension: https://php.net/manual/curl.setup.php');
throw new ErrorException('The cURL extensions is not loaded, make sure you have installed the cURL extension: https://php.net/manual/curl.setup.php');
}
$this->init();
@ -176,6 +163,44 @@ class Curl
return $this;
}
/**
* Provide a User Agent.
*
* In order to provide you cusomtized user agent name you can use this method.
*
* ```php
* $curl = new Curl();
* $curl->setUserAgent('My John Doe Agent 1.0');
* $curl->get('http://example.com/request.php');
* ```
*
* @param string $useragent The name of the user agent to set for the current request
* @return self
*/
public function setUserAgent($useragent)
{
$this->setOpt(CURLOPT_USERAGENT, $useragent);
return $this;
}
// protected methods
/**
* Set customized curl options.
*
* To see a full list of options: http://php.net/curl_setopt
*
* @see http://php.net/curl_setopt
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @param mixed $value The value to pass for the given $option
* @return bool
*/
public function setOpt($option, $value)
{
return curl_setopt($this->curl, $option, $value);
}
/**
* Handle writing the response headers
*
@ -183,6 +208,7 @@ class Curl
* @param string $header_line A line from the list of response headers
*
* @return int Returns the length of the $header_line
* @noinspection PhpUnusedParameterInspection
*/
public function addResponseHeaderLine($curl, $header_line)
{
@ -195,11 +221,19 @@ class Curl
} elseif (!$this->response_header_continue) {
$this->response_headers[] = $trimmed_header;
}
return strlen($header_line);
}
// protected methods
/**
* @deprecated calling exec() directly is discouraged
*/
public function _exec()
{
return $this->exec();
}
// public methods
/**
* Execute the curl request based on the respectiv settings.
@ -224,30 +258,133 @@ class Curl
return $this->error_code;
}
// functions
/**
* @param array|object|string $data
* Make a get request with optional data.
*
* The get request has no body data, the data will be correctly added to the $url with the http_build_query() method.
*
* @param string $url The url to make the get request for
* @param array $data Optional arguments who are part of the url
* @return self
*/
protected function preparePayload($data)
public function get($url, $data = array())
{
$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);
}
if (count($data) > 0) {
$this->setOpt(CURLOPT_URL, $url . '?' . http_build_query($data));
} else {
$this->setOpt(CURLOPT_URL, $url);
}
$this->setOpt(CURLOPT_HTTPGET, true);
$this->exec();
return $this;
}
$this->setOpt(CURLOPT_POSTFIELDS, $data);
/**
* Make a post request with optional post data.
*
* @param string $url The url to make the post request
* @param array $data Post data to pass to the url
* @return self
*/
public function post($url, $data = array())
{
$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;
}
/**
* Make a put request with optional data.
*
* The put request data can be either sent via payload or as get paramters of the string.
*
* @param string $url The url to make the put request
* @param array $data Optional data to pass to the $url
* @return self
*/
public function put($url, $data = array())
{
$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;
}
/**
* Make a patch request with optional data.
*
* The patch request data can be either sent via payload or as get paramters of the string.
*
* @param string $url The url to make the patch request
* @param array $data Optional data to pass to the $url
* @return self
*/
public function patch($url, $data = array())
{
$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;
}
// setters
/**
* Make a delete request with optional data.
*
* @param string $url The url to make the delete request
* @param array $data Optional data to pass to the $url
* @return self
*/
public function delete($url, $data = array())
{
$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;
}
/**
* Pass basic auth data.
*
* If the the rquested url is secured by an httaccess basic auth mechanism you can use this method to provided the auth data.
*
* ```php
* $curl = new Curl();
* $curl->setBasicAuthentication('john', 'doe');
* $curl->get('http://example.com/secure.php');
* ```
*
* @param string $username The username for the authentification
* @param string $password The password for the given username for the authentification
* @return self
*/
public function setBasicAuthentication($username, $password)
{
$this->setHttpAuth(self::AUTH_BASIC);
$this->setOpt(CURLOPT_USERPWD, $username . ':' . $password);
return $this;
}
/**
@ -269,164 +406,6 @@ class Curl
$this->setOpt(CURLOPT_HTTPAUTH, $httpauth);
}
// public methods
/**
* @deprecated calling exec() directly is discouraged
*/
public function _exec()
{
return $this->exec();
}
// functions
/**
* Make a get request with optional data.
*
* The get request has no body data, the data will be correctly added to the $url with the http_build_query() method.
*
* @param string $url The url to make the get request for
* @param array $data Optional arguments who are part of the url
* @return self
*/
public function get($url, $data = array())
{
if (count($data) > 0) {
$this->setOpt(CURLOPT_URL, $url.'?'.http_build_query($data));
} else {
$this->setOpt(CURLOPT_URL, $url);
}
$this->setOpt(CURLOPT_HTTPGET, true);
$this->exec();
return $this;
}
/**
* Make a post request with optional post data.
*
* @param string $url The url to make the post request
* @param array $data Post data to pass to the url
* @return self
*/
public function post($url, $data = array(), $payload = false)
{
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);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
$this->exec();
return $this;
}
/**
* Make a put request with optional data.
*
* The put request data can be either sent via payload or as get paramters of the string.
*
* @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)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
$this->exec();
return $this;
}
/**
* Make a patch request with optional data.
*
* The patch request data can be either sent via payload or as get paramters of the string.
*
* @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)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
$this->exec();
return $this;
}
/**
* Make a delete request with optional data.
*
* @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)
{
if (! empty($data)) {
if ($payload === false) {
$url .= '?'.http_build_query($data);
} else {
$this->preparePayload($data);
}
}
$this->setOpt(CURLOPT_URL, $url);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
$this->exec();
return $this;
}
// setters
/**
* Pass basic auth data.
*
* If the the rquested url is secured by an httaccess basic auth mechanism you can use this method to provided the auth data.
*
* ```php
* $curl = new Curl();
* $curl->setBasicAuthentication('john', 'doe');
* $curl->get('http://example.com/secure.php');
* ```
*
* @param string $username The username for the authentification
* @param string $password The password for the given username for the authentification
* @return self
*/
public function setBasicAuthentication($username, $password)
{
$this->setHttpAuth(self::AUTH_BASIC);
$this->setOpt(CURLOPT_USERPWD, $username.':'.$password);
return $this;
}
/**
* Provide optional header informations.
*
@ -438,38 +417,20 @@ class Curl
* $curl->get('http://example.com/request.php');
* ```
*
* @param string $key The header key
* @param string $key The header key
* @param string $value The value for the given header key
* @return self
*/
public function setHeader($key, $value)
{
$this->_headers[$key] = $key.': '.$value;
$this->_headers[$key] = $key . ': ' . $value;
$this->setOpt(CURLOPT_HTTPHEADER, array_values($this->_headers));
return $this;
}
/**
* Provide a User Agent.
*
* In order to provide you cusomtized user agent name you can use this method.
*
* ```php
* $curl = new Curl();
* $curl->setUserAgent('My John Doe Agent 1.0');
* $curl->get('http://example.com/request.php');
* ```
*
* @param string $useragent The name of the user agent to set for the current request
* @return self
*/
public function setUserAgent($useragent)
{
$this->setOpt(CURLOPT_USERAGENT, $useragent);
return $this;
}
/**
* @param $referrer
* @return Curl
* @deprecated Call setReferer() instead
*/
public function setReferrer($referrer)
@ -495,7 +456,7 @@ class Curl
/**
* Set contents of HTTP Cookie header.
*
* @param string $key The name of the cookie
* @param string $key The name of the cookie
* @param string $value The value for the provided cookie name
* @return self
*/
@ -507,53 +468,36 @@ class Curl
}
/**
* Set customized curl options.
* Return the endpoint set for curl
*
* To see a full list of options: http://php.net/curl_setopt
* @see http://php.net/curl_getinfo
*
* @see http://php.net/curl_setopt
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @param mixed $value The value to pass for the given $option
* @return string of endpoint
*/
public function setOpt($option, $value)
{
return curl_setopt($this->curl, $option, $value);
}
/**
* Get customized curl options.
*
* To see a full list of options: http://php.net/curl_getinfo
*
* @see http://php.net/curl_getinfo
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @param mixed $value The value to check for the given $option
*/
public function getOpt($option)
{
return curl_getinfo($this->curl, $option);
}
/**
* Return the endpoint set for curl
*
* @see http://php.net/curl_getinfo
*
* @return string of endpoint
*/
public function getEndpoint()
{
return $this->getOpt(CURLINFO_EFFECTIVE_URL);
}
/**
* Get customized curl options.
*
* To see a full list of options: http://php.net/curl_getinfo
*
* @see http://php.net/curl_getinfo
*
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
* @return mixed
*/
public function getOpt($option)
{
return curl_getinfo($this->curl, $option);
}
/**
* Enable verbositiy.
*
* @todo As to keep naming convention it should be renamed to `setVerbose()`
*
* @param string $on
* @param bool $on
* @return self
*/
public function verbose($on = true)
@ -662,7 +606,7 @@ class Curl
{
return $this->http_status_code >= 500 && $this->http_status_code < 600;
}
/**
* Get a specific response header key or all values from the response headers array.
*
@ -683,27 +627,27 @@ class Curl
* ```
*
* @param string $headerKey Optional key to get from the array.
* @return bool|string
* @return array
* @since 1.9
*/
public function getResponseHeaders($headerKey = null)
{
$headers = array();
$headerKey = strtolower($headerKey);
foreach ($this->response_headers as $header) {
$parts = explode(":", $header, 2);
$key = isset($parts[0]) ? $parts[0] : null;
$value = isset($parts[1]) ? $parts[1] : null;
$headers[trim(strtolower($key))] = trim($value);
}
if ($headerKey) {
return isset($headers[$headerKey]) ? $headers[$headerKey] : false;
return isset($headers[$headerKey]) ? $headers[$headerKey] : [];
}
return $headers;
}

File diff suppressed because it is too large Load diff

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,57 +3,52 @@
namespace App\SocialEntity;
use DateTime;
use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
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->attachments = new ArrayCollection();
$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;
@ -65,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
@ -110,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;
@ -163,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