Init
This commit is contained in:
5
.docker/nginx/Dockerfile
Normal file
5
.docker/nginx/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FROM nginx:alpine
|
||||||
|
#COPY ./ /var/www/html/
|
||||||
|
CMD ["nginx"]
|
||||||
|
|
||||||
|
EXPOSE 80 443
|
||||||
3
.docker/nginx/conf.d/default.conf
Normal file
3
.docker/nginx/conf.d/default.conf
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
upstream php-upstream {
|
||||||
|
server php:9000;
|
||||||
|
}
|
||||||
24
.docker/nginx/nginx.conf
Normal file
24
.docker/nginx/nginx.conf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
user nginx;
|
||||||
|
worker_processes 4;
|
||||||
|
daemon off;
|
||||||
|
|
||||||
|
error_log /var/log/nginx/error.log warn;
|
||||||
|
pid /var/run/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
error_log /dev/stderr;
|
||||||
|
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
include /etc/nginx/conf.d/*.conf;
|
||||||
|
include /etc/nginx/sites-available/*.conf;
|
||||||
|
}
|
||||||
28
.docker/nginx/sites/default.conf
Normal file
28
.docker/nginx/sites/default.conf
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
server {
|
||||||
|
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server ipv6only=on;
|
||||||
|
|
||||||
|
server_name localhost;
|
||||||
|
root /var/www/html/www/;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri /index.php =404;
|
||||||
|
fastcgi_pass php-upstream;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
fastcgi_buffers 16 16k;
|
||||||
|
fastcgi_buffer_size 32k;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_read_timeout 600;
|
||||||
|
include fastcgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\.ht {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
9
.docker/php/Dockerfile
Normal file
9
.docker/php/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
FROM thecodingmachine/php:8.0-v4-fpm
|
||||||
|
|
||||||
|
#COPY ./ /var/www/html/
|
||||||
|
|
||||||
|
#WORKDIR /var/www/html/
|
||||||
|
|
||||||
|
#CMD ["php-fpm"]
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
BIN
.docs/assets/screenshot1.png
Normal file
BIN
.docs/assets/screenshot1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 585 KiB |
BIN
.docs/assets/screenshot2.png
Normal file
BIN
.docs/assets/screenshot2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 514 KiB |
BIN
.docs/assets/screenshot3.png
Normal file
BIN
.docs/assets/screenshot3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 475 KiB |
BIN
.docs/assets/screenshot4.png
Normal file
BIN
.docs/assets/screenshot4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 507 KiB |
16
.editorconfig
Normal file
16
.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# EditorConfig is awesome: http://EditorConfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = tab
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
[{*.json, *.yaml, *.yml, *.md}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
10
.github/.kodiak.toml
vendored
Normal file
10
.github/.kodiak.toml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version = 1
|
||||||
|
|
||||||
|
[merge]
|
||||||
|
automerge_label = "automerge"
|
||||||
|
blacklist_title_regex = "^WIP.*"
|
||||||
|
blacklist_labels = ["WIP"]
|
||||||
|
method = "rebase"
|
||||||
|
delete_branch_on_merge = true
|
||||||
|
notify_on_conflict = true
|
||||||
|
optimistic_updates = false
|
||||||
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: composer
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
- "automerge"
|
||||||
192
.github/workflows/main.yaml
vendored
Normal file
192
.github/workflows/main.yaml
vendored
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
name: "build"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- ".docs/**"
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
schedule:
|
||||||
|
- cron: "0 8 * * 1" # At 08:00 on Monday
|
||||||
|
|
||||||
|
env:
|
||||||
|
extensions: "json"
|
||||||
|
cacheVersion: "1"
|
||||||
|
composerVersion: "v2"
|
||||||
|
composerInstall: "composer install"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
qa:
|
||||||
|
name: "Quality Assurance"
|
||||||
|
runs-on: "${{ matrix.operating-system }}"
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: [ "8.0" ]
|
||||||
|
operating-system: [ "ubuntu-latest" ]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v2"
|
||||||
|
|
||||||
|
- name: "Setup PHP cache environment"
|
||||||
|
id: "extcache"
|
||||||
|
uses: "shivammathur/cache-extensions@v1"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
key: "${{ env.cacheVersion }}"
|
||||||
|
|
||||||
|
- name: "Cache PHP extensions"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.extcache.outputs.dir }}"
|
||||||
|
key: "${{ steps.extcache.outputs.key }}"
|
||||||
|
restore-keys: "${{ steps.extcache.outputs.key }}"
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
tools: "composer:${{ env.composerVersion }} "
|
||||||
|
|
||||||
|
- name: "Setup problem matchers for PHP"
|
||||||
|
run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"'
|
||||||
|
|
||||||
|
- name: "Get Composer cache directory"
|
||||||
|
id: "composercache"
|
||||||
|
run: 'echo "::set-output name=dir::$(composer config cache-files-dir)"'
|
||||||
|
|
||||||
|
- name: "Cache PHP dependencies"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.composercache.outputs.dir }}"
|
||||||
|
key: "${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}"
|
||||||
|
restore-keys: "${{ runner.os }}-composer-"
|
||||||
|
|
||||||
|
- name: "Validate Composer"
|
||||||
|
run: "composer validate"
|
||||||
|
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: "${{ env.composerInstall }}"
|
||||||
|
|
||||||
|
- name: "Coding Standard"
|
||||||
|
run: "make cs"
|
||||||
|
|
||||||
|
static-analysis:
|
||||||
|
name: "Static analysis"
|
||||||
|
runs-on: "${{ matrix.operating-system }}"
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: [ "8.0" ]
|
||||||
|
operating-system: [ "ubuntu-latest" ]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v2"
|
||||||
|
|
||||||
|
- name: "Setup PHP cache environment"
|
||||||
|
id: "extcache"
|
||||||
|
uses: "shivammathur/cache-extensions@v1"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
key: "${{ env.cacheVersion }}"
|
||||||
|
|
||||||
|
- name: "Cache PHP extensions"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.extcache.outputs.dir }}"
|
||||||
|
key: "${{ steps.extcache.outputs.key }}"
|
||||||
|
restore-keys: "${{ steps.extcache.outputs.key }}"
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
tools: "composer:${{ env.composerVersion }} "
|
||||||
|
|
||||||
|
- name: "Setup problem matchers for PHP"
|
||||||
|
run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"'
|
||||||
|
|
||||||
|
- name: "Get Composer cache directory"
|
||||||
|
id: "composercache"
|
||||||
|
run: 'echo "::set-output name=dir::$(composer config cache-files-dir)"'
|
||||||
|
|
||||||
|
- name: "Cache PHP dependencies"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.composercache.outputs.dir }}"
|
||||||
|
key: "${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}"
|
||||||
|
restore-keys: "${{ runner.os }}-composer-"
|
||||||
|
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: "${{ env.composerInstall }}"
|
||||||
|
|
||||||
|
- name: "PHPStan"
|
||||||
|
run: "make phpstan"
|
||||||
|
|
||||||
|
tests:
|
||||||
|
name: "Tests"
|
||||||
|
runs-on: "${{ matrix.operating-system }}"
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php-versions: [ "8.0" ]
|
||||||
|
operating-system: [ "ubuntu-latest" ]
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: "Checkout"
|
||||||
|
uses: "actions/checkout@v2"
|
||||||
|
|
||||||
|
- name: "Setup PHP cache environment"
|
||||||
|
id: "extcache"
|
||||||
|
uses: "shivammathur/cache-extensions@v1"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
key: "${{ env.cacheVersion }}"
|
||||||
|
|
||||||
|
- name: "Cache PHP extensions"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.extcache.outputs.dir }}"
|
||||||
|
key: "${{ steps.extcache.outputs.key }}"
|
||||||
|
restore-keys: "${{ steps.extcache.outputs.key }}"
|
||||||
|
|
||||||
|
- name: "Install PHP"
|
||||||
|
uses: "shivammathur/setup-php@v2"
|
||||||
|
with:
|
||||||
|
php-version: "${{ matrix.php-versions }}"
|
||||||
|
extensions: "${{ env.extensions }}"
|
||||||
|
tools: "composer:${{ env.composerVersion }} "
|
||||||
|
|
||||||
|
- name: "Setup problem matchers for PHP"
|
||||||
|
run: 'echo "::add-matcher::${{ runner.tool_cache }}/php.json"'
|
||||||
|
|
||||||
|
- name: "Get Composer cache directory"
|
||||||
|
id: "composercache"
|
||||||
|
run: 'echo "::set-output name=dir::$(composer config cache-files-dir)"'
|
||||||
|
|
||||||
|
- name: "Cache PHP dependencies"
|
||||||
|
uses: "actions/cache@v2"
|
||||||
|
with:
|
||||||
|
path: "${{ steps.composercache.outputs.dir }}"
|
||||||
|
key: "${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}"
|
||||||
|
restore-keys: "${{ runner.os }}-composer-"
|
||||||
|
|
||||||
|
- name: "Install dependencies"
|
||||||
|
run: "${{ env.composerInstall }}"
|
||||||
|
|
||||||
|
- name: "Setup problem matchers for PHPUnit"
|
||||||
|
run: 'echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"'
|
||||||
|
|
||||||
|
- name: "Tests"
|
||||||
|
run: "make tests"
|
||||||
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Nette
|
||||||
|
/config/local.neon
|
||||||
|
|
||||||
|
# Composer
|
||||||
|
/vendor
|
||||||
|
|
||||||
|
# NodeJS
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# Assets
|
||||||
|
/www/dist
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/tests/*.log
|
||||||
|
/tests/tmp
|
||||||
|
/tests/coverage.html
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2018 Contributte
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
73
Makefile
Normal file
73
Makefile
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
############################################################
|
||||||
|
# PROJECT ##################################################
|
||||||
|
############################################################
|
||||||
|
.PHONY: project install setup clean
|
||||||
|
|
||||||
|
project: install setup
|
||||||
|
|
||||||
|
install:
|
||||||
|
composer install
|
||||||
|
|
||||||
|
setup:
|
||||||
|
mkdir -p var/tmp var/log
|
||||||
|
chmod +0777 var/tmp var/log
|
||||||
|
|
||||||
|
clean:
|
||||||
|
find var/tmp -mindepth 1 ! -name '.gitignore' -type f,d -exec rm -rf {} +
|
||||||
|
find var/log -mindepth 1 ! -name '.gitignore' -type f,d -exec rm -rf {} +
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# DEVELOPMENT ##############################################
|
||||||
|
############################################################
|
||||||
|
.PHONY: qa dev cs csf phpstan tests coverage dev build
|
||||||
|
|
||||||
|
qa: cs phpstan
|
||||||
|
|
||||||
|
cs:
|
||||||
|
vendor/bin/codesniffer app tests
|
||||||
|
|
||||||
|
csf:
|
||||||
|
vendor/bin/codefixer app tests
|
||||||
|
|
||||||
|
phpstan:
|
||||||
|
vendor/bin/phpstan analyse -c phpstan.neon --memory-limit=512M app tests/toolkit
|
||||||
|
|
||||||
|
tests:
|
||||||
|
vendor/bin/tester -s -p php --colors 1 -C tests
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
vendor/bin/tester -s -p phpdbg --colors 1 -C --coverage ./coverage.xml --coverage-src ./app tests
|
||||||
|
|
||||||
|
dev:
|
||||||
|
NETTE_DEBUG=1 NETTE_ENV=dev php -S 0.0.0.0:8000 -t www
|
||||||
|
|
||||||
|
build:
|
||||||
|
NETTE_DEBUG=1 bin/console orm:schema-tool:drop --force --full-database
|
||||||
|
NETTE_DEBUG=1 bin/console migrations:migrate --no-interaction
|
||||||
|
NETTE_DEBUG=1 bin/console doctrine:fixtures:load --no-interaction --append
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# DEPLOYMENT ###############################################
|
||||||
|
############################################################
|
||||||
|
.PHONY: deploy
|
||||||
|
|
||||||
|
deploy: clean project build clean
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# DOCKER ###################################################
|
||||||
|
############################################################
|
||||||
|
.PHONY: docker-postgres docker-postgres-stop docker-adminer docker-adminer-stop
|
||||||
|
|
||||||
|
docker-postgres: docker-postgres-stop
|
||||||
|
docker run -it -d -p 5432:5432 --name webapp_postgres -e POSTGRES_PASSWORD=webapp -e POSTGRES_USER=webapp dockette/postgres:12
|
||||||
|
|
||||||
|
docker-postgres-stop:
|
||||||
|
docker stop webapp_postgres || true
|
||||||
|
docker rm webapp_postgres || true
|
||||||
|
|
||||||
|
docker-adminer: docker-adminer-stop
|
||||||
|
docker run -it -d -p 9999:80 --name webapp_adminer dockette/adminer:dg
|
||||||
|
|
||||||
|
docker-adminer-stop:
|
||||||
|
docker stop webapp_adminer || true
|
||||||
|
docker rm webapp_adminer || true
|
||||||
236
README.md
Normal file
236
README.md
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|

|
||||||
|
|
||||||
|
<p align=center>
|
||||||
|
<a href="https://github.com/contributte/webapp-skeleton/actions"><img src="https://badgen.net/github/checks/contributte/webapp-skeleton/master"></a>
|
||||||
|
<a href="https://coveralls.io/r/contributte/webapp-skeleton"><img src="https://badgen.net/coveralls/c/github/contributte/webapp-skeleton"></a>
|
||||||
|
<a href="https://packagist.org/packages/contributte/webapp-skeleton"><img src="https://badgen.net/packagist/dm/contributte/webapp-skeleton"></a>
|
||||||
|
<a href="https://packagist.org/packages/contributte/webapp-skeleton"><img src="https://badgen.net/packagist/v/contributte/webapp-skeleton"></a>
|
||||||
|
</p>
|
||||||
|
<p align=center>
|
||||||
|
<a href="https://packagist.org/packages/contributte/webapp-skeleton"><img src="https://badgen.net/packagist/php/contributte/webapp-skeleton"></a>
|
||||||
|
<a href="https://github.com/contributte/webapp-skeleton"><img src="https://badgen.net/github/license/contributte/webapp-skeleton"></a>
|
||||||
|
<a href="https://bit.ly/ctteg"><img src="https://badgen.net/badge/support/gitter/cyan"></a>
|
||||||
|
<a href="https://bit.ly/cttfo"><img src="https://badgen.net/badge/support/forum/yellow"></a>
|
||||||
|
<a href="https://contributte.org/partners.html"><img src="https://badgen.net/badge/sponsor/donations/F96854"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align=center>
|
||||||
|
Website 🚀 <a href="https://contributte.org">contributte.org</a> | Contact 👨🏻💻 <a href="https://f3l1x.io">f3l1x.io</a> | Twitter 🐦 <a href="https://twitter.com/contributte">@contributte</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align=center>
|
||||||
|
<img src="https://api.microlink.io?url=https%3A%2F%2Fexamples.contributte.org%2Fwebapp-skeleton%2F&overlay.browser=light&screenshot=true&meta=false&embed=screenshot.url"></img>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Main goal is to provide best prepared starter-kit project for Nette developers.
|
||||||
|
|
||||||
|
Focused on:
|
||||||
|
|
||||||
|
- latest PHP 8.0
|
||||||
|
- `nette/*` packages
|
||||||
|
- Doctrine ORM via `nettrine/*`
|
||||||
|
- Symfony components via `contributte/*`
|
||||||
|
- codestyle checking via **CodeSniffer** and `ninjify/*`
|
||||||
|
- static analysing via **phpstan**
|
||||||
|
- unit / integration tests via **Nette Tester** and `ninjify/*`
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
https://examples.contributte.org/webapp-skeleton/
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To install latest version of `contributte/webapp-skeleton` use [Composer](https://getcomposer.com).
|
||||||
|
|
||||||
|
```
|
||||||
|
composer create-project -s dev contributte/webapp-skeleton acme
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install using [docker](https://github.com/docker/docker/)
|
||||||
|
|
||||||
|
1) At first, use composer to install this project.
|
||||||
|
|
||||||
|
```
|
||||||
|
composer create-project -s dev contributte/webapp-skeleton
|
||||||
|
```
|
||||||
|
|
||||||
|
2) After that, you have to setup Postgres >= 10 database. You can start it manually or use docker image `dockette/postgres:12`.
|
||||||
|
|
||||||
|
```
|
||||||
|
docker run -it -p 5432:5432 -e POSTGRES_PASSWORD=webapp -e POSTGRES_USER=webapp dockette/postgres:12
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use make task, `make docker-postgres`.
|
||||||
|
|
||||||
|
3) Custom configuration file is located at `config/local.neon`. Edit it if you want.
|
||||||
|
|
||||||
|
Default configuration should look like:
|
||||||
|
|
||||||
|
```neon
|
||||||
|
# Host Config
|
||||||
|
parameters:
|
||||||
|
# Database
|
||||||
|
database:
|
||||||
|
host: localhost
|
||||||
|
dbname: webapp
|
||||||
|
user: webapp
|
||||||
|
password: webapp
|
||||||
|
```
|
||||||
|
|
||||||
|
4) Ok database is now running and application is configured to connect to it. Let's create initial data.
|
||||||
|
|
||||||
|
Run `NETTE_DEBUG=1 bin/console migrations:migrate` to create tables. Run `NETTE_DEBUG=1 bin/console doctrine:fixtures:load --append` to create first user(s).
|
||||||
|
|
||||||
|
Or via task `make build`.
|
||||||
|
|
||||||
|
5) Start your devstack or use PHP local development server.
|
||||||
|
|
||||||
|
You can start PHP server by running `php -S localhost:8000 -t www` or use prepared make task `make dev`.
|
||||||
|
|
||||||
|
6) Open http://localhost and enjoy!
|
||||||
|
|
||||||
|
Take a look at:
|
||||||
|
- http://localhost:8000.
|
||||||
|
- http://localhost:8000/admin (admin@admin.cz / admin)
|
||||||
|
|
||||||
|
### Install using [docker-compose](https://https://github.com/docker/compose/)
|
||||||
|
|
||||||
|
1) At first, use composer to install this project.
|
||||||
|
|
||||||
|
```
|
||||||
|
composer create-project -s dev contributte/webapp-project
|
||||||
|
```
|
||||||
|
|
||||||
|
2) Modify `config/local.neon` and set host to `database`
|
||||||
|
|
||||||
|
Default configuration should look like this:
|
||||||
|
|
||||||
|
```neon
|
||||||
|
# Host Config
|
||||||
|
parameters:
|
||||||
|
# Database
|
||||||
|
database:
|
||||||
|
host: database
|
||||||
|
dbname: webapp
|
||||||
|
user: webapp
|
||||||
|
password: webapp
|
||||||
|
```
|
||||||
|
|
||||||
|
3) Run `docker-compose up`
|
||||||
|
|
||||||
|
4) Open http://localhost and enjoy!
|
||||||
|
|
||||||
|
Take a look at:
|
||||||
|
- http://localhost.
|
||||||
|
- http://localhost/admin (admin@admin.cz / admin)
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Here is a list of all features you can find in this project.
|
||||||
|
|
||||||
|
- PHP 8.0+
|
||||||
|
- :package: Packages
|
||||||
|
- Nette 3+
|
||||||
|
- Contributte
|
||||||
|
- Nettrine
|
||||||
|
- :deciduous_tree: Structure
|
||||||
|
- `app`
|
||||||
|
- `config` - configuration files
|
||||||
|
- `env` - prod/dev/test environments
|
||||||
|
- `app` - application configs
|
||||||
|
- `ext` - extensions configs
|
||||||
|
- `local.neon` - local runtime config
|
||||||
|
- `local.neon.dist` - template for local config
|
||||||
|
- `domain` - business logic and domain specific classes
|
||||||
|
- `model` - application backbone
|
||||||
|
- `modules` - Front/Admin module, presenters and components
|
||||||
|
- `resources` - static content for mails and others
|
||||||
|
- `ui` - UI components and base classes
|
||||||
|
- `bootstrap.php` - Nette entrypoint
|
||||||
|
- `bin` - console entrypoint (`bin/console`)
|
||||||
|
- `db` - database files
|
||||||
|
- `fixtures` - PHP fixtures
|
||||||
|
- `migrations` - migrations files
|
||||||
|
- `docs` - documentation
|
||||||
|
- `var`
|
||||||
|
- `log` - runtime and error logs
|
||||||
|
- `tmp` - tmp files and cache
|
||||||
|
- `tests` - test engine and unit/integration tests
|
||||||
|
- `vendor` - composer's folder
|
||||||
|
- `www` - public content
|
||||||
|
- :exclamation: Tracy
|
||||||
|
- Cool error 500 page
|
||||||
|
|
||||||
|
### Notable changes
|
||||||
|
|
||||||
|
- `$user` variable in templates [is renamed](https://github.com/contributte/webapp-skeleton/blob/master/app/model/Latte/TemplateFactory.php) to `$_user`
|
||||||
|
|
||||||
|
### Composer packages
|
||||||
|
|
||||||
|
Take a detailed look :eyes: at each single package.
|
||||||
|
|
||||||
|
- [contributte/bootstrap](https://contributte.org/packages/contributte/bootstrap.html)
|
||||||
|
- [contributte/application](https://contributte.org/packages/contributte/application.html)
|
||||||
|
- [contributte/di](https://contributte.org/packages/contributte/di.html)
|
||||||
|
- [contributte/cache](https://contributte.org/packages/contributte/cache.html)
|
||||||
|
- [contributte/http](https://contributte.org/packages/contributte/http.html)
|
||||||
|
- [contributte/forms](https://contributte.org/packages/contributte/forms.html)
|
||||||
|
- [contributte/latte](https://contributte.org/packages/contributte/latte.html)
|
||||||
|
- [contributte/mail](https://contributte.org/packages/contributte/mail.html)
|
||||||
|
- [contributte/security](https://contributte.org/packages/contributte/security.html)
|
||||||
|
- [contributte/utils](https://contributte.org/packages/contributte/utils.html)
|
||||||
|
- [contributte/tracy](https://contributte.org/packages/contributte/tracy.html)
|
||||||
|
- [contributte/console](https://contributte.org/packages/contributte/console.html)
|
||||||
|
- [contributte/webapp-skeleton](https://contributte.org/packages/contributte/webapp-skeleton.html)
|
||||||
|
- [contributte/event-dispatcher](https://contributte.org/packages/contributte/event-dispatcher.html)
|
||||||
|
- [contributte/event-dispatcher-extra](https://contributte.org/packages/contributte/event-dispatcher-extra.html)
|
||||||
|
- [contributte/neonizer](https://contributte.org/packages/contributte/neonizer.html)
|
||||||
|
- [contributte/mailing](https://contributte.org/packages/contributte/mailing.html)
|
||||||
|
- [contributte/monolog](https://contributte.org/packages/contributte/monolog.html)
|
||||||
|
|
||||||
|
**Nettrine**
|
||||||
|
|
||||||
|
- [nettrine/orm](https://contributte.org/packages/nettrine/orm.html)
|
||||||
|
- [nettrine/dbal](https://contributte.org/packages/nettrine/dbal.html)
|
||||||
|
- [nettrine/annotations](https://contributte.org/packages/nettrine/annotations.html)
|
||||||
|
- [nettrine/cache](https://contributte.org/packages/nettrine/cache.html)
|
||||||
|
- [nettrine/migrations](https://contributte.org/packages/nettrine/migrations.html)
|
||||||
|
- [nettrine/fixtures](https://contributte.org/packages/nettrine/fixtures.html)
|
||||||
|
- [nettrine/extensions](https://contributte.org/packages/nettrine/extensions.html)
|
||||||
|
|
||||||
|
**Dev**
|
||||||
|
|
||||||
|
- [contributte/dev](https://contributte.org/packages/contributte/dev.html)
|
||||||
|
- [ninjify/qa](https://contributte.org/packages/ninjify/qa.html)
|
||||||
|
- [ninjify/nunjuck](https://contributte.org/packages/ninjify/nunjuck.html)
|
||||||
|
- [phpstan/phpstan](https://github.com/phpstan/phpstan)
|
||||||
|
- [mockery/mockery](https://github.com/mockery/mockery)
|
||||||
|
- [nelmio/alice](https://github.com/nelmio/alice)
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> admin@admin.cz / admin
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
See [how to contribute](https://contributte.org/contributing.html) to this package.
|
||||||
|
|
||||||
|
This package is currently maintaining by these authors.
|
||||||
|
|
||||||
|
<a href="https://github.com/f3l1x">
|
||||||
|
<img width="80" height="80" src="https://avatars2.githubusercontent.com/u/538058?v=3&s=80">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
Consider to [support](https://contributte.org/partners.html) **contributte** development team. Also thank you for using this project.
|
||||||
2
app/.htaccess
Normal file
2
app/.htaccess
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
Order Allow,Deny
|
||||||
|
Deny from all
|
||||||
40
app/bootstrap.php
Normal file
40
app/bootstrap.php
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
use Contributte\Bootstrap\ExtraConfigurator;
|
||||||
|
use Nette\DI\Compiler;
|
||||||
|
use Tracy\Debugger;
|
||||||
|
|
||||||
|
require __DIR__ . '/../vendor/autoload.php';
|
||||||
|
|
||||||
|
$configurator = new ExtraConfigurator();
|
||||||
|
$configurator->setTempDirectory(__DIR__ . '/../var/tmp');
|
||||||
|
|
||||||
|
$configurator->onCompile[] = function (ExtraConfigurator $configurator, Compiler $compiler): void {
|
||||||
|
// Add env variables to config structure
|
||||||
|
$compiler->addConfig(['parameters' => $configurator->getEnvironmentParameters()]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// According to NETTE_DEBUG env
|
||||||
|
$configurator->setEnvDebugMode();
|
||||||
|
|
||||||
|
// Enable tracy and configure it
|
||||||
|
$configurator->enableTracy(__DIR__ . '/../var/log');
|
||||||
|
Debugger::$errorTemplate = __DIR__ . '/resources/tracy/500.phtml';
|
||||||
|
|
||||||
|
// Provide some parameters
|
||||||
|
$configurator->addParameters([
|
||||||
|
'rootDir' => realpath(__DIR__ . '/..'),
|
||||||
|
'appDir' => __DIR__,
|
||||||
|
'wwwDir' => realpath(__DIR__ . '/../www'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Load development or production config
|
||||||
|
if (getenv('NETTE_ENV', true) === 'dev') {
|
||||||
|
$configurator->addConfig(__DIR__ . '/../config/env/dev.neon');
|
||||||
|
} else {
|
||||||
|
$configurator->addConfig(__DIR__ . '/../config/env/prod.neon');
|
||||||
|
}
|
||||||
|
|
||||||
|
$configurator->addConfig(__DIR__ . '/../config/local.neon');
|
||||||
|
|
||||||
|
return $configurator->createContainer();
|
||||||
25
app/domain/Http/RequestLoggerSubscriber.php
Normal file
25
app/domain/Http/RequestLoggerSubscriber.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Domain\Http;
|
||||||
|
|
||||||
|
use Contributte\Events\Extra\Event\Application\RequestEvent;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Tracy\Debugger;
|
||||||
|
|
||||||
|
class RequestLoggerSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [RequestEvent::class => 'onRequest'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onRequest(RequestEvent $event): void
|
||||||
|
{
|
||||||
|
Debugger::barDump($event->getRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
app/domain/Order/Event/OrderCreated.php
Normal file
25
app/domain/Order/Event/OrderCreated.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Domain\Order\Event;
|
||||||
|
|
||||||
|
use Symfony\Contracts\EventDispatcher\Event;
|
||||||
|
|
||||||
|
final class OrderCreated extends Event
|
||||||
|
{
|
||||||
|
|
||||||
|
public const NAME = 'order.created';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
private $order;
|
||||||
|
|
||||||
|
public function __construct(string $order)
|
||||||
|
{
|
||||||
|
$this->order = $order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrder(): string
|
||||||
|
{
|
||||||
|
return $this->order;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
app/domain/Order/OrderLogSubscriber.php
Normal file
42
app/domain/Order/OrderLogSubscriber.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Domain\Order;
|
||||||
|
|
||||||
|
use App\Domain\Order\Event\OrderCreated;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Tracy\Debugger;
|
||||||
|
|
||||||
|
class OrderLogSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
OrderCreated::NAME => [
|
||||||
|
['onOrderCreatedBefore', 100],
|
||||||
|
['onOrderCreated', 0],
|
||||||
|
['onOrderCreatedAfter', -100],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOrderCreatedBefore(OrderCreated $event): void
|
||||||
|
{
|
||||||
|
Debugger::barDump('BEFORE');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOrderCreated(OrderCreated $event): void
|
||||||
|
{
|
||||||
|
Debugger::log($event, 'info');
|
||||||
|
Debugger::barDump($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onOrderCreatedAfter(OrderCreated $event): void
|
||||||
|
{
|
||||||
|
Debugger::barDump('AFTER');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
app/domain/User/CreateUserFacade.php
Normal file
48
app/domain/User/CreateUserFacade.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Domain\User;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
use App\Model\Database\EntityManager;
|
||||||
|
use App\Model\Security\Passwords;
|
||||||
|
|
||||||
|
class CreateUserFacade
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var EntityManager */
|
||||||
|
private $em;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
EntityManager $em
|
||||||
|
)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $data
|
||||||
|
*/
|
||||||
|
public function createUser(array $data): User
|
||||||
|
{
|
||||||
|
// Create User
|
||||||
|
$user = new User(
|
||||||
|
$data['name'],
|
||||||
|
$data['surname'],
|
||||||
|
$data['email'],
|
||||||
|
$data['username'],
|
||||||
|
Passwords::create()->hash($data['password'] ?? md5(microtime()))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set role
|
||||||
|
if (isset($data['role'])) {
|
||||||
|
$user->setRole($data['role']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save user
|
||||||
|
$this->em->persist($user);
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
app/model/App.php
Normal file
14
app/model/App.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model;
|
||||||
|
|
||||||
|
final class App
|
||||||
|
{
|
||||||
|
|
||||||
|
public const DESTINATION_FRONT_HOMEPAGE = ':Front:Home:';
|
||||||
|
public const DESTINATION_ADMIN_HOMEPAGE = ':Admin:Home:';
|
||||||
|
public const DESTINATION_SIGN_IN = ':Admin:Sign:in';
|
||||||
|
public const DESTINATION_AFTER_SIGN_IN = self::DESTINATION_ADMIN_HOMEPAGE;
|
||||||
|
public const DESTINATION_AFTER_SIGN_OUT = self::DESTINATION_FRONT_HOMEPAGE;
|
||||||
|
|
||||||
|
}
|
||||||
25
app/model/Console/HelloCommand.php
Normal file
25
app/model/Console/HelloCommand.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Console;
|
||||||
|
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class HelloCommand extends Command
|
||||||
|
{
|
||||||
|
|
||||||
|
protected function configure(): void
|
||||||
|
{
|
||||||
|
$this->setName('hello');
|
||||||
|
$this->setDescription('Hello world!');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$output->write('Hello world!');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
app/model/Database/Entity/AbstractEntity.php
Normal file
8
app/model/Database/Entity/AbstractEntity.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
abstract class AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
33
app/model/Database/Entity/Attributes/TCreatedAt.php
Normal file
33
app/model/Database/Entity/Attributes/TCreatedAt.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity\Attributes;
|
||||||
|
|
||||||
|
use App\Model\Utils\DateTime;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
trait TCreatedAt
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DateTime
|
||||||
|
* @ORM\Column(type="datetime", nullable=FALSE)
|
||||||
|
*/
|
||||||
|
protected $createdAt;
|
||||||
|
|
||||||
|
public function getCreatedAt(): DateTime
|
||||||
|
{
|
||||||
|
return $this->createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doctrine annotation
|
||||||
|
*
|
||||||
|
* @ORM\PrePersist
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setCreatedAt(): void
|
||||||
|
{
|
||||||
|
$this->createdAt = new DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
app/model/Database/Entity/Attributes/TId.php
Normal file
27
app/model/Database/Entity/Attributes/TId.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity\Attributes;
|
||||||
|
|
||||||
|
trait TId
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @ORM\Column(type="integer", nullable=FALSE)
|
||||||
|
* @ORM\Id
|
||||||
|
* @ORM\GeneratedValue
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function __clone()
|
||||||
|
{
|
||||||
|
$this->id = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
app/model/Database/Entity/Attributes/TUpdatedAt.php
Normal file
33
app/model/Database/Entity/Attributes/TUpdatedAt.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity\Attributes;
|
||||||
|
|
||||||
|
use App\Model\Utils\DateTime;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
trait TUpdatedAt
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DateTime|NULL
|
||||||
|
* @ORM\Column(type="datetime", nullable=TRUE)
|
||||||
|
*/
|
||||||
|
protected $updatedAt;
|
||||||
|
|
||||||
|
public function getUpdatedAt(): ?DateTime
|
||||||
|
{
|
||||||
|
return $this->updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Doctrine annotation
|
||||||
|
*
|
||||||
|
* @ORM\PreUpdate
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
public function setUpdatedAt(): void
|
||||||
|
{
|
||||||
|
$this->updatedAt = new DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
48
app/model/Database/Entity/DictType.php
Normal file
48
app/model/Database/Entity/DictType.php
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\Attributes\TCreatedAt;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
use App\Model\Database\Entity\Attributes\TUpdatedAt;
|
||||||
|
use App\Model\Exception\Logic\InvalidArgumentException;
|
||||||
|
use App\Model\Security\Identity;
|
||||||
|
use App\Model\Utils\DateTime;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class DictType extends AbstractEntity
|
||||||
|
{
|
||||||
|
use TId;
|
||||||
|
|
||||||
|
public function __construct($short_name,$full_name)
|
||||||
|
{
|
||||||
|
$this->shortName = $short_name;
|
||||||
|
$this->fullName = $full_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $shortName;
|
||||||
|
|
||||||
|
public function getShortName()
|
||||||
|
{
|
||||||
|
return $this->shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $fullName;
|
||||||
|
|
||||||
|
public function getFullName()
|
||||||
|
{
|
||||||
|
return $this->fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
76
app/model/Database/Entity/Dictionary.php
Normal file
76
app/model/Database/Entity/Dictionary.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class Dictionary extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($name,$fullname)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->fullName = $fullname;
|
||||||
|
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\OneToMany(targetEntity="App\Model\Database\Entity\Translation", mappedBy="dictionary", cascade={"persist"})
|
||||||
|
* @var Article[]|\Doctrine\Common\Collections\ArrayCollection
|
||||||
|
*/
|
||||||
|
protected $translations;
|
||||||
|
|
||||||
|
public function getTranslations()
|
||||||
|
{
|
||||||
|
return $this->translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Language", inversedBy="lang1_dicionaries")
|
||||||
|
*/
|
||||||
|
protected $lang1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Language", inversedBy="lang2_dicionaries")
|
||||||
|
*/
|
||||||
|
protected $lang2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $fullName;
|
||||||
|
|
||||||
|
public function setLang1($lang1)
|
||||||
|
{
|
||||||
|
$this->lang1 = $lang1;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLang2($lang2)
|
||||||
|
{
|
||||||
|
$this->lang2 = $lang2;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
54
app/model/Database/Entity/Language.php
Normal file
54
app/model/Database/Entity/Language.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class Language extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($shortname,$name)
|
||||||
|
{
|
||||||
|
$this->shortName = $shortname;
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string",length=2, unique=true, options={"fixed" = true})
|
||||||
|
*/
|
||||||
|
protected $shortName;
|
||||||
|
|
||||||
|
public function getShortName()
|
||||||
|
{
|
||||||
|
return $this->shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string",length=32)
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ORM\OneToMany(targetEntity="Dictionary", mappedBy="language")
|
||||||
|
*/
|
||||||
|
protected $dictionaries1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @ORM\OneToMany(targetEntity="Dictionary", mappedBy="language")
|
||||||
|
*/
|
||||||
|
protected $dictionaries2;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
46
app/model/Database/Entity/Pronunciation.php
Normal file
46
app/model/Database/Entity/Pronunciation.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class Pronunciation extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($type,$ipa,$filename=null)
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
$this->ipa = $ipa;
|
||||||
|
$this->filename = $filename;
|
||||||
|
$this->terms = new \Doctrine\Common\Collections\ArrayCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToMany(targetEntity="Term", mappedBy="pronunciations")
|
||||||
|
*/
|
||||||
|
private $terms;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="PronunciationType", inversedBy="pronunciations")
|
||||||
|
*/
|
||||||
|
protected $type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string",nullable=true)
|
||||||
|
*/
|
||||||
|
protected $ipa;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string", nullable=true)
|
||||||
|
*/
|
||||||
|
protected $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
32
app/model/Database/Entity/PronunciationTypes.php
Normal file
32
app/model/Database/Entity/PronunciationTypes.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\Tid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class PronunciationType extends AbstractEntity
|
||||||
|
{
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($name,$fullName)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->fullName = $fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $fullName;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
32
app/model/Database/Entity/Suffix.php
Normal file
32
app/model/Database/Entity/Suffix.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class Suffix extends AbstractEntity
|
||||||
|
{
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($text)
|
||||||
|
{
|
||||||
|
$this->text = $text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $text;
|
||||||
|
|
||||||
|
|
||||||
|
public function getText()
|
||||||
|
{
|
||||||
|
return $this->text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
42
app/model/Database/Entity/Term.php
Normal file
42
app/model/Database/Entity/Term.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
* @ORM\Table(indexes={@ORM\Index(columns={"string1"}, flags={"fulltext"}),
|
||||||
|
@ORM\Index(columns={"string2"}, flags={"fulltext"})})
|
||||||
|
*/
|
||||||
|
class Term extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct(Dictionary $dictionary,$string)
|
||||||
|
{
|
||||||
|
$this->dictionary = $dictionary;
|
||||||
|
$this->string1 = $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Dictionary", inversedBy="fullDict",cascade={"persist", "remove" })
|
||||||
|
*/
|
||||||
|
protected $dictionary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get id
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
90
app/model/Database/Entity/TermFlag.php
Normal file
90
app/model/Database/Entity/TermFlag.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class TermFlag extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($id,$class,$language,$code,$description)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->class = $class;
|
||||||
|
$this->language = $language;
|
||||||
|
$this->code = $code;
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="WordClass", inversedBy="flags")
|
||||||
|
*/
|
||||||
|
protected $class;
|
||||||
|
|
||||||
|
public function getClass()
|
||||||
|
{
|
||||||
|
return $this->class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClass($class)
|
||||||
|
{
|
||||||
|
$this->class = $class;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Language", inversedBy="flags")
|
||||||
|
*/
|
||||||
|
protected $language;
|
||||||
|
|
||||||
|
public function getLanguage()
|
||||||
|
{
|
||||||
|
return $this->language;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLanguage($language)
|
||||||
|
{
|
||||||
|
$this->language = $language;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $code;
|
||||||
|
|
||||||
|
public function getCode()
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCode($code)
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $description;
|
||||||
|
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription($description)
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
77
app/model/Database/Entity/Translation.php
Normal file
77
app/model/Database/Entity/Translation.php
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class Translation extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($dictionary,$lang1,$lang2,$lang_name1,$lang_name2,$direction)
|
||||||
|
{
|
||||||
|
$this->dictionary = $dictionary;
|
||||||
|
$this->lang1 = $lang1;
|
||||||
|
$this->lang2 = $lang2;
|
||||||
|
$this->lang_name1 = $lang_name1;
|
||||||
|
$this->lang_name2 = $lang_name2;
|
||||||
|
$this->direction = $direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Dictionary", inversedBy="translations")
|
||||||
|
*/
|
||||||
|
protected $dictionary;
|
||||||
|
|
||||||
|
public function getDictionary()
|
||||||
|
{
|
||||||
|
return $this->dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Language", inversedBy="translations1")
|
||||||
|
*/
|
||||||
|
protected $lang1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\ManyToOne(targetEntity="Language", inversedBy="translations2")
|
||||||
|
*/
|
||||||
|
protected $lang2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $lang_name1;
|
||||||
|
|
||||||
|
public function getLangName1()
|
||||||
|
{
|
||||||
|
return $this->lang_name1;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $lang_name2;
|
||||||
|
|
||||||
|
public function getLangName2()
|
||||||
|
{
|
||||||
|
return $this->lang_name2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="smallint")
|
||||||
|
*/
|
||||||
|
protected $direction;
|
||||||
|
|
||||||
|
public function getDirection()
|
||||||
|
{
|
||||||
|
return $this->direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
205
app/model/Database/Entity/User.php
Normal file
205
app/model/Database/Entity/User.php
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\Attributes\TCreatedAt;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
use App\Model\Database\Entity\Attributes\TUpdatedAt;
|
||||||
|
use App\Model\Exception\Logic\InvalidArgumentException;
|
||||||
|
use App\Model\Security\Identity;
|
||||||
|
use App\Model\Utils\DateTime;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity(repositoryClass="App\Model\Database\Repository\UserRepository")
|
||||||
|
* @ORM\Table(name="`user`")
|
||||||
|
* @ORM\HasLifecycleCallbacks
|
||||||
|
*/
|
||||||
|
class User extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
public const ROLE_ADMIN = 'admin';
|
||||||
|
public const ROLE_USER = 'user';
|
||||||
|
|
||||||
|
public const STATE_FRESH = 1;
|
||||||
|
public const STATE_ACTIVATED = 2;
|
||||||
|
public const STATE_BLOCKED = 3;
|
||||||
|
|
||||||
|
public const STATES = [self::STATE_FRESH, self::STATE_BLOCKED, self::STATE_ACTIVATED];
|
||||||
|
|
||||||
|
use TId;
|
||||||
|
use TCreatedAt;
|
||||||
|
use TUpdatedAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE, unique=false)
|
||||||
|
*/
|
||||||
|
private $name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE, unique=false)
|
||||||
|
*/
|
||||||
|
private $surname;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE, unique=TRUE)
|
||||||
|
*/
|
||||||
|
private $email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE, unique=TRUE)
|
||||||
|
*/
|
||||||
|
private $username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
* @ORM\Column(type="integer", length=10, nullable=FALSE)
|
||||||
|
*/
|
||||||
|
private $state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE)
|
||||||
|
*/
|
||||||
|
private $password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @ORM\Column(type="string", length=255, nullable=FALSE)
|
||||||
|
*/
|
||||||
|
private $role;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DateTime|NULL
|
||||||
|
* @ORM\Column(type="datetime", nullable=TRUE)
|
||||||
|
*/
|
||||||
|
private $lastLoggedAt;
|
||||||
|
|
||||||
|
public function __construct(string $name, string $surname, string $email, string $username, string $passwordHash)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->surname = $surname;
|
||||||
|
$this->email = $email;
|
||||||
|
$this->username = $username;
|
||||||
|
$this->password = $passwordHash;
|
||||||
|
|
||||||
|
$this->role = self::ROLE_USER;
|
||||||
|
$this->state = self::STATE_FRESH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeLoggedAt(): void
|
||||||
|
{
|
||||||
|
$this->lastLoggedAt = new DateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEmail(): string
|
||||||
|
{
|
||||||
|
return $this->email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsername(): string
|
||||||
|
{
|
||||||
|
return $this->username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changeUsername(string $username): void
|
||||||
|
{
|
||||||
|
$this->username = $username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLastLoggedAt(): ?DateTime
|
||||||
|
{
|
||||||
|
return $this->lastLoggedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRole(): string
|
||||||
|
{
|
||||||
|
return $this->role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRole(string $role): void
|
||||||
|
{
|
||||||
|
$this->role = $role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPasswordHash(): string
|
||||||
|
{
|
||||||
|
return $this->password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function changePasswordHash(string $password): void
|
||||||
|
{
|
||||||
|
$this->password = $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function block(): void
|
||||||
|
{
|
||||||
|
$this->state = self::STATE_BLOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activate(): void
|
||||||
|
{
|
||||||
|
$this->state = self::STATE_ACTIVATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActivated(): bool
|
||||||
|
{
|
||||||
|
return $this->state === self::STATE_ACTIVATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSurname(): string
|
||||||
|
{
|
||||||
|
return $this->surname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFullname(): string
|
||||||
|
{
|
||||||
|
return $this->name . ' ' . $this->surname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rename(string $name, string $surname): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
$this->surname = $surname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getState(): int
|
||||||
|
{
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setState(int $state): void
|
||||||
|
{
|
||||||
|
if (!in_array($state, self::STATES)) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Unsupported state %s', $state));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->state = $state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGravatar(): string
|
||||||
|
{
|
||||||
|
return 'https://www.gravatar.com/avatar/' . md5($this->email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toIdentity(): Identity
|
||||||
|
{
|
||||||
|
return new Identity($this->getId(), [$this->role], [
|
||||||
|
'email' => $this->email,
|
||||||
|
'name' => $this->name,
|
||||||
|
'surname' => $this->surname,
|
||||||
|
'state' => $this->state,
|
||||||
|
'gravatar' => $this->getGravatar(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
app/model/Database/Entity/WordClass.php
Normal file
49
app/model/Database/Entity/WordClass.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Model\Database\Entity;
|
||||||
|
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Model\Database\Entity\Attributes\TId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Entity
|
||||||
|
*/
|
||||||
|
class WordClass extends AbstractEntity
|
||||||
|
{
|
||||||
|
|
||||||
|
use Tid;
|
||||||
|
|
||||||
|
public function __construct($id,$name)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ORM\Column(type="string")
|
||||||
|
*/
|
||||||
|
protected $name;
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
28
app/model/Database/EntityManager.php
Normal file
28
app/model/Database/EntityManager.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database;
|
||||||
|
|
||||||
|
use App\Model\Database\Repository\AbstractRepository;
|
||||||
|
use Doctrine\Persistence\ObjectRepository;
|
||||||
|
use Nettrine\ORM\EntityManagerDecorator;
|
||||||
|
|
||||||
|
class EntityManager extends EntityManagerDecorator
|
||||||
|
{
|
||||||
|
|
||||||
|
use TRepositories;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $entityName
|
||||||
|
* @return AbstractRepository<T>|ObjectRepository<T>
|
||||||
|
* @internal
|
||||||
|
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
|
||||||
|
* @phpstan-template T of object
|
||||||
|
* @phpstan-param class-string<T> $entityName
|
||||||
|
* @phpstan-return ObjectRepository<T>
|
||||||
|
*/
|
||||||
|
public function getRepository($entityName): ObjectRepository
|
||||||
|
{
|
||||||
|
return parent::getRepository($entityName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
app/model/Database/Repository/AbstractRepository.php
Normal file
49
app/model/Database/Repository/AbstractRepository.php
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Repository;
|
||||||
|
|
||||||
|
use Doctrine\ORM\EntityRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-template TEntityClass of object
|
||||||
|
* @phpstan-extends EntityRepository<TEntityClass>
|
||||||
|
*/
|
||||||
|
abstract class AbstractRepository extends EntityRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all records like $key => $value pairs
|
||||||
|
*
|
||||||
|
* @param mixed[] $criteria
|
||||||
|
* @param mixed[] $orderBy
|
||||||
|
* @return mixed[]
|
||||||
|
*/
|
||||||
|
public function findPairs(?string $key, string $value, array $criteria = [], array $orderBy = []): array
|
||||||
|
{
|
||||||
|
if ($key === null) {
|
||||||
|
$key = $this->getClassMetadata()->getSingleIdentifierFieldName();
|
||||||
|
}
|
||||||
|
|
||||||
|
$qb = $this->createQueryBuilder('e')
|
||||||
|
->select(['e.' . $value, 'e.' . $key])
|
||||||
|
->resetDQLPart('from')
|
||||||
|
->from($this->getEntityName(), 'e', 'e.' . $key);
|
||||||
|
|
||||||
|
foreach ($criteria as $k => $v) {
|
||||||
|
if (is_array($v)) {
|
||||||
|
$qb->andWhere(sprintf('e.%s IN(:%s)', $k, $k))->setParameter($k, array_values($v));
|
||||||
|
} else {
|
||||||
|
$qb->andWhere(sprintf('e.%s = :%s', $k, $k))->setParameter($k, $v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($orderBy as $column => $order) {
|
||||||
|
$qb->addOrderBy($column, $order);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(function ($row) {
|
||||||
|
return reset($row);
|
||||||
|
}, $qb->getQuery()->getArrayResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
38
app/model/Database/Repository/DictTypes.php
Normal file
38
app/model/Database/Repository/DictTypes.php
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class DictTypes extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $dicttypes;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->dicttypes = $em->getRepository(DictType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($value)
|
||||||
|
{
|
||||||
|
return $this->dicttypes->find($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findBy($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->dicttypes->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->dicttypes->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAssoc($criteria, $key = NULL)
|
||||||
|
{
|
||||||
|
return $this->dicttypes->findAssoc($criteria,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
app/model/Database/Repository/Dictionaries.php
Normal file
27
app/model/Database/Repository/Dictionaries.php
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class Dictionaries extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $dictionaries;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->dictionaries = $em->getRepository(Dictionary::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->dictionaries->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->dictionaries->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/model/Database/Repository/Pronunciations.php
Normal file
33
app/model/Database/Repository/Pronunciations.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class Pronunciations extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $pronunciations;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->pronunciations = $em->getRepository(Pronunciation::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($value)
|
||||||
|
{
|
||||||
|
return $this->pronunciations->find($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findBy($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->pronunciations->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->pronunciations->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/model/Database/Repository/TermFlags.php
Normal file
37
app/model/Database/Repository/TermFlags.php
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class TermFlags extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $termFlags;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->termFlags = $em->getRepository(TermFlag::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($value)
|
||||||
|
{
|
||||||
|
return $this->termFlags->find($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->termFlags->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->termFlags->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAssoc($criteria,$key)
|
||||||
|
{
|
||||||
|
return $this->termFlags->findAssoc($criteria,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
app/model/Database/Repository/Terms.php
Normal file
46
app/model/Database/Repository/Terms.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class Terms extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $termsDao;
|
||||||
|
private $terms;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->terms = $em->getRepository(Term::class);
|
||||||
|
$this->termsDao = $em->getDao(Term::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findAll($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->terms->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findBy($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->terms->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($id)
|
||||||
|
{
|
||||||
|
return $this->terms->find($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->terms->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetch($query)
|
||||||
|
{
|
||||||
|
return $this->termsDao->fetch($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
app/model/Database/Repository/Translations.php
Normal file
33
app/model/Database/Repository/Translations.php
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?php
|
||||||
|
namespace App;
|
||||||
|
|
||||||
|
use Kdyby;
|
||||||
|
use Nette;
|
||||||
|
|
||||||
|
class Translations extends Nette\Object
|
||||||
|
{
|
||||||
|
private $em;
|
||||||
|
private $translations;
|
||||||
|
|
||||||
|
public function __construct(Kdyby\Doctrine\EntityManager $em)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->translations = $em->getRepository(Translation::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function find($value)
|
||||||
|
{
|
||||||
|
return $this->translations->find($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function findBy($criteria = [],$orderBy = [])
|
||||||
|
{
|
||||||
|
return $this->translations->findBy($criteria,$orderBy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function findPairs($criteria,$value,$orderBy,$key)
|
||||||
|
{
|
||||||
|
return $this->translations->findPairs($criteria,$value,$orderBy,$key);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/model/Database/Repository/UserRepository.php
Normal file
22
app/model/Database/Repository/UserRepository.php
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database\Repository;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method User|NULL find($id, ?int $lockMode = NULL, ?int $lockVersion = NULL)
|
||||||
|
* @method User|NULL findOneBy(array $criteria, array $orderBy = NULL)
|
||||||
|
* @method User[] findAll()
|
||||||
|
* @method User[] findBy(array $criteria, array $orderBy = NULL, ?int $limit = NULL, ?int $offset = NULL)
|
||||||
|
* @extends AbstractRepository<User>
|
||||||
|
*/
|
||||||
|
class UserRepository extends AbstractRepository
|
||||||
|
{
|
||||||
|
|
||||||
|
public function findOneByEmail(string $email): ?User
|
||||||
|
{
|
||||||
|
return $this->findOneBy(['email' => $email]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
app/model/Database/TRepositories.php
Normal file
19
app/model/Database/TRepositories.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Database;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
use App\Model\Database\Repository\UserRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mixin EntityManager
|
||||||
|
*/
|
||||||
|
trait TRepositories
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getUserRepository(): UserRepository
|
||||||
|
{
|
||||||
|
return $this->getRepository(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Exception/Logic/InvalidArgumentException.php
Normal file
10
app/model/Exception/Logic/InvalidArgumentException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception\Logic;
|
||||||
|
|
||||||
|
use App\Model\Exception\LogicException;
|
||||||
|
|
||||||
|
final class InvalidArgumentException extends LogicException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
8
app/model/Exception/LogicException.php
Normal file
8
app/model/Exception/LogicException.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception;
|
||||||
|
|
||||||
|
class LogicException extends \LogicException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Exception/Runtime/AuthenticationException.php
Normal file
10
app/model/Exception/Runtime/AuthenticationException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception\Runtime;
|
||||||
|
|
||||||
|
use Nette\Security\AuthenticationException as NetteAuthenticationException;
|
||||||
|
|
||||||
|
final class AuthenticationException extends NetteAuthenticationException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Exception/Runtime/IOException.php
Normal file
10
app/model/Exception/Runtime/IOException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception\Runtime;
|
||||||
|
|
||||||
|
use App\Model\Exception\RuntimeException;
|
||||||
|
|
||||||
|
final class IOException extends RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Exception/Runtime/InvalidStateException.php
Normal file
10
app/model/Exception/Runtime/InvalidStateException.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception\Runtime;
|
||||||
|
|
||||||
|
use App\Model\Exception\RuntimeException;
|
||||||
|
|
||||||
|
final class InvalidStateException extends RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
8
app/model/Exception/RuntimeException.php
Normal file
8
app/model/Exception/RuntimeException.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Exception;
|
||||||
|
|
||||||
|
class RuntimeException extends \RuntimeException
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
35
app/model/Latte/FilterExecutor.php
Normal file
35
app/model/Latte/FilterExecutor.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Latte;
|
||||||
|
|
||||||
|
use Latte\Engine;
|
||||||
|
|
||||||
|
final class FilterExecutor
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var Engine */
|
||||||
|
private $latte;
|
||||||
|
|
||||||
|
public function __construct(Engine $latte)
|
||||||
|
{
|
||||||
|
$this->latte = $latte;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed[] $args
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __call(string $name, array $args)
|
||||||
|
{
|
||||||
|
return $this->latte->invokeFilter($name, $args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function __get(string $name)
|
||||||
|
{
|
||||||
|
return $this->latte->invokeFilter($name, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
app/model/Latte/Filters.php
Normal file
30
app/model/Latte/Filters.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Latte;
|
||||||
|
|
||||||
|
use Nette\Neon\Neon;
|
||||||
|
use Nette\StaticClass;
|
||||||
|
use Nette\Utils\Json;
|
||||||
|
|
||||||
|
final class Filters
|
||||||
|
{
|
||||||
|
|
||||||
|
use StaticClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public static function neon($value): string
|
||||||
|
{
|
||||||
|
return Neon::encode($value, Neon::BLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $value
|
||||||
|
*/
|
||||||
|
public static function json($value): string
|
||||||
|
{
|
||||||
|
return Json::encode($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
16
app/model/Latte/Macros.php
Normal file
16
app/model/Latte/Macros.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Latte;
|
||||||
|
|
||||||
|
use Latte\Compiler;
|
||||||
|
use Latte\Macros\MacroSet;
|
||||||
|
|
||||||
|
final class Macros extends MacroSet
|
||||||
|
{
|
||||||
|
|
||||||
|
public static function register(Compiler $compiler): void
|
||||||
|
{
|
||||||
|
$compiler = new static($compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
app/model/Latte/TemplateFactory.php
Normal file
51
app/model/Latte/TemplateFactory.php
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Latte;
|
||||||
|
|
||||||
|
use App\Model\Security\SecurityUser;
|
||||||
|
use Nette\Application\UI\Control;
|
||||||
|
use Nette\Bridges\ApplicationLatte\LatteFactory;
|
||||||
|
use Nette\Bridges\ApplicationLatte\Template;
|
||||||
|
use Nette\Bridges\ApplicationLatte\TemplateFactory as NetteTemplateFactory;
|
||||||
|
use Nette\Caching\IStorage;
|
||||||
|
use Nette\Http\IRequest;
|
||||||
|
|
||||||
|
final class TemplateFactory extends NetteTemplateFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var LatteFactory */
|
||||||
|
private $latteFactory;
|
||||||
|
|
||||||
|
/** @var SecurityUser */
|
||||||
|
private $user;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
LatteFactory $latteFactory,
|
||||||
|
IRequest $httpRequest,
|
||||||
|
SecurityUser $user,
|
||||||
|
IStorage $cacheStorage,
|
||||||
|
string $templateClass = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
parent::__construct($latteFactory, $httpRequest, $user, $cacheStorage, $templateClass);
|
||||||
|
$this->latteFactory = $latteFactory;
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createTemplate(Control $control = null, string $class = null): Template
|
||||||
|
{
|
||||||
|
/** @var Template $template */
|
||||||
|
$template = parent::createTemplate($control);
|
||||||
|
|
||||||
|
// Remove default $template->user for prevent misused
|
||||||
|
unset($template->user);
|
||||||
|
|
||||||
|
// Assign new variables
|
||||||
|
$template->_user = $this->user;
|
||||||
|
$template->_template = $template;
|
||||||
|
$template->_filters = new FilterExecutor($this->latteFactory->create());
|
||||||
|
|
||||||
|
return $template;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
20
app/model/Latte/TemplateProperty.php
Normal file
20
app/model/Latte/TemplateProperty.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Latte;
|
||||||
|
|
||||||
|
use App\Model\Security\SecurityUser;
|
||||||
|
use App\Modules\Base\BasePresenter;
|
||||||
|
use App\UI\Control\BaseControl;
|
||||||
|
use Nette\Bridges\ApplicationLatte\Template;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property-read SecurityUser $_user
|
||||||
|
* @property-read BasePresenter $presenter
|
||||||
|
* @property-read BaseControl $control
|
||||||
|
* @property-read string $baseUri
|
||||||
|
* @property-read string $basePath
|
||||||
|
* @property-read array $flashes
|
||||||
|
*/
|
||||||
|
final class TemplateProperty extends Template
|
||||||
|
{
|
||||||
|
}
|
||||||
55
app/model/Router/RouterFactory.php
Normal file
55
app/model/Router/RouterFactory.php
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Router;
|
||||||
|
|
||||||
|
use Nette\Application\Routers\Route;
|
||||||
|
use Nette\Application\Routers\RouteList;
|
||||||
|
|
||||||
|
final class RouterFactory
|
||||||
|
{
|
||||||
|
|
||||||
|
public function create(): RouteList
|
||||||
|
{
|
||||||
|
$router = new RouteList();
|
||||||
|
|
||||||
|
$this->buildMailing($router);
|
||||||
|
$this->buildPdf($router);
|
||||||
|
$this->buildAdmin($router);
|
||||||
|
$this->buildFront($router);
|
||||||
|
|
||||||
|
return $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildAdmin(RouteList $router): RouteList
|
||||||
|
{
|
||||||
|
$router[] = $list = new RouteList('Admin');
|
||||||
|
$list[] = new Route('admin/<presenter>/<action>[/<id>]', 'Home:default');
|
||||||
|
|
||||||
|
return $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildFront(RouteList $router): RouteList
|
||||||
|
{
|
||||||
|
$router[] = $list = new RouteList('Front');
|
||||||
|
$list[] = new Route('<presenter>/<action>[/<id>]', 'Home:default');
|
||||||
|
|
||||||
|
return $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildMailing(RouteList $router): RouteList
|
||||||
|
{
|
||||||
|
$router[] = $list = new RouteList('Mailing');
|
||||||
|
$list[] = new Route('mailing/<presenter>/<action>[/<id>]', 'Home:default');
|
||||||
|
|
||||||
|
return $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildPdf(RouteList $router): RouteList
|
||||||
|
{
|
||||||
|
$router[] = $list = new RouteList('Pdf');
|
||||||
|
$list[] = new Route('pdf/<presenter>/<action>[/<id>]', 'Home:default');
|
||||||
|
|
||||||
|
return $router;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
app/model/Security/Authenticator/UserAuthenticator.php
Normal file
57
app/model/Security/Authenticator/UserAuthenticator.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Security\Authenticator;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
use App\Model\Database\EntityManager;
|
||||||
|
use App\Model\Exception\Runtime\AuthenticationException;
|
||||||
|
use App\Model\Security\Passwords;
|
||||||
|
use Nette\Security\Authenticator;
|
||||||
|
use Nette\Security\IIdentity;
|
||||||
|
|
||||||
|
final class UserAuthenticator implements Authenticator
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var EntityManager */
|
||||||
|
private $em;
|
||||||
|
|
||||||
|
/** @var Passwords */
|
||||||
|
private $passwords;
|
||||||
|
|
||||||
|
public function __construct(EntityManager $em, Passwords $passwords)
|
||||||
|
{
|
||||||
|
$this->em = $em;
|
||||||
|
$this->passwords = $passwords;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $username
|
||||||
|
* @param string $password
|
||||||
|
* @throws AuthenticationException
|
||||||
|
*/
|
||||||
|
public function authenticate(string $username, string $password): IIdentity
|
||||||
|
{
|
||||||
|
$user = $this->em->getUserRepository()->findOneBy(['email' => $username]);
|
||||||
|
|
||||||
|
if (!$user) {
|
||||||
|
throw new AuthenticationException('The username is incorrect.', self::IDENTITY_NOT_FOUND);
|
||||||
|
} elseif (!$user->isActivated()) {
|
||||||
|
throw new AuthenticationException('The user is not active.', self::INVALID_CREDENTIAL);
|
||||||
|
} elseif (!$this->passwords->verify($password, $user->getPasswordHash())) {
|
||||||
|
throw new AuthenticationException('The password is incorrect.', self::INVALID_CREDENTIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->changeLoggedAt();
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
return $this->createIdentity($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createIdentity(User $user): IIdentity
|
||||||
|
{
|
||||||
|
$this->em->flush();
|
||||||
|
|
||||||
|
return $user->toIdentity();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
47
app/model/Security/Authorizator/StaticAuthorizator.php
Normal file
47
app/model/Security/Authorizator/StaticAuthorizator.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Security\Authorizator;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
use Nette\Security\Permission;
|
||||||
|
|
||||||
|
final class StaticAuthorizator extends Permission
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create ACL
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->addRoles();
|
||||||
|
$this->addResources();
|
||||||
|
$this->addPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup roles
|
||||||
|
*/
|
||||||
|
protected function addRoles(): void
|
||||||
|
{
|
||||||
|
$this->addRole(User::ROLE_ADMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup resources
|
||||||
|
*/
|
||||||
|
protected function addResources(): void
|
||||||
|
{
|
||||||
|
$this->addResource('Admin:Home');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup ACL
|
||||||
|
*/
|
||||||
|
protected function addPermissions(): void
|
||||||
|
{
|
||||||
|
$this->allow(User::ROLE_ADMIN, [
|
||||||
|
'Admin:Home',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
app/model/Security/Identity.php
Normal file
15
app/model/Security/Identity.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Security;
|
||||||
|
|
||||||
|
use Nette\Security\SimpleIdentity as NetteIdentity;
|
||||||
|
|
||||||
|
class Identity extends NetteIdentity
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getFullname(): string
|
||||||
|
{
|
||||||
|
return sprintf('%s %s', $this->data['name'] ?? '', $this->data['surname'] ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
15
app/model/Security/Passwords.php
Normal file
15
app/model/Security/Passwords.php
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Security;
|
||||||
|
|
||||||
|
use Nette\Security\Passwords as NettePasswords;
|
||||||
|
|
||||||
|
final class Passwords extends NettePasswords
|
||||||
|
{
|
||||||
|
|
||||||
|
public static function create(): Passwords
|
||||||
|
{
|
||||||
|
return new Passwords();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
app/model/Security/SecurityUser.php
Normal file
19
app/model/Security/SecurityUser.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Security;
|
||||||
|
|
||||||
|
use App\Model\Database\Entity\User;
|
||||||
|
use Nette\Security\User as NetteUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method Identity getIdentity()
|
||||||
|
*/
|
||||||
|
final class SecurityUser extends NetteUser
|
||||||
|
{
|
||||||
|
|
||||||
|
public function isAdmin(): bool
|
||||||
|
{
|
||||||
|
return $this->isInRole(User::ROLE_ADMIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/Arrays.php
Normal file
10
app/model/Utils/Arrays.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Nette\Utils\Arrays as NetteArrays;
|
||||||
|
|
||||||
|
final class Arrays extends NetteArrays
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
13
app/model/Utils/DateTime.php
Normal file
13
app/model/Utils/DateTime.php
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Nette\Utils\DateTime as ContributteDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method DateTime modifyClone(string $modify = '')
|
||||||
|
*/
|
||||||
|
final class DateTime extends ContributteDateTime
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/FileSystem.php
Normal file
10
app/model/Utils/FileSystem.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Contributte\Utils\FileSystem as ContributteFileSystem;
|
||||||
|
|
||||||
|
final class FileSystem extends ContributteFileSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/Html.php
Normal file
10
app/model/Utils/Html.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Nette\Utils\Html as NetteHtml;
|
||||||
|
|
||||||
|
final class Html extends NetteHtml
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/Image.php
Normal file
10
app/model/Utils/Image.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Nette\Utils\Image as NetteImage;
|
||||||
|
|
||||||
|
class Image extends NetteImage
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/Strings.php
Normal file
10
app/model/Utils/Strings.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Contributte\Utils\Strings as ContributteStrings;
|
||||||
|
|
||||||
|
final class Strings extends ContributteStrings
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/model/Utils/Validators.php
Normal file
10
app/model/Utils/Validators.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Model\Utils;
|
||||||
|
|
||||||
|
use Contributte\Utils\Validators as ContributteValidators;
|
||||||
|
|
||||||
|
final class Validators extends ContributteValidators
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
26
app/modules/Admin/BaseAdminPresenter.php
Normal file
26
app/modules/Admin/BaseAdminPresenter.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Admin;
|
||||||
|
|
||||||
|
use App\Model\App;
|
||||||
|
use App\Modules\Base\SecuredPresenter;
|
||||||
|
use Nette\Application\UI\ComponentReflection;
|
||||||
|
|
||||||
|
abstract class BaseAdminPresenter extends SecuredPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ComponentReflection|mixed $element
|
||||||
|
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
|
||||||
|
*/
|
||||||
|
public function checkRequirements($element): void
|
||||||
|
{
|
||||||
|
parent::checkRequirements($element);
|
||||||
|
|
||||||
|
if (!$this->user->isAllowed('Admin:Home')) {
|
||||||
|
$this->flashError('You cannot access this with user role');
|
||||||
|
$this->redirect(App::DESTINATION_FRONT_HOMEPAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
app/modules/Admin/Home/HomePresenter.php
Normal file
31
app/modules/Admin/Home/HomePresenter.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Admin\Home;
|
||||||
|
|
||||||
|
use App\Domain\Order\Event\OrderCreated;
|
||||||
|
use App\Modules\Admin\BaseAdminPresenter;
|
||||||
|
use Nette\Application\UI\Form;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
|
|
||||||
|
final class HomePresenter extends BaseAdminPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var EventDispatcherInterface @inject */
|
||||||
|
public $dispatcher;
|
||||||
|
|
||||||
|
protected function createComponentOrderForm(): Form
|
||||||
|
{
|
||||||
|
$form = new Form();
|
||||||
|
|
||||||
|
$form->addText('order', 'Order name')
|
||||||
|
->setRequired(true);
|
||||||
|
$form->addSubmit('send', 'OK');
|
||||||
|
|
||||||
|
$form->onSuccess[] = function (Form $form): void {
|
||||||
|
$this->dispatcher->dispatch(new OrderCreated($form->values->order), OrderCreated::NAME);
|
||||||
|
};
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
app/modules/Admin/Home/templates/default.latte
Normal file
8
app/modules/Admin/Home/templates/default.latte
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1>SECRET PAGE!</h1>
|
||||||
|
<a n:href="Sign:out" class="btn btn-danger">Logout</a>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h2>Orders</h2>
|
||||||
|
{control orderForm}
|
||||||
75
app/modules/Admin/Sign/SignPresenter.php
Normal file
75
app/modules/Admin/Sign/SignPresenter.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Admin\Sign;
|
||||||
|
|
||||||
|
use App\Model\App;
|
||||||
|
use App\Model\Exception\Runtime\AuthenticationException;
|
||||||
|
use App\Modules\Admin\BaseAdminPresenter;
|
||||||
|
use App\UI\Form\BaseForm;
|
||||||
|
use App\UI\Form\FormFactory;
|
||||||
|
use Nette\Application\UI\ComponentReflection;
|
||||||
|
|
||||||
|
final class SignPresenter extends BaseAdminPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var string @persistent */
|
||||||
|
public $backlink;
|
||||||
|
|
||||||
|
/** @var FormFactory @inject */
|
||||||
|
public $formFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ComponentReflection|mixed $element
|
||||||
|
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
|
||||||
|
*/
|
||||||
|
public function checkRequirements($element): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionIn(): void
|
||||||
|
{
|
||||||
|
if ($this->user->isLoggedIn()) {
|
||||||
|
$this->redirect(App::DESTINATION_AFTER_SIGN_IN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionOut(): void
|
||||||
|
{
|
||||||
|
if ($this->user->isLoggedIn()) {
|
||||||
|
$this->user->logout();
|
||||||
|
$this->flashSuccess('_front.sign.out.success');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect(App::DESTINATION_AFTER_SIGN_OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function createComponentLoginForm(): BaseForm
|
||||||
|
{
|
||||||
|
$form = $this->formFactory->forBackend();
|
||||||
|
$form->addEmail('email')
|
||||||
|
->setRequired(true);
|
||||||
|
$form->addPassword('password')
|
||||||
|
->setRequired(true);
|
||||||
|
$form->addCheckbox('remember')
|
||||||
|
->setDefaultValue(true);
|
||||||
|
$form->addSubmit('submit');
|
||||||
|
$form->onSuccess[] = [$this, 'processLoginForm'];
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processLoginForm(BaseForm $form): void
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$this->user->setExpiration($form->values->remember ? '14 days' : '20 minutes');
|
||||||
|
$this->user->login($form->values->email, $form->values->password);
|
||||||
|
} catch (AuthenticationException $e) {
|
||||||
|
$form->addError('Invalid username or password');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect(App::DESTINATION_AFTER_SIGN_IN);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
29
app/modules/Admin/Sign/templates/in.latte
Normal file
29
app/modules/Admin/Sign/templates/in.latte
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{block #content}
|
||||||
|
<form n:name="loginForm" class="form-signin">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<img class="mb-4" src="https://avatars0.githubusercontent.com/u/99965?s=200&v=4" alt="" width="72" height="72">
|
||||||
|
<h1 class="h3 mb-3 font-weight-normal">Webapp Skeleton Admin</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div n:foreach="$form->errors as $error" class="alert alert-danger" role="alert">
|
||||||
|
{$error}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-label-group">
|
||||||
|
<input type="email" n:name="email" class="form-control" placeholder="Email address" required autofocus>
|
||||||
|
<label n:name="email">Email address</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-label-group">
|
||||||
|
<input type="password" n:name="password" class="form-control" placeholder="Password" required>
|
||||||
|
<label n:name="password">Password</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox mb-3">
|
||||||
|
<label>
|
||||||
|
<input n:name="remember" type="checkbox"> Remember me
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button n:name="submit" class="btn btn-lg btn-primary btn-block">Sign in</button>
|
||||||
|
<p class="mt-5 mb-3 text-muted text-center">© {=date('Y')}</p>
|
||||||
|
</form>
|
||||||
7
app/modules/Admin/templates/@layout.latte
Normal file
7
app/modules/Admin/templates/@layout.latte
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{layout '../../Base/templates/@layout.latte'}
|
||||||
|
|
||||||
|
{block #head}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.2.1/css/bootstrap.min.css" defer />
|
||||||
|
<link rel="stylesheet" href="{$basePath}/assets/admin.css" defer />
|
||||||
|
<script src="{$basePath}/assets/admin.js" defer></script>
|
||||||
|
{/block}
|
||||||
44
app/modules/Base/BaseError4xxPresenter.php
Normal file
44
app/modules/Base/BaseError4xxPresenter.php
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Base;
|
||||||
|
|
||||||
|
use App\Model\Exception\Runtime\InvalidStateException;
|
||||||
|
use Nette\Application\BadRequestException;
|
||||||
|
use Nette\Application\Request;
|
||||||
|
use Nette\Application\UI\ComponentReflection;
|
||||||
|
|
||||||
|
abstract class BaseError4xxPresenter extends SecuredPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common presenter method
|
||||||
|
*/
|
||||||
|
public function startup(): void
|
||||||
|
{
|
||||||
|
parent::startup();
|
||||||
|
|
||||||
|
if ($this->getRequest() !== null && $this->getRequest()->isMethod(Request::FORWARD)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderDefault(BadRequestException $exception): void
|
||||||
|
{
|
||||||
|
$rf1 = new ComponentReflection(static::class);
|
||||||
|
$fileName = $rf1->getFileName();
|
||||||
|
|
||||||
|
// Validate if class is not in PHP core
|
||||||
|
if ($fileName === false) {
|
||||||
|
throw new InvalidStateException('Class is defined in the PHP core or in a PHP extension');
|
||||||
|
}
|
||||||
|
|
||||||
|
$dir = dirname($fileName);
|
||||||
|
|
||||||
|
// Load template 403.latte or 404.latte or ... 4xx.latte
|
||||||
|
$file = $dir . '/templates/' . $exception->getCode() . '.latte';
|
||||||
|
$this->template->setFile(is_file($file) ? $file : $dir . '/templates/4xx.latte');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
app/modules/Base/BaseErrorPresenter.php
Normal file
57
app/modules/Base/BaseErrorPresenter.php
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Base;
|
||||||
|
|
||||||
|
use Nette\Application\BadRequestException;
|
||||||
|
use Nette\Application\Helpers;
|
||||||
|
use Nette\Application\IResponse as AppResponse;
|
||||||
|
use Nette\Application\Request;
|
||||||
|
use Nette\Application\Responses\CallbackResponse;
|
||||||
|
use Nette\Application\Responses\ForwardResponse;
|
||||||
|
use Nette\Http\IRequest;
|
||||||
|
use Nette\Http\IResponse;
|
||||||
|
use Psr\Log\LogLevel;
|
||||||
|
use Throwable;
|
||||||
|
use Tracy\Debugger;
|
||||||
|
use Tracy\ILogger;
|
||||||
|
|
||||||
|
abstract class BaseErrorPresenter extends SecuredPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ForwardResponse|CallbackResponse
|
||||||
|
*/
|
||||||
|
public function run(Request $request): AppResponse
|
||||||
|
{
|
||||||
|
$e = $request->getParameter('exception');
|
||||||
|
|
||||||
|
if ($e instanceof Throwable) {
|
||||||
|
$code = $e->getCode();
|
||||||
|
$level = ($code >= 400 && $code <= 499) ? LogLevel::WARNING : LogLevel::ERROR;
|
||||||
|
|
||||||
|
Debugger::log(sprintf(
|
||||||
|
'Code %s: %s in %s:%s',
|
||||||
|
$code,
|
||||||
|
$e->getMessage(),
|
||||||
|
$e->getFile(),
|
||||||
|
$e->getLine()
|
||||||
|
), $level);
|
||||||
|
|
||||||
|
Debugger::log($e, ILogger::EXCEPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($e instanceof BadRequestException) {
|
||||||
|
[$module, , $sep] = Helpers::splitName($request->getPresenterName());
|
||||||
|
|
||||||
|
return new ForwardResponse($request->setPresenterName($module . $sep . 'Error4xx'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CallbackResponse(function (IRequest $httpRequest, IResponse $httpResponse): void {
|
||||||
|
$header = $httpResponse->getHeader('Content-Type');
|
||||||
|
if ($header !== null && preg_match('#^text/html(?:;|$)#', $header)) {
|
||||||
|
require __DIR__ . '/templates/500.phtml';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
23
app/modules/Base/BasePresenter.php
Normal file
23
app/modules/Base/BasePresenter.php
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Base;
|
||||||
|
|
||||||
|
use App\Model\Latte\TemplateProperty;
|
||||||
|
use App\Model\Security\SecurityUser;
|
||||||
|
use App\UI\Control\TFlashMessage;
|
||||||
|
use App\UI\Control\TModuleUtils;
|
||||||
|
use Contributte\Application\UI\Presenter\StructuredTemplates;
|
||||||
|
use Nette\Application\UI\Presenter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property-read TemplateProperty $template
|
||||||
|
* @property-read SecurityUser $user
|
||||||
|
*/
|
||||||
|
abstract class BasePresenter extends Presenter
|
||||||
|
{
|
||||||
|
|
||||||
|
use StructuredTemplates;
|
||||||
|
use TFlashMessage;
|
||||||
|
use TModuleUtils;
|
||||||
|
|
||||||
|
}
|
||||||
30
app/modules/Base/SecuredPresenter.php
Normal file
30
app/modules/Base/SecuredPresenter.php
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Base;
|
||||||
|
|
||||||
|
use App\Model\App;
|
||||||
|
use Nette\Application\UI\ComponentReflection;
|
||||||
|
use Nette\Security\IUserStorage;
|
||||||
|
|
||||||
|
abstract class SecuredPresenter extends BasePresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ComponentReflection|mixed $element
|
||||||
|
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
|
||||||
|
*/
|
||||||
|
public function checkRequirements($element): void
|
||||||
|
{
|
||||||
|
if (!$this->user->isLoggedIn()) {
|
||||||
|
if ($this->user->getLogoutReason() === IUserStorage::INACTIVITY) {
|
||||||
|
$this->flashInfo('You have been logged out for inactivity');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->redirect(
|
||||||
|
App::DESTINATION_SIGN_IN,
|
||||||
|
['backlink' => $this->storeRequest()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
app/modules/Base/UnsecuredPresenter.php
Normal file
8
app/modules/Base/UnsecuredPresenter.php
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Base;
|
||||||
|
|
||||||
|
abstract class UnsecuredPresenter extends BasePresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
27
app/modules/Base/templates/500.phtml
Normal file
27
app/modules/Base/templates/500.phtml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!DOCTYPE html><!-- "' --></textarea></script></style></pre></xmp></a></audio></button></canvas></datalist></details></dialog></iframe></listing></meter></noembed></noframes></noscript></optgroup></option></progress></rp></select></table></template></title></video>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="robots" content="noindex">
|
||||||
|
<title>Server Error</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#nette-error { all: initial; position: absolute; top: 0; left: 0; right: 0; height: 70vh; min-height: 400px; display: flex; align-items: center; justify-content: center; z-index: 1000 }
|
||||||
|
#nette-error div { all: initial; max-width: 550px; background: white; color: #333; display: block }
|
||||||
|
#nette-error h1 { all: initial; font: bold 50px/1.1 sans-serif; display: block; margin: 40px }
|
||||||
|
#nette-error p { all: initial; font: 20px/1.4 sans-serif; margin: 40px; display: block }
|
||||||
|
#nette-error small { color: gray }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id=nette-error>
|
||||||
|
<div>
|
||||||
|
<h1>Server Error</h1>
|
||||||
|
|
||||||
|
<p>We're sorry! The server encountered an internal error and
|
||||||
|
was unable to complete your request. Please try again later.</p>
|
||||||
|
|
||||||
|
<p><small>error 500</small></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.body.insertBefore(document.getElementById('nette-error'), document.body.firstChild);
|
||||||
|
</script>
|
||||||
28
app/modules/Base/templates/@layout.latte
Normal file
28
app/modules/Base/templates/@layout.latte
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="shortcut icon" href="{$basePath}/favicon.ico">
|
||||||
|
|
||||||
|
<!-- Seo -->
|
||||||
|
<title>{block #title|stripHtml|trim}Webapp Skeleton{/} | Contributte</title>
|
||||||
|
|
||||||
|
<!-- Meta -->
|
||||||
|
<meta name="description" n:ifset="#description" content="{include #description}">
|
||||||
|
<meta name="keywords" n:ifset="#keywords" content="{include #keywords}">
|
||||||
|
<meta name="robots" content="index,follow">
|
||||||
|
<meta name="googlebot" content="snippet,archive">
|
||||||
|
<meta name="author" content="f3l1x">
|
||||||
|
|
||||||
|
{block #head}{/}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{block #main}
|
||||||
|
<div class="container">
|
||||||
|
{include #content}
|
||||||
|
</div>
|
||||||
|
{/}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
app/modules/Front/BaseFrontPresenter.php
Normal file
10
app/modules/Front/BaseFrontPresenter.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Front;
|
||||||
|
|
||||||
|
use App\Modules\Base\UnsecuredPresenter;
|
||||||
|
|
||||||
|
abstract class BaseFrontPresenter extends UnsecuredPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/modules/Front/Error/ErrorPresenter.php
Normal file
10
app/modules/Front/Error/ErrorPresenter.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Front\Error;
|
||||||
|
|
||||||
|
use App\Modules\Base\BaseErrorPresenter;
|
||||||
|
|
||||||
|
final class ErrorPresenter extends BaseErrorPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
app/modules/Front/Error4xx/Error4xxPresenter.php
Normal file
10
app/modules/Front/Error4xx/Error4xxPresenter.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Front\Error4xx;
|
||||||
|
|
||||||
|
use App\Modules\Base\BaseError4xxPresenter;
|
||||||
|
|
||||||
|
final class Error4xxPresenter extends BaseError4xxPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
7
app/modules/Front/Error4xx/templates/403.latte
Normal file
7
app/modules/Front/Error4xx/templates/403.latte
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1 n:block=title>Access Denied</h1>
|
||||||
|
|
||||||
|
<p>You do not have permission to view this page. Please try contact the web
|
||||||
|
site administrator if you believe you should be able to view this page.</p>
|
||||||
|
|
||||||
|
<p><small>error 403</small></p>
|
||||||
8
app/modules/Front/Error4xx/templates/404.latte
Normal file
8
app/modules/Front/Error4xx/templates/404.latte
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1 n:block=title>Page Not Found</h1>
|
||||||
|
|
||||||
|
<p>The page you requested could not be found. It is possible that the address is
|
||||||
|
incorrect, or that the page no longer exists. Please use a search engine to find
|
||||||
|
what you are looking for.</p>
|
||||||
|
|
||||||
|
<p><small>error 404</small></p>
|
||||||
6
app/modules/Front/Error4xx/templates/405.latte
Normal file
6
app/modules/Front/Error4xx/templates/405.latte
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1 n:block=title>Method Not Allowed</h1>
|
||||||
|
|
||||||
|
<p>The requested method is not allowed for the URL.</p>
|
||||||
|
|
||||||
|
<p><small>error 405</small></p>
|
||||||
6
app/modules/Front/Error4xx/templates/410.latte
Normal file
6
app/modules/Front/Error4xx/templates/410.latte
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1 n:block=title>Page Not Found</h1>
|
||||||
|
|
||||||
|
<p>The page you requested has been taken off the site. We apologize for the inconvenience.</p>
|
||||||
|
|
||||||
|
<p><small>error 410</small></p>
|
||||||
4
app/modules/Front/Error4xx/templates/4xx.latte
Normal file
4
app/modules/Front/Error4xx/templates/4xx.latte
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{block #content}
|
||||||
|
<h1 n:block=title>Oops...</h1>
|
||||||
|
|
||||||
|
<p>Your browser sent a request that this server could not understand or process.</p>
|
||||||
10
app/modules/Front/Home/HomePresenter.php
Normal file
10
app/modules/Front/Home/HomePresenter.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Front\Home;
|
||||||
|
|
||||||
|
use App\Modules\Front\BaseFrontPresenter;
|
||||||
|
|
||||||
|
final class HomePresenter extends BaseFrontPresenter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
2
app/modules/Front/Home/templates/default.latte
Normal file
2
app/modules/Front/Home/templates/default.latte
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
{block #content}
|
||||||
|
Welcome
|
||||||
38
app/modules/Front/templates/@layout.latte
Normal file
38
app/modules/Front/templates/@layout.latte
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{layout '../../Base/templates/@layout.latte'}
|
||||||
|
|
||||||
|
{block #head}
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" defer/>
|
||||||
|
<link rel="stylesheet" href="{$basePath}/assets/front.css" defer/>
|
||||||
|
<script src="{$basePath}/assets/front.js" defer></script>
|
||||||
|
{/}
|
||||||
|
|
||||||
|
{block #main}
|
||||||
|
<div class="cover-container d-flex w-100 h-100 p-3 mx-auto flex-column">
|
||||||
|
<header class="masthead mb-auto">
|
||||||
|
<div class="inner">
|
||||||
|
<h3 class="masthead-brand">Webapp</h3>
|
||||||
|
<nav class="nav nav-masthead justify-content-center">
|
||||||
|
<a n:class="$presenter->isLinkCurrent(':Front:Home:') ? active, nav-link" n:href=":Front:Home:">Home</a>
|
||||||
|
<a n:class="$presenter->isLinkCurrent(':Admin:Home:') ? active, nav-link" n:href=":Admin:Home:">Admin</a>
|
||||||
|
<a n:class="$presenter->isLinkCurrent(':Pdf:Home:') ? active, nav-link" n:href=":Pdf:Home:">PDF example</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main role="main" class="inner cover">
|
||||||
|
<h1 class="cover-heading">Webapp skeleton.</h1>
|
||||||
|
<p class="lead">This is example project based on <a href="https://nette.org">Nette Framework</a></p>
|
||||||
|
<p class="lead">And also many <a href="https://contributte.org">Contributte + Nettrine</a> packages.</p>
|
||||||
|
<div class="lead">
|
||||||
|
<a href="https://github.com/contributte/webapp-skeleton" class="btn btn-lg btn-secondary">Learn more in docs</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="mastfoot mt-auto">
|
||||||
|
<div class="inner">
|
||||||
|
<p>Cover template for <a href="https://getbootstrap.com/">Bootstrap</a>, by
|
||||||
|
<a href="https://twitter.com/mdo">@mdo</a>.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
{/}
|
||||||
35
app/modules/Mailing/Home/HomePresenter.php
Normal file
35
app/modules/Mailing/Home/HomePresenter.php
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?php declare(strict_types = 1);
|
||||||
|
|
||||||
|
namespace App\Modules\Mailing\Home;
|
||||||
|
|
||||||
|
use Contributte\Mailing\IMailBuilderFactory;
|
||||||
|
use Nette\Application\UI\Presenter;
|
||||||
|
|
||||||
|
class HomePresenter extends Presenter
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var IMailBuilderFactory */
|
||||||
|
private $mailBuilderFactory;
|
||||||
|
|
||||||
|
public function __construct(IMailBuilderFactory $mailBuilderFactory)
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->mailBuilderFactory = $mailBuilderFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actionDefault(): void
|
||||||
|
{
|
||||||
|
$mail = $this->mailBuilderFactory->create();
|
||||||
|
$mail->setSubject('Example');
|
||||||
|
$mail->addTo('foo@example.com');
|
||||||
|
|
||||||
|
$mail->setTemplateFile(__DIR__ . '/templates/Emails/email.latte');
|
||||||
|
$mail->setParameters([
|
||||||
|
'title' => 'Title',
|
||||||
|
'content' => 'Lorem ipsum dolor sit amet',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$mail->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
app/modules/Mailing/Home/templates/@layout.latte
Normal file
19
app/modules/Mailing/Home/templates/@layout.latte
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
|
||||||
|
<title>{ifset title}{include title|stripHtml} | {/ifset}Nette Web</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">{$flash->message}</div>
|
||||||
|
|
||||||
|
{include content}
|
||||||
|
|
||||||
|
{block scripts}
|
||||||
|
<script src="https://nette.github.io/resources/js/netteForms.min.js"></script>
|
||||||
|
{/block}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user