Compare commits
1 commit
develop
...
feature/ci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2372894fd |
127 changed files with 9999 additions and 11733 deletions
7
.env
7
.env
|
|
@ -35,10 +35,3 @@ MAILER_SENDER=example@localhost
|
||||||
ASSET_BASE_URL=null
|
ASSET_BASE_URL=null
|
||||||
UMAMI_URL=null
|
UMAMI_URL=null
|
||||||
|
|
||||||
MESSENGER_TRANSPORT_DSN=doctrine://default
|
|
||||||
|
|
||||||
INFLUXDB_URL=
|
|
||||||
INFLUXDB_TOKEN=
|
|
||||||
INFLUXDB_BUCKET=
|
|
||||||
INFLUXDB_ORG=
|
|
||||||
INFLUXDB_DEBUG=1
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ magephp:
|
||||||
- "/var/cache/*"
|
- "/var/cache/*"
|
||||||
- "/var/log/*"
|
- "/var/log/*"
|
||||||
- "/public/media"
|
- "/public/media"
|
||||||
- "/.secrets"
|
|
||||||
hosts:
|
hosts:
|
||||||
- ssh_host
|
- ssh_host
|
||||||
on-deploy:
|
on-deploy:
|
||||||
|
|
@ -26,4 +25,3 @@ magephp:
|
||||||
- exec: { cmd: 'make doctrine-migration', desc: 'migration' }
|
- exec: { cmd: 'make doctrine-migration', desc: 'migration' }
|
||||||
- exec: { cmd: 'php8.1 ./bin/console cache:warmup', desc: 'warmup' }
|
- exec: { cmd: 'php8.1 ./bin/console cache:warmup', desc: 'warmup' }
|
||||||
- exec: { cmd: 'php8.1 ./bin/console cache:warmup', desc: 'warmup2' }
|
- exec: { cmd: 'php8.1 ./bin/console cache:warmup', desc: 'warmup2' }
|
||||||
- exec: { cmd: './bin/messenger -a restart', desc: 'messenger' }
|
|
||||||
|
|
|
||||||
39
.novops.yml
39
.novops.yml
|
|
@ -1,39 +0,0 @@
|
||||||
environments:
|
|
||||||
build:
|
|
||||||
variables:
|
|
||||||
- name: MYSQLDUMP
|
|
||||||
value:
|
|
||||||
hvault_kv2:
|
|
||||||
mount: kv
|
|
||||||
path: deblan/deblan.io-murph
|
|
||||||
key: mysqldump
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
variables:
|
|
||||||
- name: SSH_USER
|
|
||||||
value:
|
|
||||||
hvault_kv2:
|
|
||||||
mount: kv
|
|
||||||
path: deblan/deblan.io-murph
|
|
||||||
key: ssh_user
|
|
||||||
|
|
||||||
- name: SSH_HOST
|
|
||||||
value:
|
|
||||||
hvault_kv2:
|
|
||||||
mount: kv
|
|
||||||
path: deblan/deblan.io-murph
|
|
||||||
key: ssh_host
|
|
||||||
|
|
||||||
- name: SSH_PRIV_KEY
|
|
||||||
value:
|
|
||||||
hvault_kv2:
|
|
||||||
mount: kv
|
|
||||||
path: deblan/deblan.io-murph
|
|
||||||
key: ssh_priv_key
|
|
||||||
|
|
||||||
- name: APP_DIRECTORY
|
|
||||||
value:
|
|
||||||
hvault_kv2:
|
|
||||||
mount: kv
|
|
||||||
path: deblan/deblan.io-murph
|
|
||||||
key: app_directory
|
|
||||||
118
.woodpecker.yml
Normal file
118
.woodpecker.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
pipeline:
|
||||||
|
db-wait:
|
||||||
|
image: gitnet.fr/deblan/timeout:latest
|
||||||
|
commands:
|
||||||
|
- /bin/timeout -t 30 -v -c 'while true; do nc -z -v db 3306 2>&1 | grep succeeded && exit 0; sleep 0.5; done'
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
db-create:
|
||||||
|
image: mariadb:10.3
|
||||||
|
secrets: [mysqldump]
|
||||||
|
commands:
|
||||||
|
- mysql -hdb -uroot -proot -e "CREATE DATABASE app"
|
||||||
|
- eval "$MYSQLDUMP" | mysql -hdb -uroot -proot app
|
||||||
|
when:
|
||||||
|
branch: [master, master-*, develop, develop-*]
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
app-config:
|
||||||
|
image: deblan/php:8.1
|
||||||
|
commands:
|
||||||
|
- echo APP_ENV=prod >> .env.local
|
||||||
|
- echo APP_SECRET=$(openssl rand -hex 32) >> .env.local
|
||||||
|
- echo DATABASE_URL=mysql://root:root@db/app >> .env.local
|
||||||
|
when:
|
||||||
|
branch: [master, master-*, develop, develop-*]
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
php-composer:
|
||||||
|
image: deblan/php:8.1
|
||||||
|
commands:
|
||||||
|
- apt-get update && apt-get -y install git
|
||||||
|
- composer install --no-scripts
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
db-migrate:
|
||||||
|
image: deblan/php:8.1
|
||||||
|
environment:
|
||||||
|
- PHP=php
|
||||||
|
commands:
|
||||||
|
- ./bin/doctrine-migrate
|
||||||
|
when:
|
||||||
|
branch: [master, master-*, develop, develop-*, feature/*]
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
app-jsroutes:
|
||||||
|
image: deblan/php:8.1
|
||||||
|
commands:
|
||||||
|
- php bin/console fos:js-routing:dump --format=json --target=public/js/fos_js_routes.json
|
||||||
|
when:
|
||||||
|
branch: [master, master-*, develop, develop-*, feature/*]
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
node-build:
|
||||||
|
image: node:16-alpine
|
||||||
|
environment:
|
||||||
|
- CPU_COUNT=3
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache git
|
||||||
|
- npm install -g svg2ttf ttf2eot ttf2woff2
|
||||||
|
- svg2ttf assets/fonts/deblan/src/deblan-icon.svg assets/fonts/deblan/deblan-icon.ttf
|
||||||
|
- cat assets/fonts/deblan/deblan-icon.ttf | ttf2woff2 > assets/fonts/deblan/deblan-icon.woff2
|
||||||
|
- ttf2eot assets/fonts/deblan/deblan-icon.ttf assets/fonts/deblan/deblan-icon.eot
|
||||||
|
- yarn
|
||||||
|
- test -f public/js/fos_js_routes.json || echo "{}" > public/js/fos_js_routes.json
|
||||||
|
- npm run build
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
security-check:
|
||||||
|
image: gitnet.fr/deblan/osv-detector:v0.9
|
||||||
|
commands:
|
||||||
|
- osv-detector composer.lock yarn.lock
|
||||||
|
failure: ignore
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
create-artifact:
|
||||||
|
image: alpine:latest
|
||||||
|
volumes:
|
||||||
|
- /var/www/html/artifacts:/var/www/html/artifacts
|
||||||
|
commands:
|
||||||
|
- ARTIFACT_PATH="/var/www/html/artifacts/deblan/deblan.io-murph/$CI_COMMIT_BRANCH"
|
||||||
|
- test -d && rm -fr "$ARTIFACT_PATH"
|
||||||
|
- mkdir -p "$ARTIFACT_PATH"
|
||||||
|
- cp -R * .* "$ARTIFACT_PATH"
|
||||||
|
when:
|
||||||
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
app-deploy:
|
||||||
|
image: deblan/php:8.1
|
||||||
|
secrets: [ssh_user, ssh_host, ssh_priv_key, app_directory]
|
||||||
|
volumes:
|
||||||
|
- /var/www/html/artifacts:/var/www/html/artifacts
|
||||||
|
commands:
|
||||||
|
- cd "/var/www/html/artifacts/deblan/deblan.io-murph/$CI_COMMIT_BRANCH"
|
||||||
|
- ls -lah
|
||||||
|
- exit 1
|
||||||
|
- apt-get update && apt-get -y install rsync openssh-client
|
||||||
|
- mkdir "$HOME/.ssh"
|
||||||
|
- echo "$SSH_PRIV_KEY" > "$HOME/.ssh/id_ed25519"
|
||||||
|
- chmod 700 "$HOME/.ssh"
|
||||||
|
- chmod 600 "$HOME/.ssh/id_ed25519"
|
||||||
|
- composer global require andres-montanez/magallanes
|
||||||
|
- cp .mage.yml.dist .mage.yml
|
||||||
|
- sed -i "s/ssh_user/$SSH_USER/g" .mage.yml
|
||||||
|
- sed -i "s/ssh_host/$SSH_HOST/g" .mage.yml
|
||||||
|
- sed -i "s#app_directory#$APP_DIRECTORY#g" .mage.yml
|
||||||
|
- /root/.config/composer/vendor/bin/mage deploy "$CI_BUILD_DEPLOY_TARGET"
|
||||||
|
when:
|
||||||
|
event: [deployment]
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mariadb:10.3
|
||||||
|
environment:
|
||||||
|
- MARIADB_ROOT_PASSWORD=root
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
variables:
|
|
||||||
volumes: &volumes
|
|
||||||
- node_cache:/root/.npm
|
|
||||||
- /data/${CI_REPO}:/builds
|
|
||||||
|
|
||||||
when:
|
|
||||||
event: [push, pull_request, tag, manual]
|
|
||||||
branch: [master, master-*, develop, develop-*, feature/*]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
"Wait the database":
|
|
||||||
image: gitnet.fr/deblan/timeout:latest
|
|
||||||
commands:
|
|
||||||
- /bin/timeout -t 30 -v -c 'while true; do nc -z -v db 3306 2>&1 | grep succeeded && exit 0; sleep 0.5; done'
|
|
||||||
|
|
||||||
"Create database":
|
|
||||||
image: mariadb:10.3
|
|
||||||
environment:
|
|
||||||
MYSQLDUMP:
|
|
||||||
from_secret: mysqldump
|
|
||||||
commands:
|
|
||||||
- mysql -hdb -uroot -proot -e "CREATE DATABASE app"
|
|
||||||
- eval "$MYSQLDUMP" | mysql -hdb -uroot -proot app
|
|
||||||
|
|
||||||
"Configure app":
|
|
||||||
image: deblan/php:8.1
|
|
||||||
commands:
|
|
||||||
- echo APP_ENV=prod >> .env.local
|
|
||||||
- echo APP_SECRET=$(openssl rand -hex 32) >> .env.local
|
|
||||||
- echo DATABASE_URL=mysql://root:root@db/app >> .env.local
|
|
||||||
|
|
||||||
"Installs PHP dependencies":
|
|
||||||
image: deblan/php:8.1
|
|
||||||
commands:
|
|
||||||
- apt-get update && apt-get -y install git
|
|
||||||
- composer install --no-scripts
|
|
||||||
|
|
||||||
"Migrates database":
|
|
||||||
image: deblan/php:8.3
|
|
||||||
environment:
|
|
||||||
PHP: php
|
|
||||||
commands:
|
|
||||||
- ./bin/doctrine-migrate
|
|
||||||
|
|
||||||
"Generates JS routes":
|
|
||||||
image: deblan/php:8.3
|
|
||||||
commands:
|
|
||||||
- php bin/console fos:js-routing:dump --format=json --target=public/js/fos_js_routes.json
|
|
||||||
|
|
||||||
"Build assets":
|
|
||||||
image: node:20-alpine
|
|
||||||
environment:
|
|
||||||
CPU_COUNT: 3
|
|
||||||
commands:
|
|
||||||
- apk add --no-cache git
|
|
||||||
- npm install -g svg2ttf ttf2eot ttf2woff2
|
|
||||||
- svg2ttf assets/fonts/deblan/src/deblan-icon.svg assets/fonts/deblan/deblan-icon.ttf
|
|
||||||
- cat assets/fonts/deblan/deblan-icon.ttf | ttf2woff2 > assets/fonts/deblan/deblan-icon.woff2
|
|
||||||
- ttf2eot assets/fonts/deblan/deblan-icon.ttf assets/fonts/deblan/deblan-icon.eot
|
|
||||||
- yarn
|
|
||||||
- test -f public/js/fos_js_routes.json || echo "{}" > public/js/fos_js_routes.json
|
|
||||||
- npm run build
|
|
||||||
|
|
||||||
"Check dependencies":
|
|
||||||
image: gitnet.fr/deblan/osv-detector:v0.9
|
|
||||||
commands:
|
|
||||||
- osv-detector composer.lock yarn.lock
|
|
||||||
failure: ignore
|
|
||||||
|
|
||||||
"Build the cache":
|
|
||||||
image: deblan/mage
|
|
||||||
volumes: *volumes
|
|
||||||
commands:
|
|
||||||
- cd /builds
|
|
||||||
- rsync -az "$CI_WORKSPACE/" "$CI_COMMIT_SHA"
|
|
||||||
|
|
||||||
services:
|
|
||||||
db:
|
|
||||||
image: mariadb:10.3
|
|
||||||
environment:
|
|
||||||
MARIADB_ROOT_PASSWORD: root
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
node_cache:
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
variables:
|
|
||||||
volumes: &volumes
|
|
||||||
- /data/${CI_REPO}:/builds
|
|
||||||
|
|
||||||
when:
|
|
||||||
event: [deployment]
|
|
||||||
|
|
||||||
skip_clone: true
|
|
||||||
|
|
||||||
steps:
|
|
||||||
"Deploy":
|
|
||||||
image: deblan/mage
|
|
||||||
environment:
|
|
||||||
SSH_PRIV_KEY:
|
|
||||||
from_secret: ssh_priv_key
|
|
||||||
SSH_USER:
|
|
||||||
from_secret: ssh_user
|
|
||||||
SSH_HOST:
|
|
||||||
from_secret: ssh_host
|
|
||||||
APP_DIRECTORY:
|
|
||||||
from_secret: app_directory
|
|
||||||
volumes: *volumes
|
|
||||||
commands:
|
|
||||||
- cd "/builds/$CI_COMMIT_SHA"
|
|
||||||
- mkdir "$HOME/.ssh"
|
|
||||||
- echo "$SSH_PRIV_KEY" > "$HOME/.ssh/id_ed25519"
|
|
||||||
- chmod 700 "$HOME/.ssh"
|
|
||||||
- chmod 600 "$HOME/.ssh/id_ed25519"
|
|
||||||
- cp .mage.yml.dist .mage.yml
|
|
||||||
- sed -i "s/ssh_user/$SSH_USER/g" .mage.yml
|
|
||||||
- sed -i "s/ssh_host/$SSH_HOST/g" .mage.yml
|
|
||||||
- sed -i "s#app_directory#$APP_DIRECTORY#g" .mage.yml
|
|
||||||
- mage deploy "$CI_PIPELINE_DEPLOY_TARGET"
|
|
||||||
9
Makefile
9
Makefile
|
|
@ -1,6 +1,7 @@
|
||||||
COMPOSER ?= composer
|
COMPOSER ?= composer
|
||||||
PHP_BIN ?= php8.1
|
PHP ?= php8.1
|
||||||
YARN_BIN ?= yarn
|
SSH ?= ssh
|
||||||
|
YARN ?= yarn
|
||||||
NPM_BIN ?= npm
|
NPM_BIN ?= npm
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
@ -19,10 +20,10 @@ js-routing: doctrine-migration
|
||||||
clean:
|
clean:
|
||||||
rm -fr var/cache/dev/*
|
rm -fr var/cache/dev/*
|
||||||
rm -fr var/cache/prod/*
|
rm -fr var/cache/prod/*
|
||||||
$(PHP_BIN) bin/console
|
$(PHP) bin/console
|
||||||
|
|
||||||
doctrine-migration:
|
doctrine-migration:
|
||||||
PHP=$(PHP_BIN) ./bin/doctrine-migrate
|
PHP=$(PHP) ./bin/doctrine-migrate
|
||||||
|
|
||||||
.ONESHELL:
|
.ONESHELL:
|
||||||
lint:
|
lint:
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,57 @@
|
||||||
@import "../../vendor/murph/murph-core/src/core/Resources/assets/css/admin.scss";
|
@import "../../vendor/murph/murph-core/src/core/Resources/assets/css/admin.scss";
|
||||||
@import "@kangc/v-md-editor/lib/style/base-editor.css";
|
@import "~simplemde/dist/simplemde.min.css";
|
||||||
@import "@kangc/v-md-editor/lib/theme/style/vuepress.css";
|
|
||||||
|
.CodeMirror-fullscreen, .editor-toolbar.fullscreen {
|
||||||
|
z-index: 2000;
|
||||||
|
}
|
||||||
|
|
||||||
.ejs-link {
|
.ejs-link {
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
border: 2px solid #333;
|
border: 2px solid #333;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
|
|
||||||
&--anchor {
|
&--anchor {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
}
|
|
||||||
|
|
||||||
&-content {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: top;
|
|
||||||
|
|
||||||
&--title {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--description {
|
&-content {
|
||||||
font-size: 15px;
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
|
||||||
|
&--title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--description {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--link {
|
||||||
|
padding-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&--link {
|
$image-size: 85px;
|
||||||
padding-top: 10px;
|
|
||||||
font-size: 14px;
|
&--anchor--with-image &-content {
|
||||||
line-height: 20px;
|
width: calc(100% - $image-size - 5px);
|
||||||
|
padding-right: 25px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$image-size: 85px;
|
&--image {
|
||||||
|
display: inline-block;
|
||||||
&--anchor--with-image &-content {
|
width: $image-size;
|
||||||
width: calc(100% - $image-size - 5px);
|
height: $image-size;
|
||||||
padding-right: 25px;
|
background-position: center center;
|
||||||
}
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
&--image {
|
}
|
||||||
display: inline-block;
|
|
||||||
width: $image-size;
|
|
||||||
height: $image-size;
|
|
||||||
background-position: center center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.choices__list--dropdown {
|
.choices__list--dropdown {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-md-editor {
|
|
||||||
border: 1px solid $input-border-color;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v-md-editor--fullscreen {
|
|
||||||
z-index: 3000;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,29 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
|
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
|
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import "app/config";
|
@import "app/config";
|
||||||
@import "app/prism";
|
@import 'app/prism';
|
||||||
@import "app/typo";
|
@import "app/typo";
|
||||||
@import "~tingle.js/src/tingle.css";
|
@import "~tingle.js/src/tingle.css";
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "MainFont";
|
font-family: "MainFont";
|
||||||
src: url("../fonts/ubuntu/ubuntu-light.woff2?20211108") format("woff2"), url("../fonts/ubuntu/ubuntu-light.woff?20211108") format("woff");
|
src:
|
||||||
|
url('../fonts/ubuntu/ubuntu-light.woff2?20211108') format('woff2'),
|
||||||
// url('../fonts/atkinson/WOFF2/Atkinson-Hyperlegible-Regular-102a.woff2?20220911') format('woff2'),
|
url('../fonts/ubuntu/ubuntu-light.woff?20211108') format('woff');
|
||||||
// url('../fonts/atkinson/WOFF/Atkinson-Hyperlegible-Regular-102.woff?20211108w20220911') format('woff');
|
// url('../fonts/atkinson/WOFF2/Atkinson-Hyperlegible-Regular-102a.woff2?20220911') format('woff2'),
|
||||||
|
// url('../fonts/atkinson/WOFF/Atkinson-Hyperlegible-Regular-102.woff?20211108w20220911') format('woff');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "deblan-icon";
|
font-family: "deblan-icon";
|
||||||
src: url("../fonts/deblan/deblan-icon.eot?20211108");
|
src: url('../fonts/deblan/deblan-icon.eot?20211108');
|
||||||
src: url("../fonts/deblan/deblan-icon.woff2?20211108") format("woff2"), url("../fonts/deblan/deblan-icon.woff?20211108") format("woff"), url("../fonts/deblan/deblan-icon.ttf?20211108") format("truetype");
|
src:
|
||||||
|
url('../fonts/deblan/deblan-icon.woff2?20211108') format('woff2'),
|
||||||
|
url('../fonts/deblan/deblan-icon.woff?20211108') format('woff'),
|
||||||
|
url('../fonts/deblan/deblan-icon.ttf?20211108') format('truetype');
|
||||||
|
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
|
@ -58,7 +61,7 @@ $dicons: coffee server search project share contact list response twitter diaspo
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-right {
|
.text-right {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list--inline {
|
.list--inline {
|
||||||
|
|
@ -154,9 +157,8 @@ pre[class*="language-"] {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: -19px;
|
margin-bottom: -19px;i
|
||||||
|
|
||||||
i
|
|
||||||
&:active, &:focus {
|
&:active, &:focus {
|
||||||
background: none;
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
@ -171,7 +173,6 @@ pre[class*="language-"] {
|
||||||
background: url(../images/Refresh_icon.svg);
|
background: url(../images/Refresh_icon.svg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// &-captcha {
|
// &-captcha {
|
||||||
// label {
|
// label {
|
||||||
|
|
@ -303,11 +304,9 @@ pre[class*="language-"] {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0 0%;
|
background-position: 0 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
background-position: 0 75%;
|
background-position: 0 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
background-position: 0 0%;
|
background-position: 0 0%;
|
||||||
}
|
}
|
||||||
|
|
@ -348,20 +347,18 @@ pre[class*="language-"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 10px;
|
height: 10px;
|
||||||
background: rgb(0, 0, 0);
|
background: rgb(0, 0, 0);
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
|
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(255,255,255,0) 100%);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes HeaderGradient {
|
@keyframes HeaderGradient {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0 50%;
|
background-position: 0 50%
|
||||||
}
|
}
|
||||||
|
|
||||||
50% {
|
50% {
|
||||||
background-position: 100% 50%;
|
background-position: 100% 50%
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
background-position: 0 50%;
|
background-position: 0 50%
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -414,9 +411,6 @@ pre[class*="language-"] {
|
||||||
.h1 {
|
.h1 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
font-family: MainFont;
|
|
||||||
text-shadow: none;
|
|
||||||
color: hsla(0, 0%, 100%, 0.7);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.h3 {
|
.h3 {
|
||||||
|
|
@ -478,7 +472,7 @@ pre[class*="language-"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
p a:not(.btn), ul:not(.btn-group) a:not(.btn) {
|
p a:not(.btn), ul:not(.btn-group) a:not(.btn) {
|
||||||
background: url("../images/link.svg") bottom left repeat-x;
|
background: url('../images/link.svg') bottom left repeat-x;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,35 +503,6 @@ pre[class*="language-"] {
|
||||||
@include make-pre-code;
|
@include make-pre-code;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mermaid {
|
|
||||||
box-shadow: none;
|
|
||||||
background: #343c53;
|
|
||||||
border: 0;
|
|
||||||
|
|
||||||
text, tspan {
|
|
||||||
fill: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.actor, .noteText {
|
|
||||||
text, tspan {
|
|
||||||
fill: #333 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.labelText {
|
|
||||||
fill: #333 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
line, path {
|
|
||||||
stroke: #fff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.with-title {
|
&.with-title {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
|
|
@ -548,41 +513,11 @@ pre[class*="language-"] {
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
padding-top: 10px;
|
padding-top: 10px;
|
||||||
border: 1px solid $color-code-border;
|
border: 1px solid $color-code-border;
|
||||||
|
|
||||||
// overflow: hidden;
|
// overflow: hidden;
|
||||||
//
|
//
|
||||||
// &:hover {
|
// &:hover {
|
||||||
// overflow: auto;
|
// overflow: auto;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-author-wrapper {
|
|
||||||
.post-author-avatar {
|
|
||||||
vertical-align: top;
|
|
||||||
width: 100px;
|
|
||||||
padding: 10px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-author {
|
|
||||||
display: inline-block;
|
|
||||||
width: calc(100% - 100px);
|
|
||||||
padding: 10px 10px 10px 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 774px) {
|
|
||||||
.post-author-avatar {
|
|
||||||
display: block;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-author {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
padding-right: 20px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -642,14 +577,12 @@ pre[class*="language-"] {
|
||||||
@for $i from 1 through 6 {
|
@for $i from 1 through 6 {
|
||||||
.review.offset-#{$i} {
|
.review.offset-#{$i} {
|
||||||
margin-left: 5% * $i - 1%;
|
margin-left: 5% * $i - 1%;
|
||||||
width: 100% - $i * 5%;
|
width: 100% - ($i * 5%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.review {
|
.review {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: calc($content-max-width - 60px - 2rem);
|
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
.review-avatar, .review-avatar img {
|
.review-avatar, .review-avatar img {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
|
|
@ -665,7 +598,7 @@ pre[class*="language-"] {
|
||||||
border: 1px solid $color-hr-border;
|
border: 1px solid $color-hr-border;
|
||||||
|
|
||||||
.review-content p {
|
.review-content p {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> ul {
|
> ul {
|
||||||
|
|
@ -730,7 +663,7 @@ pre[class*="language-"] {
|
||||||
|
|
||||||
.code-window {
|
.code-window {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background: $color-code-title-background url("../images/window.svg") no-repeat center right;
|
background: $color-code-title-background url('../images/window.svg') no-repeat center right;
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
font-family: Monospace;
|
font-family: Monospace;
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
|
|
@ -764,8 +697,7 @@ pre[class*="language-"] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 450px;
|
height: 450px;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background: #f2f2f2 url("../images/quick-post-load.png") no-repeat center center;
|
background: #f2f2f2 url('../images/quick-post-load.png') no-repeat center center;
|
||||||
|
|
||||||
// border: 2px solid $color-very-light-grey;
|
// border: 2px solid $color-very-light-grey;
|
||||||
border-bottom: 0;
|
border-bottom: 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
@ -904,7 +836,19 @@ pre[class*="language-"] {
|
||||||
border: 1px solid $color-white;
|
border: 1px solid $color-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
$links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, github: #8cc345, code: #51d066, mastodon: #2984d2, pixelfed: #e72151, matrix: #1a588a, gpg: #42a73b, murph: #19b4db);
|
$links: (
|
||||||
|
twitter: #20b8ff,
|
||||||
|
rss: #fd9f13,
|
||||||
|
linkedin: #006699,
|
||||||
|
diaspora: #90b92e,
|
||||||
|
github: #8cc345,
|
||||||
|
code: #51d066,
|
||||||
|
mastodon: #2984d2,
|
||||||
|
pixelfed: #e72151,
|
||||||
|
matrix: #1a588a,
|
||||||
|
gpg: #42a73b,
|
||||||
|
murph: #19b4db
|
||||||
|
);
|
||||||
|
|
||||||
@each $site, $bg in $links {
|
@each $site, $bg in $links {
|
||||||
.link-#{$site} {
|
.link-#{$site} {
|
||||||
|
|
@ -1007,29 +951,25 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes bounceIn {
|
@keyframes bounceIn {
|
||||||
0% {
|
0%{
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
50%{
|
||||||
50% {
|
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
80%{
|
||||||
80% {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
100%{
|
||||||
100% {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes knmc {
|
@keyframes knmc {
|
||||||
0% {
|
0%{
|
||||||
left: -256px;
|
left: -256px;;
|
||||||
}
|
}
|
||||||
|
100%{
|
||||||
100% {
|
|
||||||
left: 150vw;
|
left: 150vw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1134,8 +1074,6 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
|
|
||||||
.ejs-link {
|
.ejs-link {
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
width: 80%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
&--anchor {
|
&--anchor {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
@ -1260,8 +1198,6 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
}
|
}
|
||||||
|
|
||||||
.ejs-link {
|
.ejs-link {
|
||||||
width: auto;
|
|
||||||
|
|
||||||
&-content {
|
&-content {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
|
|
@ -1335,13 +1271,13 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
}
|
}
|
||||||
|
|
||||||
.invalid-feedback {
|
.invalid-feedback {
|
||||||
color: red;
|
color: red;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
|
|
||||||
.form-error-icon {
|
.form-error-icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
|
|
@ -1379,7 +1315,7 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-shadow {
|
.header-shadow {
|
||||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(255, 255, 255, 0) 100%);
|
background: linear-gradient(180deg, rgba(0, 0, 0, 0.3) 0%, rgba(255,255,255,0) 100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1392,18 +1328,3 @@ $links: (twitter: #20b8ff, rss: #fd9f13, linkedin: #006699, diaspora: #90b92e, g
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.deprecated {
|
|
||||||
color: #fff;
|
|
||||||
background: #3abff8;
|
|
||||||
padding: 1rem;
|
|
||||||
border-radius: var(--rounded-box, 1rem);
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
display: inline-block;
|
|
||||||
height: 25px;
|
|
||||||
vertical-align: top;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
.alert {
|
.alert {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border: 1px solid #333;
|
border: 1px solid #333;
|
||||||
|
|
||||||
&-success {
|
&-success {
|
||||||
border-color: #9db024;
|
border-color: #9db024;
|
||||||
background: #c6ff69;
|
background: #c6ff69;
|
||||||
color: #415f29;
|
color: #415f29;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-notice {
|
&-notice {
|
||||||
border-color: $color-blue;
|
border-color: $color-blue;
|
||||||
background: #66e6ff;
|
background: #66e6ff;
|
||||||
color: #254e5f;
|
color: #254e5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-warning {
|
&-warning {
|
||||||
border-color: #b07f29;
|
border-color: #b07f29;
|
||||||
background: #ffd465;
|
background: #ffd465;
|
||||||
color: #5f4520;
|
color: #5f4520;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-error {
|
&-error {
|
||||||
border-color: #b02e2a;
|
border-color: #b02e2a;
|
||||||
background: #ff6363;
|
background: #ff6363;
|
||||||
color: #5f2521;
|
color: #5f2521;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-notice-light {
|
&-notice-light {
|
||||||
border-color: $color-blue;
|
border-color: $color-blue;
|
||||||
background: #d9fffc;
|
background: #d9fffc;
|
||||||
color: #254e5f;
|
color: #254e5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ $color-code-text: #f8f8f2;
|
||||||
$color-code-mark-background: $color-light-blue;
|
$color-code-mark-background: $color-light-blue;
|
||||||
$color-code-title-background: #1d2231;
|
$color-code-title-background: #1d2231;
|
||||||
$color-code-title-text: #e0e0e0;
|
$color-code-title-text: #e0e0e0;
|
||||||
|
|
||||||
/* --- */
|
/* --- */
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
|
|
|
||||||
|
|
@ -8,59 +8,61 @@ https://prismjs.com/download.html#themes=prism-okaidia&languages=markup+css+clik
|
||||||
|
|
||||||
code[class*="language-"],
|
code[class*="language-"],
|
||||||
pre[class*="language-"] {
|
pre[class*="language-"] {
|
||||||
color: #f8f8f2;
|
color: #f8f8f2;
|
||||||
background: none;
|
background: none;
|
||||||
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
text-shadow: 0 1px rgba(0, 0, 0, 0.3);
|
||||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
word-spacing: normal;
|
word-spacing: normal;
|
||||||
word-break: normal;
|
word-break: normal;
|
||||||
word-wrap: normal;
|
word-wrap: normal;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
-moz-tab-size: 4;
|
|
||||||
-o-tab-size: 4;
|
-moz-tab-size: 4;
|
||||||
tab-size: 4;
|
-o-tab-size: 4;
|
||||||
-webkit-hyphens: none;
|
tab-size: 4;
|
||||||
-moz-hyphens: none;
|
|
||||||
-ms-hyphens: none;
|
-webkit-hyphens: none;
|
||||||
hyphens: none;
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Code blocks */
|
/* Code blocks */
|
||||||
pre[class*="language-"] {
|
pre[class*="language-"] {
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
margin: .5em 0;
|
margin: .5em 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
:not(pre) > code[class*="language-"],
|
:not(pre) > code[class*="language-"],
|
||||||
pre[class*="language-"] {
|
pre[class*="language-"] {
|
||||||
background: #272822;
|
background: #272822;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inline code */
|
/* Inline code */
|
||||||
:not(pre) > code[class*="language-"] {
|
:not(pre) > code[class*="language-"] {
|
||||||
padding: .1em;
|
padding: .1em;
|
||||||
border-radius: .3em;
|
border-radius: .3em;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.comment,
|
.token.comment,
|
||||||
.token.prolog,
|
.token.prolog,
|
||||||
.token.doctype,
|
.token.doctype,
|
||||||
.token.cdata {
|
.token.cdata {
|
||||||
color: #8292a2;
|
color: #8292a2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.punctuation {
|
.token.punctuation {
|
||||||
color: #f8f8f2;
|
color: #f8f8f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.namespace {
|
.token.namespace {
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.property,
|
.token.property,
|
||||||
|
|
@ -68,12 +70,12 @@ pre[class*="language-"] {
|
||||||
.token.constant,
|
.token.constant,
|
||||||
.token.symbol,
|
.token.symbol,
|
||||||
.token.deleted {
|
.token.deleted {
|
||||||
color: #f92672;
|
color: #f92672;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.boolean,
|
.token.boolean,
|
||||||
.token.number {
|
.token.number {
|
||||||
color: #ae81ff;
|
color: #ae81ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.selector,
|
.token.selector,
|
||||||
|
|
@ -82,7 +84,7 @@ pre[class*="language-"] {
|
||||||
.token.char,
|
.token.char,
|
||||||
.token.builtin,
|
.token.builtin,
|
||||||
.token.inserted {
|
.token.inserted {
|
||||||
color: #a6e22e;
|
color: #a6e22e;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.operator,
|
.token.operator,
|
||||||
|
|
@ -91,34 +93,34 @@ pre[class*="language-"] {
|
||||||
.language-css .token.string,
|
.language-css .token.string,
|
||||||
.style .token.string,
|
.style .token.string,
|
||||||
.token.variable {
|
.token.variable {
|
||||||
color: #f8f8f2;
|
color: #f8f8f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.atrule,
|
.token.atrule,
|
||||||
.token.attr-value,
|
.token.attr-value,
|
||||||
.token.function,
|
.token.function,
|
||||||
.token.class-name {
|
.token.class-name {
|
||||||
color: #e6db74;
|
color: #e6db74;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.keyword {
|
.token.keyword {
|
||||||
color: #66d9ef;
|
color: #66d9ef;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.regex,
|
.token.regex,
|
||||||
.token.important {
|
.token.important {
|
||||||
color: #fd971f;
|
color: #fd971f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.important,
|
.token.important,
|
||||||
.token.bold {
|
.token.bold {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.italic {
|
.token.italic {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
.token.entity {
|
.token.entity {
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,35 +16,34 @@
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
.h1, h1 {
|
.h1, h1 {
|
||||||
font-size: 3.2rem;
|
font-size: 3.2rem;
|
||||||
line-height: 1.32;
|
line-height: 1.32
|
||||||
}
|
}
|
||||||
|
|
||||||
.h2, h2 {
|
.h2, h2 {
|
||||||
font-size: 2.6rem;
|
font-size: 2.6rem;
|
||||||
line-height: 1.35;
|
line-height: 1.35
|
||||||
}
|
}
|
||||||
|
|
||||||
.h3, h3 {
|
.h3, h3 {
|
||||||
font-size: 2.0rem;
|
font-size: 2.0rem;
|
||||||
line-height: 1.45;
|
line-height: 1.45
|
||||||
}
|
}
|
||||||
|
|
||||||
.h4, h4 {
|
.h4, h4 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6
|
||||||
}
|
}
|
||||||
|
|
||||||
.h5, h5 {
|
.h5, h5 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
line-height: 1.75;
|
line-height: 1.75
|
||||||
}
|
}
|
||||||
|
|
||||||
.h6, h6 {
|
.h6, h6 {
|
||||||
font-size: 1.0rem;
|
font-size: 1.0rem;
|
||||||
line-height: 1.9;
|
line-height: 1.9
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// p {
|
// p {
|
||||||
// margin-top: 17px;
|
// margin-top: 17px;
|
||||||
|
|
@ -57,6 +56,7 @@
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
.h1,
|
.h1,
|
||||||
.h2,
|
.h2,
|
||||||
.h3,
|
.h3,
|
||||||
|
|
@ -69,7 +69,8 @@ h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
margin-top: 0;
|
margin-top: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mesh-viewer {
|
#mesh-viewer {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js'
|
import '../../vendor/murph/murph-core/src/core/Resources/assets/js/admin.js'
|
||||||
|
|
||||||
require('./admin_modules/md-editor')()
|
require('./admin_modules/simplemde')()
|
||||||
|
|
||||||
const $ = require('jquery')
|
const $ = require('jquery')
|
||||||
const Sortable = require('sortablejs').Sortable
|
const Sortable = require('sortablejs').Sortable
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
const Vue = require('vue').default
|
|
||||||
const VueMarkdownEditor = require('@kangc/v-md-editor')
|
|
||||||
const githubTheme = require('@kangc/v-md-editor/lib/theme/github.js')
|
|
||||||
const fr = require('@kangc/v-md-editor/lib/lang/fr-FR').default
|
|
||||||
const hljs = require('highlight.js')
|
|
||||||
|
|
||||||
VueMarkdownEditor.use(githubTheme, {Hljs: hljs})
|
|
||||||
VueMarkdownEditor.lang.use('fr-FR', fr)
|
|
||||||
Vue.use(VueMarkdownEditor)
|
|
||||||
|
|
||||||
module.exports = () => {
|
|
||||||
const components = document.querySelectorAll('.markdown-editor')
|
|
||||||
|
|
||||||
components.forEach((component) => {
|
|
||||||
return new Vue({
|
|
||||||
el: component,
|
|
||||||
template: `
|
|
||||||
<div>
|
|
||||||
<textarea :name="name" v-model="value" class="d-none"></textarea>
|
|
||||||
<v-md-editor v-model="value" mode="edit"></v-md-editor>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
name: component.getAttribute('data-name'),
|
|
||||||
value: JSON.parse(component.getAttribute('data-value')),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
|
||||||
VueMarkdownEditor
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,8 @@ module.exports = () => {
|
||||||
autoDownloadFontAwesome: false,
|
autoDownloadFontAwesome: false,
|
||||||
spellChecker: false,
|
spellChecker: false,
|
||||||
styleSelectedText: false,
|
styleSelectedText: false,
|
||||||
toolbar: [{
|
toolbar: [
|
||||||
|
{
|
||||||
name: 'bold',
|
name: 'bold',
|
||||||
action: Editor.toggleBold,
|
action: Editor.toggleBold,
|
||||||
className: 'fa fa-bold',
|
className: 'fa fa-bold',
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ const Code = require('./app/code')
|
||||||
const Knmc = require('./app/knmc')
|
const Knmc = require('./app/knmc')
|
||||||
const VideoRatio = require('./app/video-ratio')
|
const VideoRatio = require('./app/video-ratio')
|
||||||
const Stats = require('./app/stats')
|
const Stats = require('./app/stats')
|
||||||
// const Particles = require('./app/particles')
|
const Particles = require('./app/particles')
|
||||||
const MeshViewer = require('./app/mesh-viewer')
|
const MeshViewer = require('./app/mesh-viewer')
|
||||||
const SmallMenu = require('./app/small-menu')
|
const SmallMenu = require('./app/small-menu')
|
||||||
|
|
||||||
|
|
@ -22,11 +22,11 @@ const app = new App([
|
||||||
new Knmc(window),
|
new Knmc(window),
|
||||||
new VideoRatio(window),
|
new VideoRatio(window),
|
||||||
// new Stats(),
|
// new Stats(),
|
||||||
// new Particles(window),
|
new Particles(window),
|
||||||
new MeshViewer(window),
|
new MeshViewer(window),
|
||||||
new SmallMenu(window)
|
new SmallMenu(window)
|
||||||
])
|
])
|
||||||
|
|
||||||
window.addEventListener('load', function() {
|
window.addEventListener('load', function () {
|
||||||
app.init()
|
app.init()
|
||||||
}, false)
|
}, false)
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
class App {
|
class App {
|
||||||
constructor (components) {
|
constructor(components) {
|
||||||
this.components = components || []
|
this.components = components || []
|
||||||
}
|
}
|
||||||
|
|
||||||
add (c) {
|
add(c) {
|
||||||
this.components.push(c)
|
this.components.push(c)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
for (let u = 0, x = this.components.length; u < x; u++) {
|
for (let u = 0, x = this.components.length; u < x; u++) {
|
||||||
this.components[u].init()
|
this.components[u].init()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = App
|
module.exports = App
|
||||||
|
|
|
||||||
|
|
@ -22,79 +22,78 @@ require('prismjs/components/prism-shell-session')
|
||||||
require('prismjs/plugins/keep-markup/prism-keep-markup')
|
require('prismjs/plugins/keep-markup/prism-keep-markup')
|
||||||
require('prismjs/plugins/line-highlight/prism-line-highlight')
|
require('prismjs/plugins/line-highlight/prism-line-highlight')
|
||||||
require('prismjs/plugins/line-numbers/prism-line-numbers')
|
require('prismjs/plugins/line-numbers/prism-line-numbers')
|
||||||
require('mermaid')
|
|
||||||
|
|
||||||
class Code {
|
class Code {
|
||||||
constructor(w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
Prism.highlightAllUnder(document)
|
|
||||||
let elements = this.window.document.querySelectorAll('code[data-title], div[data-title]')
|
|
||||||
|
|
||||||
for (let i = 0, len = elements.length; i < len; i++) {
|
|
||||||
const element = elements[i]
|
|
||||||
|
|
||||||
if (element.tagName === 'CODE') {
|
|
||||||
var code = element
|
|
||||||
var pre = code.parentNode
|
|
||||||
var post = pre.parentNode
|
|
||||||
} else {
|
|
||||||
var code = element.querySelector('code')
|
|
||||||
|
|
||||||
if (!code) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var pre = code.parentNode
|
|
||||||
var post = pre.parentNode
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pre || !post) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.classList.add('with-title')
|
|
||||||
|
|
||||||
const title = this.window.document.createElement('div')
|
|
||||||
title.classList.add('code-title')
|
|
||||||
title.textContent = element.getAttribute('data-title')
|
|
||||||
|
|
||||||
post.insertBefore(title, pre)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elements = this.window.document.querySelectorAll('code.window')
|
init() {
|
||||||
|
Prism.highlightAllUnder(document)
|
||||||
|
let elements = this.window.document.querySelectorAll('code[data-title], div[data-title]')
|
||||||
|
|
||||||
for (let i = 0, len = elements.length; i < len; i++) {
|
for (let i = 0, len = elements.length; i < len; i++) {
|
||||||
const element = elements[i]
|
const element = elements[i]
|
||||||
|
|
||||||
if (element.tagName === 'CODE') {
|
if (element.tagName === 'CODE') {
|
||||||
var code = element
|
var code = element
|
||||||
var pre = code.parentNode
|
var pre = code.parentNode
|
||||||
var post = pre.parentNode
|
var post = pre.parentNode
|
||||||
} else {
|
} else {
|
||||||
var code = element.querySelector('code')
|
var code = element.querySelector('code')
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre = code.parentNode
|
||||||
|
var post = pre.parentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pre || !post) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.classList.add('with-title')
|
||||||
|
|
||||||
|
const title = this.window.document.createElement('div')
|
||||||
|
title.classList.add('code-title')
|
||||||
|
title.textContent = element.getAttribute('data-title')
|
||||||
|
|
||||||
|
post.insertBefore(title, pre)
|
||||||
}
|
}
|
||||||
|
|
||||||
var pre = code.parentNode
|
elements = this.window.document.querySelectorAll('code.window')
|
||||||
var post = pre.parentNode
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pre || !post) {
|
for (let i = 0, len = elements.length; i < len; i++) {
|
||||||
continue
|
const element = elements[i]
|
||||||
}
|
|
||||||
|
|
||||||
pre.classList.add('with-title')
|
if (element.tagName === 'CODE') {
|
||||||
const win = this.window.document.createElement('div')
|
var code = element
|
||||||
win.classList.add('code-window')
|
var pre = code.parentNode
|
||||||
|
var post = pre.parentNode
|
||||||
|
} else {
|
||||||
|
var code = element.querySelector('code')
|
||||||
|
|
||||||
post.insertBefore(win, pre)
|
if (!code) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var pre = code.parentNode
|
||||||
|
var post = pre.parentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pre || !post) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pre.classList.add('with-title')
|
||||||
|
const win = this.window.document.createElement('div')
|
||||||
|
win.classList.add('code-window')
|
||||||
|
|
||||||
|
post.insertBefore(win, pre)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Code
|
module.exports = Code
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
const Routing = require('./routing')
|
const Routing = require('./routing')
|
||||||
|
|
||||||
class FormPnw {
|
class FormPnw {
|
||||||
constructor (w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
const doc = this.window.document
|
const doc = this.window.document
|
||||||
|
|
||||||
doc.addEventListener('mousemove', function () {
|
doc.addEventListener('mousemove', function() {
|
||||||
const forms = doc.querySelectorAll('form[data-form-bot]')
|
const forms = doc.querySelectorAll('form[data-form-bot]')
|
||||||
|
|
||||||
for (let i = 0, len = forms.length; i < len; i++) {
|
for (let i = 0, len = forms.length; i < len; i++) {
|
||||||
const form = forms[i]
|
const form = forms[i]
|
||||||
let action = form.getAttribute('action')
|
let action = form.getAttribute('action')
|
||||||
action = action.replace(
|
action = action.replace(
|
||||||
Routing.generate('blog_tech_form_without_javascript', {
|
Routing.generate('blog_tech_form_without_javascript', {
|
||||||
_domain: window.location.hostname
|
_domain: window.location.hostname
|
||||||
}, false) + '?page=', ''
|
}, false) + '?page=', ''
|
||||||
)
|
)
|
||||||
|
|
||||||
action = decodeURIComponent(action)
|
action = decodeURIComponent(action)
|
||||||
|
|
||||||
form.setAttribute('action', action)
|
form.setAttribute('action', action)
|
||||||
form.removeAttribute('data-form-bot')
|
form.removeAttribute('data-form-bot')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = FormPnw
|
module.exports = FormPnw
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,36 @@
|
||||||
const Mario = require('../../images/mario.gif')
|
const Mario = require('../../images/mario.gif')
|
||||||
|
|
||||||
class Knmc {
|
class Knmc {
|
||||||
constructor (w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
let chars = ''
|
let chars = ''
|
||||||
const seq = '38384040373937396665'
|
const seq = '38384040373937396665'
|
||||||
const body = this.window.document.querySelector('body')
|
const body = this.window.document.querySelector('body')
|
||||||
|
|
||||||
body.addEventListener('keyup', function (e) {
|
body.addEventListener('keyup', function(e) {
|
||||||
chars += e.keyCode.toString()
|
chars += e.keyCode.toString()
|
||||||
|
|
||||||
if (chars.indexOf(seq) !== -1) {
|
if (chars.indexOf(seq) !== -1) {
|
||||||
chars = ''
|
chars = ''
|
||||||
const url = Mario
|
const url = Mario
|
||||||
const image = new Image()
|
const image = new Image()
|
||||||
image.classList.add('fixed')
|
image.classList.add('fixed')
|
||||||
image.style.position = 'fixed'
|
image.style.position = 'fixed'
|
||||||
image.style.bottom = '-11px'
|
image.style.bottom = '-11px'
|
||||||
image.style.left = '-256px'
|
image.style.left = '-256px'
|
||||||
|
|
||||||
image.onload = function () {
|
image.onload = function() {
|
||||||
image.classList.add('knmc')
|
image.classList.add('knmc')
|
||||||
body.appendChild(image)
|
body.appendChild(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
image.src = url
|
image.src = url
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Knmc
|
module.exports = Knmc
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
const lozad = require('lozad')
|
const lozad = require('lozad')
|
||||||
|
|
||||||
class LazyLoad {
|
class LazyLoad {
|
||||||
init () {
|
init() {
|
||||||
const observer = lozad('.lazy-img')
|
const observer = lozad('.lazy-img')
|
||||||
observer.observe()
|
observer.observe()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = LazyLoad
|
module.exports = LazyLoad
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,36 @@
|
||||||
const tingle = require('tingle.js/src/tingle.js')
|
const tingle = require('tingle.js/src/tingle.js')
|
||||||
|
|
||||||
class MeshViewer {
|
class MeshViewer {
|
||||||
constructor(w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
const openers = this.window.document.querySelectorAll('*[data-modal]')
|
const openers = this.window.document.querySelectorAll('*[data-modal]')
|
||||||
|
|
||||||
for (let i = 0, len = openers.length; i < len; i++) {
|
for (let i = 0, len = openers.length; i < len; i++) {
|
||||||
openers[i].addEventListener('click', (e) => {
|
openers[i].addEventListener('click', (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
let target = e.target
|
let target = e.target
|
||||||
|
|
||||||
if (target.tagName != 'A') {
|
if (target.tagName != 'A') {
|
||||||
target = target.parentNode
|
target = target.parentNode
|
||||||
}
|
}
|
||||||
|
|
||||||
const modal = new tingle.modal({
|
const modal = new tingle.modal({
|
||||||
footer: false,
|
footer: false,
|
||||||
stickyFooter: false,
|
stickyFooter: false,
|
||||||
closeMethods: ['overlay', 'button', 'escape'],
|
closeMethods: ['overlay', 'button', 'escape'],
|
||||||
closeLabel: 'Close',
|
closeLabel: 'Close',
|
||||||
cssClass: ['tingle-modal-box--mesh']
|
cssClass: ['tingle-modal-box--mesh']
|
||||||
})
|
})
|
||||||
|
|
||||||
modal.setContent('<iframe src="' + target.getAttribute('href') + '"></iframe>')
|
modal.setContent('<iframe src="' + target.getAttribute('href') + '"></iframe>')
|
||||||
modal.open()
|
modal.open()
|
||||||
})
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MeshViewer
|
module.exports = MeshViewer
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,47 @@
|
||||||
require('particles.js')
|
require('particles.js')
|
||||||
|
|
||||||
class Particles {
|
class Particles {
|
||||||
constructor(w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (this.window.innerWidth < 708) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const height = this.header.offsetHeight
|
start() {
|
||||||
const width = this.header.offsetWidth
|
if (this.window.innerWidth < 708) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.particles.style.maxHeight = height + 'px'
|
const height = this.header.offsetHeight
|
||||||
this.particles.style.maxWidth = width + 'px'
|
const width = this.header.offsetWidth
|
||||||
|
|
||||||
particlesJS.load('particles', '/js/particles.json?v=3')
|
this.particles.style.maxHeight = height + 'px'
|
||||||
}
|
this.particles.style.maxWidth = width + 'px'
|
||||||
|
|
||||||
clean() {
|
particlesJS.load('particles', '/js/particles.json?v=3')
|
||||||
this.particles.innerHTML = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
|
||||||
this.particles = this.window.document.getElementById('particles')
|
|
||||||
|
|
||||||
if (!this.particles) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.header = this.particles.parentNode
|
clean() {
|
||||||
|
this.particles.innerHTML = ''
|
||||||
|
}
|
||||||
|
|
||||||
this.clean()
|
init() {
|
||||||
this.start()
|
this.particles = this.window.document.getElementById('particles')
|
||||||
|
|
||||||
const that = this
|
if (!this.particles) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.window.addEventListener('resize', function() {
|
this.header = this.particles.parentNode
|
||||||
that.clean()
|
|
||||||
that.start()
|
this.clean()
|
||||||
}, false)
|
this.start()
|
||||||
}
|
|
||||||
|
const that = this
|
||||||
|
|
||||||
|
this.window.addEventListener('resize', function() {
|
||||||
|
that.clean()
|
||||||
|
that.start()
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Particles
|
module.exports = Particles
|
||||||
|
|
|
||||||
|
|
@ -1,122 +1,122 @@
|
||||||
const Routing = require('./routing')
|
const Routing = require('./routing')
|
||||||
|
|
||||||
class Post {
|
class Post {
|
||||||
constructor (w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
|
||||||
|
|
||||||
commentsEvents () {
|
|
||||||
const document = this.window.document
|
|
||||||
|
|
||||||
const parentCommentIdField = document.getElementById('user_comment_parentCommentId')
|
|
||||||
|
|
||||||
if (!parentCommentIdField) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAnswerAlert = document.getElementById('answer-alert')
|
commentsEvents() {
|
||||||
const cancelAnswerButton = document.getElementById('cancel-answer')
|
const document = this.window.document
|
||||||
|
|
||||||
const toogleAnswerAlert = function () {
|
const parentCommentIdField = document.getElementById('user_comment_parentCommentId')
|
||||||
if (parentCommentIdField.value) {
|
|
||||||
isAnswerAlert.classList.remove('hidden')
|
|
||||||
} else {
|
|
||||||
isAnswerAlert.classList.add('hidden')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toogleAnswerAlert()
|
if (!parentCommentIdField) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const answerButtons = document.querySelectorAll('a[data-answer]')
|
const isAnswerAlert = document.getElementById('answer-alert')
|
||||||
|
const cancelAnswerButton = document.getElementById('cancel-answer')
|
||||||
|
|
||||||
|
const toogleAnswerAlert = function() {
|
||||||
|
if (parentCommentIdField.value) {
|
||||||
|
isAnswerAlert.classList.remove('hidden')
|
||||||
|
} else {
|
||||||
|
isAnswerAlert.classList.add('hidden')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0, len = answerButtons.length; i < len; i++) {
|
|
||||||
answerButtons[i].addEventListener('click', function (e) {
|
|
||||||
parentCommentIdField.value = e.target.getAttribute('data-answer')
|
|
||||||
toogleAnswerAlert()
|
toogleAnswerAlert()
|
||||||
}, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelAnswerButton.addEventListener('click', function (e) {
|
const answerButtons = document.querySelectorAll('a[data-answer]')
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
parentCommentIdField.value = null
|
for (let i = 0, len = answerButtons.length; i < len; i++) {
|
||||||
toogleAnswerAlert()
|
answerButtons[i].addEventListener('click', function(e) {
|
||||||
}, false)
|
parentCommentIdField.value = e.target.getAttribute('data-answer')
|
||||||
|
toogleAnswerAlert()
|
||||||
const previewButton = document.querySelector('.preview-button')
|
}, false)
|
||||||
const previewRender = document.querySelector('#preview')
|
|
||||||
|
|
||||||
previewButton.addEventListener('click', function () {
|
|
||||||
if (previewRender.innerHTML === '') {
|
|
||||||
previewRender.innerHTML = '<p>Chargement en cours…</p>'
|
|
||||||
}
|
|
||||||
|
|
||||||
const content = document.querySelector('#user_comment_content').value
|
|
||||||
const httpRequest = new XMLHttpRequest()
|
|
||||||
|
|
||||||
httpRequest.onreadystatechange = function (data) {
|
|
||||||
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
|
|
||||||
const json = JSON.parse(httpRequest.response)
|
|
||||||
previewRender.innerHTML = json.render
|
|
||||||
document.location.href = '#preview'
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
httpRequest.open('POST', Routing.generate('api_blog_comment_preview'))
|
cancelAnswerButton.addEventListener('click', function(e) {
|
||||||
httpRequest.setRequestHeader(
|
e.preventDefault()
|
||||||
'Content-Type',
|
|
||||||
'application/x-www-form-urlencoded'
|
|
||||||
)
|
|
||||||
httpRequest.send('content=' + encodeURIComponent(content))
|
|
||||||
}, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
imagesEvents () {
|
parentCommentIdField.value = null
|
||||||
const document = this.window.document
|
toogleAnswerAlert()
|
||||||
let isFullscreen = false
|
|
||||||
const images = document.querySelectorAll('.body img')
|
|
||||||
|
|
||||||
const handleClick = function (image) {
|
|
||||||
if (isFullscreen) {
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen()
|
|
||||||
} else if (document.webkitExitFullscreen) {
|
|
||||||
document.webkitExitFullscreen()
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (image.requestFullscreen) {
|
|
||||||
image.requestFullscreen()
|
|
||||||
} else if (image.webkitRequestFullscreen) {
|
|
||||||
image.webkitRequestFullscreen()
|
|
||||||
} else if (image.mozRequestFullScreen) {
|
|
||||||
image.mozRequestFullScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isFullscreen = !isFullscreen
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0, len = images.length; i < len; i++) {
|
|
||||||
const image = images[i]
|
|
||||||
|
|
||||||
if (image.parentNode.tagName === 'A') {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
(function (i) {
|
|
||||||
i.addEventListener('click', function () {
|
|
||||||
handleClick(i)
|
|
||||||
}, false)
|
}, false)
|
||||||
})(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init () {
|
const previewButton = document.querySelector('.preview-button')
|
||||||
this.commentsEvents()
|
const previewRender = document.querySelector('#preview')
|
||||||
this.imagesEvents()
|
|
||||||
}
|
previewButton.addEventListener('click', function() {
|
||||||
|
if (previewRender.innerHTML === '') {
|
||||||
|
previewRender.innerHTML = '<p>Chargement en cours…</p>'
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = document.querySelector('#user_comment_content').value
|
||||||
|
const httpRequest = new XMLHttpRequest()
|
||||||
|
|
||||||
|
httpRequest.onreadystatechange = function(data) {
|
||||||
|
if (httpRequest.readyState === 4 && httpRequest.status === 200) {
|
||||||
|
const json = JSON.parse(httpRequest.response)
|
||||||
|
previewRender.innerHTML = json.render
|
||||||
|
document.location.href = '#preview'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpRequest.open('POST', Routing.generate('api_blog_comment_preview'))
|
||||||
|
httpRequest.setRequestHeader(
|
||||||
|
'Content-Type',
|
||||||
|
'application/x-www-form-urlencoded'
|
||||||
|
)
|
||||||
|
httpRequest.send('content=' + encodeURIComponent(content))
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
imagesEvents() {
|
||||||
|
const document = this.window.document
|
||||||
|
let isFullscreen = false
|
||||||
|
const images = document.querySelectorAll('.body img')
|
||||||
|
|
||||||
|
const handleClick = function(image) {
|
||||||
|
if (isFullscreen) {
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen()
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen()
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (image.requestFullscreen) {
|
||||||
|
image.requestFullscreen()
|
||||||
|
} else if (image.webkitRequestFullscreen) {
|
||||||
|
image.webkitRequestFullscreen()
|
||||||
|
} else if (image.mozRequestFullScreen) {
|
||||||
|
image.mozRequestFullScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFullscreen = !isFullscreen
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0, len = images.length; i < len; i++) {
|
||||||
|
const image = images[i]
|
||||||
|
|
||||||
|
if (image.parentNode.tagName === 'A') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
(function(i) {
|
||||||
|
i.addEventListener('click', function() {
|
||||||
|
handleClick(i)
|
||||||
|
}, false)
|
||||||
|
})(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.commentsEvents()
|
||||||
|
this.imagesEvents()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Post
|
module.exports = Post
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,34 @@
|
||||||
class PxImage {
|
class PxImage {
|
||||||
constructor (w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
const doc = this.window.document
|
const doc = this.window.document
|
||||||
|
|
||||||
const images = doc.querySelectorAll('.quick-image img, .card figure img')
|
const images = doc.querySelectorAll('.quick-image img, .card figure img')
|
||||||
|
|
||||||
for (let i = 0, len = images.length; i < len; i++) {
|
for (let i = 0, len = images.length; i < len; i++) {
|
||||||
((image) => {
|
((image) => {
|
||||||
const source = image.getAttribute('data-src')
|
const source = image.getAttribute('data-src')
|
||||||
const sourceError = image.getAttribute('data-src-error')
|
const sourceError = image.getAttribute('data-src-error')
|
||||||
const color = image.getAttribute('data-color')
|
const color = image.getAttribute('data-color')
|
||||||
const loader = new Image()
|
const loader = new Image()
|
||||||
|
|
||||||
loader.onload = () => {
|
loader.onload = () => {
|
||||||
image.style.background = `${color || null} url(${source})`
|
image.style.background = `${color ? color : null} url(${source})`
|
||||||
image.style.backgroundSize = 'cover'
|
image.style.backgroundSize = 'cover'
|
||||||
image.style.backgroundPosition = 'center'
|
image.style.backgroundPosition = 'center'
|
||||||
}
|
}
|
||||||
|
|
||||||
loader.onerror = () => {
|
loader.onerror = () => {
|
||||||
image.style.background = `${color || null} url('${sourceError}') center center`
|
image.style.background = `${color ? color : null} url('${sourceError}') center center`
|
||||||
}
|
}
|
||||||
|
|
||||||
loader.src = source
|
loader.src = source
|
||||||
})(images[i])
|
})(images[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = PxImage
|
module.exports = PxImage
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
const Routing = require('./routing')
|
const Routing = require('./routing')
|
||||||
|
|
||||||
class SmallMenu {
|
class SmallMenu {
|
||||||
constructor(w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
addEvent() {
|
addEvent() {
|
||||||
const document = this.window.document
|
const document = this.window.document
|
||||||
const menu = document.querySelector('.small-menu')
|
const menu = document.querySelector('.small-menu')
|
||||||
const opener = document.querySelector('.menu-opener')
|
const opener = document.querySelector('.menu-opener')
|
||||||
|
|
||||||
opener.addEventListener('click', () => {
|
opener.addEventListener('click', () => {
|
||||||
menu.classList.toggle('is-open')
|
menu.classList.toggle('is-open')
|
||||||
opener.classList.toggle('is-open')
|
opener.classList.toggle('is-open')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this.addEvent()
|
this.addEvent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = SmallMenu
|
module.exports = SmallMenu
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
class Stats {
|
class Stats {
|
||||||
init() {
|
init() {
|
||||||
(function(f, a, t, h, o, m) {
|
(function(f, a, t, h, o, m) {
|
||||||
a[h] = a[h] || function() {
|
a[h] = a[h] || function() {
|
||||||
(a[h].q = a[h].q || []).push(arguments)
|
(a[h].q = a[h].q || []).push(arguments)
|
||||||
}
|
}
|
||||||
o = f.createElement('script'),
|
o = f.createElement('script'),
|
||||||
m = f.getElementsByTagName('script')[0]
|
m = f.getElementsByTagName('script')[0]
|
||||||
o.async = 1;
|
o.async = 1;
|
||||||
o.src = t;
|
o.src = t;
|
||||||
o.id = 'fathom-script'
|
o.id = 'fathom-script'
|
||||||
m.parentNode.insertBefore(o, m)
|
m.parentNode.insertBefore(o, m)
|
||||||
})(document, window, '//ftm.deblan.org/tracker.js', 'fathom')
|
})(document, window, '//ftm.deblan.org/tracker.js', 'fathom')
|
||||||
fathom('set', 'siteId', 'HQAWS')
|
fathom('set', 'siteId', 'HQAWS')
|
||||||
fathom('trackPageview')
|
fathom('trackPageview')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Stats
|
module.exports = Stats
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
class VideoRatio {
|
class VideoRatio {
|
||||||
constructor (w) {
|
constructor(w) {
|
||||||
this.window = w
|
this.window = w
|
||||||
}
|
}
|
||||||
|
|
||||||
init () {
|
init() {
|
||||||
const videos = this.window.document.querySelectorAll('.video-ratio')
|
const videos = this.window.document.querySelectorAll('.video-ratio')
|
||||||
|
|
||||||
for (let i = 0, len = videos.length; i < len; i++) {
|
for (let i = 0, len = videos.length; i < len; i++) {
|
||||||
videos[i].style.paddingBottom = videos[i].getAttribute('data-ratio')
|
videos[i].style.paddingBottom = videos[i].getAttribute('data-ratio')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = VideoRatio
|
module.exports = VideoRatio
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,10 @@ import '../css/viewer.scss'
|
||||||
|
|
||||||
const container = document.getElementById('mesh-viewer')
|
const container = document.getElementById('mesh-viewer')
|
||||||
const viewer = new StlViewer(
|
const viewer = new StlViewer(
|
||||||
container, {
|
container,
|
||||||
|
{
|
||||||
auto_rotate: true,
|
auto_rotate: true,
|
||||||
allow_drag_and_drop: true,
|
allow_drag_and_drop: true,
|
||||||
models: [{
|
models: [{ filename: container.getAttribute('data-file') }]
|
||||||
filename: container.getAttribute('data-file')
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<svg
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="99.39035mm"
|
|
||||||
height="96.711884mm"
|
|
||||||
viewBox="0 0 99.39035 96.711884"
|
|
||||||
version="1.1"
|
|
||||||
id="svg80"
|
|
||||||
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
|
||||||
sodipodi:docname="favicon.svg">
|
|
||||||
<defs
|
|
||||||
id="defs74" />
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="1.4"
|
|
||||||
inkscape:cx="352.07698"
|
|
||||||
inkscape:cy="193.34964"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
inkscape:document-rotation="0"
|
|
||||||
showgrid="false"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:window-width="1918"
|
|
||||||
inkscape:window-height="1021"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="38"
|
|
||||||
inkscape:window-maximized="1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata77">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
<dc:title></dc:title>
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:label="Calque 1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-198.33095,46.217785)">
|
|
||||||
<path
|
|
||||||
style="fill:#de332c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 296.12648,44.426171 c -0.37616,2.92328 -0.59758,6.08799 -2.85992,6.06783 l -40.67456,-0.72885 -51.81177,-0.81029 -2.44928,-93.098834 c 0,0 0.47161,-3.839144 5.78393,-1.070305 l 18.27773,0.460614 1.6558,-0.665961 1.41788,0.47684 9.38085,0.02311 c 0,0 24.49397,1.995856 27.60257,0.772835 0,0 2.57574,-1.103967 4.63033,-0.769364 l 9.40198,0.03935 c 0,0 -0.35343,1.705818 3.88286,0.190643 l 6.19128,-0.134168 10.25258,-0.187043 c 0.58109,0.575105 1.71697,2.678558 0.0146,3.046832 l -0.15717,2.656438 c 0,0 0.38691,1.577371 0.24294,5.257968 l -0.31455,8.041286 -0.20367,37.847704 -0.43783,27.062555 c 0.18945,-0.0794 -1.36944,2.21097 0.17342,5.52081"
|
|
||||||
id="path44"
|
|
||||||
sodipodi:nodetypes="ccccccccccccccccccscccc" />
|
|
||||||
<path
|
|
||||||
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 235.82199,-35.0018 1.08754,-2.025235 17.77563,0.21013 1.85521,0.809043 2.16713,20.710101 -2.3198,2.469536 -19.71646,-1.002688 z"
|
|
||||||
id="path46" />
|
|
||||||
<path
|
|
||||||
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 265.2653,-10.29825 14.69965,0.279887 c 0,0 3.15158,-1.833364 4.06674,0.18447 0.91514,2.017834 0.56845,8.636437 0.51746,13.685586 -0.051,5.04915 -0.051,6.985119 -0.051,6.985119 l -18.20101,0.958779 -1.39041,-1.835613 0.97744,-2.381084 z"
|
|
||||||
id="path48" />
|
|
||||||
<path
|
|
||||||
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 210.46943,16.678368 1.70731,-0.587661 c 0,0 1.60823,-1.095195 5.34804,-0.168331 l 9.63629,-0.308391 c 0,0 2.8091,-0.446237 1.35679,3.165083 l -0.0385,18.005543 -2.95227,1.86033 -13.68848,-0.13491 -1.45506,-19.81506 z"
|
|
||||||
id="path50" />
|
|
||||||
<path
|
|
||||||
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 238.07883,15.88924 c 0,0 14.17009,-0.559639 14.45321,-0.441349 0.28313,0.118293 3.50832,-0.319106 3.60718,3.740288 0.0989,4.059391 0.52015,14.301642 0.52015,14.301642 0,0 0.26091,3.34746 -1.81487,4.95965 -2.07577,1.61219 -15.224,1.10303 -15.224,1.10303 l -2.63971,-5.62585 1.26513,-2.20493 -1.05718,-3.333734 -0.45105,-10.060183 z"
|
|
||||||
id="path52" />
|
|
||||||
<path
|
|
||||||
style="fill:#1a1a1a;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
|
||||||
d="m 267.80047,15.783724 c 0,0 -4.50081,-1.216835 -3.29521,6.498831 1.2056,7.715665 0.28562,14.423636 0.28562,14.423636 0,0 0.53697,2.11476 3.01121,2.44549 2.47425,0.33073 13.16476,-0.3909 13.16476,-0.3909 0,0 2.95523,0.0701 3.12091,-3.31289 0.16567,-3.38299 -0.18686,-14.513584 -0.18686,-14.513584 l -10e-4,-3.864433 c 0,0 0.50004,-1.593741 -1.01212,-1.688235 -1.51215,-0.0945 -15.087,0.402085 -15.087,0.402085 z"
|
|
||||||
id="path54" />
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 4.8 KiB |
129
bin/messenger
129
bin/messenger
|
|
@ -1,129 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
printf "Usage: %s [-l DEBUG_LEVEL] [-h] start|stop|restart\n" "$0"
|
|
||||||
}
|
|
||||||
|
|
||||||
help() {
|
|
||||||
cat << EOH
|
|
||||||
SYNOPSIS
|
|
||||||
$0 [-l DEBUG_LEVEL] [-h] -a start|stop|restart
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
|
|
||||||
$0 manages symfony messenger
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
-h Show this help
|
|
||||||
|
|
||||||
-l debug|info|notice|warning|error
|
|
||||||
Debug level
|
|
||||||
|
|
||||||
-a start|stop|restart|status
|
|
||||||
EOH
|
|
||||||
}
|
|
||||||
|
|
||||||
on_interrupt() {
|
|
||||||
log -l notice ""
|
|
||||||
log -l notice "Process aborted!"
|
|
||||||
|
|
||||||
exit 130
|
|
||||||
}
|
|
||||||
|
|
||||||
start_messenger() {
|
|
||||||
nohup php8.1 bin/console messenger:consume 2>/dev/null >/dev/null &
|
|
||||||
log -t -l notice "Started"
|
|
||||||
}
|
|
||||||
|
|
||||||
stop_messenger() {
|
|
||||||
php8.1 bin/console messenger:stop-workers 2>/dev/null >/dev/null
|
|
||||||
log -t -l notice "Stopped"
|
|
||||||
}
|
|
||||||
|
|
||||||
get_pid() {
|
|
||||||
pgrep -f messenger:consume
|
|
||||||
}
|
|
||||||
|
|
||||||
main() {
|
|
||||||
cd "$(dirname "0")"
|
|
||||||
|
|
||||||
ACTION=
|
|
||||||
|
|
||||||
while getopts "l:ha:" option; do
|
|
||||||
case "${option}" in
|
|
||||||
h) help; exit 0;;
|
|
||||||
l) LOG_VERBOSE="$OPTARG";;
|
|
||||||
a) ACTION="$OPTARG";;
|
|
||||||
?) log -l error "$(usage)"; exit 1;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$ACTION" = "start" ]; then
|
|
||||||
start_messenger
|
|
||||||
elif [ "$ACTION" = "stop" ]; then
|
|
||||||
stop_messenger
|
|
||||||
elif [ "$ACTION" = "restart" ]; then
|
|
||||||
stop_messenger
|
|
||||||
start_messenger
|
|
||||||
elif [ "$ACTION" = "status" ]; then
|
|
||||||
get_pid
|
|
||||||
else
|
|
||||||
log -l error "Action is required."
|
|
||||||
fi
|
|
||||||
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
log() {
|
|
||||||
LOG_VERBOSE="${LOG_VERBOSE:-info}"
|
|
||||||
LEVEL=info
|
|
||||||
TIME=
|
|
||||||
|
|
||||||
while getopts "tl:" option; do
|
|
||||||
case "${option}" in
|
|
||||||
l) LEVEL="$OPTARG"; shift $((OPTIND-1));;
|
|
||||||
t) TIME="$(printf "[%s] " "$(date +'%Y-%m-%dT%H:%M:%S.%s')")"; shift $((OPTIND-1));;
|
|
||||||
*) exit 1;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -t 2 ] && [ -z "${NO_COLOR-}" ]; then
|
|
||||||
case "${LEVEL}" in
|
|
||||||
debug) COLOR="$(tput setaf 3)";;
|
|
||||||
notice) COLOR="$(tput setaf 4)";;
|
|
||||||
warning) COLOR="$(tput setaf 5)";;
|
|
||||||
error) COLOR="$(tput setaf 1)";;
|
|
||||||
*) COLOR="$(tput sgr0)";;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "${LEVEL}" in
|
|
||||||
debug) LEVEL=100;;
|
|
||||||
notice) LEVEL=250;;
|
|
||||||
warning) LEVEL=300;;
|
|
||||||
error) LEVEL=400;;
|
|
||||||
*) LEVEL=200;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "${LOG_VERBOSE}" in
|
|
||||||
debug) LOG_VERBOSE_VALUE=100;;
|
|
||||||
notice) LOG_VERBOSE_VALUE=250;;
|
|
||||||
warning) LOG_VERBOSE_VALUE=300;;
|
|
||||||
error) LOG_VERBOSE_VALUE=400;;
|
|
||||||
*) LOG_VERBOSE_VALUE=200;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ $LEVEL -ge $LOG_VERBOSE_VALUE ]; then
|
|
||||||
printf "%s\n" "$*" | while IFS='' read -r LINE; do
|
|
||||||
printf "%s%s%s\n" "${COLOR:-}" "${TIME:-}" "$LINE" >&2
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
trap on_interrupt INT
|
|
||||||
|
|
||||||
main "$@"
|
|
||||||
|
|
||||||
|
|
@ -8,12 +8,9 @@
|
||||||
"beberlei/doctrineextensions": "^1.3",
|
"beberlei/doctrineextensions": "^1.3",
|
||||||
"friendsofsymfony/jsrouting-bundle": "^2.7",
|
"friendsofsymfony/jsrouting-bundle": "^2.7",
|
||||||
"gregwar/captcha-bundle": "^2.2",
|
"gregwar/captcha-bundle": "^2.2",
|
||||||
"guzzlehttp/guzzle": "^7.8",
|
|
||||||
"influxdata/influxdb-client-php": "^3.4",
|
|
||||||
"knplabs/knp-markdown-bundle": "^1.9",
|
"knplabs/knp-markdown-bundle": "^1.9",
|
||||||
"knplabs/knp-menu-bundle": "^3.1",
|
"knplabs/knp-menu-bundle": "^3.1",
|
||||||
"murph/murph-core": "dev-master",
|
"murph/murph-core": "^1.18",
|
||||||
"symfony/messenger": "5.4.*",
|
|
||||||
"twig/intl-extra": "^3.5"
|
"twig/intl-extra": "^3.5"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
@ -34,8 +31,7 @@
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"symfony/flex": true,
|
"symfony/flex": true,
|
||||||
"symfony/runtime": true,
|
"symfony/runtime": true
|
||||||
"php-http/discovery": true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
core:
|
core:
|
||||||
site:
|
site:
|
||||||
name: "Blog"
|
name: "Blog"
|
||||||
logo: "build/webapp/favicon.svg"
|
logo: "build/images/core/logo.svg"
|
||||||
controllers:
|
controllers:
|
||||||
- {name: 'LinkController:links', action: 'App\Controller\LinkController:links'}
|
- {name: 'LinkController:links', action: 'App\Controller\LinkController:links'}
|
||||||
- {name: 'ContactController::contact', action: 'App\Controller\ContactController::contact'}
|
- {name: 'ContactController::contact', action: 'App\Controller\ContactController::contact'}
|
||||||
|
|
@ -81,7 +81,6 @@ core:
|
||||||
- image/jpg
|
- image/jpg
|
||||||
- image/jpeg
|
- image/jpeg
|
||||||
- image/gif
|
- image/gif
|
||||||
- image/webp
|
|
||||||
- image/svg+xml
|
- image/svg+xml
|
||||||
- video/mp4
|
- video/mp4
|
||||||
- audio/mpeg3
|
- audio/mpeg3
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,18 @@ liip_imagine:
|
||||||
max: [600, 600]
|
max: [600, 600]
|
||||||
crop:
|
crop:
|
||||||
size: [600, 270]
|
size: [600, 270]
|
||||||
start: [0, 0]
|
|
||||||
project_preview_filter:
|
project_preview_filter:
|
||||||
filters:
|
filters:
|
||||||
downscale:
|
downscale:
|
||||||
max: [600, 600]
|
max: [600, 600]
|
||||||
crop:
|
crop:
|
||||||
size: [600, 270]
|
size: [600, 270]
|
||||||
start: [0, 0]
|
|
||||||
post_preview_filter:
|
post_preview_filter:
|
||||||
filters:
|
filters:
|
||||||
downscale:
|
downscale:
|
||||||
max: [600, 600]
|
max: [600, 600]
|
||||||
crop:
|
crop:
|
||||||
size: [600, 300]
|
size: [600, 300]
|
||||||
start: [0, 0]
|
|
||||||
site_avatar:
|
site_avatar:
|
||||||
filters:
|
filters:
|
||||||
downscale:
|
downscale:
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
framework:
|
|
||||||
messenger:
|
|
||||||
transports:
|
|
||||||
async: "%env(MESSENGER_TRANSPORT_DSN)%"
|
|
||||||
|
|
||||||
routing:
|
|
||||||
'App\Message\PageViewMessage': async
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
twig:
|
twig:
|
||||||
default_path: '%kernel.project_dir%/templates'
|
default_path: '%kernel.project_dir%/templates'
|
||||||
form_themes: ['form/bootstrap_4_form_theme.html.twig']
|
form_themes: ['@Core/form/bootstrap_4_form_theme.html.twig']
|
||||||
auto_reload: true
|
auto_reload: true
|
||||||
paths:
|
paths:
|
||||||
'%kernel.project_dir%/templates/core/': Core
|
'%kernel.project_dir%/templates/core/': Core
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,6 @@
|
||||||
# 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:
|
||||||
influxdb_url: '%env(INFLUXDB_URL)%'
|
|
||||||
influxdb_token: '%env(INFLUXDB_TOKEN)%'
|
|
||||||
influxdb_bucket: '%env(INFLUXDB_BUCKET)%'
|
|
||||||
influxdb_org: '%env(INFLUXDB_ORG)%'
|
|
||||||
influxdb_debug: '%env(INFLUXDB_DEBUG)%'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
# default configuration for services in *this* file
|
# default configuration for services in *this* file
|
||||||
|
|
@ -52,14 +47,6 @@ services:
|
||||||
resource: '../src/Controller/'
|
resource: '../src/Controller/'
|
||||||
tags: ['controller.service_arguments']
|
tags: ['controller.service_arguments']
|
||||||
|
|
||||||
App\Api\InfluxDB:
|
|
||||||
arguments:
|
|
||||||
$url: '%influxdb_url%'
|
|
||||||
$token: '%influxdb_token%'
|
|
||||||
$bucket: '%influxdb_bucket%'
|
|
||||||
$org: '%influxdb_org%'
|
|
||||||
$debug: '%influxdb_debug%'
|
|
||||||
|
|
||||||
site.route_loader:
|
site.route_loader:
|
||||||
class: App\Core\Router\SiteRouteLoader
|
class: App\Core\Router\SiteRouteLoader
|
||||||
tags: [routing.loader]
|
tags: [routing.loader]
|
||||||
|
|
@ -82,9 +69,5 @@ services:
|
||||||
tags:
|
tags:
|
||||||
- {name: markdown.parser, alias: comment}
|
- {name: markdown.parser, alias: comment}
|
||||||
|
|
||||||
App\EventListener\StatListener:
|
|
||||||
tags:
|
|
||||||
- { name: kernel.event_listener, event: kernel.request }
|
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
# add more service definitions when explicit configuration is needed
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|
|
||||||
10
package.json
10
package.json
|
|
@ -8,16 +8,13 @@
|
||||||
"build": "./node_modules/.bin/encore production"
|
"build": "./node_modules/.bin/encore production"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kangc/v-md-editor": "^1.7.12",
|
|
||||||
"daisyui": "^2.31.0",
|
"daisyui": "^2.31.0",
|
||||||
"editorjs-hyperlink": "^1.0.6",
|
|
||||||
"editorjs-inline-tool": "^0.4.0",
|
|
||||||
"encore": "^0.0.30-beta",
|
"encore": "^0.0.30-beta",
|
||||||
"lozad": "^1.16.0",
|
"lozad": "^1.16.0",
|
||||||
"mermaid": "^11.0.2",
|
"murph-project": "^1",
|
||||||
"murph-project": "^1.9.4",
|
|
||||||
"particles.js": "^2.0.0",
|
"particles.js": "^2.0.0",
|
||||||
"prismjs": "^1.23.0",
|
"prismjs": "^1.23.0",
|
||||||
|
"simplemde": "^1.11.2",
|
||||||
"tingle.js": "^0.16.0",
|
"tingle.js": "^0.16.0",
|
||||||
"vanillajs-datepicker": "^1.1.4",
|
"vanillajs-datepicker": "^1.1.4",
|
||||||
"vue": "^2.6.14"
|
"vue": "^2.6.14"
|
||||||
|
|
@ -28,6 +25,5 @@
|
||||||
"postcss": "^8.4.16",
|
"postcss": "^8.4.16",
|
||||||
"postcss-loader": "^7.0.1",
|
"postcss-loader": "^7.0.1",
|
||||||
"tailwindcss": "^3.1.8"
|
"tailwindcss": "^3.1.8"
|
||||||
},
|
}
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ class DateRangeAnalytic extends BaseDateRangeAnalytic
|
||||||
|
|
||||||
foreach ($entities as $key => $entity) {
|
foreach ($entities as $key => $entity) {
|
||||||
if ('view' === $type) {
|
if ('view' === $type) {
|
||||||
if (null === $this->path || str_starts_with($entity->getPath(), $this->path)) {
|
if ($this->path === null || str_starts_with($entity->getPath(), $this->path)) {
|
||||||
$newEntities[] = $entity;
|
$newEntities[] = $entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Api;
|
|
||||||
|
|
||||||
use InfluxDB2\Client;
|
|
||||||
use InfluxDB2\Model\WritePrecision;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class InfluxDB.
|
|
||||||
*
|
|
||||||
* @author Simon Vieille <simon@deblan.fr>
|
|
||||||
*/
|
|
||||||
class InfluxDB
|
|
||||||
{
|
|
||||||
protected ?Client $client = null;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
protected ?string $url,
|
|
||||||
protected ?string $token,
|
|
||||||
protected ?string $bucket,
|
|
||||||
protected ?string $org,
|
|
||||||
protected bool $debug = false
|
|
||||||
) {
|
|
||||||
if (isset($this->url, $this->token, $this->bucket, $this->org)) {
|
|
||||||
$this->client = new Client([
|
|
||||||
'url' => $this->url,
|
|
||||||
'token' => $this->token,
|
|
||||||
'bucket' => $this->bucket,
|
|
||||||
'org' => $this->org,
|
|
||||||
'debug' => $this->debug,
|
|
||||||
'precision' => WritePrecision::S,
|
|
||||||
'timeout' => 1,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAvailable(): bool
|
|
||||||
{
|
|
||||||
return null !== $this->getClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClient(): ?Client
|
|
||||||
{
|
|
||||||
return $this->client;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,7 +14,7 @@ class TTRssClient
|
||||||
$result = @file_get_contents('https://tiny.deblan.org/deblan_api/?itemsPerPage=10&page='.$page);
|
$result = @file_get_contents('https://tiny.deblan.org/deblan_api/?itemsPerPage=10&page='.$page);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$result = str_replace('\u0092', "'", $result);
|
$result = str_replace('\\u0092', "'", $result);
|
||||||
$result = str_replace(''', "'", $result);
|
$result = str_replace(''', "'", $result);
|
||||||
|
|
||||||
return json_decode($result, true);
|
return json_decode($result, true);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ class CategoryAdminController extends CrudController
|
||||||
->setMaxPerPage('index', 100)
|
->setMaxPerPage('index', 100)
|
||||||
|
|
||||||
->setView('form', 'blog/category_admin/_form.html.twig')
|
->setView('form', 'blog/category_admin/_form.html.twig')
|
||||||
->setDoubleClick('index', true)
|
|
||||||
|
|
||||||
->setDefaultSort('index', 'title', 'asc')
|
->setDefaultSort('index', 'title', 'asc')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,27 @@
|
||||||
|
|
||||||
namespace App\Controller\Blog;
|
namespace App\Controller\Blog;
|
||||||
|
|
||||||
use App\Analytic\DateRangeAnalytic;
|
|
||||||
use App\Core\Controller\Admin\Crud\CrudController;
|
use App\Core\Controller\Admin\Crud\CrudController;
|
||||||
use App\Core\Crud\CrudConfiguration;
|
use App\Core\Crud\CrudConfiguration;
|
||||||
use App\Core\Crud\Field\DatetimeField;
|
use App\Core\Crud\Field\DatetimeField;
|
||||||
use App\Core\Crud\Field\TextField;
|
use App\Core\Crud\Field\TextField;
|
||||||
use App\Core\Entity\EntityInterface;
|
|
||||||
use App\Core\Form\FileUploadHandler;
|
use App\Core\Form\FileUploadHandler;
|
||||||
use App\Core\Manager\EntityManager;
|
use App\Core\Manager\EntityManager;
|
||||||
use App\Core\Repository\Site\NodeRepository;
|
|
||||||
use App\Entity\Blog\Post;
|
|
||||||
use App\Entity\Blog\Post as Entity;
|
use App\Entity\Blog\Post as Entity;
|
||||||
use App\Factory\Blog\PostFactory as EntityFactory;
|
use App\Factory\Blog\PostFactory as EntityFactory;
|
||||||
use App\Form\Blog\Filter\PostFilterType;
|
use App\Form\Blog\Filter\PostFilterType;
|
||||||
use App\Form\Blog\PostType;
|
use App\Form\Blog\PostType;
|
||||||
|
use App\Form\Blog\PostType as EntityType;
|
||||||
use App\Repository\Blog\PostRepositoryQuery as RepositoryQuery;
|
use App\Repository\Blog\PostRepositoryQuery as RepositoryQuery;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
|
|
||||||
use Symfony\Component\Form\Form;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Form\Form;
|
||||||
|
use App\Core\Entity\EntityInterface;
|
||||||
|
use App\Entity\Blog\Post;
|
||||||
|
use App\Analytic\DateRangeAnalytic;
|
||||||
|
use App\Core\Repository\Site\NodeRepository;
|
||||||
|
|
||||||
#[Route(path: '/admin/blog/post')]
|
#[Route(path: '/admin/blog/post')]
|
||||||
class PostAdminController extends CrudController
|
class PostAdminController extends CrudController
|
||||||
|
|
@ -40,7 +37,6 @@ class PostAdminController extends CrudController
|
||||||
|
|
||||||
->setPageRoute('index', 'admin_blog_post_index')
|
->setPageRoute('index', 'admin_blog_post_index')
|
||||||
->setPageRoute('edit', 'admin_blog_post_edit')
|
->setPageRoute('edit', 'admin_blog_post_edit')
|
||||||
->setPageRoute('inline_edit', 'admin_blog_post_inline_edit')
|
|
||||||
->setPageRoute('new', 'admin_blog_post_new')
|
->setPageRoute('new', 'admin_blog_post_new')
|
||||||
->setPageRoute('show', 'admin_blog_post_show')
|
->setPageRoute('show', 'admin_blog_post_show')
|
||||||
->setPageRoute('delete', 'admin_blog_post_delete')
|
->setPageRoute('delete', 'admin_blog_post_delete')
|
||||||
|
|
@ -58,7 +54,6 @@ class PostAdminController extends CrudController
|
||||||
->setView('edit', 'blog/post_admin/edit.html.twig')
|
->setView('edit', 'blog/post_admin/edit.html.twig')
|
||||||
->setView('show', 'blog/post_admin/show.html.twig')
|
->setView('show', 'blog/post_admin/show.html.twig')
|
||||||
->setView('index', 'blog/post_admin/index.html.twig')
|
->setView('index', 'blog/post_admin/index.html.twig')
|
||||||
->setDoubleClick('index', true)
|
|
||||||
|
|
||||||
->setDefaultSort('index', 'id', 'desc')
|
->setDefaultSort('index', 'id', 'desc')
|
||||||
->setField('index', 'Titre', TextField::class, [
|
->setField('index', 'Titre', TextField::class, [
|
||||||
|
|
@ -67,7 +62,7 @@ class PostAdminController extends CrudController
|
||||||
'attr' => ['class' => 'miw-400'],
|
'attr' => ['class' => 'miw-400'],
|
||||||
])
|
])
|
||||||
->setField('index', 'ID', TextField::class, [
|
->setField('index', 'ID', TextField::class, [
|
||||||
'property_builder' => function (EntityInterface $entity) {
|
'property_builder' => function(EntityInterface $entity) {
|
||||||
return sprintf('#%d', $entity->getId());
|
return sprintf('#%d', $entity->getId());
|
||||||
},
|
},
|
||||||
'sort' => ['id', '.id'],
|
'sort' => ['id', '.id'],
|
||||||
|
|
@ -84,55 +79,19 @@ class PostAdminController extends CrudController
|
||||||
'format' => 'd/m/Y H:i',
|
'format' => 'd/m/Y H:i',
|
||||||
'sort' => ['publishedAt', '.publishedAt'],
|
'sort' => ['publishedAt', '.publishedAt'],
|
||||||
'attr' => ['class' => 'miw-200'],
|
'attr' => ['class' => 'miw-200'],
|
||||||
'inline_form' => function (FormBuilderInterface $builder) {
|
|
||||||
$builder->add(
|
|
||||||
'publishedAt',
|
|
||||||
DateTimeType::class,
|
|
||||||
[
|
|
||||||
'label' => 'Date de publication',
|
|
||||||
'required' => false,
|
|
||||||
'html5' => true,
|
|
||||||
'widget' => 'single_text',
|
|
||||||
'attr' => [
|
|
||||||
'data-datetime' => '',
|
|
||||||
],
|
|
||||||
'constraints' => [
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
->setField('index', 'Status', TextField::class, [
|
->setField('index', 'Status', TextField::class, [
|
||||||
'view' => 'blog/post_admin/field/status.html.twig',
|
'view' => 'blog/post_admin/field/status.html.twig',
|
||||||
'sort' => ['status', '.status'],
|
'sort' => ['status', '.status'],
|
||||||
'attr' => ['class' => 'miw-100'],
|
'attr' => ['class' => 'miw-100'],
|
||||||
'inline_form' => function (FormBuilderInterface $builder) {
|
|
||||||
$builder->add(
|
|
||||||
'status',
|
|
||||||
ChoiceType::class,
|
|
||||||
[
|
|
||||||
'label' => 'Statut',
|
|
||||||
'required' => true,
|
|
||||||
'choices' => [
|
|
||||||
'Brouillon' => Entity::DRAFT,
|
|
||||||
'Publié' => Entity::PUBLISHED,
|
|
||||||
],
|
|
||||||
'attr' => [
|
|
||||||
],
|
|
||||||
'constraints' => [
|
|
||||||
new NotBlank(),
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->delete($entity);
|
$manager->delete($entity);
|
||||||
})
|
})
|
||||||
->setBatchAction('index', 'draft', 'Statut : publier', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'draft', 'Statut : publier', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->update($entity->setStatus(Post::PUBLISHED));
|
$manager->update($entity->setStatus(Post::PUBLISHED));
|
||||||
})
|
})
|
||||||
->setBatchAction('index', 'publish', 'Statut : brouillon', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'publish', 'Statut : brouillon', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->update($entity->setStatus(Post::DRAFT));
|
$manager->update($entity->setStatus(Post::DRAFT));
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
|
|
@ -151,7 +110,7 @@ class PostAdminController extends CrudController
|
||||||
$factory->create($this->getUser()),
|
$factory->create($this->getUser()),
|
||||||
$entityManager,
|
$entityManager,
|
||||||
$request,
|
$request,
|
||||||
function (Entity $entity, Form $form, Request $request) use ($fileUpload) {
|
function(Entity $entity, Form $form, Request $request) use ($fileUpload) {
|
||||||
$directory = 'uploads/post/'.date('Y');
|
$directory = 'uploads/post/'.date('Y');
|
||||||
|
|
||||||
$fileUpload->handleForm(
|
$fileUpload->handleForm(
|
||||||
|
|
@ -172,7 +131,7 @@ class PostAdminController extends CrudController
|
||||||
$entity,
|
$entity,
|
||||||
$entityManager,
|
$entityManager,
|
||||||
$request,
|
$request,
|
||||||
function (Entity $entity, Form $form, Request $request) use ($fileUpload) {
|
function(Entity $entity, Form $form, Request $request) use ($fileUpload) {
|
||||||
$directory = 'uploads/post/'.date('Y');
|
$directory = 'uploads/post/'.date('Y');
|
||||||
|
|
||||||
$fileUpload->handleForm(
|
$fileUpload->handleForm(
|
||||||
|
|
@ -186,12 +145,6 @@ class PostAdminController extends CrudController
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/inline_edit/{entity}/{context}/{label}', name: 'admin_blog_post_inline_edit', methods: ['GET', 'POST'])]
|
|
||||||
public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response
|
|
||||||
{
|
|
||||||
return $this->doInlineEdit($context, $label, $entity, $entityManager, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/show/{entity}', name: 'admin_blog_post_show')]
|
#[Route(path: '/show/{entity}', name: 'admin_blog_post_show')]
|
||||||
public function show(Entity $entity): Response
|
public function show(Entity $entity): Response
|
||||||
{
|
{
|
||||||
|
|
@ -267,7 +220,8 @@ class PostAdminController extends CrudController
|
||||||
DateRangeAnalytic $analytic,
|
DateRangeAnalytic $analytic,
|
||||||
NodeRepository $nodeRepository,
|
NodeRepository $nodeRepository,
|
||||||
string $range = '7days'
|
string $range = '7days'
|
||||||
): Response {
|
): Response
|
||||||
|
{
|
||||||
if (!in_array($range, ['7days', '30days', '90days', '1year'])) {
|
if (!in_array($range, ['7days', '30days', '90days', '1year'])) {
|
||||||
throw $this->createNotFoundException();
|
throw $this->createNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,19 +7,19 @@ use App\Core\Controller\Site\PageController;
|
||||||
use App\Core\Manager\EntityManager;
|
use App\Core\Manager\EntityManager;
|
||||||
use App\Core\Site\SiteRequest;
|
use App\Core\Site\SiteRequest;
|
||||||
use App\Core\Site\SiteStore;
|
use App\Core\Site\SiteStore;
|
||||||
use App\Core\Twig\Extension\BuilderExtension;
|
|
||||||
use App\Core\Twig\Extension\EditorJsExtension;
|
|
||||||
use App\Entity\Blog\Category;
|
use App\Entity\Blog\Category;
|
||||||
use App\Entity\Blog\Post;
|
use App\Entity\Blog\Post;
|
||||||
use App\Factory\Blog\CommentFactory;
|
use App\Factory\Blog\CommentFactory;
|
||||||
use App\Form\Blog\UserCommentType;
|
use App\Form\Blog\UserCommentType;
|
||||||
use App\Manager\PostFollowManager;
|
|
||||||
use App\Markdown\Parser\Post as PostParser;
|
use App\Markdown\Parser\Post as PostParser;
|
||||||
use App\Repository\Blog\PostRepositoryQuery;
|
use App\Repository\Blog\PostRepositoryQuery;
|
||||||
use App\UrlGenerator\PostGenerator;
|
use App\UrlGenerator\PostGenerator;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
|
use App\Factory\Blog\PostFollowFactory;
|
||||||
|
use App\Manager\PostFollowManager;
|
||||||
|
use App\Core\Twig\Extension\EditorJsExtension;
|
||||||
|
|
||||||
class PostController extends PageController
|
class PostController extends PageController
|
||||||
{
|
{
|
||||||
|
|
@ -92,9 +92,9 @@ class PostController extends PageController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function posts(Request $request, int $page = 1): Response
|
public function posts(int $page = 1): Response
|
||||||
{
|
{
|
||||||
$entities = $this->createQuery($request->query->has('preview') && $this->getUser())
|
$entities = $this->createQuery()
|
||||||
->paginate($page, 9)
|
->paginate($page, 9)
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -149,34 +149,26 @@ class PostController extends PageController
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tag(string $tag, int $page = 1): Response {}
|
public function tag(string $tag, int $page = 1): Response
|
||||||
|
|
||||||
public function createQuery(bool $isPreview = false): PostRepositoryQuery
|
|
||||||
{
|
{
|
||||||
$query = $this->postQuery->create()
|
|
||||||
->orderBy('.publishedAt', 'DESC')
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!$isPreview) {
|
|
||||||
$query->published();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rss(
|
public function createQuery(): PostRepositoryQuery
|
||||||
PostParser $parser,
|
{
|
||||||
EditorJsExtension $editorJsExtension,
|
return $this->postQuery->create()
|
||||||
BuilderExtension $builderExtension
|
->orderBy('.publishedAt', 'DESC')
|
||||||
): Response {
|
->published()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rss(PostParser $parser, EditorJsExtension $editorJsExtension): Response
|
||||||
|
{
|
||||||
$entities = $this->createQuery()->paginate(1, 20);
|
$entities = $this->createQuery()->paginate(1, 20);
|
||||||
$items = [];
|
$items = [];
|
||||||
|
|
||||||
foreach ($entities as $entity) {
|
foreach ($entities as $entity) {
|
||||||
if ('editorjs' === $entity->getContentFormat()) {
|
if ($entity->getContentFormat() === 'editorjs') {
|
||||||
$description = $editorJsExtension->buildHtml($entity->getContent());
|
$description = $editorJsExtension->buildHtml($entity->getContent());
|
||||||
} elseif ('builder' === $entity->getContentFormat()) {
|
|
||||||
$description = $builderExtension->buildHtml($entity->getContent());
|
|
||||||
} else {
|
} else {
|
||||||
$description = $parser->transformMarkdown($entity->getContent());
|
$description = $parser->transformMarkdown($entity->getContent());
|
||||||
}
|
}
|
||||||
|
|
@ -197,7 +189,7 @@ class PostController extends PageController
|
||||||
'post' => $entity->getId(),
|
'post' => $entity->getId(),
|
||||||
'slug' => $entity->getSlug(),
|
'slug' => $entity->getSlug(),
|
||||||
], UrlGeneratorInterface::ABSOLUTE_URL),
|
], UrlGeneratorInterface::ABSOLUTE_URL),
|
||||||
'linkGemini' => sprintf('gemini://deblan.fr/posts/%d.gmi', $entity->getId()),
|
'linkGemini' => sprintf('gemini://deblan.io/posts/%d.gmi', $entity->getId()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ namespace App\Controller;
|
||||||
|
|
||||||
use App\Core\Controller\Site\PageController;
|
use App\Core\Controller\Site\PageController;
|
||||||
use App\Core\Notification\MailNotifier;
|
use App\Core\Notification\MailNotifier;
|
||||||
use App\Core\Setting\SettingManager;
|
|
||||||
use App\Form\ContactType;
|
use App\Form\ContactType;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use App\Core\Setting\SettingManager;
|
||||||
|
|
||||||
class ContactController extends PageController
|
class ContactController extends PageController
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
use App\Core\Controller\Dashboard\DashboardAdminController as Controller;
|
use App\Core\Controller\Dashboard\DashboardAdminController as Controller;
|
||||||
use App\Repository\Blog\PostRepositoryQuery;
|
|
||||||
use App\Repository\ProjectRepositoryQuery;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
|
@ -12,23 +10,8 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||||
class DashboardAdminController extends Controller
|
class DashboardAdminController extends Controller
|
||||||
{
|
{
|
||||||
#[Route(path: '/', name: 'admin_dashboard_index')]
|
#[Route(path: '/', name: 'admin_dashboard_index')]
|
||||||
public function index(
|
public function index(): Response
|
||||||
PostRepositoryQuery $postQuery,
|
{
|
||||||
ProjectRepositoryQuery $projectQuery
|
return $this->render('admin/dashboard.html.twig');
|
||||||
): Response {
|
|
||||||
$posts = $postQuery->create()
|
|
||||||
->orderBy('.id', 'DESC')
|
|
||||||
->paginate(1, 4)
|
|
||||||
;
|
|
||||||
|
|
||||||
$projects = $projectQuery->create()
|
|
||||||
->orderBy('.id', 'DESC')
|
|
||||||
->paginate(1, 3)
|
|
||||||
;
|
|
||||||
|
|
||||||
return $this->render('admin/dashboard.html.twig', [
|
|
||||||
'posts' => $posts,
|
|
||||||
'projects' => $projects,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ use App\Core\Crud\CrudConfiguration;
|
||||||
use App\Core\Crud\Field;
|
use App\Core\Crud\Field;
|
||||||
use App\Core\Entity\EntityInterface;
|
use App\Core\Entity\EntityInterface;
|
||||||
use App\Core\Manager\EntityManager;
|
use App\Core\Manager\EntityManager;
|
||||||
use App\Entity\Project;
|
|
||||||
use App\Entity\Project as Entity;
|
use App\Entity\Project as Entity;
|
||||||
use App\Factory\ProjectFactory as Factory;
|
use App\Factory\ProjectFactory as Factory;
|
||||||
use App\Form\ProjectType as Type;
|
use App\Form\ProjectType as Type;
|
||||||
|
|
@ -16,6 +15,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use App\Entity\Project;
|
||||||
|
|
||||||
class ProjectAdminController extends CrudController
|
class ProjectAdminController extends CrudController
|
||||||
{
|
{
|
||||||
|
|
@ -114,13 +114,13 @@ class ProjectAdminController extends CrudController
|
||||||
'attr' => ['class' => 'miw-100'],
|
'attr' => ['class' => 'miw-100'],
|
||||||
])
|
])
|
||||||
|
|
||||||
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->delete($entity);
|
$manager->delete($entity);
|
||||||
})
|
})
|
||||||
->setBatchAction('index', 'draft', 'Statut : publier', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'draft', 'Statut : publier', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->update($entity->setStatus(Project::PUBLISHED));
|
$manager->update($entity->setStatus(Project::PUBLISHED));
|
||||||
})
|
})
|
||||||
->setBatchAction('index', 'publish', 'Statut : brouillon', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'publish', 'Statut : brouillon', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->update($entity->setStatus(Project::DRAFT));
|
$manager->update($entity->setStatus(Project::DRAFT));
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ class StlMeshAdminController extends CrudController
|
||||||
'attr' => ['class' => 'col-md-12'],
|
'attr' => ['class' => 'col-md-12'],
|
||||||
])
|
])
|
||||||
|
|
||||||
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
|
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
|
||||||
$manager->delete($entity);
|
$manager->delete($entity);
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Api\TTRssClient;
|
||||||
use App\Core\Controller\Site\PageController;
|
use App\Core\Controller\Site\PageController;
|
||||||
use App\Entity\StlMesh;
|
use App\Markdown\Parser\Post as PostParser;
|
||||||
use App\Repository\StlMeshRepositoryQuery;
|
|
||||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use App\Repository\StlMeshRepositoryQuery;
|
||||||
|
use App\Entity\StlMesh;
|
||||||
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
|
@ -16,8 +18,7 @@ class StlMeshController extends PageController
|
||||||
{
|
{
|
||||||
$pager = $query->create()
|
$pager = $query->create()
|
||||||
->orderBy('.sortOrder')
|
->orderBy('.sortOrder')
|
||||||
->paginate(1, 200)
|
->paginate(1, 200);
|
||||||
;
|
|
||||||
|
|
||||||
return $this->defaultRender($this->siteRequest->getPage()->getTemplate(), [
|
return $this->defaultRender($this->siteRequest->getPage()->getTemplate(), [
|
||||||
'pager' => $pager,
|
'pager' => $pager,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Api\TTRssClient;
|
||||||
use App\Core\Controller\Site\PageController;
|
use App\Core\Controller\Site\PageController;
|
||||||
|
use App\Markdown\Parser\Post as PostParser;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class TextController extends PageController
|
class TextController extends PageController
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Controller;
|
|
||||||
|
|
||||||
use App\Core\Controller\User\UserAdminController as BaseUserAdminController;
|
|
||||||
use App\Core\Crud\CrudConfiguration;
|
|
||||||
use App\Core\Factory\UserFactory as Factory;
|
|
||||||
use App\Core\Manager\EntityManager;
|
|
||||||
use App\Core\Security\TokenGenerator;
|
|
||||||
use App\Entity\User as Entity;
|
|
||||||
use App\Repository\UserRepositoryQuery as RepositoryQuery;
|
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\HttpFoundation\Session\Session;
|
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
|
||||||
|
|
||||||
class UserAdminController extends BaseUserAdminController
|
|
||||||
{
|
|
||||||
#[Route(path: '/admin/user/{page}', name: 'admin_user_index', methods: ['GET'], requirements: ['page' => '\d+'])]
|
|
||||||
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
|
|
||||||
{
|
|
||||||
return parent::index($query, $request, $session, $page);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/new', name: 'admin_user_new', methods: ['GET', 'POST'])]
|
|
||||||
public function new(Factory $factory, EntityManager $entityManager, Request $request, TokenGenerator $tokenGenerator): Response
|
|
||||||
{
|
|
||||||
return parent::new($factory, $entityManager, $request, $tokenGenerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])]
|
|
||||||
public function show(Entity $entity): Response
|
|
||||||
{
|
|
||||||
return parent::show($entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])]
|
|
||||||
public function filter(Session $session): Response
|
|
||||||
{
|
|
||||||
return parent::filter($session);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])]
|
|
||||||
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
|
|
||||||
{
|
|
||||||
return parent::edit($entity, $entityManager, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/inline_edit/{entity}/{context}/{label}', name: 'admin_user_inline_edit', methods: ['GET', 'POST'])]
|
|
||||||
public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response
|
|
||||||
{
|
|
||||||
return parent::inlineEdit($context, $label, $entity, $entityManager, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/delete/{entity}', name: 'admin_user_delete', methods: ['DELETE', 'POST'])]
|
|
||||||
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
|
|
||||||
{
|
|
||||||
return parent::delete($entity, $entityManager, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[Route(path: '/admin/user/resetting_request/{entity}', name: 'admin_user_resetting_request', methods: ['POST'])]
|
|
||||||
public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response
|
|
||||||
{
|
|
||||||
return parent::requestResetting($entity, $eventDispatcher, $request);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getConfiguration(): CrudConfiguration
|
|
||||||
{
|
|
||||||
if ($this->configuration) {
|
|
||||||
return $this->configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getConfiguration()
|
|
||||||
->setView('form', 'admin/user_admin/_form.html.twig')
|
|
||||||
->setView('show_entity', 'admin/user_admin/_show.html.twig')
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,6 +7,9 @@ use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||||
|
|
||||||
class AppExtension extends Extension
|
class AppExtension extends Extension
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function load(array $configs, ContainerBuilder $container)
|
public function load(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
$configuration = $this->getConfiguration($configs, $container);
|
$configuration = $this->getConfiguration($configs, $container);
|
||||||
|
|
@ -15,6 +18,9 @@ class AppExtension extends Extension
|
||||||
$container->setParameter('app', $config);
|
$container->setParameter('app', $config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function getConfiguration(array $configs, ContainerBuilder $container)
|
public function getConfiguration(array $configs, ContainerBuilder $container)
|
||||||
{
|
{
|
||||||
return new Configuration();
|
return new Configuration();
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,6 @@ class Comment implements EntityInterface
|
||||||
$this->postFollows = new ArrayCollection();
|
$this->postFollows = new ArrayCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString()
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
'[%s] (%s) %s',
|
|
||||||
$this->getAuthor(),
|
|
||||||
$this->getCreatedAt()->format('d/m/Y'),
|
|
||||||
substr($this->getContent(), 0, 20).'…'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
|
@ -199,12 +189,25 @@ class Comment implements EntityInterface
|
||||||
*/
|
*/
|
||||||
public function getAvatar(): string
|
public function getAvatar(): string
|
||||||
{
|
{
|
||||||
$mail = $this->getEmail() ?? sprintf('%d@deblan.fr', $this->getId());
|
$mail = $this->getEmail() ?? sprintf('%d@deblan.io', $this->getId());
|
||||||
$hash = md5($mail);
|
$hash = md5($mail);
|
||||||
|
|
||||||
return 'https://cdn.libravatar.org/avatar/'.$hash.'?s=90&d=retro';
|
return 'https://cdn.libravatar.org/avatar/'.$hash.'?s=90&d=retro';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'[%s] (%s) %s',
|
||||||
|
$this->getAuthor(),
|
||||||
|
$this->getCreatedAt()->format('d/m/Y'),
|
||||||
|
substr($this->getContent(), 0, 20).'…'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection|PostFollow[]
|
* @return Collection|PostFollow[]
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ class Post implements EntityInterface
|
||||||
{
|
{
|
||||||
use Timestampable;
|
use Timestampable;
|
||||||
|
|
||||||
public const DRAFT = 0;
|
const DRAFT = 0;
|
||||||
public const PUBLISHED = 1;
|
const PUBLISHED = 1;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
|
|
@ -87,21 +87,11 @@ class Post implements EntityInterface
|
||||||
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
#[ORM\Column(type: 'string', length: 255, nullable: true)]
|
||||||
private $image2;
|
private $image2;
|
||||||
|
|
||||||
#[ORM\Column(type: 'array')]
|
|
||||||
private $parameters = [];
|
|
||||||
|
|
||||||
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'deprecatedPosts')]
|
|
||||||
private $recommandedPost;
|
|
||||||
|
|
||||||
#[ORM\OneToMany(mappedBy: 'recommandedPost', targetEntity: self::class)]
|
|
||||||
private $deprecatedPosts;
|
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->categories = new ArrayCollection();
|
$this->categories = new ArrayCollection();
|
||||||
$this->comments = new ArrayCollection();
|
$this->comments = new ArrayCollection();
|
||||||
$this->postFollows = new ArrayCollection();
|
$this->postFollows = new ArrayCollection();
|
||||||
$this->deprecatedPosts = new ArrayCollection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
|
|
@ -202,7 +192,7 @@ class Post implements EntityInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Category[]|Collection
|
* @return Collection|Category[]
|
||||||
*/
|
*/
|
||||||
public function getCategories(): Collection
|
public function getCategories(): Collection
|
||||||
{
|
{
|
||||||
|
|
@ -448,80 +438,4 @@ class Post implements EntityInterface
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getParameters(): ?array
|
|
||||||
{
|
|
||||||
$params = is_array($this->parameters) ? $this->parameters : [];
|
|
||||||
$names = array_map(fn (array $param): string => $param['name'], $params);
|
|
||||||
$defaultParams = [
|
|
||||||
['name' => 'podcast', 'value' => 0],
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($defaultParams as $defaultParam) {
|
|
||||||
if (!in_array($defaultParam['name'], $names)) {
|
|
||||||
$params[] = $defaultParam;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $params;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setParameters(array $parameters): self
|
|
||||||
{
|
|
||||||
$this->parameters = $parameters;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getParameter($name): ?string
|
|
||||||
{
|
|
||||||
return array_filter(
|
|
||||||
$this->getParameters(),
|
|
||||||
function (array $param) use ($name): bool {
|
|
||||||
return $name === $param['name'];
|
|
||||||
}
|
|
||||||
)[0]['value'] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRecommandedPost(): ?self
|
|
||||||
{
|
|
||||||
return $this->recommandedPost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setRecommandedPost(?self $recommandedPost): self
|
|
||||||
{
|
|
||||||
$this->recommandedPost = $recommandedPost;
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Collection<int, self>
|
|
||||||
*/
|
|
||||||
public function getDeprecatedPosts(): Collection
|
|
||||||
{
|
|
||||||
return $this->deprecatedPosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addDeprecatedPost(self $deprecatedPost): self
|
|
||||||
{
|
|
||||||
if (!$this->deprecatedPosts->contains($deprecatedPost)) {
|
|
||||||
$this->deprecatedPosts[] = $deprecatedPost;
|
|
||||||
$deprecatedPost->setRecommandedPost($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeDeprecatedPost(self $deprecatedPost): self
|
|
||||||
{
|
|
||||||
if ($this->deprecatedPosts->removeElement($deprecatedPost)) {
|
|
||||||
// set the owning side to null (unless already changed)
|
|
||||||
if ($deprecatedPost->getRecommandedPost() === $this) {
|
|
||||||
$deprecatedPost->setRecommandedPost(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class CategoriesPage extends TitledPage {}
|
class CategoriesPage extends TitledPage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class CategoryPage extends TitledPage {}
|
class CategoryPage extends TitledPage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class ContactPage extends SimplePage {}
|
class ContactPage extends SimplePage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class LinksPage extends SimplePage {}
|
class LinksPage extends SimplePage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class MeshPage extends SimplePage {}
|
class MeshPage extends SimplePage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class PostPage extends TitledPage {}
|
class PostPage extends TitledPage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class PostsPage extends TitledPage {}
|
class PostsPage extends TitledPage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,6 @@ namespace App\Entity\Page;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class SearchPage extends TitledPage {}
|
class SearchPage extends TitledPage
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ namespace App\Entity\Page;
|
||||||
|
|
||||||
use App\Core\Entity\Site\Page\Block;
|
use App\Core\Entity\Site\Page\Block;
|
||||||
use App\Core\Entity\Site\Page\FileBlock;
|
use App\Core\Entity\Site\Page\FileBlock;
|
||||||
use App\Form\MarkdownBlockType;
|
use App\Form\Type\SimpleMdTextareaBlockType;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ class SimplePage extends TitledPage
|
||||||
|
|
||||||
$builder->add(
|
$builder->add(
|
||||||
'content',
|
'content',
|
||||||
MarkdownBlockType::class,
|
SimpleMdTextareaBlockType::class,
|
||||||
[
|
[
|
||||||
'label' => 'Contenu',
|
'label' => 'Contenu',
|
||||||
'options' => [
|
'options' => [
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@
|
||||||
namespace App\Entity\Page;
|
namespace App\Entity\Page;
|
||||||
|
|
||||||
use App\Core\Entity\Site\Page\Block;
|
use App\Core\Entity\Site\Page\Block;
|
||||||
use App\Core\Entity\Site\Page\Page;
|
use App\Core\Entity\Site\Page\FileBlock;
|
||||||
use App\Core\Form\Site\Page\TextareaBlockType;
|
use App\Core\Form\Site\Page\TextareaBlockType;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use App\Core\Entity\Site\Page\Page;
|
||||||
|
|
||||||
#[ORM\Entity]
|
#[ORM\Entity]
|
||||||
class TextPage extends Page
|
class TextPage extends Page
|
||||||
|
|
@ -27,14 +28,6 @@ class TextPage extends Page
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
$builder
|
|
||||||
->remove('metaTitle')
|
|
||||||
->remove('metaDescription')
|
|
||||||
->remove('ogTitle')
|
|
||||||
->remove('ogDescription')
|
|
||||||
->remove('ogImage')
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setContent(Block $block)
|
public function setContent(Block $block)
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ class Project implements EntityInterface
|
||||||
{
|
{
|
||||||
use Timestampable;
|
use Timestampable;
|
||||||
|
|
||||||
public const DRAFT = 0;
|
const DRAFT = 0;
|
||||||
public const PUBLISHED = 1;
|
const PUBLISHED = 1;
|
||||||
|
|
||||||
#[ORM\Id]
|
#[ORM\Id]
|
||||||
#[ORM\GeneratedValue]
|
#[ORM\GeneratedValue]
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use App\Core\Entity\EntityInterface;
|
|
||||||
use App\Repository\StlMeshRepository;
|
use App\Repository\StlMeshRepository;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Core\Entity\EntityInterface;
|
||||||
|
|
||||||
#[ORM\Entity(repositoryClass: StlMeshRepository::class)]
|
#[ORM\Entity(repositoryClass: StlMeshRepository::class)]
|
||||||
class StlMesh implements EntityInterface
|
class StlMesh implements EntityInterface
|
||||||
|
|
@ -86,7 +86,7 @@ class StlMesh implements EntityInterface
|
||||||
{
|
{
|
||||||
$this->files = (array) $this->files;
|
$this->files = (array) $this->files;
|
||||||
|
|
||||||
usort($this->files, function ($a, $b) {
|
usort($this->files, function($a, $b) {
|
||||||
if ($a['position'] > $b['position']) {
|
if ($a['position'] > $b['position']) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,9 @@ class User implements PasswordAuthenticatedUserInterface, UserInterface, TwoFact
|
||||||
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
#[ORM\Column(type: 'boolean', options: ['default' => 0])]
|
||||||
private $isWriter;
|
private $isWriter;
|
||||||
|
|
||||||
public function __construct() {}
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\EventListener;
|
|
||||||
|
|
||||||
use App\Message\PageViewMessage;
|
|
||||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
|
||||||
use Symfony\Component\Messenger\MessageBusInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* class StatListener.
|
|
||||||
*
|
|
||||||
* @author Simon Vieille <simon@deblan.fr>
|
|
||||||
*/
|
|
||||||
class StatListener
|
|
||||||
{
|
|
||||||
public function __construct(protected MessageBusInterface $bus) {}
|
|
||||||
|
|
||||||
public function onKernelRequest(RequestEvent $event)
|
|
||||||
{
|
|
||||||
$this->bus->dispatch(new PageViewMessage(time()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,12 +4,11 @@ namespace App\EventSubscriber;
|
||||||
|
|
||||||
use App\Core\Event\Setting\SettingEvent;
|
use App\Core\Event\Setting\SettingEvent;
|
||||||
use App\Core\EventSubscriber\SettingEventSubscriber as EventSubscriber;
|
use App\Core\EventSubscriber\SettingEventSubscriber as EventSubscriber;
|
||||||
use App\Core\Form\FileManager\FilePickerType;
|
|
||||||
use App\Core\Form\Type\TinymceTextareaType;
|
|
||||||
use App\Core\Setting\SettingManager;
|
use App\Core\Setting\SettingManager;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
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 App\Core\Form\FileManager\FilePickerType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* class SettingEventSubscriber.
|
* class SettingEventSubscriber.
|
||||||
|
|
@ -37,7 +36,6 @@ class SettingEventSubscriber extends EventSubscriber
|
||||||
|
|
||||||
$this->manager->init('stats_umami_url', '📊 Statistiques', 'Adresse tableau de bord Umami', '');
|
$this->manager->init('stats_umami_url', '📊 Statistiques', 'Adresse tableau de bord Umami', '');
|
||||||
$this->manager->init('stats_umami_tag', '📊 Statistiques', 'Script Umami', '');
|
$this->manager->init('stats_umami_tag', '📊 Statistiques', 'Script Umami', '');
|
||||||
$this->manager->init('stats_grafana_url', '📊 Statistiques', 'Adresse tableau de bord Grafana', '');
|
|
||||||
|
|
||||||
$this->manager->init('post_author_description', '🖊️ Article', 'Description auteur', '');
|
$this->manager->init('post_author_description', '🖊️ Article', 'Description auteur', '');
|
||||||
|
|
||||||
|
|
@ -70,7 +68,7 @@ class SettingEventSubscriber extends EventSubscriber
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($entity->getCode(), ['giphy_api_key', 'stats_umami_url', 'stats_grafana_url'])) {
|
if (in_array($entity->getCode(), ['giphy_api_key', 'stats_umami_url'])) {
|
||||||
$builder->add(
|
$builder->add(
|
||||||
'value',
|
'value',
|
||||||
TextType::class,
|
TextType::class,
|
||||||
|
|
@ -95,7 +93,7 @@ class SettingEventSubscriber extends EventSubscriber
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($entity->getCode(), ['post_author_description'])) {
|
if (in_array($entity->getCode(), ['blog_footer', 'post_author_description'])) {
|
||||||
$event->setOption('view', 'large');
|
$event->setOption('view', 'large');
|
||||||
|
|
||||||
$builder->add(
|
$builder->add(
|
||||||
|
|
@ -109,20 +107,5 @@ class SettingEventSubscriber extends EventSubscriber
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($entity->getCode(), ['blog_footer'])) {
|
|
||||||
$event->setOption('view', 'large');
|
|
||||||
|
|
||||||
$builder->add(
|
|
||||||
'value',
|
|
||||||
TinymceTextareaType::class,
|
|
||||||
[
|
|
||||||
'label' => $entity->getLabel(),
|
|
||||||
'attr' => [
|
|
||||||
'rows' => 20,
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,6 @@ use App\Core\Factory\UserFactory as BaseUserFactory;
|
||||||
*
|
*
|
||||||
* @author Simon Vieille <simon@deblan.fr>
|
* @author Simon Vieille <simon@deblan.fr>
|
||||||
*/
|
*/
|
||||||
class UserFactory extends BaseUserFactory {}
|
class UserFactory extends BaseUserFactory
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@
|
||||||
namespace App\Form\Blog;
|
namespace App\Form\Blog;
|
||||||
|
|
||||||
use App\Entity\Blog\Category;
|
use App\Entity\Blog\Category;
|
||||||
use Doctrine\ORM\EntityRepository;
|
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
|
||||||
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\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
|
|
@ -12,6 +10,8 @@ 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;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
|
|
||||||
class CategoryType extends AbstractType
|
class CategoryType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Blog;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
|
||||||
|
|
||||||
class PostParameterType extends AbstractType
|
|
||||||
{
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
||||||
{
|
|
||||||
$builder->add(
|
|
||||||
'name',
|
|
||||||
TextType::class,
|
|
||||||
[
|
|
||||||
'required' => true,
|
|
||||||
'attr' => [
|
|
||||||
],
|
|
||||||
'constraints' => [
|
|
||||||
new NotBlank(),
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$builder->add(
|
|
||||||
'value',
|
|
||||||
TextType::class,
|
|
||||||
[
|
|
||||||
'required' => false,
|
|
||||||
'attr' => [
|
|
||||||
],
|
|
||||||
'constraints' => [
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
|
||||||
{
|
|
||||||
$resolver->setDefaults([
|
|
||||||
'data_class' => null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Blog;
|
namespace App\Form\Blog;
|
||||||
|
|
||||||
use App\Core\Form\FileManager\FilePickerType;
|
|
||||||
use App\Core\Form\Type\BuilderType;
|
|
||||||
use App\Core\Form\Type\CollectionType as MurphCollectionType;
|
|
||||||
use App\Core\Form\Type\EditorJsTextareaType;
|
|
||||||
use App\Entity\Blog\Category;
|
use App\Entity\Blog\Category;
|
||||||
use App\Entity\Blog\Post;
|
use App\Entity\Blog\Post;
|
||||||
use App\Form\MarkdownType;
|
|
||||||
use Doctrine\ORM\EntityRepository;
|
use Doctrine\ORM\EntityRepository;
|
||||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
|
@ -26,6 +21,9 @@ use Symfony\Component\Validator\Constraints\Image;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
use Symfony\Component\Validator\Constraints\Range;
|
use Symfony\Component\Validator\Constraints\Range;
|
||||||
use Symfony\Component\Validator\Constraints\Url;
|
use Symfony\Component\Validator\Constraints\Url;
|
||||||
|
use App\Form\Type\SimpleMdTextareaType;
|
||||||
|
use App\Core\Form\Type\EditorJsTextareaType;
|
||||||
|
use App\Core\Form\FileManager\FilePickerType;
|
||||||
|
|
||||||
class PostType extends AbstractType
|
class PostType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
@ -52,7 +50,6 @@ class PostType extends AbstractType
|
||||||
'required' => true,
|
'required' => true,
|
||||||
'choices' => [
|
'choices' => [
|
||||||
'Markdown' => 'markdown',
|
'Markdown' => 'markdown',
|
||||||
'Builder' => 'builder',
|
|
||||||
'HTML' => 'html',
|
'HTML' => 'html',
|
||||||
'Editor JS' => 'editorjs',
|
'Editor JS' => 'editorjs',
|
||||||
],
|
],
|
||||||
|
|
@ -63,9 +60,8 @@ class PostType extends AbstractType
|
||||||
);
|
);
|
||||||
|
|
||||||
$types = [
|
$types = [
|
||||||
'markdown' => MarkdownType::class,
|
'markdown' => SimpleMdTextareaType::class,
|
||||||
'builder' => BuilderType::class,
|
'html' => SimpleMdTextareaType::class,
|
||||||
'html' => MarkdownType::class,
|
|
||||||
'editorjs' => EditorJsTextareaType::class,
|
'editorjs' => EditorJsTextareaType::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -116,28 +112,6 @@ class PostType extends AbstractType
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
$builder->add(
|
|
||||||
'recommandedPost',
|
|
||||||
EntityType::class,
|
|
||||||
[
|
|
||||||
'label' => 'Article recommandé',
|
|
||||||
'class' => Post::class,
|
|
||||||
'choice_label' => 'title',
|
|
||||||
'required' => false,
|
|
||||||
'multiple' => false,
|
|
||||||
'attr' => [
|
|
||||||
'data-jschoice' => '',
|
|
||||||
],
|
|
||||||
'query_builder' => function (EntityRepository $repo) {
|
|
||||||
return $repo->createQueryBuilder('a')
|
|
||||||
->orderBy('a.title', 'ASC')
|
|
||||||
;
|
|
||||||
},
|
|
||||||
'constraints' => [
|
|
||||||
],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$builder->add(
|
$builder->add(
|
||||||
'status',
|
'status',
|
||||||
ChoiceType::class,
|
ChoiceType::class,
|
||||||
|
|
@ -197,7 +171,6 @@ class PostType extends AbstractType
|
||||||
'attr' => [
|
'attr' => [
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
new Image(),
|
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
@ -325,20 +298,6 @@ class PostType extends AbstractType
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
// $builder->add(
|
|
||||||
// 'parameters',
|
|
||||||
// MurphCollectionType::class,
|
|
||||||
// [
|
|
||||||
// 'label' => 'Paramètres',
|
|
||||||
// 'entry_type' => PostParameterType::class,
|
|
||||||
// 'by_reference' => false,
|
|
||||||
// 'allow_add' => true,
|
|
||||||
// 'allow_delete' => true,
|
|
||||||
// 'prototype' => true,
|
|
||||||
// 'row_attr' => ['class' => 'mb-3'],
|
|
||||||
// ]
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver)
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,7 @@
|
||||||
namespace App\Form\Blog;
|
namespace App\Form\Blog;
|
||||||
|
|
||||||
use App\Entity\Blog\Comment;
|
use App\Entity\Blog\Comment;
|
||||||
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
|
||||||
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\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
|
|
@ -16,6 +14,8 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Validator\Constraints\Email;
|
use Symfony\Component\Validator\Constraints\Email;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
use Symfony\Component\Validator\Constraints\Url;
|
use Symfony\Component\Validator\Constraints\Url;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
||||||
|
|
||||||
class UserCommentType extends AbstractType
|
class UserCommentType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
|
|
@ -11,6 +10,7 @@ use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
use Symfony\Component\Validator\Constraints\Email;
|
use Symfony\Component\Validator\Constraints\Email;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Gregwar\CaptchaBundle\Type\CaptchaType;
|
||||||
|
|
||||||
class ContactType extends AbstractType
|
class ContactType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form;
|
|
||||||
|
|
||||||
use App\Core\Form\Site\Page\TextareaBlockType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
|
|
||||||
class MarkdownBlockType extends TextareaBlockType
|
|
||||||
{
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options)
|
|
||||||
{
|
|
||||||
$builder->add(
|
|
||||||
'value',
|
|
||||||
MarkdownType::class,
|
|
||||||
array_merge([
|
|
||||||
'required' => false,
|
|
||||||
'label' => false,
|
|
||||||
], $options['options']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
|
||||||
|
|
||||||
class MarkdownType extends TextareaType
|
|
||||||
{
|
|
||||||
public function getBlockPrefix()
|
|
||||||
{
|
|
||||||
return 'markdown';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,10 +3,10 @@
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
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;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
|
||||||
class ProjectLinkType extends AbstractType
|
class ProjectLinkType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -65,12 +65,11 @@ class ProjectType extends AbstractType
|
||||||
FilePickerType::class,
|
FilePickerType::class,
|
||||||
[
|
[
|
||||||
'label' => 'Image',
|
'label' => 'Image',
|
||||||
'required' => true,
|
'required' => false,
|
||||||
'data_class' => null,
|
'data_class' => null,
|
||||||
'attr' => [
|
'attr' => [
|
||||||
],
|
],
|
||||||
'constraints' => [
|
'constraints' => [
|
||||||
new NotBlank(),
|
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Core\Form\FileManager\FilePickerType;
|
|
||||||
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\TextType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
use App\Core\Form\FileManager\FilePickerType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
|
||||||
class StlFileType extends AbstractType
|
class StlFileType extends AbstractType
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ use Symfony\Component\Form\FormView;
|
||||||
|
|
||||||
class SimpleMdTextareaType extends TextareaType
|
class SimpleMdTextareaType extends TextareaType
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||||
{
|
{
|
||||||
if (!isset($view->vars['attr']['data-simplemde'])) {
|
if (!isset($view->vars['attr']['data-simplemde'])) {
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,6 @@ namespace App\Form;
|
||||||
|
|
||||||
use App\Core\Form\UserType as BaseUserType;
|
use App\Core\Form\UserType as BaseUserType;
|
||||||
|
|
||||||
class UserType extends BaseUserType {}
|
class UserType extends BaseUserType
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
namespace App;
|
namespace App;
|
||||||
|
|
||||||
use App\Core\DependencyInjection\Compiler\BuilderBlockPass;
|
|
||||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
|
||||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
||||||
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
|
||||||
|
|
@ -37,9 +35,4 @@ class Kernel extends BaseKernel
|
||||||
(require $path)($routes->withPath($path), $this);
|
(require $path)($routes->withPath($path), $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function build(ContainerBuilder $container): void
|
|
||||||
{
|
|
||||||
$container->addCompilerPass(new BuilderBlockPass());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,15 @@ use Knp\Bundle\MarkdownBundle\Parser\MarkdownParser;
|
||||||
*/
|
*/
|
||||||
class Post extends MarkdownParser
|
class Post extends MarkdownParser
|
||||||
{
|
{
|
||||||
// protected $id_class_attr_catch_re = '\{((?'.'>[ ]*[#.a-z][-_:a-zA-Z0-9="\'\/\*-]+){1,})[ ]*\}';
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
//protected $id_class_attr_catch_re = '\{((?'.'>[ ]*[#.a-z][-_:a-zA-Z0-9="\'\/\*-]+){1,})[ ]*\}';
|
||||||
protected $id_class_attr_catch_re = '\{([^\}]+)\}';
|
protected $id_class_attr_catch_re = '\{([^\}]+)\}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function transformMarkdown($text)
|
public function transformMarkdown($text)
|
||||||
{
|
{
|
||||||
$html = parent::transformMarkdown($text);
|
$html = parent::transformMarkdown($text);
|
||||||
|
|
@ -21,6 +27,9 @@ class Post extends MarkdownParser
|
||||||
return str_replace(' role="doc-endnote"', '', $html);
|
return str_replace(' role="doc-endnote"', '', $html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
protected function doExtraAttributes($tag_name, $attr, $defaultIdValue = null, $classes = [])
|
protected function doExtraAttributes($tag_name, $attr, $defaultIdValue = null, $classes = [])
|
||||||
{
|
{
|
||||||
if (empty($attr) && !$defaultIdValue && empty($classes)) {
|
if (empty($attr) && !$defaultIdValue && empty($classes)) {
|
||||||
|
|
@ -93,11 +102,8 @@ class Post extends MarkdownParser
|
||||||
$codeblock
|
$codeblock
|
||||||
);
|
);
|
||||||
|
|
||||||
$codeblock = preg_replace_callback(
|
$codeblock = preg_replace_callback('/^\n+/',
|
||||||
'/^\n+/',
|
[$this, '_doFencedCodeBlocks_newlines'], $codeblock);
|
||||||
[$this, '_doFencedCodeBlocks_newlines'],
|
|
||||||
$codeblock
|
|
||||||
);
|
|
||||||
|
|
||||||
$classes = [];
|
$classes = [];
|
||||||
if ('' != $classname) {
|
if ('' != $classname) {
|
||||||
|
|
@ -109,12 +115,7 @@ class Post extends MarkdownParser
|
||||||
$attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? 'pre' : 'code', $attrs, null, $classes);
|
$attr_str = $this->doExtraAttributes($this->code_attr_on_pre ? 'pre' : 'code', $attrs, null, $classes);
|
||||||
$pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
|
$pre_attr_str = $this->code_attr_on_pre ? $attr_str : '';
|
||||||
$code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
|
$code_attr_str = $this->code_attr_on_pre ? '' : $attr_str;
|
||||||
|
$codeblock = "<pre{$pre_attr_str}><code{$code_attr_str}>{$codeblock}</code></pre>";
|
||||||
if (preg_match('/mermaid/', $code_attr_str)) {
|
|
||||||
$codeblock = "<pre{$code_attr_str}>{$codeblock}</pre>";
|
|
||||||
} else {
|
|
||||||
$codeblock = "<pre{$pre_attr_str}><code{$code_attr_str}>{$codeblock}</code></pre>";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "\n\n".$this->hashBlock($codeblock)."\n\n";
|
return "\n\n".$this->hashBlock($codeblock)."\n\n";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Message;
|
|
||||||
|
|
||||||
final class PageViewMessage
|
|
||||||
{
|
|
||||||
public function __construct(public int $time) {}
|
|
||||||
|
|
||||||
public function getTime(): int
|
|
||||||
{
|
|
||||||
return $this->time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\MessageHandler;
|
|
||||||
|
|
||||||
use App\Api\InfluxDB;
|
|
||||||
use App\Message\PageViewMessage;
|
|
||||||
use InfluxDB2\Point;
|
|
||||||
use InfluxDB2\WriteType;
|
|
||||||
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
|
|
||||||
|
|
||||||
final class PageViewMessageHandler implements MessageHandlerInterface
|
|
||||||
{
|
|
||||||
public function __construct(protected InfluxDB $influxDB) {}
|
|
||||||
|
|
||||||
public function __invoke(PageViewMessage $message)
|
|
||||||
{
|
|
||||||
if (!$this->influxDB->isAvailable()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$client = $this->influxDB->getClient();
|
|
||||||
|
|
||||||
$writeApi = $client->createWriteApi(['writeType' => WriteType::SYNCHRONOUS]);
|
|
||||||
$pageView = new Point('page_view');
|
|
||||||
$pageView
|
|
||||||
->addTag('request', 'view')
|
|
||||||
->addField('value', 1)
|
|
||||||
->time($message->getTime())
|
|
||||||
;
|
|
||||||
|
|
||||||
$writeApi->write($pageView);
|
|
||||||
$writeApi->close();
|
|
||||||
$client->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Middleware;
|
|
||||||
|
|
||||||
use Symfony\Component\Messenger\Envelope;
|
|
||||||
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
|
|
||||||
use Symfony\Component\Messenger\Middleware\StackInterface;
|
|
||||||
|
|
||||||
final class PageViewMiddleware implements MiddlewareInterface
|
|
||||||
{
|
|
||||||
public function handle(Envelope $envelope, StackInterface $stack): Envelope
|
|
||||||
{
|
|
||||||
// ...
|
|
||||||
return $stack->next()->handle($envelope, $stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repository;
|
|
||||||
|
|
||||||
use App\Entity\AppEntityBlogPost;
|
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends ServiceEntityRepository<AppEntityBlogPost>
|
|
||||||
*
|
|
||||||
* @method null|AppEntityBlogPost find($id, $lockMode = null, $lockVersion = null)
|
|
||||||
* @method null|AppEntityBlogPost findOneBy(array $criteria, array $orderBy = null)
|
|
||||||
* @method AppEntityBlogPost[] findAll()
|
|
||||||
* @method AppEntityBlogPost[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
|
||||||
*/
|
|
||||||
class AppEntityBlogPostRepository extends ServiceEntityRepository
|
|
||||||
{
|
|
||||||
public function __construct(ManagerRegistry $registry)
|
|
||||||
{
|
|
||||||
parent::__construct($registry, AppEntityBlogPost::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function add(AppEntityBlogPost $entity, bool $flush = false): void
|
|
||||||
{
|
|
||||||
$this->getEntityManager()->persist($entity);
|
|
||||||
|
|
||||||
if ($flush) {
|
|
||||||
$this->getEntityManager()->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function remove(AppEntityBlogPost $entity, bool $flush = false): void
|
|
||||||
{
|
|
||||||
$this->getEntityManager()->remove($entity);
|
|
||||||
|
|
||||||
if ($flush) {
|
|
||||||
$this->getEntityManager()->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
|
||||||
// * @return AppEntityBlogPost[] Returns an array of AppEntityBlogPost objects
|
|
||||||
// */
|
|
||||||
// public function findByExampleField($value): array
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('a')
|
|
||||||
// ->andWhere('a.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->orderBy('a.id', 'ASC')
|
|
||||||
// ->setMaxResults(10)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public function findOneBySomeField($value): ?AppEntityBlogPost
|
|
||||||
// {
|
|
||||||
// return $this->createQueryBuilder('a')
|
|
||||||
// ->andWhere('a.exampleField = :val')
|
|
||||||
// ->setParameter('val', $value)
|
|
||||||
// ->getQuery()
|
|
||||||
// ->getOneOrNullResult()
|
|
||||||
// ;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method null|Comment find($id, $lockMode = null, $lockVersion = null)
|
* @method Comment|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
* @method null|Comment findOneBy(array $criteria, array $orderBy = null)
|
* @method Comment|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
* @method Comment[] findAll()
|
* @method Comment[] findAll()
|
||||||
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
* @method Comment[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
namespace App\Repository\Blog;
|
namespace App\Repository\Blog;
|
||||||
|
|
||||||
use App\Core\Repository\RepositoryQuery;
|
use App\Core\Repository\RepositoryQuery;
|
||||||
use App\Repository\Blog\PostFollowRepository as Repository;
|
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
use App\Repository\Blog\PostFollowRepository as Repository;
|
||||||
|
|
||||||
class PostFollowRepositoryQuery extends RepositoryQuery
|
class PostFollowRepositoryQuery extends RepositoryQuery
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,15 @@ class PostRepositoryQuery extends RepositoryQuery
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function filterHandler(string $name, $value)
|
||||||
|
{
|
||||||
|
if ('category' === $name) {
|
||||||
|
$this->inCategory($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function search(?string $keywords, ?string $tag)
|
public function search(?string $keywords, ?string $tag)
|
||||||
{
|
{
|
||||||
$keywords = explode(' ', $keywords);
|
|
||||||
|
|
||||||
$filterWords = fn ($keyword) => '' !== trim($keyword) && preg_match('/[a-zA-Z]+/', $keyword);
|
|
||||||
$keywords = array_filter($keywords, $filterWords);
|
|
||||||
|
|
||||||
if ($keywords) {
|
if ($keywords) {
|
||||||
$conn = $this->repository->getEm()->getConnection();
|
$conn = $this->repository->getEm()->getConnection();
|
||||||
|
|
||||||
|
|
@ -68,106 +70,42 @@ class PostRepositoryQuery extends RepositoryQuery
|
||||||
'SELECT
|
'SELECT
|
||||||
post.id,
|
post.id,
|
||||||
post.title,
|
post.title,
|
||||||
post.content,
|
MATCH(post.title) AGAINST(:search) AS MATCH_TITLE,
|
||||||
post.published_at
|
MATCH(post.content) AGAINST(:search) AS MATCH_CONTENT
|
||||||
FROM post
|
FROM post
|
||||||
WHERE
|
WHERE
|
||||||
post.status = 1 AND
|
post.status = 1 AND
|
||||||
post.published_at < :date
|
post.published_at < :date
|
||||||
'
|
ORDER BY
|
||||||
);
|
MATCH_TITLE DESC,
|
||||||
|
MATCH_CONTENT DESC
|
||||||
|
');
|
||||||
|
|
||||||
$statement = $query->execute([
|
$statement = $query->execute([
|
||||||
|
':search' => $keywords,
|
||||||
':date' => (new \DateTime())->format('Y-m-d H:i:s'),
|
':date' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$results = $statement->fetchAll();
|
$results = $statement->fetchAll();
|
||||||
$ids = [];
|
$ids = [];
|
||||||
$matches = [];
|
|
||||||
|
|
||||||
foreach ($results as $k => $v) {
|
foreach ($results as $k => $v) {
|
||||||
$initWords = explode(' ', $v['title']);
|
$rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT'];
|
||||||
$words = [];
|
|
||||||
|
|
||||||
foreach ($initWords as $initWord) {
|
if ($rate >= 7) {
|
||||||
$words = array_merge($words, preg_split('/[:_\'-]+/', $initWord));
|
$ids[] = $v['id'];
|
||||||
}
|
|
||||||
|
|
||||||
$words = array_filter($words, $filterWords);
|
|
||||||
|
|
||||||
foreach ($keywords as $keyword) {
|
|
||||||
if (str_contains(mb_strtolower($v['content']), mb_strtolower($keyword))) {
|
|
||||||
$similarity = 99;
|
|
||||||
|
|
||||||
if (isset($matches[$v['id']])) {
|
|
||||||
$matches[$v['id']]['similarity'] += $similarity;
|
|
||||||
++$matches[$v['id']]['count'];
|
|
||||||
} else {
|
|
||||||
$matches[$v['id']] = [
|
|
||||||
'id' => $v['id'],
|
|
||||||
'title' => $v['title'],
|
|
||||||
'published_at' => $v['published_at'],
|
|
||||||
'similarity' => $similarity,
|
|
||||||
'count' => 1,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($words as $word) {
|
|
||||||
if (str_contains(mb_strtolower($word), mb_strtolower($keyword))) {
|
|
||||||
$similarity = 150;
|
|
||||||
|
|
||||||
if (isset($matches[$v['id']])) {
|
|
||||||
$matches[$v['id']]['similarity'] += $similarity;
|
|
||||||
++$matches[$v['id']]['count'];
|
|
||||||
} else {
|
|
||||||
$matches[$v['id']] = [
|
|
||||||
'id' => $v['id'],
|
|
||||||
'title' => $v['title'],
|
|
||||||
'published_at' => $v['published_at'],
|
|
||||||
'similarity' => $similarity,
|
|
||||||
'count' => 1,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$lev = levenshtein($word, $keyword);
|
|
||||||
$similarity = 100 - ($lev * 100 / mb_strlen($word));
|
|
||||||
|
|
||||||
if ($similarity > 70) {
|
|
||||||
if (isset($matches[$v['id']])) {
|
|
||||||
$matches[$v['id']]['similarity'] += $similarity;
|
|
||||||
} else {
|
|
||||||
$matches[$v['id']] = [
|
|
||||||
'id' => $v['id'],
|
|
||||||
'title' => $v['title'],
|
|
||||||
'published_at' => $v['published_at'],
|
|
||||||
'similarity' => $similarity,
|
|
||||||
'count' => 1,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches = array_filter($matches, function ($match) use ($keywords) {
|
if (0 == count($ids)) {
|
||||||
return (100 * $match['count'] / count($keywords)) > 80;
|
foreach ($results as $k => $v) {
|
||||||
});
|
$rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT'];
|
||||||
|
|
||||||
usort($matches, function ($a, $b) {
|
if ($rate >= 6) {
|
||||||
if ($a['similarity'] > $b['similarity']) {
|
$ids[] = $v['id'];
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ($b['similarity'] > $a['similarity']) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($a['published_at'] != $b['published_at']) * -1;
|
|
||||||
});
|
|
||||||
|
|
||||||
$ids = array_column($matches, 'id');
|
|
||||||
|
|
||||||
if (!$ids) {
|
if (!$ids) {
|
||||||
$ids = [-1];
|
$ids = [-1];
|
||||||
|
|
@ -189,11 +127,4 @@ class PostRepositoryQuery extends RepositoryQuery
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function filterHandler(string $name, $value)
|
|
||||||
{
|
|
||||||
if ('category' === $name) {
|
|
||||||
$this->inCategory($value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ use Doctrine\ORM\ORMException;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method null|Project find($id, $lockMode = null, $lockVersion = null)
|
* @method Project|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
* @method null|Project findOneBy(array $criteria, array $orderBy = null)
|
* @method Project|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
* @method Project[] findAll()
|
* @method Project[] findAll()
|
||||||
* @method Project[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
* @method Project[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
use App\Core\Repository\RepositoryQuery;
|
use App\Core\Repository\RepositoryQuery;
|
||||||
use App\Entity\Project;
|
|
||||||
use App\Repository\ProjectRepository as Repository;
|
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
use App\Repository\ProjectRepository as Repository;
|
||||||
|
use App\Entity\Project;
|
||||||
|
|
||||||
class ProjectRepositoryQuery extends RepositoryQuery
|
class ProjectRepositoryQuery extends RepositoryQuery
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @method null|StlMesh find($id, $lockMode = null, $lockVersion = null)
|
* @method StlMesh|null find($id, $lockMode = null, $lockVersion = null)
|
||||||
* @method null|StlMesh findOneBy(array $criteria, array $orderBy = null)
|
* @method StlMesh|null findOneBy(array $criteria, array $orderBy = null)
|
||||||
* @method StlMesh[] findAll()
|
* @method StlMesh[] findAll()
|
||||||
* @method StlMesh[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
* @method StlMesh[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
use App\Core\Repository\RepositoryQuery;
|
use App\Core\Repository\RepositoryQuery;
|
||||||
use App\Repository\StlMeshRepository as Repository;
|
|
||||||
use Knp\Component\Pager\PaginatorInterface;
|
use Knp\Component\Pager\PaginatorInterface;
|
||||||
|
use App\Repository\StlMeshRepository as Repository;
|
||||||
|
|
||||||
class StlMeshRepositoryQuery extends RepositoryQuery
|
class StlMeshRepositoryQuery extends RepositoryQuery
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ use App\Entity\User;
|
||||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
use Doctrine\Persistence\ManagerRegistry;
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
|
||||||
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
|
||||||
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
use Symfony\Component\Security\Core\User\PasswordUpgraderInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
|
||||||
|
|
||||||
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,8 @@ class BlogExtension extends AbstractExtension
|
||||||
$text = str_replace('http://upload.deblan.fr', 'https://upload.deblan.org', $text);
|
$text = str_replace('http://upload.deblan.fr', 'https://upload.deblan.org', $text);
|
||||||
$text = str_replace('http://dedi.geneweb.fr', 'http://kim.deblan.fr', $text);
|
$text = str_replace('http://dedi.geneweb.fr', 'http://kim.deblan.fr', $text);
|
||||||
$text = str_replace('http://mediaplayer.deblan.fr', 'https://mediaplayer.deblan.org', $text);
|
$text = str_replace('http://mediaplayer.deblan.fr', 'https://mediaplayer.deblan.org', $text);
|
||||||
$text = str_replace('http://blog.deblan.fr', 'https://www.deblan.fr', $text);
|
$text = str_replace('http://blog.deblan.fr', 'https://www.deblan.io', $text);
|
||||||
$text = str_replace('http://www.deblan.tv', 'https://www.deblan.fr', $text);
|
$text = str_replace('http://www.deblan.tv', 'https://www.deblan.io', $text);
|
||||||
$text = str_replace('http://www.deblan.io', 'https://www.deblan.fr', $text);
|
|
||||||
|
|
||||||
$text = preg_replace_callback(
|
$text = preg_replace_callback(
|
||||||
'`<p([^>]*)>(.*)</p([^>]*)>`isU',
|
'`<p([^>]*)>(.*)</p([^>]*)>`isU',
|
||||||
|
|
@ -135,12 +134,10 @@ class BlogExtension extends AbstractExtension
|
||||||
'#<code langage=\'([^\']*)\'>(.*)</code>#isU',
|
'#<code langage=\'([^\']*)\'>(.*)</code>#isU',
|
||||||
],
|
],
|
||||||
function ($data) {
|
function ($data) {
|
||||||
$lang = strtolower(
|
$lang = strtolower(str_replace(
|
||||||
str_replace(
|
['console', 'texte', 'apache'],
|
||||||
['console', 'texte', 'apache'],
|
['bash', 'text', 'html'],
|
||||||
['bash', 'text', 'html'],
|
$data[1])
|
||||||
$data[1]
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
$class = 'language-'.$lang;
|
$class = 'language-'.$lang;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ class ColorExtension extends AbstractExtension
|
||||||
|
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'#%s%s%s',
|
'#%s%s%s',
|
||||||
2 != strlen($red) ? '0'.$red : $red,
|
strlen($red) != 2 ? '0'.$red : $red,
|
||||||
2 != strlen($green) ? '0'.$green : $green,
|
strlen($green) != 2 ? '0'.$green : $green,
|
||||||
2 != strlen($blue) ? '0'.$blue : $blue,
|
strlen($blue) != 2 ? '0'.$blue : $blue,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue