Merge pull request #2 from Tipimi-fr/chore/sync-source-rebased
Chore/sync source rebased
This commit is contained in:
commit
b1868fc1f8
267 changed files with 8398 additions and 3592 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
|
||||
# the other below are just example for two different third party vendors
|
||||
#SMS_DSN=twilio://SID:TOKEN@default?from=FROM
|
||||
#SMS_DSN=brevo://API_KEY@default?sender=SENDER
|
||||
SMS_DSN=null://null
|
||||
###< symfony/notifier ###
|
||||
|
||||
|
|
@ -106,3 +107,7 @@ STORAGE_USE_PATH_STYLE_ENDPOINT=true
|
|||
STORAGE_KEY=app
|
||||
STORAGE_SECRET=!ChangeMe!
|
||||
###< 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
|
||||
run: docker compose exec -T php ./vendor/bin/twigcs templates/ --exclude vendor
|
||||
- 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
|
||||
run: docker run --rm -w "/usr/app" -v "${PWD}":/usr/app gmolaire/yarn yarn lint
|
||||
|
|
|
|||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
|
|
@ -124,6 +124,7 @@ jobs:
|
|||
--set=ingress.tls[0].secretName=${{ needs.meta.outputs.release_name }}-tls \
|
||||
--set=ingress.tls[0].hosts[0]=${{ vars.DOMAIN }} \
|
||||
--set=postgresql.url="${{ secrets.database-url }}" \
|
||||
--set=postgresql.enabled='${{ github.event_name == 'pull_request' }}' \
|
||||
--set=payum.apikey="${{ secrets.payum-apikey }}" \
|
||||
--set=mailer.dsn="${{ secrets.mailer-dsn }}" \
|
||||
--set=sms.dsn="${{ secrets.sms-dsn }}" \
|
||||
|
|
|
|||
14
README.md
14
README.md
|
|
@ -23,7 +23,13 @@ Le code est disponible sous licence AGPL (Affero General Public License). Voir l
|
|||
## Interface d’administration
|
||||
La plateforme dispose d’une interface d’administration, utilisant EasyAdmin, accessible aux utilisateur·rice·s disposant du rôle “administrateur·rice”.
|
||||
|
||||
Les administrateur·rice·s peuvent être ajoutés ou supprimés via l’interface d’administration.
|
||||
Les administrateur·rice·s peuvent être ajoutés ou supprimés via l’interface d’administration.
|
||||
|
||||
|
||||
## Paiement de l'adhésion à la plateforme
|
||||
Il est possible d'activer ou désactiver cette fonctionnalité dans l'interface d’administration.
|
||||
Si le paiement d'une adhésion à la plateforme est activé, il faut renseigner les tarifs d'adhésion (unique, mensuel, annuel), et le paiement devra se faire par l’utilisateur·rice après la création de son compte afin d'accéder aux fonctionnalités proposées (création d'objets, demandes d'emprunts etc.)
|
||||
Si l’adhésion a une date de fin, lors de l’expiration de celle-ci, l’utilisateur·rice devra payer à nouveau son adhésion afin d'utiliser la plateforme.
|
||||
|
||||
## Utilisateur·rice·s
|
||||
Les utilisateur·rice·s peuvent s’inscrire librement sur la plateforme avec une adresse e-mail valide.
|
||||
|
|
@ -80,7 +86,7 @@ L’utilisateur·rice peut également renseigner des périodes d’indisponibili
|
|||
La plateforme propose un traitement similaire aux objets et aux services.
|
||||
|
||||
Les objets et services peuvent être consultés par les administrateur·rice·s dans l’interface d’administration.
|
||||
Les services peuvent être activés ou désactivés pour l'instance, via l'espace d'administration.
|
||||
Les services peuvent être activés ou désactivés pour l'instance, et/ou par groupe et sous-groupe via l'espace d'administration.
|
||||
|
||||
|
||||
### Catégories
|
||||
|
|
@ -159,6 +165,8 @@ La plateforme gère l’envoi de notifications automatiques par e-mail et SMS po
|
|||
| Nouveau gérant·e ou administrateur·rice de groupe | E-mail et SMS |
|
||||
| Invitation dans un groupe | E-mail et SMS |
|
||||
| Rappel expiration d’adhésion à 1 groupe J-7 J | E-mail et SMS |
|
||||
| Confirmation du paiement de l'adhésion à la platefome | E-mail |
|
||||
| Expiration d’adhésion à la platefome | E-mail |
|
||||
|
||||
|
||||
L’envoi d’email devra être configuré pour chaque instance avec un service tiers dédié (envoi serveur, Mailgun, Sendinblue, Mailchimp, …)
|
||||
|
|
@ -187,12 +195,14 @@ L’interface d’administration permet de configurer les liens du menu. Le pied
|
|||
## Fonctionnalités configurables
|
||||
Les options suivantes peuvent être configurées manuellement dans l’espace d’administration :
|
||||
- Activation des services
|
||||
- Activation du paiement de l'adhésion et configuration des tarifs
|
||||
- Gestion des administrateur·rice·s de l’instance
|
||||
- Expéditeur des notifications (e-mail, nom)
|
||||
- Activation du lien de contact dans le menu
|
||||
- Activation des groupes
|
||||
- Création de groupe : Ouverte à tous ou uniquement par les administrateur·rice·s de l'instance
|
||||
- Création de groupe payante ou gratuite (NB : le paiement se fait hors de la plateforme)
|
||||
- Accès à la fonctionnalité Services pour un groupe ou sous-groupe
|
||||
- Conversations d’emprunt visibles ou masquées dans l’espace d’administration
|
||||
|
||||
|
||||
|
|
|
|||
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",
|
||||
"autoimport": {
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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 { Application } from '@hotwired/stimulus'
|
||||
|
||||
import PasswordVisibility from 'stimulus-password-visibility'
|
||||
import Carousel from 'stimulus-carousel'
|
||||
|
|
@ -15,7 +14,5 @@ export const app = startStimulusApp(require.context(
|
|||
// register any custom, 3rd party controllers here
|
||||
// app.register('some_controller_name', SomeImportedController);
|
||||
|
||||
const application = Application.start()
|
||||
application.register('carousel', Carousel)
|
||||
application.register('password-visibility', PasswordVisibility)
|
||||
|
||||
app.register('carousel', Carousel)
|
||||
app.register('password-visibility', PasswordVisibility)
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
}
|
||||
|
||||
&.less {
|
||||
max-height: 500px;
|
||||
max-height: 1000px;
|
||||
transition: max-height .3s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,30 +109,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
p > strong {
|
||||
> a {
|
||||
.page-content {
|
||||
a {
|
||||
color: $blue-500;;
|
||||
position: relative;
|
||||
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",
|
||||
"license": "MIT",
|
||||
"description": "APES : Platforme d'échange de services et de biens",
|
||||
"minimum-stability": "stable",
|
||||
"minimum-stability": "beta",
|
||||
"prefer-stable": true,
|
||||
"repositories": {
|
||||
"coopTilleuls/payum-mollie": {
|
||||
|
|
@ -44,50 +44,53 @@
|
|||
"odolbeau/phone-number-bundle": "^3.9",
|
||||
"payum/offline": "^1.7",
|
||||
"payum/payum-bundle": "^2.5",
|
||||
"php-http/message": "^1.16",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpdocumentor/reflection-docblock": "^5.3",
|
||||
"phpstan/phpdoc-parser": "^1.11",
|
||||
"sensio/framework-extra-bundle": "^6.2",
|
||||
"snc/redis-bundle": "^4.3",
|
||||
"stof/doctrine-extensions-bundle": "^1.7",
|
||||
"symfony/asset": "6.2.*",
|
||||
"symfony/cache": "6.2.*",
|
||||
"symfony/clock": "6.2.*",
|
||||
"symfony/console": "6.2.*",
|
||||
"symfony/doctrine-messenger": "6.2.*",
|
||||
"symfony/dotenv": "6.2.*",
|
||||
"symfony/expression-language": "6.2.*",
|
||||
"symfony/fake-sms-notifier": "6.2.*",
|
||||
"symfony/asset": "6.4.*",
|
||||
"symfony/brevo-notifier": "6.4.*",
|
||||
"symfony/cache": "6.4.*",
|
||||
"symfony/clock": "6.4.*",
|
||||
"symfony/console": "6.4.*",
|
||||
"symfony/doctrine-messenger": "6.4.*",
|
||||
"symfony/dotenv": "6.4.*",
|
||||
"symfony/expression-language": "6.4.*",
|
||||
"symfony/fake-sms-notifier": "6.4.*",
|
||||
"symfony/flex": "^2",
|
||||
"symfony/form": "6.2.*",
|
||||
"symfony/framework-bundle": "6.2.*",
|
||||
"symfony/google-mailer": "6.2.*",
|
||||
"symfony/html-sanitizer": "6.2.*",
|
||||
"symfony/http-client": "6.2.*",
|
||||
"symfony/mailer": "6.2.*",
|
||||
"symfony/form": "6.4.*",
|
||||
"symfony/framework-bundle": "6.4.*",
|
||||
"symfony/google-mailer": "6.4.*",
|
||||
"symfony/html-sanitizer": "6.4.*",
|
||||
"symfony/http-client": "6.4.*",
|
||||
"symfony/mailer": "6.4.*",
|
||||
"symfony/mercure-bundle": "^0.3.5",
|
||||
"symfony/messenger": "6.2.*",
|
||||
"symfony/mime": "6.2.*",
|
||||
"symfony/messenger": "6.4.*",
|
||||
"symfony/mime": "6.4.*",
|
||||
"symfony/monolog-bundle": "^3.8",
|
||||
"symfony/notifier": "6.2.*",
|
||||
"symfony/ovh-cloud-notifier": "6.2.*",
|
||||
"symfony/property-access": "6.2.*",
|
||||
"symfony/property-info": "6.2.*",
|
||||
"symfony/proxy-manager-bridge": "6.2.*",
|
||||
"symfony/rate-limiter": "6.2.*",
|
||||
"symfony/notifier": "6.4.*",
|
||||
"symfony/ovh-cloud-notifier": "6.4.*",
|
||||
"symfony/property-access": "6.4.*",
|
||||
"symfony/property-info": "6.4.*",
|
||||
"symfony/proxy-manager-bridge": "6.4.*",
|
||||
"symfony/rate-limiter": "6.4.*",
|
||||
"symfony/requirements-checker": "^2.0",
|
||||
"symfony/runtime": "6.2.*",
|
||||
"symfony/security-bundle": "6.2.*",
|
||||
"symfony/serializer": "6.2.*",
|
||||
"symfony/runtime": "6.4.*",
|
||||
"symfony/security-bundle": "6.4.*",
|
||||
"symfony/serializer": "6.4.*",
|
||||
"symfony/stimulus-bundle": "^2.14",
|
||||
"symfony/translation-contracts": "^3.2",
|
||||
"symfony/twig-bridge": "6.2.*",
|
||||
"symfony/twig-bundle": "6.2.*",
|
||||
"symfony/twilio-notifier": "6.2.*",
|
||||
"symfony/uid": "6.2.*",
|
||||
"symfony/twig-bridge": "6.4.*",
|
||||
"symfony/twig-bundle": "6.4.*",
|
||||
"symfony/twilio-notifier": "6.4.*",
|
||||
"symfony/uid": "6.4.*",
|
||||
"symfony/ux-autocomplete": "^2.7",
|
||||
"symfony/validator": "6.2.*",
|
||||
"symfony/validator": "6.4.*",
|
||||
"symfony/webpack-encore-bundle": "^1.16",
|
||||
"symfony/workflow": "6.2.*",
|
||||
"symfony/yaml": "6.2.*",
|
||||
"symfony/workflow": "6.4.*",
|
||||
"symfony/yaml": "6.4.*",
|
||||
"twig/cssinliner-extra": "^3.4",
|
||||
"twig/extra-bundle": "^3.4",
|
||||
"twig/inky-extra": "^3.4",
|
||||
|
|
@ -151,7 +154,7 @@
|
|||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"require": "6.2.*",
|
||||
"require": "6.4.*",
|
||||
"docker": true
|
||||
}
|
||||
},
|
||||
|
|
@ -168,13 +171,13 @@
|
|||
"phpunit/phpunit": "^9.5",
|
||||
"rector/rector": "^0.14.5",
|
||||
"staabm/annotate-pull-request-from-checkstyle": "^1.8",
|
||||
"symfony/browser-kit": "6.2.*",
|
||||
"symfony/css-selector": "6.2.*",
|
||||
"symfony/debug-bundle": "6.2.*",
|
||||
"symfony/browser-kit": "6.4.*",
|
||||
"symfony/css-selector": "6.4.*",
|
||||
"symfony/debug-bundle": "6.4.*",
|
||||
"symfony/maker-bundle": "^1.47",
|
||||
"symfony/panther": "^2.0",
|
||||
"symfony/phpunit-bridge": "^6.1",
|
||||
"symfony/web-profiler-bundle": "6.2.*",
|
||||
"symfony/web-profiler-bundle": "6.4.*",
|
||||
"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
|
||||
|
||||
## 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],
|
||||
League\FlysystemBundle\FlysystemBundle::class => ['all' => true],
|
||||
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
|
||||
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
|
||||
Symfony\UX\Autocomplete\AutocompleteBundle::class => ['all' => true],
|
||||
Payum\Bundle\PayumBundle\PayumBundle::class => ['all' => true],
|
||||
FOS\CKEditorBundle\FOSCKEditorBundle::class => ['all' => true],
|
||||
Misd\PhoneNumberBundle\MisdPhoneNumberBundle::class => ['all' => true],
|
||||
Symfony\UX\StimulusBundle\StimulusBundle::class => ['all' => true],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -6,5 +6,7 @@ twig:
|
|||
|
||||
fos_ck_editor:
|
||||
configs:
|
||||
default:
|
||||
versionCheck: false
|
||||
main_config:
|
||||
toolbar: full
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ security:
|
|||
access_control:
|
||||
- { path: ^/admin, roles: [ROLE_ADMIN, ROLE_GROUP_ADMIN] }
|
||||
# to synchronize with MyAccountAction
|
||||
- { path: ^/en/my-account/, roles: ROLE_USER }
|
||||
- { path: ^/fr/mon-compte/, roles: ROLE_USER }
|
||||
- { path: ^/en/my-account, roles: MEMBERSHIP_PAID }
|
||||
- { path: ^/fr/mon-compte, roles: MEMBERSHIP_PAID }
|
||||
|
||||
role_hierarchy:
|
||||
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
|
||||
services:
|
||||
# https://localhost/_profiler/phpinfo
|
||||
|
|
@ -30,8 +28,13 @@ services:
|
|||
maildev:
|
||||
image: maildev/maildev
|
||||
ports:
|
||||
- 1080:1080
|
||||
- 1025:1025
|
||||
- "1080:1080"
|
||||
- "1025:1025"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -O - http://0.0.0.0:1080/healthz || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
storage:
|
||||
image: minio/minio
|
||||
|
|
|
|||
|
|
@ -6,18 +6,8 @@ services:
|
|||
ports:
|
||||
- "1081:1080"
|
||||
- "1026:1025"
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-qO-", "http://localhost:1080"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
ports:
|
||||
- "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
|
||||
services:
|
||||
php:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
version: "3.4"
|
||||
|
||||
services:
|
||||
php:
|
||||
build:
|
||||
|
|
@ -89,7 +87,7 @@ services:
|
|||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- '6389:6379'
|
||||
- "6389:6379"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli -h 127.0.0.1 ping | grep 'PONG' || exit 1"]
|
||||
interval: 10s
|
||||
|
|
|
|||
|
|
@ -21,16 +21,21 @@ Access `https://localhost` in your browser and accept the security risk.
|
|||
|
||||
You should have access now to:
|
||||
|
||||
* Main project : https://localhost
|
||||
* Meilisearch : http://localhost:7700/
|
||||
* The main frontend: https://localhost
|
||||
* 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
|
||||
|
||||
You should have access now to:
|
||||
|
||||
* Adminer : http://localhost:8989/?pgsql=database&username=app&db=app&ns=public&select=group
|
||||
* Mailcatcher : http://localhost:1081/
|
||||
* Maildev : http://localhost:1080
|
||||
|
||||
## Makefile
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ This page documents the settings that can be configured as environment varibles
|
|||
for the production environment.
|
||||
Defaults value are shown in the [.env](../.env) file.
|
||||
|
||||
|
||||
## Application
|
||||
|
||||
| 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.
|
||||
|
||||
Yo use Gmail with a secret key use:
|
||||
To use Gmail with a secret key use:
|
||||
|
||||
MAILER_DSN=gmail://email@example.com:secretkey@default
|
||||
|
||||
|
|
@ -58,14 +57,19 @@ Twilio, Sendgrid, Mailingblue...
|
|||
|
||||
SMS_DSN=null://null
|
||||
|
||||
This is the main parameter to send DNS. If you leave `null://null`, nothing will
|
||||
be send without errors. It can be useful when having issues with your SMS provider
|
||||
and wanting to disable it temporarly.
|
||||
This is the main parameter to send DNS. If you leave `null://null`, no SMS will
|
||||
be sent.
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ App\Entity\Configuration:
|
|||
|
||||
features (extends configuration_template):
|
||||
configuration:
|
||||
services:
|
||||
servicesEnabled: true
|
||||
global:
|
||||
globalServicesEnabled: true
|
||||
globalPaidMembership: false
|
||||
notificationsSender:
|
||||
notificationsSenderEmail: info@example.com
|
||||
notificationsSenderName: Contact
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ App\Entity\Configuration:
|
|||
|
||||
features (extends configuration_template):
|
||||
configuration:
|
||||
services:
|
||||
servicesEnabled: true
|
||||
global:
|
||||
globalName: Echanges de biens et de services
|
||||
globalServicesEnabled: true
|
||||
globalPaidMembership: false
|
||||
notificationsSender:
|
||||
notificationsSenderEmail: info@example.com
|
||||
notificationsSenderName: Contact
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@ App\Entity\GroupOffer:
|
|||
# Templates
|
||||
group_offer_group_1_template (template, extends group_offer_template):
|
||||
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: '@group_7'
|
||||
type: !php/enum App\Enum\Group\GroupOfferType::YEARLY
|
||||
type: !php/enum App\Enum\OfferType::YEARLY
|
||||
|
||||
# Group 1
|
||||
group_offer_group_1_10 (extends group_offer_group_1_template):
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ App\Entity\Page:
|
|||
<p> </p>
|
||||
<hr />
|
||||
<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>
|
||||
<hr />
|
||||
<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):
|
||||
type: !php/enum App\Enum\Product\ProductType::SERVICE
|
||||
visibility: !php/enum App\Enum\Product\ProductVisibility::RESTRICTED
|
||||
|
||||
# Loic —————————————————————————————————————————————————————————————————————
|
||||
loic_object_1 (extends object):
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ App\Entity\User:
|
|||
devAccount: true
|
||||
address: '@address_loic'
|
||||
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):
|
||||
id: <uuid('1ed69804-eeb9-6c32-990b-632c3a6846ba')>
|
||||
|
|
@ -42,6 +46,12 @@ App\Entity\User:
|
|||
firstname: 'Kevin'
|
||||
lastname: 'Pirouet'
|
||||
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):
|
||||
id: <uuid('1ed69804-eeb9-6e6c-bce0-632c3a6846ba')>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ metadata:
|
|||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
schedule: '0 0 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -23,19 +24,20 @@ spec:
|
|||
serviceAccountName: {{ include "plateforme-ebs.serviceAccountName" . }}
|
||||
restartPolicy: Never
|
||||
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 }}"
|
||||
imagePullPolicy: {{ .Values.php.image.pullPolicy }}
|
||||
command: ['/bin/sh', '-c']
|
||||
args: ['
|
||||
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;
|
||||
']
|
||||
args: ['
|
||||
set -ex;
|
||||
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:
|
||||
- name: API_ENTRYPOINT_HOST
|
||||
valueFrom:
|
||||
|
|
@ -198,4 +200,4 @@ spec:
|
|||
periodSeconds: 3
|
||||
resources:
|
||||
{{- toYaml .Values.resources.fixtures | nindent 16 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ metadata:
|
|||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
schedule: '6 1 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -78,6 +79,23 @@ spec:
|
|||
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:
|
||||
|
|
@ -93,6 +111,11 @@ spec:
|
|||
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:
|
||||
|
|
|
|||
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 }}
|
||||
spec:
|
||||
schedule: '12 2 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -78,6 +79,23 @@ spec:
|
|||
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:
|
||||
|
|
@ -93,6 +111,11 @@ spec:
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ metadata:
|
|||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
schedule: '3 21 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -78,6 +79,23 @@ spec:
|
|||
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:
|
||||
|
|
@ -93,6 +111,11 @@ spec:
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -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 }}
|
||||
spec:
|
||||
schedule: '44 4 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -78,6 +79,23 @@ spec:
|
|||
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:
|
||||
|
|
@ -93,6 +111,11 @@ spec:
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ metadata:
|
|||
{{- include "plateforme-ebs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
schedule: '2 4 * * *'
|
||||
timeZone: "Europe/Paris"
|
||||
jobTemplate:
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
@ -78,6 +79,23 @@ spec:
|
|||
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:
|
||||
|
|
@ -93,6 +111,11 @@ spec:
|
|||
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:
|
||||
|
|
|
|||
|
|
@ -33,18 +33,14 @@ spec:
|
|||
command: ['/bin/sh', '-c']
|
||||
args: ['
|
||||
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:
|
||||
- name: API_ENTRYPOINT_HOST
|
||||
valueFrom:
|
||||
|
|
|
|||
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"
|
||||
|
||||
postgresql:
|
||||
enabled: false
|
||||
url: change_me
|
||||
|
||||
php:
|
||||
fixtureJob:
|
||||
enabled: true
|
||||
fixtureCron:
|
||||
enabled: true
|
||||
|
|
|
|||
|
|
@ -33,10 +33,13 @@ php:
|
|||
usePathStyleEndpoint: true
|
||||
publicKey: ""
|
||||
secret: ""
|
||||
fixtureJob:
|
||||
enabled: false
|
||||
fixtureCron:
|
||||
enabled: false
|
||||
fixtureJob:
|
||||
enabled: false
|
||||
|
||||
dailyCronjobs:
|
||||
enabled: true
|
||||
|
||||
maildev:
|
||||
enabled: false
|
||||
|
|
@ -48,9 +51,6 @@ mailer:
|
|||
sms:
|
||||
dsn: "null://null"
|
||||
|
||||
dailyCronjobs:
|
||||
enabled: true
|
||||
|
||||
consumer:
|
||||
# We don't use async for now so consumer isn't needed
|
||||
enabled: false
|
||||
|
|
@ -80,6 +80,7 @@ postgresql:
|
|||
username: "example"
|
||||
password: "!ChangeMe!"
|
||||
database: "api"
|
||||
postgresPassword: "!ChangeMe!"
|
||||
# Persistent Volume Storage configuration.
|
||||
# ref: https://kubernetes.io/docs/user-guide/persistent-volumes
|
||||
pullPolicy: IfNotPresent
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ final class EndMembershipCommand extends Command
|
|||
/** @var UserGroup $userGroup */
|
||||
$user = $userGroup->getUser();
|
||||
$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(),
|
||||
$user->getDisplayName(),
|
||||
$userGroup->getMembership()->value,
|
||||
|
|
@ -88,7 +88,7 @@ final class EndMembershipCommand extends Command
|
|||
++$count;
|
||||
}
|
||||
|
||||
$io->note(sprintf(' > %d deletion(s) done.', $count));
|
||||
$io->note(\sprintf(' > %d deletion(s) done.', $count));
|
||||
$this->memoryReport($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 = 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);
|
||||
$io->section('Sending notificaitons...');
|
||||
$count = 0;
|
||||
|
|
@ -72,7 +72,7 @@ final class NotifyMembershipExpirationCommand extends Command
|
|||
/** @var UserGroup $userGroup */
|
||||
$user = $userGroup->getUser();
|
||||
$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(),
|
||||
$user->getDisplayName(),
|
||||
$userGroup->getEndAt()?->format('Y-m-d'),
|
||||
|
|
@ -88,7 +88,7 @@ final class NotifyMembershipExpirationCommand extends Command
|
|||
++$count;
|
||||
}
|
||||
|
||||
$io->note(sprintf(' > %d notification(s) sent.', $count));
|
||||
$io->note(\sprintf(' > %d notification(s) sent.', $count));
|
||||
|
||||
$this->memoryReport($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->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->getStatus()->value,
|
||||
$referenceDate->format('Y-m-d')
|
||||
|
|
@ -106,7 +106,7 @@ final class NotifyServiceRequestDatesCommand extends Command
|
|||
++$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->done($io);
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ abstract class AbstractCategoryCrudController extends AbstractCrudController imp
|
|||
$idField,
|
||||
$createdAt,
|
||||
$updatedAt,
|
||||
];
|
||||
];
|
||||
}
|
||||
|
||||
public function moveUp(AdminContext $context): Response
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Controller\i18nTrait;
|
||||
use App\EasyAdmin\Field\FieldTrait;
|
||||
use App\EasyAdmin\Filter\EnumFilter;
|
||||
use App\EasyAdmin\Filter\UuidFilter;
|
||||
|
|
@ -15,8 +16,10 @@ use App\Enum\Product\ProductType;
|
|||
use App\Enum\Product\ProductVisibility;
|
||||
use App\Flysystem\EasyAdminHelper;
|
||||
use App\Flysystem\MediaManager;
|
||||
use App\Form\Type\Product\AbstractProductFormType;
|
||||
use App\Helper\CsvExporter;
|
||||
use App\Repository\CategoryRepository;
|
||||
use App\Repository\GroupRepository;
|
||||
use App\Repository\ProductRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||
|
|
@ -54,6 +57,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
|
|||
abstract class AbstractProductCrudController extends AbstractCrudController implements AdminSecuredCrudControllerInterface
|
||||
{
|
||||
use FieldTrait;
|
||||
use i18nTrait;
|
||||
|
||||
abstract public function getProductType(): ProductType;
|
||||
|
||||
|
|
@ -74,6 +78,7 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
|||
private readonly TranslatorInterface $translator,
|
||||
private readonly FilterFactory $filterFactory,
|
||||
private readonly SluggerInterface $slugger,
|
||||
protected readonly GroupRepository $groupRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +93,9 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
|||
'@EasyAdmin/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)
|
||||
->setFormTypeOption('class', ProductVisibility::class)
|
||||
->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')
|
||||
->setQueryBuilder(function (QueryBuilder $queryBuilder) {
|
||||
return $this->categoryRepository->addTypeFilter($queryBuilder, $this->getProductType());
|
||||
|
|
@ -300,6 +314,7 @@ abstract class AbstractProductCrudController extends AbstractCrudController impl
|
|||
'statusField',
|
||||
'visibilityField',
|
||||
'groupsField',
|
||||
'groupsFieldList',
|
||||
'ownerField',
|
||||
'categoryField',
|
||||
'nameField',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use App\Flysystem\MediaManager;
|
|||
use App\Helper\CsvExporter;
|
||||
use App\Mailer\AppMailer;
|
||||
use App\Mailer\Email\Admin\PromoteToAdmin\PromoteToAdminEmail;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
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\BooleanField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\EmailField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IdField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextareaField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Filter\DateTimeFilter;
|
||||
|
|
@ -89,6 +92,7 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
|||
#[Autowire('%user_base_path%')]
|
||||
private readonly string $userBasePath,
|
||||
AppMailer $mailer,
|
||||
private readonly ConfigurationRepository $configurationRepository,
|
||||
) {
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
|
@ -355,6 +359,20 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
|||
$vacationModeField = BooleanField::new('vacationMode');
|
||||
$addressField = AssociationField::new('address');
|
||||
$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(
|
||||
'idField',
|
||||
|
|
@ -378,6 +396,12 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
|||
'vacationModeField',
|
||||
'addressField',
|
||||
'groupsCountField',
|
||||
'membershipPaidField',
|
||||
'startAt',
|
||||
'endAt',
|
||||
'expiresInField',
|
||||
'payedAt',
|
||||
'offerField',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -445,4 +469,9 @@ abstract class AbstractUserCrudController extends AbstractCrudController impleme
|
|||
|
||||
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\Controller\AbstractDashboardController;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
public const DOMAIN = 'admin';
|
||||
|
|
@ -60,7 +63,7 @@ final class DashboardController extends AbstractDashboardController
|
|||
private readonly AdminUrlGenerator $adminUrlGenerator,
|
||||
private readonly AuthorizationChecker $authorizationChecker,
|
||||
private readonly UserRepository $userRepository,
|
||||
private readonly ServiceRequestRepository $requestRepository
|
||||
private readonly ServiceRequestRepository $requestRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -159,8 +162,8 @@ final class DashboardController extends AbstractDashboardController
|
|||
yield MenuItem::linkToCrud('menu.pages', 'fas fa-hat-wizard', Page::class)->setPermission(User::ROLE_ADMIN);
|
||||
|
||||
yield MenuItem::subMenu('menu.categories', 'fa-solid fa-folder')->setSubItems([
|
||||
MenuItem::linkToUrl('menu.objects', 'fa-solid fa-box', $categoryObjectUrl)->setPermission(User::ROLE_ADMIN),
|
||||
MenuItem::linkToUrl('menu.services', 'fa-regular fa-handshake', $categoryServiceUrl)->setPermission(User::ROLE_ADMIN),
|
||||
MenuItem::linkToUrl('menu.objects', 'fa-solid fa-box', $categoryObjectUrl)->setPermission(User::ROLE_ADMIN),
|
||||
MenuItem::linkToUrl('menu.services', 'fa-regular fa-handshake', $categoryServiceUrl)->setPermission(User::ROLE_ADMIN),
|
||||
])->setPermission(User::ROLE_ADMIN);
|
||||
|
||||
// —————————————————————————————————————————————————————————————————————
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use App\Form\Type\Security\GroupInvitationFormType;
|
|||
use App\Helper\CsvExporter;
|
||||
use App\Message\Command\Group\CreateGroupInvitationMessage;
|
||||
use App\MessageBus\CommandBus;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use App\Security\Checker\AuthorizationChecker;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||
|
|
@ -66,6 +67,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
private readonly TranslatorInterface $translator,
|
||||
private readonly FilterFactory $filterFactory,
|
||||
private readonly SluggerInterface $slugger,
|
||||
private readonly ConfigurationRepository $configurationRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +77,7 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
->setEntityLabelInPlural('groups')
|
||||
->setSearchFields(['name', 'description'])
|
||||
->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 */
|
||||
$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());
|
||||
|
||||
return $qb;
|
||||
|
|
@ -230,8 +233,24 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
->setFormTypeOption('class', GroupMembership::class)
|
||||
->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')
|
||||
->setRequired(false);
|
||||
->setRequired(false)
|
||||
->addWebpackEncoreEntries('admin')
|
||||
->setFormTypeOption('attr', [
|
||||
'data-controller' => 'admin-parentgroup',
|
||||
'data-admin-parentgroup-target' => 'parentField',
|
||||
])
|
||||
;
|
||||
$childrenField = AssociationField::new('children');
|
||||
$usersField = AssociationField::new('userGroups')
|
||||
->setTemplatePath('admin/group/user_groups_field.html.twig');
|
||||
|
|
@ -247,14 +266,20 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
$panels = $this->getPanels();
|
||||
|
||||
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) {
|
||||
$typeField->setChoices(GroupType::cases());
|
||||
$membershipField->setChoices(GroupMembership::cases());
|
||||
|
||||
return [
|
||||
$fields = [
|
||||
$nameField,
|
||||
$typeField,
|
||||
$membershipField,
|
||||
|
|
@ -264,11 +289,29 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
$invitationByAdminField,
|
||||
$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
|
||||
|
||||
return [
|
||||
$fields = [
|
||||
$panels['information'],
|
||||
$nameField,
|
||||
$parentField,
|
||||
|
|
@ -283,6 +326,12 @@ final class GroupCrudController extends AbstractCrudController implements GroupA
|
|||
$updatedAt,
|
||||
$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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ use App\EasyAdmin\Field\FieldTrait;
|
|||
use App\EasyAdmin\Filter\EnumFilter;
|
||||
use App\EasyAdmin\Filter\UserGroup\MyGroupFilter;
|
||||
use App\EasyAdmin\Filter\UuidFilter;
|
||||
use App\EasyAdmin\Form\Type\GroupOfferTypeType;
|
||||
use App\EasyAdmin\Form\Type\OfferTypeType;
|
||||
use App\Entity\GroupOffer;
|
||||
use App\Entity\User;
|
||||
use App\Enum\Group\GroupMembership;
|
||||
use App\Enum\Group\GroupOfferType;
|
||||
use App\Enum\Group\UserMembership;
|
||||
use App\Enum\OfferType;
|
||||
use App\Security\Checker\AuthorizationChecker;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
|
||||
|
|
@ -65,7 +65,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
|||
return $filters
|
||||
->add(UuidFilter::new('id'))
|
||||
->add(MyGroupFilter::new('group'))
|
||||
->add(EnumFilter::new('membership', GroupOfferTypeType::class))
|
||||
->add(EnumFilter::new('membership', OfferTypeType::class))
|
||||
->add('name')
|
||||
->add('active')
|
||||
;
|
||||
|
|
@ -98,7 +98,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
|||
|
||||
/** @var User $user */
|
||||
$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());
|
||||
|
||||
return $qb;
|
||||
|
|
@ -129,8 +129,8 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
|||
$nameField = TextField::new('name');
|
||||
$typeField = ChoiceField::new('type')
|
||||
->setFormType(EnumType::class)
|
||||
->setFormTypeOption('class', GroupOfferType::class)
|
||||
->setChoices(GroupOfferType::getAsArray());
|
||||
->setFormTypeOption('class', OfferType::class)
|
||||
->setChoices(OfferType::getAsArray());
|
||||
|
||||
$priceField = MoneyField::new('price')
|
||||
->setCurrencyPropertyPath('currency')
|
||||
|
|
@ -149,7 +149,7 @@ final class GroupOfferCrudController extends AbstractCrudController implements G
|
|||
}
|
||||
|
||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||
$typeField->setChoices(GroupOfferType::cases());
|
||||
$typeField->setChoices(OfferType::cases());
|
||||
|
||||
return [
|
||||
$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;
|
||||
|
||||
use App\Entity\Product;
|
||||
use App\Enum\Group\UserMembership;
|
||||
use App\Enum\Product\ProductStatus;
|
||||
use App\Enum\Product\ProductType;
|
||||
use App\Enum\Product\ProductVisibility;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField;
|
||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ImageField;
|
||||
|
||||
|
|
@ -36,6 +39,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
|||
{
|
||||
$product = parent::createEntity($entityFqcn);
|
||||
$product->setCurrency(null); // remove the default value which is not needed here
|
||||
$product->setVisibility(ProductVisibility::RESTRICTED);
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
|
@ -49,6 +53,8 @@ final class ServiceCrudController extends AbstractProductCrudController
|
|||
'typeField' => $typeField,
|
||||
'statusField' => $statusField,
|
||||
'visibilityField' => $visibilityField,
|
||||
'groupsField' => $groupsField,
|
||||
'groupsFieldList' => $groupsFieldList,
|
||||
'ownerField' => $ownerField,
|
||||
'categoryField' => $categoryField,
|
||||
'nameField' => $nameField,
|
||||
|
|
@ -61,7 +67,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
|||
|
||||
// list
|
||||
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 */
|
||||
|
|
@ -71,10 +77,35 @@ final class ServiceCrudController extends AbstractProductCrudController
|
|||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||
/** @var ChoiceField $statusField */
|
||||
$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
|
||||
|
|
@ -85,6 +116,7 @@ final class ServiceCrudController extends AbstractProductCrudController
|
|||
$categoryField,
|
||||
$statusField,
|
||||
$visibilityField,
|
||||
$groupsFieldList,
|
||||
$nameField,
|
||||
$descriptionField,
|
||||
$durationField,
|
||||
|
|
|
|||
|
|
@ -48,16 +48,27 @@ final class UserCrudController extends AbstractUserCrudController
|
|||
'vacationModeField' => $vacationModeField,
|
||||
'addressField' => $addressField,
|
||||
'groupsCountField' => $groupsCountField,
|
||||
'membershipPaidField' => $membershipPaidField,
|
||||
'startAt' => $startAt,
|
||||
'endAt' => $endAt,
|
||||
'expiresInField' => $expiresInField,
|
||||
'payedAt' => $payedAt,
|
||||
'offerField' => $offerField,
|
||||
] = $this->getFields($pageName);
|
||||
|
||||
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();
|
||||
|
||||
if ($pageName === Crud::PAGE_NEW || $pageName === Crud::PAGE_EDIT) {
|
||||
return [
|
||||
$editFields = [
|
||||
$panels['information'],
|
||||
$emailField,
|
||||
$firstNameField,
|
||||
|
|
@ -74,9 +85,21 @@ final class UserCrudController extends AbstractUserCrudController
|
|||
$enabledField,
|
||||
$emailConfirmedField,
|
||||
];
|
||||
if ($this->platformRequiresGlobalPayment()) {
|
||||
$editFields = array_merge($editFields, [
|
||||
$panels['payment_information'],
|
||||
$membershipPaidField,
|
||||
$offerField,
|
||||
$startAt,
|
||||
$endAt,
|
||||
$payedAt,
|
||||
]);
|
||||
}
|
||||
|
||||
return $editFields;
|
||||
}
|
||||
|
||||
return [
|
||||
$showFields = [
|
||||
$panels['information'],
|
||||
$emailField,
|
||||
$firstNameField,
|
||||
|
|
@ -97,5 +120,17 @@ final class UserCrudController extends AbstractUserCrudController
|
|||
$updatedAt,
|
||||
$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 */
|
||||
$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());
|
||||
|
||||
return $qb;
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ final class CreateGroupAction extends AbstractController
|
|||
use FlashTrait;
|
||||
use GroupTrait;
|
||||
|
||||
public const MAX_ELEMENT_BY_PAGE = 20;
|
||||
|
||||
public function __construct(
|
||||
private readonly QueryBus $queryBus,
|
||||
private readonly GroupRepository $groupRepository,
|
||||
|
|
@ -52,7 +50,7 @@ final class CreateGroupAction extends AbstractController
|
|||
) {
|
||||
}
|
||||
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route([
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/groups/create-my-group',
|
||||
'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
|
||||
{
|
||||
// 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();
|
||||
if (!$configuration->isGroupsCreationForAll()) {
|
||||
throw $this->createAccessDeniedException('Cannot create group with current settings.');
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ trait PaginationTrait
|
|||
*
|
||||
* @implements PaginationInterface<int,Product>
|
||||
*
|
||||
* @return PaginationInterface<int,Product>
|
||||
* @return PaginationInterface<int,mixed>
|
||||
*/
|
||||
private function paginate(SearchResult $searchResult): PaginationInterface
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Payment;
|
||||
namespace App\Controller\Payment\Group;
|
||||
|
||||
use App\Controller\FlashTrait;
|
||||
use App\Controller\i18nTrait;
|
||||
|
|
@ -18,7 +18,7 @@ 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\Attribute\Route;
|
||||
use Symfony\Component\Routing\Requirement\Requirement;
|
||||
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||
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', [
|
||||
'%group%' => $groupOffer->getGroup()->getName()],
|
||||
));
|
||||
$request->getSession()->remove('payment_in_progress');
|
||||
} else {
|
||||
$this->addFlashWarning($this->translator->trans($this->getI18nPrefix().'.status.'.$status->getValue()));
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Payment;
|
||||
namespace App\Controller\Payment\Group;
|
||||
|
||||
use App\Entity\GroupOffer;
|
||||
use App\Repository\GroupOfferRepository;
|
||||
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Payment;
|
||||
namespace App\Controller\Payment\Group;
|
||||
|
||||
use App\Controller\User\MyAccountAction;
|
||||
use App\Entity\User;
|
||||
use App\Payment\PayumManager;
|
||||
use App\Repository\GroupOfferRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
// use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
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\QueryBus;
|
||||
use App\MessageHandler\Command\Security\AccountCreateStep1CommandHandler;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
|
@ -41,6 +42,7 @@ final class AccountCreateController extends AbstractController
|
|||
private readonly QueryBus $queryBus,
|
||||
private readonly CommandBus $commandBus,
|
||||
private readonly Security $security,
|
||||
private readonly ConfigurationRepository $configurationRepository,
|
||||
) {
|
||||
$this->i18nPrefix = $this->getI18nPrefix();
|
||||
}
|
||||
|
|
@ -97,6 +99,7 @@ final class AccountCreateController extends AbstractController
|
|||
return $this->redirectToRoute('app_login');
|
||||
}
|
||||
|
||||
$configuration = $this->configurationRepository->getInstanceConfigurationOrCreate();
|
||||
// nominal case: user found and token not expired
|
||||
$form = $this->createForm(AccountCreateStep2FormType::class, $user->setStep2Defaults())->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
|
@ -110,13 +113,26 @@ final class AccountCreateController extends AbstractController
|
|||
// page group.
|
||||
$group = $user->getMyGroupsAsInvited()->first();
|
||||
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());
|
||||
}
|
||||
|
||||
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
|
||||
$this->addFlashSuccess($this->i18nPrefix.'.step2.flash.success');
|
||||
$this->addFlashSuccess($successMessage);
|
||||
|
||||
return $this->redirectToRoute(MyAccountAction::ROUTE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ final class AddressController extends AbstractController
|
|||
/**
|
||||
* @see UserAddressQueryHandler
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route(path: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-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'));
|
||||
}
|
||||
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route(path: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/my-address/step-2',
|
||||
'fr' => MyAccountAction::BASE_URL_FR.'/mon-adresse/etape-2',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||
/**
|
||||
* @see UserGroupControllerTest
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
final class UserGroupController extends AbstractController
|
||||
{
|
||||
use SecurityTrait;
|
||||
|
|
@ -39,7 +39,7 @@ final class UserGroupController extends AbstractController
|
|||
|
||||
public function __construct(
|
||||
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: [
|
||||
'en' => self::BASE_URL_EN,
|
||||
'fr' => self::BASE_URL_FR,
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ final class DeleteProductUnavailabilityAction extends AbstractController
|
|||
}
|
||||
|
||||
#[Route(path: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/products/unavailability/{id}/delete',
|
||||
'fr' => MyAccountAction::BASE_URL_FR.'/produits/indisponibilite/{id}/supprimer',
|
||||
],
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/products/unavailability/{id}/delete',
|
||||
'fr' => MyAccountAction::BASE_URL_FR.'/produits/indisponibilite/{id}/supprimer',
|
||||
],
|
||||
name: 'app_user_product_delete_availability',
|
||||
requirements: [
|
||||
'id' => Requirement::UUID_V6,
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ final class ProductAvailabilityController extends AbstractController
|
|||
public const ROUTE = 'app_user_product_availabilities';
|
||||
|
||||
public function __construct(
|
||||
public readonly ProductRepository $productRepository,
|
||||
private readonly QueryBus $queryBus,
|
||||
private readonly CommandBus $commandBus,
|
||||
public readonly ProductRepository $productRepository,
|
||||
private readonly QueryBus $queryBus,
|
||||
private readonly CommandBus $commandBus,
|
||||
) {
|
||||
}
|
||||
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route(path: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/my-products/{id}/availabilities',
|
||||
'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\Entity\Product;
|
||||
use App\Entity\User;
|
||||
use App\Enum\Product\ProductVisibility;
|
||||
use App\Form\Type\Product\ServiceFormType;
|
||||
use App\MessageBus\QueryBus;
|
||||
use App\Repository\ConfigurationRepository;
|
||||
|
|
@ -56,6 +57,7 @@ final class ServiceController extends AbstractController
|
|||
{
|
||||
if ($this->configurationRepository->getServicesParameter()) {
|
||||
$product = $this->productManager->initService($user);
|
||||
$product->setVisibility(ProductVisibility::RESTRICTED);
|
||||
$form = $this->getForm($product, $request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
/** @var array<UploadedFile>|null $images */
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||
/**
|
||||
* @see UserProductsControllerTest
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route(name: 'app_user_')]
|
||||
final class UserProductsController extends AbstractController
|
||||
{
|
||||
|
|
@ -49,7 +49,7 @@ final class UserProductsController extends AbstractController
|
|||
/**
|
||||
* @implements PaginationInterface<int,Product>
|
||||
*
|
||||
* @return PaginationInterface<int,Product>
|
||||
* @return PaginationInterface<int,mixed>
|
||||
*/
|
||||
private function paginate(Query $query, int $page): PaginationInterface
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
|||
/**
|
||||
* @see ConversationControllerTest
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
class ConversationController extends AbstractController
|
||||
{
|
||||
use FlashTrait;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ final class MyLendingsAction extends AbstractController
|
|||
) {
|
||||
}
|
||||
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
#[Route(path: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/my-lendings',
|
||||
'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: [
|
||||
'en' => MyAccountAction::BASE_URL_EN.'/my-loans',
|
||||
'fr' => MyAccountAction::BASE_URL_FR.'/mes-emprunts',
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use Symfony\Component\Uid\Uuid;
|
|||
/**
|
||||
* @see ServiceRequestControllerTest
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
class ServiceRequestController extends AbstractController
|
||||
{
|
||||
use FlashTrait;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use function Symfony\Component\String\u;
|
|||
/**
|
||||
* @see ServiceRequestStatusWorkflowControllerTest
|
||||
*/
|
||||
#[isGranted(User::ROLE_USER)]
|
||||
#[IsGranted(User::ROLE_USER)]
|
||||
class ServiceRequestStatusWorkflowController extends AbstractController
|
||||
{
|
||||
use FlashTrait;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ trait i18nTrait
|
|||
* Get the i18n prefix of a given class to have a consistent key naming in i18n
|
||||
* files.
|
||||
*/
|
||||
public function getI18nPrefix(string $class = null): string
|
||||
public function getI18nPrefix(?string $class = null): string
|
||||
{
|
||||
$class = $class ?? $this::class;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Symfony\Component\Validator\Validator\ValidatorInterface;
|
|||
final class ValidationProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ValidatorInterface $validator
|
||||
private readonly ValidatorInterface $validator,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ final class ValidationProcessor implements ProcessorInterface
|
|||
/** @var ConstraintViolationList $violations */
|
||||
$violations = $this->validator->validate($object);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
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
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserManager $userManager
|
||||
private readonly UserManager $userManager,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ final class MessageManager
|
|||
ServiceRequest $serviceRequest,
|
||||
string $messageTemplate,
|
||||
array $messageParameters = [],
|
||||
\DateTimeImmutable $createdAt = null,
|
||||
?\DateTimeImmutable $createdAt = null,
|
||||
): Message {
|
||||
$message = (new Message())
|
||||
->setServiceRequest($serviceRequest)
|
||||
|
|
@ -61,7 +61,7 @@ final class MessageManager
|
|||
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())
|
||||
->setServiceRequest($serviceRequest)
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class ProductManager
|
|||
try {
|
||||
$this->productStorage->delete($image);
|
||||
} 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);
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ final class UserManager
|
|||
try {
|
||||
$this->userStorage->delete((string) $user->getAvatar());
|
||||
} 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();
|
||||
$this->save($user, true);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use App\Form\Type\Product\SearchFormType;
|
|||
*/
|
||||
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->page = $page;
|
||||
|
|
@ -78,9 +78,9 @@ final class Search
|
|||
*/
|
||||
public function hasProximity(): bool
|
||||
{
|
||||
return $this->hasCity() &&
|
||||
($this->city?->hasLocality() ?? false) &&
|
||||
$this->hasDistance()
|
||||
return $this->hasCity()
|
||||
&& ($this->city?->hasLocality() ?? false)
|
||||
&& $this->hasDistance()
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ trait FieldTrait
|
|||
/**
|
||||
* 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)
|
||||
->renderAsSwitch(false)
|
||||
|
|
@ -30,6 +30,7 @@ trait FieldTrait
|
|||
return [
|
||||
'information' => FormField::addPanel('panel.information', 'fas fa-info-circle'),
|
||||
'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;
|
||||
|
||||
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())
|
||||
->setFilterFqcn(__CLASS__)
|
||||
|
|
@ -32,7 +32,7 @@ final class EnumFilter implements FilterInterface
|
|||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ final class GroupFilter implements FilterInterface
|
|||
{
|
||||
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())
|
||||
->setFilterFqcn(__CLASS__)
|
||||
|
|
@ -37,7 +37,7 @@ final class GroupFilter implements FilterInterface
|
|||
/** @var Group $group */
|
||||
$group = $filterDataDto->getValue();
|
||||
$queryBuilder
|
||||
->innerJoin(sprintf('%s.userGroups', $filterDataDto->getEntityAlias()), 'ug')
|
||||
->innerJoin(\sprintf('%s.userGroups', $filterDataDto->getEntityAlias()), 'ug')
|
||||
->andWhere('ug.group = :group')
|
||||
->setParameter(':group', $group->getId())
|
||||
;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ final class MyUsersFilter implements FilterInterface
|
|||
{
|
||||
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())
|
||||
->setFilterFqcn(__CLASS__)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ final class MyGroupFilter implements FilterInterface
|
|||
{
|
||||
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())
|
||||
->setFilterFqcn(__CLASS__)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ final class UuidFilter implements FilterInterface
|
|||
{
|
||||
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())
|
||||
->setFilterFqcn(__CLASS__)
|
||||
|
|
@ -37,7 +37,7 @@ final class UuidFilter implements FilterInterface
|
|||
*/
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class GroupType extends AbstractType
|
|||
{
|
||||
public function __construct(
|
||||
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;
|
||||
|
||||
use App\Controller\Admin\DashboardController;
|
||||
use App\Enum\Group\GroupOfferType;
|
||||
use App\Enum\OfferType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
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
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'choices' => GroupOfferType::getAsArray(),
|
||||
'choices' => OfferType::getAsArray(),
|
||||
'translation_domain' => DashboardController::DOMAIN,
|
||||
]);
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ class UserType extends AbstractType
|
|||
{
|
||||
public function __construct(
|
||||
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)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Length(max: 255)]
|
||||
private string $address;
|
||||
private ?string $address = null;
|
||||
|
||||
/**
|
||||
* 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)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Length(max: 255)]
|
||||
private string $locality = '';
|
||||
private ?string $locality = '';
|
||||
|
||||
/**
|
||||
* Postal code, eg: "59160".
|
||||
|
|
@ -98,7 +98,7 @@ class Address implements \Stringable
|
|||
#[ORM\Column(type: Types::STRING, length: 10, nullable: false)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Length(max: 20)]
|
||||
private string $postalCode;
|
||||
private ?string $postalCode = null;
|
||||
|
||||
/**
|
||||
* ISO code of the country, eg: "FR".
|
||||
|
|
@ -108,7 +108,7 @@ class Address implements \Stringable
|
|||
#[ORM\Column(type: Types::STRING, length: 2, nullable: false)]
|
||||
#[Assert\NotBlank]
|
||||
#[Assert\Country]
|
||||
private string $country;
|
||||
private ?string $country = null;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#[ORM\Column(type: Types::BIGINT, nullable: true)]
|
||||
private int $osmId;
|
||||
private string $osmId;
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
|
|
@ -167,12 +167,12 @@ class Address implements \Stringable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getAddress(): string
|
||||
public function getAddress(): ?string
|
||||
{
|
||||
return $this->address;
|
||||
}
|
||||
|
||||
public function setAddress(string $address): self
|
||||
public function setAddress(?string $address): self
|
||||
{
|
||||
$this->address = $address;
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ class Address implements \Stringable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getLocality(): string
|
||||
public function getLocality(): ?string
|
||||
{
|
||||
return $this->locality;
|
||||
}
|
||||
|
|
@ -237,7 +237,7 @@ class Address implements \Stringable
|
|||
return $this->locality !== '';
|
||||
}
|
||||
|
||||
public function setLocality(string $locality): self
|
||||
public function setLocality(?string $locality): self
|
||||
{
|
||||
$this->locality = $locality;
|
||||
|
||||
|
|
@ -256,24 +256,24 @@ class Address implements \Stringable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getPostalCode(): string
|
||||
public function getPostalCode(): ?string
|
||||
{
|
||||
return $this->postalCode;
|
||||
}
|
||||
|
||||
public function setPostalCode(string $postalCode): self
|
||||
public function setPostalCode(?string $postalCode): self
|
||||
{
|
||||
$this->postalCode = $postalCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCountry(): string
|
||||
public function getCountry(): ?string
|
||||
{
|
||||
return $this->country;
|
||||
}
|
||||
|
||||
public function setCountry(string $country): self
|
||||
public function setCountry(?string $country): self
|
||||
{
|
||||
$this->country = $country;
|
||||
|
||||
|
|
@ -340,12 +340,12 @@ class Address implements \Stringable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getOsmId(): int
|
||||
public function getOsmId(): string
|
||||
{
|
||||
return $this->osmId;
|
||||
}
|
||||
|
||||
public function setOsmId(int $osmId): Address
|
||||
public function setOsmId(string $osmId): Address
|
||||
{
|
||||
$this->osmId = $osmId;
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ class Address implements \Stringable
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getSubAndLocality(): string
|
||||
public function getSubAndLocality(): ?string
|
||||
{
|
||||
if (u($this->subLocality)->isEmpty()) {
|
||||
return $this->locality;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue