Merge pull request #6 from Apes-HDF/chore/sync-les-tilleuls
Chore/sync les tilleuls
This commit is contained in:
commit
e64e6b4b0b
267 changed files with 8403 additions and 3591 deletions
5
.env
5
.env
|
|
@ -74,6 +74,7 @@ MAILER_DSN=null://null
|
||||||
# This is main the DNS that is used by the notifier component
|
# This is main the DNS that is used by the notifier component
|
||||||
# the other below are just example for two different third party vendors
|
# the other below are just example for two different third party vendors
|
||||||
#SMS_DSN=twilio://SID:TOKEN@default?from=FROM
|
#SMS_DSN=twilio://SID:TOKEN@default?from=FROM
|
||||||
|
#SMS_DSN=brevo://API_KEY@default?sender=SENDER
|
||||||
SMS_DSN=null://null
|
SMS_DSN=null://null
|
||||||
###< symfony/notifier ###
|
###< symfony/notifier ###
|
||||||
|
|
||||||
|
|
@ -106,3 +107,7 @@ STORAGE_USE_PATH_STYLE_ENDPOINT=true
|
||||||
STORAGE_KEY=app
|
STORAGE_KEY=app
|
||||||
STORAGE_SECRET=!ChangeMe!
|
STORAGE_SECRET=!ChangeMe!
|
||||||
###< league/flysystem-bundle ###
|
###< league/flysystem-bundle ###
|
||||||
|
|
||||||
|
###> symfony/brevo-notifier ###
|
||||||
|
# BREVO_DSN=brevo://API_KEY@default?sender=SENDER
|
||||||
|
###< symfony/brevo-notifier ###
|
||||||
|
|
|
||||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -51,6 +51,6 @@ jobs:
|
||||||
- name: Twig Linter
|
- name: Twig Linter
|
||||||
run: docker compose exec -T php ./vendor/bin/twigcs templates/ --exclude vendor
|
run: docker compose exec -T php ./vendor/bin/twigcs templates/ --exclude vendor
|
||||||
- name: Install eslint
|
- name: Install eslint
|
||||||
run: docker run --rm -w "/usr/app" -v "${PWD}":/usr/app gmolaire/yarn yarn add eslint
|
run: docker run --rm -w "/usr/app" -v "${PWD}":/usr/app gmolaire/yarn yarn add eslint@8.57.0
|
||||||
- name: Run eslint on javascript files
|
- name: Run eslint on javascript files
|
||||||
run: docker run --rm -w "/usr/app" -v "${PWD}":/usr/app gmolaire/yarn yarn lint
|
run: docker run --rm -w "/usr/app" -v "${PWD}":/usr/app gmolaire/yarn yarn lint
|
||||||
|
|
|
||||||
10
.github/workflows/deploy.yml
vendored
10
.github/workflows/deploy.yml
vendored
|
|
@ -3,12 +3,20 @@ name: Deploy
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
secrets:
|
secrets:
|
||||||
|
kubeconfig:
|
||||||
|
description: kubeconfig stored as a base64 encrypted secret
|
||||||
|
required: true
|
||||||
domain:
|
domain:
|
||||||
description: Main project deploy domain used in URLs
|
description: Main project deploy domain used in URLs
|
||||||
required: true
|
required: true
|
||||||
payum-apikey:
|
payum-apikey:
|
||||||
description: Payum API Key
|
description: Payum API Key
|
||||||
required: true
|
required: true
|
||||||
|
sms-dsn:
|
||||||
|
description: SMS DSN
|
||||||
|
required: true
|
||||||
|
mailer-dsn:
|
||||||
|
description: MAILER DSN
|
||||||
storage-key:
|
storage-key:
|
||||||
description: storage key
|
description: storage key
|
||||||
required: true
|
required: true
|
||||||
|
|
@ -117,6 +125,8 @@ jobs:
|
||||||
--set=ingress.tls[0].secretName=${{ needs.meta.outputs.release_name }}-tls \
|
--set=ingress.tls[0].secretName=${{ needs.meta.outputs.release_name }}-tls \
|
||||||
--set=ingress.tls[0].hosts[0]=${{ vars.DOMAIN }} \
|
--set=ingress.tls[0].hosts[0]=${{ vars.DOMAIN }} \
|
||||||
--set=postgresql.url="${{ secrets.database-url }}" \
|
--set=postgresql.url="${{ secrets.database-url }}" \
|
||||||
|
--set=postgresql.enabled='${{ github.event_name == 'pull_request' }}' \
|
||||||
|
--set=sms.dsn="${{ secrets.sms-dsn }}" \
|
||||||
--set=payum.apikey="${{ secrets.payum-apikey }}" \
|
--set=payum.apikey="${{ secrets.payum-apikey }}" \
|
||||||
--set=mailer.dsn="${{ secrets.mailer-dsn }}" \
|
--set=mailer.dsn="${{ secrets.mailer-dsn }}" \
|
||||||
--set=php.storage.bucket="${{ vars.STORAGE_BUCKET }}" \
|
--set=php.storage.bucket="${{ vars.STORAGE_BUCKET }}" \
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
|
# https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
|
||||||
ARG PHP_VERSION=8.1
|
ARG PHP_VERSION=8.1
|
||||||
ARG CADDY_VERSION=2
|
ARG CADDY_VERSION=2.8.4
|
||||||
|
|
||||||
# yarn build
|
# yarn build
|
||||||
FROM gmolaire/yarn AS yarn_build
|
FROM gmolaire/yarn AS yarn_build
|
||||||
|
|
@ -24,7 +24,7 @@ RUN yarn build
|
||||||
FROM php:${PHP_VERSION}-fpm-alpine AS app_php
|
FROM php:${PHP_VERSION}-fpm-alpine AS app_php
|
||||||
|
|
||||||
# needed for security update until base image is updated
|
# needed for security update until base image is updated
|
||||||
RUN apk upgrade libcurl curl openssl openssl-dev libressl libcrypto3 libssl3
|
#RUN apk upgrade libcurl curl openssl openssl-dev libressl libcrypto3 libssl3
|
||||||
|
|
||||||
# Allow to use development versions of Symfony
|
# Allow to use development versions of Symfony
|
||||||
ARG STABILITY="stable"
|
ARG STABILITY="stable"
|
||||||
|
|
@ -188,7 +188,7 @@ RUN rm -f .env.local.php
|
||||||
# Temporary fix for https://github.com/dunglas/mercure/issues/770
|
# Temporary fix for https://github.com/dunglas/mercure/issues/770
|
||||||
# https://github.com/dunglas/symfony-docker/pull/407/files
|
# https://github.com/dunglas/symfony-docker/pull/407/files
|
||||||
|
|
||||||
FROM caddy:2.7-builder-alpine AS app_caddy_builder
|
FROM caddy:2.8.4-builder-alpine AS app_caddy_builder
|
||||||
|
|
||||||
# RUN xcaddy build \
|
# RUN xcaddy build \
|
||||||
# --with github.com/dunglas/mercure \
|
# --with github.com/dunglas/mercure \
|
||||||
|
|
@ -204,7 +204,7 @@ RUN xcaddy build \
|
||||||
FROM caddy:${CADDY_VERSION} AS app_caddy
|
FROM caddy:${CADDY_VERSION} AS app_caddy
|
||||||
|
|
||||||
# needed for security update until base image is updated
|
# needed for security update until base image is updated
|
||||||
RUN apk upgrade libcurl curl openssl openssl-dev libressl libcrypto1.1 libssl1.1 libcrypto3 libssl3
|
#RUN apk upgrade libcurl curl openssl openssl-dev libressl libcrypto1.1 libssl1.1 libcrypto3 libssl3
|
||||||
|
|
||||||
WORKDIR /srv/app
|
WORKDIR /srv/app
|
||||||
|
|
||||||
|
|
|
||||||
8
assets/admin.js
Normal file
8
assets/admin.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { startStimulusApp } from '@symfony/stimulus-bridge'
|
||||||
|
import AdminParentgroup from './controllers/admin_parentgroup_controller'
|
||||||
|
|
||||||
|
import './styles/admin.css'
|
||||||
|
|
||||||
|
const app = startStimulusApp()
|
||||||
|
|
||||||
|
app.register('admin-parentgroup', AdminParentgroup)
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
"fetch": "eager",
|
"fetch": "eager",
|
||||||
"autoimport": {
|
"autoimport": {
|
||||||
"tom-select/dist/css/tom-select.default.css": true,
|
"tom-select/dist/css/tom-select.default.css": true,
|
||||||
|
"tom-select/dist/css/tom-select.bootstrap4.css": false,
|
||||||
"tom-select/dist/css/tom-select.bootstrap5.css": false
|
"tom-select/dist/css/tom-select.bootstrap5.css": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
241
assets/controllers/admin_parentgroup_controller.js
Normal file
241
assets/controllers/admin_parentgroup_controller.js
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
import { Controller} from '@hotwired/stimulus'
|
||||||
|
|
||||||
|
/* stimulusFetch: 'lazy' */
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ['servicesEnabledField', 'parentField', 'idField', 'ownerField']
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
const parentFields = document.querySelectorAll('[data-label="Parent"]')
|
||||||
|
const trs = Array.from(parentFields)
|
||||||
|
.map(e => e.firstElementChild)
|
||||||
|
.filter(e => e.tagName === 'A')
|
||||||
|
.map(e => e.closest('tr'))
|
||||||
|
for (const tr of trs) {
|
||||||
|
this.checkDisabledServices(tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parentFieldTargetConnected(element) {
|
||||||
|
// edit page
|
||||||
|
const parentGroupId = element.value
|
||||||
|
const servicesEnabledToggle = document.getElementById('Group_servicesEnabled')
|
||||||
|
this.checkGroupEditDisableServices(parentGroupId, servicesEnabledToggle)
|
||||||
|
|
||||||
|
element.addEventListener('change', () => {
|
||||||
|
const newParentGroupId = element.value
|
||||||
|
this.checkGroupEditDisableServices(newParentGroupId, servicesEnabledToggle)
|
||||||
|
})
|
||||||
|
|
||||||
|
// list page
|
||||||
|
const observer = new MutationObserver( ( ) => {
|
||||||
|
if (element.tomselect) {
|
||||||
|
observer.disconnect()
|
||||||
|
|
||||||
|
const toggle = document.getElementById('Group_servicesEnabled')
|
||||||
|
toggle.addEventListener('change', () => {
|
||||||
|
this.updateParentOptions(toggle.checked, this.parentFieldTarget)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
observer.observe(element, {attributes: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateParentOptions(servicesEnabled, parentField) {
|
||||||
|
const url = `/api/groups?services_enabled=${servicesEnabled}`
|
||||||
|
|
||||||
|
const response = await fetch(url, { method: 'GET' })
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await response.json()
|
||||||
|
const groups = data['hydra:member']
|
||||||
|
|
||||||
|
// Remove options
|
||||||
|
parentField.tomselect.clearOptions()
|
||||||
|
|
||||||
|
// Populate with new options
|
||||||
|
groups.map(group => {
|
||||||
|
parentField.tomselect.addOption(new Option(group.name, group.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
servicesEnabledFieldTargetConnected() {
|
||||||
|
this.servicesEnabledFieldTarget.addEventListener('change', () => {
|
||||||
|
if(!this.servicesEnabledFieldTarget.checked) {
|
||||||
|
const params = new URLSearchParams(this.servicesEnabledFieldTarget.getAttribute('data-toggle-url'))
|
||||||
|
this.disableServicesForChildGroups(params.get('entityId'))
|
||||||
|
}
|
||||||
|
const parentFields = document.querySelectorAll('[data-label="Parent"]')
|
||||||
|
const trs = Array.from(parentFields)
|
||||||
|
.map(e => e.firstElementChild)
|
||||||
|
.filter(e => e.tagName === 'A')
|
||||||
|
.map(e => e.closest('tr'))
|
||||||
|
for (const tr of trs) {
|
||||||
|
this.checkDisabledServices(tr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerFieldTargetConnected() {
|
||||||
|
const initialUserId = this.ownerFieldTarget.value
|
||||||
|
const groupsField = document.getElementById('Product_groups')
|
||||||
|
this.replaceGroups(initialUserId, groupsField)
|
||||||
|
this.ownerFieldTarget.addEventListener('change', () => {
|
||||||
|
const userId = this.ownerFieldTarget.value
|
||||||
|
|
||||||
|
this.replaceGroups(userId, groupsField)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkDisabledServices(tr) {
|
||||||
|
const id = tr.getAttribute('data-id')
|
||||||
|
const url = `/api/groups/${id}`
|
||||||
|
|
||||||
|
const response = await fetch(url, {method: 'GET'})
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const group = await response.json()
|
||||||
|
|
||||||
|
let disabledTr = false
|
||||||
|
for (const parentUrl of group.parentsRecursively) {
|
||||||
|
const parentResponse = await fetch(parentUrl, { method: 'GET' })
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parent = await parentResponse.json()
|
||||||
|
if (!parent.servicesEnabled) {
|
||||||
|
tr.querySelector('[data-admin-parentgroup-target="servicesEnabledField"]').disabled = true
|
||||||
|
disabledTr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!disabledTr) {
|
||||||
|
tr.querySelector('[data-admin-parentgroup-target="servicesEnabledField"]').disabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disableServicesForChildGroups(groupId) {
|
||||||
|
const url = `/api/groups/${groupId}/disable_child_services`
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/merge-patch+json',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await response.json()
|
||||||
|
const groupChild = data.childrenRecursively
|
||||||
|
|
||||||
|
const groupChildId = groupChild.map(group => {
|
||||||
|
return group.split('/')[3]
|
||||||
|
})
|
||||||
|
|
||||||
|
const allToggles = document.querySelectorAll('[data-admin-parentgroup-target="servicesEnabledField"]')
|
||||||
|
Array.from(allToggles).map(toggle => {
|
||||||
|
const params = new URLSearchParams(toggle.getAttribute('data-toggle-url'))
|
||||||
|
if(groupChildId.includes(params.get('entityId'))) {
|
||||||
|
toggle.checked = false
|
||||||
|
toggle.disabled = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async replaceGroups(userId, groupsField) {
|
||||||
|
const url = `/api/groups?user=${userId}&services_enabled=true&admin=0`
|
||||||
|
const response = await fetch(url, {method: 'GET'})
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await response.json()
|
||||||
|
const groups = data['hydra:member']
|
||||||
|
|
||||||
|
const parentDiv = groupsField.parentElement
|
||||||
|
const smallElement = parentDiv.querySelector('small')
|
||||||
|
|
||||||
|
const saveContinue = document.getElementsByClassName('action-saveAndContinue')[0]
|
||||||
|
const saveReturn = document.getElementsByClassName('action-saveAndReturn')[0]
|
||||||
|
const saveAdd = document.getElementsByClassName('action-saveAndAddAnother')[0]
|
||||||
|
|
||||||
|
|
||||||
|
groupsField.tomselect.clear()
|
||||||
|
groupsField.tomselect.clearOptions()
|
||||||
|
if (groups.length === 0) {
|
||||||
|
groupsField.tomselect.disable()
|
||||||
|
groupsField.tomselect.lock()
|
||||||
|
// show helper
|
||||||
|
if (null !== smallElement) {
|
||||||
|
smallElement.style.visibility = 'visible'
|
||||||
|
}
|
||||||
|
if (undefined !== saveContinue) {
|
||||||
|
saveContinue.disabled = true
|
||||||
|
}
|
||||||
|
if (undefined !== saveAdd) {
|
||||||
|
saveAdd.disabled = true
|
||||||
|
}
|
||||||
|
saveReturn.disabled = true
|
||||||
|
} else {
|
||||||
|
groups.map(group => {
|
||||||
|
groupsField.tomselect.addOption(new Option(group.name, group.id))
|
||||||
|
})
|
||||||
|
groupsField.tomselect.enable()
|
||||||
|
groupsField.tomselect.unlock()
|
||||||
|
|
||||||
|
// remove helper
|
||||||
|
if (null !== smallElement) {
|
||||||
|
smallElement.style.visibility = 'hidden'
|
||||||
|
}
|
||||||
|
if (undefined !== saveContinue) {
|
||||||
|
saveContinue.disabled = false
|
||||||
|
}
|
||||||
|
if (undefined !== saveAdd) {
|
||||||
|
saveAdd.disabled = false
|
||||||
|
}
|
||||||
|
saveReturn.disabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkGroupEditDisableServices(parentGroupId, servicesEnabledToggle) {
|
||||||
|
const url = `/api/groups/${parentGroupId}`
|
||||||
|
|
||||||
|
const response = await fetch(url, {method: 'GET'})
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parentGroup = await response.json()
|
||||||
|
let parentAll = parentGroup.parentsRecursively
|
||||||
|
parentAll.push(parentGroup['@id'])
|
||||||
|
|
||||||
|
const parentDiv = servicesEnabledToggle.parentElement.parentElement
|
||||||
|
const smallElement = parentDiv.querySelector('small')
|
||||||
|
|
||||||
|
let disabled = false
|
||||||
|
for (const parentUrl of parentAll) {
|
||||||
|
const parentResponse = await fetch(parentUrl, { method: 'GET' })
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parent = await parentResponse.json()
|
||||||
|
if (!parent.servicesEnabled) {
|
||||||
|
servicesEnabledToggle.checked = false
|
||||||
|
servicesEnabledToggle.disabled = true
|
||||||
|
if (null !== smallElement) {
|
||||||
|
smallElement.style.visibility = 'visible'
|
||||||
|
}
|
||||||
|
disabled = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!disabled) {
|
||||||
|
servicesEnabledToggle.disabled = false
|
||||||
|
if (null !== smallElement) {
|
||||||
|
smallElement.style.visibility = 'hidden'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
assets/controllers/parentgroup_controller.js
Normal file
36
assets/controllers/parentgroup_controller.js
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { Controller} from '@hotwired/stimulus'
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ['servicesEnabledField', 'parentField']
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParentOptions() {
|
||||||
|
const userId = this.servicesEnabledFieldTarget.getAttribute('data-user-id')
|
||||||
|
const servicesEnabled = this.servicesEnabledFieldTarget.checked
|
||||||
|
|
||||||
|
const url = `/api/groups?user=${userId}&services_enabled=${servicesEnabled}`
|
||||||
|
this.addGroupsWithEnabledServices(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
async addGroupsWithEnabledServices(url) {
|
||||||
|
const response = await fetch(url, { method: 'GET' })
|
||||||
|
if (!response.ok) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const data = await response.json()
|
||||||
|
const groups = data['hydra:member']
|
||||||
|
|
||||||
|
// Remove options and set a default value
|
||||||
|
Array.from(this.parentFieldTarget.options).map((group) => {
|
||||||
|
this.parentFieldTarget.remove(group)
|
||||||
|
})
|
||||||
|
this.parentFieldTarget.add(new Option())
|
||||||
|
|
||||||
|
// Populate with new options
|
||||||
|
groups.map(group => {
|
||||||
|
this.parentFieldTarget.add(new Option(group.name, group.id))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { startStimulusApp } from '@symfony/stimulus-bridge'
|
import { startStimulusApp } from '@symfony/stimulus-bridge'
|
||||||
import { Application } from '@hotwired/stimulus'
|
|
||||||
|
|
||||||
import PasswordVisibility from 'stimulus-password-visibility'
|
import PasswordVisibility from 'stimulus-password-visibility'
|
||||||
import Carousel from 'stimulus-carousel'
|
import Carousel from 'stimulus-carousel'
|
||||||
|
|
@ -15,7 +14,5 @@ export const app = startStimulusApp(require.context(
|
||||||
// register any custom, 3rd party controllers here
|
// register any custom, 3rd party controllers here
|
||||||
// app.register('some_controller_name', SomeImportedController);
|
// app.register('some_controller_name', SomeImportedController);
|
||||||
|
|
||||||
const application = Application.start()
|
app.register('carousel', Carousel)
|
||||||
application.register('carousel', Carousel)
|
app.register('password-visibility', PasswordVisibility)
|
||||||
application.register('password-visibility', PasswordVisibility)
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.less {
|
&.less {
|
||||||
max-height: 500px;
|
max-height: 1000px;
|
||||||
transition: max-height .3s ease-in-out;
|
transition: max-height .3s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,30 +109,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p > strong {
|
.page-content {
|
||||||
> a {
|
a {
|
||||||
color: $blue-500;;
|
color: $blue-500;;
|
||||||
position: relative;
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: .5s;
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
background-color: $blue-500;
|
|
||||||
content: "";
|
|
||||||
height: 2px;
|
|
||||||
left: 0;
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
transform: scaleX(0);
|
|
||||||
transform-origin: right;
|
|
||||||
transition: transform .5s;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover:after {
|
|
||||||
transform: scaleX(1);
|
|
||||||
transform-origin: left;
|
|
||||||
}
|
}
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
4
assets/styles/admin.css
Normal file
4
assets/styles/admin.css
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/* Remove the extra arrow added by Tom Select */
|
||||||
|
.ts-wrapper.single .ts-control:after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
3
assets/styles/app.css
Normal file
3
assets/styles/app.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
body {
|
||||||
|
background-color: skyblue;
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
"type": "project",
|
"type": "project",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"description": "APES : Platforme d'échange de services et de biens",
|
"description": "APES : Platforme d'échange de services et de biens",
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "beta",
|
||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"repositories": {
|
"repositories": {
|
||||||
"coopTilleuls/payum-mollie": {
|
"coopTilleuls/payum-mollie": {
|
||||||
|
|
@ -44,50 +44,53 @@
|
||||||
"odolbeau/phone-number-bundle": "^3.9",
|
"odolbeau/phone-number-bundle": "^3.9",
|
||||||
"payum/offline": "^1.7",
|
"payum/offline": "^1.7",
|
||||||
"payum/payum-bundle": "^2.5",
|
"payum/payum-bundle": "^2.5",
|
||||||
|
"php-http/message": "^1.16",
|
||||||
|
"php-http/message-factory": "^1.1",
|
||||||
"phpdocumentor/reflection-docblock": "^5.3",
|
"phpdocumentor/reflection-docblock": "^5.3",
|
||||||
"phpstan/phpdoc-parser": "^1.11",
|
"phpstan/phpdoc-parser": "^1.11",
|
||||||
"sensio/framework-extra-bundle": "^6.2",
|
|
||||||
"snc/redis-bundle": "^4.3",
|
"snc/redis-bundle": "^4.3",
|
||||||
"stof/doctrine-extensions-bundle": "^1.7",
|
"stof/doctrine-extensions-bundle": "^1.7",
|
||||||
"symfony/asset": "6.2.*",
|
"symfony/asset": "6.4.*",
|
||||||
"symfony/cache": "6.2.*",
|
"symfony/brevo-notifier": "6.4.*",
|
||||||
"symfony/clock": "6.2.*",
|
"symfony/cache": "6.4.*",
|
||||||
"symfony/console": "6.2.*",
|
"symfony/clock": "6.4.*",
|
||||||
"symfony/doctrine-messenger": "6.2.*",
|
"symfony/console": "6.4.*",
|
||||||
"symfony/dotenv": "6.2.*",
|
"symfony/doctrine-messenger": "6.4.*",
|
||||||
"symfony/expression-language": "6.2.*",
|
"symfony/dotenv": "6.4.*",
|
||||||
"symfony/fake-sms-notifier": "6.2.*",
|
"symfony/expression-language": "6.4.*",
|
||||||
|
"symfony/fake-sms-notifier": "6.4.*",
|
||||||
"symfony/flex": "^2",
|
"symfony/flex": "^2",
|
||||||
"symfony/form": "6.2.*",
|
"symfony/form": "6.4.*",
|
||||||
"symfony/framework-bundle": "6.2.*",
|
"symfony/framework-bundle": "6.4.*",
|
||||||
"symfony/google-mailer": "6.2.*",
|
"symfony/google-mailer": "6.4.*",
|
||||||
"symfony/html-sanitizer": "6.2.*",
|
"symfony/html-sanitizer": "6.4.*",
|
||||||
"symfony/http-client": "6.2.*",
|
"symfony/http-client": "6.4.*",
|
||||||
"symfony/mailer": "6.2.*",
|
"symfony/mailer": "6.4.*",
|
||||||
"symfony/mercure-bundle": "^0.3.5",
|
"symfony/mercure-bundle": "^0.3.5",
|
||||||
"symfony/messenger": "6.2.*",
|
"symfony/messenger": "6.4.*",
|
||||||
"symfony/mime": "6.2.*",
|
"symfony/mime": "6.4.*",
|
||||||
"symfony/monolog-bundle": "^3.8",
|
"symfony/monolog-bundle": "^3.8",
|
||||||
"symfony/notifier": "6.2.*",
|
"symfony/notifier": "6.4.*",
|
||||||
"symfony/ovh-cloud-notifier": "6.2.*",
|
"symfony/ovh-cloud-notifier": "6.4.*",
|
||||||
"symfony/property-access": "6.2.*",
|
"symfony/property-access": "6.4.*",
|
||||||
"symfony/property-info": "6.2.*",
|
"symfony/property-info": "6.4.*",
|
||||||
"symfony/proxy-manager-bridge": "6.2.*",
|
"symfony/proxy-manager-bridge": "6.4.*",
|
||||||
"symfony/rate-limiter": "6.2.*",
|
"symfony/rate-limiter": "6.4.*",
|
||||||
"symfony/requirements-checker": "^2.0",
|
"symfony/requirements-checker": "^2.0",
|
||||||
"symfony/runtime": "6.2.*",
|
"symfony/runtime": "6.4.*",
|
||||||
"symfony/security-bundle": "6.2.*",
|
"symfony/security-bundle": "6.4.*",
|
||||||
"symfony/serializer": "6.2.*",
|
"symfony/serializer": "6.4.*",
|
||||||
|
"symfony/stimulus-bundle": "^2.14",
|
||||||
"symfony/translation-contracts": "^3.2",
|
"symfony/translation-contracts": "^3.2",
|
||||||
"symfony/twig-bridge": "6.2.*",
|
"symfony/twig-bridge": "6.4.*",
|
||||||
"symfony/twig-bundle": "6.2.*",
|
"symfony/twig-bundle": "6.4.*",
|
||||||
"symfony/twilio-notifier": "6.2.*",
|
"symfony/twilio-notifier": "6.4.*",
|
||||||
"symfony/uid": "6.2.*",
|
"symfony/uid": "6.4.*",
|
||||||
"symfony/ux-autocomplete": "^2.7",
|
"symfony/ux-autocomplete": "^2.7",
|
||||||
"symfony/validator": "6.2.*",
|
"symfony/validator": "6.4.*",
|
||||||
"symfony/webpack-encore-bundle": "^1.16",
|
"symfony/webpack-encore-bundle": "^1.16",
|
||||||
"symfony/workflow": "6.2.*",
|
"symfony/workflow": "6.4.*",
|
||||||
"symfony/yaml": "6.2.*",
|
"symfony/yaml": "6.4.*",
|
||||||
"twig/cssinliner-extra": "^3.4",
|
"twig/cssinliner-extra": "^3.4",
|
||||||
"twig/extra-bundle": "^3.4",
|
"twig/extra-bundle": "^3.4",
|
||||||
"twig/inky-extra": "^3.4",
|
"twig/inky-extra": "^3.4",
|
||||||
|
|
@ -151,7 +154,7 @@
|
||||||
"extra": {
|
"extra": {
|
||||||
"symfony": {
|
"symfony": {
|
||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "6.2.*",
|
"require": "6.4.*",
|
||||||
"docker": true
|
"docker": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -168,13 +171,13 @@
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.5",
|
||||||
"rector/rector": "^0.14.5",
|
"rector/rector": "^0.14.5",
|
||||||
"staabm/annotate-pull-request-from-checkstyle": "^1.8",
|
"staabm/annotate-pull-request-from-checkstyle": "^1.8",
|
||||||
"symfony/browser-kit": "6.2.*",
|
"symfony/browser-kit": "6.4.*",
|
||||||
"symfony/css-selector": "6.2.*",
|
"symfony/css-selector": "6.4.*",
|
||||||
"symfony/debug-bundle": "6.2.*",
|
"symfony/debug-bundle": "6.4.*",
|
||||||
"symfony/maker-bundle": "^1.47",
|
"symfony/maker-bundle": "^1.47",
|
||||||
"symfony/panther": "^2.0",
|
"symfony/panther": "^2.0",
|
||||||
"symfony/phpunit-bridge": "^6.1",
|
"symfony/phpunit-bridge": "^6.1",
|
||||||
"symfony/web-profiler-bundle": "6.2.*",
|
"symfony/web-profiler-bundle": "6.4.*",
|
||||||
"zenstruck/messenger-test": "^1.5"
|
"zenstruck/messenger-test": "^1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6771
composer.lock
generated
6771
composer.lock
generated
File diff suppressed because it is too large
Load diff
22
composer.md
22
composer.md
|
|
@ -1,23 +1 @@
|
||||||
# Note regarding composer.json
|
# Note regarding composer.json
|
||||||
|
|
||||||
## Meilisearch
|
|
||||||
|
|
||||||
meilisearch/meilisearch-php
|
|
||||||
|
|
||||||
Is locked to `v0.26.0` to prevent this error:
|
|
||||||
|
|
||||||
Executing script cache:clear [KO]
|
|
||||||
[KO]
|
|
||||||
Script cache:clear returned with error code 1
|
|
||||||
!!
|
|
||||||
!! In DebugClassLoader.php line 327:
|
|
||||||
!!
|
|
||||||
!! Case mismatch between loaded and declared class names: "MeiliSearch\Client"
|
|
||||||
!! vs "Meilisearch\Client".
|
|
||||||
!!
|
|
||||||
!!
|
|
||||||
!!
|
|
||||||
Script @auto-scripts was called via post-update-cmd
|
|
||||||
|
|
||||||
|
|
||||||
I have opened a ticket https://github.com/meilisearch/meilisearch-php/issues/452.
|
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,9 @@ return [
|
||||||
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
|
||||||
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
|
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
|
||||||
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
||||||
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
|
||||||
Symfony\UX\Autocomplete\AutocompleteBundle::class => ['all' => true],
|
Symfony\UX\Autocomplete\AutocompleteBundle::class => ['all' => true],
|
||||||
Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true],
|
Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true],
|
||||||
FOS\CKEditorBundle\FOSCKEditorBundle::class => ['all' => true],
|
FOS\CKEditorBundle\FOSCKEditorBundle::class => ['all' => true],
|
||||||
Misd\PhoneNumberBundle\MisdPhoneNumberBundle::class => ['all' => true],
|
Misd\PhoneNumberBundle\MisdPhoneNumberBundle::class => ['all' => true],
|
||||||
|
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,7 @@ twig:
|
||||||
|
|
||||||
fos_ck_editor:
|
fos_ck_editor:
|
||||||
configs:
|
configs:
|
||||||
|
default:
|
||||||
|
versionCheck: false
|
||||||
main_config:
|
main_config:
|
||||||
toolbar: full
|
toolbar: full
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ security:
|
||||||
access_control:
|
access_control:
|
||||||
- { path: ^/admin, roles: [ROLE_ADMIN, ROLE_GROUP_ADMIN] }
|
- { path: ^/admin, roles: [ROLE_ADMIN, ROLE_GROUP_ADMIN] }
|
||||||
# to synchronize with MyAccountAction
|
# to synchronize with MyAccountAction
|
||||||
- { path: ^/en/my-account/, roles: ROLE_USER }
|
- { path: ^/en/my-account, roles: MEMBERSHIP_PAID }
|
||||||
- { path: ^/fr/mon-compte/, roles: ROLE_USER }
|
- { path: ^/fr/mon-compte, roles: MEMBERSHIP_PAID }
|
||||||
|
|
||||||
role_hierarchy:
|
role_hierarchy:
|
||||||
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH, ROLE_GROUP_ADMIN]
|
ROLE_ADMIN: [ROLE_USER, ROLE_ALLOWED_TO_SWITCH, ROLE_GROUP_ADMIN]
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
# https://stackoverflow.com/q/69809320/633864
|
|
||||||
sensio_framework_extra:
|
|
||||||
router:
|
|
||||||
annotations: false
|
|
||||||
request:
|
|
||||||
converters: false
|
|
||||||
auto_convert: false
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
version: "3.4"
|
|
||||||
|
|
||||||
# Development environment override
|
# Development environment override
|
||||||
services:
|
services:
|
||||||
# https://localhost/_profiler/phpinfo
|
# https://localhost/_profiler/phpinfo
|
||||||
|
|
@ -30,8 +28,13 @@ services:
|
||||||
maildev:
|
maildev:
|
||||||
image: maildev/maildev
|
image: maildev/maildev
|
||||||
ports:
|
ports:
|
||||||
- 1080:1080
|
- "1080:1080"
|
||||||
- 1025:1025
|
- "1025:1025"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "wget -O - http://0.0.0.0:1080/healthz || exit 1"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
storage:
|
storage:
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,8 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- "1081:1080"
|
- "1081:1080"
|
||||||
- "1026:1025"
|
- "1026:1025"
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "-qO-", "http://localhost:1080"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 5s
|
|
||||||
retries: 5
|
|
||||||
|
|
||||||
adminer:
|
adminer:
|
||||||
image: adminer
|
image: adminer
|
||||||
ports:
|
ports:
|
||||||
- "8989:8080"
|
- "8989:8080"
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "curl --silent --fail http://127.0.0.1:8080/ || exit 1"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 30s
|
|
||||||
retries: 10
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
version: "3.4"
|
|
||||||
|
|
||||||
# Production environment override
|
# Production environment override
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
version: "3.4"
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
php:
|
php:
|
||||||
build:
|
build:
|
||||||
|
|
@ -89,7 +87,7 @@ services:
|
||||||
redis:
|
redis:
|
||||||
image: redis:7-alpine
|
image: redis:7-alpine
|
||||||
ports:
|
ports:
|
||||||
- '6389:6379'
|
- "6389:6379"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "redis-cli -h 127.0.0.1 ping | grep 'PONG' || exit 1"]
|
test: ["CMD-SHELL", "redis-cli -h 127.0.0.1 ping | grep 'PONG' || exit 1"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
|
|
|
||||||
|
|
@ -21,16 +21,21 @@ Access `https://localhost` in your browser and accept the security risk.
|
||||||
|
|
||||||
You should have access now to:
|
You should have access now to:
|
||||||
|
|
||||||
* Main project : https://localhost
|
* The main frontend: https://localhost
|
||||||
* Meilisearch : http://localhost:7700/
|
* The Meilisearch UI: http://localhost:7700/
|
||||||
|
|
||||||
To access the dev tools, run=
|
Note that you can also use the [online meilisearch-ui](https://meilisearch-ui.riccox.com).
|
||||||
|
Be careful it is not an official Meilisearch website, use only for dev data, do not
|
||||||
|
send cloud credentials.
|
||||||
|
|
||||||
|
To access the dev tools, run:
|
||||||
|
|
||||||
make start-dev
|
make start-dev
|
||||||
|
|
||||||
You should have access now to:
|
You should have access now to:
|
||||||
|
|
||||||
* Adminer : http://localhost:8989/?pgsql=database&username=app&db=app&ns=public&select=group
|
* Adminer : http://localhost:8989/?pgsql=database&username=app&db=app&ns=public&select=group
|
||||||
|
* Mailcatcher : http://localhost:1081/
|
||||||
* Maildev : http://localhost:1080
|
* Maildev : http://localhost:1080
|
||||||
|
|
||||||
## Makefile
|
## Makefile
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ This page documents the settings that can be configured as environment varibles
|
||||||
for the production environment.
|
for the production environment.
|
||||||
Defaults value are shown in the [.env](../.env) file.
|
Defaults value are shown in the [.env](../.env) file.
|
||||||
|
|
||||||
|
|
||||||
## Application
|
## Application
|
||||||
|
|
||||||
| name | default value |
|
| name | default value |
|
||||||
|
|
@ -47,7 +46,7 @@ For example, to use a standart SMTP server use:
|
||||||
|
|
||||||
Where mailer is the DSN of your SMTP server and 1025 the port to use.
|
Where mailer is the DSN of your SMTP server and 1025 the port to use.
|
||||||
|
|
||||||
Yo use Gmail with a secret key use:
|
To use Gmail with a secret key use:
|
||||||
|
|
||||||
MAILER_DSN=gmail://email@example.com:secretkey@default
|
MAILER_DSN=gmail://email@example.com:secretkey@default
|
||||||
|
|
||||||
|
|
@ -58,14 +57,19 @@ Twilio, Sendgrid, Mailingblue...
|
||||||
|
|
||||||
SMS_DSN=null://null
|
SMS_DSN=null://null
|
||||||
|
|
||||||
This is the main parameter to send DNS. If you leave `null://null`, nothing will
|
This is the main parameter to send DNS. If you leave `null://null`, no SMS will
|
||||||
be send without errors. It can be useful when having issues with your SMS provider
|
be sent.
|
||||||
and wanting to disable it temporarly.
|
It may be useful when having issues with your SMS provider and wanting to disable it temporarily.
|
||||||
|
|
||||||
For example, to use a service like Twilio, the parameters should look like:
|
Below are the supported value templates to use depending on your SMS provider.
|
||||||
|
|
||||||
SMS_DSN=twilio://AccountSID:AuthToken@default?from=%2BFROMNUMBER
|
### Brevo
|
||||||
|
|
||||||
|
SMS_DSN=brevo://API_KEY@default?sender=PHONE_NUMBER
|
||||||
|
|
||||||
|
### Twilio
|
||||||
|
|
||||||
|
SMS_DSN=twilio://AccountSID:AuthToken@default?from=PHONE_NUMBER
|
||||||
|
|
||||||
## Meilisearch
|
## Meilisearch
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@ App\Entity\Configuration:
|
||||||
|
|
||||||
features (extends configuration_template):
|
features (extends configuration_template):
|
||||||
configuration:
|
configuration:
|
||||||
services:
|
global:
|
||||||
servicesEnabled: true
|
globalServicesEnabled: true
|
||||||
|
globalPaidMembership: false
|
||||||
notificationsSender:
|
notificationsSender:
|
||||||
notificationsSenderEmail: info@example.com
|
notificationsSenderEmail: info@example.com
|
||||||
notificationsSenderName: Contact
|
notificationsSenderName: Contact
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ App\Entity\Configuration:
|
||||||
|
|
||||||
features (extends configuration_template):
|
features (extends configuration_template):
|
||||||
configuration:
|
configuration:
|
||||||
services:
|
global:
|
||||||
servicesEnabled: true
|
globalName: Echanges de biens et de services
|
||||||
|
globalServicesEnabled: true
|
||||||
|
globalPaidMembership: false
|
||||||
notificationsSender:
|
notificationsSender:
|
||||||
notificationsSenderEmail: info@example.com
|
notificationsSenderEmail: info@example.com
|
||||||
notificationsSenderName: Contact
|
notificationsSenderName: Contact
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ App\Entity\GroupOffer:
|
||||||
# Templates
|
# Templates
|
||||||
group_offer_group_1_template (template, extends group_offer_template):
|
group_offer_group_1_template (template, extends group_offer_template):
|
||||||
group: '@group_1'
|
group: '@group_1'
|
||||||
type: !php/enum App\Enum\Group\GroupOfferType::YEARLY
|
type: !php/enum App\Enum\OfferType::YEARLY
|
||||||
|
|
||||||
group_offer_group_7_template (template, extends group_offer_template):
|
group_offer_group_7_template (template, extends group_offer_template):
|
||||||
group: '@group_7'
|
group: '@group_7'
|
||||||
type: !php/enum App\Enum\Group\GroupOfferType::YEARLY
|
type: !php/enum App\Enum\OfferType::YEARLY
|
||||||
|
|
||||||
# Group 1
|
# Group 1
|
||||||
group_offer_group_1_10 (extends group_offer_group_1_template):
|
group_offer_group_1_10 (extends group_offer_group_1_template):
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ App\Entity\Page:
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2>Dépôt</h2>
|
<h2>Dépôt</h2>
|
||||||
<p>Toutes les informations sur le projet et le code sont disponibles sur <strong><u><a href="https://github.com/Apes-HDF/EBS">le dépôt public</a></u></strong>.</p>
|
<p>Toutes les informations sur le projet et le code sont disponibles sur <strong><a href="https://github.com/Apes-HDF/EBS">le dépôt public</a></strong>.</p>
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<hr />
|
<hr />
|
||||||
<h2>Démo</h2>
|
<h2>Démo</h2>
|
||||||
|
|
|
||||||
14
fixtures/test/platform_offer.yaml
Normal file
14
fixtures/test/platform_offer.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
App\Entity\PlatformOffer:
|
||||||
|
platform_offer_template (template):
|
||||||
|
configuration: '@features'
|
||||||
|
|
||||||
|
platform_offer_1 (extends platform_offer_template):
|
||||||
|
id: <uuid('016b2a27-1037-6d47-bcdc-ec5efbd723f2')>
|
||||||
|
name: Lorem ipsum
|
||||||
|
price: 2000
|
||||||
|
type: !php/enum App\Enum\OfferType::YEARLY
|
||||||
|
|
||||||
|
platform_offer_2 (extends platform_offer_template):
|
||||||
|
name: Aliquet risus
|
||||||
|
price: 200
|
||||||
|
type: !php/enum App\Enum\OfferType::MONTHLY
|
||||||
|
|
@ -8,6 +8,7 @@ App\Entity\Product:
|
||||||
|
|
||||||
service (template, extends product):
|
service (template, extends product):
|
||||||
type: !php/enum App\Enum\Product\ProductType::SERVICE
|
type: !php/enum App\Enum\Product\ProductType::SERVICE
|
||||||
|
visibility: !php/enum App\Enum\Product\ProductVisibility::RESTRICTED
|
||||||
|
|
||||||
# Loic —————————————————————————————————————————————————————————————————————
|
# Loic —————————————————————————————————————————————————————————————————————
|
||||||
loic_object_1 (extends object):
|
loic_object_1 (extends object):
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@ App\Entity\User:
|
||||||
devAccount: true
|
devAccount: true
|
||||||
address: '@address_loic'
|
address: '@address_loic'
|
||||||
avatar: '7c732ddb-9c13-45eb-aea0-e614f2340e6d.jpg'
|
avatar: '7c732ddb-9c13-45eb-aea0-e614f2340e6d.jpg'
|
||||||
|
membershipPaid: true
|
||||||
|
platformOffer: '@platform_offer_1'
|
||||||
|
startAt: <date_create_immutable('-1 year - 1 day')>
|
||||||
|
endAt: <date_create_immutable('-1 day')>
|
||||||
|
|
||||||
admin_kevin (extends admin_template):
|
admin_kevin (extends admin_template):
|
||||||
id: <uuid('1ed69804-eeb9-6c32-990b-632c3a6846ba')>
|
id: <uuid('1ed69804-eeb9-6c32-990b-632c3a6846ba')>
|
||||||
|
|
@ -42,6 +46,12 @@ App\Entity\User:
|
||||||
firstname: 'Kevin'
|
firstname: 'Kevin'
|
||||||
lastname: 'Pirouet'
|
lastname: 'Pirouet'
|
||||||
avatar: '7c732ddb-9c13-45eb-aea0-e614f2340e6d.jpg'
|
avatar: '7c732ddb-9c13-45eb-aea0-e614f2340e6d.jpg'
|
||||||
|
type: !php/enum App\Enum\User\UserType::ADMIN
|
||||||
|
membershipPaid: true
|
||||||
|
platformOffer: '@platform_offer_1'
|
||||||
|
startAt: <date_create_immutable('-1 year + 7 day')>
|
||||||
|
endAt: <date_create_immutable('+7 day midnight')>
|
||||||
|
roles: [ !php/const App\Entity\User::ROLE_ADMIN, !php/const App\Entity\User::MEMBERSHIP_PAID]
|
||||||
|
|
||||||
admin_apes (extends admin_template):
|
admin_apes (extends admin_template):
|
||||||
id: <uuid('1ed69804-eeb9-6e6c-bce0-632c3a6846ba')>
|
id: <uuid('1ed69804-eeb9-6e6c-bce0-632c3a6846ba')>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{- if .Values.php.fixtureCron.enabled }}
|
||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: CronJob
|
kind: CronJob
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -6,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '0 0 * * *'
|
schedule: '0 0 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -22,19 +24,20 @@ spec:
|
||||||
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
||||||
restartPolicy: Never
|
restartPolicy: Never
|
||||||
containers:
|
containers:
|
||||||
- name: {{ .Chart.Name }}-cronjob-notify-ms-e-7
|
- name: {{ .Chart.Name }}-cronjob-fixture-reset
|
||||||
image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}"
|
image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}"
|
||||||
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
||||||
command: ['/bin/sh', '-c']
|
command: ['/bin/sh', '-c']
|
||||||
args: ['
|
args: ['
|
||||||
|
set -ex;
|
||||||
bin/console doctrine:database:drop --if-exists --force;
|
bin/console doctrine:database:drop --if-exists --force;
|
||||||
bin/console doctrine:database:create --if-not-exists;
|
bin/console doctrine:database:create --if-not-exists;
|
||||||
bin/console doctrine:schema:create;
|
bin/console doctrine:schema:create;
|
||||||
bin/console doctrine:schema:validate;
|
bin/console doctrine:schema:validate;
|
||||||
bin/console messenger:setup-transports;
|
bin/console messenger:setup-transports;
|
||||||
bin/console hautelook:fixtures:load --no-interaction -vv --no-bundles;
|
bin/console hautelook:fixtures:load --no-interaction -vv --no-bundles;
|
||||||
bin/console app:index-products;
|
bin/console app:index-products;'
|
||||||
']
|
]
|
||||||
env:
|
env:
|
||||||
- name: API_ENTRYPOINT_HOST
|
- name: API_ENTRYPOINT_HOST
|
||||||
valueFrom:
|
valueFrom:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '6 1 * * *'
|
schedule: '6 1 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -78,6 +79,23 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: database-url
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
- name: MERCURE_URL
|
- name: MERCURE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
@ -93,6 +111,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: mercure-jwt-secret
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
- name: STORAGE_BUCKET
|
- name: STORAGE_BUCKET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
|
||||||
171
helm/chart/templates/cronjobs/cronjobEndPlatformMembership.yaml
Normal file
171
helm/chart/templates/cronjobs/cronjobEndPlatformMembership.yaml
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
{{- if .Values.dailyCronjobs.enabled }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: {{ include "plateforme-ebs" . }}-cronjob-end-p-membership
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
schedule: '17 1 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
|
jobTemplate:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollme: {{ randAlphaNum 5 | quote }}
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}-cronjob-end-p-membership
|
||||||
|
image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
||||||
|
command: ['/bin/sh', '-c']
|
||||||
|
args: ['bin/console app:end-platform-membership --env=prod']
|
||||||
|
env:
|
||||||
|
- name: API_ENTRYPOINT_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-host
|
||||||
|
- name: JWT_PASSPHRASE
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-passphrase
|
||||||
|
- name: JWT_PUBLIC_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-public-key
|
||||||
|
- name: JWT_SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-secret-key
|
||||||
|
- name: TRUSTED_HOSTS
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-hosts
|
||||||
|
- name: TRUSTED_PROXIES
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-proxies
|
||||||
|
- name: APP_ENV
|
||||||
|
value: "prod"
|
||||||
|
- name: APP_DEBUG
|
||||||
|
value: "0"
|
||||||
|
- name: APP_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-app-secret
|
||||||
|
- name: CORS_ALLOW_ORIGIN
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-cors-allow-origin
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
|
- name: MERCURE_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-url
|
||||||
|
- name: MERCURE_PUBLIC_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-public-url
|
||||||
|
- name: MERCURE_JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
|
- name: STORAGE_BUCKET
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-bucket
|
||||||
|
- name: STORAGE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-endpoint
|
||||||
|
- name: STORAGE_REGION
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-region
|
||||||
|
- name: STORAGE_USE_PATH_STYLE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-use-path-style-endpoint
|
||||||
|
- name: STORAGE_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-key
|
||||||
|
- name: STORAGE_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-secret
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command: ["/bin/sh", "-c", "/bin/sleep 1; kill -QUIT 1"]
|
||||||
|
startupProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
failureThreshold: 40
|
||||||
|
periodSeconds: 3
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources.fixtures | nindent 16 }}
|
||||||
|
{{- end }}
|
||||||
|
|
@ -7,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '12 2 * * *'
|
schedule: '12 2 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -78,6 +79,23 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: database-url
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
- name: MERCURE_URL
|
- name: MERCURE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
@ -93,6 +111,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: mercure-jwt-secret
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
- name: STORAGE_BUCKET
|
- name: STORAGE_BUCKET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '3 21 * * *'
|
schedule: '3 21 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -78,6 +79,23 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: database-url
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
- name: MERCURE_URL
|
- name: MERCURE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
@ -93,6 +111,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: mercure-jwt-secret
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
- name: STORAGE_BUCKET
|
- name: STORAGE_BUCKET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
{{- if .Values.dailyCronjobs.enabled }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: {{ include "plateforme-ebs" . }}-cronjob-notify-pms-e-1
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
schedule: '20 2 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
|
jobTemplate:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollme: {{ randAlphaNum 5 | quote }}
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}-cronjob-notify-pms-e-1
|
||||||
|
image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
||||||
|
command: ['/bin/sh', '-c']
|
||||||
|
args: ['bin/console app:notify-platform-membership-expiration 1 --env=prod']
|
||||||
|
env:
|
||||||
|
- name: API_ENTRYPOINT_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-host
|
||||||
|
- name: JWT_PASSPHRASE
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-passphrase
|
||||||
|
- name: JWT_PUBLIC_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-public-key
|
||||||
|
- name: JWT_SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-secret-key
|
||||||
|
- name: TRUSTED_HOSTS
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-hosts
|
||||||
|
- name: TRUSTED_PROXIES
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-proxies
|
||||||
|
- name: APP_ENV
|
||||||
|
value: "prod"
|
||||||
|
- name: APP_DEBUG
|
||||||
|
value: "0"
|
||||||
|
- name: APP_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-app-secret
|
||||||
|
- name: CORS_ALLOW_ORIGIN
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-cors-allow-origin
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
|
- name: MERCURE_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-url
|
||||||
|
- name: MERCURE_PUBLIC_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-public-url
|
||||||
|
- name: MERCURE_JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
|
- name: STORAGE_BUCKET
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-bucket
|
||||||
|
- name: STORAGE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-endpoint
|
||||||
|
- name: STORAGE_REGION
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-region
|
||||||
|
- name: STORAGE_USE_PATH_STYLE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-use-path-style-endpoint
|
||||||
|
- name: STORAGE_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-key
|
||||||
|
- name: STORAGE_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-secret
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command: ["/bin/sh", "-c", "/bin/sleep 1; kill -QUIT 1"]
|
||||||
|
startupProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
failureThreshold: 40
|
||||||
|
periodSeconds: 3
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources.fixtures | nindent 16 }}
|
||||||
|
{{- end }}
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
{{- if .Values.dailyCronjobs.enabled }}
|
||||||
|
apiVersion: batch/v1
|
||||||
|
kind: CronJob
|
||||||
|
metadata:
|
||||||
|
name: {{ include "plateforme-ebs" . }}-cronjob-notify-pms-e-7
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
|
spec:
|
||||||
|
schedule: '10 21 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
|
jobTemplate:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rollme: {{ randAlphaNum 5 | quote }}
|
||||||
|
labels:
|
||||||
|
{{- include "plateforme-ebs.selectorLabels" . | nindent 8 }}
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
spec:
|
||||||
|
{{- with .Values.imagePullSecrets }}
|
||||||
|
imagePullSecrets:
|
||||||
|
{{- toYaml . | nindent 12 }}
|
||||||
|
{{- end }}
|
||||||
|
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
||||||
|
restartPolicy: Never
|
||||||
|
containers:
|
||||||
|
- name: {{ .Chart.Name }}-cronjob-notify-pms-e-7
|
||||||
|
image: "{{ .Values.php.image.repository }}:{{ .Values.php.image.tag | default .Chart.AppVersion }}"
|
||||||
|
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
||||||
|
command: ['/bin/sh', '-c']
|
||||||
|
args: ['bin/console app:notify-platform-membership-expiration 7 --env=prod']
|
||||||
|
env:
|
||||||
|
- name: API_ENTRYPOINT_HOST
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-host
|
||||||
|
- name: JWT_PASSPHRASE
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-passphrase
|
||||||
|
- name: JWT_PUBLIC_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-public-key
|
||||||
|
- name: JWT_SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-jwt-secret-key
|
||||||
|
- name: TRUSTED_HOSTS
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-hosts
|
||||||
|
- name: TRUSTED_PROXIES
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-trusted-proxies
|
||||||
|
- name: APP_ENV
|
||||||
|
value: "prod"
|
||||||
|
- name: APP_DEBUG
|
||||||
|
value: "0"
|
||||||
|
- name: APP_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-app-secret
|
||||||
|
- name: CORS_ALLOW_ORIGIN
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-cors-allow-origin
|
||||||
|
- name: DATABASE_URL
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
|
- name: MERCURE_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-url
|
||||||
|
- name: MERCURE_PUBLIC_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-public-url
|
||||||
|
- name: MERCURE_JWT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
|
- name: STORAGE_BUCKET
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-bucket
|
||||||
|
- name: STORAGE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-endpoint
|
||||||
|
- name: STORAGE_REGION
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-region
|
||||||
|
- name: STORAGE_USE_PATH_STYLE_ENDPOINT
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-use-path-style-endpoint
|
||||||
|
- name: STORAGE_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-key
|
||||||
|
- name: STORAGE_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: php-storage-secret
|
||||||
|
lifecycle:
|
||||||
|
preStop:
|
||||||
|
exec:
|
||||||
|
command: ["/bin/sh", "-c", "/bin/sleep 1; kill -QUIT 1"]
|
||||||
|
startupProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
failureThreshold: 40
|
||||||
|
periodSeconds: 3
|
||||||
|
readinessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
livenessProbe:
|
||||||
|
exec:
|
||||||
|
command:
|
||||||
|
- docker-healthcheck
|
||||||
|
periodSeconds: 3
|
||||||
|
resources:
|
||||||
|
{{- toYaml .Values.resources.fixtures | nindent 16 }}
|
||||||
|
{{- end }}
|
||||||
|
|
@ -7,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '44 4 * * *'
|
schedule: '44 4 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -78,6 +79,23 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: database-url
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
- name: MERCURE_URL
|
- name: MERCURE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
@ -93,6 +111,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: mercure-jwt-secret
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
- name: STORAGE_BUCKET
|
- name: STORAGE_BUCKET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ metadata:
|
||||||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||||
spec:
|
spec:
|
||||||
schedule: '2 4 * * *'
|
schedule: '2 4 * * *'
|
||||||
|
timeZone: "Europe/Paris"
|
||||||
jobTemplate:
|
jobTemplate:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
|
|
@ -78,6 +79,23 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: database-url
|
key: database-url
|
||||||
|
- name: MAILER_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: mailer-dsn
|
||||||
|
{{- if .Values.meilisearch.enabled }}
|
||||||
|
- name: MEILISEARCH_API_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ printf "%s-%s" (include "meilisearch.fullname" .Subcharts.meilisearch ) "master-key" }}
|
||||||
|
key: MEILI_MASTER_KEY
|
||||||
|
- name: MEILISEARCH_URL
|
||||||
|
valueFrom:
|
||||||
|
configMapKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: meilisearch-url
|
||||||
|
{{- end }}
|
||||||
- name: MERCURE_URL
|
- name: MERCURE_URL
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
@ -93,6 +111,11 @@ spec:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
name: {{ include "plateforme-ebs" . }}
|
name: {{ include "plateforme-ebs" . }}
|
||||||
key: mercure-jwt-secret
|
key: mercure-jwt-secret
|
||||||
|
- name: SMS_DSN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: {{ include "plateforme-ebs" . }}
|
||||||
|
key: sms-dsn
|
||||||
- name: STORAGE_BUCKET
|
- name: STORAGE_BUCKET
|
||||||
valueFrom:
|
valueFrom:
|
||||||
configMapKeyRef:
|
configMapKeyRef:
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{- if .Values.php.fixtureJob.enabled -}}
|
||||||
apiVersion: batch/v1
|
apiVersion: batch/v1
|
||||||
kind: Job
|
kind: Job
|
||||||
metadata:
|
metadata:
|
||||||
|
|
@ -32,18 +33,14 @@ spec:
|
||||||
command: ['/bin/sh', '-c']
|
command: ['/bin/sh', '-c']
|
||||||
args: ['
|
args: ['
|
||||||
set -ex;
|
set -ex;
|
||||||
echo no fixtures job at the moment
|
bin/console doctrine:database:drop --if-exists --force;
|
||||||
|
bin/console doctrine:database:create --if-not-exists;
|
||||||
|
bin/console doctrine:schema:create;
|
||||||
|
bin/console doctrine:schema:validate;
|
||||||
|
bin/console messenger:setup-transports;
|
||||||
|
bin/console hautelook:fixtures:load --no-interaction -vv --no-bundles;
|
||||||
|
bin/console app:index-products;
|
||||||
']
|
']
|
||||||
# if [ "$APP_ENV" != "prod" ]; then
|
|
||||||
# composer install --prefer-dist --no-progress --no-suggest --no-interaction;
|
|
||||||
# fi;
|
|
||||||
# bin/console doctrine:database:drop --if-exists --force;
|
|
||||||
# bin/console doctrine:database:create --if-not-exists;
|
|
||||||
# bin/console doctrine:schema:create;
|
|
||||||
# bin/console doctrine:schema:validate;
|
|
||||||
# bin/console messenger:setup-transports;
|
|
||||||
# bin/console hautelook:fixtures:load --no-interaction -vv --no-bundles;
|
|
||||||
# bin/console app:index-products;
|
|
||||||
env:
|
env:
|
||||||
- name: API_ENTRYPOINT_HOST
|
- name: API_ENTRYPOINT_HOST
|
||||||
valueFrom:
|
valueFrom:
|
||||||
|
|
@ -208,3 +205,4 @@ spec:
|
||||||
tolerations:
|
tolerations:
|
||||||
{{- toYaml . | nindent 8 }}
|
{{- toYaml . | nindent 8 }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
38
helm/chart/values-nonprod.yml
Normal file
38
helm/chart/values-nonprod.yml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
imagePullSecrets:
|
||||||
|
- name: regcred
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
className: nginx
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: letsencrypt-production
|
||||||
|
hosts:
|
||||||
|
- host: toset
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: toset
|
||||||
|
hosts:
|
||||||
|
- toset
|
||||||
|
|
||||||
|
meilisearch:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClass: "standard"
|
||||||
|
size: "1Gi"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
master:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClass: "standard"
|
||||||
|
size: "1Gi"
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
url: change_me
|
||||||
|
|
||||||
|
php:
|
||||||
|
fixtureJob:
|
||||||
|
enabled: true
|
||||||
|
fixtureCron:
|
||||||
|
enabled: true
|
||||||
|
|
@ -29,4 +29,10 @@ redis:
|
||||||
size: "1Gi"
|
size: "1Gi"
|
||||||
|
|
||||||
postgresql:
|
postgresql:
|
||||||
enabled: false
|
url: change_me
|
||||||
|
|
||||||
|
php:
|
||||||
|
fixtureJob:
|
||||||
|
enabled: true
|
||||||
|
fixtureCron:
|
||||||
|
enabled: true
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,13 @@ php:
|
||||||
usePathStyleEndpoint: true
|
usePathStyleEndpoint: true
|
||||||
publicKey: ""
|
publicKey: ""
|
||||||
secret: ""
|
secret: ""
|
||||||
|
fixtureCron:
|
||||||
|
enabled: false
|
||||||
|
fixtureJob:
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
dailyCronjobs:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
maildev:
|
maildev:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
@ -41,9 +48,6 @@ mailer:
|
||||||
sms:
|
sms:
|
||||||
dsn: "null://null"
|
dsn: "null://null"
|
||||||
|
|
||||||
dailyCronjobs:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
consumer:
|
consumer:
|
||||||
# We don't use async for now so consumer isn't needed
|
# We don't use async for now so consumer isn't needed
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
@ -73,6 +77,7 @@ postgresql:
|
||||||
username: "example"
|
username: "example"
|
||||||
password: "!ChangeMe!"
|
password: "!ChangeMe!"
|
||||||
database: "api"
|
database: "api"
|
||||||
|
postgresPassword: "!ChangeMe!"
|
||||||
# Persistent Volume Storage configuration.
|
# Persistent Volume Storage configuration.
|
||||||
# ref: https://kubernetes.io/docs/user-guide/persistent-volumes
|
# ref: https://kubernetes.io/docs/user-guide/persistent-volumes
|
||||||
pullPolicy: IfNotPresent
|
pullPolicy: IfNotPresent
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ final class EndMembershipCommand extends Command
|
||||||
/** @var UserGroup $userGroup */
|
/** @var UserGroup $userGroup */
|
||||||
$user = $userGroup->getUser();
|
$user = $userGroup->getUser();
|
||||||
$group = $userGroup->getGroup();
|
$group = $userGroup->getGroup();
|
||||||
$io->comment(sprintf(' > deleting membership for %s of %s (%s) (%s)',
|
$io->comment(\sprintf(' > deleting membership for %s of %s (%s) (%s)',
|
||||||
$group->getName(),
|
$group->getName(),
|
||||||
$user->getDisplayName(),
|
$user->getDisplayName(),
|
||||||
$userGroup->getMembership()->value,
|
$userGroup->getMembership()->value,
|
||||||
|
|
@ -88,7 +88,7 @@ final class EndMembershipCommand extends Command
|
||||||
++$count;
|
++$count;
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->note(sprintf(' > %d deletion(s) done.', $count));
|
$io->note(\sprintf(' > %d deletion(s) done.', $count));
|
||||||
$this->memoryReport($io);
|
$this->memoryReport($io);
|
||||||
$this->done($io);
|
$this->done($io);
|
||||||
|
|
||||||
|
|
|
||||||
102
src/Command/EndPlatformMembershipCommand.php
Normal file
102
src/Command/EndPlatformMembershipCommand.php
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Doctrine\Manager\UserManager;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Enum\OfferType;
|
||||||
|
use App\Mailer\AppMailer;
|
||||||
|
use App\Mailer\Email\Command\EndPlatformMembershipMail;
|
||||||
|
use App\Notifier\SmsNotifier;
|
||||||
|
use App\Notifier\SmsNotifierTrait;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
|
use App\Repository\UserRepository;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name: self::CMD, description: self::DESCRIPTION)]
|
||||||
|
class EndPlatformMembershipCommand extends Command
|
||||||
|
{
|
||||||
|
use CommandTrait;
|
||||||
|
use SmsNotifierTrait;
|
||||||
|
|
||||||
|
public const CMD = 'app:end-platform-membership';
|
||||||
|
public const DESCRIPTION = 'Check overdue platform membership and set user as unpaid';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserRepository $userRepository,
|
||||||
|
#[Autowire('%kernel.environment%')]
|
||||||
|
private readonly string $environment,
|
||||||
|
private readonly AppMailer $appMailer,
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
|
private readonly TranslatorInterface $translator,
|
||||||
|
private readonly SmsNotifier $notifier,
|
||||||
|
#[Autowire('%brand%')]
|
||||||
|
private readonly string $brand,
|
||||||
|
private readonly UserManager $userManager,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->configureCommand(self::DESCRIPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||||
|
if (!$configuration->getPaidMembership()) {
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
$platform = $configuration->getPlatformName();
|
||||||
|
$io->title(self::DESCRIPTION.' ('.$this->environment.' env)');
|
||||||
|
$this->memoryReport($io);
|
||||||
|
|
||||||
|
$io->section('Getting concerned membership...');
|
||||||
|
$query = $this->userRepository->getExpiredMembership();
|
||||||
|
|
||||||
|
$io->section('Processing user updates...');
|
||||||
|
$count = 0;
|
||||||
|
/** @var User $user */
|
||||||
|
foreach ($query->toIterable() as $user) {
|
||||||
|
if ($user->getPlatformOffer()?->getType() === OfferType::ONESHOT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->comment(\sprintf(' > ending platform membership expiration for user %s (%s)',
|
||||||
|
$user->getDisplayName(),
|
||||||
|
$user->getEmail(),
|
||||||
|
));
|
||||||
|
|
||||||
|
// save it here for the mail before setting it back to null
|
||||||
|
$endAt = $user->getEndAt();
|
||||||
|
|
||||||
|
$user->setMembershipPaid(false)
|
||||||
|
->setEndAt(null)
|
||||||
|
->setPayedAt(null)
|
||||||
|
->setStartAt(null)
|
||||||
|
->setPlatformOffer(null);
|
||||||
|
|
||||||
|
$this->userManager->save($user, true);
|
||||||
|
|
||||||
|
$this->appMailer->send(EndPlatformMembershipMail::class, compact('user', 'platform', 'endAt'));
|
||||||
|
$this->sendSms($user, EndPlatformMembershipMail::class, ['%platform%' => $platform]);
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->note(\sprintf(' > %d update(s) done.', $count));
|
||||||
|
$this->memoryReport($io);
|
||||||
|
$this->done($io);
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -64,7 +64,7 @@ final class NotifyMembershipExpirationCommand extends Command
|
||||||
$days = $input->getArgument('days');
|
$days = $input->getArgument('days');
|
||||||
$days = max(1, (int) $days);
|
$days = max(1, (int) $days);
|
||||||
|
|
||||||
$io->section(sprintf('Getting membership expiring in %d days...', $days));
|
$io->section(\sprintf('Getting membership expiring in %d days...', $days));
|
||||||
$query = $this->userGroupRepository->getExpiring($days);
|
$query = $this->userGroupRepository->getExpiring($days);
|
||||||
$io->section('Sending notificaitons...');
|
$io->section('Sending notificaitons...');
|
||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
@ -72,7 +72,7 @@ final class NotifyMembershipExpirationCommand extends Command
|
||||||
/** @var UserGroup $userGroup */
|
/** @var UserGroup $userGroup */
|
||||||
$user = $userGroup->getUser();
|
$user = $userGroup->getUser();
|
||||||
$group = $userGroup->getGroup();
|
$group = $userGroup->getGroup();
|
||||||
$io->comment(sprintf(' > notifying membership for %s of %s/%s (%s) (%s)',
|
$io->comment(\sprintf(' > notifying membership for %s of %s/%s (%s) (%s)',
|
||||||
$group->getName(),
|
$group->getName(),
|
||||||
$user->getDisplayName(),
|
$user->getDisplayName(),
|
||||||
$userGroup->getEndAt()?->format('Y-m-d'),
|
$userGroup->getEndAt()?->format('Y-m-d'),
|
||||||
|
|
@ -88,7 +88,7 @@ final class NotifyMembershipExpirationCommand extends Command
|
||||||
++$count;
|
++$count;
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->note(sprintf(' > %d notification(s) sent.', $count));
|
$io->note(\sprintf(' > %d notification(s) sent.', $count));
|
||||||
|
|
||||||
$this->memoryReport($io);
|
$this->memoryReport($io);
|
||||||
$this->done($io);
|
$this->done($io);
|
||||||
|
|
|
||||||
95
src/Command/NotifyPlatformMembershipExpirationCommand.php
Normal file
95
src/Command/NotifyPlatformMembershipExpirationCommand.php
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Command;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Enum\OfferType;
|
||||||
|
use App\Mailer\AppMailer;
|
||||||
|
use App\Mailer\Email\Command\NotifyPlatformMembershipExpirationMail;
|
||||||
|
use App\Notifier\SmsNotifier;
|
||||||
|
use App\Notifier\SmsNotifierTrait;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
|
use App\Repository\UserRepository;
|
||||||
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
#[AsCommand(name: self::CMD, description: self::DESCRIPTION)]
|
||||||
|
class NotifyPlatformMembershipExpirationCommand extends Command
|
||||||
|
{
|
||||||
|
use CommandTrait;
|
||||||
|
use SmsNotifierTrait;
|
||||||
|
|
||||||
|
public const CMD = 'app:notify-platform-membership-expiration';
|
||||||
|
public const DESCRIPTION = 'Notify expiring platform membership.';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly UserRepository $userRepository,
|
||||||
|
private readonly TranslatorInterface $translator,
|
||||||
|
private readonly AppMailer $appMailer,
|
||||||
|
private readonly SmsNotifier $notifier,
|
||||||
|
#[Autowire('%kernel.environment%')]
|
||||||
|
private readonly string $environment,
|
||||||
|
#[Autowire('%brand%')]
|
||||||
|
private readonly string $brand,
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
|
) {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->configureCommand(self::DESCRIPTION);
|
||||||
|
$this->addArgument('days', InputArgument::REQUIRED, 'Number of days from tomorrow (1 = notify members expiring tomorrow)');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||||
|
if (!$configuration->getPaidMembership()) {
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
$platform = $configuration->getPlatformName();
|
||||||
|
$io->title(self::DESCRIPTION.' ('.$this->environment.' env)');
|
||||||
|
$this->memoryReport($io);
|
||||||
|
|
||||||
|
/** @var string $days */
|
||||||
|
$days = $input->getArgument('days');
|
||||||
|
$days = max(1, (int) $days);
|
||||||
|
|
||||||
|
$io->section(\sprintf('Getting platform membership expiring in %d days...', $days));
|
||||||
|
$query = $this->userRepository->getExpiring($days);
|
||||||
|
$io->section('Sending notifications...');
|
||||||
|
$count = 0;
|
||||||
|
/** @var User $user */
|
||||||
|
foreach ($query->toIterable() as $user) {
|
||||||
|
if ($user->getPlatformOffer()?->getType() === OfferType::ONESHOT) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->comment(\sprintf(' > notifying platform membership expiration for user %s (%s)',
|
||||||
|
$user->getDisplayName(),
|
||||||
|
$user->getEmail(),
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->appMailer->send(NotifyPlatformMembershipExpirationMail::class, compact('user', 'days', 'platform'));
|
||||||
|
$this->sendSms($user, NotifyPlatformMembershipExpirationMail::class, ['%days%' => $days, '%platform%' => $platform]);
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
|
||||||
|
$io->note(\sprintf(' > %d notification(s) sent.', $count));
|
||||||
|
|
||||||
|
$this->memoryReport($io);
|
||||||
|
$this->done($io);
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -84,7 +84,7 @@ final class NotifyServiceRequestDatesCommand extends Command
|
||||||
$serviceRequest->getStartAt() :
|
$serviceRequest->getStartAt() :
|
||||||
$serviceRequest->getEndAt()
|
$serviceRequest->getEndAt()
|
||||||
;
|
;
|
||||||
$io->comment(sprintf(' > notifying owner and recipient for service request %s (%s) starting on %s.',
|
$io->comment(\sprintf(' > notifying owner and recipient for service request %s (%s) starting on %s.',
|
||||||
$serviceRequest->getId(),
|
$serviceRequest->getId(),
|
||||||
$serviceRequest->getStatus()->value,
|
$serviceRequest->getStatus()->value,
|
||||||
$referenceDate->format('Y-m-d')
|
$referenceDate->format('Y-m-d')
|
||||||
|
|
@ -106,7 +106,7 @@ final class NotifyServiceRequestDatesCommand extends Command
|
||||||
++$count;
|
++$count;
|
||||||
}
|
}
|
||||||
|
|
||||||
$io->note(sprintf(' > %d notification(s) sent.', $count * 2)); // owner and recipient
|
$io->note(\sprintf(' > %d notification(s) sent.', $count * 2)); // owner and recipient
|
||||||
|
|
||||||
$this->memoryReport($io);
|
$this->memoryReport($io);
|
||||||
$this->done($io);
|
$this->done($io);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller\Admin;
|
namespace App\Controller\Admin;
|
||||||
|
|
||||||
|
use App\Controller\i18nTrait;
|
||||||
use App\EasyAdmin\Field\FieldTrait;
|
use App\EasyAdmin\Field\FieldTrait;
|
||||||
use App\EasyAdmin\Filter\EnumFilter;
|
use App\EasyAdmin\Filter\EnumFilter;
|
||||||
use App\EasyAdmin\Filter\UuidFilter;
|
use App\EasyAdmin\Filter\UuidFilter;
|
||||||
|
|
@ -15,8 +16,10 @@ use App\Enum\Product\ProductType;
|
||||||
use App\Enum\Product\ProductVisibility;
|
use App\Enum\Product\ProductVisibility;
|
||||||
use App\Flysystem\EasyAdminHelper;
|
use App\Flysystem\EasyAdminHelper;
|
||||||
use App\Flysystem\MediaManager;
|
use App\Flysystem\MediaManager;
|
||||||
|
use App\Form\Type\Product\AbstractProductFormType;
|
||||||
use App\Helper\CsvExporter;
|
use App\Helper\CsvExporter;
|
||||||
use App\Repository\CategoryRepository;
|
use App\Repository\CategoryRepository;
|
||||||
|
use App\Repository\GroupRepository;
|
||||||
use App\Repository\ProductRepository;
|
use App\Repository\ProductRepository;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||||
|
|
@ -54,6 +57,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
abstract class AbstractProductCrudController extends AbstractCrudController implements AdminSecuredCrudControllerInterface
|
abstract class AbstractProductCrudController extends AbstractCrudController implements AdminSecuredCrudControllerInterface
|
||||||
{
|
{
|
||||||
use FieldTrait;
|
use FieldTrait;
|
||||||
|
use i18nTrait;
|
||||||
|
|
||||||
abstract public function getProductType(): ProductType;
|
abstract public function getProductType(): ProductType;
|
||||||
|
|
||||||
|
|
@ -74,6 +78,7 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly FilterFactory $filterFactory,
|
private readonly FilterFactory $filterFactory,
|
||||||
private readonly SluggerInterface $slugger,
|
private readonly SluggerInterface $slugger,
|
||||||
|
protected readonly GroupRepository $groupRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,6 +93,9 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
||||||
'@EasyAdmin/crud/form_theme.html.twig',
|
'@EasyAdmin/crud/form_theme.html.twig',
|
||||||
'easy_admin/crud/form_theme.html.twig',
|
'easy_admin/crud/form_theme.html.twig',
|
||||||
])
|
])
|
||||||
|
->setFormOptions([
|
||||||
|
'validation_groups' => [AbstractProductFormType::class],
|
||||||
|
])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,9 +249,15 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
||||||
->setFormType(EnumType::class)
|
->setFormType(EnumType::class)
|
||||||
->setFormTypeOption('class', ProductVisibility::class)
|
->setFormTypeOption('class', ProductVisibility::class)
|
||||||
->setChoices(ProductVisibility::getAsArray());
|
->setChoices(ProductVisibility::getAsArray());
|
||||||
$groupsField = CollectionField::new('groups');
|
$groupsField = AssociationField::new('groups')->onlyOnForms();
|
||||||
|
$groupsFieldList = CollectionField::new('groups')->hideOnForm();
|
||||||
|
|
||||||
$ownerField = AssociationField::new('owner');
|
$ownerField = AssociationField::new('owner')
|
||||||
|
->setFormTypeOption('attr', [
|
||||||
|
'data-controller' => 'admin-parentgroup',
|
||||||
|
'data-admin-parentgroup-target' => 'ownerField',
|
||||||
|
])
|
||||||
|
->addWebpackEncoreEntries('admin');
|
||||||
$categoryField = AssociationField::new('category')
|
$categoryField = AssociationField::new('category')
|
||||||
->setQueryBuilder(function (QueryBuilder $queryBuilder) {
|
->setQueryBuilder(function (QueryBuilder $queryBuilder) {
|
||||||
return $this->categoryRepository->addTypeFilter($queryBuilder, $this->getProductType());
|
return $this->categoryRepository->addTypeFilter($queryBuilder, $this->getProductType());
|
||||||
|
|
@ -300,6 +314,7 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
||||||
'statusField',
|
'statusField',
|
||||||
'visibilityField',
|
'visibilityField',
|
||||||
'groupsField',
|
'groupsField',
|
||||||
|
'groupsFieldList',
|
||||||
'ownerField',
|
'ownerField',
|
||||||
'categoryField',
|
'categoryField',
|
||||||
'nameField',
|
'nameField',
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use App\Flysystem\MediaManager;
|
||||||
use App\Helper\CsvExporter;
|
use App\Helper\CsvExporter;
|
||||||
use App\Mailer\AppMailer;
|
use App\Mailer\AppMailer;
|
||||||
use App\Mailer\Email\Admin\PromoteToAdmin\PromoteToAdminEmail;
|
use App\Mailer\Email\Admin\PromoteToAdmin\PromoteToAdminEmail;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||||
|
|
@ -37,10 +38,12 @@ use EasyCorp\Bundle\EasyAdminBundle\Factory\FormFactory;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\DateField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Filter\DateTimeFilter;
|
use EasyCorp\Bundle\EasyAdminBundle\Filter\DateTimeFilter;
|
||||||
|
|
@ -89,6 +92,7 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
||||||
#[Autowire('%user_base_path%')]
|
#[Autowire('%user_base_path%')]
|
||||||
private readonly string $userBasePath,
|
private readonly string $userBasePath,
|
||||||
AppMailer $mailer,
|
AppMailer $mailer,
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
) {
|
) {
|
||||||
$this->mailer = $mailer;
|
$this->mailer = $mailer;
|
||||||
}
|
}
|
||||||
|
|
@ -355,6 +359,20 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
||||||
$vacationModeField = BooleanField::new('vacationMode');
|
$vacationModeField = BooleanField::new('vacationMode');
|
||||||
$addressField = AssociationField::new('address');
|
$addressField = AssociationField::new('address');
|
||||||
$groupsCountField = AssociationField::new('userGroups')->setLabel('Groups number');
|
$groupsCountField = AssociationField::new('userGroups')->setLabel('Groups number');
|
||||||
|
$membershipPaidField = $this->getSimpleBooleanField('membershipPaid');
|
||||||
|
$startAt = DateField::new('startAt');
|
||||||
|
$endAt = DateField::new('endAt');
|
||||||
|
$expiresInField = IntegerField::new('expiresIn')
|
||||||
|
->formatValue(function ($value) {
|
||||||
|
return $value !== null ? $this->translator->trans($this->getI18nPrefix().'.expires_in.formatted_value', ['%days%' => $value], 'admin') : '';
|
||||||
|
})
|
||||||
|
->setFormTypeOptions([
|
||||||
|
'attr' => ['readonly' => 'readonly'],
|
||||||
|
'required' => false,
|
||||||
|
])
|
||||||
|
;
|
||||||
|
$payedAt = DateTimeField::new('payedAt');
|
||||||
|
$offerField = AssociationField::new('platformOffer');
|
||||||
|
|
||||||
return compact(
|
return compact(
|
||||||
'idField',
|
'idField',
|
||||||
|
|
@ -378,6 +396,12 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
||||||
'vacationModeField',
|
'vacationModeField',
|
||||||
'addressField',
|
'addressField',
|
||||||
'groupsCountField',
|
'groupsCountField',
|
||||||
|
'membershipPaidField',
|
||||||
|
'startAt',
|
||||||
|
'endAt',
|
||||||
|
'expiresInField',
|
||||||
|
'payedAt',
|
||||||
|
'offerField',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -445,4 +469,9 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
||||||
|
|
||||||
return $builder;
|
return $builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function platformRequiresGlobalPayment(): bool
|
||||||
|
{
|
||||||
|
return $this->configurationRepository->getInstanceConfigurationOrCreate()->getPaidMembership();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,15 +22,18 @@ use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
|
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
|
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All /admin routes are protected at the security.yaml level.
|
* All /admin routes are protected at the security.yaml level.
|
||||||
|
* ROLE_ADMIN inherits the ROLE_GROUP_ADMIN role.
|
||||||
|
*
|
||||||
|
* @see security.yaml
|
||||||
*/
|
*/
|
||||||
#[Security("is_granted('".User::ROLE_ADMIN."') or is_granted('".User::ROLE_GROUP_ADMIN."')")]
|
#[IsGranted(User::ROLE_GROUP_ADMIN)]
|
||||||
final class DashboardController extends AbstractDashboardController
|
final class DashboardController extends AbstractDashboardController
|
||||||
{
|
{
|
||||||
public const DOMAIN = 'admin';
|
public const DOMAIN = 'admin';
|
||||||
|
|
@ -60,7 +63,7 @@ final class DashboardController extends AbstractDashboardController
|
||||||
private readonly AdminUrlGenerator $adminUrlGenerator,
|
private readonly AdminUrlGenerator $adminUrlGenerator,
|
||||||
private readonly AuthorizationChecker $authorizationChecker,
|
private readonly AuthorizationChecker $authorizationChecker,
|
||||||
private readonly UserRepository $userRepository,
|
private readonly UserRepository $userRepository,
|
||||||
private readonly ServiceRequestRepository $requestRepository
|
private readonly ServiceRequestRepository $requestRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ use App\Form\Type\Security\GroupInvitationFormType;
|
||||||
use App\Helper\CsvExporter;
|
use App\Helper\CsvExporter;
|
||||||
use App\Message\Command\Group\CreateGroupInvitationMessage;
|
use App\Message\Command\Group\CreateGroupInvitationMessage;
|
||||||
use App\MessageBus\CommandBus;
|
use App\MessageBus\CommandBus;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
use App\Security\Checker\AuthorizationChecker;
|
use App\Security\Checker\AuthorizationChecker;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||||
|
|
@ -66,6 +67,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
private readonly TranslatorInterface $translator,
|
private readonly TranslatorInterface $translator,
|
||||||
private readonly FilterFactory $filterFactory,
|
private readonly FilterFactory $filterFactory,
|
||||||
private readonly SluggerInterface $slugger,
|
private readonly SluggerInterface $slugger,
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +77,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
->setEntityLabelInPlural('groups')
|
->setEntityLabelInPlural('groups')
|
||||||
->setSearchFields(['name', 'description'])
|
->setSearchFields(['name', 'description'])
|
||||||
->setDefaultSort(['id' => 'ASC'])
|
->setDefaultSort(['id' => 'ASC'])
|
||||||
|
->overrideTemplate('crud/field/boolean', 'admin/field/services_enabled.html.twig')
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -209,7 +212,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$qb->andWhere(sprintf('%s.id IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
$qb->andWhere(\sprintf('%s.id IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
||||||
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
|
|
@ -230,8 +233,24 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
->setFormTypeOption('class', GroupMembership::class)
|
->setFormTypeOption('class', GroupMembership::class)
|
||||||
->setChoices(GroupMembership::getAsArray());
|
->setChoices(GroupMembership::getAsArray());
|
||||||
|
|
||||||
|
if ($this->configurationRepository->getInstanceConfigurationOrCreate()->getServicesEnabled()) {
|
||||||
|
$servicesEnabledField = BooleanField::new('servicesEnabled')
|
||||||
|
->renderAsSwitch()
|
||||||
|
->setFormTypeOption('attr', [
|
||||||
|
'data-controller' => 'admin-parentgroup',
|
||||||
|
'data-admin-parentgroup-target' => 'servicesEnabledField',
|
||||||
|
])
|
||||||
|
->addWebpackEncoreEntries('admin');
|
||||||
|
}
|
||||||
|
|
||||||
$parentField = AssociationField::new('parent')
|
$parentField = AssociationField::new('parent')
|
||||||
->setRequired(false);
|
->setRequired(false)
|
||||||
|
->addWebpackEncoreEntries('admin')
|
||||||
|
->setFormTypeOption('attr', [
|
||||||
|
'data-controller' => 'admin-parentgroup',
|
||||||
|
'data-admin-parentgroup-target' => 'parentField',
|
||||||
|
])
|
||||||
|
;
|
||||||
$childrenField = AssociationField::new('children');
|
$childrenField = AssociationField::new('children');
|
||||||
$usersField = AssociationField::new('userGroups')
|
$usersField = AssociationField::new('userGroups')
|
||||||
->setTemplatePath('admin/group/user_groups_field.html.twig');
|
->setTemplatePath('admin/group/user_groups_field.html.twig');
|
||||||
|
|
@ -247,14 +266,20 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
$panels = $this->getPanels();
|
$panels = $this->getPanels();
|
||||||
|
|
||||||
if ($pageName === Crud::PAGE_INDEX) {
|
if ($pageName === Crud::PAGE_INDEX) {
|
||||||
return [$nameField, $typeField, $parentField, $membershipField, $usersField, $createdAt, $updatedAt];
|
$fields = [$nameField, $typeField, $parentField, $membershipField, $usersField, $createdAt, $updatedAt];
|
||||||
|
|
||||||
|
if ($this->configurationRepository->getInstanceConfigurationOrCreate()->getServicesEnabled()) {
|
||||||
|
array_splice($fields, 3, 0, [$servicesEnabledField]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||||
$typeField->setChoices(GroupType::cases());
|
$typeField->setChoices(GroupType::cases());
|
||||||
$membershipField->setChoices(GroupMembership::cases());
|
$membershipField->setChoices(GroupMembership::cases());
|
||||||
|
|
||||||
return [
|
$fields = [
|
||||||
$nameField,
|
$nameField,
|
||||||
$typeField,
|
$typeField,
|
||||||
$membershipField,
|
$membershipField,
|
||||||
|
|
@ -264,11 +289,29 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
$invitationByAdminField,
|
$invitationByAdminField,
|
||||||
$membershipField,
|
$membershipField,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($this->configurationRepository->getInstanceConfigurationOrCreate()->getServicesEnabled()) {
|
||||||
|
$i18prefix = $this->getI18nPrefix(self::class);
|
||||||
|
/** @var Group|null $group */
|
||||||
|
$group = $this->getContext()?->getEntity()?->getInstance();
|
||||||
|
if (null !== $group) {
|
||||||
|
foreach ($group->getParentsRecursively() as $parent) {
|
||||||
|
if (!$parent->getServicesEnabled()) {
|
||||||
|
$servicesEnabledField->setDisabled();
|
||||||
|
$servicesEnabledField->setHelp($i18prefix.'.field.services_enabled.parent_disabled');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
array_splice($fields, 4, 0, [$servicesEnabledField]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
// show
|
// show
|
||||||
|
|
||||||
return [
|
$fields = [
|
||||||
$panels['information'],
|
$panels['information'],
|
||||||
$nameField,
|
$nameField,
|
||||||
$parentField,
|
$parentField,
|
||||||
|
|
@ -283,6 +326,12 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
$updatedAt,
|
$updatedAt,
|
||||||
$createdAt,
|
$createdAt,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($this->configurationRepository->getInstanceConfigurationOrCreate()->getServicesEnabled()) {
|
||||||
|
array_splice($fields, 3, 0, [$servicesEnabledField]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -309,7 +358,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For now we export exactly what we see in the list to avoid seurity problems.
|
* For now we export exactly what we see in the list to avoid security problems.
|
||||||
*/
|
*/
|
||||||
public function export(AdminContext $context): Response
|
public function export(AdminContext $context): Response
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ use App\EasyAdmin\Field\FieldTrait;
|
||||||
use App\EasyAdmin\Filter\EnumFilter;
|
use App\EasyAdmin\Filter\EnumFilter;
|
||||||
use App\EasyAdmin\Filter\UserGroup\MyGroupFilter;
|
use App\EasyAdmin\Filter\UserGroup\MyGroupFilter;
|
||||||
use App\EasyAdmin\Filter\UuidFilter;
|
use App\EasyAdmin\Filter\UuidFilter;
|
||||||
use App\EasyAdmin\Form\Type\GroupOfferTypeType;
|
use App\EasyAdmin\Form\Type\OfferTypeType;
|
||||||
use App\Entity\GroupOffer;
|
use App\Entity\GroupOffer;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Enum\Group\GroupMembership;
|
use App\Enum\Group\GroupMembership;
|
||||||
use App\Enum\Group\GroupOfferType;
|
|
||||||
use App\Enum\Group\UserMembership;
|
use App\Enum\Group\UserMembership;
|
||||||
|
use App\Enum\OfferType;
|
||||||
use App\Security\Checker\AuthorizationChecker;
|
use App\Security\Checker\AuthorizationChecker;
|
||||||
use Doctrine\ORM\QueryBuilder;
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||||
|
|
@ -65,7 +65,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
||||||
return $filters
|
return $filters
|
||||||
->add(UuidFilter::new('id'))
|
->add(UuidFilter::new('id'))
|
||||||
->add(MyGroupFilter::new('group'))
|
->add(MyGroupFilter::new('group'))
|
||||||
->add(EnumFilter::new('membership', GroupOfferTypeType::class))
|
->add(EnumFilter::new('membership', OfferTypeType::class))
|
||||||
->add('name')
|
->add('name')
|
||||||
->add('active')
|
->add('active')
|
||||||
;
|
;
|
||||||
|
|
@ -98,7 +98,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$qb->andWhere(sprintf('%s.group IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
$qb->andWhere(\sprintf('%s.group IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
||||||
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
|
|
@ -129,8 +129,8 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
||||||
$nameField = TextField::new('name');
|
$nameField = TextField::new('name');
|
||||||
$typeField = ChoiceField::new('type')
|
$typeField = ChoiceField::new('type')
|
||||||
->setFormType(EnumType::class)
|
->setFormType(EnumType::class)
|
||||||
->setFormTypeOption('class', GroupOfferType::class)
|
->setFormTypeOption('class', OfferType::class)
|
||||||
->setChoices(GroupOfferType::getAsArray());
|
->setChoices(OfferType::getAsArray());
|
||||||
|
|
||||||
$priceField = MoneyField::new('price')
|
$priceField = MoneyField::new('price')
|
||||||
->setCurrencyPropertyPath('currency')
|
->setCurrencyPropertyPath('currency')
|
||||||
|
|
@ -149,7 +149,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||||
$typeField->setChoices(GroupOfferType::cases());
|
$typeField->setChoices(OfferType::cases());
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$groupField,
|
$groupField,
|
||||||
|
|
|
||||||
121
src/Controller/Admin/PlatformOfferCrudController.php
Normal file
121
src/Controller/Admin/PlatformOfferCrudController.php
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller\Admin;
|
||||||
|
|
||||||
|
use App\Controller\FlashTrait;
|
||||||
|
use App\Controller\i18nTrait;
|
||||||
|
use App\EasyAdmin\Field\FieldTrait;
|
||||||
|
use App\EasyAdmin\Filter\EnumFilter;
|
||||||
|
use App\EasyAdmin\Filter\UuidFilter;
|
||||||
|
use App\EasyAdmin\Form\Type\OfferTypeType;
|
||||||
|
use App\Entity\PlatformOffer;
|
||||||
|
use App\Enum\OfferType;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\BooleanField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\CurrencyField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\MoneyField;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
||||||
|
|
||||||
|
final class PlatformOfferCrudController extends AbstractCrudController implements AdminSecuredCrudControllerInterface
|
||||||
|
{
|
||||||
|
use FlashTrait;
|
||||||
|
use FieldTrait;
|
||||||
|
use i18nTrait;
|
||||||
|
|
||||||
|
public function configureCrud(Crud $crud): Crud
|
||||||
|
{
|
||||||
|
return $crud
|
||||||
|
->setEntityLabelInPlural('platform_offers')
|
||||||
|
->setSearchFields(['name'])
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureFilters(Filters $filters): Filters
|
||||||
|
{
|
||||||
|
return $filters
|
||||||
|
->add(UuidFilter::new('id'))
|
||||||
|
->add(EnumFilter::new('type', OfferTypeType::class))
|
||||||
|
->add('name')
|
||||||
|
->add('active')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureActions(Actions $actions): Actions
|
||||||
|
{
|
||||||
|
return $actions
|
||||||
|
->add(Crud::PAGE_INDEX, Action::DETAIL)
|
||||||
|
->add(Crud::PAGE_EDIT, Action::DETAIL)
|
||||||
|
->add(Crud::PAGE_EDIT, Action::INDEX)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getEntityFqcn(): string
|
||||||
|
{
|
||||||
|
return PlatformOffer::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureFields(string $pageName): iterable
|
||||||
|
{
|
||||||
|
$idField = IdField::new('id')
|
||||||
|
->setLabel('id')
|
||||||
|
->hideOnForm();
|
||||||
|
|
||||||
|
$nameField = TextField::new('name');
|
||||||
|
$typeField = ChoiceField::new('type')
|
||||||
|
->setFormType(EnumType::class)
|
||||||
|
->setFormTypeOption('class', OfferType::class)
|
||||||
|
->setChoices(OfferType::getAsArray());
|
||||||
|
|
||||||
|
$priceField = MoneyField::new('price')
|
||||||
|
->setCurrencyPropertyPath('currency')
|
||||||
|
->setStoredAsCents();
|
||||||
|
$currencyField = CurrencyField::new('currency');
|
||||||
|
|
||||||
|
$activeField = BooleanField::new('active')
|
||||||
|
->setTemplatePath('easy_admin/field/boolean.html.twig')
|
||||||
|
;
|
||||||
|
$createdAtField = DateTimeField::new('createdAt');
|
||||||
|
$updatedAtField = DateTimeField::new('updatedAt');
|
||||||
|
|
||||||
|
$panels = $this->getPanels();
|
||||||
|
if ($pageName === Crud::PAGE_INDEX) {
|
||||||
|
return [$nameField, $typeField, $priceField, $activeField, $createdAtField, $updatedAtField];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||||
|
$typeField->setChoices(OfferType::cases());
|
||||||
|
|
||||||
|
return [
|
||||||
|
$nameField,
|
||||||
|
$typeField,
|
||||||
|
$priceField,
|
||||||
|
$currencyField,
|
||||||
|
$activeField,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// show
|
||||||
|
return [
|
||||||
|
$panels['information'],
|
||||||
|
$nameField,
|
||||||
|
$typeField,
|
||||||
|
$priceField,
|
||||||
|
$currencyField,
|
||||||
|
|
||||||
|
$panels['tech_information'],
|
||||||
|
$idField,
|
||||||
|
$updatedAtField,
|
||||||
|
$createdAtField,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,10 +5,13 @@ declare(strict_types=1);
|
||||||
namespace App\Controller\Admin;
|
namespace App\Controller\Admin;
|
||||||
|
|
||||||
use App\Entity\Product;
|
use App\Entity\Product;
|
||||||
|
use App\Enum\Group\UserMembership;
|
||||||
use App\Enum\Product\ProductStatus;
|
use App\Enum\Product\ProductStatus;
|
||||||
use App\Enum\Product\ProductType;
|
use App\Enum\Product\ProductType;
|
||||||
use App\Enum\Product\ProductVisibility;
|
use App\Enum\Product\ProductVisibility;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||||
|
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
||||||
|
|
||||||
|
|
@ -36,6 +39,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
||||||
{
|
{
|
||||||
$product = parent::createEntity($entityFqcn);
|
$product = parent::createEntity($entityFqcn);
|
||||||
$product->setCurrency(null); // remove the default value which is not needed here
|
$product->setCurrency(null); // remove the default value which is not needed here
|
||||||
|
$product->setVisibility(ProductVisibility::RESTRICTED);
|
||||||
|
|
||||||
return $product;
|
return $product;
|
||||||
}
|
}
|
||||||
|
|
@ -49,6 +53,8 @@ final class ServiceCrudController extends AbstractProductCrudController
|
||||||
'typeField' => $typeField,
|
'typeField' => $typeField,
|
||||||
'statusField' => $statusField,
|
'statusField' => $statusField,
|
||||||
'visibilityField' => $visibilityField,
|
'visibilityField' => $visibilityField,
|
||||||
|
'groupsField' => $groupsField,
|
||||||
|
'groupsFieldList' => $groupsFieldList,
|
||||||
'ownerField' => $ownerField,
|
'ownerField' => $ownerField,
|
||||||
'categoryField' => $categoryField,
|
'categoryField' => $categoryField,
|
||||||
'nameField' => $nameField,
|
'nameField' => $nameField,
|
||||||
|
|
@ -61,7 +67,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
||||||
|
|
||||||
// list
|
// list
|
||||||
if ($pageName === Crud::PAGE_INDEX) {
|
if ($pageName === Crud::PAGE_INDEX) {
|
||||||
return [$nameField, $ownerField, $categoryField, $statusField, $visibilityField, $imageField, $createdAt];
|
return [$nameField, $ownerField, $categoryField, $statusField, $visibilityField, $groupsFieldList, $imageField, $createdAt];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var ImageField $imageField */
|
/** @var ImageField $imageField */
|
||||||
|
|
@ -71,10 +77,35 @@ final class ServiceCrudController extends AbstractProductCrudController
|
||||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||||
/** @var ChoiceField $statusField */
|
/** @var ChoiceField $statusField */
|
||||||
$statusField->setChoices(ProductStatus::cases());
|
$statusField->setChoices(ProductStatus::cases());
|
||||||
/** @var ChoiceField $visibilityField */
|
|
||||||
$visibilityField->setChoices(ProductVisibility::cases());
|
|
||||||
|
|
||||||
return [$nameField, $ownerField, $categoryField, $statusField, $visibilityField, $descriptionField, $imageField, $durationField];
|
$i18prefix = $this->getI18nPrefix(self::class);
|
||||||
|
|
||||||
|
/** @var AssociationField $groupsField */
|
||||||
|
$groupsField->setHelp($i18prefix.'.field.groups.help');
|
||||||
|
|
||||||
|
if ($pageName === Crud::PAGE_NEW) {
|
||||||
|
return [$nameField, $ownerField, $categoryField, $statusField, $groupsField, $descriptionField, $imageField, $durationField];
|
||||||
|
}
|
||||||
|
/** @var Product|null $product */
|
||||||
|
$product = $this->getContext()?->getEntity()?->getInstance();
|
||||||
|
$owner = $product?->getOwner();
|
||||||
|
if (null !== $owner && !$owner->getUserGroupsConfirmedWithServices()->isEmpty()) {
|
||||||
|
$groupsField->setQueryBuilder(function (QueryBuilder $queryBuilder) use ($owner) {
|
||||||
|
return $queryBuilder
|
||||||
|
->join('entity.userGroups', 'ug')
|
||||||
|
->andWhere('ug.membership != :membership')
|
||||||
|
->andWhere('ug.user = :user')
|
||||||
|
->andWhere('entity.servicesEnabled = :true')
|
||||||
|
->setParameter('user', $owner)
|
||||||
|
->setParameter('membership', UserMembership::INVITATION)
|
||||||
|
->setParameter('true', true)
|
||||||
|
;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
$groupsField->setDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$nameField, $ownerField, $categoryField, $statusField, $groupsField, $descriptionField, $imageField, $durationField];
|
||||||
}
|
}
|
||||||
|
|
||||||
// detail
|
// detail
|
||||||
|
|
@ -85,6 +116,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
||||||
$categoryField,
|
$categoryField,
|
||||||
$statusField,
|
$statusField,
|
||||||
$visibilityField,
|
$visibilityField,
|
||||||
|
$groupsFieldList,
|
||||||
$nameField,
|
$nameField,
|
||||||
$descriptionField,
|
$descriptionField,
|
||||||
$durationField,
|
$durationField,
|
||||||
|
|
|
||||||
|
|
@ -48,16 +48,27 @@ final class UserCrudController extends AbstractUserCrudController
|
||||||
'vacationModeField' => $vacationModeField,
|
'vacationModeField' => $vacationModeField,
|
||||||
'addressField' => $addressField,
|
'addressField' => $addressField,
|
||||||
'groupsCountField' => $groupsCountField,
|
'groupsCountField' => $groupsCountField,
|
||||||
|
'membershipPaidField' => $membershipPaidField,
|
||||||
|
'startAt' => $startAt,
|
||||||
|
'endAt' => $endAt,
|
||||||
|
'expiresInField' => $expiresInField,
|
||||||
|
'payedAt' => $payedAt,
|
||||||
|
'offerField' => $offerField,
|
||||||
] = $this->getFields($pageName);
|
] = $this->getFields($pageName);
|
||||||
|
|
||||||
if ($pageName === Crud::PAGE_INDEX) {
|
if ($pageName === Crud::PAGE_INDEX) {
|
||||||
return [$emailField, $firstNameField, $lastNameField, $enabledField, $emailConfirmedField, $avatarField, $createdAt, $updatedAt, $loginAt, $groupsCountField];
|
$listFields = [$emailField, $firstNameField, $lastNameField, $enabledField, $emailConfirmedField, $avatarField, $createdAt, $updatedAt, $loginAt, $groupsCountField];
|
||||||
|
if ($this->platformRequiresGlobalPayment()) {
|
||||||
|
$listFields[] = $membershipPaidField;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $listFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
$panels = $this->getPanels();
|
$panels = $this->getPanels();
|
||||||
|
|
||||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||||
return [
|
$editFields = [
|
||||||
$panels['information'],
|
$panels['information'],
|
||||||
$emailField,
|
$emailField,
|
||||||
$firstNameField,
|
$firstNameField,
|
||||||
|
|
@ -74,9 +85,21 @@ final class UserCrudController extends AbstractUserCrudController
|
||||||
$enabledField,
|
$enabledField,
|
||||||
$emailConfirmedField,
|
$emailConfirmedField,
|
||||||
];
|
];
|
||||||
|
if ($this->platformRequiresGlobalPayment()) {
|
||||||
|
$editFields = array_merge($editFields, [
|
||||||
|
$panels['payment_information'],
|
||||||
|
$membershipPaidField,
|
||||||
|
$offerField,
|
||||||
|
$startAt,
|
||||||
|
$endAt,
|
||||||
|
$payedAt,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return $editFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
$showFields = [
|
||||||
$panels['information'],
|
$panels['information'],
|
||||||
$emailField,
|
$emailField,
|
||||||
$firstNameField,
|
$firstNameField,
|
||||||
|
|
@ -97,5 +120,17 @@ final class UserCrudController extends AbstractUserCrudController
|
||||||
$updatedAt,
|
$updatedAt,
|
||||||
$loginAt,
|
$loginAt,
|
||||||
];
|
];
|
||||||
|
if ($this->platformRequiresGlobalPayment()) {
|
||||||
|
$showFields = array_merge($showFields, [
|
||||||
|
$panels['payment_information'],
|
||||||
|
$membershipPaidField,
|
||||||
|
$startAt,
|
||||||
|
$endAt,
|
||||||
|
$payedAt,
|
||||||
|
$expiresInField,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $showFields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ final class UserGroupCrudController extends AbstractCrudController implements Gr
|
||||||
|
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$qb->andWhere(sprintf('%s.group IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
$qb->andWhere(\sprintf('%s.group IN (:groups)', $qb->getRootAliases()[0] ?? ''))
|
||||||
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
->setParameter(':groups', $user->getMyGroupsAsAdmin());
|
||||||
|
|
||||||
return $qb;
|
return $qb;
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ final class CreateGroupAction extends AbstractController
|
||||||
use FlashTrait;
|
use FlashTrait;
|
||||||
use GroupTrait;
|
use GroupTrait;
|
||||||
|
|
||||||
public const MAX_ELEMENT_BY_PAGE = 20;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly QueryBus $queryBus,
|
private readonly QueryBus $queryBus,
|
||||||
private readonly GroupRepository $groupRepository,
|
private readonly GroupRepository $groupRepository,
|
||||||
|
|
@ -52,7 +50,7 @@ final class CreateGroupAction extends AbstractController
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route([
|
#[Route([
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/groups/create-my-group',
|
'en' => MyAccountAction::BASE_URL_EN.'/groups/create-my-group',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/groupes/creer-mon-groupe',
|
'fr' => MyAccountAction::BASE_URL_FR.'/groupes/creer-mon-groupe',
|
||||||
|
|
@ -61,6 +59,16 @@ final class CreateGroupAction extends AbstractController
|
||||||
)]
|
)]
|
||||||
public function createGroup(Request $request, #[CurrentUser] User $user): Response
|
public function createGroup(Request $request, #[CurrentUser] User $user): Response
|
||||||
{
|
{
|
||||||
|
// Admin must use the admin interface
|
||||||
|
if ($user->isAdmin()) {
|
||||||
|
return $this->redirect(
|
||||||
|
$this->adminUrlGenerator
|
||||||
|
->setController(GroupCrudController::class)
|
||||||
|
->set('crudAction', Crud::PAGE_NEW)
|
||||||
|
->generateUrl()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||||
if (!$configuration->isGroupsCreationForAll()) {
|
if (!$configuration->isGroupsCreationForAll()) {
|
||||||
throw $this->createAccessDeniedException('Cannot create group with current settings.');
|
throw $this->createAccessDeniedException('Cannot create group with current settings.');
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ trait PaginationTrait
|
||||||
*
|
*
|
||||||
* @implements PaginationInterface<int,Product>
|
* @implements PaginationInterface<int,Product>
|
||||||
*
|
*
|
||||||
* @return PaginationInterface<int,Product>
|
* @return PaginationInterface<int,mixed>
|
||||||
*/
|
*/
|
||||||
private function paginate(SearchResult $searchResult): PaginationInterface
|
private function paginate(SearchResult $searchResult): PaginationInterface
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller\Payment;
|
namespace App\Controller\Payment\Group;
|
||||||
|
|
||||||
use App\Controller\FlashTrait;
|
use App\Controller\FlashTrait;
|
||||||
use App\Controller\i18nTrait;
|
use App\Controller\i18nTrait;
|
||||||
|
|
@ -18,7 +18,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use Symfony\Component\Routing\Requirement\Requirement;
|
use Symfony\Component\Routing\Requirement\Requirement;
|
||||||
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
@ -67,6 +67,7 @@ final class DoneAction extends AbstractController
|
||||||
$this->addFlashSuccess($this->translator->trans($this->getI18nPrefix().'.flash.success', [
|
$this->addFlashSuccess($this->translator->trans($this->getI18nPrefix().'.flash.success', [
|
||||||
'%group%' => $groupOffer->getGroup()->getName()],
|
'%group%' => $groupOffer->getGroup()->getName()],
|
||||||
));
|
));
|
||||||
|
$request->getSession()->remove('payment_in_progress');
|
||||||
} else {
|
} else {
|
||||||
$this->addFlashWarning($this->translator->trans($this->getI18nPrefix().'.status.'.$status->getValue()));
|
$this->addFlashWarning($this->translator->trans($this->getI18nPrefix().'.status.'.$status->getValue()));
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller\Payment;
|
namespace App\Controller\Payment\Group;
|
||||||
|
|
||||||
use App\Entity\GroupOffer;
|
use App\Entity\GroupOffer;
|
||||||
use App\Repository\GroupOfferRepository;
|
use App\Repository\GroupOfferRepository;
|
||||||
|
|
@ -2,14 +2,13 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace App\Controller\Payment;
|
namespace App\Controller\Payment\Group;
|
||||||
|
|
||||||
use App\Controller\User\MyAccountAction;
|
use App\Controller\User\MyAccountAction;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Payment\PayumManager;
|
use App\Payment\PayumManager;
|
||||||
use App\Repository\GroupOfferRepository;
|
use App\Repository\GroupOfferRepository;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
// use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
84
src/Controller/Payment/PlatformMembership/DoneAction.php
Executable file
84
src/Controller/Payment/PlatformMembership/DoneAction.php
Executable file
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller\Payment\PlatformMembership;
|
||||||
|
|
||||||
|
use App\Controller\FlashTrait;
|
||||||
|
use App\Controller\i18nTrait;
|
||||||
|
use App\Entity\PaymentToken;
|
||||||
|
use App\Entity\PlatformOffer;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Message\Command\Payment\PlatformMembershipPaidCommand;
|
||||||
|
use App\MessageBus\CommandBusInterface;
|
||||||
|
use Payum\Core\Payum;
|
||||||
|
use Payum\Core\Request\GetHumanStatus;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Component\Routing\Requirement\Requirement;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
#[IsGranted(User::ROLE_USER)]
|
||||||
|
final class DoneAction extends AbstractController
|
||||||
|
{
|
||||||
|
use i18nTrait;
|
||||||
|
use FlashTrait;
|
||||||
|
|
||||||
|
public const ROUTE_NAME = 'app_platform_payment_done';
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private readonly CommandBusInterface $commandBus,
|
||||||
|
private readonly Payum $payum,
|
||||||
|
private readonly TranslatorInterface $translator,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/Payum/Payum/blob/master/docs/symfony/get-it-started.md#payment-is-done
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/payment/{id}/done',
|
||||||
|
name: self::ROUTE_NAME,
|
||||||
|
requirements: ['id' => Requirement::UUID_V6],
|
||||||
|
)]
|
||||||
|
public function __invoke(Request $request, #[MapEntity(expr: 'repository.findOneActive(id)')] PlatformOffer $platformOffer, #[CurrentUser] User $user): Response
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
/** @var PaymentToken $token */
|
||||||
|
$token = $this->payum->getHttpRequestVerifier()->verify($request);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->error($e->getMessage());
|
||||||
|
throw new UnprocessableEntityHttpException('Cannot verify Payum token.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var GetHumanStatus $status */
|
||||||
|
$status = $this->commandBus->dispatch(new PlatformMembershipPaidCommand($platformOffer->getId(), $user->getId(), $token));
|
||||||
|
|
||||||
|
$request->getSession()->remove('payment_in_progress');
|
||||||
|
// Not captured
|
||||||
|
if (!$status->isCaptured()) {
|
||||||
|
$this->addFlashWarning($this->translator->trans($this->getI18nPrefix().'.status.'.$status->getValue()));
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_my_account');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addFlashSuccess($this->translator->trans($this->getI18nPrefix().'.flash.success', [
|
||||||
|
'%platform%' => $platformOffer->getConfiguration()?->getPlatformName()],
|
||||||
|
));
|
||||||
|
|
||||||
|
$group = $user->getMyGroupsAsInvited()->first();
|
||||||
|
if ($group !== false) {
|
||||||
|
return $this->redirectToRoute('app_group_show_logged', $group->getRoutingParameters());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_user_my_account');
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/Controller/Payment/PlatformMembership/PrepareAction.php
Normal file
69
src/Controller/Payment/PlatformMembership/PrepareAction.php
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller\Payment\PlatformMembership;
|
||||||
|
|
||||||
|
use App\Entity\PlatformOffer;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Payment\PayumManager;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
|
use App\Repository\PlatformOfferRepository;
|
||||||
|
use Symfony\Bridge\Doctrine\Attribute\MapEntity;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
use Symfony\Component\Routing\Requirement\Requirement;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
|
|
||||||
|
#[IsGranted(User::ROLE_USER)]
|
||||||
|
final class PrepareAction extends AbstractController
|
||||||
|
{
|
||||||
|
public const ROUTE_NAME = 'app_platform_payment_prepare';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see https://github.com/Payum/Payum/blob/master/docs/symfony/get-it-started.md#prepare-order)
|
||||||
|
*/
|
||||||
|
#[Route(
|
||||||
|
path: '/payment/{id}/prepare',
|
||||||
|
name: self::ROUTE_NAME,
|
||||||
|
requirements: ['id' => Requirement::UUID_V6],
|
||||||
|
methods: ['POST'],
|
||||||
|
)]
|
||||||
|
public function preparePayment(Request $request, #[MapEntity(expr: 'repository.findOneActive(id)')] PlatformOffer $platformOffer, #[CurrentUser] User $user, PayumManager $payumManager): Response
|
||||||
|
{
|
||||||
|
/** @var ?string $token */
|
||||||
|
$token = $request->request->get('token');
|
||||||
|
if (!$this->isCsrfTokenValid('payment_prepare', $token)) {
|
||||||
|
throw new UnprocessableEntityHttpException('Invalid CSRF token');
|
||||||
|
}
|
||||||
|
|
||||||
|
$request->getSession()->set('payment_in_progress', true);
|
||||||
|
|
||||||
|
// create and save the payment main reference
|
||||||
|
$payment = $payumManager->getPayment($platformOffer, $user);
|
||||||
|
|
||||||
|
// create the capture token and redirect to the capture action
|
||||||
|
$captureToken = $payumManager->getCaptureToken($payment, DoneAction::ROUTE_NAME, [
|
||||||
|
'id' => (string) $platformOffer->getId(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $this->redirect($captureToken->getTargetUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route(path: [
|
||||||
|
'en' => '/en/subcription',
|
||||||
|
'fr' => '/fr/abonnement',
|
||||||
|
], name: 'redirect_to_payment')]
|
||||||
|
public function redirectToPayment(PlatformOfferRepository $platformOfferRepository, ConfigurationRepository $configurationRepository): Response
|
||||||
|
{
|
||||||
|
$offers = $platformOfferRepository->findBy(['active' => true]);
|
||||||
|
$lowOffer = $platformOfferRepository->findLowOffer();
|
||||||
|
$platformName = $configurationRepository->getInstanceConfigurationOrCreate()->getPlatformName();
|
||||||
|
|
||||||
|
return $this->render('pages/redirect_to_payment.html.twig', compact('offers', 'lowOffer', 'platformName'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,7 @@ use App\Message\Query\Security\GetUserByTokenQuery;
|
||||||
use App\MessageBus\CommandBus;
|
use App\MessageBus\CommandBus;
|
||||||
use App\MessageBus\QueryBus;
|
use App\MessageBus\QueryBus;
|
||||||
use App\MessageHandler\Command\Security\AccountCreateStep1CommandHandler;
|
use App\MessageHandler\Command\Security\AccountCreateStep1CommandHandler;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Bundle\SecurityBundle\Security;
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
@ -41,6 +42,7 @@ final class AccountCreateController extends AbstractController
|
||||||
private readonly QueryBus $queryBus,
|
private readonly QueryBus $queryBus,
|
||||||
private readonly CommandBus $commandBus,
|
private readonly CommandBus $commandBus,
|
||||||
private readonly Security $security,
|
private readonly Security $security,
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
) {
|
) {
|
||||||
$this->i18nPrefix = $this->getI18nPrefix();
|
$this->i18nPrefix = $this->getI18nPrefix();
|
||||||
}
|
}
|
||||||
|
|
@ -97,6 +99,7 @@ final class AccountCreateController extends AbstractController
|
||||||
return $this->redirectToRoute('app_login');
|
return $this->redirectToRoute('app_login');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||||
// nominal case: user found and token not expired
|
// nominal case: user found and token not expired
|
||||||
$form = $this->createForm(AccountCreateStep2FormType::class, $user->setStep2Defaults())->handleRequest($request);
|
$form = $this->createForm(AccountCreateStep2FormType::class, $user->setStep2Defaults())->handleRequest($request);
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
|
@ -110,13 +113,26 @@ final class AccountCreateController extends AbstractController
|
||||||
// page group.
|
// page group.
|
||||||
$group = $user->getMyGroupsAsInvited()->first();
|
$group = $user->getMyGroupsAsInvited()->first();
|
||||||
if ($group !== false) {
|
if ($group !== false) {
|
||||||
$this->addFlashSuccess($this->i18nPrefix.'.step2.with_invitation.flash.success');
|
// If platform needs payment, redirect to payment
|
||||||
|
if ($configuration->getPaidMembership()) {
|
||||||
|
$successMessage = $this->i18nPrefix.'.step2.with_invitation.global_paid_membership.flash.success';
|
||||||
|
$this->addFlashSuccess($successMessage);
|
||||||
|
|
||||||
|
return $this->redirectToRoute('redirect_to_payment');
|
||||||
|
}
|
||||||
|
$successMessage = $this->i18nPrefix.'.step2.with_invitation.flash.success';
|
||||||
|
$this->addFlashSuccess($successMessage);
|
||||||
|
|
||||||
return $this->redirectToRoute('app_group_show_logged', $group->getRoutingParameters());
|
return $this->redirectToRoute('app_group_show_logged', $group->getRoutingParameters());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($configuration->getPaidMembership()) {
|
||||||
|
$successMessage = $this->i18nPrefix.'.step2.global_paid_membership.flash.success';
|
||||||
|
} else {
|
||||||
|
$successMessage = $this->i18nPrefix.'.step2.flash.success';
|
||||||
|
}
|
||||||
// otherwise go to the address form
|
// otherwise go to the address form
|
||||||
$this->addFlashSuccess($this->i18nPrefix.'.step2.flash.success');
|
$this->addFlashSuccess($successMessage);
|
||||||
|
|
||||||
return $this->redirectToRoute(MyAccountAction::ROUTE);
|
return $this->redirectToRoute(MyAccountAction::ROUTE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ final class AddressController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* @see UserAddressQueryHandler
|
* @see UserAddressQueryHandler
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-1',
|
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-1',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/mon-adresse/etape-1',
|
'fr' => MyAccountAction::BASE_URL_FR.'/mon-adresse/etape-1',
|
||||||
|
|
@ -75,7 +75,7 @@ final class AddressController extends AbstractController
|
||||||
return $this->render('pages/account/address/step1.html.twig', compact('form'));
|
return $this->render('pages/account/address/step1.html.twig', compact('form'));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-2',
|
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-2',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/mon-adresse/etape-2',
|
'fr' => MyAccountAction::BASE_URL_FR.'/mon-adresse/etape-2',
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
/**
|
/**
|
||||||
* @see UserGroupControllerTest
|
* @see UserGroupControllerTest
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
final class UserGroupController extends AbstractController
|
final class UserGroupController extends AbstractController
|
||||||
{
|
{
|
||||||
use SecurityTrait;
|
use SecurityTrait;
|
||||||
|
|
@ -39,7 +39,7 @@ final class UserGroupController extends AbstractController
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly QueryBus $queryBus,
|
private readonly QueryBus $queryBus,
|
||||||
private readonly CommandBus $commandBus
|
private readonly CommandBus $commandBus,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ final class MyAccountAction extends AbstractController
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => self::BASE_URL_EN,
|
'en' => self::BASE_URL_EN,
|
||||||
'fr' => self::BASE_URL_FR,
|
'fr' => self::BASE_URL_FR,
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ final class ProductAvailabilityController extends AbstractController
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/my-products/{id}/availabilities',
|
'en' => MyAccountAction::BASE_URL_EN.'/my-products/{id}/availabilities',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/mes-produits/{id}/disponibilites',
|
'fr' => MyAccountAction::BASE_URL_FR.'/mes-produits/{id}/disponibilites',
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use App\Controller\User\MyAccountAction;
|
||||||
use App\Doctrine\Manager\ProductManager;
|
use App\Doctrine\Manager\ProductManager;
|
||||||
use App\Entity\Product;
|
use App\Entity\Product;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
|
use App\Enum\Product\ProductVisibility;
|
||||||
use App\Form\Type\Product\ServiceFormType;
|
use App\Form\Type\Product\ServiceFormType;
|
||||||
use App\MessageBus\QueryBus;
|
use App\MessageBus\QueryBus;
|
||||||
use App\Repository\ConfigurationRepository;
|
use App\Repository\ConfigurationRepository;
|
||||||
|
|
@ -56,6 +57,7 @@ final class ServiceController extends AbstractController
|
||||||
{
|
{
|
||||||
if ($this->configurationRepository->getServicesParameter()) {
|
if ($this->configurationRepository->getServicesParameter()) {
|
||||||
$product = $this->productManager->initService($user);
|
$product = $this->productManager->initService($user);
|
||||||
|
$product->setVisibility(ProductVisibility::RESTRICTED);
|
||||||
$form = $this->getForm($product, $request);
|
$form = $this->getForm($product, $request);
|
||||||
if ($form->isSubmitted() && $form->isValid()) {
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
/** @var array<UploadedFile>|null $images */
|
/** @var array<UploadedFile>|null $images */
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
/**
|
/**
|
||||||
* @see UserProductsControllerTest
|
* @see UserProductsControllerTest
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(name: 'app_user_')]
|
#[Route(name: 'app_user_')]
|
||||||
final class UserProductsController extends AbstractController
|
final class UserProductsController extends AbstractController
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +49,7 @@ final class UserProductsController extends AbstractController
|
||||||
/**
|
/**
|
||||||
* @implements PaginationInterface<int,Product>
|
* @implements PaginationInterface<int,Product>
|
||||||
*
|
*
|
||||||
* @return PaginationInterface<int,Product>
|
* @return PaginationInterface<int,mixed>
|
||||||
*/
|
*/
|
||||||
private function paginate(Query $query, int $page): PaginationInterface
|
private function paginate(Query $query, int $page): PaginationInterface
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||||
/**
|
/**
|
||||||
* @see ConversationControllerTest
|
* @see ConversationControllerTest
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
class ConversationController extends AbstractController
|
class ConversationController extends AbstractController
|
||||||
{
|
{
|
||||||
use FlashTrait;
|
use FlashTrait;
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ final class MyLendingsAction extends AbstractController
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/my-lendings',
|
'en' => MyAccountAction::BASE_URL_EN.'/my-lendings',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/mes-prets',
|
'fr' => MyAccountAction::BASE_URL_FR.'/mes-prets',
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ class MyLoansAction extends AbstractController
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
#[Route(path: [
|
#[Route(path: [
|
||||||
'en' => MyAccountAction::BASE_URL_EN.'/my-loans',
|
'en' => MyAccountAction::BASE_URL_EN.'/my-loans',
|
||||||
'fr' => MyAccountAction::BASE_URL_FR.'/mes-emprunts',
|
'fr' => MyAccountAction::BASE_URL_FR.'/mes-emprunts',
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ use Symfony\Component\Uid\Uuid;
|
||||||
/**
|
/**
|
||||||
* @see ServiceRequestControllerTest
|
* @see ServiceRequestControllerTest
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
class ServiceRequestController extends AbstractController
|
class ServiceRequestController extends AbstractController
|
||||||
{
|
{
|
||||||
use FlashTrait;
|
use FlashTrait;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ use function Symfony\Component\String\u;
|
||||||
/**
|
/**
|
||||||
* @see ServiceRequestStatusWorkflowControllerTest
|
* @see ServiceRequestStatusWorkflowControllerTest
|
||||||
*/
|
*/
|
||||||
#[isGranted(User::ROLE_USER)]
|
#[IsGranted(User::ROLE_USER)]
|
||||||
class ServiceRequestStatusWorkflowController extends AbstractController
|
class ServiceRequestStatusWorkflowController extends AbstractController
|
||||||
{
|
{
|
||||||
use FlashTrait;
|
use FlashTrait;
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ trait i18nTrait
|
||||||
* Get the i18n prefix of a given class to have a consistent key naming in i18n
|
* Get the i18n prefix of a given class to have a consistent key naming in i18n
|
||||||
* files.
|
* files.
|
||||||
*/
|
*/
|
||||||
public function getI18nPrefix(string $class = null): string
|
public function getI18nPrefix(?string $class = null): string
|
||||||
{
|
{
|
||||||
$class = $class ?? $this::class;
|
$class = $class ?? $this::class;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||||
final class ValidationProcessor implements ProcessorInterface
|
final class ValidationProcessor implements ProcessorInterface
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ValidatorInterface $validator
|
private readonly ValidatorInterface $validator,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@ final class ValidationProcessor implements ProcessorInterface
|
||||||
/** @var ConstraintViolationList $violations */
|
/** @var ConstraintViolationList $violations */
|
||||||
$violations = $this->validator->validate($object);
|
$violations = $this->validator->validate($object);
|
||||||
if ($violations->count() > 0) {
|
if ($violations->count() > 0) {
|
||||||
$message = sprintf("Error when validating fixture \"%s\", violation(s) detected:\n%s", $id, $violations);
|
$message = \sprintf("Error when validating fixture \"%s\", violation(s) detected:\n%s", $id, $violations);
|
||||||
throw new \DomainException($message);
|
throw new \DomainException($message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
148
src/Doctrine/Behavior/AbstractOfferEntity.php
Normal file
148
src/Doctrine/Behavior/AbstractOfferEntity.php
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Doctrine\Behavior;
|
||||||
|
|
||||||
|
use App\Enum\OfferType;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Bridge\Doctrine\IdGenerator\UuidGenerator;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
|
#[ORM\MappedSuperclass]
|
||||||
|
abstract class AbstractOfferEntity implements \Stringable
|
||||||
|
{
|
||||||
|
use TimestampableEntity;
|
||||||
|
|
||||||
|
final public const DEFAULT_CURRENCY = 'EUR';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a V6 uuid.
|
||||||
|
*/
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\Column(type: 'uuid', unique: true)]
|
||||||
|
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||||
|
#[ORM\CustomIdGenerator(class: UuidGenerator::class)]
|
||||||
|
protected Uuid $id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Short name of the offer.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
|
||||||
|
#[Assert\NotBlank]
|
||||||
|
#[Assert\Length(max: 255)]
|
||||||
|
protected string $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of offer.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(name: 'type', type: 'string', nullable: false, enumType: OfferType::class)]
|
||||||
|
#[Assert\NotBlank]
|
||||||
|
protected OfferType $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Price, we stored the amount multiplied by 100 so we can use an integer for
|
||||||
|
* this property.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: Types::INTEGER, nullable: false)]
|
||||||
|
protected int $price;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associated currency for the price property.
|
||||||
|
*
|
||||||
|
* @see https://en.wikipedia.org/wiki/ISO_4217
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: Types::STRING, nullable: false)]
|
||||||
|
protected string $currency = self::DEFAULT_CURRENCY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the offer is visible on the front site. Can be used to deactivate offers
|
||||||
|
* for some time.
|
||||||
|
*/
|
||||||
|
#[ORM\Column(type: 'boolean', nullable: false)]
|
||||||
|
protected bool $active = true;
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->name.' ('.$this->type->value.')';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): Uuid
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setId(Uuid $uuid): self
|
||||||
|
{
|
||||||
|
$this->id = $uuid;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(string $name): self
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): OfferType
|
||||||
|
{
|
||||||
|
return $this->type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setType(OfferType $type): self
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPrice(int $price): self
|
||||||
|
{
|
||||||
|
$this->price = $price;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrice(): int
|
||||||
|
{
|
||||||
|
return $this->price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActualPrice(): int
|
||||||
|
{
|
||||||
|
return $this->price / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrency(): string
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrency(string $currency): self
|
||||||
|
{
|
||||||
|
$this->currency = $currency;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool
|
||||||
|
{
|
||||||
|
return $this->active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setActive(bool $active): self
|
||||||
|
{
|
||||||
|
$this->active = $active;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/Doctrine/Listener/MembershipPaidListener.php
Normal file
40
src/Doctrine/Listener/MembershipPaidListener.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Doctrine\Listener;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Repository\ConfigurationRepository;
|
||||||
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||||
|
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||||
|
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||||
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
|
|
||||||
|
#[AsEventListener(event: ExceptionEvent::class, method: 'onKernelException')]
|
||||||
|
final class MembershipPaidListener
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ConfigurationRepository $configurationRepository,
|
||||||
|
private readonly Security $security,
|
||||||
|
private readonly RouterInterface $router,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onKernelException(ExceptionEvent $event): void
|
||||||
|
{
|
||||||
|
$user = $this->security->getUser();
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$config = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||||
|
$session = $event->getRequest()->getSession();
|
||||||
|
/** @var bool $isPaymentInProgress */
|
||||||
|
$isPaymentInProgress = $session->get('payment_in_progress');
|
||||||
|
|
||||||
|
if ($config->getPaidMembership() && !$user->isMembershipPaid() && !$isPaymentInProgress) {
|
||||||
|
$event->setResponse(new RedirectResponse($this->router->generate('redirect_to_payment')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@ use function Symfony\Component\String\u;
|
||||||
final class UserListener
|
final class UserListener
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly UserManager $userManager
|
private readonly UserManager $userManager,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ final class MessageManager
|
||||||
ServiceRequest $serviceRequest,
|
ServiceRequest $serviceRequest,
|
||||||
string $messageTemplate,
|
string $messageTemplate,
|
||||||
array $messageParameters = [],
|
array $messageParameters = [],
|
||||||
\DateTimeImmutable $createdAt = null,
|
?\DateTimeImmutable $createdAt = null,
|
||||||
): Message {
|
): Message {
|
||||||
$message = (new Message())
|
$message = (new Message())
|
||||||
->setServiceRequest($serviceRequest)
|
->setServiceRequest($serviceRequest)
|
||||||
|
|
@ -61,7 +61,7 @@ final class MessageManager
|
||||||
return $message;
|
return $message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createFromRecipientMessage(ServiceRequest $serviceRequest, string $message, \DateTimeImmutable $createdAt = null): Message
|
public function createFromRecipientMessage(ServiceRequest $serviceRequest, string $message, ?\DateTimeImmutable $createdAt = null): Message
|
||||||
{
|
{
|
||||||
$message = (new Message())
|
$message = (new Message())
|
||||||
->setServiceRequest($serviceRequest)
|
->setServiceRequest($serviceRequest)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class ProductManager
|
||||||
try {
|
try {
|
||||||
$this->productStorage->delete($image);
|
$this->productStorage->delete($image);
|
||||||
} catch (FilesystemException $e) {
|
} catch (FilesystemException $e) {
|
||||||
$this->logger->warning(sprintf('Unable to delete product (%s) image %s: %s', $product->getId(), $image, $e->getMessage()));
|
$this->logger->warning(\sprintf('Unable to delete product (%s) image %s: %s', $product->getId(), $image, $e->getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
$product->deleteImage($image);
|
$product->deleteImage($image);
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@ final class UserManager
|
||||||
try {
|
try {
|
||||||
$this->userStorage->delete((string) $user->getAvatar());
|
$this->userStorage->delete((string) $user->getAvatar());
|
||||||
} catch (FilesystemException $e) {
|
} catch (FilesystemException $e) {
|
||||||
$this->logger->warning(sprintf('Unable to avatar of user (%s) image %s: %s', $user->getId(), $user->getAvatar(), $e->getMessage()));
|
$this->logger->warning(\sprintf('Unable to avatar of user (%s) image %s: %s', $user->getId(), $user->getAvatar(), $e->getMessage()));
|
||||||
}
|
}
|
||||||
$user->deleteAvatar();
|
$user->deleteAvatar();
|
||||||
$this->save($user, true);
|
$this->save($user, true);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use App\Form\Type\Product\SearchFormType;
|
||||||
*/
|
*/
|
||||||
final class Search
|
final class Search
|
||||||
{
|
{
|
||||||
public function __construct(string $q, int $page = 1, User $user = null)
|
public function __construct(string $q, int $page = 1, ?User $user = null)
|
||||||
{
|
{
|
||||||
$this->q = $q;
|
$this->q = $q;
|
||||||
$this->page = $page;
|
$this->page = $page;
|
||||||
|
|
@ -78,9 +78,9 @@ final class Search
|
||||||
*/
|
*/
|
||||||
public function hasProximity(): bool
|
public function hasProximity(): bool
|
||||||
{
|
{
|
||||||
return $this->hasCity() &&
|
return $this->hasCity()
|
||||||
($this->city?->hasLocality() ?? false) &&
|
&& ($this->city?->hasLocality() ?? false)
|
||||||
$this->hasDistance()
|
&& $this->hasDistance()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ trait FieldTrait
|
||||||
/**
|
/**
|
||||||
* Render the boolean without switch: ✅ ❌.
|
* Render the boolean without switch: ✅ ❌.
|
||||||
*/
|
*/
|
||||||
public function getSimpleBooleanField(string $propertyName, string $label = null): BooleanField
|
public function getSimpleBooleanField(string $propertyName, ?string $label = null): BooleanField
|
||||||
{
|
{
|
||||||
return BooleanField::new($propertyName, $label)
|
return BooleanField::new($propertyName, $label)
|
||||||
->renderAsSwitch(false)
|
->renderAsSwitch(false)
|
||||||
|
|
@ -30,6 +30,7 @@ trait FieldTrait
|
||||||
return [
|
return [
|
||||||
'information' => FormField::addPanel('panel.information', 'fas fa-info-circle'),
|
'information' => FormField::addPanel('panel.information', 'fas fa-info-circle'),
|
||||||
'tech_information' => FormField::addPanel('panel.tech_information', 'fas fa-history'),
|
'tech_information' => FormField::addPanel('panel.tech_information', 'fas fa-history'),
|
||||||
|
'payment_information' => FormField::addPanel('panel.payment_information', 'fas fa-dollar-sign'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ final class EnumFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
public static function new(string $propertyName, string $formType, string $label = null): self
|
public static function new(string $propertyName, string $formType, ?string $label = null): self
|
||||||
{
|
{
|
||||||
return (new self())
|
return (new self())
|
||||||
->setFilterFqcn(__CLASS__)
|
->setFilterFqcn(__CLASS__)
|
||||||
|
|
@ -32,7 +32,7 @@ final class EnumFilter implements FilterInterface
|
||||||
*/
|
*/
|
||||||
public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
|
public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
|
||||||
{
|
{
|
||||||
$queryBuilder->andWhere(sprintf('%s.%s = :value', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty()))
|
$queryBuilder->andWhere(\sprintf('%s.%s = :value', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty()))
|
||||||
->setParameter('value', $filterDataDto->getValue());
|
->setParameter('value', $filterDataDto->getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ final class GroupFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
public static function new(string $propertyName, string $label = null): self
|
public static function new(string $propertyName, ?string $label = null): self
|
||||||
{
|
{
|
||||||
return (new self())
|
return (new self())
|
||||||
->setFilterFqcn(__CLASS__)
|
->setFilterFqcn(__CLASS__)
|
||||||
|
|
@ -37,7 +37,7 @@ final class GroupFilter implements FilterInterface
|
||||||
/** @var Group $group */
|
/** @var Group $group */
|
||||||
$group = $filterDataDto->getValue();
|
$group = $filterDataDto->getValue();
|
||||||
$queryBuilder
|
$queryBuilder
|
||||||
->innerJoin(sprintf('%s.userGroups', $filterDataDto->getEntityAlias()), 'ug')
|
->innerJoin(\sprintf('%s.userGroups', $filterDataDto->getEntityAlias()), 'ug')
|
||||||
->andWhere('ug.group = :group')
|
->andWhere('ug.group = :group')
|
||||||
->setParameter(':group', $group->getId())
|
->setParameter(':group', $group->getId())
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ final class MyUsersFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
public static function new(string $propertyName, string $label = null): self
|
public static function new(string $propertyName, ?string $label = null): self
|
||||||
{
|
{
|
||||||
return (new self())
|
return (new self())
|
||||||
->setFilterFqcn(__CLASS__)
|
->setFilterFqcn(__CLASS__)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ final class MyGroupFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
public static function new(string $propertyName, string $label = null): self
|
public static function new(string $propertyName, ?string $label = null): self
|
||||||
{
|
{
|
||||||
return (new self())
|
return (new self())
|
||||||
->setFilterFqcn(__CLASS__)
|
->setFilterFqcn(__CLASS__)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ final class UuidFilter implements FilterInterface
|
||||||
{
|
{
|
||||||
use FilterTrait;
|
use FilterTrait;
|
||||||
|
|
||||||
public static function new(string $propertyName, string $label = null): self
|
public static function new(string $propertyName, ?string $label = null): self
|
||||||
{
|
{
|
||||||
return (new self())
|
return (new self())
|
||||||
->setFilterFqcn(__CLASS__)
|
->setFilterFqcn(__CLASS__)
|
||||||
|
|
@ -37,7 +37,7 @@ final class UuidFilter implements FilterInterface
|
||||||
*/
|
*/
|
||||||
public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
|
public function apply(QueryBuilder $queryBuilder, FilterDataDto $filterDataDto, ?FieldDto $fieldDto, EntityDto $entityDto): void
|
||||||
{
|
{
|
||||||
$queryBuilder->andWhere(sprintf('%s.%s = :value', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty()))
|
$queryBuilder->andWhere(\sprintf('%s.%s = :value', $filterDataDto->getEntityAlias(), $filterDataDto->getProperty()))
|
||||||
->setParameter('value', $filterDataDto->getValue());
|
->setParameter('value', $filterDataDto->getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ class GroupType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Security $security,
|
private readonly Security $security,
|
||||||
private readonly AuthorizationChecker $authorizationChecker
|
private readonly AuthorizationChecker $authorizationChecker,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,20 +5,20 @@ declare(strict_types=1);
|
||||||
namespace App\EasyAdmin\Form\Type;
|
namespace App\EasyAdmin\Form\Type;
|
||||||
|
|
||||||
use App\Controller\Admin\DashboardController;
|
use App\Controller\Admin\DashboardController;
|
||||||
use App\Enum\Group\GroupOfferType;
|
use App\Enum\OfferType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form type for the GroupOfferType enumeration.
|
* Form type for the OfferType enumeration.
|
||||||
*/
|
*/
|
||||||
class GroupOfferTypeType extends AbstractType
|
class OfferTypeType extends AbstractType
|
||||||
{
|
{
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'choices' => GroupOfferType::getAsArray(),
|
'choices' => OfferType::getAsArray(),
|
||||||
'translation_domain' => DashboardController::DOMAIN,
|
'translation_domain' => DashboardController::DOMAIN,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ class UserType extends AbstractType
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly Security $security,
|
private readonly Security $security,
|
||||||
private readonly AuthorizationChecker $authorizationChecker
|
private readonly AuthorizationChecker $authorizationChecker,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class Address implements \Stringable
|
||||||
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
|
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
|
||||||
#[Assert\NotBlank]
|
#[Assert\NotBlank]
|
||||||
#[Assert\Length(max: 255)]
|
#[Assert\Length(max: 255)]
|
||||||
private string $address;
|
private ?string $address = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional information for the address, eg: APT 555.
|
* Additional information for the address, eg: APT 555.
|
||||||
|
|
@ -90,7 +90,7 @@ class Address implements \Stringable
|
||||||
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
|
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
|
||||||
#[Assert\NotBlank]
|
#[Assert\NotBlank]
|
||||||
#[Assert\Length(max: 255)]
|
#[Assert\Length(max: 255)]
|
||||||
private string $locality = '';
|
private ?string $locality = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Postal code, eg: "59160".
|
* Postal code, eg: "59160".
|
||||||
|
|
@ -98,7 +98,7 @@ class Address implements \Stringable
|
||||||
#[ORM\Column(type: Types::STRING, length: 10, nullable: false)]
|
#[ORM\Column(type: Types::STRING, length: 10, nullable: false)]
|
||||||
#[Assert\NotBlank]
|
#[Assert\NotBlank]
|
||||||
#[Assert\Length(max: 20)]
|
#[Assert\Length(max: 20)]
|
||||||
private string $postalCode;
|
private ?string $postalCode = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ISO code of the country, eg: "FR".
|
* ISO code of the country, eg: "FR".
|
||||||
|
|
@ -108,7 +108,7 @@ class Address implements \Stringable
|
||||||
#[ORM\Column(type: Types::STRING, length: 2, nullable: false)]
|
#[ORM\Column(type: Types::STRING, length: 2, nullable: false)]
|
||||||
#[Assert\NotBlank]
|
#[Assert\NotBlank]
|
||||||
#[Assert\Country]
|
#[Assert\Country]
|
||||||
private string $country;
|
private ?string $country = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Latitude of the address (north/south), eg: "50.6322562".
|
* Latitude of the address (north/south), eg: "50.6322562".
|
||||||
|
|
@ -148,7 +148,7 @@ class Address implements \Stringable
|
||||||
* OpenStreetMap identifier, usefull to create link to maps.
|
* OpenStreetMap identifier, usefull to create link to maps.
|
||||||
*/
|
*/
|
||||||
#[ORM\Column(type: Types::BIGINT, nullable: true)]
|
#[ORM\Column(type: Types::BIGINT, nullable: true)]
|
||||||
private int $osmId;
|
private string $osmId;
|
||||||
|
|
||||||
public function __toString(): string
|
public function __toString(): string
|
||||||
{
|
{
|
||||||
|
|
@ -167,12 +167,12 @@ class Address implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAddress(): string
|
public function getAddress(): ?string
|
||||||
{
|
{
|
||||||
return $this->address;
|
return $this->address;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setAddress(string $address): self
|
public function setAddress(?string $address): self
|
||||||
{
|
{
|
||||||
$this->address = $address;
|
$this->address = $address;
|
||||||
|
|
||||||
|
|
@ -227,7 +227,7 @@ class Address implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLocality(): string
|
public function getLocality(): ?string
|
||||||
{
|
{
|
||||||
return $this->locality;
|
return $this->locality;
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +237,7 @@ class Address implements \Stringable
|
||||||
return $this->locality !== '';
|
return $this->locality !== '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLocality(string $locality): self
|
public function setLocality(?string $locality): self
|
||||||
{
|
{
|
||||||
$this->locality = $locality;
|
$this->locality = $locality;
|
||||||
|
|
||||||
|
|
@ -256,24 +256,24 @@ class Address implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPostalCode(): string
|
public function getPostalCode(): ?string
|
||||||
{
|
{
|
||||||
return $this->postalCode;
|
return $this->postalCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setPostalCode(string $postalCode): self
|
public function setPostalCode(?string $postalCode): self
|
||||||
{
|
{
|
||||||
$this->postalCode = $postalCode;
|
$this->postalCode = $postalCode;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCountry(): string
|
public function getCountry(): ?string
|
||||||
{
|
{
|
||||||
return $this->country;
|
return $this->country;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCountry(string $country): self
|
public function setCountry(?string $country): self
|
||||||
{
|
{
|
||||||
$this->country = $country;
|
$this->country = $country;
|
||||||
|
|
||||||
|
|
@ -340,12 +340,12 @@ class Address implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOsmId(): int
|
public function getOsmId(): string
|
||||||
{
|
{
|
||||||
return $this->osmId;
|
return $this->osmId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setOsmId(int $osmId): Address
|
public function setOsmId(string $osmId): Address
|
||||||
{
|
{
|
||||||
$this->osmId = $osmId;
|
$this->osmId = $osmId;
|
||||||
|
|
||||||
|
|
@ -378,7 +378,7 @@ class Address implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSubAndLocality(): string
|
public function getSubAndLocality(): ?string
|
||||||
{
|
{
|
||||||
if (u($this->subLocality)->isEmpty()) {
|
if (u($this->subLocality)->isEmpty()) {
|
||||||
return $this->locality;
|
return $this->locality;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ use App\Doctrine\Behavior\TimestampableEntity;
|
||||||
use App\Enum\ConfigurationType;
|
use App\Enum\ConfigurationType;
|
||||||
use App\Message\Command\Admin\ParametersFormCommand;
|
use App\Message\Command\Admin\ParametersFormCommand;
|
||||||
use App\Repository\ConfigurationRepository;
|
use App\Repository\ConfigurationRepository;
|
||||||
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
|
|
@ -25,6 +27,13 @@ class Configuration
|
||||||
#[Assert\NotBlank]
|
#[Assert\NotBlank]
|
||||||
protected ConfigurationType $type = ConfigurationType::INSTANCE;
|
protected ConfigurationType $type = ConfigurationType::INSTANCE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Collection<int, PlatformOffer>
|
||||||
|
*/
|
||||||
|
#[ORM\OneToMany(mappedBy: 'configuration', targetEntity: PlatformOffer::class, orphanRemoval: true)]
|
||||||
|
#[ORM\OrderBy(['price' => 'ASC'])]
|
||||||
|
private Collection $offers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associative array to store parameters.
|
* Associative array to store parameters.
|
||||||
*
|
*
|
||||||
|
|
@ -33,6 +42,11 @@ class Configuration
|
||||||
#[ORM\Column(type: 'json')]
|
#[ORM\Column(type: 'json')]
|
||||||
private array $configuration = [];
|
private array $configuration = [];
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->offers = new ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
public function getId(): ?int
|
public function getId(): ?int
|
||||||
{
|
{
|
||||||
return $this->id;
|
return $this->id;
|
||||||
|
|
@ -76,21 +90,35 @@ class Configuration
|
||||||
/** end of basic getters and setters ------------------------------------------------ */
|
/** end of basic getters and setters ------------------------------------------------ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool[]
|
* @return array<string, bool|string>
|
||||||
*/
|
*/
|
||||||
public function getServices(): array
|
public function getGlobals(): array
|
||||||
{
|
{
|
||||||
/** @var array<string, bool> $services */
|
/** @var array<string, bool|string> $globals */
|
||||||
$services = $this->configuration['services'] ?? [];
|
$globals = $this->configuration['global'] ?? [];
|
||||||
|
|
||||||
return $services;
|
return $globals;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlatformName(): string
|
||||||
|
{
|
||||||
|
$globals = $this->getGlobals();
|
||||||
|
|
||||||
|
return (string) ($globals['globalName'] ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getServicesEnabled(): bool
|
public function getServicesEnabled(): bool
|
||||||
{
|
{
|
||||||
$services = $this->getServices();
|
$globals = $this->getGlobals();
|
||||||
|
|
||||||
return $services['servicesEnabled'];
|
return (bool) $globals['globalServicesEnabled'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPaidMembership(): bool
|
||||||
|
{
|
||||||
|
$globals = $this->getGlobals();
|
||||||
|
|
||||||
|
return (bool) $globals['globalPaidMembership'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,16 @@ namespace App\Entity;
|
||||||
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
|
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
|
||||||
use ApiPlatform\Metadata\ApiFilter;
|
use ApiPlatform\Metadata\ApiFilter;
|
||||||
use ApiPlatform\Metadata\ApiProperty;
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\Get;
|
||||||
|
use ApiPlatform\Metadata\GetCollection;
|
||||||
|
use ApiPlatform\Metadata\Patch;
|
||||||
use App\Doctrine\Behavior\TimestampableEntity;
|
use App\Doctrine\Behavior\TimestampableEntity;
|
||||||
use App\Enum\Group\GroupMembership;
|
use App\Enum\Group\GroupMembership;
|
||||||
use App\Enum\Group\GroupType;
|
use App\Enum\Group\GroupType;
|
||||||
use App\Repository\GroupRepository;
|
use App\Repository\GroupRepository;
|
||||||
|
use App\State\GroupsProvider;
|
||||||
|
use App\State\Processor\GroupChildServicesEnabledProcessor;
|
||||||
use App\Validator as AppAssert;
|
use App\Validator as AppAssert;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
|
|
@ -26,6 +32,17 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||||
#[ORM\Index(columns: ['type'])]
|
#[ORM\Index(columns: ['type'])]
|
||||||
#[ApiFilter(OrderFilter::class, properties: ['name'])]
|
#[ApiFilter(OrderFilter::class, properties: ['name'])]
|
||||||
#[AppAssert\Constraints\Group\GroupParentNotSelf]
|
#[AppAssert\Constraints\Group\GroupParentNotSelf]
|
||||||
|
#[ApiResource(
|
||||||
|
operations: [
|
||||||
|
new GetCollection(provider: GroupsProvider::class),
|
||||||
|
new Patch(
|
||||||
|
uriTemplate: '/groups/{id}/disable_child_services',
|
||||||
|
input: false,
|
||||||
|
processor: GroupChildServicesEnabledProcessor::class
|
||||||
|
),
|
||||||
|
new Get(),
|
||||||
|
]
|
||||||
|
)]
|
||||||
class Group implements \Stringable
|
class Group implements \Stringable
|
||||||
{
|
{
|
||||||
use TimestampableEntity;
|
use TimestampableEntity;
|
||||||
|
|
@ -123,6 +140,9 @@ class Group implements \Stringable
|
||||||
#[ORM\ManyToMany(targetEntity: Product::class, mappedBy: 'groups')]
|
#[ORM\ManyToMany(targetEntity: Product::class, mappedBy: 'groups')]
|
||||||
private Collection $products;
|
private Collection $products;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'boolean')]
|
||||||
|
private bool $servicesEnabled = false;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->children = new ArrayCollection();
|
$this->children = new ArrayCollection();
|
||||||
|
|
@ -340,6 +360,16 @@ class Group implements \Stringable
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getServicesEnabled(): bool
|
||||||
|
{
|
||||||
|
return $this->servicesEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setServicesEnabled(bool $servicesEnabled): void
|
||||||
|
{
|
||||||
|
$this->servicesEnabled = $servicesEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
// End of basic 'etters ----------------------------------------------------
|
// End of basic 'etters ----------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -384,4 +414,34 @@ class Group implements \Stringable
|
||||||
{
|
{
|
||||||
return !$this->getActiveOffers()->isEmpty();
|
return !$this->getActiveOffers()->isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<Group>
|
||||||
|
*/
|
||||||
|
public function getParentsRecursively(): array
|
||||||
|
{
|
||||||
|
$parents = [];
|
||||||
|
$parent = $this->getParent();
|
||||||
|
if (null !== $parent) {
|
||||||
|
$parents = $parent->getParentsRecursively();
|
||||||
|
$parents[] = $parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<Group>
|
||||||
|
*/
|
||||||
|
public function getChildrenRecursively(): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
$children = $this->getChildren();
|
||||||
|
foreach ($children as $child) {
|
||||||
|
$result = array_merge($result, $child->getChildrenRecursively());
|
||||||
|
$result[] = $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue