Compare commits

..

No commits in common. "main" and "1.1.0" have entirely different histories.
main ... 1.1.0

131 changed files with 22041 additions and 38089 deletions

10
.env
View file

@ -1,5 +1,5 @@
# In all environments, the following files are loaded if they exist, # In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former: # the later taking precedence over the former:
# #
# * .env contains default values for the environment variables needed by the app # * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides # * .env.local uncommitted file with local overrides
@ -9,13 +9,13 @@
# Real environment variables win over .env files. # Real environment variables win over .env files.
# #
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED 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). # Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration # https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
###> symfony/framework-bundle ### ###> symfony/framework-bundle ###
APP_ENV=prod APP_ENV=dev
APP_SECRET=7189792ca5da6b84aff72ec1c63d95ae APP_SECRET=7189792ca5da6b84aff72ec1c63d95ae
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ### ###< symfony/framework-bundle ###

14
.gitignore vendored
View file

@ -12,17 +12,3 @@
###> symfony/web-server-bundle ### ###> symfony/web-server-bundle ###
/.web-server-pid /.web-server-pid
###< symfony/web-server-bundle ### ###< 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

View file

@ -1,56 +0,0 @@
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,79 +1,25 @@
**If you want to contribute to a _translation_:**[![Crowdin](https://badges.crowdin.net/fediplan/localized.svg)](https://crowdin.com/project/fediplan) **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
There are 2 methods to run Fediplan **1 - Clone the repository**
1. Build and run Docker image
2. Manual install
### 1. Build and run Docker image **2 - Install vendors:**
#### Required programs `composer install -o`
- `git`
- `docker`
#### Steps PS: You need to install `composer`, just use:
1. Install required programs `apt install composer`
2. Clone this repo<br> Or you can do that with the following commands:
`git clone https://framagit.org/tom79/fediplan.git fediplan`
3. Build the Docker image<br> ```
`docker build --tag fediplan 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;"
4. Run the docker image<br> php composer-setup.php
`docker run --detach --restart unless-stopped --name fediplan fediplan:latest` php -r "unlink('composer-setup.php');"
```
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/) See: [Download Composer](https://getcomposer.org/download/)

View file

@ -1,10 +0,0 @@
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
View file

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

View file

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

View file

@ -1,16 +0,0 @@
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';
}
}

View file

@ -1 +0,0 @@
{"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

@ -1 +0,0 @@
{"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

@ -1,3 +0,0 @@
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

View file

@ -1,22 +0,0 @@
<?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,19 +3,40 @@
use App\Kernel; use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
if (!is_dir(dirname(__DIR__).'/vendor')) { if (false === in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
throw new LogicException('Dependencies are missing. Try running "composer install".'); echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.\PHP_SAPI.' SAPI'.\PHP_EOL;
} }
if (!is_file(dirname(__DIR__).'/vendor/autoload_runtime.php')) { set_time_limit(0);
throw new LogicException('Symfony Runtime is missing. Try running "composer require symfony/runtime".');
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.');
} }
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; $input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}
return function (array $context) { if ($input->hasParameterOption('--no-debug', true)) {
$kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
}
return new Application($kernel); 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);

View file

@ -1,54 +1,33 @@
{ {
"type": "project", "type": "project",
"license": "proprietary", "license": "proprietary",
"minimum-stability": "stable",
"prefer-stable": true,
"require": { "require": {
"php": ">=8.2", "php": "^7.1.3",
"ext-ctype": "*", "ext-ctype": "*",
"ext-curl": "*",
"ext-iconv": "*", "ext-iconv": "*",
"craue/formflow-bundle": "*", "craue/formflow-bundle": "^3.2",
"curl/curl": "^2.5", "doctrine/collections": "^1.6",
"friendsofsymfony/jsrouting-bundle": "*", "friendsofsymfony/jsrouting-bundle": "^2.4",
"phpdocumentor/reflection-docblock": "^5.4", "sensio/framework-extra-bundle": "^5.4",
"phpstan/phpdoc-parser": "^1.29", "symfony/asset": "4.3.*",
"symfony/asset": "7.0.*", "symfony/console": "4.3.*",
"symfony/asset-mapper": "7.0.*", "symfony/debug": "4.3.*",
"symfony/console": "7.0.*", "symfony/dotenv": "4.3.*",
"symfony/dotenv": "7.0.*", "symfony/flex": "^1.3.1",
"symfony/expression-language": "7.0.*", "symfony/framework-bundle": "4.3.*",
"symfony/flex": "^2", "symfony/polyfill-intl-messageformatter": "^1.15",
"symfony/form": "7.0.*", "symfony/security-bundle": "4.3.*",
"symfony/framework-bundle": "7.0.*", "symfony/translation": "4.3.*",
"symfony/http-client": "7.0.*", "symfony/twig-bundle": "4.3.*",
"symfony/intl": "7.0.*", "symfony/yaml": "4.3.*",
"symfony/mime": "7.0.*", "twig/extensions": "^1.5"
"symfony/monolog-bundle": "^3.0", },
"symfony/notifier": "7.0.*", "require-dev": {
"symfony/process": "7.0.*", "symfony/web-server-bundle": "4.3.*"
"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": { "config": {
"allow-plugins": { "preferred-install": {
"php-http/discovery": true, "*": "dist"
"symfony/flex": true,
"symfony/runtime": true
}, },
"sort-packages": true "sort-packages": true
}, },
@ -63,22 +42,17 @@
} }
}, },
"replace": { "replace": {
"paragonie/random_compat": "2.*",
"symfony/polyfill-ctype": "*", "symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*", "symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*", "symfony/polyfill-php71": "*",
"symfony/polyfill-php73": "*", "symfony/polyfill-php70": "*",
"symfony/polyfill-php74": "*", "symfony/polyfill-php56": "*"
"symfony/polyfill-php80": "*",
"symfony/polyfill-php81": "*",
"symfony/polyfill-php82": "*"
}, },
"scripts": { "scripts": {
"auto-scripts": { "auto-scripts": {
"cache:clear": "symfony-cmd", "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": [ "post-install-cmd": [
"@auto-scripts" "@auto-scripts"
@ -93,17 +67,7 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "7.0.*" "require": "4.3.*"
} }
},
"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.*"
} }
} }

7983
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,15 +2,10 @@
return [ return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], 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\SecurityBundle\SecurityBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
FOS\JsRoutingBundle\FOSJsRoutingBundle::class => ['all' => true], FOS\JsRoutingBundle\FOSJsRoutingBundle::class => ['all' => true],
Craue\FormFlowBundle\CraueFormFlowBundle::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

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

View file

@ -1,5 +0,0 @@
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

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

View file

@ -1,16 +1,16 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework: framework:
secret: '%env(APP_SECRET)%' secret: '%env(APP_SECRET)%'
#csrf_protection: true #csrf_protection: true
#http_method_override: true
# Note that the session will be started ONLY if you read or write from it. # Enables session support. Note that the session will ONLY be started if you read or write from it.
session: true # Remove or comment this section to explicitly disable session support.
session:
handler_id: null
cookie_secure: auto
cookie_samesite: lax
#esi: true #esi: true
#fragments: true #fragments: true
php_errors:
when@test: log: true
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View file

@ -1,10 +1,4 @@
framework: framework:
router: router:
# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. strict_requirements: null
# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands utf8: true
#default_uri: http://localhost
when@prod:
framework:
router:
strict_requirements: null

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,8 @@
framework: framework:
validation: validation:
# Enables validator auto-mapping support. email_validation_mode: html5
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
#auto_mapping:
# App\Entity\: []
when@test: # Enables validator auto-mapping support.
framework: # For instance, basic validation constraints will be inferred from Doctrine's metadata.
validation: #auto_mapping:
not_compromised_password: false # App\Entity\: []

View file

@ -1,17 +0,0 @@
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 }

View file

@ -1,5 +0,0 @@
<?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,5 +1,3 @@
controllers: #index:
resource: # path: /
path: ../src/Controller/ # controller: App\Controller\DefaultController::index
namespace: App\Controller
type: attribute

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,56 +0,0 @@
# 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

@ -1,56 +0,0 @@
[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

View file

@ -1,47 +0,0 @@
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;
}

View file

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

View file

@ -1,23 +0,0 @@
[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

View file

@ -1,28 +0,0 @@
<?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,268 +1,57 @@
/*! /*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/) * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors * Copyright 2011-2018 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * 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)
*/ */
: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, *::before,
*::after { *::after {
box-sizing: border-box; box-sizing: border-box;
} }
@media (prefers-reduced-motion: no-preference) { html {
:root { font-family: sans-serif;
scroll-behavior: smooth; 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;
} }
body { body {
margin: 0; margin: 0;
font-family: var(--bs-body-font-family); 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: var(--bs-body-font-size); font-size: 1rem;
font-weight: var(--bs-body-font-weight); font-weight: 400;
line-height: var(--bs-body-line-height); line-height: 1.5;
color: var(--bs-body-color); color: #212529;
text-align: var(--bs-body-text-align); text-align: left;
background-color: var(--bs-body-bg); background-color: #fff;
-webkit-text-size-adjust: 100%; }
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
[tabindex="-1"]:focus {
outline: 0 !important;
} }
hr { hr {
margin: 1rem 0; box-sizing: content-box;
color: inherit; height: 0;
border: 0; overflow: visible;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
} }
h6, h5, h4, h3, h2, h1 { h1, h2, h3, h4, h5, h6 {
margin-top: 0; margin-top: 0;
margin-bottom: 0.5rem; 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 { p {
@ -270,12 +59,13 @@ p {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
abbr[title] { abbr[title],
abbr[data-original-title] {
text-decoration: underline;
-webkit-text-decoration: underline dotted; -webkit-text-decoration: underline dotted;
text-decoration: underline dotted; text-decoration: underline dotted;
cursor: help; cursor: help;
-webkit-text-decoration-skip-ink: none; border-bottom: 0;
text-decoration-skip-ink: none;
} }
address { address {
@ -284,11 +74,6 @@ address {
line-height: inherit; line-height: inherit;
} }
ol,
ul {
padding-left: 2rem;
}
ol, ol,
ul, ul,
dl { dl {
@ -308,7 +93,7 @@ dt {
} }
dd { dd {
margin-bottom: 0.5rem; margin-bottom: .5rem;
margin-left: 0; margin-left: 0;
} }
@ -316,139 +101,118 @@ blockquote {
margin: 0 0 1rem; margin: 0 0 1rem;
} }
dfn {
font-style: italic;
}
b, b,
strong { strong {
font-weight: bolder; font-weight: bolder;
} }
small { small {
font-size: 0.875em; font-size: 80%;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
} }
sub, sub,
sup { sup {
position: relative; position: relative;
font-size: 0.75em; font-size: 75%;
line-height: 0; line-height: 0;
vertical-align: baseline; vertical-align: baseline;
} }
sub { sub {
bottom: -0.25em; bottom: -.25em;
} }
sup { sup {
top: -0.5em; top: -.5em;
} }
a { a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); color: #007bff;
text-decoration: underline; text-decoration: none;
} background-color: transparent;
a:hover { -webkit-text-decoration-skip: objects;
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
} }
a:not([href]):not([class]), a:not([href]):not([class]):hover { a:hover {
color: #0056b3;
text-decoration: underline;
}
a:not([href]):not([tabindex]) {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
color: inherit;
text-decoration: none;
}
a:not([href]):not([tabindex]):focus {
outline: 0;
}
pre, pre,
code, code,
kbd, kbd,
samp { samp {
font-family: var(--bs-font-monospace); font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 1em; font-size: 1em;
} }
pre { pre {
display: block;
margin-top: 0; margin-top: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
overflow: auto; overflow: auto;
font-size: 0.875em; -ms-overflow-style: scrollbar;
}
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 { figure {
margin: 0 0 1rem; margin: 0 0 1rem;
} }
img, img {
vertical-align: middle;
border-style: none;
}
svg { svg {
overflow: hidden;
vertical-align: middle; vertical-align: middle;
} }
table { table {
caption-side: bottom;
border-collapse: collapse; border-collapse: collapse;
} }
caption { caption {
padding-top: 0.5rem; padding-top: 0.75rem;
padding-bottom: 0.5rem; padding-bottom: 0.75rem;
color: var(--bs-secondary-color); color: #6c757d;
text-align: left; text-align: left;
caption-side: bottom;
} }
th { th {
text-align: inherit; text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
} }
label { label {
display: inline-block; display: inline-block;
margin-bottom: 0.5rem;
} }
button { button {
border-radius: 0; border-radius: 0;
} }
button:focus:not(:focus-visible) { button:focus {
outline: 0; outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
} }
input, input,
@ -462,45 +226,46 @@ textarea {
line-height: inherit; line-height: inherit;
} }
button,
input {
overflow: visible;
}
button, button,
select { select {
text-transform: none; text-transform: none;
} }
[role=button] {
cursor: pointer;
}
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, button,
[type=button], html [type="button"],
[type=reset], [type="reset"],
[type=submit] { [type="submit"] {
-webkit-appearance: button; -webkit-appearance: button;
} }
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled) {
cursor: pointer;
}
::-moz-focus-inner { button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
padding: 0; padding: 0;
border-style: none; 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 { textarea {
overflow: auto;
resize: vertical; resize: vertical;
} }
@ -512,55 +277,34 @@ fieldset {
} }
legend { legend {
float: left; display: block;
width: 100%; width: 100%;
max-width: 100%;
padding: 0; padding: 0;
margin-bottom: 0.5rem; margin-bottom: .5rem;
font-size: calc(1.275rem + 0.3vw); font-size: 1.5rem;
line-height: inherit; line-height: inherit;
} color: inherit;
@media (min-width: 1200px) { white-space: normal;
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
} }
::-webkit-datetime-edit-fields-wrapper, progress {
::-webkit-datetime-edit-text, vertical-align: baseline;
::-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;
} }
::-webkit-inner-spin-button { [type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto; height: auto;
} }
[type=search] { [type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px; outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none; -webkit-appearance: none;
} }
::-webkit-color-swatch-wrapper { [type="search"]::-webkit-search-cancel-button,
padding: 0; [type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
} }
::-webkit-file-upload-button { ::-webkit-file-upload-button {
@ -568,30 +312,20 @@ legend + * {
-webkit-appearance: button; -webkit-appearance: button;
} }
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output { output {
display: inline-block; display: inline-block;
} }
iframe {
border: 0;
}
summary { summary {
display: list-item; display: list-item;
cursor: pointer; cursor: pointer;
} }
progress { template {
vertical-align: baseline; display: none;
} }
[hidden] { [hidden] {
display: none !important; display: none !important;
} }
/*# sourceMappingURL=bootstrap-reboot.css.map */ /*# 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

View file

@ -1,14 +0,0 @@
/* 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 */

16377
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

42
public/css/bootstrap4-toggle.min.css vendored Normal file
View file

@ -0,0 +1,42 @@
/*\
|*| ========================================================================
|*| 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,9 +1,27 @@
<?php <?php
use App\Kernel; use App\Kernel;
use Symfony\Component\Debug\Debug;
use Symfony\Component\HttpFoundation\Request;
require_once dirname(__DIR__).'/vendor/autoload_runtime.php'; require dirname(__DIR__).'/config/bootstrap.php';
return function (array $context) { if ($_SERVER['APP_DEBUG']) {
return new Kernel($context['APP_ENV'], (bool) $context['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);

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

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

8154
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

11
public/js/bootstrap4-toggle.min.js vendored Normal file
View file

@ -0,0 +1,11 @@
/*\
|*| ========================================================================
|*| 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,11 +269,6 @@ document = window.document || {};
emoji: "" emoji: ""
}, },
custom_emoji: {
icon: "grinning",
title: "Custom emoji",
emoji: "",
},
smileys_people: { smileys_people: {
icon: "yum", icon: "yum",
title: "Smileys & People", title: "Smileys & People",
@ -973,7 +968,7 @@ document = window.document || {};
var mainBlock = div('category-block').attr({"data-tone": 0}).prependTo(emojisList); var mainBlock = div('category-block').attr({"data-tone": 0}).prependTo(emojisList);
$.each(options.filters, function(filter, params) { $.each(options.filters, function(filter, params) {
let skin = 0; var skin = 0;
if (filter === 'recent' && !self.recentEmojis) { if (filter === 'recent' && !self.recentEmojis) {
return; return;
} }
@ -986,13 +981,13 @@ document = window.document || {};
.wrapInner(shortnameTo(params.icon, self.emojiTemplateAlt)) .wrapInner(shortnameTo(params.icon, self.emojiTemplateAlt))
.appendTo(filters); .appendTo(filters);
} else if (options.tones) { } else if (options.tones) {
skin = 6; skin = 5;
} else { } else {
return; return;
} }
do { do {
let category, var category,
items = params.emoji.replace(/[\s,;]+/g, '|'); items = params.emoji.replace(/[\s,;]+/g, '|');
if (skin === 0) { if (skin === 0) {
@ -1006,46 +1001,27 @@ document = window.document || {};
"data-tone": skin "data-tone": skin
}).appendTo(emojisList); }).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);
}
$('<div class="emojionearea-category-title"/>').text(titleTab).prependTo(category); if (skin > 0) {
}); category.hide();
}else { items = items.split('|').join('_tone' + skin + '|') + '_tone' + skin;
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); } while (--skin > 0);
}); });
options.filters = null; options.filters = null;
if (!self.sprite) { if (!self.sprite) {
self.lasyEmoji = emojisList.find(".lazy-emoji"); self.lasyEmoji = emojisList.find(".lazy-emoji");
@ -1400,14 +1376,13 @@ document = window.document || {};
self.editor.html(self.content = ''); self.editor.html(self.content = '');
} }
source[sourceValFunc](self.getText()); source[sourceValFunc](self.getText());
var count = 0; let inputText;
$('.emojionearea-editor').each(function() { inputText = self.getText();
var currentElement = $(this); inputText = inputText
count += currentElement.text() .replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3')
.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');
.replace(/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&/=]*)/g, 'xxxxxxxxxxxxxxxxxxxxxxx').length;
}); $("#count").text(inputText.length);
$("#count").text(count);
}); });
if (options.shortcuts) { if (options.shortcuts) {
self.on("@keydown", function(_, e) { self.on("@keydown", function(_, e) {
@ -1457,11 +1432,12 @@ document = window.document || {};
id: css_class, id: css_class,
match: /\B((:[\-+\w]*)|(@[\-+\w]*)|(#[\-+\w]*))$/, match: /\B((:[\-+\w]*)|(@[\-+\w]*)|(#[\-+\w]*))$/,
search: function (term, callback) { search: function (term, callback) {
if (term.startsWith(":")) { if (term.startsWith(":")) {
callback($.map(map, function (emoji) { callback($.map(map, function (emoji) {
return emoji.indexOf(term) === 0 ? emoji : null; return emoji.indexOf(term) === 0 ? emoji : null;
})); }));
} else if (term.startsWith("@") && term.substring(1).length > 1){ } else if (term.startsWith("@")){
$.ajax({ $.ajax({
url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=accounts&q="+term.substring(1), url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=accounts&q="+term.substring(1),
headers: {"Authorization": $('#data_api').attr('data-token')}, headers: {"Authorization": $('#data_api').attr('data-token')},
@ -1471,7 +1447,7 @@ document = window.document || {};
return value; return value;
})); }));
}); });
}else if (term.startsWith("#") && term.substring(1).length > 1){ }else if (term.startsWith("#")){
$.ajax({ $.ajax({
url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=hashtags&q="+term.substring(1), url: "https://"+$('#data_api').attr('data-instance')+"/api/v2/search?type=hashtags&q="+term.substring(1),
headers: {"Authorization": $('#data_api').attr('data-token')}, headers: {"Authorization": $('#data_api').attr('data-token')},
@ -1481,10 +1457,6 @@ document = window.document || {};
return value; return value;
})); }));
}); });
} else {
callback($.map(map, function () {
return null;
}));
} }
}, },
template: function (value) { template: function (value) {
@ -1504,6 +1476,7 @@ document = window.document || {};
}else if (typeof value.name != 'undefined') { }else if (typeof value.name != 'undefined') {
return "#"+value.name+ "&nbsp;"; return "#"+value.name+ "&nbsp;";
}else{ }else{
return shortnameTo(value, self.emojiTemplate); return shortnameTo(value, self.emojiTemplate);
} }
}, },

View file

@ -1,5 +1,4 @@
<?php <?php
/** /**
* Created by fediplan. * Created by fediplan.
* User: tom79 * User: tom79
@ -11,44 +10,38 @@ namespace App\Controller;
use App\Form\ComposeType; use App\Form\ComposeType;
use App\Form\ConnectMastodonAccountFlow; use App\Form\ConnectMastodonAccountFlow;
use App\Security\MastodonAccount;
use App\Services\Mastodon_api; use App\Services\Mastodon_api;
use App\SocialEntity\Client; use App\SocialEntity\Client;
use App\SocialEntity\Compose; use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use App\SocialEntity\PollOption; use App\SocialEntity\PollOption;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
use Exception;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormError; use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorInterface;
use \Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use \Symfony\Component\HttpFoundation\Response;
class FediPlanController extends AbstractController class FediPlanController extends AbstractController
{ {
#[Route( /**
'/{_locale}', * @Route("/{_locale}",name="index", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
name: 'index', */
requirements: ['_locale' => '%allowed_language%'], public function indexAction(Request $request, AuthorizationCheckerInterface $authorizationChecker, ConnectMastodonAccountFlow $flow, Mastodon_api $mastodon_api, TranslatorInterface $translator, EventDispatcherInterface $eventDispatcher)
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'); $local = $request->getSession()->get('_locale');
return $this->redirect($this->generateUrl('schedule', ['_locale' => $local])); return $this->redirect($this->generateUrl('schedule',['_locale' => $local]));
} }
$client = new Client(); $client = new Client();
$flow->bind($client); $flow->bind($client);
@ -57,27 +50,31 @@ class FediPlanController extends AbstractController
$client_id = null; $client_id = null;
$client_secret = null; $client_secret = null;
if ($flow->isValid($form)) { if ($flow->isValid($form)) {
if ($flow->getCurrentStep() == 1) { if( $flow->getCurrentStep() == 1){
$host = $client->getHost(); $host = $client->getHost();
$result = $mastodon_api->getInstanceNodeInfo($host); $result = $mastodon_api->getInstanceNodeInfo($host);
//We currently only support Mastodon accounts //We currently only support Mastodon accounts
if ($result != "MASTODON" && $result != "PLEROMA") { if( $result != "MASTODON" && $result != "PLEROMA"){
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_only', [], 'fediplan', 'en'))); $form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_only',[],'fediplan','en')));
} else { }else{
$mastodon_api->set_url("https://" . $host); $mastodon_api->set_url("https://" . $host);
$mastodon_api->set_scopes([]); $mastodon_api->set_scopes([]);
$createApp = $mastodon_api->create_app("FediPlan", [], '', "https://plan.fedilab.app"); $createApp = $mastodon_api->create_app("FediPlan", [], '', "https://plan.fedilab.app");
if (isset($createApp['error'])) { if( isset($createApp['error']) ){
$form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_client_id', [], 'fediplan', 'en'))); $form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_client_id',[],'fediplan','en')));
} else { }else{
// form for the next step // form for the next step
$mastodon_api->set_client($createApp['response']['client_id'], $createApp['response']['client_secret']); $mastodon_api->set_client($createApp['response']['client_id'], $createApp['response']['client_secret']);
$urlToMastodon = $mastodon_api->getAuthorizationUrl(); $urlToMastodon = $mastodon_api->getAuthorizationUrl();
$flow->saveCurrentStepData($form); if( isset($createApp['error']) ){
$client_id = $createApp['response']['client_id']; $form->get('host')->addError(new FormError($translator->trans('error.instance.mastodon_oauth_url',[],'fediplan','en')));
$client_secret = $createApp['response']['client_secret']; }else{
$flow->nextStep(); $flow->saveCurrentStepData($form);
$form = $flow->createForm(); $client_id = $createApp['response']['client_id'];
$client_secret = $createApp['response']['client_secret'];
$flow->nextStep();
$form = $flow->createForm();
}
} }
} }
@ -88,34 +85,49 @@ class FediPlanController extends AbstractController
$mastodon_api->set_scopes([]); $mastodon_api->set_scopes([]);
$mastodon_api->set_client($client->getClientId(), $client->getClientSecret()); $mastodon_api->set_client($client->getClientId(), $client->getClientSecret());
$reply = $mastodon_api->loginAuthorization($code); $reply = $mastodon_api->loginAuthorization($code);
if (isset($reply['error'])) { if( isset($reply['error']) ){
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_token', [], 'fediplan', 'en')));
} else { /* $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{
$access_token = $reply['response']['access_token']; $access_token = $reply['response']['access_token'];
$token_type = $reply['response']['token_type']; $token_type = $reply['response']['token_type'];
$mastodon_api->set_url("https://" . $client->getHost()); $mastodon_api->set_url("https://" . $client->getHost());
$mastodon_api->set_token($access_token, $token_type); $mastodon_api->set_token($access_token, $token_type);
$accountReply = $mastodon_api->accounts_verify_credentials(); try {
if (isset($accountReply['error'])) { $accountReply = $mastodon_api->accounts_verify_credentials();
$form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_account', [], 'fediplan', 'en'))); } catch (\ErrorException $e) {
} else { }
$account = $mastodon_api->getSingleAccount($accountReply['response']); if( isset($accountReply['error']) ){
$instanceReply = $mastodon_api->get_instance(); $form->get('code')->addError(new FormError($translator->trans('error.instance.mastodon_account',[],'fediplan','en')));
$instance = $mastodon_api->getInstanceConfiguration($instanceReply['response']); }else{
$session = $request->getSession(); $Account = $mastodon_api->getSingleAccount($accountReply['response']);
$session->set("instance",$instance); $Account->setInstance($host);
$account->setInstance($host); $Account->setToken($token_type ." ".$access_token);
$account->setToken($token_type . " " . $access_token); $token = new UsernamePasswordToken($Account, null, 'main', array('ROLE_USER'));
$token = new UsernamePasswordToken($account, 'main', array('ROLE_USER')); $this->get('security.token_storage')->setToken($token);
try { $event = new InteractiveLoginEvent($request, $token);
$this->container->get('security.token_storage')->setToken($token); $eventDispatcher->dispatch("security.interactive_login", $event);
$event = new InteractiveLoginEvent($request, $token); return $this->redirectToRoute('schedule');
$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')));
}
} }
} }
} }
@ -134,85 +146,80 @@ class FediPlanController extends AbstractController
} }
#[Route( /**
'/{_locale}/schedule', * @Route("/{_locale}/schedule", name="schedule", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
name: 'schedule', */
requirements: ['_locale' => '%allowed_language%'], public function schedule(Request $request, Mastodon_api $mastodon_api)
defaults: ['_locale'=>'en']
)]
public function schedule(Request $request, Mastodon_api $mastodon_api, TranslatorInterface $translator): Response
{ {
$compose = new Compose(); $compose = new Compose();
$pollOption1 = new PollOption(); $pollOption1 = new PollOption();
$pollOption1->setTitle(""); $pollOption1->setTitle("");
$options = $compose->getPollOptions(); $compose->getPollOptions()->add($pollOption1);
$options[] = $pollOption1;
$pollOption2 = new PollOption(); $pollOption2 = new PollOption();
$pollOption2->setTitle(""); $pollOption2->setTitle("");
$options[] = $pollOption2; $compose->getPollOptions()->add($pollOption2);
$compose->setPollOptions($options); $form = $this->createForm(ComposeType::class, $compose, ['user' => $this->getUser()]);
$user = $this->getUser();
$form = $this->createForm(ComposeType::class, $compose, ['user' => $user]);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
/** @var $data Compose */ /** @var $data Compose */
$data = $form->getData(); $data = $form->getData();
/* @var $user MastodonAccount */
$user = $this->getUser();
$mastodon_api->set_url("https://" . $user->getInstance()); $mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" ", $user->getToken())[1];
$token = explode(" " ,$user->getToken())[1];
$type = explode(" ", $user->getToken())[0]; $type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type); $mastodon_api->set_token($token, $type);
$params = []; $params = [];
//Update media description and store their id //Update media description and store their id
foreach ($_POST as $key => $value) { foreach ($_POST as $key => $value){
if ($key != "compose") { if( $key != "compose"){
if (str_contains($key, 'media_id_')) {
if (strpos($key, 'media_id_') !== false){
$mediaId = $value; $mediaId = $value;
$description = $_POST['media_description_' . $mediaId]; $description = $_POST['media_description_'.$mediaId];
//update description if needed //update description if needed
if ($description != null && trim($description) != "") { if( $description != null && trim($description) != ""){
$mastodon_api->update_media($mediaId, ['description' => $description]); try {
$res = $mastodon_api->update_media($mediaId, ['description' => $description]);
} catch (\ErrorException $e) {}
} }
$params['media_ids'][] = $mediaId; $params['media_ids'][] = $mediaId;
} }
} }
} }
//Schedule status //Schedule status
if ($data->getContentWarning()) { if( $data->getContentWarning() ){
$params['spoiler_text'] = $data->getContentWarning(); $params['spoiler_text'] = $data->getContentWarning();
} }
if ($data->getContent()) { if( $data->getContent() ){
$params['status'] = $data->getContent(); $params['status'] = $data->getContent();
} }
if ($data->getVisibility()) { if( $data->getVisibility() ){
$params['visibility'] = $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(); $pollExpiresAt = $data->getPollExpiresAt();
$isPollMultiple = $data->isPollMultiple(); $isPollMultiple = $data->isPollMultiple();
if (count($pollOptions) > 0) { if( count($pollOptions) > 0){
$count_correct_values = 0; $count_correct_values = 0;
foreach ($pollOptions as $po) { foreach($pollOptions as $po) {
/** @var $po PollOption */ /** @var $po PollOption */
if ($po->getTitle() != null && strlen(trim($po->getTitle())) > 0) { if( $po->getTitle() != null && strlen(trim($po->getTitle())) > 0){
$count_correct_values++; $count_correct_values++;
} }
} }
if ($count_correct_values > 1) { if( $count_correct_values > 1 ){
$params['poll']['options'] = []; $params['poll']['options'] = [];
foreach ($pollOptions as $po) { foreach($pollOptions as $po) {
/** @var $po PollOption */ /** @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()); $params['poll']['options'][] = trim($po->getTitle());
} }
} }
@ -221,125 +228,121 @@ class FediPlanController extends AbstractController
} }
} }
try { try {
$date = new DateTime($data->getScheduledAt()->format("Y-m-d H:i"), new DateTimeZone($data->getTimeZone())); $date = new DateTime( $data->getScheduledAt()->format("Y-m-d H:i"), new DateTimeZone($data->getTimeZone()) );
$date->setTimezone(new DateTimeZone("UTC")); $date->setTimezone( new DateTimeZone("UTC"));
$params['scheduled_at'] = $date->format(DateTime::ISO8601); $params['scheduled_at'] = $date->format(DateTime::ISO8601);
} catch (Exception $e) { } catch (\Exception $e) {}
} try {
$response = $mastodon_api->post_statuses($params); $response = $mastodon_api->post_statuses($params);
} catch (\ErrorException $e) {}
$session = $request->getSession(); $session = $request->getSession();
if (isset($response['error'])) { if( isset($response['error']) ){
$session->getFlashBag()->add( $session->getFlashBag()->add(
'Error', 'Error',
$response['error_message'] $response['error_message']
); );
} else { }else{
unset($compose); unset($compose);
unset($form); unset($form);
$compose = new Compose(); $compose = new Compose();
$pollOption1 = new PollOption(); $pollOption1 = new PollOption();
$pollOption1->setTitle(""); $pollOption1->setTitle("");
$options = $compose->getPollOptions(); $compose->getPollOptions()->add($pollOption1);
$options[] = $pollOption1;
$pollOption2 = new PollOption(); $pollOption2 = new PollOption();
$pollOption2->setTitle(""); $pollOption2->setTitle("");
$options[] = $pollOption2; $compose->getPollOptions()->add($pollOption2);
$compose->setPollOptions($options);
$session->getFlashBag()->add( $session->getFlashBag()->add(
'Success', 'Success',
$translator->trans('common.schedule_success', [], 'fediplan', 'en') 'The message has been scheduled'
); );
$form = $this->createForm(ComposeType::class, $compose, ['user' => $this->getUser()]); $form = $this->createForm(ComposeType::class, $compose, ['user' => $this->getUser()]);
} }
} }
/** @var $user MastodonAccount */
$user = $this->getUser(); $user = $this->getUser();
/** @var $user MastodonAccount */
return $this->render("fediplan/schedule.html.twig",[
return $this->render("fediplan/schedule.html.twig", [
'form' => $form->createView(), 'form' => $form->createView(),
'instance' => $user->getInstance(), 'instance' => $user->getInstance(),
'token' => $user->getToken(), 'token' => $user->getToken(),
]); ]);
} }
#[Route(
'/{_locale}/scheduled', /**
name: 'scheduled', * @Route("/{_locale}/scheduled", name="scheduled", defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
requirements: ['_locale' => '%allowed_language%'], */
defaults: ['_locale'=>'en'] public function scheduled()
)]
public function scheduled(): Response
{ {
$user = $this->getUser(); return $this->render("fediplan/scheduled.html.twig");
return $this->render("fediplan/scheduled.html.twig", [ 'instance' => $user->getInstance(),]);
} }
#[Route( /**
'/{_locale}/scheduled/messages/{max_id}', * @Route("/{_locale}/scheduled/messages/{max_id}", options={"expose"=true}, name="load_more")
name: 'load_more', */
options: ['expose' => true] public function loadMoreAction(Mastodon_api $mastodon_api, String $max_id = null){
)]
public function loadMoreAction(Mastodon_api $mastodon_api, ?string $max_id = null , int $limit = 10): JsonResponse
{
/** @var $user MastodonAccount */
$user = $this->getUser(); $user = $this->getUser();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance()); $mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" ", $user->getToken())[1];
$token = explode(" " ,$user->getToken())[1];
$type = explode(" ", $user->getToken())[0]; $type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type); $mastodon_api->set_token($token, $type);
$params = []; $params = [];
$params['limit'] = $limit;
if ($max_id != null) { if( $max_id != null){
$params['max_id'] = $max_id; $params['max_id'] = $max_id;
} }
$scheduled_reply = $mastodon_api->get_scheduled($params); $scheduled_reply = [];
$statuses = $mastodon_api->getScheduledStatuses($scheduled_reply['response'], $user); try {
$data['max_id'] = $statuses[count($statuses)-1]->getId(); $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'];
$data['html'] = $this->renderView('fediplan/Ajax/layout.html.twig', ['statuses' => $statuses]); $data['html'] = $this->renderView('fediplan/Ajax/layout.html.twig', ['statuses' => $statuses]);
return new JsonResponse($data); return new JsonResponse($data);
} }
#[Route( /**
'/{_locale}/scheduled/delete/messages/{id}', * @Route("/{_locale}/scheduled/delete/messages/{id}", options={"expose"=true}, name="delete_message", methods={"POST"}, defaults={"_locale"="en"}, requirements={"_locale": "%allowed_language%"})
name: 'delete_message', */
requirements: ['_locale' => '%allowed_language%'], public function deleteMessage(Mastodon_api $mastodon_api, String $id = null){
options: ['expose' => true],
defaults: ['_locale'=>'en'],
methods: ['POST']
)]
public function deleteMessage(Mastodon_api $mastodon_api, ?string $id = null): JsonResponse
{
$user = $this->getUser(); $user = $this->getUser();
/** @var $mastodon_api Mastodon_api */
$mastodon_api->set_url("https://" . $user->getInstance()); $mastodon_api->set_url("https://" . $user->getInstance());
$token = explode(" ", $user->getToken())[1]; $token = explode(" " ,$user->getToken())[1];
$type = explode(" ", $user->getToken())[0]; $type = explode(" ", $user->getToken())[0];
$mastodon_api->set_token($token, $type); $mastodon_api->set_token($token, $type);
$response = $mastodon_api->delete_scheduled($id); $response = [];
try {
$response = $mastodon_api->delete_scheduled($id);
} catch (\ErrorException $e) {}
return new JsonResponse($response); return new JsonResponse($response);
} }
/**
#[Route( * @Route("/about",defaults={"_locale"="en"})
'/{_locale}/about', * @Route("/{_locale}/about", name="about", defaults={"_locale":"en"}, requirements={"_locale": "%allowed_language%"})
name: 'about', */
requirements: ['_locale' => '%allowed_language%'], public function about()
defaults: ['_locale'=>'en']
)]
public function about(): Response
{ {
return $this->render("fediplan/about.html.twig"); return $this->render("fediplan/about.html.twig");
} }
#[Route( /**
'/logout', * @Route("/logout", name="logout")
name: 'logout' */
)] public function logout()
public function logout(): Response
{ {
return $this->render("fediplan/index.html.twig"); return $this->render("fediplan/index.html.twig");
} }

View file

@ -15,22 +15,14 @@ use Symfony\Component\HttpKernel\KernelEvents;
class LocaleSubscriber implements EventSubscriberInterface class LocaleSubscriber implements EventSubscriberInterface
{ {
private string $defaultLocale; private $defaultLocale;
public function __construct($defaultLocale = 'en') public function __construct($defaultLocale = 'en')
{ {
$this->defaultLocale = $defaultLocale; $this->defaultLocale = $defaultLocale;
} }
public static function getSubscribedEvents(): array public function onKernelRequest(RequestEvent $event)
{
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(); $request = $event->getRequest();
if (!$request->hasPreviousSession()) { if (!$request->hasPreviousSession()) {
@ -45,4 +37,12 @@ class LocaleSubscriber implements EventSubscriberInterface
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale)); $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,5 +1,4 @@
<?php /** @noinspection PhpTranslationKeyInspection */ <?php
/** /**
* Created by fediplan. * Created by fediplan.
* User: tom79 * User: tom79
@ -10,29 +9,27 @@
namespace App\Form; namespace App\Form;
use App\Security\MastodonAccount;
use App\SocialEntity\Compose; use App\SocialEntity\Compose;
use App\SocialEntity\MastodonAccount;
use DateTime; use DateTime;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType; 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\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType; 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\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TimezoneType; use Symfony\Component\Form\Extension\Core\Type\TimezoneType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Translator;
class ComposeType extends AbstractType class ComposeType extends AbstractType {
{
private Security $securityContext; private $securityContext;
private $translator; private $translator;
public function __construct(Security $securityContext, Translator $translator) public function __construct(Security $securityContext, Translator $translator)
@ -41,19 +38,19 @@ class ComposeType extends AbstractType
$this->translator = $translator; $this->translator = $translator;
} }
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options)
{ {
/**@var $user MastodonAccount */ /**@var $user MastodonAccount*/
$user = $options['user']; $user = $options['user'];
if ($user->getDefaultSensitivity()) { if( $user->getDefaultSensitivity()) {
$checkbox = [ $checkbox = [
'required' => false, 'required' => false,
'attr' => ['checked' => 'checked'], 'attr' => ['checked' => 'checked'],
'label' => 'page.schedule.form.sensitive', 'label' => 'page.schedule.form.sensitive',
'translation_domain' => 'fediplan' 'translation_domain' => 'fediplan'
]; ];
} else { }else {
$checkbox = ['required' => false, 'label' => 'page.schedule.form.sensitive', $checkbox = ['required' => false, 'label' => 'page.schedule.form.sensitive',
'translation_domain' => 'fediplan']; 'translation_domain' => 'fediplan'];
} }
@ -77,14 +74,12 @@ class ComposeType extends AbstractType
'data' => $user->getDefaultVisibility(), 'data' => $user->getDefaultVisibility(),
'label' => 'page.schedule.form.visibility', 'label' => 'page.schedule.form.visibility',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
$builder->add('attach_poll', HiddenType::class, ['required' => true, 'empty_data' => 0]);
$builder->add('timeZone', TimezoneType::class, $builder->add('timeZone', TimezoneType::class,
[ [
'label' => 'page.schedule.form.timeZone', 'label' => 'page.schedule.form.timeZone',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
$builder->add('sensitive', CheckboxType::class, $checkbox); $builder->add('sensitive', CheckboxType::class, $checkbox);
$builder->add('scheduled_at', DateTimeType::class, [ $builder->add('scheduled_at', DateTimeType::class,[
'widget' => 'single_text', 'widget' => 'single_text',
"data" => new DateTime(), "data" => new DateTime(),
'label' => 'page.schedule.form.scheduled_at', 'label' => 'page.schedule.form.scheduled_at',
@ -92,7 +87,7 @@ class ComposeType extends AbstractType
$builder->add('poll_options', CollectionType::class, $builder->add('poll_options', CollectionType::class,
[ [
'entry_type' => PollOptionType::class, 'entry_type' => PollOptionType::class,
'by_reference' => false, 'by_reference' => false,
'allow_add' => true, 'allow_add' => true,
'prototype' => true, 'prototype' => true,
@ -106,16 +101,16 @@ class ComposeType extends AbstractType
$builder->add('poll_expires_at', ChoiceType::class, $builder->add('poll_expires_at', ChoiceType::class,
[ [
'choices' => [ 'choices' => [
$this->translator->trans('poll.duration_m', ['minutes' => 5], 'fediplan') => 5 * 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_m', ['minutes' => 30], 'fediplan') => 30*60,
$this->translator->trans('poll.duration_h', ['hours' => 1], 'fediplan') => 60 * 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_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' => 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' => 3], 'fediplan') => 3*24*60*60,
$this->translator->trans('poll.duration_d', ['days' => 7], 'fediplan') => 7 * 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, 'required' => false,
'label' => 'page.schedule.form.end_in', 'label' => 'page.schedule.form.end_in',
'translation_domain' => 'fediplan']); 'translation_domain' => 'fediplan']);
@ -126,7 +121,7 @@ class ComposeType extends AbstractType
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'data_class' => Compose::class, 'data_class' => Compose::class,

View file

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

View file

@ -7,22 +7,19 @@
*/ */
namespace App\Form; namespace App\Form;
use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\OptionsResolver\OptionsResolver;
class ConnectMastodonAccountType extends AbstractType class ConnectMastodonAccountType extends AbstractType {
{
public function buildForm(FormBuilderInterface $builder, array $options): void public function buildForm(FormBuilderInterface $builder, array $options) {
{
switch ($options['flow_step']) { switch ($options['flow_step']) {
case 1: case 1:
$builder->add('host', TextType::class, [ $builder->add('host', TextType::class, [
'label' => 'page.index.form.instance', 'label' => 'page.index.form.instance',
'translation_domain' => 'fediplan' 'translation_domain' => 'fediplan'
]); ]);
break; break;
@ -38,12 +35,11 @@ class ConnectMastodonAccountType extends AbstractType
} }
} }
public function getBlockPrefix(): string public function getBlockPrefix() {
{
return 'addMastodonAccount'; return 'addMastodonAccount';
} }
public function configureOptions(OptionsResolver $resolver): void public function configureOptions(OptionsResolver $resolver)
{ {
$resolver->setDefaults([ $resolver->setDefaults([
'validation_groups' => ['registration'], 'validation_groups' => ['registration'],

View file

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

View file

@ -3,9 +3,51 @@
namespace App; namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait; 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\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
class Kernel extends BaseKernel class Kernel extends BaseKernel
{ {
use MicroKernelTrait; 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,79 +0,0 @@
<?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,10 +1,7 @@
<?php /** @noinspection PhpUnused */ <?php
namespace App\Services; namespace App\Services;
use CurlFile;
use ErrorException;
/** /**
* An object-oriented wrapper of the PHP cURL extension. * An object-oriented wrapper of the PHP cURL extension.
* *
@ -70,75 +67,91 @@ class Curl
* @var string The user agent name which is set when making a request * @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)'; 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 * @var resource Contains the curl resource created by `curl_init()` function
*/ */
public $curl; public $curl;
/** /**
* @var bool Whether an error occured or not * @var bool Whether an error occured or not
*/ */
public $error = false; public $error = false;
/** /**
* @var int Contains the error code of the curren request, 0 means no error happend * @var int Contains the error code of the curren request, 0 means no error happend
*/ */
public $error_code = 0; public $error_code = 0;
/** /**
* @var string If the curl request failed, the error message is contained * @var string If the curl request failed, the error message is contained
*/ */
public $error_message = null; public $error_message = null;
/** /**
* @var bool Whether an error occured or not * @var bool Whether an error occured or not
*/ */
public $curl_error = false; public $curl_error = false;
/** /**
* @var int Contains the error code of the curren request, 0 means no error happend. * @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 * @see https://curl.haxx.se/libcurl/c/libcurl-errors.html
*/ */
public $curl_error_code = 0; public $curl_error_code = 0;
/** /**
* @var string If the curl request failed, the error message is contained * @var string If the curl request failed, the error message is contained
*/ */
public $curl_error_message = null; public $curl_error_message = null;
/** /**
* @var bool Whether an error occured or not * @var bool Whether an error occured or not
*/ */
public $http_error = false; public $http_error = false;
/** /**
* @var int Contains the status code of the current processed request. * @var int Contains the status code of the current processed request.
*/ */
public $http_status_code = 0; public $http_status_code = 0;
/** /**
* @var string If the curl request failed, the error message is contained * @var string If the curl request failed, the error message is contained
*/ */
public $http_error_message = null; public $http_error_message = null;
/** /**
* @var string|array TBD (ensure type) Contains the request header informations * @var string|array TBD (ensure type) Contains the request header informations
*/ */
public $request_headers = null; public $request_headers = null;
/** /**
* @var string|array TBD (ensure type) Contains the response header informations * @var string|array TBD (ensure type) Contains the response header informations
*/ */
public $response_headers = array(); public $response_headers = array();
/** /**
* @var string Contains the response from the curl request * @var string Contains the response from the curl request
*/ */
public $response = null; public $response = null;
/** /**
* @var bool Whether the current section of response headers is after 'HTTP/1.1 100 Continue' * @var bool Whether the current section of response headers is after 'HTTP/1.1 100 Continue'
*/ */
protected $response_header_continue = false; protected $response_header_continue = false;
private $_cookies = array();
private $_headers = array();
/** /**
* Constructor ensures the available curl extension is loaded. * Constructor ensures the available curl extension is loaded.
* *
* @throws ErrorException * @throws \ErrorException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('curl')) { 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(); $this->init();
@ -163,44 +176,6 @@ class Curl
return $this; 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 * Handle writing the response headers
* *
@ -208,7 +183,6 @@ class Curl
* @param string $header_line A line from the list of response headers * @param string $header_line A line from the list of response headers
* *
* @return int Returns the length of the $header_line * @return int Returns the length of the $header_line
* @noinspection PhpUnusedParameterInspection
*/ */
public function addResponseHeaderLine($curl, $header_line) public function addResponseHeaderLine($curl, $header_line)
{ {
@ -221,19 +195,11 @@ class Curl
} elseif (!$this->response_header_continue) { } elseif (!$this->response_header_continue) {
$this->response_headers[] = $trimmed_header; $this->response_headers[] = $trimmed_header;
} }
return strlen($header_line); 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. * Execute the curl request based on the respectiv settings.
@ -258,133 +224,30 @@ class Curl
return $this->error_code; return $this->error_code;
} }
// functions
/** /**
* Make a get request with optional data. * @param array|object|string $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()) protected function preparePayload($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;
}
/**
* 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_POST, true);
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'POST');
$this->setOpt(CURLOPT_POSTFIELDS, $payload);
$this->exec();
return $this;
}
if (is_array($data) || is_object($data)) {
$skip = false;
foreach ($data as $key => $value) {
// If a value is an instance of CurlFile skip the http_build_query
// see issue https://github.com/php-mod/curl/issues/46
// suggestion from: https://stackoverflow.com/a/36603038/4611030
if ($value instanceof \CurlFile) {
$skip = true;
}
}
if (!$skip) {
$data = http_build_query($data);
}
}
/** $this->setOpt(CURLOPT_POSTFIELDS, $data);
* Make a put request with optional data.
*
* 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;
} }
/** /**
@ -406,6 +269,164 @@ class Curl
$this->setOpt(CURLOPT_HTTPAUTH, $httpauth); $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. * Provide optional header informations.
* *
@ -417,20 +438,38 @@ class Curl
* $curl->get('http://example.com/request.php'); * $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 * @param string $value The value for the given header key
* @return self * @return self
*/ */
public function setHeader($key, $value) public function setHeader($key, $value)
{ {
$this->_headers[$key] = $key . ': ' . $value; $this->_headers[$key] = $key.': '.$value;
$this->setOpt(CURLOPT_HTTPHEADER, array_values($this->_headers)); $this->setOpt(CURLOPT_HTTPHEADER, array_values($this->_headers));
return $this; return $this;
} }
/** /**
* @param $referrer * Provide a User Agent.
* @return Curl *
* 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;
}
/**
* @deprecated Call setReferer() instead * @deprecated Call setReferer() instead
*/ */
public function setReferrer($referrer) public function setReferrer($referrer)
@ -456,7 +495,7 @@ class Curl
/** /**
* Set contents of HTTP Cookie header. * 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 * @param string $value The value for the provided cookie name
* @return self * @return self
*/ */
@ -468,36 +507,53 @@ class Curl
} }
/** /**
* Return the endpoint set for curl * Set customized curl options.
* *
* @see http://php.net/curl_getinfo * To see a full list of options: http://php.net/curl_setopt
* *
* @return string of endpoint * @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
*/ */
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() public function getEndpoint()
{ {
return $this->getOpt(CURLINFO_EFFECTIVE_URL); 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. * Enable verbositiy.
* *
* @param bool $on * @todo As to keep naming convention it should be renamed to `setVerbose()`
*
* @param string $on
* @return self * @return self
*/ */
public function verbose($on = true) public function verbose($on = true)
@ -606,7 +662,7 @@ class Curl
{ {
return $this->http_status_code >= 500 && $this->http_status_code < 600; 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. * Get a specific response header key or all values from the response headers array.
* *
@ -627,27 +683,27 @@ class Curl
* ``` * ```
* *
* @param string $headerKey Optional key to get from the array. * @param string $headerKey Optional key to get from the array.
* @return array * @return bool|string
* @since 1.9 * @since 1.9
*/ */
public function getResponseHeaders($headerKey = null) public function getResponseHeaders($headerKey = null)
{ {
$headers = array(); $headers = array();
$headerKey = strtolower($headerKey); $headerKey = strtolower($headerKey);
foreach ($this->response_headers as $header) { foreach ($this->response_headers as $header) {
$parts = explode(":", $header, 2); $parts = explode(":", $header, 2);
$key = isset($parts[0]) ? $parts[0] : null; $key = isset($parts[0]) ? $parts[0] : null;
$value = isset($parts[1]) ? $parts[1] : null; $value = isset($parts[1]) ? $parts[1] : null;
$headers[trim(strtolower($key))] = trim($value); $headers[trim(strtolower($key))] = trim($value);
} }
if ($headerKey) { if ($headerKey) {
return isset($headers[$headerKey]) ? $headers[$headerKey] : []; return isset($headers[$headerKey]) ? $headers[$headerKey] : false;
} }
return $headers; return $headers;
} }

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,225 +0,0 @@
<?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

@ -1,68 +1,70 @@
<?php <?php
namespace App\Security; namespace App\SocialEntity;
use App\SocialEntity\Client;
use App\SocialEntity\CustomField;
use App\SocialEntity\Emoji;
use Symfony\Component\Security\Core\User\UserInterface;
class MastodonAccount implements UserInterface use DateTimeInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
class MastodonAccount
{ {
private string $acct;
private string $id;
private string $account_id; private $id;
private string $username; private $account_id;
private string $display_name; private $username;
private bool $locked; private $acct;
private \DateTime $created_at; private $display_name;
private int $followers_count; private $locked;
private int $following_count; private $created_at;
private int $statuses_count; private $followers_count;
private string $note; private $following_count;
private string $url; private $statuses_count;
private string $avatar; private $note;
private string $avatar_static; private $url;
private string $header; private $avatar;
private string $header_static; private $avatar_static;
private MastodonAccount $moved; private $header;
private bool $bot; private $header_static;
private string $instance; private $moved;
private Client $client; private $bot;
private string $token; private $instance;
private array $Fields; private $client;
/** @var Emoji[] */
private array $Emojis;
private string $default_sensitivity; private $token;
private string $default_visibility; private $Fields;
private $Emojis;
private $default_sensitivity;
private $default_visibility;
public function __construct() public function __construct()
{ {
$this->Fields = array(); $this->Fields = new ArrayCollection();
$this->Emojis = array(); $this->Emojis = new ArrayCollection();
} }
@ -119,12 +121,12 @@ class MastodonAccount implements UserInterface
return $this; return $this;
} }
public function getCreatedAt(): ?\DateTime public function getCreatedAt(): ?DateTimeInterface
{ {
return $this->created_at; return $this->created_at;
} }
public function setCreatedAt(\DateTime $created_at): self public function setCreatedAt(DateTimeInterface $created_at): self
{ {
$this->created_at = $created_at; $this->created_at = $created_at;
@ -322,52 +324,59 @@ class MastodonAccount implements UserInterface
return $this; return $this;
} }
/**
public function getFields(): array * @return Collection|CustomField[]
*/
public function getFields(): Collection
{ {
return $this->Fields; return $this->Fields;
} }
public function addField(CustomField $field): self public function addField(CustomField $field): self
{ {
if (in_array($field, $this->Fields) === false) { if (!$this->Fields->contains($field)) {
$this->Fields[] = $field; $this->Fields[] = $field;
$field->setMastodonAccount($this); $field->setMastodonAccount($this);
} }
return $this; return $this;
} }
public function removeField(CustomField $field): self public function removeField(CustomField $field): self
{ {
if ($this->Fields->contains($field)) {
if (($key = array_search($field, $this->Fields)) !== false) { $this->Fields->removeElement($field);
unset($this->Fields[$key]);
// set the owning side to null (unless already changed) // set the owning side to null (unless already changed)
if ($field->getMastodonAccount() === $this) { if ($field->getMastodonAccount() === $this) {
$field->setMastodonAccount(null); $field->setMastodonAccount(null);
} }
} }
return $this; return $this;
} }
public function getEmojis(): array /**
* @return Collection|Emoji[]
*/
public function getEmojis(): Collection
{ {
return $this->Emojis; return $this->Emojis;
} }
public function addEmoji(Emoji $emoji): self public function addEmoji(Emoji $emoji): self
{ {
if (in_array($emoji, $this->Emojis) === false) { if (!$this->Emojis->contains($emoji)) {
$this->Emojis[] = $emoji; $this->Emojis[] = $emoji;
$emoji->setMastodonAccount($this); $emoji->setMastodonAccount($this);
} }
return $this; return $this;
} }
public function removeEmoji(Emoji $emoji): self public function removeEmoji(Emoji $emoji): self
{ {
if (($key = array_search($emoji, $this->Emojis)) !== false) { if ($this->Emojis->contains($emoji)) {
unset($this->Emojis[$key]); $this->Emojis->removeElement($emoji);
// set the owning side to null (unless already changed) // set the owning side to null (unless already changed)
if ($emoji->getMastodonAccount() === $this) { if ($emoji->getMastodonAccount() === $this) {
$emoji->setMastodonAccount(null); $emoji->setMastodonAccount(null);
@ -381,7 +390,7 @@ class MastodonAccount implements UserInterface
/** /**
* @return mixed * @return mixed
*/ */
public function getDefaultSensitivity(): mixed public function getDefaultSensitivity()
{ {
return $this->default_sensitivity; return $this->default_sensitivity;
} }
@ -409,52 +418,6 @@ class MastodonAccount implements UserInterface
{ {
$this->default_visibility = $default_visibility; $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

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

View file

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

View file

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

View file

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

View file

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

View file

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

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