64 Commits
Author SHA1 Message Date
mcndave f5014e1cee Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2026-10-19 15:42:47 -04:00
mcndave 502795d688 - Fixed a missing cast in two files 2026-07-16 17:38:58 -04:00
mcndave 4c4737750b - Allowing lean up to at least 1.4.2 2026-07-16 14:12:34 -04:00
mcndave 8323c6218e - Added lean 1.4.2 version 2026-07-16 11:59:27 -04:00
mcndave 6d8763a22c - Fixes made mostly for PHP 8.5 2026-07-14 15:45:38 +00:00
mcndave 0471c0ab83 Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2026-07-01 14:39:05 -04:00
mcndave 0d3f807d9d - Fixed reversed slash 2026-07-01 14:38:55 -04:00
Dev e5fab028b3 - Bug fixes made while working on reservations fp 2026-06-09 19:16:39 +00:00
Dave Mc Nicoll 7117021750 - WIP on database storage filling 2026-05-21 15:21:45 +00:00
mcndave 2fd9331c15 - Now checking supportAlterColumnAction in database migration from adapter 2025-07-17 18:40:50 +00:00
Dave Mc Nicoll 3de4b78a0b - Some bugfixes in templates 2025-05-21 18:41:55 +00:00
Dave Mc Nicoll 5b68bb191b - WIP on Mssql migrations 2025-05-19 17:30:42 +00:00
Dave Mc Nicoll d8b208426a - Removed Database (again) from form) 2024-12-09 16:41:44 +00:00
Dave Mc Nicoll 3a8e56a617 - Moved Database into migration folder (form) 2024-12-02 15:25:03 -05:00
mcndave 39b02507a3 - Fixed path entity naming 2024-11-17 19:27:30 -05:00
Dave Mc Nicoll ce2b9399c2 Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2024-11-13 14:04:01 -05:00
Dave Mc Nicoll cd38800131 Merge branch 'notes-2.x' 2024-11-13 14:03:51 -05:00
Dave Mc Nicoll 8c71d0b4d8 - Removed firewall middleware 2024-11-12 13:56:12 +00:00
Dave Mc Nicoll 0b1286c05f - WIP on CLI 2024-11-11 20:19:24 +00:00
Dave Mc Nicoll e260177943 - Removed firewall parts, moved into another module 2024-11-10 13:09:23 +00:00
Dave Mc Nicoll 73fca52013 - Fixed a bug where an empty definition would crash the migration: 2024-11-01 16:14:52 -04:00
Dave Mc Nicoll 474ec986a6 - Work done URL overridding 2024-10-29 14:13:59 +00:00
Dave Mc Nicoll ac568b1750 - WIP on Lean console definition change 2024-08-30 19:01:21 +00:00
Dave Mc Nicoll d76148fed9 Merge branch 'master' into notes-2.x 2024-05-21 13:04:28 +00:00
Dave Mc Nicoll e032c89fdc - Fixed Lean' console default adapter 2024-05-21 13:04:11 +00:00
Dave Mc Nicoll 6001ecdecd - Fixed lean console name 2024-05-20 18:56:39 +00:00
mcndave 38638b5ad4 - WIP on Notes v2.x 2024-05-09 19:48:21 +00:00
mcndave 91ddd4d64c - Ignore most method of ControllerTrait 2024-05-09 19:47:05 +00:00
mcndave fd51903a8a - Started WIP on firewall 2023-12-04 12:32:25 -05:00
mcndave 55f5071475 - Begin work on firewall 2023-11-17 23:35:56 -05:00
mcndave e78ee3a458 - Prepping to begin the firewall module 2023-11-17 22:33:23 -05:00
mcndave a5f305fb7f - Removed an empty style 2023-11-08 06:51:45 -05:00
mcndave a5d8e0a1aa - Added config autoload 2023-11-07 16:25:26 +00:00
mcndave 10317a55f2 - Fixed default layout indentation 2023-11-07 16:22:54 +00:00
mcndave 0de4a9d05a - Merged latest work 2023-11-03 20:21:27 -04:00
mcndave e898c6a23e - Added a new phpinfo menu item: 2023-10-31 16:12:26 +00:00
mcndave 64b9a779de - tmp fix for css path while implementing path from picea-asset url builder 2023-10-31 14:19:11 +00:00
mcndave ce5a97ce45 Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2023-10-30 15:24:26 +00:00
mcndave ea82d8dd08 - WIP on assets 2023-10-30 15:24:12 +00:00
mcndave b64f416fc0 - WIP on asset 2023-10-17 21:59:41 -04:00
mcndave e3de6146ec - WIP on picea's asset. 2023-10-17 20:13:44 -04:00
mcndave 58a96c4b5c - WIP on Lean's autoload component 2023-10-17 18:04:13 +00:00
mcndave 74d88dc8bf - Fixed typo 2023-10-17 17:27:17 +00:00
mcndave 406694ce51 - WIP on Lean's definitions autoload 2023-10-17 17:21:03 +00:00
mcndave 6be483dcf7 - small quick css patch 2023-10-12 13:05:45 -04:00
mcndave 7ec6aa3f06 - Minor adjustment made 2023-09-19 23:51:49 -04:00
mcndave fbfbd48eb8 - Reworked the UI a bit 2023-09-19 23:39:28 -04:00
Dave Mc Nicoll 7b4d1e5cd7 - Small bugfixe in database migration tool 2023-08-31 09:17:29 -04:00
mcndave d133ac662e - Update on database migration 2023-08-29 20:28:45 +00:00
mcndave 5035a65672 - WIP on database migration 2023-07-14 19:14:50 -04:00
Dave Mc Nicoll f66ac02bcd - Working on databases migrations 2023-05-30 15:36:01 -04:00
mcndave 963598764a - WIP on Alter table 2023-03-30 15:25:27 +00:00
mcndave f9229b890d - WIP on SQLite migration 2023-03-27 18:39:20 +00:00
mcndave eda0ee6f33 - Minor bugfixes dones 2023-03-23 15:07:35 +00:00
mcndave bf1bec644b - Still WIP on storage and a new Update function also started 2023-03-22 19:09:37 -04:00
mcndave 689120dd44 Merged attributes with current main 2023-01-31 14:18:00 +00:00
mcndave 6ff29a576c - Some bugfixes were made and annotations were converted into attributes 2023-01-21 20:27:08 +00:00
mcndave 9c8f9903fc Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2022-11-29 19:44:39 +00:00
mcndave 24e5490c71 - Work done on the console to have at least some working functionality from every parts 2022-11-29 19:29:15 +00:00
Dave Mc Nicoll 94b5aff9e9 - Fixed the unmerged code from previous push 2022-11-11 13:58:50 +00:00
Dave Mc Nicoll 9c5cf3d1b1 Merge branch 'master' of https://git.mcnd.ca/mcndave/lean-console 2022-10-10 15:32:12 +00:00
Dave Mc Nicoll da72314c62 - Quickfix from error found during PHP 8 migration 2022-10-10 15:31:55 +00:00
mcndave e78492d398 - Merged latest commit and pushed modifications 2022-05-15 02:45:41 +00:00
mcndave 616d46cdde - Fixed the database interface 2022-05-15 02:41:49 +00:00
60 changed files with 1729 additions and 314 deletions

No files matched your search

@@ -1,43 +1,60 @@
/*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */html,body,p,ol,ul,li,dl,dt,dd,blockquote,figure,fieldset,legend,textarea,pre,iframe,hr,h1,h2,h3,h4,h5,h6{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:left} /*! minireset.css v0.0.6 | MIT License | github.com/jgthms/minireset.css */html,body,p,ol,ul,li,dl,dt,dd,blockquote,figure,fieldset,legend,textarea,pre,iframe,hr,h1,h2,h3,h4,h5,h6{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,*::before,*::after{box-sizing:inherit}img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}td:not([align]),th:not([align]){text-align:left}
.grid{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:-webkit-box;display:flex;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;-webkit-box-flex:0;flex:0 1 auto;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;margin:0 -8px 0 -8px}.grid.grid-nogutter{margin:0}.grid.grid-nogutter>.col{padding:0}.col{box-sizing:border-box;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;-webkit-flex-basis:0;flex-basis:0;max-width:100%;min-width:0;padding:0 8px 0 8px}.col-align-top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.col-align-bottom{align-self:flex-end}.col-align-middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.col-top{justify-content:flex-start !important;flex-direction:column;display:flex}.col-bottom{justify-content:flex-end !important;flex-direction:column;display:flex}.col-middle{justify-content:center;flex-direction:column;display:flex}.grid-start{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.grid-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.grid-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.grid-around{justify-content:space-around}.grid-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.col-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.col-last{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.grid-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.col-fixed{flex:initial}.col-grow-2{flex-grow:2}.col-grow-3{flex-grow:3}.col-grow-4{flex-grow:4}.col-grow-5{flex-grow:5}.col-grow-6{flex-grow:6}.col-grow-7{flex-grow:7}.col-grow-8{flex-grow:8}.col-grow-9{flex-grow:9}.col-grow-10{flex-grow:10}.col-grow-11{flex-grow:11}.col-1{-ms-flex-preferred-size:8.33333%;-webkit-flex-basis:8.33333%;flex-basis:8.33333%;max-width:8.33333%}.col-2{-ms-flex-preferred-size:16.66667%;-webkit-flex-basis:16.66667%;flex-basis:16.66667%;max-width:16.66667%}.col-3{-ms-flex-preferred-size:25%;-webkit-flex-basis:25%;flex-basis:25%;max-width:25%}.col-4{-ms-flex-preferred-size:33.33333%;-webkit-flex-basis:33.33333%;flex-basis:33.33333%;max-width:33.33333%}.col-5{-ms-flex-preferred-size:41.66667%;-webkit-flex-basis:41.66667%;flex-basis:41.66667%;max-width:41.66667%}.col-6{-ms-flex-preferred-size:50%;-webkit-flex-basis:50%;flex-basis:50%;max-width:50%}.col-7{-ms-flex-preferred-size:58.33333%;-webkit-flex-basis:58.33333%;flex-basis:58.33333%;max-width:58.33333%}.col-8{-ms-flex-preferred-size:66.66667%;-webkit-flex-basis:66.66667%;flex-basis:66.66667%;max-width:66.66667%}.col-9{-ms-flex-preferred-size:75%;-webkit-flex-basis:75%;flex-basis:75%;max-width:75%}.col-10{-ms-flex-preferred-size:83.33333%;-webkit-flex-basis:83.33333%;flex-basis:83.33333%;max-width:83.33333%}.col-11{-ms-flex-preferred-size:91.66667%;-webkit-flex-basis:91.66667%;flex-basis:91.66667%;max-width:91.66667%}.col-12{-ms-flex-preferred-size:100%;-webkit-flex-basis:100%;flex-basis:100%;max-width:100%}@media only screen and (max-width: 480px){.col-sm{flex:100%;max-width:100%}}@media only screen and (max-width: 624px){.col-md{flex:100%;max-width:100%}}@media only screen and (max-width: 744px){.col-lg{flex:100%;max-width:100%}} .grid{box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:-webkit-box;display:flex;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;-webkit-box-flex:0;flex:0 1 auto;-webkit-flex-direction:row;-ms-flex-direction:row;-webkit-box-orient:horizontal;-webkit-box-direction:normal;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;margin:0 -8px 0 -8px}.grid.grid-nogutter{margin:0}.grid.grid-nogutter>.col{padding:0}.col{box-sizing:border-box;-webkit-flex:0 0 auto;-ms-flex:0 0 auto;flex:0 0 auto;-webkit-flex-grow:1;-ms-flex-positive:1;-webkit-box-flex:1;flex-grow:1;-ms-flex-preferred-size:0;-webkit-flex-basis:0;flex-basis:0;max-width:100%;min-width:0;padding:0 8px 0 8px}.col-align-top{-webkit-align-self:flex-start;-ms-flex-item-align:start;align-self:flex-start}.col-align-bottom{align-self:flex-end}.col-align-middle{-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.col-top{justify-content:flex-start !important;flex-direction:column;display:flex}.col-bottom{justify-content:flex-end !important;flex-direction:column;display:flex}.col-middle{justify-content:center;flex-direction:column;display:flex}.grid-start{-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.grid-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.grid-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.grid-around{justify-content:space-around}.grid-between{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.col-first{-webkit-box-ordinal-group:0;-ms-flex-order:-1;order:-1}.col-last{-webkit-box-ordinal-group:2;-ms-flex-order:1;order:1}.grid-reverse{-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-ms-flex-direction:row-reverse;flex-direction:row-reverse}.col-fixed{flex:initial}.col-grow-2{flex-grow:2}.col-grow-3{flex-grow:3}.col-grow-4{flex-grow:4}.col-grow-5{flex-grow:5}.col-grow-6{flex-grow:6}.col-grow-7{flex-grow:7}.col-grow-8{flex-grow:8}.col-grow-9{flex-grow:9}.col-grow-10{flex-grow:10}.col-grow-11{flex-grow:11}.col-1{-ms-flex-preferred-size:8.33333%;-webkit-flex-basis:8.33333%;flex-basis:8.33333%;max-width:8.33333%}.col-2{-ms-flex-preferred-size:16.66667%;-webkit-flex-basis:16.66667%;flex-basis:16.66667%;max-width:16.66667%}.col-3{-ms-flex-preferred-size:25%;-webkit-flex-basis:25%;flex-basis:25%;max-width:25%}.col-4{-ms-flex-preferred-size:33.33333%;-webkit-flex-basis:33.33333%;flex-basis:33.33333%;max-width:33.33333%}.col-5{-ms-flex-preferred-size:41.66667%;-webkit-flex-basis:41.66667%;flex-basis:41.66667%;max-width:41.66667%}.col-6{-ms-flex-preferred-size:50%;-webkit-flex-basis:50%;flex-basis:50%;max-width:50%}.col-7{-ms-flex-preferred-size:58.33333%;-webkit-flex-basis:58.33333%;flex-basis:58.33333%;max-width:58.33333%}.col-8{-ms-flex-preferred-size:66.66667%;-webkit-flex-basis:66.66667%;flex-basis:66.66667%;max-width:66.66667%}.col-9{-ms-flex-preferred-size:75%;-webkit-flex-basis:75%;flex-basis:75%;max-width:75%}.col-10{-ms-flex-preferred-size:83.33333%;-webkit-flex-basis:83.33333%;flex-basis:83.33333%;max-width:83.33333%}.col-11{-ms-flex-preferred-size:91.66667%;-webkit-flex-basis:91.66667%;flex-basis:91.66667%;max-width:91.66667%}.col-12{-ms-flex-preferred-size:100%;-webkit-flex-basis:100%;flex-basis:100%;max-width:100%}@media only screen and (max-width: 480px){.col-sm{flex:100%;max-width:100%}}@media only screen and (max-width: 624px){.col-md{flex:100%;max-width:100%}}@media only screen and (max-width: 744px){.col-lg{flex:100%;max-width:100%}}
.align-items-center{align-items:center}.align-items-top{align-items:start} .align-items-center{align-items:center}.align-items-top{align-items:start}
.justify-content-around{justify-content: space-around;}.justify-content-end{justify-content: end}
.flex {display:flex}
:root { :root {--color-red: #d47474}
--color-red: #d47474
}
.text-center{text-align:center;}.text-right{text-align:right}.text-left{text-align:left;} .text-center{text-align:center;}.text-right{text-align:right}.text-left{text-align:left;}
.m-0 {margin:0!important}.mx-0 {margin-left:0!important;margin-right:0!important}.my-0 {margin-top:0!important;margin-bottom:0!important}
.p-0 {padding:0!important}.px-0 {padding-left:0!important;padding-right:0!important}.py-0 {padding-top:0!important;padding-bottom:0!important}
[class^="btn-"], [class~=" btn-"] { display: inline-block; font-weight: 400; text-align: center; white-space: nowrap; vertical-align: middle; user-select: none; border: 1px solid transparent; padding: .375rem .75rem; font-size: 1rem; line-height: 1.5; border-radius: .25rem; cursor:pointer; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; background:rgba(0,0,0,0.3); color:rgba(255,255,255,0.8); } [class^="btn-"], [class~=" btn-"] { display: inline-block; font-weight: 400; font-size:16px; text-align: center; white-space: nowrap; vertical-align: middle; user-select: none; border: 1px solid transparent; padding: .375rem .75rem; line-height: 1.5; border-radius: .25rem; cursor:pointer; transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out; background:rgba(0,0,0,0.3); color:rgba(255,255,255,0.8); text-decoration:none; }
[class^="btn-"]:hover, [class~=" btn-"]:hover {border-color: rgba(0, 0, 0, 0.5);text-decoration: underline;} [class^="btn-"]:hover, [class~=" btn-"]:hover {border-color: rgba(0, 0, 0, 0.5);text-decoration: underline;}
.btn-red {background: rgba(255,0,0,0.5);color: rgba(255, 255, 255, 0.9);} .btn-red {background: rgba(155,0,0,0.5);color: rgba(255, 255, 255, 0.9);}
.btn-green {background: rgba(0,155,0,0.5);color: rgba(255, 255, 255, 0.9);}
.btn-blue {background: rgba(0,0,155,0.5);color: rgba(255, 255, 255, 0.9);}
.btn-yellow {background: rgba(249, 200, 11, 0.69);color: rgba(255, 255, 255, 0.9);}
.bg-yellow {background:#c09f1f!important}
code {white-space:pre;font-family:monospace;color: #cacaca;background: #434446;display: block;padding: 1rem;line-height: 1.5em;border: 1px solid #2e2e30;} body{background:#f3f3f9}
h1 {font-size:2rem;color:#525960} code {white-space:pre-line;font-family:monospace;color: #e6e6e6;background: #343537;display: block;padding: 1rem;line-height: 1.5em;border: 1px solid #2e2e30;}
body {font-family:Helvetica Neue, Helvetica, Arial, sans-serif;} body {font-family:Helvetica Neue, Helvetica, Arial, sans-serif;}
summary {cursor:pointer;} summary {cursor:pointer;}
#body-main {background:#ccc;min-height:calc(100vh - 90px);display:flex}
#body-main {min-height:calc(100vh - 30px);display:flex}
/* Header */ /* Header */
#body-header {background: #6b7fa8; color:#fff; min-height:60px;line-height:60px;padding:0 1rem} #body-header {background: #fff; color:#fff; min-height:60px;line-height:60px;padding:0 1rem;position:sticky;top:0;border-bottom:1px solid #ebedee;}
#body-header .page-title {font-size:1.5rem} #body-header > div {min-height: 60px;}
#body-header .page-title:before {content:"🔳"; margin-right:10px} #right-panel {max-width:calc(100% - 240px);width:100%;margin-left:auto}
#subheader {background:#fff; padding: 1rem 1.4rem;display:flex;justify-content: space-between;box-shadow:0 1px 2px rgba(55,65,75,0.15)}
#subheader h1 {font-size:1.1rem;font-size:inherit; font-weight:bold}
#subheader .breadcrumb {font-size:0.9rem;display:flex;align-items:end}
#subheader .breadcrumb-item + .breadcrumb-item:before {content:"›";padding:0 6px;}
/* Footer */ /* Footer */
#body-footer {background: #475262;color: #fff;height: 30px;line-height: 30px;font-size: 0.8rem;padding: 0 0.5rem;text-align: right;border-top:2px solid #434446;} #body-footer {background: #fff;color: #333;height: 30px;line-height: 30px;font-size: 0.8rem;padding: 0 0.5rem;text-align: right;border-top:1px solid #ebedee;}
/* Sidebar */ /* Sidebar */
#main-nav {background:#6b7fa8;border-top:2px solid #44526f;min-width:210px;color:#fff;font-family:consolas, courier new, monospace;font-size:0.9rem} #main-nav {background:#373737;width:240px;color:#fff;font-family:consolas, courier new, monospace;font-size:0.9rem;position:fixed;top:0;bottom:0;overflow-y:auto;padding-bottom:2rem;border-right: 2px solid #333;}
#main-nav label {background:rgba(0,0,0,0.2);text-transform:uppercase ; display:block;padding:0.8rem 1rem;font-size:1.15em;} #main-nav .app-title {font-size:1.3rem; text-align:center;padding:1.8rem 0}
#main-nav .app-title:before {content:"🔳"; margin-right:10px}
#main-nav label {color: #a4a4a4;text-transform:uppercase ; display:block;padding:0.8rem 1rem;font-size:1.15em;}
#main-nav label:before {content:"⊙"; margin-right:10px} #main-nav label:before {content:"⊙"; margin-right:10px}
#main-nav a {color: #efecec;text-decoration:none;display:block;padding:0rem 1rem;background:rgba(255,255,255,0.05);transition:all 0.3s ease;line-height:3em;font-size:0.9em} #main-nav a {color: #efecec;text-decoration:none;display:block;padding:0 1rem 0 2rem;transition:all 0.3s ease;line-height:2.5em;font-size:0.9em}
#main-nav a:before {content:"›"; margin-right:10px;line-height:3em} #main-nav a:before {content:"›"; margin-right:10px;}
#main-nav a:hover {background:rgba(255,255,255,0.1)} #main-nav a:hover {background:rgba(255,255,255,0.1)}
#main-nav li + li {border-top:1px solid #555}
/* Main content */ /* Main content */
#main-content {padding:4vh 2vw;border-top:2px solid #7d7d7d;width:100%;} #main-content {padding:4vh 2vw;width:100%;margin-left:auto;}
#main-content section {border-radius:4px;box-shadow: 1px 1px 3px rgba(0,0,0,0.5)} #main-content section {border-radius:0.3rem;box-shadow: 0 1px 2px rgba(56,65,74,.15);display: flex;flex-direction: column;}
#main-content section article {background:#fff;padding:2vh 1vw;} #main-content section article {border-radius:0.3rem;font-family: Courier New , Courier, monospace;font-size: 0.8em;background:#fff;padding:1.5rem;border: 1px solid #00000029;}
#main-content section header {background:#e8e8e8;padding:2vh 1vw;} #main-content section header + article {border-top:0;border-top-left-radius: unset;border-top-right-radius: unset}
#main-content section header:has(+ article) {border: 1px solid #00000029;border-bottom:0}
#main-content section header {border-top-left-radius:0.3rem;border-top-right-radius:0.3rem;background:#373737;padding:1rem 1rem;font-size:1.2em;color: #fff;border-bottom:1px solid #7b7b7b;display:flex;align-items:center}
#main-content section article:has(+ footer) {border-bottom-left-radius:unset;border-bottom-right-radius:unset;border-bottom:0}
#main-content section footer {border-bottom-left-radius:0.3rem;border-bottom-right-radius:0.3rem;background:#f9f9f9;padding:1.5vh 1vw;line-height:1.5vh}
#main-content section article + footer {border: 1px solid #00000029;border-top:0}
#main-content section article [class*="col-"] {overflow-x:auto}
+15 -2
View File
@@ -6,15 +6,28 @@
"authors": [ "authors": [
{ {
"name": "Dave Mc Nicoll", "name": "Dave Mc Nicoll",
"email": "mcndave@gmail.com" "email": "info@mcnd.ca"
} }
], ],
"require": { "require": {
"mcnd/lean": "dev-master" "mcnd/lean": "^1.4.2"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Lean\\Console\\": "src/" "Lean\\Console\\": "src/"
} }
},
"extra" : {
"lean" : {
"autoload": {
"definitions" : [
"meta/definitions/software.php",
"meta/definitions/storage.php"
],
"config": [
"meta/config.php"
]
}
}
} }
} }
+20
View File
@@ -0,0 +1,20 @@
<?php
return [
'lean' => [
'autoload' => [
getenv('DEBUG') ? 'lean.console' : null,
]
],
'ulmus' => [
'connections' => [
'lean.console' => [
'adapter' => 'SQLite',
'path' => getenv('PROJECT_PATH') . DIRECTORY_SEPARATOR . "var/lean-console.sqlite3",
'pragma_begin' => array_filter([ "foreign_keys=ON", "synchronous=NORMAL", getenv('DEBUG') ? "journal_mode=WAL" : "" ]),
'pragma_close' => [ "analysis_limit=500", "optimize" ],
],
],
],
];
+24 -5
View File
@@ -2,30 +2,49 @@
use Lean\Console\Lib\DatabaseMigrations; use Lean\Console\Lib\DatabaseMigrations;
$path = dirname(__DIR__, 2);
return [ return [
'lean.console' => [ 'lean.console' => [
'picea' => [ 'picea' => [
'context' => Lean\Console\View::class,
'view' => [ 'view' => [
[ [
'path' => getenv("PROJECT_PATH") . "/vendor/mcnd/lean-console/view/", 'path' => implode(DIRECTORY_SEPARATOR, [ $path , "view", '' ]),
'order' => 99, 'order' => 99,
], ],
], ],
'asset' => [
[
'path' => implode(DIRECTORY_SEPARATOR, [ $path, "asset", '' ]),
'order' => 10
],
],
], ],
'ulmus' => [], 'ulmus' => [
'entities' => [
Lean\Console\Entity\Firewall::class => implode(DIRECTORY_SEPARATOR, [ $path , "src", "Entity", "Firewall" ]),
],
],
'tell' => [ 'tell' => [
'json' => [ 'json' => [
[ [
'path' => getenv("PROJECT_PATH") . "/vendor/mcnd/lean-console/meta/i18n", 'path' => implode(DIRECTORY_SEPARATOR, [ $path, "meta", "i18n", "" ]),
'order' => 99, 'order' => 99,
], ],
] ]
], ],
'routes' => [ 'cli' => [
'Lean\\Console\\Controller' => getenv("PROJECT_PATH") . "/vendor/mcnd/lean-console/src/Controller/", 'Lean\\Console\\Cli' => implode(DIRECTORY_SEPARATOR, [ $path, "src", "Cli", "" ]),
],
'routes' => ! getenv('DEBUG') ? [] : [
Lean\Console\Controller::class => implode(DIRECTORY_SEPARATOR, [ $path, "src", "Controller", "" ]),
], ],
], ],
+27
View File
@@ -0,0 +1,27 @@
<?php
use Psr\Container\ContainerInterface;
use Ulmus\ConnectionAdapter,
Ulmus\Container\AdapterProxy;
use Storage\Session;
use function DI\autowire, DI\create, DI\get;
return [
/* 'lean:adapter.sqlite' => function($c) {
$adapter = new ConnectionAdapter('lean.console', $c->get('config')['ulmus'], false);
$adapter->resolveConfiguration();
return $adapter;
},
# @TODO BACKWARD COMPATIBILIY, TO BE REMOVED SOON !
'console.adapter' => function($c) {
$adapter = new ConnectionAdapter('lean.console', $c->get('config')['ulmus'], false);
$adapter->resolveConfiguration();
return $adapter;
}*/
];
+6
View File
@@ -0,0 +1,6 @@
{
"title": "Caching -- LEAN",
"page-title": "LEAN - Caching",
"clear": "Clear",
"breadcrumb": "Caching"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"title": "Console -- LEAN", "title": "Console -- LEAN",
"page-title": "LEAN - Console" "page-title": "Console"
} }
+10
View File
@@ -0,0 +1,10 @@
{
"title" : "Debugging -- LEAN",
"page-title": "Debugging",
"logs": {
"breadcrumb": "Logs"
},
"phpinfo": {
"breadcrumb": "phpinfo()"
}
}
+9 -1
View File
@@ -1,7 +1,8 @@
{ {
"lean": { "lean": {
"label": "Lean", "label": "Lean",
"dashboard": "Dashboard" "dashboard": "Dashboard",
"update": "Update(s)"
}, },
"storage": { "storage": {
@@ -20,6 +21,13 @@
"label": "Caching" "label": "Caching"
}, },
"debugging": {
"label": "Debugging",
"logs": "Logs",
"phpinfo": "PhpInfo"
},
"request": { "request": {
"label": "Request", "label": "Request",
"routes": "Routes" "routes": "Routes"
+1
View File
@@ -2,6 +2,7 @@
"title": "Request -- LEAN", "title": "Request -- LEAN",
"page-title": "LEAN - Request", "page-title": "LEAN - Request",
"route": { "route": {
"breadcrumb": "Routes",
"header": "List of active routes", "header": "List of active routes",
"name": "Name", "name": "Name",
"uri": "URI", "uri": "URI",
+22 -4
View File
@@ -4,22 +4,40 @@
"database": { "database": {
"header": "List of databases", "header": "List of databases",
"breadcrumb": "Database",
"name": "Database", "name": "Database",
"adapter": "Adapter", "adapter": "Adapter",
"host": "Hostname:port", "host": "Hostname:port",
"username": "Username", "username": "Username",
"connection": "Connection", "connection": "Connection",
"table": { "table": {
"header": "Tables details", "header": "Tables details",
"fields": "{$count} fields", "fields": "{$count} fields",
"name": "Name", "name": "Table Name",
"actions": "Actions", "actions": "Actions",
"count": "Entries",
"table-fields" : "Fields in table", "table-fields" : "Fields in table",
"create": "Create table", "create": "Create table",
"createAll": "Create all tables", "createAll": "Create all tables",
"query": "SQL Query" "query": "SQL Query",
} "add-column": "+ New row",
"alter": "Alter table",
"alterAll": "Alter all tables",
"uptodate": "This table is up-to-date"
} }
},
"database_insert": {
"add": "Create",
"breadcrumb": "Insert",
"readonly": "This field is read-only",
"table": {
"column": "Column",
"type": "Data type",
"function": "Function",
"attributes": "Attributs",
"value": "Value"
}
}
} }
+6
View File
@@ -0,0 +1,6 @@
{
"title": "Templating -- LEAN",
"page-title": "LEAN - Templating",
"breadcrumb": "Templating",
"header": "Views"
}
+8
View File
@@ -0,0 +1,8 @@
{
"title": "Update -- LEAN",
"page-title": "LEAN - Update",
"git": {
"breadcrumb": "Update",
"header": "Git Status"
}
}
-1
View File
@@ -2,6 +2,5 @@
"title" : "Console -- LEAN", "title" : "Console -- LEAN",
"page-title": "Console", "page-title": "Console",
"breadcrumb" : { "breadcrumb" : {
"index" : "Negundo"
} }
} }
+11
View File
@@ -0,0 +1,11 @@
{
"title" : "Debugging -- LEAN",
"page-title": "Déboguage",
"logs": {
"breadcrumb": "Journaux d'événements",
"line": "Lines number: "
},
"phpinfo": {
"breadcrumb": "phpinfo()"
}
}
+6
View File
@@ -20,6 +20,12 @@
"label": "Caching" "label": "Caching"
}, },
"debugging": {
"label": "Déboguage",
"logs": "Journaux d'événements",
"phpinfo": "PhpInfo"
},
"request": { "request": {
"label": "Request", "label": "Request",
"routes": "Routes" "routes": "Routes"
+6 -1
View File
@@ -13,13 +13,18 @@
"table": { "table": {
"header": "Tables details", "header": "Tables details",
"fields": "{$count} fields", "fields": "{$count} fields",
"name": "Name", "name": "Table Name",
"actions": "Actions", "actions": "Actions",
"table-fields" : "Fields in table", "table-fields" : "Fields in table",
"create": "Create table", "create": "Create table",
"createAll": "Create all tables", "createAll": "Create all tables",
"query": "SQL Query" "query": "SQL Query"
} }
},
"session": {
"header": "Liste des sessions",
"breadcrumb": "Sessions",
"count": "{$count} session(s)"
} }
} }
+7
View File
@@ -0,0 +1,7 @@
{
"title" : "Console -- LEAN",
"page-title": "Console",
"breadcrumb" : {
"index" : "Negundo"
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Lean\Console\Cli;
use Mcnd\CLI\OptionValueTypeEnum;
use Notes\CLI\Attribute\{Option, Command};
use Psr\Http\Message\{ServerRequestInterface, ResponseInterface};
use Lean\Console\{Lib, };
# https://www.asciiart.eu/text-to-ascii-art
#[Command(description: PHP_EOL . <<<CLI
#[color:gray, bg: black]╔═════════════════════════╗[#]
#[color:gray, bg: black]║ _ ║[#]
#[color:gray, bg: black]║ | | ___ __ _ _ __ ║[#]
#[color:gray, bg: black]║ | | / _ \/ _` | '_ \ ║[#]
#[color:gray, bg: black]║ | |__| __/ (_| | | | | ║[#]
#[color:gray, bg: black]║ |_____\___|\__,_|_| |_| ║[#]
#[color:gray, bg: black]║ ║[#]
#[color:gray, bg: black]╚═════════════════════════╝[#]
Try #[color:gray, bg: black]'cli lean-console --help'[#] for more information.
CLI
, command: 'lean-console')]
#[Option(switch: ['-h', '--help'], description: "Display help for the given command. When no command is given display help for the list command")]
#[Option(switch: ['-q', '--quiet'], description: "Do not output any message")]
#[Option(switch: ['-n', '--no-interaction'], description: "Run script without user interaction")]
#[Option(switch: ['-v', '-vv', '-vvv', '--verbose'], description: "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", value: OptionValueTypeEnum::OptionalValue)]
class Console
{
use Lib\ConsoleControllerTrait;
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Lean\Console\Cli;
use Mcnd\CLI\OptionValueTypeEnum;
use Notes\CLI\Attribute\{Option, Command};
use Psr\Http\Message\{ServerRequestInterface, ResponseInterface};
use Lean\Console\{Lib, };
# https://www.asciiart.eu/text-to-ascii-art
#[Command(description: PHP_EOL . <<<CLI
#[color:gray, bg: black]╔═════════════════════════╗[#]
#[color:gray, bg: black]║ _ ║[#]
#[color:gray, bg: black]║ | | ___ __ _ _ __ ║[#]
#[color:gray, bg: black]║ | | / _ \/ _` | '_ \ ║[#]
#[color:gray, bg: black]║ | |__| __/ (_| | | | | ║[#]
#[color:gray, bg: black]║ |_____\___|\__,_|_| |_| ║[#]
#[color:gray, bg: black]║ ║[#]
#[color:gray, bg: black]║ STORAGE ║[#]
#[color:gray, bg: black]╚═════════════════════════╝[#]
Try #[color:gray, bg: black]'cli lean-console storage --help'[#] for more information.
CLI
, command: 'lean-console storage')]
#[Option(switch: ['-h', '--help'], description: "Display help for the given command. When no command is given display help for the list command")]
#[Option(switch: ['-q', '--quiet'], description: "Do not output any message")]
#[Option(switch: ['-n', '--no-interaction'], description: "Run script without user interaction")]
#[Option(switch: ['-v', '-vv', '-vvv', '--verbose'], description: "Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug", value: OptionValueTypeEnum::OptionalValue)]
class Storage
{
use Lib\ConsoleControllerTrait;
#[Command(description: "Generate entities based on an existing connection", command: "entity:generate")]
#[Option(switch: ['-l', '--list'], description: "")]
public function generate_entity(ServerRequestInterface $request, array $arguments): ResponseInterface
{
dump($arguments);
return $this->renderCLI($request, ":)");
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Lean\Console\Controller;
use League\Route\Router;
use Picea\{FileFetcher, Picea, Caching\Opcache, Extension\UrlExtension, Ui\Method\FormHandler};
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Lean\Console\{ Form, Lib };
use Notes\Tell\Attribute\Language, Notes\Route\Attribute\Method\Route;
class Caching extends Console {
use Lib\ConsoleControllerTrait;
#[Route(route: "/caching/picea", name: "lean.console:caching")]
public function picea(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$list = [];
if ( isset($this->picea) ) {
new FormHandler($request, new Form\Caching\Picea($this->picea, $list), $this->pushContext(new Lib\FormContext($request, "caching.picea")));
}
return $this->renderView("lean-console/page/caching/picea", get_defined_vars());
}
}
+16 -11
View File
@@ -2,28 +2,33 @@
namespace Lean\Console\Controller; namespace Lean\Console\Controller;
use Picea\Ui\Method\FormHandler;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface }; use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Notes\Route\Annotation\Object\Route as RouteParam, use Notes\Tell\Attribute\Language, Notes\Route\Attribute\Method\Route;
Notes\Route\Annotation\Method\Route,
Notes\Security\Annotation\Security,
Notes\Tell\Annotation\Language;
use \Lean\Console\Lib; use \Lean\Console\{ Lib, Form };
/**
* @Language("lean.console")
*/
class Console { class Console {
use Lib\ConsoleControllerTrait; use Lib\ConsoleControllerTrait;
/** #[Route(route: "/", name: "lean.console:home")]
* @Route("/", "name" => "lean.console:home")
*/
public function home(ServerRequestInterface $request, array $arguments) : ResponseInterface public function home(ServerRequestInterface $request, array $arguments) : ResponseInterface
{ {
return $this->renderView("lean-console/page/dashboard/index", [ return $this->renderView("lean-console/page/dashboard/index", [
]); ]);
} }
#[Route(route: "/update", name: "lean.console:update")]
public function update(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
new FormHandler($request, $git = new Form\Update\Git(getenv('PROJECT_PATH')));
$status = iterator_to_array($git->run("/usr/bin/git status -s"));
$diff = iterator_to_array($git->run("/usr/bin/git diff"));
$lastUpdate = iterator_to_array($git->run("/usr/bin/git show --no-patch --format=%ci"))[0] ?? null;
return $this->renderView("lean-console/page/update/index", get_defined_vars());
}
} }
+44
View File
@@ -0,0 +1,44 @@
<?php
namespace Lean\Console\Controller;
use Picea\{Ui\Method\FormHandler};
use Lean\File;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Lean\Console\{ Form, Lib };
use Notes\Route\Attribute\Method\Route;
class Debugging extends Console {
use Lib\ConsoleControllerTrait;
#[Route(route: "/debugging/logs", name: "lean.console:debugging-logs")]
public function logs(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$list = [];
# new FormHandler($request, new Form\Debugging\Logs(), $this->pushContext(new Lib\FormContext($request, "debugging.logs")));
$path = getenv('LOGS_PATH');
$files = array_map(fn($e) => basename($e), glob("$path/*"));
$default = basename(ini_get('error_log'));
$open = File::sanitizeFilePath($request->getQueryParams()['logfile'] ?? end($files));
$content = File::tail($path . DIRECTORY_SEPARATOR . $open, $request->getQueryParams()['line'] ?? 100);
return $this->renderView("lean-console/page/debugging/logs", get_defined_vars());
}
#[Route(route: "/debugging/phpinfo", name: "lean.console:debugging-phpinfo")]
public function phpinfo(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
ob_start() ;
phpinfo() ;
$phpinfo = ob_get_contents() ;
ob_end_clean() ;
return $this->renderView("lean-console/page/debugging/phpinfo", get_defined_vars());
}
}
-28
View File
@@ -1,28 +0,0 @@
<?php
namespace Lean\Console\Controller;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Notes\Route\Annotation\Object\Route as RouteParam,
Notes\Route\Annotation\Method\Route,
Notes\Security\Annotation\Security,
Notes\Tell\Annotation\Language;
use \Lean\Console\Lib;
/**
* @Language("lean.dev")
*/
class Dev {
use Lib\ConsoleControllerTrait;
/**
* @Route("/phpinfo", "name" => "lean.dev:phpinfo")
*/
public function home(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
phpinfo();
die();
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Lean\Console\Controller;
class Firewall
{
}
+13 -19
View File
@@ -3,39 +3,33 @@
namespace Lean\Console\Controller; namespace Lean\Console\Controller;
use League\Route\Router; use League\Route\Router;
use Lean\Routing;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface }; use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Notes\Route\Annotation\Object\Route as RouteParam, use Notes\Tell\Attribute\Language, Notes\Route\Attribute\Method\Route;
Notes\Route\Annotation\Method\Route,
Notes\Security\Annotation\Security,
Notes\Tell\Annotation\Language;
use \Lean\Console\Lib; use \Lean\Console\Lib;
use Picea\Extension\UrlExtension; use Picea\Extension\UrlExtension;
/**
* @Language("lean.request")
*/
class Request extends Console { class Request extends Console {
use Lib\ConsoleControllerTrait; use Lib\ConsoleControllerTrait;
/** #[Route(route: "/request/routes", name: "lean.console:request.route")]
* @Route("/request/routes", "name" => "lean.console:request.route")
* @param ServerRequestInterface $request
* @param array $arguments
* @return ResponseInterface
*/
public function routes(ServerRequestInterface $request, array $arguments) : ResponseInterface public function routes(ServerRequestInterface $request, array $arguments) : ResponseInterface
{ {
$routes = $this->container->has(UrlExtension::class) ? $this->container->get(UrlExtension::class)->getRouteList(true) : false; $routing = $this->container->has(Routing::class) ? $this->container->get(Routing::class) : false;
uksort($routes, function($a, $b) { if ( $routing === false ) {
return $a <=> $b; throw new \Exception("Lean's routing library do not seems to be integrated into your container definition...");
}
$routes = $routing->fetchRoutesAttributes();
uasort($routes, function($a, $b) {
return ( $a->class . $a->name ) <=> ( $b->class . $b->name );
}); });
return $this->renderView("lean-console/page/request/route", [ return $this->renderView("lean-console/page/request/route", get_defined_vars());
'routes' => $routes
]);
} }
} }
+52 -20
View File
@@ -7,10 +7,7 @@ use League\Route\Router;
use Picea\Ui\Method\FormHandler; use Picea\Ui\Method\FormHandler;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface }; use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Notes\Route\Annotation\Object\Route as RouteParam, use Notes\Tell\Attribute\Language, Notes\Route\Attribute\Method\Route;
Notes\Route\Annotation\Method\Route,
Notes\Security\Annotation\Security,
Notes\Tell\Annotation\Language;
use \Lean\Console\Lib, use \Lean\Console\Lib,
\Lean\Console\Form; \Lean\Console\Form;
@@ -20,36 +17,71 @@ use Picea\Extension\UrlExtension;
use Ulmus\Annotation\Classes\Table, use Ulmus\Annotation\Classes\Table,
Ulmus\Container\AdapterProxy; Ulmus\Container\AdapterProxy;
/**
* @Language("lean.storage")
*/
class Storage extends Console { class Storage extends Console {
use Lib\ConsoleControllerTrait; use Lib\ConsoleControllerTrait;
/** #[Route(route: "/storage/database", name: "lean.console:storage.database")]
* @Route("/storage/database", "name" => "lean.console:request.route")
* @param ServerRequestInterface $request
* @param array $arguments
* @return ResponseInterface
*/
public function database(ServerRequestInterface $request, array $arguments) : ResponseInterface public function database(ServerRequestInterface $request, array $arguments) : ResponseInterface
{ {
$connections = $this->container->has(AdapterProxy::class) ? $this->container->get(AdapterProxy::class)->connections : false; $connections = $this->container->has(AdapterProxy::class) ? $this->container->get(AdapterProxy::class)->connections : false;
$migrations = $this->container->has(Lib\DatabaseMigrations::class) ? $this->container->get(Lib\DatabaseMigrations::class) : false; $migrations = $this->container->has(Lib\DatabaseMigrations::class) ? $this->container->get(Lib\DatabaseMigrations::class) : false;
$migrations->getEntities(); $migrations->getEntities();
$form = new FormHandler($request, new Form\Database($migrations), null); $context = (new FormHandler($request, new Form\Storage\Database\Migrate($migrations), $this->pushContext(new Lib\FormContext($request, "database.migrate"))))->context;
$context = $form->context;
return $this->renderView("lean-console/page/storage/database", get_defined_vars()); return $this->renderView("lean-console/page/storage/database", get_defined_vars());
} }
public function createTable() : void #[Route(route: "/storage/database/import", name: "lean.console:storage.database_import")]
public function database_import(ServerRequestInterface $request, array $arguments) : ResponseInterface
{ {
foreach($migrations->entities as $table) { $connections = $this->container->has(AdapterProxy::class) ? $this->container->get(AdapterProxy::class)->connections : false;
// $repository = $table $migrations = $this->container->has(Lib\DatabaseMigrations::class) ? $this->container->get(Lib\DatabaseMigrations::class) : false;
// $entity::repository()->createTable(); $migrations->getEntities();
$form = new Form\Storage\Database\Import($migrations, $connections);
$context = (new FormHandler($request, $form, $this->pushContext(new Lib\FormContext($request, "database.import"))))->context;
return $this->renderView("lean-console/page/storage/database_import", get_defined_vars());
} }
#[Route(route: "/storage/database/insert", name: "lean.console:storage.database_insert")]
public function insert(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$class = $request->getQueryParams()['source'] ?? $request->getParsedBody()['source'];
$context = (new FormHandler($request, new Form\Storage\Database\Insert(new $class())))->context;
return $this->renderView("lean-console/page/storage/database_insert", get_defined_vars());
}
#[Route(route: "/storage/session", name: "lean.console:storage.session")] # migrated from: "/storage/session", "name" => "lean.console:storage.session"
public function session(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$path = ini_get("session.save_path");
$sessions = preg_grep("/^sess_/", scandir($path));
return $this->renderView("lean-console/page/storage/session", get_defined_vars());
}
#[Route(route: "/storage/session/content/{hash}", name: "lean.console:storage.session_content")] # migrated from: "/storage/session/content/{hash}", "name" => "lean.console:storage.session_content"
public function sessionContent(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$sess = session_encode();
session_decode(file_get_contents($this->sessionPath() . $arguments['hash']));
$data = array_combine(array_keys($_SESSION), array_values($_SESSION));
session_decode($sess);
return $this->renderJson($data);
}
protected function sessionPath() : string
{
return rtrim(ini_get("session.save_path"), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
} }
} }
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Lean\Console\Controller;
use League\Route\Router;
use Picea\FileFetcher;
use Picea\Picea;
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
use Notes\Tell\Attribute\Language, Notes\Route\Attribute\Method\Route;
use \Lean\Console\Lib;
use Picea\Extension\UrlExtension;
class Templating extends Console {
use Lib\ConsoleControllerTrait;
#[Route(route: "/templating/picea", name: "lean.console:templating.picea")]
public function picea(ServerRequestInterface $request, array $arguments) : ResponseInterface
{
$picea = $this->container->has(Picea::class) ? $this->container->get(Picea::class) : false;
$list = $picea->fileFetcher->getFileList();
return $this->renderView("lean-console/page/templating/picea", get_defined_vars());
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace Lean\Console\Entity\Mysql;
use Ulmus\Common\EntityField;
use Ulmus\Entity\Field\Datetime,
Ulmus\Entity\InformationSchema;
use Ulmus\EntityCollection;
use Ulmus\Migration\FieldDefinition;
use Ulmus\Attribute\Obj\Table;
#[Table(name: "columns", database: "information_schema")] # migrated from: 'name' => "columns", 'database' => "information_schema"
class Column extends InformationSchema\Column
{
public function differsFromFieldDefinition(FieldDefinition $definition) : bool
{
switch(true) {
//case $definition->
// return true;
}
return false;
}
/* public static function entityCollection(...$arguments) : EntityCollection
{
return new class(...$arguments) extends EntityCollection
{
public function getEntityData() : array
{
return [
];
}
};
}
*/
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Lean\Console\Entity\Mysql;
use Ulmus\Common\EntityField;
use Ulmus\Entity\Field\Datetime,
Ulmus\Entity\Sqlite;
use Ulmus\EntityCollection;
use Ulmus\Migration\FieldDefinition;
use Ulmus\Attribute\Obj\Table;
#[Table(name: "columns", database: "information_schema")]
class Column extends Sqlite
{
public function differsFromFieldDefinition(FieldDefinition $definition) : bool
{
switch(true) {
//case $definition->
// return true;
}
return false;
}
/* public static function entityCollection(...$arguments) : EntityCollection
{
return new class(...$arguments) extends EntityCollection
{
public function getEntityData() : array
{
return [
];
}
};
}
*/
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace Lean\Console\Form\Caching;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\Lib;
use Picea\{Caching\Opcache, Compiler\Context };
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\Common\EntityResolver;
use \Ulmus\Entity\InformationSchema\Table;
use RecursiveIteratorIterator, RecursiveDirectoryIterator;
class Picea implements FormInterface
{
public \Picea\Picea $picea;
public array $folders;
public function __construct(\Picea\Picea $picea, array &$folders)
{
$this->folders = &$folders;
$this->picea = $picea;
}
public function initialize(FormContextInterface $context) : void
{
if ($this->picea->cache instanceof Opcache) {
$path = $this->picea->cache->cachePath() . "/";
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
foreach ($iterator as $file) {
if ($file->isFile() && in_array($file->getExtension(), [ 'php' ])) {
$this->folders[$path][] = $file;
}
}
}
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
$deleted = $failed = 0;
foreach($this->folders as $path => $files) {
foreach($files as $file)
{
unlink($file->getPathname()) ? $deleted++ : $failed++;
}
}
if ($deleted) {
# $context->pushMessage();
}
if ($failed) {
# $context->pushMessage();
}
}
}
-109
View File
@@ -1,109 +0,0 @@
<?php
namespace Lean\Console\Form;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\Lib;
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\Common\EntityResolver;
use \Ulmus\Entity\InformationSchema\Table;
class Database implements FormInterface
{
protected ? Lib\DatabaseMigrations $migration;
public function __construct(Lib\DatabaseMigrations $migration)
{
$this->migration = $migration;
}
public function initialize(FormContextInterface $context) : void
{
$context->tableExist = [];
foreach($this->migration->entities as $entity => $table) {
$tableName = $table->tableName();
$databaseName = $table->databaseName();
if ( ! $tableName || ! $databaseName ) {
continue;
}
$table = Table::repository()
->where(Table::field('name'), $table->tableName())
->where(Table::field('schema'), $table->databaseName())
->loadOne();
if ( $table ) {
$fields = $entity::resolveEntity()->fieldList(EntityResolver::KEY_COLUMN_NAME, true);
$alter = [];
foreach($fields as $field => $definition) {
if ( $table->columns->filtersCollection(fn($e) => $e->name === $field)->count() === 0 ) {
$alter[$field] = $definition;
}
}
if ( $alter ) {
$context->status[$entity] = [
'msg' => "alter",
"query" => $entity::repository()->alterSqlQuery($alter)->getSqlQuery(true),
];
}
else {
# if columns are different
$context->status[$entity] = [
'msg' => "up-to-date",
'query' => "",
];
}
}
else {
$context->status[$entity] = [
'msg' => "unexisting",
'query' => $entity::repository()->createSqlQuery()->getSqlQuery(true),
];
}
}
if ( $context->formSent() ) {
}
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
if ( $context->create ?? false ) {
$context->create::repository()->createTable();
}
elseif ( $context->createAll ?? false ) {
foreach(explode(',', $context->createAll) as $entity) {
$entity::repository()->createTable();
}
}
elseif ( $context->alter ?? false ) {
}
$this->initialize($context);
}
public function getContext(ServerRequestInterface $request) : FormContextInterface
{
return new class($request) extends FormContext {
public array $status = [];
public array $actions = [];
};
}
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Lean\Console\Form\Debugging;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\Lib;
use Picea\{Caching\Opcache, Compiler\Context };
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\Common\EntityResolver;
use \Ulmus\Entity\InformationSchema\Table;
use RecursiveIteratorIterator, RecursiveDirectoryIterator;
class Logs implements FormInterface
{
public function __construct()
{
}
public function initialize(FormContextInterface $context) : void
{
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
}
}
@@ -0,0 +1,34 @@
<?php
namespace Lean\Console\Form\Storage\Database;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\{ Lib, Entity };
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{ EntityCollection };
class GenerateEntity implements FormInterface
{
public function __construct()
{
}
public function initialize(FormContextInterface $context) : void
{
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
}
}
+63
View File
@@ -0,0 +1,63 @@
<?php
namespace Lean\Console\Form\Storage\Database;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\{ Lib, Entity };
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{ EntityCollection };
class Import implements FormInterface
{
protected ? Lib\DatabaseMigrations $migration;
protected array $connections;
public function __construct(Lib\DatabaseMigrations $migration, array $connections)
{
$this->migration = $migration;
$this->connections = $connections;
}
public function initialize(FormContextInterface $context) : void
{
$context->sourceTables ??= new EntityCollection();
$context->destinationTables ??= new EntityCollection();
}
public function validate(FormContextInterface $context) : bool
{
if ($context->source) {
$filtered = array_filter($this->migration->entities, fn($e, $key) => $key::repository()->adapter->name === $context->source, ARRAY_FILTER_USE_BOTH);
$context->sourceTables->replaceWith($filtered);
}
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
if ($context->copy) {
foreach (array_filter($context->copy) as $dest => $source) {
foreach($source::repository()->yield() as $src) {
$dest::repository()->save( $src->toArray(), null, true );
}
}
}
}
public static function getContext(ServerRequestInterface $request, ? string $formName = null) : FormContextInterface
{
return new class($request, $formName) extends FormContext {
public EntityCollection $sourceTables;
public EntityCollection $destinationTables;
};
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
namespace Lean\Console\Form\Storage\Database;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\{ Lib, Entity };
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{Attribute\Property\Field, Entity\EntityInterface, Entity\Field\Datetime, EntityCollection};
class Insert implements FormInterface
{
public function __construct(
protected EntityInterface $entity
) { }
public function initialize(FormContextInterface $context) : void
{
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
foreach ($this->entity::resolveEntity()->fieldList() as $field) {
$type = $field->getTypes()[0];
$value = $context->{$field->name};
if (! $field->getAttribute(Field::class)->object->readonly) {
if ($type->isEnum() && $value) {
$this->entity->{$field->name} = $type->type::from($value);
}
else {
$this->entity->{$field->name} = $value;
}
}
}
# Temp patch while fixing default value in sqlite
if (property_exists($this->entity, 'createdAt')) {
$this->entity->createdAt = new Datetime();
}
$this->entity::repository()->save($this->entity);
}
}
+163
View File
@@ -0,0 +1,163 @@
<?php
namespace Lean\Console\Form\Storage\Database;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\Lib;
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\Common\EntityResolver;
use \Ulmus\Entity\InformationSchema\Table;
use Ulmus\Ulmus;
class Migrate implements FormInterface
{
public function __construct(
protected ? Lib\DatabaseMigrations $migration,
protected null|Lib\Database\DefinitionCollection $definition = null,
)
{
$this->definition ??= new Lib\Database\DefinitionCollection();
}
public function initialize(FormContextInterface $context) : void
{
foreach($this->migration->entities as $entity => $table) {
$previous = null;
$connection = $entity::resolveEntity()->sqlAdapter();
$adapter = $connection->adapter();
$tableName = $table->tableName();
$databaseName = $table->databaseName();
if ( ! $tableName || ! $databaseName ) {
continue;
}
# Query current table's columns
$tableEntity = $adapter->schemaTable($connection, $databaseName, $tableName);
$this->definition[$entity]['table'] = $tableDefinition = new Lib\Database\Definition(
entity: $entity,
modifier: new Lib\Database\Modifier( type: Lib\Database\ModifierTypeEnum::None, ),
);
if ( $tableEntity ) {
$this->definition[$entity]['columns'] = new Lib\Database\DefinitionCollection();
foreach($table->fieldList(EntityResolver::KEY_COLUMN_NAME, true) as $field => $definition) {
if ( isset($definition->value) ) {
$definition->value = $entity::repository()->adapter->adapter()->writableValue($definition->value);
}
$this->definition[$entity]['columns'][$field] = $def = new Lib\Database\Definition(
entity: $entity,
field: $field,
definition: $definition,
modifier: new Lib\Database\Modifier( type: Lib\Database\ModifierTypeEnum::None, ),
previous: $def ?? null,
);
$column = $tableEntity->columns->filtersOne(fn($e) => strtolower($e->name) === $field);
# Column do not exists
if ( $column === null ) {
$def->action = "add";
}
elseif ( ! $column->matchFieldDefinition($definition) ) {
$def->action = "change";
}
if ($def->action && $adapter->supportAlterColumnAction($def->action)) {
$def->modifier = new Lib\Database\Modifier(
type: Lib\Database\ModifierTypeEnum::Alter,
query: $entity::repository()
->alterSqlQuery([ $def->toArray() ])
->getSqlQuery(true)
);
}
}
$alter = $this->definition[$entity]['columns']->filterAlter();
if ( $alter->count() ) {
if ( $adapter->splitAlterQuery() ) {
$query = $alter->map(fn(Lib\Database\Definition $map) => $entity::repository()
->alterSqlQuery([ $map->toArray() ])
->getSqlQuery(true));
}
else {
$query = $entity::repository()
->alterSqlQuery($alter->map(fn(Lib\Database\Definition $map) => $map->toArray()))
->getSqlQuery(true);
}
$tableDefinition->modifier = new Lib\Database\Modifier(
type: Lib\Database\ModifierTypeEnum::Alter,
query: $query,
);
}
}
else {
$tableDefinition->modifier = new Lib\Database\Modifier(
type: Lib\Database\ModifierTypeEnum::Create,
query: $entity::repository()
->createSqlQuery()
->getSqlQuery(true)
);
}
}
$context->definition = $this->definition;
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
$create = $context->createAll ? explode(',', $context->createAll) : [ $context->create ];
foreach(array_filter($create) as $entity) {
if ($this->definition[$entity]['table']->modifier->type === Lib\Database\ModifierTypeEnum::Create) {
$entity::repository()->createTable();
}
}
$alter = $context->alterAll ? explode(',', $context->alterAll) : [ $context->alter ];
foreach(array_filter($alter) as $entity) {
if ($this->definition[$entity]['table']->modifier->type === Lib\Database\ModifierTypeEnum::Alter) {
$adapter = $entity::resolveEntity()->sqlAdapter()->adapter();
$def = $this->definition[$entity]['columns']->filterAlter();
if ( $adapter->splitAlterQuery() ) {
$def->walk(fn(Lib\Database\Definition $definition) =>
$entity::repository()->alterTable([ $definition->toArray() ])
);
}
else {
$entity::repository()->alterTable($def->map( fn(Lib\Database\Definition $e) => $e->toArray() ));
}
}
}
$this->initialize($context);
}
public static function getContext(ServerRequestInterface $request, ? string $formName = null) : FormContextInterface
{
return new class($request, $formName) extends FormContext {
public array $status = [];
public array $actions = [];
};
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Lean\Console\Form\Update;
use Picea\Ui\Method\{ FormInterface, FormContext, FormContextInterface };
use \Lean\Console\{ Lib, Entity };
use Picea\Compiler\Context;
use Psr\Http\Message\ServerRequestInterface;
use Ulmus\{ EntityCollection };
class Git implements FormInterface
{
public function __construct(
public string $projectPath = "",
)
{
}
public function initialize(FormContextInterface $context) : void
{
}
public function validate(FormContextInterface $context) : bool
{
return $context->valid();
}
public function execute(FormContextInterface $context) : void
{
}
public function run(string $command) : \Generator
{
$command = escapeshellcmd($command);
$handle = popen("$command", 'r');
while ( false !== $output = fgets($handle, 1024) ){
yield trim($output);
}
pclose($handle);
}
}
+2
View File
@@ -4,8 +4,10 @@ namespace Lean\Console;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
#[\Deprecated]
abstract class Lean abstract class Lean
{ {
#[\Deprecated]
public static function definitions() : array public static function definitions() : array
{ {
return require(dirname(__DIR__) . "/meta/definitions/software.php"); return require(dirname(__DIR__) . "/meta/definitions/software.php");
+20 -10
View File
@@ -2,27 +2,37 @@
namespace Lean\Console\Lib; namespace Lean\Console\Lib;
use DI\Attribute\Inject;
use Notes\Attribute\Ignore;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use Storage\Session; use Storage\Session;
use Lean\Console\Form;
use Notes\Security\Attribute\{ Security, Taxus };
use Picea; use Picea;
/** #[Security(locked: false)]
* @Language("lean.route") #[Taxus("dev")]
* @RouteParam("methods" => [ 'GET', 'POST', 'DELETE' ], "base" => "/~") #[\Notes\Route\Attribute\Object\Route(method: [ 'GET', 'POST' ] , base: "/~")]
* @Security('locked' => true)
* @Taxus("admin")
*/
trait ConsoleControllerTrait trait ConsoleControllerTrait
{ {
use \Lean\ControllerTrait; use \Lean\ControllerTrait;
#[Inject]
protected ContainerInterface $container; protected ContainerInterface $container;
public function __construct(? Picea\Picea $picea, Session $session, ContainerInterface $container) { #[Ignore]
$this->picea = $picea; public function version() : string
$this->session = $session; {
$this->container = $container; $version = iterator_to_array(
new Form\Update\Git(getenv('PROJECT_PATH'))->run("/usr/bin/git describe --tags")
);
# dump($version);
return "1.5.0";
} }
} }
+22
View File
@@ -0,0 +1,22 @@
<?php
namespace Lean\Console\Lib\Database;
use Notes\Common\ReflectedProperty;
class Definition
{
public function __construct(
public string $entity,
public string|null $field = null,
public array|null|ReflectedProperty $definition = null,
public null|Modifier|array $modifier = null,
public null|Definition $previous = null,
public null|string $action = null,
) {}
public function toArray() : array
{
return array_filter(get_object_vars($this));
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Lean\Console\Lib\Database;
class DefinitionCollection extends \Ulmus\EntityCollection
{
public function filterType(ModifierTypeEnum $type) : self
{
return $this->filtersCollection(fn(Definition $e) => $e->modifier->type === $type);
}
public function filterCreate() : self
{
return $this->filterType(ModifierTypeEnum::Create);
}
public function filterAlter() : self
{
return $this->filterType(ModifierTypeEnum::Alter);
}
public function filterDelete() : self
{
return $this->filterType(ModifierTypeEnum::Delete);
}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Lean\Console\Lib\Database;
class Modifier
{
public function __construct(
public ModifierTypeEnum $type,
public null|array|string $query = null,
) {}
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Lean\Console\Lib\Database;
enum ModifierTypeEnum: string
{
case Alter = 'alter';
case Create = 'create';
case Delete = 'delete';
case None = 'none';
}
+10 -2
View File
@@ -2,6 +2,8 @@
namespace Lean\Console\Lib; namespace Lean\Console\Lib;
use Ulmus\Adapter\SqlAdapterInterface;
class DatabaseMigrations class DatabaseMigrations
{ {
public array $folderList; public array $folderList;
@@ -20,14 +22,20 @@ class DatabaseMigrations
foreach($this->folderList as $namespace => $folder) { foreach($this->folderList as $namespace => $folder) {
foreach(static::files($folder) as $file) { foreach(static::files($folder) as $file) {
$name = $file->getBasename("." . $file->getExtension()); $name = $file->getBasename("." . $file->getExtension());
$subNs = substr($file->getPath(), strlen($folder)); $subNs = ltrim(str_replace('/', '\\', substr($file->getPath(), strlen($folder))), '\\');
$entity = rtrim($namespace, "\\") . ( $subNs ? "\\$subNs" : "" ) . "\\{$name}"; $entity = rtrim($namespace, "\\") . ( $subNs ? "\\$subNs" : "" ) . "\\{$name}";
if ( ! method_exists($entity, 'resolveEntity') ) { if ( ! method_exists($entity, 'resolveEntity') ) {
continue; continue;
} }
$this->entities[$entity] = $entity::resolveEntity(); $resolve = $entity::resolveEntity();
if ( $resolve->sqlAdapter()->adapter() instanceof SqlAdapterInterface) {
$this->entities[$entity] = $resolve;
}
else {
}
} }
} }
+5
View File
@@ -0,0 +1,5 @@
<?php
namespace Lean\Console\Lib;
class FormContext extends \Picea\Ui\Method\FormContext { }
+9
View File
@@ -0,0 +1,9 @@
<?php
class Triggers
{
public function onUpdateCurrentTimestamp(string $table) : string
{
}
}
+18 -6
View File
@@ -1,28 +1,40 @@
<nav id="main-nav"> <nav id="main-nav">
<div class="app-title">LEAN {{ $this->version() }}</div>
<label>{% lang "lean.nav.lean.label" %}</label> <label>{% lang "lean.nav.lean.label" %}</label>
<ul> <ul>
<li><a href="{{ url('~') }}">{% lang "lean.nav.lean.dashboard" %}</a></li> <li><a href="{% route 'lean.console:home' %}">{% lang "lean.nav.lean.dashboard" %}</a></li>
<li><a href="{% route 'lean.console:update' %}">{% lang "lean.nav.lean.update" %}</a></li>
</ul> </ul>
<label>{% lang "lean.nav.request.label" %}</label> <label>{% lang "lean.nav.request.label" %}</label>
<ul> <ul>
<li><a href="{{ url('~/request/routes') }}">{% lang "lean.nav.request.routes" %}</a></li> <li><a href="{% route 'lean.console:request.route' %}">{% lang "lean.nav.request.routes" %}</a></li>
</ul> </ul>
<label>{% lang "lean.nav.storage.label" %}</label> <label>{% lang "lean.nav.storage.label" %}</label>
<ul> <ul>
{#<li><a href="{{ url('~/storage') }}">{% lang "lean.nav.storage.summary" %}</a></li>#} {#<li><a href="{{ url('~/storage') }}">{% lang "lean.nav.storage.summary" %}</a></li>#}
<li><a href="{{ url('~/storage/database') }}">{% lang "lean.nav.storage.database" %}</a></li> <li><a href="{% route 'lean.console:storage.database' %}">{% lang "lean.nav.storage.database" %}</a></li>
<li><a href="{{ url('~/storage/session') }}">{% lang "lean.nav.storage.session" %}</a></li> <li><a href="{% route 'lean.console:storage.session' %}">{% lang "lean.nav.storage.session" %}</a></li>
</ul> </ul>
<label>{% lang "lean.nav.templating.label" %}</label> <label>{% lang "lean.nav.templating.label" %}</label>
<ul> <ul>
<li><a href="{{ url('~/templating/picea') }}">{% lang "lean.nav.templating.picea" %}</a></li> <li><a href="{% route 'lean.console:templating.picea' %}">{% lang "lean.nav.templating.picea" %}</a></li>
</ul> </ul>
<label>{% lang "lean.nav.caching.label" %}</label> <label>{% lang "lean.nav.caching.label" %}</label>
<ul> <ul>
<li><a href="{{ url('~/storage') }}">{% lang "lean.nav.templating.picea" %}</a></li> <li><a href="{% route 'lean.console:caching' %}">{% lang "lean.nav.templating.picea" %}</a></li>
</ul>
<label>{% lang "lean.nav.debugging.label" %}</label>
<ul>
<li><a href="{% route 'lean.console:debugging-logs' %}">{% lang "lean.nav.debugging.logs" %}</a></li>
</ul>
<ul>
<li><a href="{% route 'lean.console:debugging-phpinfo' %}">{% lang "lean.nav.debugging.phpinfo" %}</a></li>
</ul> </ul>
</nav> </nav>
<hr>
+25 -12
View File
@@ -10,35 +10,48 @@
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%3E%3Ctext%20x='0'%20y='14'%3E🔳%3C/text%3E%3C/svg%3E" type="image/svg+xml" /> <link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%2016%2016'%3E%3Ctext%20x='0'%20y='14'%3E🔳%3C/text%3E%3C/svg%3E" type="image/svg+xml" />
<link rel="stylesheet" href='{% asset:path "lean-console/lean-console-bw.css" %}'>
<title>{{ title() }}</title> <title>{{ title() }}</title>
<style>
{% include "lean-console/base/asset/lean.css" %}
</style>
{% section "head" %}{% endsection %} {% section "head" %}{% endsection %}
</head> </head>
<body> <body>
<header id="body-header"> <main id="body-main">
{% section "header" %}{% endsection %}
</header>
<main id="body-main">
{% section "nav" %} {% section "nav" %}
{% view "lean-console/base/console/main-nav" %} {% view "lean-console/base/console/main-nav" %}
{% endsection %} {% endsection %}
<div id="right-panel">
<header id="body-header">
{% section "header" %}
<div class="flex align-items-center justify-content-end">
<a class="btn-blue" href="{% url %}">⇠ Return</a>
</div>
{% endsection %}
</header>
<div id="subheader">
<h1>{% section "subheader" %}{% _ "page-title" %}{% endsection %}</h1>
{% section "breadcrumb" %}
<ul class="breadcrumb">
<li class="breadcrumb-item">Console</li>
{% section "breadcrumb-items" %}{% endsection %}
</ul>
{% endsection %}
</div>
<section id="main-content"> <section id="main-content">
{% section "content" %}{% endsection %} {% section "content" %}{% endsection %}
</section> </section>
</main> </div>
</main>
<footer id="body-footer"> <footer id="body-footer">
{% section "footer" %} {% section "footer" %}
&copy; {{ date('Y') }} - LEAN &copy; {{ date('Y') }} - LEAN
{% endsection %} {% endsection %}
</footer> </footer>
</body> </body>
</html> </html>
@@ -0,0 +1,50 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.caching" %}
{% title _("title") %}
{% section "breadcrumb-items" %}
<li class="breadcrumb-item">{% _ 'breadcrumb' %}</li>
{% endsection %}
{% section "content" %}
{% foreach $list as $path => $folder %}
<section>
<header>
<h2>{{ $path }}</h2>
{% ui:form.post "caching.picea" %}
<button class="btn-red" type="submit">{% _ 'clear' %}</button>
{% ui:endform %}
</header>
<article class="picea">
{% foreach $folder as $file %}
{% php $import = include($file) %}
<div class="grid">
<strong class="col route-name">{{ substr((string) $file, strlen($path)) }}</strong>
<div class="col">
{{ $import['namespace'] }}
</div>
<div class="col">
{{ $import['view'] }}
</div>
</div>
{% endforeach %}
</article>
</section>
{% endforeach %}
<style>
section > header {display:flex;justify-content: space-between}
.picea h5 {font-size:1.5em; line-height:2em; font-weight:bold;text-decoration:underline}
.picea strong.grid {background: #242528;color: #fff;}
.picea .grid {padding:0.66rem 0.3rem;align-items:center}
.picea .grid strong {color:#444}
.picea .grid:nth-child(even) {background: #e3e3e3 }
</style>
{% endsection %}
+13 -4
View File
@@ -4,10 +4,19 @@
{% title _("title") %} {% title _("title") %}
{% section "header" %}
<div class="page-title">{% _ "page-title" %}</div>
{% endsection %}
{% section "content" %} {% section "content" %}
<section>
<article>Welcome into your Lean Console section. Select the appropriate menu item from the sidebar.</article>
</section>
<section style="margin-top:1rem;">
<article>
<div>Firewall</div>
<br>
<b>@todo</b>
<u>latest 50 connections</u>
<u>banned users / IP !</u>
<u>fail2ban integration</u>
</article>
</section>
{% endsection %} {% endsection %}
@@ -0,0 +1,33 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.debugging" %}
{% title _("title") %}
{% section "content" %}
<section>
<header>
<h2>{{ $path }}</h2>
{% ui:form.get current_url() %}
<small>{% _ 'logs.line' %} {% ui:text 'line', get('line') ?? 100, [ 'onchange' => "this.closest('form').submit()" ] %}</small>
{% ui:select 'logfile', array_combine($files, $files), get('file') ?? $open, [ 'onchange' => "this.closest('form').submit()" ] %}
{% ui:endform %}
</header>
<article class="logs">
<div class="log-item">
{% foreach array_reverse($content) as $line %}
<div class="line">{{ $line }}</div>
{% endforeach %}
</div>
</article>
</section>
<style>
section > header {display:flex;justify-content: space-between}
.log-item {max-height: 60vh; overflow-y:auto}
.log-item .line {padding:0.5rem}
.log-item .line {background: #efefef}
.log-item .line:nth-child(even) {background: #ccc}
</style>
{% endsection %}
@@ -0,0 +1,22 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.debugging" %}
{% title _("title") %}
{% section "header" %}
<div class="page-title">{% _ "page-title" %}</div>
{% endsection %}
{% section "breadcrumb-items" %}
<li class="breadcrumb-item">{% _ 'phpinfo.breadcrumb' %}</li>
{% endsection %}
{% section "content" %}
<section>
<iframe src="data:text/html;base64,{{ base64_encode($phpinfo) }}" style="width:100%; height: calc(100vh - 112px - 8vh)">
</iframe>
</section>
{% endsection %}
+27 -15
View File
@@ -4,34 +4,46 @@
{% title _("title") %} {% title _("title") %}
{% section "header" %} {% section "breadcrumb-items" %}
<div class="page-title">{% _ "page-title" %}</div> <li class="breadcrumb-item">{% _ 'route.breadcrumb' %}</li>
{% endsection %} {% endsection %}
{% function routeHtml($route) %}
<div class="grid">
<strong class="col-2 route-name">{{ $route->name }}</strong>
<div class="col-3 route-uri">{{ $route->getRoute() }}</div>
<div class="col-2 route-methods">{{ implode(', ', (array) ( $route->method ?? $route->methods )) }}</div>
<div class="col controller-method">
<span class="class">{{ $route->class }}</span>::<span>{{ $route->classMethod }}</span>
</div>
</div>
{% endfunction %}
{% section "content" %} {% section "content" %}
<section> <section>
<header> <header>
<h1>{% _ 'route.header' %}</h1> <h2>{% _ 'route.header' %}</h2>
</header> </header>
<article class="routes"> <article class="routes">
{% if $routes %} {% if $routes %}
<strong class="grid"> <strong class="grid">
<div class="col route-name">{% _ 'route.name' %}</div> <div class="col-2 route-name">{% _ 'route.name' %}</div>
<div class="col route-uri">{% _ 'route.uri' %}</div> <div class="col-3 route-uri">{% _ 'route.uri' %}</div>
<div class="col route-methods">{% _ 'route.methods' %}</div> <div class="col-2 route-methods">{% _ 'route.methods' %}</div>
<div class="col controller-method">{% _ 'route.target' %}</div> <div class="col controller-method">{% _ 'route.target' %}</div>
</strong> </strong>
{% foreach $routes as $name => $route %} {% foreach $routes as $route %}
<div class="grid"> {{ routeHtml($route) }}
<strong class="col route-name">{{ $name }}</strong>
<div class="col route-uri">{{ $route['route'] }}</div> {% if $overridden = $routing->findOverriddenRoute($route) %}
<div class="col route-methods">{{ implode(', ', $route['routeMethods']) }}</div> <div style="background:rgba(255,0,0,0.2); text-decoration:line-through">
<div class="col controller-method"> {% foreach $routing->findOverriddenRoute($route) as $overridden %}
<span class="class">{{ $route['class'] }}</span>::<span>{{ $route['classMethod'] }}</span> {{ routeHtml($overridden) }}
</div> {% endforeach %}
</div> </div>
{% endif %}
{% endforeach %} {% endforeach %}
{% else %} {% else %}
{% _ 'routeless' %} {% _ 'routeless' %}
@@ -40,7 +52,7 @@
</section> </section>
<style> <style>
.routes strong.grid {background:#172842;color:#eeffee} .routes strong.grid {background: #242528;color: #fff;}
.routes .grid {padding:0.66rem 0.3rem} .routes .grid {padding:0.66rem 0.3rem}
.routes .grid strong {color:#444} .routes .grid strong {color:#444}
.routes .grid:nth-child(even) {background: #e3e3e3 } .routes .grid:nth-child(even) {background: #e3e3e3 }
+55 -41
View File
@@ -1,21 +1,21 @@
{% use Lean\Console\Lib\Database\ModifierTypeEnum %}
{% extends "lean-console/base/layout" %} {% extends "lean-console/base/layout" %}
{% language.set "lean.storage" %} {% language.set "lean.storage" %}
{% title _("title") %} {% title _("title") %}
{% section "header" %} {% section "breadcrumb-items" %}
<div class="page-title">{% _ "page-title" %}</div> <li class="breadcrumb-item">{% _ 'database.breadcrumb' %}</li>
{% endsection %} {% endsection %}
{% section "content" %} {% section "content" %}
<section>
<header>
<h1>{% _ 'database.header' %}</h1>
</header>
<article class="databases">
{% if $connections %} {% if $connections %}
{% foreach $connections as $connection %}
<section>
{% php $conf = $connection->getConfiguration(); %}
<article class="databases">
<strong class="grid db-grid"> <strong class="grid db-grid">
<div class="col db-connection">{% _ 'database.connection' %}</div> <div class="col db-connection">{% _ 'database.connection' %}</div>
<div class="col db-adapter">{% _ 'database.adapter' %}</div> <div class="col db-adapter">{% _ 'database.adapter' %}</div>
@@ -24,85 +24,99 @@
<div class="col db-username">{% _ 'database.username' %}</div> <div class="col db-username">{% _ 'database.username' %}</div>
</strong> </strong>
{% foreach $connections as $connection %}
{% php $conf = $connection->getConfiguration(); %}
<div class="grid db-grid"> <div class="grid db-grid">
<div class="col db-connection">{{ $connection->name }}</div> <div class="col db-connection">{{ $connection->name }}</div>
<div class="col db-adapter">{{ $conf['adapter'] }}</div> <div class="col db-adapter">{{ $connection->adapter()::class }}</div>
<div class="col db-host">{{ $conf['host'] }}:{{ $conf['port'] }}</div> <div class="col db-host">{{ $connection->adapter()->hostname ?? '-' }}:{{ $connection->adapter()->port ?? '-' }}</div>
<div class="col db-name">{{ $conf['database'] }}</div> <div class="col db-name">{{ $connection->adapter()->database ?? $connection->adapter()->path ?? '-' }}</div>
<div class="col db-username">{{ $conf['username'] }}</div> <div class="col db-username">{{ $connection->adapter()->username ?? '-' }}</div>
</div> </div>
<div class="tables"> <div class="tables">
<strong class="grid table-item"> <strong class="grid table-item">
<div class="col">{% _ 'database.table.name' %}</div> <div class="col col-4">{% _ 'database.table.name' %}</div>
<div class="col">{% _ 'database.table.table-fields' %}</div> <div class="col col-2 text-center">{% _ 'database.table.table-fields' %}</div>
<div class="col col-grow-2">{% _ 'database.table.query' %}</div> <div class="col col-3 text-center">{% _ 'database.table.query' %}</div>
<div class="col text-right">{% _ 'database.table.actions' %}</div> <div class="col col-2 text-center">{% _ 'database.table.count' %}</div>
<div class="col col-1 text-center">{% _ 'database.table.actions' %}</div>
</strong> </strong>
{% ui.form.post "database" %} {% ui:form.post "database.migrate", current_url() %}
{% foreach array_filter($migrations->entities, fn($e) => $e->databaseAdapter() === $connection) as $entity => $item %} {% foreach array_filter($migrations->entities, fn($e) => $e->databaseAdapter() === $connection) as $entity => $item %}
<div class="grid table-item align-items-center"> <div class="grid table-item align-items-center">
<div class="col"><span>📄</span> <span>{{ $item->tableName() }}</span></div> <div class="col col-4"><span>📄</span> <span>{{ $item->tableName() }}</span></div>
<div class="col">{% _ "database.table.fields", [ 'count' => count( $item->fieldList() ) ] %}</div> <div class="col col-2 text-center">{% _ "database.table.fields", [ 'count' => count( $item->fieldList() ) ] %}</div>
<div class="col col-grow-2"> <div class="col col-3 text-center">
{% if $context->status[$entity]['query'] %} {% if $context->definition[$entity]['table']->modifier->query ?? false %}
<code>{{ $context->status[$entity]['query'] }}</code> <code>{{= implode('<br><br>',(array) $context->definition[$entity]['table']->modifier->query) }}</code>
{% else %} {% else %}
<div>🗸 This table is up-to-date</div> <div style="color:#abecab">&#x2713 {% _ 'database.table.uptodate' %}</div>
{% endif %} {% endif %}
</div> </div>
<div class="col"> <div class="col col-2 text-center">
{% switch $context->status[$entity]['msg'] %} {% if $context->definition[$entity]['table']->modifier->type !== ModifierTypeEnum::Create %}
{% case 'unexisting' %} {{ $entity::repository()->count() }}
{% endif %}
</div>
<div class="col col-1 text-center">
{% switch $context->definition[$entity]['table']->modifier->type ?? false %}
{% case ModifierTypeEnum::Create %}
{% php $createAll[] = $entity %} {% php $createAll[] = $entity %}
<div class="text-right"> <div class="text-right">
<button name="create" value="{{$entity}}" class="btn-blue">🗸 {% _ 'database.table.create' %}</button> <button name="create" value="{{$entity}}" class="btn-blue">&#x2713; {% _ 'database.table.create' %}</button>
</div> </div>
{% break %} {% break %}
{% case 'altering' %} {% case ModifierTypeEnum::Alter %}
{% php $alter[] = $entity %} {% php $alterAll[] = $entity %}
<div class="text-right"> <div class="text-right">
<button name="create" value="{{$entity}}" class="btn-blue">🗸 {% _ 'database.table.alter' %}</button> <button name="alter" value="{{$entity}}" class="btn-blue">&#x2713; {% _ 'database.table.alter' %}</button>
</div> </div>
{% break %} {% break %}
{% endswitch %} {% endswitch %}
<div class="text-right">
<a href="{% route 'lean.console:storage.database_insert', [ 'source' => $entity ] %}" class="btn-gray">{% _ 'database.table.add-column' %}</a>
</div>
</div> </div>
</div> </div>
{% endforeach %} {% endforeach %}
{% if ($createAll ?? 0) > 1 %} {% if ($createAll ?? 0) > 1 %}
<div class="text-right" style="padding:0.5rem 0"> <div class="text-right" style="padding:0.5rem 0">
<button name="createAll" value="{{ implode(',', $createAll) }}" class="btn-red">🗸 {% _ 'database.table.createAll' %}</button> <button name="createAll" value="{{ implode(',', $createAll) }}" class="btn-red">&#x2713; {% _ 'database.table.createAll' %}</button>
</div> </div>
{% endif %} {% endif %}
{% ui.endform %}
</div>
{% endforeach %} {% if ($alterAll ?? 0) > 1 %}
<div class="text-right" style="padding:0.5rem 0">
<button name="alterAll" value="{{ implode(',', $alterAll) }}" class="btn-red">&#x2713; {% _ 'database.table.alterAll' %}</button>
</div>
{% endif %} {% endif %}
{% ui:endform %}
</div>
</article> </article>
</section> </section>
{% endforeach %}
{% endif %}
<style> <style>
.databases strong.grid {background:#172842;color:#fff} .databases strong.grid {background: #888a91;color:#fff}
.databases .db-grid {padding:0.66rem 0.3rem} .databases .db-grid {padding:0.66rem 0.3rem}
.databases .db-grid strong {color:#444} .databases .db-grid strong {color:#444}
.databases .db-grid:nth-child(even) {background: #e3e3e3 } .databases .db-grid:nth-child(even) {background: #e3e3e3;line-height: 1.5em;font-size: 1.3em;}
.databases .tables .table-item {padding:0.33rem 0;background:rgba(255,255,255,0.1)} .databases .tables .table-item {padding:0.33rem 0;background:rgba(255,255,255,0.1);line-height: 1.8em;}
.databases .tables .table-item:nth-child(even) {background:rgba(255,255,255,0.2)} .databases .tables .table-item:nth-child(even) {background:rgba(255,255,255,0.2)}
.databases summary {background: #172841;color:#fff;margin: 0 -8px 0 -8px;padding:7px 10px} .databases summary {background: #172841;color:#fff;margin: 0 -8px 0 -8px;padding:7px 10px}
.tables {background: #172841;margin: 0 -8px 0 -8px;color: #fff;padding: 4px 12px;} .tables {background: #2d3137;margin: 0 -8px 0 -8px;color: #fff;padding: 2px 10px;}
.tables label {padding:10px 5px;display:block} .tables label {padding:10px 5px;display:block}
section + section { margin-top:15px; }
</style> </style>
{% endsection %} {% endsection %}
@@ -0,0 +1,67 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.storage" %}
{% title _("title") %}
{% section "content" %}
{% if $connections %}
{% ui:form.post "database.import" %}
<section>
<header>Import data from a connection into another</header>
<article class="databases">
{% if ! $context->source %}
{% php $select = array_combine(array_column($connections, 'name'), array_column($connections, 'name')) %}
<div class="db-grid">
Source connection: {% ui:select "source", $select %}
</div>
<div class="db-grid">
Destination connection: {% ui:select "destination", $select %}
</div>
{% else %}
{% ui:hidden 'source', $context->source %}
{% ui:hidden 'destination', $context->destination %}
<div class="db-grid">
COPY DATA FROM → TO:
{% php $dest = array_filter($migrations->entities, fn($e, $key) => $key::repository()->adapter->name === $context->destination, ARRAY_FILTER_USE_BOTH); %}
{% foreach $dest as $key => $value %}
<div class="db-grid">
<span>{% ui:select "copy[$key]", [ null => '' ] + $context->sourceTables->buildArray('entityClass', 'entityClass') %}</span>
<span>→</span>
<span>{{ sprintf('%s (%s)', $key, $value->tableName()) }}</span>
</div>
{% endforeach %}
</div>
{% endif %}
</article>
<footer class="text-center">
<button type="submit" class="btn-blue">Copy</button>
<a href="{% route 'lean.console:storage.database' %}" class="btn-red">Cancel</a>
</footer>
</section>
{% ui:endform %}
{% else %}
No connection found
{% endif %}
<style>
.databases strong.grid {background:#181818;color:#fff}
.databases .db-grid {padding:0.66rem 0.3rem}
.databases .db-grid strong {color:#444}
.databases .db-grid:nth-child(even) {background: #eaeaea }
.databases .tables .table-item {padding:0.33rem 0;background:rgba(255,255,255,0.1);line-height: 1.8em;}
.databases .tables .table-item:nth-child(even) {background:rgba(255,255,255,0.2)}
.databases summary {background: #172841;color:#fff;margin: 0 -8px 0 -8px;padding:7px 10px}
.tables {background: #2d3137;margin: 0 -8px 0 -8px;color: #fff;padding: 2px 10px;}
.tables label {padding:10px 5px;display:block}
section + section { margin-top:15px; }
</style>
{% endsection %}
@@ -0,0 +1,117 @@
{% use Ulmus\Attribute\Property\Field %}
{% use Lean\Console\Lib\Database\ModifierTypeEnum %}
{% extends "lean-console/base/layout" %}
{% language.set "lean.storage" %}
{% title _("title") %}
{% section "breadcrumb-items" %}
<li class="breadcrumb-item"><a href="{% route 'lean.console:storage.database' %}">{% _ 'database.breadcrumb' %}</a></li>
<li class="breadcrumb-item">{% _ 'database_insert.breadcrumb' %}</li>
{% endsection %}
{% section "content" %}
<section class="insert">
<header class="bg-yellow">
<h2>{{ $class }}</h2>
</header>
<article>
<div class="database_insert">
<div class="tables">
<strong class="grid table-item">
<div class="col">{% _ 'database_insert.table.column' %}</div>
<div class="col">{% _ 'database_insert.table.type' %}</div>
<div class="col text-center">{% _ 'database_insert.table.attributes' %}</div>
<div class="col col-grow-4 text-center">{% _ 'database_insert.table.value' %}</div>
</strong>
{% ui:form.post "database.insert", current_url(get()) %}
{% foreach $class::resolveEntity()->fieldList() as $field %}
<div class="grid table-item align-items-center column-item">
<div class="col">
<span>{{ $field->name }}</span>
</div>
<div class="col ">
{% foreach $field->getTypes() as $type %}
<span title="{{ $type->type }}">{{ $type->isObject() ? substr($type->type, strrpos($type->type, '\\') + 1) : $type->type }}</span>
{% endforeach %}
</div>
<div class="col attributes text-center">
{% foreach $field->attributes as $attribute %}
<span class="attribute" title="{% json.pretty.html $attribute->object %}">{{ $attribute->tag }}</span>
{% endforeach %}
</div>
<div class="col col-grow-4">
{% foreach $field->getTypes() as $type %}
{% php $attr = $field->getAttribute(Field::class)->object->readonly ? [ 'disabled' => 'disabled', 'placeholder' => _('database_insert.readonly') ] : [] %}
{% if enum_exists($type->type) %}
{% php $enum = array_map(fn($e) => $e->value, $type->type::cases()) %}
{% if $type->nullable %}
{% php $enum = [ null => null ] + $enum %}
{% endif %}
{% ui:select $field->name, array_combine($enum, $enum), $context->{$field->name}, $attr %}
{% else %}
{% ui:text $field->name, $context->{$field->name}, $attr %}
{% endif %}
{% endforeach %}
</div>
{# <div class="col">{% _ "database.table.fields", [ 'count' => count( $item->fieldList() ) ] %}</div>
<div class="col">
{% if $context->definition[$entity]['table']->modifier->query ?? false %}
<code>{{= implode('<br><br>',(array) $context->definition[$entity]['table']->modifier->query) }}</code>
{% else %}
<div style="color:#abecab">&#x2713 {% _ 'database.table.uptodate' %}</div>
{% endif %}
</div>
<div class="col">
</div> #}
</div>
{% endforeach %}
<div class="grid table-item">
<div class="col"></div>
<div class="col"></div>
<div class="col text-center"></div>
<div class="col col-grow-4 text-right py-2">
<button type="submit" class="btn-yellow">{% _ 'database_insert.add' %}</button>
</div>
</div>
{% ui:endform %}
</div>
</div>
</article>
</section>
<style>
#main-content .insert article {background: #6a6a6a;padding:10px;}
select, input:not([type="file"]):not([type="button"]) {display: block;width: 100%;height: calc(2rem + 2px);padding: .375rem .75rem;font-size: 1rem;line-height: 1.5;color: #495057;background-color: #fff;background-clip: padding-box;border: 1px solid #ced4da; border-radius: 0; transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;}
select:disabled, input[disabled] {filter: contrast(60%);}
.database_insert strong.grid {background: #888a91;color:#fff}
.database_insert .db-grid {padding:0.66rem 0.3rem}
.database_insert .db-grid strong {color:#444}
.database_insert .db-grid:nth-child(even) {background: #e3e3e3;line-height: 1.5em;font-size: 1.3em;}
.database_insert .tables {color:#fff}
.database_insert .tables .table-item {padding:0.33rem 0;background:rgba(255,255,255,0.1);line-height: 1.8em;}
.database_insert .tables .table-item:nth-child(even) {background:rgba(255,255,255,0.2)}
.database_insert .attribute {display: inline-block;padding: .25em .4em 0 .4em;font-size: 70%;font-weight: 700;line-height: 1.2rem;text-align: center;white-space: nowrap;vertical-align: baseline;border-radius: .25rem;color:#ffe300; border:1px solid #ffe300}
.database_insert summary {background: #172841;color:#fff;margin: 0 -8px 0 -8px;padding:7px 10px}
.tables {background: #2d3137; #fff;padding: 2px 10px;}
.tables label {padding:10px 5px;display:block}
section + section { margin-top:15px; }
</style>
{% endsection %}
@@ -0,0 +1,42 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.storage" %}
{% title _("title") %}
{% section "breadcrumb-items" %}
<li class="breadcrumb-item">{% _ 'session.breadcrumb' %}</li>
{% endsection %}
{% section "content" %}
<section>
<header>
<h2>{% _ 'session.header' %}</h2>
</header>
<article class="sessions">
<strong class="grid">
<div class="col route-name">{% _ 'session.count', [ 'count' => count($sessions) ] %}</div>
</strong>
{% foreach $sessions as $session %}
<div class="grid">
<strong class="col route-name">{{ $session }}</strong>
<div class="col">{{ date('Y-m-d H:i:s', filemtime(realpath($path . DIRECTORY_SEPARATOR . $session))) }}</div>
<div class="col text-right">
<a target="_blank" class="btn-red" href="{% route 'lean.console:storage.session_content', [ 'hash' => $session ] %}">
Voir
</a>
</div>
</div>
{% endforeach %}
</article>
</section>
<style>
.sessions strong.grid {background: #242528;color: #fff;}
.sessions .grid {padding:0.66rem 0.3rem;align-items:center;justify-content:space-between;}
.sessions .grid strong {color:#444}
.sessions .grid:nth-child(even) {background: #e3e3e3 }
</style>
{% endsection %}
@@ -0,0 +1,41 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.templating" %}
{% title _("title") %}
{% section "breadcrumb-items" %}
<li class="breadcrumb-item">{% _ 'breadcrumb' %}</li>
{% endsection %}
{% section "content" %}
{% foreach $list as $path => $folder %}
<section>
<header>
<h2>{{ $path }}</h2>
</header>
<article class="picea">
{% foreach $folder as $file %}
<div class="grid">
<strong class="col route-name">{{ substr((string) $file, strlen($path)) }}</strong>
<div>
{#<a target="_blank" class="btn-red" href="">
Voir
</a>#}
</div>
</div>
{% endforeach %}
</article>
</section>
{% endforeach %}
<style>
.picea h5 {font-size:1.5em; line-height:2em; font-weight:bold;text-decoration:underline}
.picea strong.grid {background: #242528;color: #fff;}
.picea .grid {padding:0.66rem 0.3rem;align-items:center}
.picea .grid strong {color:#444}
.picea .grid:nth-child(even) {background: #e3e3e3 }
section + section {margin-top:1.2rem}
</style>
{% endsection %}
+76
View File
@@ -0,0 +1,76 @@
{% extends "lean-console/base/layout" %}
{% language.set "lean.update" %}
{% title _("title") %}
{% function setStatusModifierClass($line) : string %}
{% return match(substr($line, 0, 2)) {
'??' => "action-add",
default => false,
} ?: match(substr($line, 0, 1)) {
'M' => "action-delete",
default => "",
}%}
{% endfunction %}
{% function setDiffModifierClass($line) : string %}
{% return match(substr($line, 0, 2)) {
'--' => "action-delete",
'++' => "action-add",
'@@' => "action-group",
default => false,
} ?: match(substr($line, 0, 1)) {
'-' => "action-delete",
'+' => "action-add",
default => "",
}%}
{% endfunction %}
{% section "content" %}
<section>
<header>
<h1>{% _ 'git.header' %}</h1>
</header>
<article class="updates">
<div class="git-status">
<strong style="display:block;margin-bottom:7px">'git status' output</strong>
<code style="white-space:normal;font-size:120%">
{% foreach $status as $line %}
<div class="{{ setStatusModifierClass($line) }}">
{% if $line %}
{{ $line }}
{% endif %}
</div>
{% endforeach %}
</code>
</div>
<div class="git-diff" style="margin-top:2rem">
<strong style="display:block;margin-bottom:7px">'git diff' output</strong>
<code style="white-space:normal;font-size:120%">
{% foreach $diff as $line %}
<div class="{{ setDiffModifierClass($line) }}">
{% if $line %}
{{ $line }}
{% endif %}
</div>
{% endforeach %}
</code>
</div>
</article>
<footer>
{{ $lastUpdate }}
</footer>
</section>
{% endsection %}
<style>
.action-delete {color:#f88}
.action-add {color: #88ff8a}
.action-group {color:#88bcff}
</style>