Pour tout problème contactez-nous par mail : support@froggit.fr | La FAQ :grey_question: | Rejoignez-nous sur le Chat :speech_balloon:

Skip to content
Snippets Groups Projects
Commit 08fe74fc authored by Trehinos's avatar Trehinos
Browse files

Merge branch 'dev' into 'master'

- Strings : ::token added (mirror ::split)

See merge request Trehinos/Thor!37
parents 931fbf95 3ff7eb72
No related branches found
No related tags found
No related merge requests found
Showing
with 352 additions and 215 deletions
app_vendor: Trehinos
app_name: Thor
app_version: 1.0.0
app_version_name: 'Thor v1'
app_version: 1.1.0
app_version_name: 'v1.1'
env: DEV
lang: fr
timezone: Europe/Paris
......
......@@ -16,6 +16,7 @@ vendors-css:
cache: true
list:
- vendors/bootstrap.min.css
- vendors/fontawesome/all.min.css
- vendors/datatables.min.css
vendors-js:
type: js
......@@ -23,6 +24,5 @@ vendors-js:
list:
- vendors/jquery.min.js
- vendors/bootstrap.bundle.min.js
- vendors/fontawesome.min.js
- vendors/datatables.min.js
- vendors/dt-langs/fr.js
......@@ -13,13 +13,13 @@ Main elements of Thor
*/
#footer {
position: fixed;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
background: #00000077;
color: #fff;
background: #ffffff77;
color: #000;
line-height: 14px;
font-size: 8pt;
padding: 8px 4px;
......@@ -27,14 +27,14 @@ Main elements of Thor
}
#footer button {
color: #ddf;
color: #440;
text-decoration: none;
border: none;
background: none;
padding: 0 2px;
}
#footer button:hover {
color: #bbf;
color: #330;
}
#content {
......@@ -43,22 +43,11 @@ Main elements of Thor
position: relative;
}
#toolbar {
z-index: 1;
width: 100%;
background: #bbb;
color: #444;
font-weight: 900;
padding: 8px 16px;
height: 48px;
line-height: 24px;
}
#titlebar {
z-index: 1;
width: 100%;
background: #444;
color: #ccc;
background: #ddd;
color: #444;
font-weight: 900;
height: 24px;
line-height: 24px;
......@@ -66,10 +55,9 @@ Main elements of Thor
}
#page {;
padding: 144px 16px 96px 16px;
padding: 80px 16px 96px 16px;
}
#modal .modal-header {
position: relative;
}
......@@ -115,7 +103,7 @@ Main elements of Thor on pages
vertical-align: middle;
}
#menu .dropdown-menu li span.keys {
#menu .dropdown-menu span.keys {
opacity: 0.1;
transition: opacity ease 300ms;
}
......
let toastList = [];
function showToasts() {
$("#btn-show-toasts").find(".fa-lg").removeClass("text-danger");
$("#btn-show-toasts").removeClass("btn-danger").addClass("btn-dark");
toastList.forEach((toast) => {
toast.show();
});
......@@ -11,12 +11,12 @@ function initToasts() {
let toastElList = [].slice.call(document.querySelectorAll('.toast'));
toastList = toastElList.map(function (toastEl) {
let toast = new bootstrap.Toast(toastEl);
$("#btn-show-toasts").find(".fa-lg").removeClass("text-danger");
$("#btn-show-toasts").removeClass("btn-danger").addClass("btn-dark");
toast.show();
toastEl.addEventListener('hidden.bs.toast', () => {
if ($(".toast.show").length < 1) {
$("#btn-show-toasts").find(".fa-lg").addClass("text-danger");
$("#btn-show-toasts").removeClass("btn-dark").addClass("btn-danger");
}
});
......
vendors @ d04d24dd
Subproject commit a6d71f602067b8ed66473777df941155b628cf41
Subproject commit d04d24ddb7cacbed82421668f6aa718e345fe2a3
......@@ -178,3 +178,7 @@ clear/logs:
route/list:
class: Thor\Framework\Commands\Http\RoutesList
description: 'list all routes in app/res/static/routes.yml'
test:
class: App\Test
description: '...'
cli: Thor\Cli\CliKernel
daemon: Thor\Cli\DaemonScheduler
daemon: Thor\Cli\Daemon\DaemonScheduler
api: Thor\Http\HttpKernel
web: Thor\Web\WebKernel
......@@ -10,6 +10,7 @@
{% block head_js %}{% endblock %}
</head>
<body>
{% include "components.html.twig" %}
{% block body_html %}
<div class="container">
<h1>{% block body_title %}{% endblock %}</h1>
......
<template id="thor-vendors-css">{{ asset("thor-css") }}{{ asset("vendors-css") }}</template>
<script>
const E = {
create: (type = "div", classes = "", text = null) => {
let e = document.createElement(type);
e.setAttribute("class", classes);
if (text !== null) {
e.textContent = text;
}
return e;
},
icon: (type) => {
return E.create("i", `fas fa-${type}`);
},
template: (id) => {
return document.getElementById(id);
}
};
class ThorElement extends HTMLElement {
constructor(type = "div", classes = "", text = null) {
super();
let styles = E.template("thor-vendors-css").cloneNode(true).innerHTML;
this.shadow = this.attachShadow({mode: "open"});
this.shadow.innerHTML = styles;
this.root = E.create(type, classes, text);
}
}
class ThorButton extends ThorElement {
constructor() {
super("button", "box");
let menuTarget = this.getAttribute("menu-target");
let link = this.getAttribute("link");
let icon = this.getAttribute("icon") ?? "circle";
let text = this.textContent;
let color = this.getAttribute("color");
if (link !== null) {
this.root.setAttribute("onclick", `window.location.href = "${link}";`);
} else if (menuTarget !== null) {
this.root.setAttribute("onclick", `menuClick($('#${menuTarget}'))`);
}
if (color !== null) {
this.root.setAttribute("style", `background: ${color};`)
}
if (icon !== null) {
this.root.appendChild(E.icon(`${icon} box-icon`));
}
this.root.appendChild(E.create("span", "box-text", text));
this.shadow.appendChild(this.root);
}
}
class ThorPanel extends ThorElement {
constructor() {
super("div", "card");
let description = this.getAttribute("description") ?? "";
let body = E.create("div", "card-body", description);
this.root.appendChild(body);
let footer = E.create("div", "card-footer");
for (let node of this.childNodes) {
footer.appendChild(node.cloneNode(true));
}
this.root.appendChild(footer);
this.shadow.appendChild(this.root);
}
}
customElements.define("thor-button", ThorButton);
customElements.define("thor-panel", ThorPanel);
</script>
......@@ -3,33 +3,30 @@
{% set is_group = item.group is defined %}
{% if (item.authorization is defined and authorized(item.authorization)) or item.authorization is not defined %}
{% if not is_group %}
<li class="nav-item">
<a
href="#"
<button type="button"
id="btn-{{ item.route }}"
onclick="menuClick($(this));"
data-url="{% if item.route != '.' %}{{ url(item.route) }}{% else %}#{% endif %}"
data-item="{{ item.route }}"
title="{{ DICT.menu[item.label] ?? item.label ?? '' }}"
class="nav-link {% if server.currentRouteName == item.route %}active{% endif %}"
class="btn btn-light text-start m-1 w-100 {% if server.currentRouteName == item.route %}active{% endif %}"
>
{{ icon(item.icon, (item.icon_col ?? 'fas'), true) }}
<span class="menu-label" title="{{ item.key }}">
{{ DICT.menu[item.label] ?? item.label ?? '' }}
</span>
</a>
</li>
</button>
{% else %}
{% set subactive = false %}
{% for sub_item in item.group %}
{% if sub_item.route == server.currentRouteName %}{% set subactive = true %}{% endif %}
{% endfor %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle {% if subactive %}active{% endif %}" href="#" data-bs-toggle="dropdown">
<div class="dropdown">
<button type="button" class="btn btn-light text-start m-1 w-100 dropdown-toggle {% if subactive %}active{% endif %}" href="#" data-bs-toggle="dropdown">
{{ icon(item.icon, (item.icon_col ?? 'fas'), true) }}
{{ DICT.menu[item.label] ?? item.label ?? '' }}
</a>
<ul class="dropdown-menu">
</button>
<ul class="dropdown-menu w-100">
{% if item.route is defined %}
<li>
<a
......@@ -81,7 +78,7 @@
{% endif %}
{% endfor %}
</ul>
</li>
</div>
{% endif %}
{% endif %}
{% endfor %}
......@@ -95,11 +92,12 @@
let url = $elem.data("url");
loadPage(url, null, (response) => {
$elem.addClass("active");
unselect($("#menu ul li a"));
unselect($("#menu .btn, #menu a"));
if ($elem.hasClass("dropdown-item")) {
$elem.parent().parent().parent().find("a.dropdown-toggle").addClass("active");
$elem.parent().parent().parent().find("button.dropdown-toggle").addClass("active");
}
window.history.pushState({"html": response}, null, "?menuItem=" + $elem.data("item"));
$("#menu .btn-close").click();
});
}
</script>
......@@ -5,100 +5,77 @@
{% endblock %}
{% block body_html %}
<nav id="menu" class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark">
{% block menu %}
{% block menu %}
<div class="offcanvas offcanvas-start" id="menu">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="offcanvasLabel">Menu</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
{{ render('menu') }}
</div>
<div id="footer" class="pt-2">
<div>
{{ icon('bolt', 'far fa-lg', true, 'color: #db0') }}
{{ appVendor }}/{{ appName }} {{ versionName|raw }} [{{ version }}]
</div>
<div>{{ icon('copyright', 'far fa-lg', true)|raw }} 2022 {{ appVendor }}</div>
{% block footer %}
<div class="mt-1 pt-1 border-top border-secondary" style="font-size: 0.9em;">
{{ icon('info-circle', 'fas fa-lg', true)|raw }}
<button
id="btn-about"
onclick="menuClick($(this));"
data-url="{{ url('about') }}"
data-item="about">
{{ DICT.footer.about }}
</button>
<button
id="btn-legal"
onclick="menuClick($(this));"
data-url="{{ url('legal') }}"
data-item="legal"
>
{{ DICT.footer.legal }}
</button>
<button
id="btn-changelog"
onclick="menuClick($(this));"
data-url="{{ url('changelog') }}"
data-item="changelog"
>
{{ DICT.footer.changelog }}
</button>
</div>
{% endblock %}
</div>
</div>
<nav id="navbar" class="navbar fixed-top navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<span id="title" class="navbar-brand" style="color: #bbb;">
{{ icon('bolt', 'fad', true, '--fa-secondary-opacity: 1; --fa-primary-color: #fe4; --fa-secondary-color: #fc4;') }}
<span style="color: #fa4">{{ appName|upper }}</span>
<span id="title" class="navbar-brand">
{{ icon('bolt', 'far fa-lg', true, 'color: #960') }}
<span style="color: #333">{{ appName|upper }}</span>
</span>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#menu-navbar">
{{ icon('list-alt') }}
</button>
<div class="collapse navbar-collapse" id="menu-navbar">
{{ render('menu') }}
<ul class="navbar-nav me-0">
<li class="nav-item dropdown"
title="Revoir les notifications"
<div class="input-group me-4" style="max-width: 320px;">
<button class="btn btn-dark text-nowrap" href="#"
data-bs-toggle="offcanvas" data-bs-target="#menu"
>
<a class="nav-link" href="#"
onclick="showToasts();" id="btn-show-toasts"
>
<i class="fas fa-comment-alt-exclamation {% if _messages|length > 0 %}text-danger{% endif %} fa-lg"></i>
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown">
<i class="fas fa-user fa-lg"></i>
{{ server.security.currentIdentity.identifier }}
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item link-danger" href="{{ url('logout') }}">
{{ icon('sign-out', 'fas') }}
{{ DICT.menu.logout }}
</a>
</li>
<li class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="#"
onclick="Modal.load('{{ url('user-parameters-form', {public_id: server.security.currentIdentity.publicId}) }}');"
>
{{ icon('user', 'fas') }}
{{ DICT.menu.profile }}
</a>
</li>
<li>
<a class="dropdown-item" href="#"
onclick="Modal.load('{{ url('users-change-password-form', {public_id: server.security.currentIdentity.publicId}) }}');"
>
{{ icon('lock', 'fas') }}
{{ DICT.users.change_password }}
</a>
</li>
</ul>
</li>
{{ icon("bars") }} MENU
</button>
<div class="input-group-text"><label for="search">{{ icon("search") }}</label></div>
<input id="search" type="search" class="form-control">
</div>
<div class="flex-grow-1"></div>
<ul class="navbar-nav me-0">
{% include "thor/user-item.html.twig" %}
</ul>
</div>
</div>
{% endblock %}
</nav>
</nav>
{% endblock %}
<div id="content">{% block body_content %}{% endblock %}</div>
<div id="footer" class="pt-2">
<div>
{{ icon('bolt', 'fad fa-lg', true, '--fa-secondary-opacity: 1; --fa-primary-color: #fe4; --fa-secondary-color: #fc4;')|raw }}
{{ appVendor }}/{{ appName }} {{ versionName|raw }} [{{ version }}]
</div>
<div>{{ icon('copyright', 'far fa-lg', true)|raw }} 2022 {{ appVendor }}</div>
{% block footer %}
<div class="mt-1 pt-1 border-top border-secondary" style="font-size: 0.9em;">
{{ icon('info-circle', 'fas fa-lg', true)|raw }}
<button
id="btn-about"
onclick="menuClick($(this));"
data-url="{{ url('about') }}"
data-item="about">
{{ DICT.footer.about }}
</button>
<button
id="btn-legal"
onclick="menuClick($(this));"
data-url="{{ url('legal') }}"
data-item="legal"
>
{{ DICT.footer.legal }}
</button>
<button
id="btn-changelog"
onclick="menuClick($(this));"
data-url="{{ url('changelog') }}"
data-item="changelog"
>
{{ DICT.footer.changelog }}
</button>
</div>
{% endblock %}
</div>
<div id="modal" class="modal" tabindex="-1" role="dialog">
{% include "modal-base.html.twig" %}
......
......@@ -6,7 +6,6 @@
{% endfor %}
</div>
<div id="titlebar" class="fixed-top" style="top: 56px">{% block titlebar %}{% endblock %}</div>
<div id="toolbar" class="fixed-top" style="top: 80px;">{% block toolbar %}{% endblock %}</div>
<div id="page">{% block page %}{% endblock %}</div>
{% block page_js %}{% endblock %}
<script>
......
......@@ -3,38 +3,24 @@
{% block titlebar %}{{ icon('book', 'fas', true) }} {{ DICT.index.title }}{% endblock %}
{% block page %}
<div class="alert alert-info mb-5">
{{ DICT.index.thor_description }}
</div>
<div class="row">
<div class="col-6">
<div class="card">
<div class="card-body">
{{ DICT.index.documentation_text }}
</div>
<div class="card-footer">
<button onclick="menuClick($('#btn-documentation'))" class="box" style="background: #469">
{{ icon('book box-icon') }}
<span class="box-text">{{ DICT.index.documentation_button }}</span>
</button>
</div>
</div>
<div class="container">
<div class="alert alert-info mb-2">
{{ DICT.index.thor_description }}
</div>
<div class="col-6">
<div class="card">
<div class="card-body">
{{ DICT.index.features_text }}
</div>
<div class="card-footer">
<button onclick="menuClick($('#btn-users'))" class="box" style="background: #832">
{{ icon('users box-icon') }}
<span class="box-text">{{ DICT.index.users_button }}</span>
</button>
<button onclick="menuClick($('#btn-manage-permissions'))" class="box" style="background: #a60">
{{ icon('gavel box-icon') }}
<span class="box-text">{{ DICT.index.permissions_button }}</span>
</button>
</div>
<div class="row">
<div class="col-6">
<thor-panel description="{{ DICT.index.documentation_text }}">
<thor-button menu-target="btn-documentation" icon="book"
color="#469">{{ DICT.index.documentation_button }}</thor-button>
</thor-panel>
</div>
<div class="col-6">
<thor-panel description="{{ DICT.index.features_text }}">
<thor-button menu-target="btn-users" icon="users"
color="#832">{{ DICT.index.users_button }}</thor-button>
<thor-button menu-target="btn-manage-permissions" icon="gavel"
color="#a60">{{ DICT.index.permissions_button }}</thor-button>
</thor-panel>
</div>
</div>
</div>
......
<div class="btn-group">
<button type="button" class="btn btn-dark"
onclick="Modal.load('{{ url('user-parameters-form', {public_id: server.security.currentIdentity.publicId}) }}');"
>
<i class="fas fa-user fa-lg"></i>
{{ server.security.currentIdentity.identifier }}
</button>
<a class="btn {% if _messages|length > 0 %}btn-danger{% else %}btn-dark{% endif %}" href="#"
onclick="showToasts();" id="btn-show-toasts"
>
<i class="fas fa-comment-alt-exclamation fa-lg"></i>
</a>
<button type="button" class="btn btn-dark dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item link-danger" href="{{ url('logout') }}">
{{ icon('sign-out', 'fas') }}
{{ DICT.menu.logout }}
</a>
</li>
<li class="dropdown-divider"></li>
<li>
<a class="dropdown-item" href="#"
onclick="Modal.load('{{ url('users-change-password-form', {public_id: server.security.currentIdentity.publicId}) }}');"
>
{{ icon('lock', 'fas') }}
{{ DICT.users.change_password }}
</a>
</li>
</ul>
</div>
<?php
namespace App;
use Thor\Cli\Command\Command;
final class Test extends Command
{
public function execute(): void
{
$enc = mb_list_encodings();
print_r(
array_combine(
$enc,
array_map(
fn($aliases) => implode(', ', $aliases),
array_map(
fn($e) => mb_encoding_aliases($e),
$enc
)
)
),
);
}
}
{
"name": "trehinos/thor",
"description": "Smart framework for PHP-8.1",
"version": "0.9.6",
"type": "project",
"keywords": ["php8.1", "framework", "http", "daemon"],
"license": "MIT",
"minimum-stability": "stable",
"authors": [
{
"name": "Sébastien GELDREICH",
"email": "trehinos@gmail.com"
"name": "trehinos/thor",
"description": "Smart framework for PHP-8.2",
"version": "1.1.0",
"type": "project",
"keywords": [
"php8.2",
"framework",
"http",
"daemon"
],
"license": "MIT",
"minimum-stability": "stable",
"authors": [
{
"name": "Sébastien GELDREICH",
"email": "trehinos@gmail.com"
}
],
"autoload": {
"psr-4": {
"Thor\\": "thor/",
"App\\": "app/src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"config": {
"vendor-dir": "vendors/",
"bin-dir": "bin/"
},
"require": {
"php": "^8.2",
"ext-curl": "*",
"ext-ftp": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"ext-readline": "*",
"ext-zip": "*",
"phpoffice/phpspreadsheet": "1.*",
"datatables.net/editor-php": "2.*",
"symfony/var-dumper": "5.*",
"symfony/yaml": "5.*",
"twig/twig": "3.*"
},
"require-dev": {
"phpunit/phpunit": "^9"
}
],
"autoload": {
"psr-4": {
"Thor\\": "thor/",
"App\\": "app/src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"config": {
"vendor-dir": "vendors/",
"bin-dir": "bin/"
},
"require": {
"php": "^8.1",
"ext-calendar": "*",
"ext-curl": "*",
"ext-ftp": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"ext-zip": "*",
"ext-readline": "*",
"phpoffice/phpspreadsheet": "1.*",
"datatables.net/editor-php": "2.*",
"symfony/var-dumper": "5.*",
"symfony/yaml": "5.*",
"twig/twig": "3.*"
},
"require-dev": {
"phpunit/phpunit": "^9"
}
}
......@@ -4,11 +4,11 @@ namespace Thor\Cli;
use Thor\Globals;
use Thor\Debug\Logger;
use Thor\Process\Option;
use Thor\Debug\LogLevel;
use Thor\Process\Argument;
use Thor\Process\CommandError;
use Thor\Cli\Command\Option;
use Thor\Cli\Command\Argument;
use Thor\Process\KernelInterface;
use Thor\Cli\Command\CommandError;
use Thor\Configuration\Configuration;
use Thor\Database\PdoExtension\PdoHandler;
use Thor\Framework\Factories\Configurations;
......
<?php
namespace Thor\Process;
use Thor\Configuration\Configuration;
namespace Thor\Cli\Command;
class Argument
{
......
<?php
namespace Thor\Process;
namespace Thor\Cli\Command;
use Thor\Cli\CliKernel;
use Thor\Cli\Console\Mode;
use Thor\Cli\Console\Color;
use Thor\Process\Executable;
use Thor\Cli\Console\Console;
use Thor\Cli\Console\FixedOutput;
use JetBrains\PhpStorm\ArrayShape;
......@@ -33,12 +34,26 @@ abstract class Command implements Executable
$this->context = [];
}
/**
* Returns true if the $commandLineArguments array corresponds to this command.
*
* @param array $commandLineArguments
*
* @return bool
*/
public function matches(array $commandLineArguments): bool
{
$command = array_shift($commandLineArguments);
return $command === $this->command;
}
/**
* Sets the context this command execute with.
*
* @param array $context
*
* @return void
*/
public function setContext(array $context): void
{
$this->context = $context;
......
<?php
namespace Thor\Process;
namespace Thor\Cli\Command;
class CommandError extends \RuntimeException
use Thor\Debug\ThorException;
class CommandError extends ThorException
{
public const NOT_FOUND = 1;
public const MISMATCH = 2;
public const MISUSAGE = 3;
public static function notFound(string $input): self
{
return new self("Command \"$input\" not found");
return new self(self::NOT_FOUND, "Command \"$input\" not found");
}
public static function mismatch(Command $cliCommand, string $input): self
{
return new self("The command line \"$input\" mismatches the \"{$cliCommand->command}\" command.");
return new self(self::MISMATCH, "The command line \"$input\" mismatches the \"{$cliCommand->command}\" command.");
}
public static function misusage(Command $cliCommand): self
{
$cliCommand->usage();
return new self("Invalid usage of \"{$cliCommand->command}\"");
return new self(self::MISUSAGE, "Invalid usage of \"{$cliCommand->command}\"");
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment