Browse Source

init murph

Signed-off-by: Simon Vieille <simon@deblan.fr>
develop
Simon Vieille 6 months ago
commit
40ed84dea3
  1. 35
      .env
  2. 6
      .env.test
  3. 27
      .gitignore
  4. 27
      Makefile
  5. 440
      assets/css/admin.scss
  6. BIN
      assets/images/blank.png
  7. 92
      assets/images/core/logo.svg
  8. BIN
      assets/images/logo.png
  9. BIN
      assets/images/no-image.png
  10. 31
      assets/js/addons/checkbox-checker.js
  11. 8
      assets/js/addons/choices.js
  12. 26
      assets/js/addons/datepicker.js
  13. 7
      assets/js/addons/dbclick.js
  14. 43
      assets/js/addons/document-selector.js
  15. 23
      assets/js/addons/editor.js
  16. 84
      assets/js/addons/form-collection.js
  17. 15
      assets/js/addons/form-confirm.js
  18. 11
      assets/js/addons/form.js
  19. 31
      assets/js/addons/modal.js
  20. 47
      assets/js/addons/panel.js
  21. 82
      assets/js/addons/password.js
  22. 44
      assets/js/addons/push-state.js
  23. 27
      assets/js/addons/rest-choices.js
  24. 24
      assets/js/addons/table-fixed.js
  25. 122
      assets/js/addons/table-selectable.js
  26. 11
      assets/js/addons/toast.js
  27. 5
      assets/js/addons/tooltip.js
  28. 28
      assets/js/admin.js
  29. 43
      bin/console
  30. 7
      bin/doctrine-migrate
  31. 13
      bin/phpunit
  32. 110
      composer.json
  33. 22
      config/bundles.php
  34. 9
      config/packages/app.yaml
  35. 3
      config/packages/assets.yaml
  36. 19
      config/packages/cache.yaml
  37. 4
      config/packages/dev/debug.yaml
  38. 19
      config/packages/dev/monolog.yaml
  39. 4
      config/packages/dev/swiftmailer.yaml
  40. 6
      config/packages/dev/web_profiler.yaml
  41. 30
      config/packages/doctrine.yaml
  42. 5
      config/packages/doctrine_migrations.yaml
  43. 17
      config/packages/framework.yaml
  44. 3
      config/packages/mailer.yaml
  45. 16
      config/packages/notifier.yaml
  46. 8
      config/packages/prod/deprecations.yaml
  47. 20
      config/packages/prod/doctrine.yaml
  48. 16
      config/packages/prod/monolog.yaml
  49. 3
      config/packages/prod/routing.yaml
  50. 4
      config/packages/prod/webpack_encore.yaml
  51. 7
      config/packages/routing.yaml
  52. 15
      config/packages/scheb_2fa.yaml
  53. 50
      config/packages/security.yaml
  54. 3
      config/packages/sensio_framework_extra.yaml
  55. 4
      config/packages/stof_doctrine_extensions.yaml
  56. 4
      config/packages/swiftmailer.yaml
  57. 4
      config/packages/test/framework.yaml
  58. 12
      config/packages/test/monolog.yaml
  59. 2
      config/packages/test/swiftmailer.yaml
  60. 2
      config/packages/test/twig.yaml
  61. 3
      config/packages/test/validator.yaml
  62. 6
      config/packages/test/web_profiler.yaml
  63. 2
      config/packages/test/webpack_encore.yaml
  64. 8
      config/packages/translation.yaml
  65. 6
      config/packages/twig.yaml
  66. 8
      config/packages/validator.yaml
  67. 30
      config/packages/webpack_encore.yaml
  68. 5
      config/preload.php
  69. 15
      config/routes.yaml
  70. 11
      config/routes/annotations.yaml
  71. 3
      config/routes/dev/framework.yaml
  72. 7
      config/routes/dev/web_profiler.yaml
  73. 7
      config/routes/scheb_2fa.yaml
  74. 54
      config/services.yaml
  75. 20
      core/Annotation/UrlGenerator.php
  76. 96
      core/Authenticator/LoginFormAuthenticator.php
  77. 23
      core/Bundle/CoreBundle.php
  78. 0
      core/Controller/.gitignore
  79. 150
      core/Controller/Account/AccountAdminController.php
  80. 31
      core/Controller/Admin/AdminController.php
  81. 155
      core/Controller/Auth/AuthController.php
  82. 27
      core/Controller/Dashboard/DashboardAdminController.php
  83. 82
      core/Controller/Site/MenuAdminController.php
  84. 116
      core/Controller/Site/NavigationAdminController.php
  85. 283
      core/Controller/Site/NodeAdminController.php
  86. 109
      core/Controller/Site/PageAdminController.php
  87. 48
      core/Controller/Site/PageController.php
  88. 40
      core/Controller/Site/SitemapController.php
  89. 66
      core/Controller/Site/TreeAdminController.php
  90. 138
      core/Controller/User/UserAdminController.php
  91. 52
      core/DependencyInjection/Configuration.php
  92. 28
      core/DependencyInjection/CoreExtension.php
  93. 59
      core/Doctrine/Timestampable.php
  94. 0
      core/Entity/.gitignore
  95. 7
      core/Entity/EntityInterface.php
  96. 146
      core/Entity/Site/Menu.php
  97. 138
      core/Entity/Site/Navigation.php
  98. 400
      core/Entity/Site/Node.php
  99. 82
      core/Entity/Site/Page/Block.php
  100. 36
      core/Entity/Site/Page/FileBlock.php

35
.env

@ -0,0 +1,35 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=e6e287f176fe2c69112fc620e1801bf0
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
#
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data.db"
# DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7"
DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=13&charset=utf8"
###< doctrine/doctrine-bundle ###
###> symfony/swiftmailer-bundle ###
# For Gmail as a transport, use: "gmail://username:password@localhost"
# For a generic SMTP server, use: "smtp://localhost:25?encryption=&auth_mode="
# Delivery is disabled by default via "null://localhost"
MAILER_URL=null://localhost
###< symfony/swiftmailer-bundle ###

6
.env.test

@ -0,0 +1,6 @@
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
PANTHER_ERROR_SCREENSHOT_DIR=./var/error-screenshots

27
.gitignore

@ -0,0 +1,27 @@
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/src/Command/TestCommand.php
/var/
/vendor/
###< symfony/framework-bundle ###
###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> symfony/webpack-encore-bundle ###
/node_modules/
/public/build/
npm-debug.log
yarn-error.log
###< symfony/webpack-encore-bundle ###
/public/uploads/
!/public/uploads/.gitkeep

27
Makefile

@ -0,0 +1,27 @@
COMPOSER ?= composer
PHP ?= php7.4
SSH ?= ssh
WEBPACK ?= webpack
YARN ?= yarn
all: dep asset clean
.ONESHELL:
dep:
$(COMPOSER) update --ignore-platform-reqs
$(COMPOSER) install --ignore-platform-reqs
$(YARN)
asset-watch:
$(WEBPACK) -w
asset:
$(YARN)
$(WEBPACK)
clean:
rm -fr var/cache/dev/*
rm -fr var/cache/prod/*
doctrine-migration:
PHP=$(PHP) ./bin/doctrine-migrate

440
assets/css/admin.scss

@ -0,0 +1,440 @@
$theme-colors: (
"primary": #1ab5dc,
"primary-light": lighten(#3183aa, 40%),
"dark-blue": #1e2430,
);
$grid-gutter-width: 0px;
$pagination-color: #343a40;
$pagination-bg: #ffffff;
$pagination-active-color: #ffffff;
$pagination-active-bg: #343a40;
@import "~choices.js/src/styles/choices.scss";
@import "~bootstrap/scss/bootstrap.scss";
@import "~@fortawesome/fontawesome-free/css/all.css";
#logo {
width: 30px;
}
.choices__list--dropdown {
display: none;
}
.choices__list--dropdown.is-active {
display: block;
}
.dropdown-toggle-hide-after {
&::after {
display: none;
}
}
.login {
&-container {
margin-top: 5%;
margin-bottom: 5%;
}
&-form {
padding: 5%;
}
&-image {
width: 100%;
max-width: 80%;
}
}
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
padding: 71px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
.sidebar-sticky {
position: relative;
top: 0;
height: calc(100vh - 71px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
@supports ((position: -webkit-sticky) or (position: sticky)) {
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
}
}
.actions-container {
padding-right: 25px;
}
.thead-light {
a, th {
color: #333333;
}
}
tr.table-primary-light {
background-color: #ecf5fa;
}
.td-nowrap {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bg-dark-blue {
background: #242b3b;
color: #fff;
.nav-item-label {
color: #fff;
}
}
.nav-pills {
.nav-item {
margin-right: 3px;
}
.nav-link:not(.active) {
color: #333;
background: #eee;
}
}
.sidebar {
.nav-link {
font-weight: 500;
color: #333;
border-left: 4px solid map-get($theme-colors, 'dark-blue');
padding-top: 14px;
padding-bottom: 14px;
.fa {
font-size: 1.2rem;
margin-right: 5px;
min-width: 30px;
color: #fff;
}
&.active {
font-weight: bold;
border-left: 4px solid map-get($theme-colors, 'primary');
background: map-get($theme-colors, 'dark-blue');
}
}
&-heading {
font-size: .75rem;
text-transform: uppercase;
display: flex;
}
@media screen and (max-width: 1130px) {
.nav-link {
font-size: 14px;
}
}
@media screen and (max-width: 770px) {
.nav {
padding-left: 0;
}
.nav-link {
padding-left: 10px;
}
.nav-item-label {
display: none;
}
.sidebar-heading {
display: none;
}
width: 50px;
}
}
*[data-selectable-selector] {
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
*[data-selectable-selector] {
&:hover {
cursor: pointer;
}
}
.footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
z-index: 1000;
height: 35px;
background: #f8f9fa;
}
.body {
padding-top: 60px;
.nav {
padding-left: 10px;
}
}
@media screen and (max-width: 580px) {
.body {
margin-left: 45px;
}
.sidebar {
width: 50px;
max-width: 100% !important;
.sidebar-sticky {
width: 50px;
max-width: 100% !important;
}
}
}
table.table-fixed, .table-fixed > table {
width: 100%;
tbody {
overflow: auto;
width: 100%;
height: 500px;
}
thead, tbody, tr, td, th {
display: block;
}
tbody {
td, th {
float: left;
min-height: 60px;
}
tr {
clear: left;
}
}
thead {
tr {
th {
float: left;
&.sorted {
&::before {
content: '\f0dc';
font-family: 'FontAwesome';
color: #aaa;
margin-right: 3px;
}
}
}
}
}
}
.toast-container {
display: flex;
position: relative;
z-index: 1060;
.toast-wrapper {
position: fixed;
top: 20px;
right: 20px;
z-index: 1060;
width: 300px;
}
}
.tab-form {
padding: 15px;
}
.icon-margin {
margin-right: 4px;
}
.file-icon {
font-size: 2em;
}
.d-ib {
display: inline-block;
}
.list-checkbox {
vertical-align: middle;
margin-right: 10px;
margin-top: -2px;
}
.password-strenth {
padding: 0 0 5px 0;
margin-top: -4px;
.col-sm {
height: 8px;
border: 2px solid #fff;
}
&-info {
font-size: 13px;
height: 22px;
}
}
.notification-bell:not([disabled]) {
[data-counter]:after {
display: block;
color: #fff;
background: red;
width: 9px;
height: 9px;
position: absolute;
content: ' ';
top: 4px;
right: 10px;
border-radius: 4px;
}
}
.form-error-icon {
margin-right: 4px;
}
.custom-file-label::after {
content: "Parcourir";
}
#lease_template_html {
height: calc(100vh - 270px);
}
.panel {
&-toggler {
&:hover {
cursor: pointer;
}
}
&-content {
display: block;
&:not(.active) {
display: none;
}
}
}
*[data-collection-delete-container] {
cursor: pointer;
}
*[data-collection-add] {
cursor: pointer;
}
.login-image {
width: 50%;
}
.tree {
position: relative;
background: white;
color: #212529;
span {
font-style: italic;
letter-spacing: .4px;
color: #a8a8a8;
}
.fa-folder-open, .fa-folder {
color: #007bff;
}
.fa-html5 {
color: #f21f10;
}
ul {
padding-left: 5px;
list-style: none;
margin: 0;
padding-bottom: 0;
li {
position: relative;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 15px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
&:before {
position: absolute;
top: 15px;
left: 0;
width: 10px;
height: 1px;
margin: auto;
content: '';
background-color: #666;
}
&:after {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 1px;
height: 100%;
content: '';
background-color: #666;
}
&:last-child:after {
height: 15px;
}
}
a {
cursor: pointer;
&:hover {
text-decoration: none;
}
}
}
}
fieldset.form-group {
margin-bottom: 0;
}

BIN
assets/images/blank.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

92
assets/images/core/logo.svg

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="92.5"
height="92.500008"
viewBox="0 0 24.473958 24.473961"
version="1.1"
id="svg2782"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="logo.svg">
<defs
id="defs2776" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.959798"
inkscape:cx="144.24896"
inkscape:cy="62.558177"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1918"
inkscape:window-height="1017"
inkscape:window-x="0"
inkscape:window-y="41"
inkscape:window-maximized="0" />
<metadata
id="metadata2779">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Calque 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-93.596354,-136.59635)">
<g
transform="translate(14.977383,9.0140333)"
id="g2760">
<path
inkscape:connector-curvature="0"
id="rect2455"
d="m 80.981321,127.58232 h 19.749259 c 1.30874,0 2.36235,1.05361 2.36235,2.36235 v 19.74926 c 0,1.30874 -1.05361,2.36235 -2.36235,2.36235 H 80.981321 c -1.30874,0 -2.36235,-1.05361 -2.36235,-2.36235 v -19.74926 c 0,-1.30874 1.05361,-2.36235 2.36235,-2.36235 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#1e2430;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.58333302;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;enable-background:accumulate" />
<g
transform="translate(-28.224115,84.535074)"
id="text2474-5"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24.23528671px;line-height:125%;font-family:Tahoma;-inkscape-font-specification:'Tahoma, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
aria-label="M">
<path
inkscape:connector-curvature="0"
id="path2522"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:'Trebuchet MS';-inkscape-font-specification:'Trebuchet MS';fill:#ffcc00;fill-opacity:1;stroke-width:0.26458332px"
d="m 125.90001,62.475697 h -2.98208 l -1.79871,-9.348573 -3.49093,9.573412 h -1.10052 l -3.49093,-9.573412 -1.86971,9.348573 h -2.97024 l 3.49092,-17.34811 h 1.63304 l 3.75126,11.679798 3.66843,-11.679798 h 1.62121 z" />
</g>
<path
inkscape:connector-curvature="0"
id="rect2455-3-6"
d="m 102.39375,128.26496 -23.092649,23.09213 c 0.427643,0.43204 1.021784,0.69919 1.680519,0.69919 h 19.74918 c 1.30874,0 2.36213,-1.05339 2.36213,-2.36213 v -19.74918 c 0,-0.65861 -0.26729,-1.25238 -0.69918,-1.68001 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;vector-effect:none;fill:#19b4db;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10.58333302;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;enable-background:accumulate" />
<path
inkscape:connector-curvature="0"
id="path2525-9-7"
d="m 94.723251,132.53384 -4.531505,4.53151 -1.343589,4.27726 -0.713134,-2.22054 -2.044319,2.04432 2.213301,6.0694 h 1.10019 l 3.491262,-9.57306 1.798338,9.34826 h 2.982249 z m -11.105782,11.10527 -3.371887,3.37188 h 2.697509 z"
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:24.23528481px;line-height:125%;font-family:'Trebuchet MS';-inkscape-font-specification:'Trebuchet MS';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#1e2430;fill-opacity:1;stroke:#1e2430;stroke-width:0.26458332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
assets/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
assets/images/no-image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

31
assets/js/addons/checkbox-checker.js

@ -0,0 +1,31 @@
const $ = require('jquery');
module.exports = function() {
$('*[data-checkbox-ckecker]').click(function() {
const wrapperName = $(this).attr('data-checkbox-ckecker');
if (!wrapperName) {
return;
}
const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]');
$(checkboxes).each(function(i, v) {
$(v).prop('checked', true);
})
})
$('*[data-checkbox-unckecker]').click(function() {
const wrapperName = $(this).attr('data-checkbox-unckecker');
if (!wrapperName) {
return;
}
const checkboxes = $('*[data-checkbox-wrapper="' + wrapperName + '"] *[data-checkbox] input[type="checkbox"]');
$(checkboxes).each(function(i, v) {
$(v).prop('checked', false);
})
})
};

8
assets/js/addons/choices.js

@ -0,0 +1,8 @@
const Choices = require('choices.js');
const $ = require('jquery');
module.exports = function() {
$('*[data-jschoice]').each(function(key, item) {
new Choices(item);
});
}

26
assets/js/addons/datepicker.js

@ -0,0 +1,26 @@
const Datepicker = require('vanillajs-datepicker')
const isDateSupported = () => {
const input = document.createElement('input');
const value = 'a';
input.setAttribute('type', 'date');
input.setAttribute('value', value);
return input.value !== value;
}
module.exports = () => {
if (isDateSupported()) {
return
}
const inputs = document.querySelectorAll('input[type="date"]')
const size = inputs.length
for (var i = 0, c = inputs.length; i < c; i++) {
new Datepicker.Datepicker(inputs[i], {
format: 'yyyy-mm-dd'
})
}
}

7
assets/js/addons/dbclick.js

@ -0,0 +1,7 @@
const $ = require('jquery');
module.exports = function() {
$('*[data-dblclick]').dblclick(function(e) {
document.location.href = $(this).attr('data-dblclick');
})
};

43
assets/js/addons/document-selector.js

@ -0,0 +1,43 @@
const $ = require('jquery');
let DocumentSelector = () => {
let forms = $('.document-selector-form');
let btnSubmit = $('#download-archive-form button');
let handler = function() {
forms.each((fi, f) => {
let form = $(f);
let ids = form.find('.document-selector-ids');
let btn = form.find('.document-selector-button');
ids.html('');
let hasSelection = false;
$('*[data-documents] *[data-selectable-row] input[data-selectable-checkbox]').each((i, c) => {
let checkbox = $(c);
if (checkbox.is(':checked')) {
ids.append(checkbox[0].outerHTML);
hasSelection = true;
}
});
if (hasSelection && btn.length) {
btn.removeAttr('disabled');
ids.find('input').prop('checked', true);
} else {
btn.attr('disabled', 'disabled');
}
})
}
$('*[data-documents] *[data-selectable-row]').click(function() {
window.setTimeout(handler, 100)
});
$('*[data-documents] *[data-selectable-row]').on('clicked', function() {
window.setTimeout(handler, 100)
});
}
module.exports = DocumentSelector;

23
assets/js/addons/editor.js

@ -0,0 +1,23 @@
module.exports = function() {
if (typeof tinymce === 'undefined') {
return;
}
tinymce.init({
selector: '*[data-tinymce]',
base_url: '/vendor/tinymce/',
cache_suffix: '?v=4.1.6',
language: 'fr_FR',
plugins: 'print preview importcss searchreplace visualblocks visualchars fullscreen template table charmap hr pagebreak nonbreaking toc insertdatetime advlist lists wordcount textpattern noneditable help charmap quickbars',
menubar: 'file edit view insert format tools table tc help',
toolbar: 'undo redo | bold italic underline strikethrough | fontselect fontsizeselect formatselect | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | forecolor backcolor casechange permanentpen formatpainter removeformat | pagebreak | charmap | fullscreen preview | code',
importcss_append: true,
image_caption: true,
quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
noneditable_noneditable_class: "mceNonEditable",
toolbar_drawer: 'sliding',
spellchecker_dialog: true,
tinycomments_mode: 'embedded',
contextmenu: "link image imagetools table configurepermanentpen",
});
};

84
assets/js/addons/form-collection.js

@ -0,0 +1,84 @@
const $ = require('jquery');
const DeleteHandler = (e) => {
e.stopPropagation()
const target = e.target;
let button = $(target);
if (button.is('[data-collection-delete-container]')) {
button = button.find('*[data-collection-delete]').first()
}
const id = button.attr('data-collection-delete');
const collection = button.parents('[data-collection]')
const item = collection.find('*[data-collection-item="' + id + '"]')
if (confirm('Validez-vous la suppression ?')) {
item.remove();
collection.trigger('collection.update');
}
}
const CollectionInitilizedAndUpdated = (e) => {
const target = $(e.target)
target.find('*[data-collection-empty]').toggleClass(
'd-none',
target.find('*[data-collection-item]').length !== 0
);
target.find('*[data-collection-nonempty]').toggleClass(
'd-none',
target.find('*[data-collection-item]').length === 0
);
}
const FormCollection = () => {
$('*[data-collection]').on(
'collection.update',
CollectionInitilizedAndUpdated
);
$('*[data-collection]').on(
'collection.init',
CollectionInitilizedAndUpdated
);
$('body').on(
'click',
'*[data-collection-delete], *[data-collection-delete-container]',
DeleteHandler
);
$('body').on('click', '*[data-collection-add]', (e) => {
e.stopPropagation()
const collectionId = $(e.target).attr('data-collection-add')
const collectionContainer = $('*[data-collection="' + collectionId + '"]')
const prototypeContent = $('#' + collectionId).html()
let name = 0
collectionContainer.find('*[data-collection-item]').each(function() {
var n = parseInt($(this).attr('data-collection-item'))
if (n >= name) {
name = n + 1
}
})
collectionContainer.append(prototypeContent)
const item = collectionContainer.children('*[data-collection-item]:last-child')
const deleteBtn = $('<span data-collection-delete="__name__" class="fa fa-trash"></span>')
item.find('*[data-collection-delete-container]').first().append(deleteBtn)
item.html(item.html().replace(/__name__/g, name))
item.attr('data-collection-item', name)
collectionContainer.trigger('collection.update');
});
$('*[data-collection]').trigger('collection.init');
}
module.exports = FormCollection;

15
assets/js/addons/form-confirm.js

@ -0,0 +1,15 @@
const $ = require('jquery');
module.exports = function() {
$('*[data-form-confirm]').submit(function(e) {
let message = $(this).attr('data-form-confirm');
if (!message) {
message = 'Confimez-vous cette action ?';
}
if (!confirm(message)) {
e.preventDefault();
}
})
};

11
assets/js/addons/form.js

@ -0,0 +1,11 @@
const $ = require('jquery');
module.exports = function() {
$('.custom-file-input').on('change', function(event) {
let inputFile = event.currentTarget;
$(inputFile).parent()
.find('.custom-file-label')
.html(inputFile.files[0].name);
});
};

31
assets/js/addons/modal.js

@ -0,0 +1,31 @@
const $ = require('jquery');
module.exports = function() {
$('body').on('click', '*[data-modal]', (e) => {
e.preventDefault();
e.stopPropagation();
let container = $('#modal-container');
if (!container.length) {
container = $('<div id="modal-container" class="modal">');
$('body').append(container);
}
container.html('');
const url = $(e.target).attr('data-modal');
container.load(url, function() {
$(container).modal('show');
});
});
const urlParams = new URLSearchParams(window.location.search)
const dataModal = urlParams.get('data-modal')
if (dataModal) {
$('*[data-modal="' + dataModal + '"]').first().click();
}
}

47
assets/js/addons/panel.js

@ -0,0 +1,47 @@
const $ = require('jquery');
let Pannel = () => {
let panels = $('.panel');
panels.each((i, p) => {
let panel = $(p);
let content = panel.find('.panel-content').first();
let togglers = panel.find('.panel-toggler');
togglers.each((k, t) => {
let toggler = $(t);
if (!toggler.is('.fa')) {
return;
}
if (content.is('.active')) {
toggler.removeClass('fa-arrow-down');
toggler.addClass('fa-arrow-up');
} else {
toggler.removeClass('fa-arrow-up');
toggler.addClass('fa-arrow-down');
}
})
togglers.click(function(e) {
e.stopPropagation();
content.toggleClass('active');
togglers.each((k, t) => {
let toggler = $(t);
if (!toggler.is('.fa')) {
return;
}
toggler
.toggleClass('fa-arrow-down')
.toggleClass('fa-arrow-up');
})
});
});
}
module.exports = Pannel;

82
assets/js/addons/password.js

@ -0,0 +1,82 @@
const $ = require('jquery');
const zxcvbn = require('zxcvbn');
let scoreColors = [
'danger',
'danger',
'warning',
'warning',
'success',
];
let scoreInfos = {
"This is a top-10 common password": "Parmis le top 10 des mots de passes communs",
"This is a top-100 common password": "Parmis le top 100 des mots de passes communs",
"This is a very common password": "Mot de passe vraiment trop commun",
"This is similar to a commonly used password": "Similaire à un mot de passe commun",
"A word by itself is easy to guess": "Ce mot est trop simple à deviner",
"Names and surnames by themselves are easy to guess": "Les noms ou les surnoms sont simples à deviner",
"Common names and surnames are easy to guess": "Les noms ou les surnoms sont simples à deviner",
"Straight rows of keys are easy to guess": "Combinaison de touches trop simple",
"Short keyboard patterns are easy to guess": "Combinaison de touches trop simple",
"Repeats like \"aaa\" are easy to guess'": "Les répétitions comme \"aaa\" sont simples à deviner",
"Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": "Les répétitions comme \"abcabcabc\" sont simples à deviner",
"Sequences like abc or 6543 are easy to guess": "Les séquences comme \"abc\" ou \"6543\" sont simples à deviner",
"Recent years are easy to guess": "Les années sont simples à deviner",
"Dates are often easy to guess": "Les dates sont souvent simples à deviner",
}
let checkPassword = function(password, confirmation, indicator, submit) {
let result = zxcvbn(password.val());
let score = result.score;
let cols = indicator.children('.col-sm');
let info = indicator.children('.password-strenth-info');
info.text('');
cols.attr('class', 'col-sm');
for (var u = 0; u <= 5; u++) {
let col = cols.eq(u);
if (u <= score) {
col.addClass('bg-' + scoreColors[score]);
} else {
col.addClass('bg-light');
}
}
console.log(result)
info.text(scoreInfos[result.feedback.warning]);
info.attr('class', 'col-12 password-strenth-info text-' + scoreColors[score]);
if (score < 4 || confirmation.val() !== password.val()) {
submit.attr('disabled', 'disabled');
} else {
submit.removeAttr('disabled');
}
}
module.exports = function() {
let passwordNew = $('#form-password-new');
let passwordConfirmation = $('#form-password-confirmation');
let passwordSubmit = $('#form-password-submit');
let passwordStrength = $('#form-password-strength');
if (passwordStrength.length) {
passwordNew.keyup(function() {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit);
});
passwordNew.change(function() {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit);
});
passwordConfirmation.keyup(function() {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit);
});
passwordConfirmation.change(function() {
checkPassword(passwordNew, passwordConfirmation, passwordStrength, passwordSubmit);
});
}
};

44
assets/js/addons/push-state.js

@ -0,0 +1,44 @@
const $ = require('jquery');
module.exports = function() {
$('*[data-pushstate]').click((e) => {
var url = $(e.target).attr('data-pushstate');
history.pushState({url: url}, null, url);
history.replaceState({url: url}, null, url);
});
let forms = $('form[data-formpushstate]');
let checkAndUsePushState = () => {
let state = [window.location.pathname, window.location.search].join('');
$('*[data-pushstate]').each((i, v) => {
let method = 'compare';
if ($(v).is('[data-pushstate-method]')) {
method = $(v).attr('data-pushstate-method')
}
var isThisOne = false;
if (method === 'compare' && $(v).attr('data-pushstate') === state) {
isThisOne = true;
}
if (method === 'indexOf' && state.indexOf($(v).attr('data-pushstate')) !== -1) {
isThisOne = true;
}
if (isThisOne) {
$(v).click();
forms.attr('action', state);
}
});
}
checkAndUsePushState();
$(window).on('statechange', checkAndUsePushState, false);
}

27
assets/js/addons/rest-choices.js

@ -0,0 +1,27 @@
const $ = require('jquery');
const Choices = require('choices.js');
module.exports = function() {
$('*[data-rest-choices]').each(function(key, item) {
const url = $(this).attr('data-rest-choices');
new Choices(item, {
searchPlaceholderValue: 'Chercher',
}).setChoices(function() {
return fetch(url)
.then(function(response) {
return response.json();
})
.then(function(data) {
return data.map(function(d) {
return {
label: d.label,
value: d.value
};
});
});
})
.then(function(instance) {
});
})
};

24
assets/js/addons/table-fixed.js

@ -0,0 +1,24 @@
const $ = require('jquery');
let resizeTbody = (tbody) => {
tbody.height($(window).height() - tbody.offset().top - 20);
}
let tableFixed = () => {
let tables = $('table[data-table-fixed], *[data-table-fixed] > table');
tables.each((i, t) => {
let table = $(t);
table.addClass('table-fixed');
let tbody = table.find('tbody');
resizeTbody(tbody);
$(window).resize(function() {
resizeTbody(tbody);
});
});
}
module.exports = tableFixed;

122
assets/js/addons/table-selectable.js

@ -0,0 +1,122 @@
const $ = require('jquery');
const selectedClass = 'table-primary-light';
let toggleRow = (row, checkbox, checkboxIsClicked) => {
row.toggleClass(selectedClass);
if (checkboxIsClicked) {
checkbox.prop('checked', checkbox.prop('checked'));
return;
}
if (checkbox.length) {
checkbox.prop('checked', !checkbox.prop('checked'));
}
}
let unactiveRow = (row, checkbox) => {
row.removeClass(selectedClass);
if (checkbox.length) {
checkbox.prop('checked', false);
}
}
let activeRow = (row, checkbox) => {
row.addClass(selectedClass);
if (checkbox.length) {
checkbox.prop('checked', true);
}
}
let tableSelectable = () => {
let tables = $('*[data-selectable]');
tables.each((i, t) => {
var table = $(t);
var rows = table.find('*[data-selectable-row]');
let selectedIndex = null;
var tbody = table.find('tbody');
var resizer = () => {
tbody.height($(window).height() - tbody.offset().top - 20);
}
window.setInterval(resizer, 1000);
resizer();
$(window).resize(resizer);
((rows) => {
rows.each((i, r) => {
let row = $(r);
let checkbox = row.find('*[data-selectable-checkbox]');
let selectors = row.find('*[data-selectable-selector]');
((row, selectors, checkbox, index) => {
selectors.click((e) => {
if (event.target.nodeName === 'INPUT') {
e.stopPropagation();
checkbox.trigger('clicked');
return toggleRow(row, checkbox, true);
}
if (window.event.ctrlKey) {
e.preventDefault();
return toggleRow(row, checkbox);
}
if (window.event.button === 0) {
if (!window.event.ctrlKey && !window.event.shiftKey) {
rows.each((z, r2) => {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
});
toggleRow(row, checkbox);
if (row.hasClass(selectedClass)) {
selectedIndex = index;
} else {
selectedIndex = null;
}
return;
}
if (window.event.shiftKey) {
if (selectedIndex !== null) {
rows.each((z, r2) => {
if (selectedIndex <= index) {
if (z >= selectedIndex && z <= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
}
} else {
if (z <= selectedIndex && z >= index) {
activeRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
} else {
unactiveRow($(r2), $(r2).find('*[data-selectable-checkbox]'));
}
}
});
//selectedIndex = index;
}
}
}
});
})(row, selectors, checkbox, i);
});
})(rows);
});
}
module.exports = tableSelectable;

11
assets/js/addons/toast.js

@ -0,0 +1,11 @@
const $ = require('jquery');
module.exports = function() {
$('.toast').toast({
animation: true,
autohide: true,
delay: 5000,
});
$('.toast').toast('show');
};

5
assets/js/addons/tooltip.js

@ -0,0 +1,5 @@
const $ = require('jquery');
module.exports = function() {
$('*[data-toggle="tooltip"]').tooltip();
};

28
assets/js/admin.js

@ -0,0 +1,28 @@
/*const imagesContext = require.context(
'../images',
true, /\.(png|jpg|jpeg|gif|ico|svg|webp)$/
);
imagesContext.keys().forEach(imagesContext);*/
import '../css/admin.scss';
require('../../node_modules/bootstrap/dist/js/bootstrap.min.js');
// require('./addons/table-selectable.js')();
require('./addons/table-fixed.js')();
// require('./addons/document-selector.js')();
require('./addons/form-confirm.js')();
require('./addons/form.js')();
require('./addons/dbclick.js')();
require('./addons/toast.js')();
require('./addons/modal.js')();
require('./addons/push-state.js')();
require('./addons/password.js')();
require('./addons/tooltip.js')();
require('./addons/editor.js')();
require('./addons/panel.js')();
require('./addons/choices.js')();
require('./addons/checkbox-checker.js')();
require('./addons/rest-choices.js')();
require('./addons/form-collection.js')();
require('./addons/datepicker.js')();

43
bin/console

@ -0,0 +1,43 @@
#!/usr/bin/env php
<?php
use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Dotenv\Dotenv;
use Symfony\Component\ErrorHandler\Debug;
if (!in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
}
set_time_limit(0);
require dirname(__DIR__).'/vendor/autoload.php';
if (!class_exists(Application::class) || !class_exists(Dotenv::class)) {
throw new LogicException('You need to add "symfony/framework-bundle" and "symfony/dotenv" as Composer dependencies.');
}
$input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}
if ($input->hasParameterOption('--no-debug', true)) {
putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = '0');
}
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
if ($_SERVER['APP_DEBUG']) {
umask(0000);
if (class_exists(Debug::class)) {
Debug::enable();
}
}
$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);
$application = new Application($kernel);
$application->run($input);

7
bin/doctrine-migrate

@ -0,0 +1,7 @@
#!/bin/sh
CLASS_NAME="$(echo "yes" | "$PHP" ./bin/console doctrine:migration:diff -e dev | grep -o "Version[0-9]*" | tail -n 1)"
if [ -n "$CLASS_NAME" ]; then
echo "yes" | "$PHP" ./bin/console doctrine:migration:exec --up "DoctrineMigrations\\$CLASS_NAME" -e dev
fi

13
bin/phpunit

@ -0,0 +1,13 @@
#!/usr/bin/env php
<?php
if (!file_exists(dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php')) {
echo "Unable to find the `simple-phpunit.php` script in `vendor/symfony/phpunit-bridge/bin/`.\n";
exit(1);
}
if (false === getenv('SYMFONY_PHPUNIT_DIR')) {
putenv('SYMFONY_PHPUNIT_DIR='.__DIR__.'/.phpunit');
}
require dirname(__DIR__).'/vendor/symfony/phpunit-bridge/bin/simple-phpunit.php';

110
composer.json

@ -0,0 +1,110 @@
{
"type": "project",
"license": "proprietary",
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"bjeavons/zxcvbn-php": "^1.2",
"cocur/slugify": "^4.0",
"composer/package-versions-deprecated": "1.11.99.1",
"doctrine/annotations": "^1.0",
"doctrine/doctrine-bundle": "^2.2",
"doctrine/doctrine-migrations-bundle": "^3.0",
"doctrine/orm": "^2.8",
"knplabs/knp-paginator-bundle": "^5.4",
"phpdocumentor/reflection-docblock": "^5.2",
"scheb/2fa-google-authenticator": "^5.7",
"scheb/2fa-qr-code": "^5.7",
"sensio/framework-extra-bundle": "^6.1",
"stof/doctrine-extensions-bundle": "^1.6",
"symfony/apache-pack": "^1.0",
"symfony/asset": "5.2.*",
"symfony/console": "5.2.*",
"symfony/dotenv": "5.2.*",
"symfony/event-dispatcher": "5.2.*",
"symfony/expression-language": "5.2.*",
"symfony/finder": "5.2.*",
"symfony/flex": "^1.3.1",
"symfony/form": "5.2.*",
"symfony/framework-bundle": "5.2.*",
"symfony/http-client": "5.2.*",
"symfony/intl": "5.2.*",
"symfony/mailer": "5.2.*",
"symfony/mime": "5.2.*",
"symfony/monolog-bundle": "^3.1",
"symfony/notifier": "5.2.*",
"symfony/process": "5.2.*",
"symfony/property-access": "5.2.*",
"symfony/property-info": "5.2.*",
"symfony/proxy-manager-bridge": "5.2.*",
"symfony/security-bundle": "5.2.*",
"symfony/serializer": "5.2.*",
"symfony/string": "5.2.*",
"symfony/swiftmailer-bundle": "^3.5",
"symfony/translation": "5.2.*",
"symfony/twig-bundle": "^5.2",
"symfony/validator": "5.2.*",
"symfony/web-link": "5.2.*",
"symfony/webpack-encore-bundle": "^1.11",
"symfony/yaml": "5.2.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
"require-dev": {
"symfony/browser-kit": "^5.2",
"symfony/css-selector": "^5.2",
"symfony/debug-bundle": "^5.2",
"symfony/maker-bundle": "^1.0",
"symfony/phpunit-bridge": "^5.2",
"symfony/stopwatch": "^5.2",
"symfony/var-dumper": "^5.2",
"symfony/web-profiler-bundle": "^5.2"
},
"config": {
"optimize-autoloader": true,
"preferred-install": {
"*": "dist"
},
"sort-packages": true
},
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Core\\": "core/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
}
},
"replace": {
"symfony/polyfill-ctype": "*",
"symfony/polyfill-iconv": "*",
"symfony/polyfill-php72": "*"
},
"scripts": {
"auto-scripts": {
"cache:clear": "symfony-cmd",
"assets:install %PUBLIC_DIR%": "symfony-cmd"
},
"post-install-cmd": [
"@auto-scripts"
],
"post-update-cmd": [
"@auto-scripts"
]
},
"conflict": {
"symfony/symfony": "*"
},
"extra": {
"symfony": {
"allow-contrib": false,
"require": "5.2.*"
}
}
}

22
config/bundles.php

@ -0,0 +1,22 @@
<?php
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true],
Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true],
Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle::class => ['all' => true],
Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle::class => ['all' => true],
App\Core\Bundle\CoreBundle::class => ['all' => true],
App\Bundle\AppBundle::class => ['all' => true],
];

9
config/packages/app.yaml

@ -0,0 +1,9 @@
core:
site:
name: "Murph"
logo: "build/images/core/logo.svg"
pages:
App\Entity\Page\SimplePage:
name: 'Page simple'
templates:
- {name: "Template 1", file: "page/simple/page.html.twig"}

3
config/packages/assets.yaml

@ -0,0 +1,3 @@
framework:
assets:
json_manifest_path: '%kernel.project_dir%/public/build/manifest.json'

19
config/packages/cache.yaml

@ -0,0 +1,19 @@
framework:
cache:
# Unique name of your app: used to compute stable namespaces for cache keys.
#prefix_seed: your_vendor_name/app_name