Compare commits

...

158 commits

Author SHA1 Message Date
Simon Vieille 26cbaa8469 Merge branch 'develop' 2024-05-03 17:58:22 +02:00
Simon Vieille 7897bafcc2
fix undefined window.tinymce.murph 2024-05-03 17:57:43 +02:00
Simon Vieille 63a8a60e2d Merge branch 'develop' 2024-03-31 16:57:56 +02:00
Simon Vieille d01e9d618b
add border color on tinymce editor 2024-03-31 16:57:46 +02:00
Simon Vieille 96347a1730 Merge branch 'develop' 2024-03-31 16:50:47 +02:00
Simon Vieille aeb0e6c109 allow to use window.tinymceModes to add or override tinymce modes 2024-03-31 16:50:18 +02:00
Simon Vieille 64258a2d8c Merge branch 'develop' 2024-03-25 15:27:12 +01:00
Simon Vieille 70329ceeda
fix template of CrudController (maker) 2024-03-25 15:27:06 +01:00
Simon Vieille 1adb1ebe2b Merge branch 'develop' 2024-03-25 13:04:17 +01:00
Simon Vieille f57cc8e4d6
fix hidden save button in file manager 2024-03-25 13:04:16 +01:00
Simon Vieille b21967028e Merge branch 'develop' 2024-03-25 13:03:01 +01:00
Simon Vieille 5b22851674
fix hidden save button in file manager 2024-03-25 13:02:20 +01:00
Simon Vieille 4082bb171a
fix use of IsGranted is CrudController 2024-02-04 16:48:44 +01:00
Simon Vieille dc19617fb1 Merge branch 'develop' 2024-02-04 16:33:34 +01:00
Simon Vieille dbd2036fb0
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:33:30 +01:00
Simon Vieille 8e2566abc8
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:33:22 +01:00
Simon Vieille 66e1a9c87f
rollback commit 614ae40 (add IsGranted in all methods of the CrudController maker template) 2024-02-04 16:31:21 +01:00
Simon Vieille 6709c0a303
update changelog 2024-02-04 16:17:49 +01:00
Simon Vieille 614ae40901 add IsGranted in all methods of the CrudController maker template 2024-02-04 16:16:22 +01:00
Simon Vieille 430bff9433
fix default crud sort 2024-02-02 20:04:19 +01:00
Simon Vieille 6441da8a27
fix default crud sort 2024-02-02 20:03:57 +01:00
Simon Vieille 801e3317e7
update changelog 2024-02-02 20:02:00 +01:00
Simon Vieille f7604d2a45
fix default crud sort 2024-02-02 20:01:23 +01:00
Simon Vieille 8a632a1b14
update Murph version constant 2024-02-01 18:46:33 +01:00
Simon Vieille 4701090134
update changelog 2024-01-27 15:49:58 +01:00
Simon Vieille c40c7e3362
fix type casting in slugifier 2024-01-27 15:49:39 +01:00
Simon Vieille 8edbf0cc08
add default default on node's code when slugify 2023-11-27 10:32:38 +01:00
Simon Vieille 175321bc2d
fix issue on file manager when a file is selected in the file in the file picker 2023-11-16 23:06:00 +01:00
Simon Vieille 5d6531d197
fix sidebar scroll algo 2023-11-14 23:21:24 +01:00
Simon Vieille 90603f62e0
add side bar scroll animation 2023-11-14 23:08:09 +01:00
Simon Vieille 053f4aa5b8
add auto-scroll on current sidebar item 2023-11-14 23:03:43 +01:00
Simon Vieille 79754d45c1
add auto-scroll on current sidebar item
remove jquery from sidebar module
2023-11-14 22:51:40 +01:00
Simon Vieille c98ea50f30
fix undefined pager on index 2023-11-13 16:06:06 +01:00
Simon Vieille 0f1bc761b2
add no-wrap around the thead sort link 2023-11-10 19:42:45 +01:00
Simon Vieille 50dbb07314
update changelog 2023-11-10 19:35:48 +01:00
Simon Vieille 72e783f865
fix render of the URL when the window is small 2023-11-10 19:35:39 +01:00
Simon Vieille ee28c9abb7
copy the pager in the bottom of the index table
remove the with class of the action column
2023-11-10 19:28:20 +01:00
Simon Vieille 2bd6836a7f
fix issue on file manager when a file is selected in the file in the file picker 2023-11-09 17:19:09 +01:00
Simon Vieille 6f961ba79b
release v1.23.0 2023-11-01 16:31:33 +01:00
Simon Vieille e095fc4197
update changelog 2023-10-27 17:03:08 +02:00
Simon Vieille 6736f94eea
set searchFields option on jschoice manager 2023-10-27 17:02:15 +02:00
Simon Vieille 93a1e7811d
change colors on js-choices element 2023-10-25 20:15:21 +02:00
Simon Vieille a0027c0b69 Merge branch 'feature/theming' into develop 2023-10-25 19:59:32 +02:00
Simon Vieille 498c71081d
add red * on required label 2023-10-25 19:59:28 +02:00
Simon Vieille 8713b401f9
rollback modal changes 2023-10-25 19:36:10 +02:00
Simon Vieille 1463f43298
update changelog 2023-10-25 19:22:56 +02:00
Simon Vieille b89e036c49 change border colors of inputs when focused 2023-10-25 19:22:44 +02:00
Simon Vieille 1d0b657c83
add sass classes to mange with of elements
fix the aspect of the actions's column in the crud

add background in the modal header

change vavigation pills colors
2023-10-25 19:12:02 +02:00
Simon Vieille 0cadf28738
update changelog 2023-10-20 09:46:57 +02:00
Simon Vieille ede8d4fdcb
remove unsed twig in mail notifier 2023-10-20 09:45:58 +02:00
Simon Vieille 5c3f2ab1e7
refactor services using constructor property promotions 2023-10-20 09:44:18 +02:00
Simon Vieille c1eb277a6a
add 'Length' constraint in forms 2023-10-19 20:51:23 +02:00
Simon Vieille d3f27d97ad
apply php linter 2023-10-12 16:15:07 +02:00
Simon Vieille 5e392d469a
update changelog 2023-10-12 16:04:49 +02:00
Simon Vieille 67f79083ef
change params given to the callback of a global batch action (page removed, add selected items) 2023-10-12 16:04:43 +02:00
Simon Vieille b9b07c1409
fix issue with CrudConfiguration::setGlobalBatchAction 2023-10-12 16:04:03 +02:00
Simon Vieille 521ed5ce64 Merge branch 'feature/batch' into develop 2023-10-12 15:28:17 +02:00
Simon Vieille dda43ef3cc
change CrudController::doBatch to manage a global batch action 2023-10-12 15:28:14 +02:00
Simon Vieille c65cc26be8
batch form is not submitted with XHR when it's a global action 2023-10-12 15:27:36 +02:00
Simon Vieille 2f884df602
add CrudConfiguration::setGlobalBatchAction method 2023-10-12 15:25:52 +02:00
Simon Vieille 8979fc5beb
fix test in RepositoryQuery::addForcedFilterHandler 2023-10-10 10:18:25 +02:00
Simon Vieille bd663838f6
allow to define templates show before and after a murph collection item 2023-10-06 12:46:51 +02:00
Simon Vieille 177b23365b
update changelog 2023-10-04 13:55:08 +02:00
Simon Vieille 645ae700d4
remove parameter $option on CrudConfiguration::setForm
fix CrudController make template
2023-10-04 13:54:37 +02:00
Simon Vieille 7614c24012
fix regression on crud sorting 2023-10-04 13:47:01 +02:00
Simon Vieille 1f2edf183b
release v1.22.0 2023-09-28 18:14:49 +02:00
Simon Vieille 28a4f63640
fix compatibility of TranslatableEntityManager with the entity manager 2023-09-28 10:32:49 +02:00
Simon Vieille 7fceefa6d3
add flush option in the entity manager on create, update, remove, and persist methods 2023-09-28 10:31:00 +02:00
Simon Vieille a08c62229d
update changelog 2023-09-27 15:17:54 +02:00
Simon Vieille f97f1dfedf
add crud sorting in the session 2023-09-27 15:17:35 +02:00
Simon Vieille 45b3f6bb80
update changelog 2023-09-27 10:22:29 +02:00
Simon Vieille 21ee41ff29
add count method on repository query
add addForcedFilterHandler method
2023-09-27 10:21:49 +02:00
Simon Vieille 8d5de79192
add 'inline_form_validation' option to validate inline forms with custom algo 2023-09-26 16:12:23 +02:00
Simon Vieille 212afe2775
fix href_attr allowed types 2023-09-15 15:27:56 +02:00
Simon Vieille 16dd0d5744
fix session name in inline form controller 2023-09-14 16:57:39 +02:00
Simon Vieille 9957523c59
add context variable into admin controller's views 2023-09-09 13:48:03 +02:00
Simon Vieille 688f66e94e
update default class on toggle display 2023-08-31 16:20:07 +02:00
Simon Vieille c9b997e75d add new options in BooleanField: toggle|checkbox_class_when_true and toggle|checkbox_class_when_false 2023-08-31 16:12:57 +02:00
Simon Vieille 374db9117f
release v1.21.1 2023-08-17 20:24:12 +02:00
Simon Vieille 4048152a8e
update changelog 2023-08-17 20:16:04 +02:00
Simon Vieille 4385e7a525
add form error handle in settings actions 2023-08-17 20:15:02 +02:00
Simon Vieille 5d3999f766
update translations 2023-08-17 08:59:06 +02:00
Simon Vieille 03c0d6cfd2
fix modal hiding when a file is successfuly uploaded in the file manager on tinymce 2023-08-16 10:08:02 +02:00
Simon Vieille 290a4750bc Revert "remove sensio/framework-extra-bundle"
This reverts commit 7d647d3bb4.
2023-08-14 14:32:34 +02:00
Simon Vieille 7d647d3bb4
remove sensio/framework-extra-bundle 2023-08-14 14:29:11 +02:00
Simon Vieille d637c44e5c
replace the navigation badge with a fontawesome square 2023-08-14 12:47:33 +02:00
Simon Vieille 19d26e6bd0
fix modal hiding when a file is successfuly uploaded in the file manager 2023-08-14 11:39:53 +02:00
Simon Vieille 109584d933
update changelog 2023-08-13 22:38:42 +02:00
Simon Vieille c3209c68dd
fix modal hiding when a file is successfuly uploaded in the file manager 2023-08-13 22:38:10 +02:00
Simon Vieille 4a5b67dd93
update changelog 2023-08-13 22:12:44 +02:00
Simon Vieille 4573a8d31e
fix tinymce reload when modal is closed and reopened 2023-08-13 22:11:44 +02:00
Simon Vieille 405909a4e7
remove last request data after filling the form 2023-08-11 19:28:09 +02:00
Simon Vieille 2b3e2027c9
update changelog 2023-08-11 19:20:43 +02:00
Simon Vieille 76b25f3bca
add form error handle in inline edit action
refill the form using last request
2023-08-11 19:18:11 +02:00
Simon Vieille ca25210d1c
release v1.21.0 2023-08-11 09:47:46 +02:00
Simon Vieille 0ab960ed0a
allow to use array syntax in string builder filter 2023-08-08 14:18:10 +02:00
Simon Vieille 8e0a7f178b
fix block in BooleanField template 2023-08-08 12:15:01 +02:00
Simon Vieille 200dd0b8d6
add the option 'display' on BooleanField
add displays toggle and checkbox for BooleanField
2023-08-08 12:13:28 +02:00
Simon Vieille bd4338bb2d
fix getColor return type 2023-08-07 19:28:51 +02:00
Simon Vieille 64b524b04e
update changelog 2023-08-07 19:20:44 +02:00
Simon Vieille 6a5c5d899f
add color property in Navigation
add badge with navigation color in admin views
2023-08-07 19:20:14 +02:00
Simon Vieille b38fe0fe00
update changelog 2023-08-07 18:39:33 +02:00
Simon Vieille d21ab30ebe
add default_value option in crud fields 2023-08-07 18:31:22 +02:00
Simon Vieille a96a6377d5
add associated nodes in page form 2023-08-07 18:24:31 +02:00
Simon Vieille 946a421900
add default value for $route param in doDelete method 2023-08-04 11:08:56 +02:00
Simon Vieille f7eb5b0b49
fix routes in the global settings controller 2023-07-28 10:43:21 +02:00
Simon Vieille 0dadc670d9
update changelog 2023-07-27 18:15:33 +02:00
Simon Vieille 4a0b13e6e3
update version 2023-07-27 18:15:09 +02:00
Simon Vieille 1936b366df
remove logo in login pages if empty 2023-07-27 18:14:17 +02:00
Simon Vieille 4541bbfb8a
update changelog 2023-07-26 11:31:19 +02:00
Simon Vieille f3674ad4e0 Merge branch 'master' into develop 2023-07-26 11:29:07 +02:00
Simon Vieille ddf1fecc90
fix collection widget: allow_add/allow_delete and prototype 2023-07-26 11:28:55 +02:00
Simon Vieille 0c61cb9355
update changelog 2023-07-26 11:28:03 +02:00
Simon Vieille fc1a1c617e
update changelog 2023-07-20 09:20:18 +02:00
Simon Vieille bea2d1cc9f
add default templates when a crud is generated 2023-07-19 22:01:25 +02:00
Simon Vieille 4bf6b048c3
add missing configuration property
remove user admin controller routes
2023-07-19 21:43:16 +02:00
Simon Vieille 81194a1d67
add boolean 'is_disabled' in the menu item template options 2023-07-19 21:42:28 +02:00
Simon Vieille 10221591c2 core.site.name and core.site.logo are not longer required 2023-07-19 20:42:35 +02:00
Simon Vieille 231742eb0d
fix responsive of account edit template 2023-07-19 20:28:37 +02:00
Simon Vieille f1d956ee5c
add context variable in each controllers to simplify overrides 2023-07-19 20:20:55 +02:00
Simon Vieille 5de35c3408
add context variable in each controllers to simplify overrides 2023-07-19 20:20:45 +02:00
Simon Vieille f144760085
add boolean field for CRUD 2023-07-19 20:19:16 +02:00
Simon Vieille e2c9ecb941
add option to remove itetables values and/or specifics keys in the twig toArray function 2023-07-19 19:36:32 +02:00
Simon Vieille f9a20716a0
add twig block to override defaults actions in crud index template 2023-07-19 14:18:50 +02:00
Simon Vieille b107f077de
fix redirect listener: use boolean instead of integer 2023-07-04 12:49:56 +02:00
Simon Vieille 2729b38fd8
add variable for the sidebar size 2023-06-07 10:57:15 +02:00
Simon Vieille baeecc0f7f
fix translations 2023-06-05 16:52:58 +02:00
Simon Vieille 8667188675
add filename generator setter in FileUploadHandler 2023-05-27 15:48:58 +02:00
Simon Vieille 895d5065ca
add setter to define all fields in a defined context 2023-05-27 15:08:16 +02:00
Simon Vieille c688cc0552
add trans filter in inline form modal title 2023-05-27 15:02:17 +02:00
Simon Vieille c0340ec5a9 fix maker CrudController template: remove bad pasted code 2023-05-27 14:55:21 +02:00
Simon Vieille 650ea3ede0
add form options in the crud filter action 2023-05-27 11:43:45 +02:00
Simon Vieille 497ee2f027
add form options in the crud filter action 2023-05-27 11:34:13 +02:00
Simon Vieille 8b13d37e71
update file details view on the file manager 2023-05-19 20:30:35 +02:00
Simon Vieille 3219ba47aa
add block class name for the choice type in the page maker 2023-05-19 19:55:02 +02:00
Simon Vieille 7ca8a4aa04
fix filemanager date ordering
add order by type
2023-04-17 13:36:09 +02:00
Simon Vieille 7ba417a03e
enable double click on cruds
add inline form on user display names
2023-04-16 23:19:34 +02:00
Simon Vieille 27cbfd1110
release v1.19.0 2023-04-15 10:48:51 +02:00
Simon Vieille 1069a4ad46
fix(crud): fix setter of double click 2023-04-14 10:57:53 +02:00
Simon Vieille a134a41926
feat(filemanager): rename date name 2023-04-14 07:11:30 +02:00
Simon Vieille b915b8b3ed
feat(crud): add the option "inline_form" in fields
feat(crud): allows to enable and disable double click listener on a table row

feat(crud): update de template of the controller
2023-04-14 07:09:57 +02:00
Simon Vieille 42ab4d85f0
show size and last modified date of files in file manager 2023-03-05 21:33:34 +01:00
Simon Vieille 902ac81e4b
allow to remove meta datas of a page when enditing 2023-03-05 20:39:52 +01:00
Simon Vieille e74469a687
update the view 'show' of a navigation 2023-03-02 21:12:18 +01:00
Simon Vieille 219526e3a2
allow webp image in filemanager 2023-03-02 19:10:08 +01:00
Simon Vieille 1cea077598
fix translation 2023-03-02 19:06:50 +01:00
Simon Vieille de6ca18c0a
release v1.18.0 2023-01-13 18:20:51 +01:00
Simon Vieille 3127018a73
fix(crud): remove default page value in abstract crud controller 2023-01-09 08:10:34 +01:00
Simon Vieille 9e0eda4338
fix(crud): allow POST in delete actions 2023-01-09 08:07:40 +01:00
Simon Vieille 03afb6fb4b Merge branch 'feature/updates' into develop 2023-01-08 20:54:16 +01:00
Simon Vieille e402c49835
fix(admin): test site_logo before using it 2023-01-08 20:50:00 +01:00
Simon Vieille 0a7550940d
feat(dep): add symfony/runtime 5.4 2023-01-08 19:59:54 +01:00
Simon Vieille 5626a2522a
feat(dep): remove symfony/runtime 2023-01-08 19:40:34 +01:00
Simon Vieille 8ebb54c9a9
feat(dep): change version of symfony/runtime 2023-01-08 19:40:00 +01:00
Simon Vieille 98c2d70367
feat(dep): add symfony/flex 2023-01-08 19:32:02 +01:00
Simon Vieille e623e9fb46
feat(dep): add symfony/runtime 2023-01-08 19:30:15 +01:00
Simon Vieille 6b222dd4f7 style(css): replace 4 spaces by 2 spaces 2023-01-08 18:09:03 +01:00
Simon Vieille bc1f8e7cf9 fix(ui): update z-index of choices__list--dropdown 2023-01-08 18:07:54 +01:00
147 changed files with 2134 additions and 1401 deletions

View file

@ -1,5 +1,119 @@
## [Unreleased]
### Added
* allow to use `window.tinymceModes` to add or override tinymce modes
* add border color on tinymce editor
### Fixed
* fix default crud sort
* fix hidden save button in file manager
* fix template of CrudController (maker)
* fix undefined `window.tinymce.murph`
## [v1.24.1] - 2024-02-01
### Fixed
* update Murph version constant
## [v1.24.0] - 2024-01-27
### Added
* add CSS class `no-wrap`
* copy the pager of the CRUD at the bottom of the list
### Fixed
* fix an issue with the file manager when editing an item opened in a modal
* fix type casting in slugifier
## [v1.23.0] - 2023-11-01
### Added
* allow to define templates show before and after a murph collection item
* add global batch actions
* add constraint `Length` in forms
* add sass classes to mange with of elements
* set searchFields option on jschoice manager (search on labels)
### Changed
* refactor services using constructor property promotions
* remove twig in the mail notifier service
* change pills colors
* change border colors of inputs when focused
* change colors on js-choices element
### Fixed
* fix regression on crud sorting
* fix test in RepositoryQuery::addForcedFilterHandler
* remove parameter $option on CrudConfiguration::setForm and fix CrudController make template
* fix the aspect of the actions's column in the crud
## [v1.22.0] - 2023-09-28
### Added
* add new options in BooleanField: `toggle|checkbox_class_when_true` and `toggle|checkbox_class_when_false`
* add `count` method in repository query
* add `addForcedFilterHandler` method in repository query
* add `inline_form_validation` option to validate inline forms with custom algo
* add crud sorting parameters in the session
* add flush option in the entity manager on create, update, remove, and persist methods
## [1.21.1] - 2023-08-17
### Added
* add form error handle in inline edit action and refill the form using the previous request content
* add form error handle in ssettings actions and refill the form using the previous request content
### Fixed
* fix tinymce reload when modal is closed and reopened
* fix modal hiding when a file is successfuly uploaded in the file manager
## [1.21.0] - 2023-08-11
### Added
* allow to use array syntax in string builder filter
* add color property in Navigation
* add badge with navigation color in admin views
* add `default_value` option in crud fields
* add `display` option in BooleanField
* add associated nodes in page form
### Fixed
* fix routes in the global settings controller
## [1.20.0] - 2023-07-27
### Added
* enable double click on cruds
* add block class name for the choice type in the page maker
* update file details view on the file manager
* add form options in the crud filter action
* add trans filter in inline form modal title
* add setter to define all fields in a defined context
* add filename generator setter in FileUploadHandler
* add variable for the sidebar size
* add twig block to override defaults actions in crud index template
* add option to remove iterable values and/or specifics keys in the twig toArray function
* add boolean field for CRUD
* add context variable in each controllers to simplify overrides
* core.site.name and core.site.logo are not longer required
* add default templates when a crud is generated
* add boolean 'is_disabled' in the menu item template options
### Fixed
* fix filemanager date ordering
* fix maker CrudController template: remove bad pasted code
* fix redirect listener: use boolean instead of integer
* fix responsive of account edit template
* fix collection widget: allow_add/allow_delete and prototype
### Changed
* user admin routes are defined in core, custom controller is not required
## [1.19.0] - 2023-04-15
### Added
* feat(page): forms for metas, opengraph and extra informations can be removed
* feat(navigation): user interface is improved
* feat(file): webp is allowed and shown in form widgets and in file manager details
* feat(file): the file manager now show the size and the modification date of a file
* feat(crud): add option `action` in field to add a link to the view page or to the edition page
* feat(crud): add option `inline_form` in field to configure to edit the data
* feat(crud): add `setDoubleClick` in the crud configuration
## [1.18.0] - 2023-01-13
### Added
* feat(dep): add symfony/runtime
* feat(dep): add symfony/flex
### Fixed
* fix(crud): allow POST in delete actions
* fix(crud): remove default page value in abstract crud controller
* fix(admin): test site_logo before using it
* fix(ui): update z-index of choices__list--dropdown
## [1.17.1] - 2022-12-03
### Fixed
* add mising attribute on timestampable (doctrine)

View file

@ -37,7 +37,7 @@
"symfony/event-dispatcher": "5.4.*",
"symfony/expression-language": "5.4.*",
"symfony/finder": "5.4.*",
"symfony/flex": "^1.3.1",
"symfony/flex": "^2.2",
"symfony/form": "5.4.*",
"symfony/framework-bundle": "5.4.*",
"symfony/http-client": "5.4.*",
@ -60,7 +60,8 @@
"symfony/webpack-encore-bundle": "^1.11",
"symfony/yaml": "5.4.*",
"twig/extra-bundle": "^2.12|^3.3",
"twig/twig": "^2.12|^3.3"
"twig/twig": "^2.12|^3.3",
"symfony/runtime": "^5.4"
},
"autoload": {
"psr-4": {

View file

@ -10,14 +10,12 @@ namespace App\Core\Ab;
class AbTest implements AbTestInterface
{
protected $results;
protected string $name;
protected array $variations = [];
protected array $probabilities = [];
protected int $duration = 3600 * 24;
public function __construct(string $name)
public function __construct(protected string $name)
{
$this->name = $name;
}
public function getName(): string

View file

@ -13,18 +13,16 @@ use App\Core\Repository\Analytic\ViewRepositoryQuery;
*/
class DateRangeAnalytic
{
protected ViewRepositoryQuery $viewQuery;
protected RefererRepositoryQuery $refererQuery;
protected ?Node $node;
protected ?\DateTime $from;
protected ?\DateTime $to;
protected bool $reload = true;
protected array $cache = [];
public function __construct(ViewRepositoryQuery $viewQuery, RefererRepositoryQuery $refererQuery)
{
$this->viewQuery = $viewQuery;
$this->refererQuery = $refererQuery;
public function __construct(
protected ViewRepositoryQuery $viewQuery,
protected RefererRepositoryQuery $refererQuery
) {
}
public function getViews(): array
@ -83,7 +81,7 @@ class DateRangeAnalytic
$datas[$index]['mobileViews'] += $entity->getMobileViews();
}
uasort($datas, function($a, $b) {
uasort($datas, function ($a, $b) {
if ($a['views'] > $b['views']) {
return -1;
}
@ -130,7 +128,7 @@ class DateRangeAnalytic
$datas[$index]['uris'][$path] += $entity->getViews();
}
uasort($datas, function($a, $b) {
uasort($datas, function ($a, $b) {
if ($a['views'] > $b['views']) {
return -1;
}

View file

@ -10,16 +10,10 @@ namespace App\Core\Annotation;
#[\Attribute]
class UrlGenerator
{
public string $service;
public string $method;
public array $options = [];
public function __construct(string $service, string $method, array $options = [])
{
$this->service = $service;
$this->method = $method;
$this->options = $options;
public function __construct(
public string $service,
public string $method,
public array $options = []
) {
}
}

View file

@ -23,20 +23,12 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private EntityManagerInterface $entityManager;
private UrlGeneratorInterface $urlGenerator;
private CsrfTokenManagerInterface $csrfTokenManager;
private UserPasswordEncoderInterface $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
public function __construct(
private EntityManagerInterface $entityManager,
private UrlGeneratorInterface $urlGenerator,
private CsrfTokenManagerInterface $csrfTokenManager,
private UserPasswordEncoderInterface $passwordEncoder
) {
}
public function supports(Request $request)

View file

@ -12,8 +12,8 @@
namespace App\Core\Bundle;
use App\Core\DependencyInjection\CoreExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class CoreBundle extends Bundle
{

View file

@ -5,13 +5,13 @@ namespace App\Core\Cache;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpClient\Exception\ClientException;
use Symfony\Component\HttpClient\Exception\TransportException;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;
/**
* class SymfonyCacheManager.
@ -20,15 +20,11 @@ use Symfony\Component\HttpClient\Exception\TransportException;
*/
class SymfonyCacheManager
{
protected KernelInterface $kernel;
protected HttpClientInterface $httpClient;
protected UrlGeneratorInterface $urlGenerator;
public function __construct(KernelInterface $kernel, HttpClientInterface $httpClient, UrlGeneratorInterface $urlGenerator)
{
$this->kernel = $kernel;
$this->httpClient = $httpClient;
$this->urlGenerator = $urlGenerator;
public function __construct(
protected KernelInterface $kernel,
protected HttpClientInterface $httpClient,
protected UrlGeneratorInterface $urlGenerator
) {
}
public function cleanRouting()

View file

@ -18,19 +18,12 @@ class UserCreateCommand extends Command
{
protected static $defaultName = 'murph:user:create';
protected static $defaultDescription = 'Creates a user';
protected UserFactory $userFactory;
protected EntityManager $entityManager;
protected TokenGeneratorInterface $tokenGenerator;
public function __construct(
UserFactory $userFactory,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator
protected UserFactory $userFactory,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator
) {
$this->userFactory = $userFactory;
$this->entityManager = $entityManager;
$this->tokenGenerator = $tokenGenerator;
parent::__construct();
}

View file

@ -26,7 +26,7 @@ abstract class CrudController extends AdminController
abstract protected function getConfiguration(): CrudConfiguration;
protected function doIndex(int $page = 1, RepositoryQuery $query, Request $request, Session $session): Response
protected function doIndex(int $page, RepositoryQuery $query, Request $request, Session $session, string $context = 'index'): Response
{
$configuration = $this->getConfiguration();
@ -35,13 +35,14 @@ abstract class CrudController extends AdminController
$pager = $query
->usefilters($this->filters)
->paginate($page, $configuration->getmaxperpage('index'))
->paginate($page, $configuration->getMaxPerPage($context))
;
return $this->render($this->getConfiguration()->getView('index'), [
return $this->render($this->getConfiguration()->getView($context), [
'configuration' => $configuration,
'pager' => $pager,
'sort' => $this->sort,
'context' => $context,
'filters' => [
'show' => null !== $configuration->getForm('filter'),
'isEmpty' => empty($this->filters),
@ -49,13 +50,13 @@ abstract class CrudController extends AdminController
]);
}
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null): Response
protected function doNew(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeCreate = null, string $context = 'new'): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions('new'));
$form = $this->createForm($configuration->getForm('new'), $entity, $configuration->getFormOptions($context));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
@ -76,30 +77,32 @@ abstract class CrudController extends AdminController
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView('new'), [
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'configuration' => $configuration,
'context' => $context,
'entity' => $entity,
]);
}
protected function doShow(EntityInterface $entity): Response
protected function doShow(EntityInterface $entity, string $context = 'show'): Response
{
$configuration = $this->getConfiguration();
return $this->render($configuration->getView('show'), [
return $this->render($configuration->getView($context), [
'entity' => $entity,
'context' => $context,
'configuration' => $configuration,
]);
}
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
protected function doEdit(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null, string $context = 'edit'): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions('edit'));
$form = $this->createForm($configuration->getForm('edit'), $entity, $configuration->getFormOptions($context));
if ($request->isMethod('POST')) {
$form->handleRequest($request);
@ -112,22 +115,109 @@ abstract class CrudController extends AdminController
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute($configuration->getPageRoute('edit'), array_merge(
return $this->redirectToRoute($configuration->getPageRoute($context), array_merge(
['entity' => $entity->getId()],
$configuration->getPageRouteParams('edit')
$configuration->getPageRouteParams($context)
));
}
$this->addFlash('warning', 'The form is not valid.');
}
return $this->render($configuration->getView('edit'), [
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
'entity' => $entity,
]);
}
protected function doSort(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
protected function doInlineEdit(string $context, string $label, EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeUpdate = null): Response
{
$configuration = $this->getConfiguration();
$this->prepareEntity($entity);
$builder = $this->createFormBuilder($entity);
$callback = $configuration->getFields($context)[$label]['options']['inline_form'] ?? null;
$validationCallback = $configuration->getFields($context)[$label]['options']['inline_form_validation'] ?? null;
if (null === $callback) {
throw $this->createNotFoundException();
}
call_user_func_array($callback, [$builder, $entity]);
$redirectTo = $request->query->get('redirectTo');
$form = $builder->getForm();
$session = $request->getSession();
$lastRequestId = sprintf(
'inline_request_%s_%s_%s_%s',
get_class($entity),
$entity->getId(),
$context,
$label
);
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if (null !== $validationCallback) {
call_user_func_array($validationCallback, [$entity, $form, $request]);
}
if ($form->isValid()) {
if (null !== $beforeUpdate) {
call_user_func_array($beforeUpdate, [$entity, $form, $request]);
}
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirect($redirectTo);
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render($configuration->getView('inline_edit'), [
'form' => $form->createView(),
'configuration' => $configuration,
'entity' => $entity,
'context' => $context,
'label' => $label,
'redirectTo' => $redirectTo,
]);
}
protected function doSort(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
{
$configuration = $this->getConfiguration();
$context = $request->query->get('context', 'index');
@ -165,7 +255,7 @@ abstract class CrudController extends AdminController
return $this->json([]);
}
protected function doBatch(int $page = 1, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
protected function doBatch(int $page, RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session): Response
{
$configuration = $this->getConfiguration();
$datas = $request->request->get('batch', []);
@ -194,16 +284,39 @@ abstract class CrudController extends AdminController
$query->useFilters($this->filters);
if ('selection' === $target) {
$isSelection = true;
$pager = $query->paginate($page, $configuration->getMaxPerPage($context));
} else {
$isSelection = false;
$pager = $query->find();
$useSelection = 'selection' === $target;
if ($batchAction['isGlobal']) {
$selection = null;
if ($useSelection) {
$queryClone = clone $query;
$pager = $queryClone->paginate($page, $configuration->getMaxPerPage($context));
$selection = [];
foreach ($pager as $key => $entity) {
if (isset($items[$key + 1])) {
$selection[] = $entity;
}
}
}
$result = $callback($query, $entityManager, $selection);
if ($result instanceof Response) {
return $result;
}
return $this->redirect($request->query->get('redirectTo'));
}
$pager = $useSelection
? $query->paginate($page, $configuration->getMaxPerPage($context))
: $query->find()
;
foreach ($pager as $key => $entity) {
if (($isSelection && isset($items[$key + 1])) || !$isSelection) {
if (($useSelection && isset($items[$key + 1])) || !$useSelection) {
$callback($entity, $entityManager);
}
}
@ -213,7 +326,7 @@ abstract class CrudController extends AdminController
return $this->json([]);
}
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null): Response
protected function doDelete(EntityInterface $entity, EntityManager $entityManager, Request $request, callable $beforeDelete = null, string $route = 'index'): Response
{
$configuration = $this->getConfiguration();
@ -227,10 +340,10 @@ abstract class CrudController extends AdminController
$this->addFlash('success', 'The data has been removed.');
}
return $this->redirectToRoute($configuration->getPageRoute('index'));
return $this->redirectToRoute($configuration->getPageRoute($route));
}
protected function doFilter(Session $session): Response
protected function doFilter(Session $session, string $context = 'filter'): Response
{
$configuration = $this->getConfiguration();
$type = $configuration->getForm('filter');
@ -239,11 +352,12 @@ abstract class CrudController extends AdminController
throw $this->createNotFoundException();
}
$form = $this->createForm($type);
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
$form->submit($session->get($form->getName(), []));
return $this->render($configuration->getView('filter'), [
return $this->render($configuration->getView($context), [
'form' => $form->createView(),
'context' => $context,
'configuration' => $configuration,
]);
}
@ -257,7 +371,7 @@ abstract class CrudController extends AdminController
return;
}
$form = $this->createForm($type);
$form = $this->createForm($type, null, $configuration->getFormOptions('filter'));
if ($request->query->has($form->getName())) {
$filters = $request->query->get($form->getName());
@ -304,9 +418,27 @@ abstract class CrudController extends AdminController
}
$defaultSort = $configuration->getDefaultSort($context);
$session = $request->getSession();
$name = $request->query->get('_sort', $defaultSort['label'] ?? null);
$direction = strtolower($request->query->get('_sort_direction', $defaultSort['direction'] ?? 'asc'));
$sessionId = sprintf('%s_%s_sort', $context, get_called_class());
$sessionSortName = sprintf('%s_label', $sessionId);
$sessionSortDirection = sprintf('%s_direction', $sessionId);
$name = $request->query->get('_sort', $session->get($sessionSortName)) ?? $defaultSort['label'] ?? null;
$direction = strtolower(
$request->query->get(
'_sort_direction',
$session->get($sessionSortDirection)
) ?? $defaultSort['direction'] ?? 'asc'
);
$session->set($sessionSortName, $name);
$session->set($sessionSortDirection, $direction);
if (empty($name)) {
return;
}
if (!in_array($direction, ['asc', 'desc'])) {
$direction = 'asc';

View file

@ -3,8 +3,6 @@
namespace App\Core\Controller\Dashboard;
use App\Core\Controller\Admin\AdminController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DashboardAdminController extends AdminController
{

View file

@ -390,7 +390,7 @@ class FileManagerAdminController extends AdminController
]);
}
#[Route(path: '/delete', name: 'admin_file_manager_delete', methods: ['DELETE'])]
#[Route(path: '/delete', name: 'admin_file_manager_delete', methods: ['DELETE', 'POST'])]
public function delete(FsFileManager $manager, Request $request): Response
{
$path = $request->request->get('file');

View file

@ -61,7 +61,7 @@ class RedirectAdminController extends CrudController
return $this->doBatch($page, $query, $entityManager, $request, $session);
}
#[Route(path: '/admin/redirect/delete/{entity}', name: 'admin_redirect_delete', methods: ['DELETE'])]
#[Route(path: '/admin/redirect/delete/{entity}', name: 'admin_redirect_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
@ -102,11 +102,11 @@ class RedirectAdminController extends CrudController
'attr' => ['class' => 'col-6'],
])
->setField('index', 'Enabled', Field\ButtonField::class, [
'property_builder' => function(EntityInterface $entity) {
'property_builder' => function (EntityInterface $entity) {
return $entity->getIsEnabled() ? 'Yes' : 'No';
},
'attr' => ['class' => 'col-1'],
'button_attr_builder' => function(EntityInterface $entity) {
'button_attr_builder' => function (EntityInterface $entity) {
return ['class' => 'btn btn-sm btn-'.($entity->getIsEnabled() ? 'success' : 'primary')];
},
])

View file

@ -31,11 +31,28 @@ class NavigationSettingAdminController extends AdminController
$eventDispatcher->dispatch($event, NavigationSettingEvent::FORM_INIT_EVENT);
$form = $builder->getForm();
$redirectTo = $request->query->get('redirectTo');
$session = $request->getSession();
$lastRequestId = sprintf('setting_request_%s_%s', get_class($entity), $entity->getId());
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$entityManager->update($entity);
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
@ -44,17 +61,25 @@ class NavigationSettingAdminController extends AdminController
]);
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render('@Core/setting/navigation_setting_admin/edit.html.twig', [
'form' => $form->createView(),
'entity' => $entity,
'options' => $event->getData()['options'],
'redirectTo' => $redirectTo,
]);
}
#[Route(path: '/delete/{entity}', name: 'admin_navigation_setting_delete', methods: ['DELETE'])]
#[Route(path: '/delete/{entity}', name: 'admin_navigation_setting_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {

View file

@ -12,14 +12,10 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* @Route("/admin/setting")
*/
#[Route(path: '/admin/setting')]
class SettingAdminController extends AdminController
{
/**
* @Route("/{page}", name="admin_setting_index", requirements={"page": "\d+"})
*/
#[Route(path: '/{page}', name: 'admin_setting_index', requirements: ['page' => '\d+'])]
public function index(
RepositoryQuery $query,
EventDispatcherInterface $eventDispatcher,
@ -38,9 +34,7 @@ class SettingAdminController extends AdminController
]);
}
/**
* @Route("/edit/{entity}", name="admin_setting_edit")
*/
#[Route(path: '/edit/{entity}', name: 'admin_setting_edit')]
public function edit(
Entity $entity,
EntityManager $entityManager,
@ -57,30 +51,53 @@ class SettingAdminController extends AdminController
$eventDispatcher->dispatch($event, SettingEvent::FORM_INIT_EVENT);
$form = $builder->getForm();
$redirectTo = $request->query->get('redirectTo');
$session = $request->getSession();
$lastRequestId = sprintf('setting_request_%s_%s', get_class($entity), $entity->getId());
$lastRequest = $session->get($lastRequestId);
if (null !== $lastRequest && !$request->isMethod('POST')) {
$fakeRequest = Request::create(
uri: $request->getUri(),
method: 'POST',
parameters: [$form->getName() => $lastRequest]
);
$form->handleRequest($fakeRequest);
$session->remove($lastRequestId);
}
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$entityManager->update($entity);
$session->remove($lastRequestId);
$entityManager->update($entity);
$this->addFlash('success', 'The data has been saved.');
return $this->redirectToRoute('admin_setting_index');
}
$session->set($lastRequestId, $request->request->get('form'));
$this->addFlash('warning', 'The form is not valid.');
return $this->redirect(sprintf(
'%s?data-modal=%s',
$redirectTo,
urlencode($request->getUri())
));
}
return $this->render('@Core/setting/setting_admin/edit.html.twig', [
'form' => $form->createView(),
'entity' => $entity,
'options' => $event->getData()['options'],
'redirectTo' => $redirectTo,
]);
}
/**
* @Route("/delete/{entity}", name="admin_setting_delete", methods={"DELETE"})
*/
#[Route(path: '/delete/{entity}', name: 'admin_setting_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {

View file

@ -53,7 +53,7 @@ class MenuAdminController extends AdminController
]);
}
#[Route(path: '/delete/{entity}', name: 'admin_site_menu_delete', methods: ['DELETE'])]
#[Route(path: '/delete/{entity}', name: 'admin_site_menu_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
if ($this->isCsrfTokenValid('delete'.$entity->getId(), $request->request->get('_token'))) {

View file

@ -67,12 +67,12 @@ class NavigationAdminController extends CrudController
}
#[Route(path: '/admin/site/navigation/sort/{page}', name: 'admin_site_navigation_sort', methods: ['POST'], requirements: ['page' => '\d+'])]
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1, ): Response
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doSort($page, $query, $entityManager, $request, $session);
}
#[Route(path: '/admin/site/navigation/delete/{entity}', name: 'admin_site_navigation_delete', methods: ['DELETE'])]
#[Route(path: '/admin/site/navigation/delete/{entity}', name: 'admin_site_navigation_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
@ -104,9 +104,11 @@ class NavigationAdminController extends CrudController
->setView('form', '@Core/site/navigation_admin/_form.html.twig')
->setIsSortableCollection('index', true)
->setDoubleClick('index', true)
->setField('index', 'Label', Field\TextField::class, [
'property' => 'label',
'view' => '@Core/site/navigation_admin/field/label.html.twig',
'attr' => ['class' => 'miw-200'],
])
->setField('index', 'Domain', Field\ButtonField::class, [

View file

@ -2,7 +2,6 @@
namespace App\Core\Controller\Site;
use App\Core\Controller\Admin\AdminController;
use App\Core\Entity\Site\Node;
use App\Core\Entity\Site\Node as Entity;
use App\Core\Entity\Site\Page\Page;
@ -14,15 +13,15 @@ use App\Core\Form\Site\NodeType as EntityType;
use App\Core\Manager\EntityManager;
use App\Core\Repository\Site\NodeRepository;
use App\Core\Site\ControllerLocator;
use App\Core\Site\RoleLocator;
use App\Core\Site\PageLocator;
use App\Core\Site\RoleLocator;
use App\Core\Sitemap\SitemapBuilder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
#[Route(path: '/admin/site/node')]
class NodeAdminController extends AbstractController
@ -145,7 +144,7 @@ class NodeAdminController extends AbstractController
$page = $entity->getPage();
if ($page !== null) {
if (null !== $page) {
$pageConfiguration = $pageLocator->getPages()[get_class($page)] ?? null;
} else {
$pageConfiguration = null;
@ -239,7 +238,7 @@ class NodeAdminController extends AbstractController
]).sprintf('#node-%d', $entity->getId()));
}
#[Route(path: '/delete/{entity}', name: 'admin_site_node_delete', methods: ['DELETE'])]
#[Route(path: '/delete/{entity}', name: 'admin_site_node_delete', methods: ['DELETE', 'POST'])]
public function delete(
Entity $entity,
NodeRepository $nodeRepository,

View file

@ -5,19 +5,19 @@ namespace App\Core\Controller\Site;
use App\Core\Controller\Admin\Crud\CrudController;
use App\Core\Crud\CrudConfiguration;
use App\Core\Crud\Field;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Page\Page as Entity;
use App\Core\Event\Page\PageEditEvent;
use App\Core\Form\Site\Page\Filter\PageFilterType as FilterType;
use App\Core\Form\Site\Page\PageType as Type;
use App\Core\Manager\EntityManager;
use App\Core\Repository\Site\Page\PageRepositoryQuery as RepositoryQuery;
use App\Core\Site\PageLocator;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use App\Core\Event\Page\PageEditEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use App\Core\Entity\EntityInterface;
class PageAdminController extends CrudController
{
@ -61,7 +61,7 @@ class PageAdminController extends CrudController
return $this->doEdit($entity, $entityManager, $request);
}
#[Route(path: '/admin/site/page/delete/{entity}', name: 'admin_site_page_delete', methods: ['DELETE'])]
#[Route(path: '/admin/site/page/delete/{entity}', name: 'admin_site_page_delete', methods: ['DELETE', 'POST'])]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
@ -95,6 +95,8 @@ class PageAdminController extends CrudController
->setAction('index', 'show', false)
->setAction('edit', 'show', false)
->setDoubleClick('index', true)
->setField('index', 'Name', Field\TextField::class, [
'property' => 'name',
'sort' => ['name', '.name'],
@ -112,7 +114,7 @@ class PageAdminController extends CrudController
}],
'attr' => ['class' => 'col-6'],
])
->setBatchAction('index', 'delete', 'Delete', function(EntityInterface $entity, EntityManager $manager) {
->setBatchAction('index', 'delete', 'Delete', function (EntityInterface $entity, EntityManager $manager) {
$manager->delete($entity);
})
;

View file

@ -8,55 +8,55 @@ use App\Core\Crud\Field;
use App\Core\Event\Account\PasswordRequestEvent;
use App\Core\Factory\UserFactory as Factory;
use App\Core\Manager\EntityManager;
use App\Core\Security\TokenGenerator;
use App\Entity\User as Entity;
use App\Form\UserType as Type;
use App\Repository\UserRepositoryQuery as RepositoryQuery;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
use App\Core\Security\TokenGenerator;
class UserAdminController extends CrudController
{
#[Route(path: '/admin/user/{page}', name: 'admin_user_index', methods: ['GET'], requirements: ['page' => '\d+'])]
protected ?CrudConfiguration $configuration = null;
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
{
return $this->doIndex($page, $query, $request, $session);
}
#[Route(path: '/admin/user/new', name: 'admin_user_new', methods: ['GET', 'POST'])]
public function new(Factory $factory, EntityManager $entityManager, Request $request, TokenGenerator $tokenGenerator): Response
{
return $this->doNew($factory->create(null, $tokenGenerator->generateToken()), $entityManager, $request);
}
#[Route(path: '/admin/user/show/{entity}', name: 'admin_user_show', methods: ['GET'])]
public function show(Entity $entity): Response
{
return $this->doShow($entity);
}
#[Route(path: '/admin/user/filter', name: 'admin_user_filter', methods: ['GET'])]
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
#[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])]
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doEdit($entity, $entityManager, $request);
}
#[Route(path: '/admin/user/delete/{entity}', name: 'admin_user_delete', methods: ['DELETE'])]
public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doInlineEdit($context, $label, $entity, $entityManager, $request);
}
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
}
#[Route(path: '/admin/user/resetting_request/{entity}', name: 'admin_user_resetting_request', methods: ['POST'])]
public function requestResetting(Entity $entity, EventDispatcherInterface $eventDispatcher, Request $request): Response
{
if ($this->isCsrfTokenValid('resetting_request'.$entity->getId(), $request->request->get('_token'))) {
@ -81,6 +81,7 @@ class UserAdminController extends CrudController
->setPageRoute('index', 'admin_user_index')
->setPageRoute('new', 'admin_user_new')
->setPageRoute('edit', 'admin_user_edit')
->setPageRoute('inline_edit', 'admin_user_inline_edit')
->setPageRoute('show', 'admin_user_show')
->setPageRoute('delete', 'admin_user_delete')
->setPageRoute('filter', 'admin_user_filter')
@ -96,6 +97,7 @@ class UserAdminController extends CrudController
->setView('edit', '@Core/user/user_admin/edit.html.twig')
->setDefaultSort('index', 'username')
->setDoubleClick('index', true)
->setField('index', 'E-mail', Field\TextField::class, [
'property' => 'email',
@ -106,6 +108,9 @@ class UserAdminController extends CrudController
'property' => 'displayName',
'sort' => ['displayName', '.displayName'],
'attr' => ['class' => 'miw-200'],
'inline_form' => function (FormBuilderInterface $builder) {
$builder->add('displayName', null);
},
])
;
}

View file

@ -17,6 +17,7 @@ class CrudConfiguration
protected array $actionTitles = [];
protected array $forms = [];
protected array $formOptions = [];
protected array $inlineForms = [];
protected array $views = [];
protected array $viewDatas = [];
protected array $fields = [];
@ -39,7 +40,7 @@ class CrudConfiguration
return self::$self;
}
/* -- */
// --
public function setPageTitle(string $page, string $title): self
{
@ -53,7 +54,7 @@ class CrudConfiguration
return $this->pageTitles[$page] ?? $default;
}
/* -- */
// --
public function setPageRoute(string $page, string $route): self
{
@ -79,9 +80,9 @@ class CrudConfiguration
return $this->pageRouteParams[$page] ?? [];
}
/* -- */
// --
public function setForm(string $context, string $form, array $options = []): self
public function setForm(string $context, string $form): self
{
$this->forms[$context] = $form;
@ -105,7 +106,7 @@ class CrudConfiguration
return $this->formOptions[$context] ?? [];
}
/* -- */
// --
public function setAction(string $page, string $action, bool|callable $enabled): self
{
@ -134,8 +135,24 @@ class CrudConfiguration
);
}
public function setBatchAction(string $page, string $action, string $label, callable $callback): self
{
public function setGlobalBatchAction(
string $page,
string $action,
string $label,
callable $callback
): self {
$this->setBatchAction($page, $action, $label, $callback);
$this->batchActions[$page][$action]['isGlobal'] = true;
return $this;
}
public function setBatchAction(
string $page,
string $action,
string $label,
callable $callback
): self {
if (!isset($this->batchActions[$page])) {
$this->batchActions[$page] = [];
}
@ -143,6 +160,7 @@ class CrudConfiguration
$this->batchActions[$page][$action] = [
'label' => $label,
'callback' => $callback,
'isGlobal' => false,
];
return $this;
@ -163,7 +181,7 @@ class CrudConfiguration
return !empty($this->batchActions[$page]);
}
/* -- */
// --
public function setActionTitle(string $page, string $action, string $title): self
{
@ -181,7 +199,7 @@ class CrudConfiguration
return $this->actionTitles[$page][$action] ?? $default;
}
/* -- */
// --
public function setView(string $context, string $view): self
{
@ -229,7 +247,7 @@ class CrudConfiguration
return $this->viewDatas[$context][$name] ?? $defaultValue;
}
/* -- */
// --
public function setField(string $context, string $label, string $field, array $options): self
{
@ -250,9 +268,16 @@ class CrudConfiguration
return $this->fields[$context] ?? [];
}
/* -- */
public function setFields(string $context, array $fields): self
{
$this->fields[$context] = $fields;
public function setMaxPerPage(string $page, int $max)
return $this;
}
// --
public function setMaxPerPage(string $page, int $max): self
{
$this->maxPerPage[$page] = $max;
@ -264,7 +289,21 @@ class CrudConfiguration
return $this->maxPerPage[$page] ?? $default;
}
/* -- */
// --
public function setDoubleClick(string $page, bool $enabled): self
{
$this->doubleClick[$page] = $enabled;
return $this;
}
public function getDoubleClick(string $page): bool
{
return $this->doubleClick[$page] ?? false;
}
// --
public function setI18n(array $locales, string $defaultLocale): self
{
@ -289,7 +328,7 @@ class CrudConfiguration
return !empty($this->locales);
}
/* -- */
// --
public function setDefaultSort(string $context, string $label, string $direction = 'asc'): self
{

View file

@ -0,0 +1,32 @@
<?php
namespace App\Core\Crud\Field;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* class BooleanField.
*
* @author Simon Vieille <simon@deblan.fr>
*/
class BooleanField extends Field
{
public function configureOptions(OptionsResolver $resolver): OptionsResolver
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'view' => '@Core/admin/crud/field/boolean.html.twig',
'display' => 'toggle',
'checkbox_class_when_true' => 'fa-check-square',
'checkbox_class_when_false' => 'fa-square',
'toggle_class_when_true' => 'bg-success',
'toggle_class_when_false' => 'bg-secondary',
'default_value' => false,
]);
$resolver->setAllowedTypes('display', 'string');
return $resolver;
}
}

View file

@ -28,24 +28,31 @@ abstract class Field
$resolver->setDefaults([
'property' => null,
'property_builder' => null,
'default_value' => null,
'view' => null,
'action' => null,
'raw' => false,
'sort' => null,
'href' => null,
'href_attr' => [],
'attr' => [],
'inline_form' => null,
'inline_form_validation' => null,
]);
$resolver->setRequired('view');
$resolver->setAllowedTypes('property', ['null', 'string']);
$resolver->setAllowedTypes('view', 'string');
$resolver->setAllowedTypes('action', ['null', 'string']);
$resolver->setAllowedTypes('attr', 'array');
$resolver->setAllowedTypes('href', ['null', 'string', 'callable']);
$resolver->setAllowedTypes('href_attr', 'array', 'callable');
$resolver->setAllowedTypes('inline_form', ['null', 'callable']);
$resolver->setAllowedTypes('inline_form_validation', ['null', 'callable']);
$resolver->setAllowedTypes('href_attr', ['array', 'callable']);
$resolver->setAllowedTypes('raw', 'boolean');
$resolver->setAllowedTypes('property_builder', ['null', 'callable']);
$resolver->setAllowedValues('sort', function($value) {
if ($value === null) {
$resolver->setAllowedValues('sort', function ($value) {
if (null === $value) {
return true;
}

View file

@ -15,6 +15,7 @@ class Configuration implements ConfigurationInterface
'image/jpeg',
'image/gif',
'image/svg+xml',
'image/webp',
'video/mp4',
'audio/mpeg3',
'audio/x-mpeg-3',
@ -45,101 +46,100 @@ class Configuration implements ConfigurationInterface
$treeBuilder->getRootNode()
->children()
->arrayNode('site')
->children()
->scalarNode('name')
->defaultValue('Murph')
->isRequired()
->cannotBeEmpty()
->end()
->scalarNode('logo')
->defaultValue('build/images/core/logo.svg')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('controllers')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('action')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->arrayNode('security')
->children()
->arrayNode('roles')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('role')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('pages')
->prototype('array')
->children()
->scalarNode('name')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('templates')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('file')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('editor_js')
->children()
->arrayNode('blocks')
->scalarPrototype()
->end()
->end()
->end()
->end()
->arrayNode('file_manager')
->children()
->arrayNode('mimes')
->scalarPrototype()
->end()
->defaultValue($defaultMimetypes)
->end()
->scalarNode('path')
->defaultValue('%kernel.project_dir%/public/uploads')
->cannotBeEmpty()
->end()
->scalarNode('path_uri')
->defaultValue('/uploads')
->cannotBeEmpty()
->end()
->arrayNode('path_locked')
->scalarPrototype()
->end()
->defaultValue($defaultLocked)
->end()
->end()
->end()
->end();
->arrayNode('site')
->children()
->scalarNode('name')
->defaultValue('Murph')
->isRequired()
->end()
->scalarNode('logo')
->defaultValue('build/images/core/logo.svg')
->isRequired()
->end()
->arrayNode('controllers')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('action')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->arrayNode('security')
->children()
->arrayNode('roles')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('role')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('pages')
->prototype('array')
->children()
->scalarNode('name')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('templates')
->prototype('array')
->children()
->scalarNode('name')
->cannotBeEmpty()
->end()
->scalarNode('file')
->cannotBeEmpty()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->end()
->arrayNode('editor_js')
->children()
->arrayNode('blocks')
->scalarPrototype()
->end()
->end()
->end()
->end()
->arrayNode('file_manager')
->children()
->arrayNode('mimes')
->scalarPrototype()
->end()
->defaultValue($defaultMimetypes)
->end()
->scalarNode('path')
->defaultValue('%kernel.project_dir%/public/uploads')
->cannotBeEmpty()
->end()
->scalarNode('path_uri')
->defaultValue('/uploads')
->cannotBeEmpty()
->end()
->arrayNode('path_locked')
->scalarPrototype()
->end()
->defaultValue($defaultLocked)
->end()
->end()
->end()
->end()
;
return $treeBuilder;
}

View file

@ -2,10 +2,9 @@
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'analytic_referer')]
#[ORM\Entity(repositoryClass: ViewRepository::class)]

View file

@ -2,10 +2,9 @@
namespace App\Core\Entity\Analytic;
use App\Core\Entity\Site\Node;
use App\Repository\Entity\Analytic\NodeViewRepository;
use Doctrine\ORM\Mapping as ORM;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'analytic_view')]
#[ORM\Entity(repositoryClass: ViewRepository::class)]

View file

@ -42,6 +42,9 @@ class Navigation implements EntityInterface
#[ORM\Column(type: 'string', length: 10)]
protected $locale = 'en';
#[ORM\Column(type: 'string', length: 7, nullable: true)]
protected $color;
#[ORM\Column(type: 'integer', nullable: true)]
protected $sortOrder;
@ -64,7 +67,7 @@ class Navigation implements EntityInterface
return $this->label;
}
public function setLabel(string $label): self
public function setLabel(?string $label): self
{
$this->label = $label;
@ -237,4 +240,16 @@ class Navigation implements EntityInterface
return false;
}
public function setColor(string $color): self
{
$this->color = $color;
return $this;
}
public function getColor(): ?string
{
return $this->color;
}
}

View file

@ -5,13 +5,12 @@ namespace App\Core\Entity\Site\Page;
use App\Core\Doctrine\Timestampable;
use App\Core\Entity\EntityInterface;
use App\Core\Entity\Site\Node;
use App\Core\File\FileAttribute;
use App\Core\Repository\Site\Page\PageRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\File\File;
use App\Core\File\FileAttribute;
#[ORM\Entity(repositoryClass: PageRepository::class)]
#[ORM\DiscriminatorColumn(name: 'class_key', type: 'string')]
@ -89,7 +88,7 @@ class Page implements EntityInterface
}
/**
* @return Collection|Block[]
* @return Block[]|Collection
*/
public function getBlocks(): Collection
{

View file

@ -15,11 +15,8 @@ class AbTestEvent extends Event
public const INIT_EVENT = 'ab_test.init';
public const RUN_EVENT = 'ab_test.run';
protected AbTest $test;
public function __construct(AbTest $test)
public function __construct(protected AbTest $test)
{
$this->test = $test;
}
public function getTest(): AbTest

View file

@ -12,13 +12,10 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class PasswordRequestEvent extends Event
{
const EVENT = 'account_event.password_request';
public const EVENT = 'account_event.password_request';
protected User $user;
public function __construct(User $user)
public function __construct(protected User $user)
{
$this->user = $user;
}
public function getUser(): User

View file

@ -12,18 +12,15 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class EntityManagerEvent extends Event
{
const CREATE_EVENT = 'entity_manager_event.create';
const UPDATE_EVENT = 'entity_manager_event.update';
const DELETE_EVENT = 'entity_manager_event.delete';
const PRE_CREATE_EVENT = 'entity_manager_event.pre_create';
const PRE_UPDATE_EVENT = 'entity_manager_event.pre_update';
const PRE_DELETE_EVENT = 'entity_manager_event.pre_delete';
public const CREATE_EVENT = 'entity_manager_event.create';
public const UPDATE_EVENT = 'entity_manager_event.update';
public const DELETE_EVENT = 'entity_manager_event.delete';
public const PRE_CREATE_EVENT = 'entity_manager_event.pre_create';
public const PRE_UPDATE_EVENT = 'entity_manager_event.pre_update';
public const PRE_DELETE_EVENT = 'entity_manager_event.pre_delete';
protected EntityInterface $entity;
public function __construct(EntityInterface $entity)
public function __construct(protected EntityInterface $entity)
{
$this->entity = $entity;
}
public function getEntity(): EntityInterface

View file

@ -2,8 +2,8 @@
namespace App\Core\Event\Page;
use Symfony\Contracts\EventDispatcher\Event;
use App\Core\Entity\Site\Page\Page;
use Symfony\Contracts\EventDispatcher\Event;
/**
* class PageEditEvent.
@ -12,14 +12,12 @@ use App\Core\Entity\Site\Page\Page;
*/
class PageEditEvent extends Event
{
const FORM_INIT_EVENT = 'page_edit_event.form_init';
public const FORM_INIT_EVENT = 'page_edit_event.form_init';
protected Page $page;
protected array $pageBuilderOptions = [];
public function __construct(Page $page)
public function __construct(protected Page $page)
{
$this->page = $page;
}
public function getPage()

View file

@ -11,14 +11,11 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class NavigationSettingEvent extends Event
{
const INIT_EVENT = 'navigation_setting_event.init';
const FORM_INIT_EVENT = 'navigation_setting_event.form_init';
public const INIT_EVENT = 'navigation_setting_event.init';
public const FORM_INIT_EVENT = 'navigation_setting_event.form_init';
protected $data;
public function __construct($data = null)
public function __construct(protected $data = null)
{
$this->data = $data;
}
public function getData()

View file

@ -11,14 +11,11 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class SettingEvent extends Event
{
const INIT_EVENT = 'setting_event.init';
const FORM_INIT_EVENT = 'setting_event.form_init';
public const INIT_EVENT = 'setting_event.init';
public const FORM_INIT_EVENT = 'setting_event.form_init';
protected $data;
public function __construct($data = null)
public function __construct(protected $data = null)
{
$this->data = $data;
}
public function getData()

View file

@ -11,7 +11,7 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class TaskInitEvent extends Event
{
const INIT_EVENT = 'task_event.init';
public const INIT_EVENT = 'task_event.init';
protected array $tasks = [];

View file

@ -14,17 +14,13 @@ use Symfony\Contracts\EventDispatcher\Event;
*/
class TaskRunRequestedEvent extends Event
{
const RUN_REQUEST_EVENT = 'task_event.run_request';
public const RUN_REQUEST_EVENT = 'task_event.run_request';
protected string $task;
protected InputBag $parameters;
protected BufferedOutput $output;
public function __construct(string $task, InputBag $parameters, BufferedOutput $output)
{
$this->task = $task;
$this->parameters = $parameters;
$this->output = $output;
public function __construct(
protected string $task,
protected InputBag $parameters,
protected BufferedOutput $output
) {
}
public function getTask(): string

View file

@ -6,7 +6,6 @@ use App\Core\Ab\AbContainer;
use App\Core\Ab\AbTest;
use App\Core\Entity\Site\Node;
use App\Core\Event\Ab\AbTestEvent;
use App\Core\Repository\Site\NodeRepository;
use App\Core\Site\SiteRequest;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\HttpFoundation\Cookie;
@ -21,19 +20,13 @@ use Symfony\Component\HttpKernel\Event\ResponseEvent;
*/
class AbListener
{
protected EventDispatcherInterface $eventDispatcher;
protected AbContainer $container;
protected SiteRequest $siteRequest;
protected ?Node $node;
public function __construct(
AbContainer $container,
EventDispatcherInterface $eventDispatcher,
SiteRequest $siteRequest
protected AbContainer $container,
protected EventDispatcherInterface $eventDispatcher,
protected SiteRequest $siteRequest
) {
$this->eventDispatcher = $eventDispatcher;
$this->container = $container;
$this->siteRequest = $siteRequest;
}
public function onKernelRequest(RequestEvent $event)
@ -70,6 +63,16 @@ class AbListener
}
}
public function onKernelResponse(ResponseEvent $event)
{
$cookies = $event->getRequest()->attributes->get('ab_test_cookies', []);
foreach ($cookies as $name => $value) {
$cookie = Cookie::create($name, $value['value'], time() + $value['duration']);
$event->getResponse()->headers->setCookie($cookie);
}
}
protected function getCookieName(): string
{
return 'ab_test_'.$this->getAbTestCode();
@ -96,14 +99,4 @@ class AbListener
return true;
}
public function onKernelResponse(ResponseEvent $event)
{
$cookies = $event->getRequest()->attributes->get('ab_test_cookies', []);
foreach ($cookies as $name => $value) {
$cookie = Cookie::create($name, $value['value'], time() + $value['duration']);
$event->getResponse()->headers->setCookie($cookie);
}
}
}

View file

@ -23,30 +23,18 @@ use Symfony\Component\HttpKernel\Event\RequestEvent;
*/
class AnalyticListener
{
protected NodeRepository $nodeRepository;
protected ViewRepositoryQuery $viewRepositoryQuery;
protected ViewFactory $viewFactory;
protected RefererRepositoryQuery $refererRepositoryQuery;
protected RefererFactory $refererFactory;
protected EntityManager $manager;
protected DeviceDetector $deviceDetector;
protected Request $request;
protected Node $node;
public function __construct(
NodeRepository $nodeRepository,
ViewRepositoryQuery $viewRepositoryQuery,
ViewFactory $viewFactory,
RefererRepositoryQuery $refererRepositoryQuery,
RefererFactory $refererFactory,
EntityManager $manager
protected NodeRepository $nodeRepository,
protected ViewRepositoryQuery $viewRepositoryQuery,
protected ViewFactory $viewFactory,
protected RefererRepositoryQuery $refererRepositoryQuery,
protected RefererFactory $refererFactory,
protected EntityManager $manager
) {
$this->nodeRepository = $nodeRepository;
$this->viewRepositoryQuery = $viewRepositoryQuery;
$this->viewFactory = $viewFactory;
$this->refererRepositoryQuery = $refererRepositoryQuery;
$this->refererFactory = $refererFactory;
$this->manager = $manager;
$this->createDeviceDetector();
}

View file

@ -3,11 +3,10 @@
namespace App\Core\EventListener;
use App\Core\Repository\RedirectRepositoryQuery;
use App\Core\Router\RedirectBuilder;
use App\Core\Router\RedirectMatcher;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use App\Core\Router\RedirectBuilder;
/**
* class RedirectListener.
@ -16,15 +15,11 @@ use App\Core\Router\RedirectBuilder;
*/
class RedirectListener
{
protected RedirectMatcher $matcher;
protected RedirectBuilder $builder;
protected RedirectRepositoryQuery $repository;
public function __construct(RedirectMatcher $matcher, RedirectBuilder $builder, RedirectRepositoryQuery $repository)
{
$this->matcher = $matcher;
$this->builder = $builder;
$this->repository = $repository;
public function __construct(
protected RedirectMatcher $matcher,
protected RedirectBuilder $builder,
protected RedirectRepositoryQuery $repository
) {
}
public function onKernelException(ExceptionEvent $event)
@ -37,7 +32,7 @@ class RedirectListener
$redirects = $this->repository
->orderBy('.sortOrder')
->where('.isEnabled=1')
->where('.isEnabled=true')
->find()
;

View file

@ -17,24 +17,13 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class PasswordRequestEventSubscriber implements EventSubscriberInterface
{
protected MailNotifier $notifier;
protected UrlGeneratorInterface $urlGenerator;
protected EntityManager $entityManager;
protected TokenGeneratorInterface $tokenGenerator;
protected TranslatorInterface $translator;
public function __construct(
MailNotifier $notifier,
UrlGeneratorInterface $urlGenerator,
EntityManager $entityManager,
TokenGeneratorInterface $tokenGenerator,
TranslatorInterface $translator
protected MailNotifier $notifier,
protected UrlGeneratorInterface $urlGenerator,
protected EntityManager $entityManager,
protected TokenGeneratorInterface $tokenGenerator,
protected TranslatorInterface $translator
) {
$this->notifier = $notifier;
$this->urlGenerator = $urlGenerator;
$this->entityManager = $entityManager;
$this->tokenGenerator = $tokenGenerator;
$this->translator = $translator;
}
public static function getSubscribedEvents()

View file

@ -16,12 +16,12 @@ use Symfony\Component\Security\Core\Exception\AccessDeniedException;
*/
class RequestSecurityEventSubscriber implements EventSubscriberInterface
{
protected NodeRepository $nodeRepository;
protected AuthorizationChecker $authorizationChecker;
public function __construct(NodeRepository $nodeRepository, ContainerInterface $container)
{
$this->nodeRepository = $nodeRepository;
public function __construct(
protected NodeRepository $nodeRepository,
ContainerInterface $container
) {
$this->authorizationChecker = $container->get('security.authorization_checker');
}

View file

@ -4,18 +4,15 @@ namespace App\Core\EventSubscriber\Site;
use App\Core\Site\SiteRequest;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use function Symfony\Component\String\u;
class ForcedDomainEventSubscriber implements EventSubscriberInterface
{
protected SiteRequest $siteRequest;
public function __construct(SiteRequest $siteRequest)
public function __construct(protected SiteRequest $siteRequest)
{
$this->siteRequest = $siteRequest;
}
public function onKernelResponse(ResponseEvent $event)
@ -38,7 +35,8 @@ class ForcedDomainEventSubscriber implements EventSubscriberInterface
->replace(
'://'.$this->siteRequest->getDomain(),
'://'.$navigation->getDomain()
);
)
;
$event->getResponse()->headers->set('Location', $uri);
$event->getResponse()->setStatusCode(Response::HTTP_MOVED_PERMANENTLY);

View file

@ -20,27 +20,14 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class MenuEventSubscriber extends EntityManagerEventSubscriber
{
protected NodeFactory $nodeFactory;
protected NodeRepository $nodeRepository;
protected EntityManager $entityManager;
protected CodeSlugify $slugify;
protected SymfonyCacheManager $cacheManager;
protected TranslatorInterface $translation;
public function __construct(
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
CodeSlugify $slugify,
SymfonyCacheManager $cacheManager,
TranslatorInterface $translator
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected CodeSlugify $slugify,
protected SymfonyCacheManager $cacheManager,
protected TranslatorInterface $translator
) {
$this->nodeFactory = $nodeFactory;
$this->nodeRepository = $nodeRepository;
$this->entityManager = $entityManager;
$this->slugify = $slugify;
$this->cacheManager = $cacheManager;
$this->translator = $translator;
}
public function supports(EntityInterface $entity): bool

View file

@ -17,11 +17,9 @@ use App\Core\Slugify\CodeSlugify;
class NavigationEventSubscriber extends EntityManagerEventSubscriber
{
public function __construct(
EntityManager $entityManager,
CodeSlugify $slugify
protected EntityManager $entityManager,
protected CodeSlugify $slugify
) {
$this->entityManager = $entityManager;
$this->slugify = $slugify;
}
public function supports(EntityInterface $entity): bool

View file

@ -12,7 +12,6 @@ use App\Core\Repository\Site\NodeRepository;
use App\Core\Slugify\CodeSlugify;
use App\Core\Slugify\RouteParameterSlugify;
use App\Core\Slugify\Slugify;
use Symfony\Component\HttpKernel\KernelInterface;
use function Symfony\Component\String\u;
/**
@ -22,27 +21,14 @@ use function Symfony\Component\String\u;
*/
class NodeEventSubscriber extends EntityManagerEventSubscriber
{
protected NodeFactory $nodeFactory;
protected EntityManager $entityManager;
protected KernelInterface $kernel;
protected Slugify $slugify;
protected CodeSlugify $codeSlugify;
protected RouteParameterSlugify $routeParameterSlugify;
public function __construct(
NodeFactory $nodeFactory,
NodeRepository $nodeRepository,
EntityManager $entityManager,
Slugify $slugify,
CodeSlugify $codeSlugify,
RouteParameterSlugify $routeParameterSlugify
protected NodeFactory $nodeFactory,
protected NodeRepository $nodeRepository,
protected EntityManager $entityManager,
protected Slugify $slugify,
protected CodeSlugify $codeSlugify,
protected RouteParameterSlugify $routeParameterSlugify
) {
$this->nodeFactory = $nodeFactory;
$this->nodeRepository = $nodeRepository;
$this->entityManager = $entityManager;
$this->slugify = $slugify;
$this->codeSlugify = $codeSlugify;
$this->routeParameterSlugify = $routeParameterSlugify;
}
public function supports(EntityInterface $entity): bool
@ -63,7 +49,7 @@ class NodeEventSubscriber extends EntityManagerEventSubscriber
$node = $event->getEntity();
$node->setCode($this->codeSlugify->slugify($node->getCode()));
$node->setCode($this->codeSlugify->slugify($node->getCode() ?? ''));
if ($node->getDisableUrl()) {
$node->setUrl(null);

View file

@ -17,11 +17,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class BlockEventSubscriber extends EntityManagerEventSubscriber
{
protected FileUploadHandler $fileUpload;
public function __construct(FileUploadHandler $fileUpload)
public function __construct(protected FileUploadHandler $fileUpload)
{
$this->fileUpload = $fileUpload;
}
public function supports(EntityInterface $entity): bool

View file

@ -16,11 +16,8 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class PageEventSubscriber extends EntityManagerEventSubscriber
{
protected FileUploadHandler $fileUpload;
public function __construct(FileUploadHandler $fileUpload)
public function __construct(protected FileUploadHandler $fileUpload)
{
$this->fileUpload = $fileUpload;
}
public function supports(EntityInterface $entity): bool

View file

@ -18,13 +18,10 @@ use Symfony\Component\HttpKernel\KernelInterface;
*/
class SiteEventSubscriber extends EntityManagerEventSubscriber
{
protected KernelInterface $kernel;
protected SymfonyCacheManager $cacheManager;
public function __construct(KernelInterface $kernel, SymfonyCacheManager $cacheManager)
{
$this->kernel = $kernel;
$this->cacheManager = $cacheManager;
public function __construct(
protected KernelInterface $kernel,
protected SymfonyCacheManager $cacheManager
) {
}
public function supports(EntityInterface $entity): bool

View file

@ -13,11 +13,8 @@ use App\Core\Event\Task\TaskRunRequestedEvent;
*/
class CacheCleanTaskEventSubscriber extends TaskEventSubscriber
{
protected SymfonyCacheManager $cacheManager;
public function __construct(SymfonyCacheManager $cacheManager)
public function __construct(protected SymfonyCacheManager $cacheManager)
{
$this->cacheManager = $cacheManager;
}
public function onInit(TaskInitEvent $event)

View file

@ -2,7 +2,6 @@
namespace App\Core\Factory;
use App\Core\Factory\FactoryInterface;
use App\Core\Entity\Redirect as Entity;
class RedirectFactory implements FactoryInterface

View file

@ -23,22 +23,15 @@ class FsFileManager
protected string $path;
protected string $pathUri;
protected array $pathLocked;
protected FileUploadHandler $uploadHandler;
protected FileInformationFactory $fileInformationFactory;
protected FileInformationRepositoryQuery $fileInformationRepositoryQuery;
public function __construct(
ParameterBagInterface $params,
FileUploadHandler $uploadHandler,
FileInformationFactory $fileInformationFactory,
FileInformationRepositoryQuery $fileInformationRepositoryQuery
protected FileUploadHandler $uploadHandler,
protected FileInformationFactory $fileInformationFactory,
protected FileInformationRepositoryQuery $fileInformationRepositoryQuery
) {
$config = $params->get('core')['file_manager'];
$this->uploadHandler = $uploadHandler;
$this->fileInformationFactory = $fileInformationFactory;
$this->fileInformationRepositoryQuery = $fileInformationRepositoryQuery;
$this->mimes = $config['mimes'];
$this->path = $config['path'];
$this->pathUri = $this->normalizePath($config['path_uri']);
@ -91,12 +84,16 @@ class FsFileManager
$this->applySort($finder, $options['sort'] ?? 'name', $options['sort_direction'] ?? 'asc');
foreach ($finder as $file) {
$splInfo = $this->getSplInfo($directory.'/'.$file->getBasename());
$data['files'][] = [
'basename' => $file->getBasename(),
'path' => $directory,
'webPath' => $this->pathUri.'/'.$directory.'/'.$file->getBasename(),
'locked' => $this->isLocked($directory.'/'.$file->getBasename()),
'mime' => mime_content_type($file->getRealPath()),
'size' => $splInfo ? $splInfo->getSize() : null,
'updated_at' => $splInfo ? date('Y-m-d H:i', $splInfo->getMTime()) : null,
];
}
@ -297,14 +294,16 @@ class FsFileManager
protected function applySort(Finder $finder, string $sort, string $direction)
{
if ('name' === $sort) {
$finder->sortByName();
} elseif ('modification_date' === $sort) {
$finder->sortByModifiedTime();
}
$sorts = [
'name' => 'sortByName',
'type' => 'sortByType',
'updated_at' => 'sortByModifiedTime',
];
if ('desc' === $direction) {
$finder->reverseSorting();
$finder->{$sorts[$sort]}()->reverseSorting();
} else {
$finder->{$sorts[$sort]}();
}
}

View file

@ -6,7 +6,6 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
class FilePickerType extends AbstractType
{

View file

@ -11,6 +11,15 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
*/
class FileUploadHandler
{
protected $filenameGenerator;
public function setFilenameGenerator(callable $filenameGenerator): self
{
$this->filenameGenerator = $filenameGenerator;
return $this;
}
public function handleForm(?UploadedFile $uploadedFile, string $path, ?callable $afterUploadCallback = null, bool $keepOriginalFilename = false): void
{
if (null === $uploadedFile) {
@ -21,9 +30,11 @@ class FileUploadHandler
if ($keepOriginalFilename) {
$filename = $originalFilename.'.'.$uploadedFile->guessExtension();
} else {
} elseif (!is_callable($this->filenameGenerator)) {
$safeFilename = transliterator_transliterate('Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename);
$filename = date('Ymd-his').$safeFilename.'.'.$uploadedFile->guessExtension();
} else {
$filename = call_user_func($this->filenameGenerator, $uploadedFile);
}
$uploadedFile->move($path, $filename);

View file

@ -2,13 +2,11 @@
namespace App\Core\Form\Filter;
use App\Core\Entity\Redirect;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
class RedirectFilterType extends AbstractType
{

View file

@ -4,12 +4,12 @@ namespace App\Core\Form;
use App\Core\Entity\Redirect;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class RedirectType extends AbstractType
{

View file

@ -7,6 +7,7 @@ use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class MenuType extends AbstractType
@ -23,6 +24,7 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -37,6 +39,7 @@ class MenuType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);

View file

@ -7,6 +7,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class NavigationAdditionalDomainType extends AbstractType
@ -24,6 +25,7 @@ class NavigationAdditionalDomainType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);

View file

@ -6,6 +6,7 @@ use App\Core\Entity\Site\Navigation;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ColorType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
@ -26,6 +27,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -38,6 +40,21 @@ class NavigationType extends AbstractType
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
$builder->add(
'color',
ColorType::class,
[
'label' => 'Color',
'required' => true,
'attr' => [
],
'constraints' => [
new NotBlank(),
],
@ -54,6 +71,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -94,7 +112,7 @@ class NavigationType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(['min' => 2, 'max' => 10]),
new Length(min: 2, max: 10),
],
]
);

View file

@ -13,6 +13,7 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class NodeType extends AbstractType
@ -29,6 +30,7 @@ class NodeType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -43,6 +45,7 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -82,6 +85,7 @@ class NodeType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -116,6 +120,9 @@ class NodeType extends AbstractType
return $choices;
}),
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -2,11 +2,11 @@
namespace App\Core\Form\Site\Page;
use App\Core\Entity\Site\Page\Block;
use App\Core\Form\FileManager\FilePickerType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\AbstractType;
use App\Core\Entity\Site\Page\Block;
class FilePickerBlockType extends AbstractType
{

View file

@ -6,10 +6,10 @@ use App\Core\Entity\Site\Navigation;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class PageFilterType extends AbstractType
{

View file

@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Image;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
class PageType extends AbstractType
@ -26,6 +27,7 @@ class PageType extends AbstractType
],
'constraints' => [
new NotBlank(),
new Length(max: 255),
],
]
);
@ -39,6 +41,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -52,6 +55,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -65,6 +69,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);
@ -78,6 +83,7 @@ class PageType extends AbstractType
'attr' => [
],
'constraints' => [
new Length(max: 255),
],
]
);

View file

@ -20,6 +20,10 @@ class CollectionType extends BaseCollectionType
'collection_name' => $options['collection_name'],
'label_add' => $options['label_add'],
'label_delete' => $options['label_delete'],
'allow_add' => $options['allow_add'],
'allow_delete' => $options['allow_delete'],
'template_before_item' => $options['template_before_item'],
'template_after_item' => $options['template_after_item'],
]);
}
@ -31,6 +35,8 @@ class CollectionType extends BaseCollectionType
'collection_name' => '',
'label_add' => 'Add',
'label_delete' => 'Delete',
'template_before_item' => null,
'template_after_item' => null,
]);
}

View file

@ -11,6 +11,7 @@ use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Filesystem\Filesystem;
use function Symfony\Component\String\u;
class MakeCrudController extends AbstractMaker
@ -103,6 +104,17 @@ class MakeCrudController extends AbstractMaker
$options
);
$views = ['_form.html.twig', '_show.html.twig'];
$directory = sprintf('templates/admin/%s_admin/', $options['route']);
$filesystem = new Filesystem();
$filesystem->mkdir($directory);
foreach ($views as $view) {
$filesystem->dumpFile(
$directory.$view,
sprintf("{{ include('@Core/admin/crud/%s') }}\n", $view)
);
}
$generator->writeChanges();
$this->writeSuccessMessage($io);

View file

@ -11,7 +11,6 @@ use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use function Symfony\Component\String\u;
class MakeFactory extends AbstractMaker
{

View file

@ -8,11 +8,11 @@ use Symfony\Bundle\MakerBundle\DependencyBuilder;
use Symfony\Bundle\MakerBundle\Generator;
use Symfony\Bundle\MakerBundle\InputConfiguration;
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Filesystem\Filesystem;
class MakePage extends AbstractMaker
@ -92,7 +92,8 @@ class MakePage extends AbstractMaker
$this->writeSuccessMessage($io);
$io->text('Register the page in <comment>config/packages/app.yaml</comment>: ');
$io->text(<<< EOF
$io->text(
<<< EOF
core:
site:
@ -100,10 +101,18 @@ core:
{$pageClassNameDetails->getFullName()}:
name: {$pageClassNameDetails->getShortName()}
templates:
- {name: "Default", file: "${templatePath}"}
- {name: "Default", file: "{$templatePath}"}
EOF
);
);
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
private function askForNextBlock(ConsoleStyle $io, array $blocks, bool $isFirstField)
@ -138,7 +147,7 @@ EOF
$types = [
'text' => null,
'textarea' => null,
'choice' => null,
'choice' => 'BlockEntity\\ChoiceBlock::class',
'collection' => 'BlockEntity\\CollectionBlock::class',
'editor_js_textarea' => null,
'file' => 'BlockEntity\\FileBlock::class',
@ -183,12 +192,4 @@ EOF
$io->writeln(sprintf(' * <comment>%s</comment>', $type));
}
}
public function configureDependencies(DependencyBuilder $dependencies)
{
$dependencies->addClassDependency(
Annotation::class,
'doctrine/annotations'
);
}
}

View file

@ -4,7 +4,6 @@ namespace App\Core\Manager;
use App\Core\Entity\EntityInterface;
use App\Core\Event\EntityManager\EntityManagerEvent;
use Doctrine\ORM\EntityManager as DoctrineEntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@ -15,23 +14,19 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface;
*/
class EntityManager
{
protected EventDispatcherInterface $eventDispatcher;
protected DoctrineEntityManager $entityManager;
public function __construct(EventDispatcherInterface $eventDispatcher, EntityManagerInterface $entityManager)
{
$this->eventDispatcher = $eventDispatcher;
$this->entityManager = $entityManager;
public function __construct(
protected EventDispatcherInterface $eventDispatcher,
protected EntityManagerInterface $entityManager
) {
}
public function create(EntityInterface $entity, bool $dispatchEvent = true): self
public function create(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_CREATE_EVENT);
}
$this->persist($entity);
$this->persist($entity, $flush);
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::CREATE_EVENT);
@ -40,13 +35,13 @@ class EntityManager
return $this;
}
public function update(EntityInterface $entity, bool $dispatchEvent = true): self
public function update(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_UPDATE_EVENT);
}
$this->persist($entity);
$this->persist($entity, $flush);
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::UPDATE_EVENT);
@ -55,14 +50,17 @@ class EntityManager
return $this;
}
public function delete(EntityInterface $entity, bool $dispatchEvent = true): self
public function delete(EntityInterface $entity, bool $dispatchEvent = true, bool $flush = true): self
{
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::PRE_DELETE_EVENT);
}
$this->entityManager->remove($entity);
$this->flush();
if ($flush) {
$this->flush();
}
if ($dispatchEvent) {
$this->eventDispatcher->dispatch(new EntityManagerEvent($entity), EntityManagerEvent::DELETE_EVENT);
@ -90,9 +88,12 @@ class EntityManager
return $this->entityManager;
}
protected function persist(EntityInterface $entity)
protected function persist(EntityInterface $entity, bool $flush = true)
{
$this->entityManager->persist($entity);
$this->flush();
if ($flush) {
$this->flush();
}
}
}

View file

@ -11,10 +11,13 @@ use App\Core\Entity\EntityInterface;
*/
class TranslatableEntityManager extends EntityManager
{
protected function persist(EntityInterface $entity)
protected function persist(EntityInterface $entity, bool $flush = true)
{
$this->entityManager->persist($entity);
$this->entityManager->persist($entity, $flush);
$entity->mergeNewTranslations();
$this->flush();
if ($flush) {
$this->flush();
}
}
}

View file

@ -3,7 +3,7 @@
namespace App\Core;
if (!defined('MURPH_VERSION')) {
define('MURPH_VERSION', 'v1.17.1');
define('MURPH_VERSION', 'v1.24.1');
}
/**

View file

@ -2,10 +2,10 @@
namespace App\Core\Notification;
use App\Entity\User;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Twig\Environment as TwigEnvironment;
use App\Entity\User;
/**
* class MailNotifier.
@ -14,7 +14,6 @@ use App\Entity\User;
*/
class MailNotifier
{
protected MailerInterface $mailer;
protected array $attachments = [];
protected array $recipients = [];
protected array $bccRecipients = [];
@ -22,10 +21,8 @@ class MailNotifier
protected ?string $from = null;
protected ?string $replyTo = null;
public function __construct(TwigEnvironment $twig, MailerInterface $mailer)
public function __construct(protected MailerInterface $mailer)
{
$this->mailer = $mailer;
$this->twig = $twig;
}
public function setMailer(Swift_Mailer $mailer): self

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Referer|null find($id, $lockMode = null, $lockVersion = null)
* @method Referer|null findOneBy(array $criteria, array $orderBy = null)
* @method null|Referer find($id, $lockMode = null, $lockVersion = null)
* @method null|Referer findOneBy(array $criteria, array $orderBy = null)
* @method Referer[] findAll()
* @method Referer[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -3,9 +3,9 @@
namespace App\Core\Repository\Analytic;
use App\Core\Repository\Analytic\RefererRepository as Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class RefererRepositoryQuery extends RepositoryQuery
{

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method View|null find($id, $lockMode = null, $lockVersion = null)
* @method View|null findOneBy(array $criteria, array $orderBy = null)
* @method null|View find($id, $lockMode = null, $lockVersion = null)
* @method null|View findOneBy(array $criteria, array $orderBy = null)
* @method View[] findAll()
* @method View[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -3,9 +3,9 @@
namespace App\Core\Repository\Analytic;
use App\Core\Repository\Analytic\ViewRepository as Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Component\HttpFoundation\Request;
use App\Core\Repository\RepositoryQuery;
class ViewRepositoryQuery extends RepositoryQuery
{

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method FileInformation|null find($id, $lockMode = null, $lockVersion = null)
* @method FileInformation|null findOneBy(array $criteria, array $orderBy = null)
* @method null|FileInformation find($id, $lockMode = null, $lockVersion = null)
* @method null|FileInformation findOneBy(array $criteria, array $orderBy = null)
* @method FileInformation[] findAll()
* @method FileInformation[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method NavigationSetting|null find($id, $lockMode = null, $lockVersion = null)
* @method NavigationSetting|null findOneBy(array $criteria, array $orderBy = null)
* @method null|NavigationSetting find($id, $lockMode = null, $lockVersion = null)
* @method null|NavigationSetting findOneBy(array $criteria, array $orderBy = null)
* @method NavigationSetting[] findAll()
* @method NavigationSetting[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Redirect|null find($id, $lockMode = null, $lockVersion = null)
* @method Redirect|null findOneBy(array $criteria, array $orderBy = null)
* @method null|Redirect find($id, $lockMode = null, $lockVersion = null)
* @method null|Redirect findOneBy(array $criteria, array $orderBy = null)
* @method Redirect[] findAll()
* @method Redirect[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -2,9 +2,8 @@
namespace App\Core\Repository;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use App\Core\Repository\RedirectRepository as Repository;
use Knp\Component\Pager\PaginatorInterface;
class RedirectRepositoryQuery extends RepositoryQuery
{

View file

@ -98,6 +98,26 @@ abstract class RepositoryQuery
return $this;
}
public function count()
{
return $this
->select(sprintf('COUNT(%s.id) as total', $this->id))
->query
->getQuery()
->setMaxResults(1)
->getOneOrNullResult()['total']
;
}
protected function addForcedFilterHandler(string $name): self
{
if (!in_array($name, $this->forcedFilterHandlers)) {
$this->forcedFilterHandlers[] = $name;
}
return $this;
}
protected function populateDqlId(&$data)
{
if (is_string($data)) {

View file

@ -7,8 +7,8 @@ use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @method Setting|null find($id, $lockMode = null, $lockVersion = null)
* @method Setting|null findOneBy(array $criteria, array $orderBy = null)
* @method null|Setting find($id, $lockMode = null, $lockVersion = null)
* @method null|Setting findOneBy(array $criteria, array $orderBy = null)
* @method Setting[] findAll()
* @method Setting[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/

View file

@ -32,9 +32,9 @@ class NodeRepository extends NestedTreeRepository
;
}
return $query->getQuery()
return null !== $query->getQuery()
->setMaxResults(1)
->getOneOrNullResult() !== null
->getOneOrNullResult()
;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
import '../../../../../../../../assets/css/admin.scss';
require('../../../../../../../../node_modules/bootstrap/dist/js/bootstrap.min.js')
require('./modules/sidebar.js')()
require('./modules/table-fixed.js')()
require('./modules/form-confirm.js')()
require('./modules/form-file.js')()
@ -27,5 +28,4 @@ require('./modules/file-manager.js')()
require('./modules/file-picker.js')()
require('./modules/analytics.js')()
require('./modules/page.js')()
require('./modules/sidebar.js')()
require('./modules/node.js')()

View file

@ -19,7 +19,7 @@ Routing.setRoutingData(routes)
const map = {
'fa fa-file-pdf': ['application/pdf'],
'fa fa-file-image': ['image/png', 'image/jpg', 'image/jpeg', 'image/gif'],
'fa fa-file-image': ['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/webp'],
'fa fa-file-audio': ['application/ogg', 'audio/mp3', 'audio/mpeg', 'audio/wav'],
'fa fa-file-archive': ['application/zip', 'multipart/x-zip', 'application/rar', 'application/x-rar-compressed', 'application/x-zip-compressed', 'application/tar', 'application/x-tar'],
'fa fa-file-alt': ['application/rtf'],
@ -53,7 +53,7 @@ export default {
return
}
if (['image/png', 'image/jpg', 'image/jpeg', 'image/gif'].indexOf(this.mime) === -1) {
if (['image/png', 'image/jpg', 'image/jpeg', 'image/gif', 'image/webp'].indexOf(this.mime) === -1) {
this.thumbnail = null
return
}

View file

@ -25,7 +25,8 @@
<div class="breadcrumb mb-0 file-manager-views">
<select v-model="sort" class="form-control form-control-sm d-inline w-auto ml-1">
<option value="name">Name</option>
<option value="modification_date">Date</option>
<option value="updated_at">Date</option>
<option value="type">Type</option>
</select>
<select v-model="sortDirection" class="form-control form-control-sm d-inline w-auto ml-1">
<option value="asc">ASC</option>
@ -106,7 +107,7 @@
<td width="10">
<span class="fa fa-folder text-warning"></span>
</td>
<td>
<td colspan="2">
..
</td>
</tr>
@ -115,7 +116,7 @@
<td width="10">
<span class="fa fa-folder text-warning"></span>
</td>
<td>
<td colspan="2">
<div v-if="item.locked" class="float-right">
<span class="btn btn-sm btn-light">
<span class="fa fa-lock"></span>
@ -138,6 +139,12 @@
<span v-html="item.basename"></span>
</td>
<td class="text-right">
<span class="d-none d-sm-block">
<span class="btn btn-sm btn-light border-light mr-1 mb-1" v-text="readableFilesize(item.size)"></span>
<span class="btn btn-sm btn-light border-light mr-1 mb-1" v-text="item.updated_at"></span>
</span>
</td>
</tr>
</table>
</div>
@ -323,6 +330,26 @@ export default {
.catch((e) => {
alert('An error occured')
})
},
readableFilesize (size) {
if (size <= 0) {
return '0 KB'
}
if (size === 1) {
return '1 byte'
}
const mod = 1024
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
let i = 0
while (size > mod && i < units.length - 1) {
size /= mod
++i
}
return `${size.toFixed(2)} ${units[i]}`
}
},
mounted () {
@ -358,7 +385,7 @@ export default {
$(events).each((k, event) => {
body.on(event + '.success', () => {
$('div[id^=modal-container]').modal('hide')
$('div[id^=modal-container-]').modal('hide')
that.refresh()
})
})
@ -366,6 +393,10 @@ export default {
body.on('file_manager.info.update.success', () => {
$('*[data-modal="' + that.modalUrl + '"]').click()
})
body.on('file_manager.info.directory.request_open', () => {
$('*[data-modal="' + that.modalUrl + '"]').click()
})
},
watch: {
directory (directory) {

View file

@ -8,16 +8,28 @@ module.exports = () => {
const form = $('#form-batch')
form.submit((e) => {
e.preventDefault()
const select = document.querySelector('#form-batch-action')
const options = select.querySelectorAll('#form-batch-action option')
let doPrevent = true
const route = form.attr('action')
const datas = form.serialize()
options.forEach((option) => {
if (option.value === select.value && option.getAttribute('data-isglobal') === 'true') {
doPrevent = false
}
})
form.addClass('is-loading')
if (doPrevent) {
e.preventDefault()
$.post(route, datas)
.always(() => {
document.location.reload()
})
const route = form.attr('action')
const datas = form.serialize()
form.addClass('is-loading')
$.post(route, datas)
.always(() => {
document.location.reload()
})
}
})
}

View file

@ -1,8 +1,9 @@
const Choices = require('choices.js')
const $ = require('jquery')
module.exports = function () {
$('*[data-jschoice]').each(function (key, item) {
return new Choices(item)
module.exports = () => {
document.querySelectorAll('*[data-jschoice]').forEach((item) => {
return new Choices(item, {
searchFields: ['label'],
})
})
}

View file

@ -36,8 +36,9 @@ const fileManagerBrowser = function (callback) {
const clickCallback = (e) => {
callback($(e.target).attr('data-value'), {})
$('div[id^="modal-container-"]').modal('hide')
container.modal('hide')
$('#fm-modal').next('.modal-backdrop').remove()
$('#fm-modal').next('div[id^="modal-container-"]').modal('hide')
$('#fm-modal').modal('hide')
$('body').off('click', '#file-manager-insert', clickCallback)
}

View file

@ -1,9 +1,34 @@
const $ = require('jquery')
const SidebarOpener = () => {
$('.sidebar-toggler .btn').click(() => {
$('.sidebar').toggleClass('is-open')
const Sidebar = () => {
const menu = document.querySelector('.sidebar')
if (!menu) {
return
}
const stickyMenu = menu.querySelector('.sidebar-sticky')
const items = stickyMenu.querySelectorAll('a.nav-link')
const currentItem = menu.querySelector('.nav-link.active')
items.forEach((item) => {
item.addEventListener('click', () => {
localStorage.setItem('sidebar-item-top', stickyMenu.scrollTop)
})
})
const toggler = menu.querySelector('.sidebar-toggler .btn')
toggler.addEventListener('click', () => {
menu.classList.toggle('is-open')
})
if (currentItem) {
stickyMenu.scrollTo({
top: Math.min(currentItem.offsetTop, localStorage.getItem('sidebar-item-top') ?? 0),
behavior: 'smooth'
})
}
}
module.exports = SidebarOpener
module.exports = Sidebar

View file

@ -36,7 +36,7 @@ const fileManagerBrowser = function (callback) {
const clickCallback = (e) => {
callback($(e.target).attr('data-value'), {})
$('div[id^="modal-container"]').modal('hide')
$('div[id^="modal-container-"]').modal('hide')
container.modal('hide')
$('body').off('click', '#file-manager-insert', clickCallback)
@ -53,7 +53,7 @@ const fileManagerBrowser = function (callback) {
})
}
if (typeof window.tinymce !== 'undefined') {
const createTinymceConfig = function () {
window.tinymce.murph = window.tinymce.murph || {}
window.tinymce.murph.selector = window.tinymce.murph.selector || '*[data-tinymce]'
window.tinymce.murph.configurationBase = window.tinymce.murph.configurationBase || {
@ -80,6 +80,7 @@ if (typeof window.tinymce !== 'undefined') {
}
window.tinymce.murph.modes = window.tinymce.murph.modes || {}
window.tinymceModes = window.tinymceModes || {}
window.tinymce.murph.modes.default = window.tinymce.murph.modes.default || {
plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars link image code autoresize',
@ -95,6 +96,11 @@ if (typeof window.tinymce !== 'undefined') {
toolbar: 'undo redo | bold italic underline'
}
window.tinymce.murph.modes = {
...window.tinymce.murph.modes,
...window.tinymceModes
}
tinymce.addI18n('fr_FR', {
Redo: 'R\u00e9tablir',
Undo: 'Annuler',
@ -612,15 +618,20 @@ const doInitEditor = () => {
}
module.exports = function () {
if (typeof tinymce === 'undefined') {
return
}
const observer = new MutationObserver(doInitEditor)
const config = { attributes: false, childList: true, subtree: true }
observer.observe(document.querySelector('body'), config)
$(() => {
createTinymceConfig()
doInitEditor()
const observer = new MutationObserver(doInitEditor)
const config = { attributes: false, childList: true, subtree: true }
observer.observe(document.querySelector('body'), config)
$('body').on('hidden.bs.modal', '.modal', (e) => {
if (!$('.tox-dialog').length) {
$(e.target).find('.tox-tinymce').each(() => {
window.tinymce.remove($(this).prev().attr('id'))
})
}
})
})
}

View file

@ -1,68 +1,79 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Controller\Admin\Crud\CrudController;
use App\Core\Crud\CrudConfiguration;
use App\Core\Crud\Field;
use App\Core\Entity\EntityInterface;
use App\Core\Manager\EntityManager;
use <?= $entity ?> as Entity;
use <?= $factory ?> as Factory;
use <?= $form ?> as Type;
use <?= $repository_query ?> as RepositoryQuery;
use <?php echo $entity; ?> as Entity;
use <?php echo $factory; ?> as Factory;
use <?php echo $form; ?> as Type;
use <?php echo $repository_query; ?> as RepositoryQuery;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
class <?= $class_name; ?> extends CrudController
class <?php echo $class_name; ?> extends CrudController
{
#[Route(path: '/admin/user/edit/{entity}', name: 'admin_user_edit', methods: ['GET', 'POST'])]
protected ?CrudConfiguration $configuration = null;
#[Route(path: "/admin/<?= $route; ?>/{page}", name: "admin_<?= $route; ?>_index", methods: ['GET'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/{page}', name: 'admin_<?php echo $route; ?>_index', methods: ['GET'], requirements: ['page' => '\d+'])]
public function index(RepositoryQuery $query, Request $request, Session $session, int $page = 1): Response
{
return $this->doIndex($page, $query, $request, $session);
}
#[Route(path: "/admin/<?= $route; ?>/new", name: "admin_<?= $route; ?>_new", methods: ['GET', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/new', name: 'admin_<?php echo $route; ?>_new', methods: ['GET', 'POST'])]
public function new(Factory $factory, EntityManager $entityManager, Request $request): Response
{
return $this->doNew($factory->create(), $entityManager, $request);
}
#[Route(path: "/admin/<?= $route; ?>/show/{entity}", name: "admin_<?= $route; ?>_show", methods: ['GET'])]
#[Route(path: '/admin/<?php echo $route; ?>/show/{entity}', name: 'admin_<?php echo $route; ?>_show', methods: ['GET'])]
#[IsGranted('show', 'entity')]
public function show(Entity $entity): Response
{
return $this->doShow($entity);
}
#[Route(path: "/admin/<?= $route; ?>/filter", name: "admin_<?= $route; ?>_filter", methods: ['GET'])]
#[Route(path: '/admin/<?php echo $route; ?>/filter', name: 'admin_<?php echo $route; ?>_filter', methods: ['GET'])]
public function filter(Session $session): Response
{
return $this->doFilter($session);
}
#[Route(path: "/admin/<?= $route; ?>/edit/{entity}", name: "admin_<?= $route; ?>_edit", methods: ['GET', 'POST'])]
#[Route(path: '/admin/<?php echo $route; ?>/edit/{entity}', name: 'admin_<?php echo $route; ?>_edit', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'entity')]
public function edit(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doEdit($entity, $entityManager, $request);
}
#[Route(path: "/admin/<?= $route; ?>/sort/{page}", name: "admin_<?= $route; ?>_sort", methods: ['POST'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/inline_edit/{entity}/{context}/{label}', name: 'admin_<?php echo $route; ?>_inline_edit', methods: ['GET', 'POST'])]
#[IsGranted('edit', 'entity')]
public function inlineEdit(string $context, string $label, Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doInlineEdit($context, $label, $entity, $entityManager, $request);
}
#[Route(path: '/admin/<?php echo $route; ?>/sort/{page}', name: 'admin_<?php echo $route; ?>_sort', methods: ['POST'], requirements: ['page' => '\d+'])]
public function sort(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doSort($page, $query, $entityManager, $request, $session);
}
#[Route(path: "/admin/<?= $route; ?>/batch/{page}", name: "admin_<?= $route; ?>_batch", methods: ['POST'], requirements: ['page' => '\d+'])]
#[Route(path: '/admin/<?php echo $route; ?>/batch/{page}', name: 'admin_<?php echo $route; ?>_batch', methods: ['POST'], requirements: ['page' => '\d+'])]
public function batch(RepositoryQuery $query, EntityManager $entityManager, Request $request, Session $session, int $page = 1): Response
{
return $this->doBatch($page, $query, $entityManager, $request, $session);
}
#[Route(path: "/admin/<?= $route; ?>/delete/{entity}", name: "admin_<?= $route; ?>_delete", methods: ['DELETE'])]
#[Route(path: '/admin/<?php echo $route; ?>/delete/{entity}', name: 'admin_<?php echo $route; ?>_delete', methods: ['DELETE', 'POST'])]
#[IsGranted('delete', 'entity')]
public function delete(Entity $entity, EntityManager $entityManager, Request $request): Response
{
return $this->doDelete($entity, $entityManager, $request);
@ -70,23 +81,32 @@ class <?= $class_name; ?> extends CrudController
protected function getConfiguration(): CrudConfiguration
{
return CrudConfiguration::create()
->setPageTitle('index', 'List of <?= $entity; ?>')
if ($this->configuration) {
return $this->configuration;
}
return $this->configuration = CrudConfiguration::create()
->setPageTitle('index', 'List of <?php echo $entity; ?>')
->setPageTitle('edit', 'Edition of {id}')
->setPageTitle('new', 'New <?= $entity; ?>')
->setPageTitle('new', 'New <?php echo $entity; ?>')
->setPageTitle('show', 'View of {id}')
->setPageRoute('index', 'admin_<?= $route; ?>_index')
->setPageRoute('new', 'admin_<?= $route; ?>_new')
->setPageRoute('edit', 'admin_<?= $route; ?>_edit')
->setPageRoute('show', 'admin_<?= $route; ?>_show')
->setPageRoute('sort', 'admin_<?= $route; ?>_sort')
->setPageRoute('batch', 'admin_<?= $route; ?>_batch')
->setPageRoute('delete', 'admin_<?= $route; ?>_delete')
->setPageRoute('filter', 'admin_<?= $route; ?>_filter')
->setPageRoute('index', 'admin_<?php echo $route; ?>_index')
->setPageRoute('new', 'admin_<?php echo $route; ?>_new')
->setPageRoute('edit', 'admin_<?php echo $route; ?>_edit')
->setPageRoute('inline_edit', 'admin_<?php echo $route; ?>_inline_edit')
->setPageRoute('show', 'admin_<?php echo $route; ?>_show')
->setPageRoute('sort', 'admin_<?php echo $route; ?>_sort')
->setPageRoute('batch', 'admin_<?php echo $route; ?>_batch')
->setPageRoute('delete', 'admin_<?php echo $route; ?>_delete')
->setPageRoute('filter', 'admin_<?php echo $route; ?>_filter')
->setForm('edit', Type::class, [])
->setForm('edit', Type::class)
->setForm('new', Type::class)
->setView('form', 'admin/<?php echo $route; ?>_admin/_form.html.twig')
->setView('show_entity', 'admin/<?php echo $route; ?>_admin/_show.html.twig')
// ->setForm('filter', Type::class)
// ->setMaxPerPage('index', 20)
@ -98,6 +118,7 @@ class <?= $class_name; ?> extends CrudController
// ->setAction('index', 'show', true)
// ->setAction('index', 'edit', true)
// ->setAction('index', 'delete', true)
// ->setDoubleClick('index', false)
// ->setAction('edit', 'back', true)
// ->setAction('edit', 'show', true)
@ -128,6 +149,6 @@ class <?= $class_name; ?> extends CrudController
protected function getSection(): string
{
return '<?= $route; ?>';
return '<?php echo $route; ?>';
}
}

View file

@ -1,11 +1,11 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Factory\FactoryInterface;
use <?= $entity ?> as Entity;
use <?php echo $entity; ?> as Entity;
class <?= $class_name; ?> implements FactoryInterface
class <?php echo $class_name; ?> implements FactoryInterface
{
public function create(): Entity
{

View file

@ -1,6 +1,6 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Entity\Site\Page\Page;
use App\Core\Entity\Site\Page as BlockEntity;
@ -9,28 +9,28 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Form\FormBuilderInterface;
#[ORM\Entity]
class <?= $class_name; ?> extends Page
class <?php echo $class_name; ?> extends Page
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
<?php if (count($blocks)): ?> $builder
<?php foreach ($blocks as $block): ?>
->add('<?= $block['name'] ?>', <?= $block['type'] ?>)
<?php endforeach; ?>
<?php if (count($blocks)) { ?> $builder
<?php foreach ($blocks as $block) { ?>
->add('<?php echo $block['name']; ?>', <?php echo $block['type']; ?>)
<?php } ?>
;
<?php endif; ?>
<?php } ?>
}
<?php foreach ($blocks as $block): ?>
public function set<?= $block['camelCase'] ?>(BlockEntity\Block $block)
<?php foreach ($blocks as $block) { ?>
public function set<?php echo $block['camelCase']; ?>(BlockEntity\Block $block)
{
return $this->setBlock($block);
}
public function get<?= $block['camelCase'] ?>()
public function get<?php echo $block['camelCase']; ?>()
{
return $this->getBlock('<?= $block['name'] ?>'<?php if ($block['class']): ?>, <?= $block['class'] ?><?php endif; ?>);
return $this->getBlock('<?php echo $block['name']; ?>'<?php if ($block['class']) { ?>, <?php echo $block['class']; ?><?php } ?>);
}
<?php endforeach; ?>
<?php } ?>
}

View file

@ -1,15 +1,15 @@
<?= "<?php\n" ?>
<?php echo "<?php\n"; ?>
namespace <?= $namespace; ?>;
namespace <?php echo $namespace; ?>;
use App\Core\Repository\RepositoryQuery;
use Knp\Component\Pager\PaginatorInterface;
use <?= $repository; ?> as Repository;
use <?php echo $repository; ?> as Repository;
class <?= $class_name; ?> extends RepositoryQuery
class <?php echo $class_name; ?> extends RepositoryQuery
{
public function __construct(Repository $repository, PaginatorInterface $paginator)
{
parent::__construct($repository, '<?= $id; ?>', $paginator);
parent::__construct($repository, '<?php echo $id; ?>', $paginator);
}
}

View file

@ -64,7 +64,7 @@
"Display name": "Nom d'affichage"
"Administrator": "Administrateur⋅trice"
"Writer": "Rédacteur⋅trice"
"Reference": "Élement de référence"
"Reference": "Référence"
"My account": "Mon compte"
"Logout": "Déconnexion"
"Dashboard": "Tableau de bord"
@ -144,7 +144,7 @@
"Username or e-mail": "Identifiant ou e-mail"
"Submit": "Soumettre"
"If the information submitted corresponds to a user account, you will receive an e-mail with the link to initiate the password change procedure.": "Si les informations soumises correspondent à un compte utilisateur, vous allez recevoir un e-mail avec en lien pour enclancher la procédure de changement de mot de passe."
"Enter your account username or email address. An e-mail will be sent to you to initiate the password change procedure. ": "Saisissez le nom d'utilisateur ou l'adresse e-mail de votre compte. Un e-mail vous sera envoyé pour enclancher la procédure de changement de mot de passe."
"Enter your account username or email address. An e-mail will be sent to you to initiate the password change procedure.": "Saisissez le nom d'utilisateur ou l'adresse e-mail de votre compte. Un e-mail vous sera envoyé pour enclancher la procédure de changement de mot de passe."
"Show the login page": "Afficher la page de connexion"
"A password reset request has been made. If you are the source of this request, click on the link below or copy and paste the address if the link does not work.": "Une demande de réinitialisation de mot de passe a été réalisée. Si vous êtes à l'origine de cette demande, cliquer sur le lien ci-dessous ou copier et coller l'adresse si le lien ne fonctionne pas."
"Edit the routing": "Éditez le routage"
@ -163,7 +163,7 @@
"Filter": "Filtrer"
"Force this domain": "Forcer ce nom de domaine"
"Additional domains": "Domaines additionnels"
"Regular expression: do not add the delimiter": "Expréssion régulière : ne pas ajouter de délimiteur"
"Regular expression: do not add the delimiter": "Expression régulière : ne pas ajouter de délimiteur"
"Content type": "Type de contenu"
'Leave blank equals "text/html"': 'Laissez vide équivaut à "text/html"'
"Close": "Fermer"
@ -223,3 +223,4 @@
"At least one role": "Au moins un rôle"
"All roles": "Tous les rôles"
"Enable A/B Testing": "Activer le test A/B"
"Color": "Couleur"

View file

@ -2,7 +2,7 @@
{% import _self as macros %}
{% block title %}{{ 'My account'|trans }} - {{ parent() }}{% endblock %}
{% block title %}{{ 'My account'|trans }} {{ parent() ? ('- ' ~ parent()) : '' }}{% endblock %}
{% block body %}
<div class="bg-light">
@ -12,7 +12,7 @@
</div>
<div class="row">
<div class="col-6">
<div class="col-12 col-md-6">
<form action="{{ path('admin_account_password') }}" method="post">
<div class="tab-content">
<div class="tab-pane active">
@ -102,7 +102,7 @@
</form>
</div>
<div class="col-6">
<div class="col-12 col-md-6">
<form action="{{ path('admin_account_2fa') }}" method="post">
<div class="tab-content">
<div class="tab-pane active">

View file

@ -9,4 +9,3 @@
{% endfor %}
</div>
</div>

View file

@ -1,6 +1,6 @@
<div class="row">
<div class="col-md-12 p-3">
{% for item in entity_to_array(entity) %}
{% for item in entity_to_array(entity, false, ['id']) %}
<div class="font-weight-bold">{{ item.name|trans }}</div>
<div class="mb-3">{{ item.value }}</div>
{% endfor %}

View file

@ -4,7 +4,7 @@
{% block body_class %}has-form{% endblock %}
{% block title %}{{ configuration.pageTitle(context)|trans|build_string(entity) }} - {{ parent() }}{% endblock %}
{% block title %}{{ configuration.pageTitle(context)|trans|build_string(entity) }} {{ parent() ? ('- ' ~ parent()) : '' }}{% endblock %}
{% block body %}
{% block header %}

View file

@ -0,0 +1,22 @@
{%- block value -%}
{% set value = value|default(options.default_value) %}
<span class="field-boolean">
{% if options.display == 'checkbox' %}
{% block checkbox %}
<span class="fa {{ value ? options.checkbox_class_when_true : options.checkbox_class_when_false }}"></span>
{% endblock %}
{% elseif options.display == 'toggle' %}
{% block toggle %}
{% if value %}
<span class="rounded-pill pl-3 pr-1 text-white {{ options.toggle_class_when_true }}">
<span class="fa fa-circle"></span>
</span>
{% else %}
<span class="rounded-pill pr-3 pl-1 text-white {{ options.toggle_class_when_false }}">
<span class="fa fa-circle"></span>
</span>
{% endif %}
{% endblock %}
{% endif %}
</span>
{%- endblock -%}

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