Init
This commit is contained in:
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>
|
||||
9
app/modules/Mailing/Home/templates/Emails/email.latte
Normal file
9
app/modules/Mailing/Home/templates/Emails/email.latte
Normal file
@@ -0,0 +1,9 @@
|
||||
{layout $_config->layout}
|
||||
|
||||
{block #header}
|
||||
{$title}
|
||||
{/block}
|
||||
|
||||
{block #content}
|
||||
{$content}
|
||||
{/block}
|
||||
38
app/modules/Mailing/Home/templates/Home/default.latte
Normal file
38
app/modules/Mailing/Home/templates/Home/default.latte
Normal file
@@ -0,0 +1,38 @@
|
||||
{* This is the welcome page, you can delete it *}
|
||||
|
||||
{block content}
|
||||
<div id="banner">
|
||||
<h1 n:block=title>Congratulations!</h1>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h2>You have successfully created your <a href="https://nette.org">Nette</a> Web project.</h2>
|
||||
|
||||
<p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABVCAYAAAD0bJKxAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAACudJREFUeNrMXG1sFMcZfvfu7ECw7+xDaQL+AApE1B+EJLapSNs4QIhtVWpSVTJNVIkqatX+II1apf8SoTZSq/KHiv5oqRolVaUS5UfgD04qtRAcUGipSrDdjxjHxDa0xOIcDBiMOU9nZj9udndmZ2Y/zjen0d7uzs7u+8z7Pu87H7sGCFLvrqfWGABbwMyBqfW5C5BrvhFYBkFFpiMvP3HlQ94JgwPI43izD+du1dpbn8XArLlRmaLLW+Qiznte3n7lPS4wPU/uyuHN6zg/rXpPwzAvb+kfhSzWGJTMg0fHBfGe3ZQ+hbORonIQcN5wAdOz80kCygnWbOrzeWhsbITabBZyuSz/ptYNjU3HwaidXhIBw6SF4i2YW5iGIlownz+FAUpTKLpfsTQnYwqI9tmgVFVVwUMPb4F8fqWjTsW7RQtiDio43WMsg3S6puwChtXE6lQNrKi6D67dnoC5uzOAiqY8qTS1mHWkTGrXjp1rcB0v2hdtfHAj1pAcLBaLUFxkcvEuzkUr33WdIxipZm1QUMiskHLLmiFtVNHyWAzyfGt/8ufPfc3WmD0swCMj/6RZyCucYy35Mcimb8bCJShZog1MBBysNcRyjmawGW1RIdige4vMAy2RgNIqRfUv4mwCgzUGoTo/XbNUgpTuiipJwGg3qHPIV0Sqij47FHckLqBi/Uiwn1F9JkNYMyqft0EJWh+yhEQ2MAINUeEWFzYoPg5ZMgJmrs2UorQQ3CIhGZSghkSqBsnNIhOKW3wgSmRACVoSitdUEVLkGCOoLxDyAcvlwWR1I+4Jg88xOtzCtaSKETAi+fqVQf8mcZFvbAJGMSUf+YbgFtEDLbmAEJLzXO46KrdYWobEalB+ARN11zG3cFkFFNSLVGkhtLsWAVkm4kVJgcfGMTKyNUS8wlynRb4oIWVBMVxiyTE+Pu7nGCOMlyIcg5ZOQKXLVOo1LGywMJk4ngtVmoBhb2zluvr6mNw1CmEiuMCqulZYXpVzDn08fTo2jYuCXzqdJqYk6F3zHLbQXetz97KqLPxg+3HXsbfO7oW/T7wp65smZ6qMHCnR4DHS+Kl2ztjcsqrXV6xlda+7nKLqq2S2TpUx9Ewk2zX8SKum1tW9nGN9sCyThdsLs9EpBkXgGaIxNGqVZFlFSLMVifAEBJJu3bkGlz8bdgHmKs6bfok4fcKrt6RRyAJGoT4pcCpqypoRoy1j06dg7NNTLnOKRcCwc1sOx0QzXefhdFqQNaORSwMwcnnA2W9r6KPEHMvknSb/8PtKcfSwFXoW9SuaqPB2GsbAEE4hJrW8OucAd/bim1K+6FjXD60NvbD+vseca23zJFo4+NEhrJGnlTmI9a4pbTPlNB2yIl+k0IKstlyaGYbbd2bpcQKQi2cknuTFXX+B/q6DFGQWFJLIfltjH3x/+xHoWNuvSVaS3rU2sSuOdnas3e38H/zoN04ZAkznOvMcEYqYEwVNUCsh7Ib6NijcnKDaMXNz0oqPcrQeG6zdWw/CZdwApBF0vFL43jVjWr6YA4nNiAjjmNHUgHPfkaljLnNqwyZydvywcMj0bx//ES5cOUXLeNO7Q7+AH/Uch3xNM93/8oPfhcNnXpC3HCNHKnIA5+h6sJqSX1tDDwOKCQR7GTnGahYKsOuxT0+XQPHcjmjau8P7S4SONZDvmjmG5It8Ax5CDhxS8iAd60pmNDQ14LvPMHNsw/2PQf29TfzoVUHAS4VhF+foxj+ZOKJGhOTXm2bUXgJh8pjvOgJW4caEYwJtjb1w8j+HlJ5r/f3bqJmSTulqsvUQMrl/weIhmWdSGqhSHbySVUOEZN3pt7/ye245ViBCoif/fUjYbnks7FPtL0F7k98z8RqmcGNSY8w3TJfCg4JKsNXJmBERgpiKLDXk24UCdX5+NzzT8aoPEELIrDnyPI5wAgAJNMaQBG/aw4R2y9Y0USHDJGpORGuQ22ye3XawFA8VhuCd8/thaNLNWwe+Na38jCDsXUO4yTYVjWHNiHDIT99+NBDY57vfoOZBUhfWjPf+5Tanns0/doHyqz89jc1zVjlGEY6Fo4C+UtRhQZ7XIMI5BItbVegMrJ2hiQGXOREuYQtveKBkIu98uJ+CInOuog6n79khYNghCjZeoIhQrBn99cJhqas8P3HMrXFN7i4Cm+asWMhbV3tjrzSY84IS2LtWGWYQDT14BSR/O9eXtOUqNqMpHF/IYh6iASw4XUwd3pRf0ewTkHQnera8FKwxIo2FOE0pQE1ZoVgTkZkkW7bR8k52vVOYV+z0TNersP6Bbc6lG/D/vT1H6DW6QxAIacQxSp5KEOA15NtgpRWskXQGm5GqpRKNeQ5KnmcrBnjgnBnmD/xjP3xnhxkH3Yvd9Qs9R33Xz81fo0TfuLLd/5goeKRWSWPUTImvpl0b3GZ06eqwcmh+ax6b0yeMOeG67NPnsTb9YXAvFZ6XRv97Cva99Ygr/iG9blAZvbMHGzoffoTMYXRHMaOWr1+BbMM8J0AjoXnWinZnKb/oDFuQ+IdkJ3j732lPlJyFzc19kK81y9zCQI3iMrQByPW1pesL1ydx40wG3py8aFG93DjxSt/YE1pdApFZIcGKqqmrw5F4i7Q4EUik/XNYqz4YPSxE9+r1CZqV+4BUEEO/SyAEUXX8Vbl/imIpolXUM0tANSZKV4B1hZUiYGRhoPS+UsjBu3AzbolOmoUcpPeWyUS7GfJzTAUIGLr3617ny/SuwYhgS0ssZAzvQyEA/nLWKKstyy1kIubonnDTJRYNUFDMNBLnKlnJhJveclbRw+mudkhYwDiSOeEWcbItyorNxAQM8W4T8k24RSEIw3AHb2UUMNTtZCuq4nDXNqhIZdVmOQXUvZzTsNK3T3TvVAkCRqlMqDFh3z5BlSSgAvhIiWOSfIYyxDdJfGxDbzlrXBpTRgGDL0UcOQxPgGcEX6TzgOV+Qx/F9aYqf31MtI4dqiQBwzZgrO5a0IlEib1mwq8vjne1E3DX4SfqkhAwDkeR2MuiSypgyLpcqzbB/ARTLIGRaC5ae80/BC+g1lotHmT63nrMkxft3vU5jRGGeEwigdgmjirTGVoRxSP129d+dxTDdU4CrPJy+KitqL0EHsSv8OlOy6bMrzIdoRrzhZYWst2DzxKTqqukFox1BkFSoGo5+fSb8frP+sc/sTkG3j/zAfl6YDfOn4WOY2JuQV2uCfM2iq0p1RiUTJVBrMb5iJlxbba0EulLW79IFrQdAOuDXqpp01cLULv6TqjWLstcEacqEpUQTslU0xCFgNL982+O08nw6xgTB5izj9bBjtFF+r9106a1YH5B8XHcZ6rDLNwddLPN35iBXOMCVFySjgFRD/TLX3+vcMA+dnJwEO7Mz4PhcT6Gwtb5v/P5mrpVGzMPkclMywwMS63dW0CGrba0bMnFSx0fHR803P/NGNRA1mchzW4f2Zrn7DKIBqvc4wCv/XDmhAc+15bUmeYJzcmi4ynBcVkdPOB57Y04HY8wr1UtKlzvnDOs6FcmUCrgWNgt+98LDvuQixwBFz3nZFtRPUIgw4ASJHBKsB+UvfcAjvCybH/gxGD2Fz3gpphjwNn3BbcZibmkJMdk/1MKZhekMSigpXn/FxXKiupzmVIqwP5l/KLDRTJiB0WegSBuAL33TET7XI+k6p1AAigEAGAodM2C3mlBgmMywlbZAjjrqtSTobEvKwsaGgMhQFPd56b/CzAArAe2YDJd4I4AAAAASUVORK5CYII=" alt="">
|
||||
If you are exploring Nette for the first time, you should read the
|
||||
<a href="https://doc.nette.org/quickstart">Quick Start</a>, <a href="https://doc.nette.org">documentation</a>,
|
||||
<a href="https://pla.nette.org">tutorials</a> and <a href="https://forum.nette.org">forum</a>.</p>
|
||||
|
||||
<h2>We hope you enjoy Nette!</h2>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
html { font: normal 18px/1.3 Georgia, "New York CE", utopia, serif; color: #666; -webkit-text-stroke: 1px rgba(0,0,0,0); overflow-y: scroll; }
|
||||
body { background: #3484d2; color: #333; margin: 2em auto; padding: 0 .5em; max-width: 600px; min-width: 320px; }
|
||||
|
||||
a { color: #006aeb; padding: 3px 1px; }
|
||||
a:hover, a:active, a:focus { background-color: #006aeb; text-decoration: none; color: white; }
|
||||
|
||||
#banner { border-radius: 12px 12px 0 0; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAB5CAMAAADPursXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGBQTFRFD1CRDkqFDTlmDkF1D06NDT1tDTNZDk2KEFWaDTZgDkiCDTtpDT5wDkZ/DTBVEFacEFOWD1KUDTRcDTFWDkV9DkR7DkN4DkByDTVeDC9TDThjDTxrDkeADkuIDTRbDC9SbsUaggAAAEdJREFUeNqkwYURgAAQA7DH3d3335LSKyxAYpf9vWCpnYbf01qcOdFVXc14w4BznNTjkQfsscAdU3b4wIh9fDVYc4zV8xZgAAYaCMI6vPgLAAAAAElFTkSuQmCC); }
|
||||
|
||||
h1 { font: inherit; color: white; font-size: 50px; line-height: 121px; margin: 0; padding-left: 4%; background: url(https://files.nette.org/images/logo-nette@2.png) no-repeat 95%; background-size: 130px auto; text-shadow: 1px 1px 0 rgba(0, 0, 0, .9); }
|
||||
@media (max-width: 600px) {
|
||||
h1 { background: none; font-size: 40px; }
|
||||
}
|
||||
|
||||
#content { background: white; border: 1px solid #eff4f7; border-radius: 0 0 12px 12px; padding: 10px 4%; overflow: hidden; }
|
||||
|
||||
h2 { font: inherit; padding: 1.2em 0; margin: 0; }
|
||||
|
||||
img { border: none; float: right; margin: 0 0 1em 3em; }
|
||||
</style>
|
||||
46
app/modules/Pdf/Home/HomePresenter.php
Normal file
46
app/modules/Pdf/Home/HomePresenter.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\Modules\Pdf\Home;
|
||||
|
||||
use App\Modules\Base\BasePresenter;
|
||||
use Contributte\PdfResponse\PdfResponse;
|
||||
use Nette\Bridges\ApplicationLatte\Template;
|
||||
|
||||
class HomePresenter extends BasePresenter
|
||||
{
|
||||
|
||||
/** @inject */
|
||||
public PdfResponse $pdfResponse;
|
||||
|
||||
private function createPdf(): PdfResponse
|
||||
{
|
||||
/** @var Template $template */
|
||||
$template = $this->createTemplate();
|
||||
$template->setFile(__DIR__ . '/../../../resources/pdf/example.latte');
|
||||
$template->title = 'Contributte PDF example';
|
||||
|
||||
$this->pdfResponse->setTemplate($template->renderToString());
|
||||
$this->pdfResponse->documentTitle = 'Contributte PDF example'; // creates filename 2012-06-30-my-super-title.pdf
|
||||
$this->pdfResponse->pageFormat = 'A4-L'; // wide format
|
||||
$this->pdfResponse->getMPDF()->SetFooter('|Contributte PDF|'); // footer
|
||||
|
||||
return $this->pdfResponse;
|
||||
}
|
||||
|
||||
public function actionViewPdf(): void
|
||||
{
|
||||
$pdf = $this->createPdf();
|
||||
$pdf->setSaveMode(PdfResponse::INLINE);
|
||||
|
||||
$this->sendResponse($pdf);
|
||||
}
|
||||
|
||||
public function actionDownloadPdf(): void
|
||||
{
|
||||
$pdf = $this->createPdf();
|
||||
$pdf->setSaveMode(PdfResponse::DOWNLOAD);
|
||||
|
||||
$this->sendResponse($pdf);
|
||||
}
|
||||
|
||||
}
|
||||
34
app/modules/Pdf/Home/templates/default.latte
Normal file
34
app/modules/Pdf/Home/templates/default.latte
Normal file
@@ -0,0 +1,34 @@
|
||||
{block #content}
|
||||
<div id="banner">
|
||||
<h1 n:block=title>Contributte PDF example</h1>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<h2>Welcome in example of using contributte/pdf.</h2>
|
||||
|
||||
<a n:href="Home:viewPdf" target="_blank">View generated PDF</a>
|
||||
<br><br>
|
||||
<a n:href="Home:downloadPdf" target="_blank">Download generated PDF</a>
|
||||
<p></p>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
html { font: normal 18px/1.3 Georgia, "New York CE", utopia, serif; color: #666; -webkit-text-stroke: 1px rgba(0,0,0,0); overflow-y: scroll; }
|
||||
body { background: #3484d2; color: #333; margin: 2em auto; padding: 0 .5em; max-width: 800px; min-width: 320px; }
|
||||
|
||||
a { color: #006aeb; padding: 3px 1px; }
|
||||
a:hover, a:active, a:focus { background-color: #006aeb; text-decoration: none; color: white; }
|
||||
|
||||
#banner { border-radius: 12px 12px 0 0; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAB5CAMAAADPursXAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAGBQTFRFD1CRDkqFDTlmDkF1D06NDT1tDTNZDk2KEFWaDTZgDkiCDTtpDT5wDkZ/DTBVEFacEFOWD1KUDTRcDTFWDkV9DkR7DkN4DkByDTVeDC9TDThjDTxrDkeADkuIDTRbDC9SbsUaggAAAEdJREFUeNqkwYURgAAQA7DH3d3335LSKyxAYpf9vWCpnYbf01qcOdFVXc14w4BznNTjkQfsscAdU3b4wIh9fDVYc4zV8xZgAAYaCMI6vPgLAAAAAElFTkSuQmCC); }
|
||||
|
||||
h1 { font: inherit; color: white; font-size: 50px; line-height: 121px; margin: 0; padding-left: 4%; background: url(https://files.nette.org/images/logo-nette@2.png) no-repeat 95%; background-size: 130px auto; text-shadow: 1px 1px 0 rgba(0, 0, 0, .9); }
|
||||
@media (max-width: 600px) {
|
||||
h1 { background: none; font-size: 40px; }
|
||||
}
|
||||
|
||||
#content { background: white; border: 1px solid #eff4f7; border-radius: 0 0 12px 12px; padding: 10px 4%; overflow: hidden; }
|
||||
|
||||
h2 { font: inherit; padding: 1.2em 0; margin: 0; }
|
||||
|
||||
img { border: none; float: right; margin: 0 0 1em 3em; }
|
||||
</style>
|
||||
19
app/modules/Pdf/templates/@layout.latte
Normal file
19
app/modules/Pdf/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/3/netteForms.min.js"></script>
|
||||
{/block}
|
||||
</body>
|
||||
</html>
|
||||
9
app/resources/mail/@layout.latte
Normal file
9
app/resources/mail/@layout.latte
Normal file
@@ -0,0 +1,9 @@
|
||||
{layout $_defaults->layout}
|
||||
|
||||
{block #title}
|
||||
Webapp Skeleton
|
||||
{/block}
|
||||
|
||||
{block #copyright}
|
||||
Webapp Skeleton
|
||||
{/block}
|
||||
10
app/resources/mail/signIn/signIn.latte
Normal file
10
app/resources/mail/signIn/signIn.latte
Normal file
@@ -0,0 +1,10 @@
|
||||
{layout $_config->layout}
|
||||
|
||||
{block #header}
|
||||
Automatické přihlášení
|
||||
{/block}
|
||||
|
||||
{block #content}
|
||||
<h2>Přihlášení</h2>
|
||||
<a href="{link Admin:SignIn:, do => auth, token => $token}" target="_blank">Přihlásit se</a>
|
||||
{/block}
|
||||
171
app/resources/pdf/example.latte
Normal file
171
app/resources/pdf/example.latte
Normal file
@@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<title>{$title}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<img src="https://heatbadger.now.sh/github/readme/contributte/pdf/">
|
||||
<h2>Lorem ipsum</h2>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Neque porro quisquam est, qui dolorem ipsum quia
|
||||
dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et
|
||||
dolore magnam aliquam quaerat voluptatem. Sed elit dui, pellentesque a, faucibus vel, interdum nec, diam.
|
||||
Etiam posuere lacus quis dolor. Aliquam ante. Sed vel lectus. Donec odio tempus molestie, porttitor ut,
|
||||
iaculis quis, sem. Aliquam erat volutpat. Fusce wisi. Cum sociis natoque penatibus et magnis dis parturient
|
||||
montes, nascetur ridiculus mus. Nullam eget nisl. Etiam bibendum elit eget erat.
|
||||
</p>
|
||||
<h1 id="readme">ReadMe</h1>
|
||||
<h2 id="usage">Usage</h2>
|
||||
<h3 id="how-to-prepare-pdf-from-template">How to prepare PDF from template</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-comment">// in a Presenter</span>
|
||||
<span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span
|
||||
class="hljs-title">actionPdf</span><span class="hljs-params">()</span>
|
||||
</span>{
|
||||
$template = <span class="hljs-keyword">$this</span>->createTemplate();
|
||||
$template->setFile(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
$template->someValue = <span class="hljs-number">123</span>;
|
||||
<span class="hljs-comment">// Tip: In template to make a new page use <pagebreak></span>
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
|
||||
<span class="hljs-comment">// optional</span>
|
||||
$pdf->documentTitle = date(<span class="hljs-string">"Y-m-d"</span>) . <span class="hljs-string">" My super title"</span>; <span
|
||||
class="hljs-comment">// creates filename 2012-06-30-my-super-title.pdf</span>
|
||||
$pdf->pageFormat = <span class="hljs-string">"A4-L"</span>; <span class="hljs-comment">// wide format</span>
|
||||
$pdf->getMPDF()->setFooter(<span class="hljs-string">"|© www.mysite.com|"</span>); <span class="hljs-comment">// footer</span>
|
||||
|
||||
<span class="hljs-comment">// do something with $pdf</span>
|
||||
<span class="hljs-keyword">$this</span>->sendResponse($pdf);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="save-file-to-server">Save file to server</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$template = <span class="hljs-keyword">$this</span>->createTemplate();
|
||||
$template->setFile(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
|
||||
$pdf->save(<span class="hljs-keyword">__DIR__</span> . <span
|
||||
class="hljs-string">"/path/to/directory"</span>); <span class="hljs-comment">// as a filename $this->documentTitle will be used</span>
|
||||
$pdf->save(<span class="hljs-keyword">__DIR__</span> . <span
|
||||
class="hljs-string">"/path/to/directory"</span>, <span class="hljs-string">"filename"</span>); <span
|
||||
class="hljs-comment">// OR use a custom name</span>
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="attach-file-to-an-email">Attach file to an email</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$template = <span class="hljs-keyword">$this</span>->createTemplate();
|
||||
$template->setFile(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
|
||||
$savedFile = $pdf->save(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/directory"</span>);
|
||||
$mail = <span class="hljs-keyword">new</span> Nette\Mail\Message;
|
||||
$mail->addTo(<span class="hljs-string">"john@doe.com"</span>);
|
||||
$mail->addAttachment($savedFile);
|
||||
$mailer = <span class="hljs-keyword">new</span> SendmailMailer();
|
||||
$mailer->send($mail);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="force-file-to-download">Force file to download</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$template = <span class="hljs-keyword">$this</span>->createTemplate();
|
||||
$template->setFile(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
$pdf->setSaveMode(PdfResponse::DOWNLOAD); <span class="hljs-comment">//default behavior</span>
|
||||
<span class="hljs-keyword">$this</span>->sendResponse($pdf);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="force-file-to-display-in-a-browser">Force file to display in a browser</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$template = <span class="hljs-keyword">$this</span>->createTemplate();
|
||||
$template->setFile(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
$pdf->setSaveMode(PdfResponse::INLINE);
|
||||
<span class="hljs-keyword">$this</span>->sendResponse($pdf);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="set-a-pdf-background-easily">Set a pdf background easily</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse(<span
|
||||
class="hljs-string">''</span>);
|
||||
$pdf->setBackgroundTemplate(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/an/existing/file.pdf"</span>);
|
||||
|
||||
<span class="hljs-comment">// to write into an existing document use the following statements</span>
|
||||
$mpdf = $pdf->getMPDF();
|
||||
$mpdf->WriteFixedPosHTML(<span class="hljs-string">'hello world'</span>, <span
|
||||
class="hljs-number">1</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>, <span
|
||||
class="hljs-number">10</span>);
|
||||
|
||||
<span class="hljs-comment">// to write to another page</span>
|
||||
$mpdf->AddPage();
|
||||
|
||||
<span class="hljs-comment">// to move to exact page, use</span>
|
||||
$mpdf->page = <span class="hljs-number">3</span>; <span class="hljs-comment">// = move to 3rd page</span>
|
||||
|
||||
<span class="hljs-keyword">$this</span>->sendResponse($pdf);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="create-pdf-with-latte-only">Create pdf with latte only</h3>
|
||||
<pre><code class="lang-php"><span class="hljs-keyword">public</span> <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span> <span class="hljs-title">actionPdf</span><span
|
||||
class="hljs-params">()</span>
|
||||
</span>{
|
||||
$latte = <span class="hljs-keyword">new</span> Latte\Engine;
|
||||
$latte->setTempDirectory(<span class="hljs-string">'/path/to/cache'</span>);
|
||||
$latte->addFilter(<span class="hljs-string">'money'</span>, <span class="hljs-function"><span
|
||||
class="hljs-keyword">function</span><span class="hljs-params">($val)</span> </span>{ <span
|
||||
class="hljs-keyword">return</span> ...; }); <span
|
||||
class="hljs-comment">// formerly registerHelper()</span>
|
||||
|
||||
$latte->onCompile[] = <span class="hljs-function"><span class="hljs-keyword">function</span><span
|
||||
class="hljs-params">($latte)</span> </span>{
|
||||
$latte->addMacro(...); <span class="hljs-comment">// when you want add some own macros, see http://goo.gl/d5A1u2</span>
|
||||
};
|
||||
|
||||
$template = $latte->renderToString(<span class="hljs-keyword">__DIR__</span> . <span class="hljs-string">"/path/to/template.latte"</span>);
|
||||
|
||||
$pdf = <span class="hljs-keyword">new</span> \Contributte\PdfResponse\PdfResponse($template);
|
||||
<span class="hljs-keyword">$this</span>->sendResponse($pdf);
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="configuration-of-custom-temp-dir-for-mpdf-in-pdfresponse">
|
||||
Configuration of custom temp dir for mPDF in PdfResponse
|
||||
</h3>
|
||||
<pre><code class="lang-yml">services:
|
||||
-<span class="ruby"></span> factory: Contributte\PdfResponse\PdfResponse
|
||||
setup:
|
||||
-<span class="ruby"> $mpdfConfig([<span class="hljs-symbol">tempDir:</span> %tempDir<span
|
||||
class="hljs-string">%/mpdf])</span></span>
|
||||
</code></pre>
|
||||
<h2 id="see-also">See also</h2>
|
||||
<ul>
|
||||
<li><a href="https://forum.nette.org/cs/3726-addon-pdfresponse-pdfresponse">Nette forum</a> (czech)</li>
|
||||
<li><a href="https://componette.com/joseki/pdfresponse/">Componette</a></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<p>Thanks for testing, reporting and contributing.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
4
app/resources/tracy/500.json
Normal file
4
app/resources/tracy/500.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"code": 500,
|
||||
"error": "Internal server error"
|
||||
}
|
||||
250
app/resources/tracy/500.phtml
Normal file
250
app/resources/tracy/500.phtml
Normal file
@@ -0,0 +1,250 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Server Error</title>
|
||||
<style>
|
||||
/**/
|
||||
:root {
|
||||
--main-color: #eaeaea;
|
||||
--stroke-color: black;
|
||||
}
|
||||
|
||||
/**/
|
||||
body {
|
||||
background: var(--main-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 100px auto 0 auto;
|
||||
color: var(--stroke-color);
|
||||
font-family: 'Encode Sans Semi Condensed', Verdana, sans-serif;
|
||||
font-size: 10rem;
|
||||
line-height: 10rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 20px auto 30px auto;
|
||||
font-family: 'Encode Sans Semi Condensed', Verdana, sans-serif;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 200;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
-webkit-transition: opacity 0.5s linear, margin-top 0.5s linear; /* Safari */
|
||||
transition: opacity 0.5s linear, margin-top 0.5s linear;
|
||||
}
|
||||
|
||||
.loading h1, .loading h2 {
|
||||
margin-top: 0px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gears {
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: auto;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.gear {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
background: var(--stroke-color);
|
||||
}
|
||||
|
||||
.gear:before {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
z-index: 2;
|
||||
content: "";
|
||||
border-radius: 50%;
|
||||
background: var(--main-color);
|
||||
}
|
||||
|
||||
.gear:after {
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
top: 25px;
|
||||
z-index: 3;
|
||||
content: "";
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
border: 5px solid var(--stroke-color);
|
||||
box-sizing: border-box;
|
||||
background: var(--main-color);
|
||||
}
|
||||
|
||||
.gear.one {
|
||||
left: -130px;
|
||||
}
|
||||
|
||||
.gear.two {
|
||||
top: -75px;
|
||||
}
|
||||
|
||||
.gear.three {
|
||||
top: -235px;
|
||||
left: 130px;
|
||||
}
|
||||
|
||||
.gear .bar {
|
||||
position: absolute;
|
||||
left: -15px;
|
||||
top: 50%;
|
||||
z-index: 0;
|
||||
width: 150px;
|
||||
height: 30px;
|
||||
margin-top: -15px;
|
||||
border-radius: 5px;
|
||||
background: var(--stroke-color);
|
||||
}
|
||||
|
||||
.gear .bar:before {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
bottom: 5px;
|
||||
z-index: 1;
|
||||
content: "";
|
||||
border-radius: 2px;
|
||||
background: var(--main-color);
|
||||
}
|
||||
|
||||
.gear .bar:nth-child(2) {
|
||||
transform: rotate(60deg);
|
||||
-webkit-transform: rotate(60deg);
|
||||
}
|
||||
|
||||
.gear .bar:nth-child(3) {
|
||||
transform: rotate(120deg);
|
||||
-webkit-transform: rotate(120deg);
|
||||
}
|
||||
|
||||
@-webkit-keyframes clockwise {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes anticlockwise {
|
||||
0% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes clockwiseError {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
20% {
|
||||
-webkit-transform: rotate(30deg);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: rotate(25deg);
|
||||
}
|
||||
60% {
|
||||
-webkit-transform: rotate(30deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes anticlockwiseErrorStop {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
20% {
|
||||
-webkit-transform: rotate(-30deg);
|
||||
}
|
||||
60% {
|
||||
-webkit-transform: rotate(-30deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes anticlockwiseError {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
20% {
|
||||
-webkit-transform: rotate(-30deg);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform: rotate(-25deg);
|
||||
}
|
||||
60% {
|
||||
-webkit-transform: rotate(-30deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
.gear.one {
|
||||
-webkit-animation: anticlockwiseErrorStop 2s linear infinite;
|
||||
}
|
||||
|
||||
.gear.two {
|
||||
-webkit-animation: anticlockwiseError 2s linear infinite;
|
||||
}
|
||||
|
||||
.gear.three {
|
||||
-webkit-animation: clockwiseError 2s linear infinite;
|
||||
}
|
||||
|
||||
.loading .gear.one, .loading .gear.three {
|
||||
-webkit-animation: clockwise 3s linear infinite;
|
||||
}
|
||||
|
||||
.loading .gear.two {
|
||||
-webkit-animation: anticlockwise 3s linear infinite;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>500</h1>
|
||||
<h2>Unexpected Error <b>:(</b></h2>
|
||||
<div class="gears">
|
||||
<div class="gear one">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
<div class="gear two">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
<div class="gear three">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1
app/resources/tracy/500.txt
Normal file
1
app/resources/tracy/500.txt
Normal file
@@ -0,0 +1 @@
|
||||
Internal server error
|
||||
16
app/ui/Control/BaseControl.php
Normal file
16
app/ui/Control/BaseControl.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Control;
|
||||
|
||||
use App\Model\Latte\TemplateProperty;
|
||||
use App\Modules\Base\BasePresenter;
|
||||
use Nette\Application\UI\Control;
|
||||
|
||||
/**
|
||||
* @property-read TemplateProperty $template
|
||||
* @property-read BasePresenter $presenter
|
||||
*/
|
||||
abstract class BaseControl extends Control
|
||||
{
|
||||
|
||||
}
|
||||
49
app/ui/Control/TFlashMessage.php
Normal file
49
app/ui/Control/TFlashMessage.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Control;
|
||||
|
||||
use App\Modules\Base\BasePresenter;
|
||||
use stdClass;
|
||||
|
||||
/**
|
||||
* @mixin BasePresenter
|
||||
*/
|
||||
trait TFlashMessage
|
||||
{
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @param string $message
|
||||
* @param string $type
|
||||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint
|
||||
*/
|
||||
public function flashMessage($message, $type = 'info'): stdClass
|
||||
{
|
||||
if ($this->isAjax()) {
|
||||
$this->redrawControl('flashes');
|
||||
}
|
||||
|
||||
return parent::flashMessage($message, $type);
|
||||
}
|
||||
|
||||
public function flashInfo(string $message): stdClass
|
||||
{
|
||||
return $this->flashMessage($message, 'info');
|
||||
}
|
||||
|
||||
public function flashSuccess(string $message): stdClass
|
||||
{
|
||||
return $this->flashMessage($message, 'success');
|
||||
}
|
||||
|
||||
public function flashWarning(string $message): stdClass
|
||||
{
|
||||
return $this->flashMessage($message, 'warning');
|
||||
}
|
||||
|
||||
public function flashError(string $message): stdClass
|
||||
{
|
||||
return $this->flashMessage($message, 'danger');
|
||||
}
|
||||
|
||||
}
|
||||
63
app/ui/Control/TModuleUtils.php
Normal file
63
app/ui/Control/TModuleUtils.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Control;
|
||||
|
||||
use App\Model\Exception\Runtime\InvalidStateException;
|
||||
use App\Modules\Base\BasePresenter;
|
||||
|
||||
/**
|
||||
* @mixin BasePresenter
|
||||
*/
|
||||
trait TModuleUtils
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets module name
|
||||
*/
|
||||
public function getModuleName(): string
|
||||
{
|
||||
$name = $this->getName();
|
||||
|
||||
// Validate presenter has a proper name
|
||||
if ($name === null) {
|
||||
throw new InvalidStateException('Presenter doesn\'t have a name');
|
||||
}
|
||||
|
||||
$parts = explode(':', $name);
|
||||
|
||||
return current($parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is current module active?
|
||||
*
|
||||
* @param string $module Module name
|
||||
*/
|
||||
public function isModuleCurrent(string $module): bool
|
||||
{
|
||||
return strpos($this->getAction(true), $module) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets template dir
|
||||
*/
|
||||
public function getTemplateDir(): string
|
||||
{
|
||||
$fileName = $this->getReflection()->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');
|
||||
}
|
||||
|
||||
$realpath = realpath(dirname($fileName) . '/../templates');
|
||||
|
||||
// Validate if file exists
|
||||
if ($realpath === false) {
|
||||
throw new InvalidStateException('File does not exist');
|
||||
}
|
||||
|
||||
return $realpath;
|
||||
}
|
||||
|
||||
}
|
||||
19
app/ui/Control/TTranslate.php
Normal file
19
app/ui/Control/TTranslate.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Control;
|
||||
|
||||
use App\Modules\Base\BasePresenter;
|
||||
use Nette\Localization\ITranslator;
|
||||
|
||||
/**
|
||||
* @mixin BasePresenter
|
||||
*/
|
||||
trait TTranslate
|
||||
{
|
||||
|
||||
protected function _(string $message): string // phpcs:ignore
|
||||
{
|
||||
return $this->getContext()->getByType(ITranslator::class)->translate($message);
|
||||
}
|
||||
|
||||
}
|
||||
31
app/ui/Form/BaseForm.php
Normal file
31
app/ui/Form/BaseForm.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Form;
|
||||
|
||||
use Nette\Application\UI\Form;
|
||||
use Nette\Forms\Controls\TextInput;
|
||||
|
||||
class BaseForm extends Form
|
||||
{
|
||||
|
||||
public function addFloat(string $name, ?string $label = null): TextInput
|
||||
{
|
||||
$input = self::addText($name, $label);
|
||||
$input->addCondition(self::FILLED)
|
||||
->addRule(self::MAX_LENGTH, null, 255)
|
||||
->addRule(self::FLOAT);
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
public function addNumeric(string $name, ?string $label = null): TextInput
|
||||
{
|
||||
$input = self::addText($name, $label);
|
||||
$input->addCondition(self::FILLED)
|
||||
->addRule(self::MAX_LENGTH, null, 255)
|
||||
->addRule(self::NUMERIC);
|
||||
|
||||
return $input;
|
||||
}
|
||||
|
||||
}
|
||||
23
app/ui/Form/FormFactory.php
Normal file
23
app/ui/Form/FormFactory.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace App\UI\Form;
|
||||
|
||||
final class FormFactory
|
||||
{
|
||||
|
||||
private function create(): BaseForm
|
||||
{
|
||||
return new BaseForm();
|
||||
}
|
||||
|
||||
public function forFrontend(): BaseForm
|
||||
{
|
||||
return $this->create();
|
||||
}
|
||||
|
||||
public function forBackend(): BaseForm
|
||||
{
|
||||
return $this->create();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user