Initial commit: apps.prometeysev.ru

This commit is contained in:
root committed 2026-10-22 00:22:57 +03:00
commit 96ccabcd8b
6561 files changed
+1270349

No files matched your search

+42
View File
@@ -0,0 +1,42 @@
# Contributing
Pull requests are highly appreciated. Here's a quick guide.
Fork, then clone the repo:
git clone git@github.com:your-username/TwigView.git
Set up your machine:
composer install
Make sure the tests pass:
make unit
Make sure the tests pass on all supported PHP versions (requires docker):
make dunit
Make your change. Add tests for your change. Make the tests pass:
make dunit && make unit
Before committing and submitting your pull request make sure it passes PSR2 coding style, unit tests pass and pass on all supported PHP versions:
make contrib
Push to your fork and [submit a pull request][pr].
[pr]: https://help.github.com/articles/creating-a-pull-request/
At this point you're waiting on me. I like to at least comment on pull requests
within a day or two. I may suggest some changes or improvements or alternatives.
Some things that will increase the chance that your pull request is accepted:
* Write tests.
* Follow PSR2 (travis will also check for this).
* Write a [good commit message][commit].
[commit]: http://chris.beams.io/posts/git-commit/
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Cees-Jan Kiewiet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+429
View File
@@ -0,0 +1,429 @@
# TwigView plugin for CakePHP #
[![Build Status](https://travis-ci.org/WyriHaximus/TwigView.png)](https://travis-ci.org/WyriHaximus/TwigView)
[![Latest Stable Version](https://poser.pugx.org/WyriHaximus/Twig-View/v/stable.png)](https://packagist.org/packages/WyriHaximus/Twig-View)
[![Total Downloads](https://poser.pugx.org/WyriHaximus/Twig-View/downloads.png)](https://packagist.org/packages/WyriHaximus/Twig-View/stats)
[![Code Coverage](https://scrutinizer-ci.com/g/WyriHaximus/TwigView/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/WyriHaximus/TwigView/?branch=master)
[![License](https://poser.pugx.org/wyrihaximus/Twig-View/license.png)](https://packagist.org/packages/wyrihaximus/Twig-View)
[![PHP 7 ready](http://php7ready.timesplinter.ch/WyriHaximus/TwigView/badge.svg)](https://travis-ci.org/WyriHaximus/TwigView)
This plugin for version 3 the [CakePHP Framework](http://cakephp.org) allows you to use the [Twig Templating Language](http://twig.sensiolabs.org) for your views.
In addition to enabling the use of most of Twig's features, the plugin is tightly integrated with the CakePHP view renderer giving you full access to helpers, objects and elements.
## Installation ##
To install via [Composer](http://getcomposer.org/), use the command below, it will automatically detect the latest version and bind it with `~`.
```bash
composer require wyrihaximus/twig-view
```
## Configuration ##
### Load Plugin
Add the following to your `config/bootstrap.php` to load the plugin.
```php
Plugin::load('WyriHaximus/TwigView', [
'bootstrap' => true,
]);
```
### Use View class
Instead of extending from the `View` let `AppView` extend `TwigView`:
```php
namespace App\View;
use WyriHaximus\TwigView\View\TwigView;
class AppView extends TwigView
{
}
```
## Quick Start
### Load Helpers
In `Controller/AppController` load your helpers:
```PHP
public $helpers = ['Html', 'Form']; // And more
```
Or in your AppView file :
```PHP
public function initialize()
{
parent::initialize();
$this->loadHelper('Html');
$this->loadHelper('Form');
}
```
Note: TwigView will look for its templates with the extension `.twig` and then for `.tpl` (deprecated).
### Layout
Replace `Template/Layout/default.ctp` by this `Layout/default.twig`
``` twig
<!DOCTYPE html>
<html>
<head>
{{ Html.charset()|raw }}
<title>
{{ __('myTwigExample') }}
{{ _view.fetch('title')|raw }}
</title>
{{ Html.meta('icon')|raw }}
{{ Html.css('default.app.css')|raw }}
{{ Html.script('app')|raw }}
{{ _view.fetch('meta')|raw }}
{{ _view.fetch('css')|raw }}
{{ _view.fetch('script')|raw }}
</head>
<body>
<header>
{{ _view.fetch('header')|raw }}
</header>
{{ Flash.render()|raw }}
<section>
<h1>{{ _view.fetch('title')|raw }}</h1>
{{ _view.fetch('content')|raw }}
</section>
<footer>
{{ _view.fetch('footer')|raw }}
</footer>
</body>
</html>
```
### Template View
Create a template, for exemple `Template/Users/index.twig` like this
```Twig
{{ _view.assign('title', __("I'm title")) }}
{{ _view.start('header') }}
<p>I'm header</p>
{{ _view.end() }}
{{ _view.start('footer') }}
<p>I'm footer</p>
{{ _view.end() }}
<p>I'm content</p>
```
## Usage
### Use `$this`
With twig `$this` is replaced by `_view`
For example, without using Twig writing
```php
<?= $this->fetch('content') ?>
```
But with Twig
```twig
{{ _view.fetch('content')|raw }}
```
### Helpers
Any helper you defined in your controller.
```php
public $helpers = ['Html', 'Form']; // ...
```
Can be access by their CamelCase name, for example creating a form using the `FormHelper`:
```twig
{{ Html.link('Edit user', {'controller':'Users', 'action': 'edit' ~ '/' ~ user.id}, {'class':'myclass'})|raw }}
```
### Elements
Basic
```Twig
{% element 'Element' %}
```
With variables or options
```Twig
{% element 'Plugin.Element' {
dataName: 'dataValue'
} {
optionName: 'optionValue'
} %}
```
### Cells
Store in context then echo it
```twig
{% cell cellObject = 'Plugin.Cell' {
dataName: 'dataValue'
} {
optionName: 'optionValue'
} %}
{{ cellObject|raw }}
```
Fetch and directly echo it
```twig
{% cell 'Plugin.Cell' {
dataName: 'dataValue'
} {
optionName: 'optionValue'
} %}
```
### Extends
If i want extend to `Common/demo.twig`
```twig
<div id="sidebar">
{% block sidebar %}{% endblock %}
</div>
<div id="content">
{% block body %}{% endblock %}
</div>
```
We can write in a view
```twig
{% extends 'Common/demo' %}
{% block sidebar %}
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
{% endblock %}
{% block body %}
{{ _view.assign('title', __("I'm title")) }}
{{ _view.start('header') }}
<p>I'm header</p>
{{ _view.end() }}
{{ _view.start('footer') }}
<p>I'm footer</p>
{{ _view.end() }}
<p>I'm content</p>
{% endblock %}
```
**Note : the block `body` is required, it's equivalent to `<?= $this->fetch('content') ?>`**
### Filters
* `debug` maps to [`debug`](https://book.cakephp.org/3.0/en/development/debugging.html#basic-debugging)
* `pr` maps to `pr`
* `low` maps to [`low`](http://php.net/low)
* `up` maps to [`up`](http://php.net/up)
* `env` maps to [`env`](http://php.net/env)
* `count` maps to [`count`](http://php.net/count)
* `pluralize` maps to [`Cake\Utility\Inflector::pluralize`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::pluralize)
* `singularize` maps to [`Cake\Utility\Inflector::singularize`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::singularize)
* `camelize` maps to [`Cake\Utility\Inflector::camelize`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::camelize)
* `underscore` maps to [`Cake\Utility\Inflector::underscore`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::underscore)
* `humanize` maps to [`Cake\Utility\Inflector::humanize`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::humanize)
* `tableize` maps to [`Cake\Utility\Inflector::tableize`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::tableize)
* `classify` maps to [`Cake\Utility\Inflector::classify`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::classify)
* `variable` maps to [`Cake\Utility\Inflector::variable`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::variable)
* `slug` maps to [`Cake\Utility\Inflector::slug`](https://book.cakephp.org/3.0/en/core-libraries/inflector.html#Cake\\Utility\\Inflector::slug)
* `toReadableSize` maps to [`Cake\I18n\Number::toReadableSize`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::toReadableSize)
* `fromReadableSize` maps to [`Cake\I18n\Number::fromReadableSize`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::fromReadableSize)
* `toPercentage` maps to [`Cake\I18n\Number::toPercentage`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::toPercentage)
* `number_format` maps to [`Cake\I18n\Number::format`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::format)
* `formatDelta` maps to [`Cake\I18n\Number::formatDelta`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::formatDelta)
* `currency` maps to [`Cake\I18n\Number::currency`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::currency)
* `substr` maps to [`substr`](http://php.net/substr)
* `tokenize` maps to [`Cake\Utility\Text::tokenize`](https://book.cakephp.org/3.0/en/core-libraries/text.html#simple-string-parsing)
* `insert` maps to [`Cake\Utility\Text::insert`](https://book.cakephp.org/3.0/en/core-libraries/text.html#formatting-strings)
* `cleanInsert` maps to [`Cake\Utility\Text::cleanInsert`](https://book.cakephp.org/3.0/en/core-libraries/text.html#formatting-strings)
* `wrap` maps to [`Cake\Utility\Text::wrap`](https://book.cakephp.org/3.0/en/core-libraries/text.html#wrapping-text)
* `wrapBlock` maps to [`Cake\Utility\Text::wrapBlock`](https://book.cakephp.org/3.0/en/core-libraries/text.html#wrapping-text)
* `wordWrap` maps to [`Cake\Utility\Text::wordWrap`](https://book.cakephp.org/3.0/en/core-libraries/text.html#wrapping-text)
* `highlight` maps to [`Cake\Utility\Text::highlight`](https://book.cakephp.org/3.0/en/core-libraries/text.html#highlighting-substrings)
* `tail` maps to [`Cake\Utility\Text::tail`](https://book.cakephp.org/3.0/en/core-libraries/text.html#truncating-the-tail-of-a-string)
* `truncate` maps to [`Cake\Utility\Text::truncate`](https://book.cakephp.org/3.0/en/core-libraries/text.html#truncating-text)
* `excerpt` maps to [`Cake\Utility\Text::excerpt`](https://book.cakephp.org/3.0/en/core-libraries/text.html#extracting-an-excerpt)
* `toList` maps to [`Cake\Utility\Text::toList`](https://book.cakephp.org/3.0/en/core-libraries/text.html#converting-an-array-to-sentence-)
* `stripLinks` maps to [`Cake\Utility\Text::stripLinks`](https://book.cakephp.org/3.0/en/core-libraries/text.html#removing-links)
* `isMultibyte` maps to `Cake\Utility\Text::isMultibyte`
* `utf8` maps to `Cake\Utility\Text::utf8`
* `ascii` maps to `Cake\Utility\Text::ascii`
* `parseFileSize` maps to [`Cake\Utility\Text::parseFileSize`](https://book.cakephp.org/3.0/en/core-libraries/text.html#simple-string-parsing)
* `serialize` maps to [`serialize`](http://php.net/serialize)
* `unserialize` maps to [`unserialize`](http://php.net/unserialize)
* `md5` maps to [`md5`](http://php.net/md5)
* `base64_encode` maps to [`base64_encode`](http://php.net/base64_encode)
* `base64_decode` maps to [`base64_decode`](http://php.net/base64_decode)
* `nl2br` maps to [`nl2br`](http://php.net/nl2br)
* `string` cast to [`string`](http://php.net/manual/en/language.types.type-juggling.php)
### Functions
* `in_array` maps to [`in_array`](http://php.net/in_array)
* `explode` maps to [`explode`](http://php.net/explode)
* `array` cast to [`array`](http://php.net/manual/en/language.types.type-juggling.php)
* `array_push` maps to [`push`](http://php.net/push)
* `array_add` maps to [`add`](http://php.net/add)
* `array_prev` maps to [`prev`](http://php.net/prev)
* `array_next` maps to [`next`](http://php.net/next)
* `array_current` maps to [`current`](http://php.net/current)
* `array_each` maps to [`each`](http://php.net/each)
* `__` maps to [`__`](https://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html)
* `__d` maps to [`__d`](https://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html)
* `__n` maps to [`__n`](https://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html)
* `__x` maps to [`__x`](https://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html)
* `__dn` maps to [`__dn`](https://book.cakephp.org/3.0/en/core-libraries/internationalization-and-localization.html)
* `defaultCurrency` maps to [`Cake\I18n\Number::defaultCurrency`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::defaultCurrency)
* `number_formatter` maps to [`Cake\I18n\Number::formatter`](https://book.cakephp.org/3.0/en/core-libraries/number.html#Cake\\I18n\\Number::formatter)
* `uuid` maps to [`Cake\Utility\Text::uuid`](https://book.cakephp.org/3.0/en/core-libraries/text.html#generating-uuids)
* `time` passed the first and optional second argument into [`new \Cake\I18n\Time()`](https://book.cakephp.org/3.0/en/core-libraries/time.html#creating-time-instances)
* `timezones` maps to `Cake\I18n\Time::listTimezones`
* `elementExists` maps to `Cake\View\View::elementExists`,
* `getVars` maps to `Cake\View\View::getVars`
* `get` maps to `Cake\View\View::get`
### Twig
Visite [Twig Documentaion](http://twig.sensiolabs.org/documentation) for more tips
### Extra included extensions
* [Breakpoint extension](https://github.com/ajgarlag/AjglBreakpointTwigExtension)
* [aptoma/twig-markdown](https://github.com/aptoma/twig-markdown)
* [jasny/twig-extensions](https://github.com/jasny/twig-extensions)
* [umpirsky/twig-php-function](https://github.com/umpirsky/twig-php-function)
## Events ##
This plugin emits several events.
### Loaders ###
The default loader can be replace by listening to the `WyriHaximus\TwigView\Event\LoaderEvent::EVENT`, for example with [twital](https://github.com/goetas/twital):
```php
<?php
use Cake\Event\EventListenerInterface;
use Goetas\Twital\TwitalLoader;
use WyriHaximus\TwigView\Event\ConstructEvent;
use WyriHaximus\TwigView\Event\LoaderEvent;
class LoaderListener implements EventListenerInterface
{
public function implementedEvents()
{
return [
LoaderEvent::EVENT => 'loader',
ConstructEvent::EVENT => 'construct',
];
}
public function loader(LoaderEvent $event)
{
$event->result = new TwitalLoader($event->getLoader());
}
/**
* We've also listening in on this event so we can add the needed extensions to check for to the view
*/
public function construct(ConstructEvent $event)
{
$event->getTwigView()->unshiftExtension('.twital.html');
$event->getTwigView()->unshiftExtension('.twital.xml');
$event->getTwigView()->unshiftExtension('.twital.xhtml');
}
}
```
### Extensions ###
Extensions can be added to the twig environment by listening to the `WyriHaximus\TwigView\Event\ConstructEvent::EVENT`, for example:
```php
<?php
use Cake\Event\EventListenerInterface;
use WyriHaximus\TwigView\Event\ConstructEvent;
class LoaderListener implements EventListenerInterface
{
public function implementedEvents()
{
return [
ConstructEvent::EVENT => 'construct',
];
}
public function construct(ConstructEvent $event)
{
$event->getTwig()->addExtension(new YourTwigExtension);
}
}
```
## Bake
You can use Bake to generate your basic CRUD views using the `theme` option.
Let's say you have a `TasksController` for which you want to generate twig templates.
You can use the following command to generate your index, add, edit and view file formatted
using Twig :
```bash
bin/cake bake twig_template Tasks all -t WyriHaximus/TwigView
```
## Screenshots ##
### Profiler ###
![Profiler](/docs/images/profiler.png)
### Templates found ###
![Templates found](/docs/images/templates-found.png)
## License ##
Copyright 2015 [Cees-Jan Kiewiet](http://wyrihaximus.net/)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
+55
View File
@@ -0,0 +1,55 @@
{
"name": "wyrihaximus/twig-view",
"description": "Twig powered View for CakePHP3",
"type":"cakephp-plugin",
"license": "MIT",
"keywords": [
"twig",
"view",
"cakephp",
"cakephp3"
],
"authors": [
{
"name": "Cees-Jan Kiewiet",
"email": "ceesjank@gmail.com",
"homepage": "http://wyrihaximus.net/"
}
],
"require": {
"php": "^5.6 || ^7.0",
"ajgl/breakpoint-twig-extension": "^0.3.0",
"aptoma/twig-markdown": "^2.0",
"asm89/twig-cache-extension": "^1.0",
"cakephp/cakephp": "^3.6",
"jasny/twig-extensions": "^1.0",
"twig/twig": "^1.27",
"umpirsky/twig-php-function": "0.1"
},
"require-dev": {
"cakephp/bake": "^1.5",
"cakephp/debug_kit": "^3.0",
"phake/phake": "^1.0.4",
"phpunit/phpunit": "^5.7.14",
"squizlabs/php_codesniffer": "^1.5.6",
"wyrihaximus/phpunit-class-reflection-helpers": "dev-master"
},
"autoload": {
"psr-4": {
"WyriHaximus\\TwigView\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"WyriHaximus\\CakePHP\\Tests\\TwigView\\": "tests/",
"App\\": "tests/test_app/",
"Cake\\Test\\": "./vendor/cakephp/cakephp/tests"
}
},
"config": {
"sort-packages": true,
"platform": {
"php": "5.6.0"
}
}
}
+2179
View File
File diff suppressed because it is too large. Load diff
+19
View File
@@ -0,0 +1,19 @@
<?php
use Cake\Core\Configure;
use Cake\Core\Plugin;
use Cake\Event\EventManager;
use WyriHaximus\TwigView\Event;
EventManager::instance()->on(new Event\ExtensionsListener());
EventManager::instance()->on(new Event\TokenParsersListener());
if (Configure::read('debug') && Plugin::getCollection()->has('DebugKit')) {
Configure::write('DebugKit.panels', array_merge(
(array)Configure::read('DebugKit.panels'),
[
'WyriHaximus/TwigView.Twig',
]
));
EventManager::instance()->on(new Event\ProfilerListener());
}
+17
View File
@@ -0,0 +1,17 @@
<?php
return [
'WyriHaximus' => [
'TwigView' => [
'environment' => [
// Anything you would pass into \Twig_Environment to overwrite the default settings, see: http://twig.sensiolabs.org/doc/api.html#environment-options
],
'markdown' => [
'engine' => 'engine', // See https://github.com/aptoma/twig-markdown#installation
],
'flags' => [
'potentialDangerous' => false,
],
],
],
];
+78
View File
@@ -0,0 +1,78 @@
## Change AppView
```PHP
// src/View/AppView.php
namespace App\View;
use WyriHaximus\TwigView\View\TwigView;
class AppView extends TwigView
{
// Code ...
}
```
## Load Helpers
```PHP
// src/View/AppView.php
namespace App\View;
use WyriHaximus\TwigView\View\TwigView;
class AppView extends TwigView
{
public function initialize()
{
$this->loadHelper('Html');
$this->loadHelper('Form');
}
```
Note: TwigView will look for its templates with the extension `.twig` and then for `.tpl` (deprecated).
## Create the default layout to be used by TwigView named `default.twig` instead of `default.ctp`
Layout example
```Twig
<!DOCTYPE html>
<html>
<head>
{{ Html.charset()|raw }}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{{ __('MySite') }}
{{ _view.fetch('title')|raw }}
</title>
{{ Html.meta('icon')|raw }}
{{ Html.css('app.css')|raw }}
{{ Html.script('app')|raw }}
{{ _view.fetch('meta')|raw }}
{{ _view.fetch('css')|raw }}
{{ _view.fetch('script')|raw }}
</head>
<body>
<header>
{{ _view.fetch('header')|raw }}
</header>
<section>
<h1 class="page-title">{{ _view.fetch('title') }}</h1>
{{ _view.fetch('content')|raw }}
</section>
</body>
```
## Create a view template
in Template/Controller/action.twig
```Twig
{{ _view.start('header') }}
<p>It's my header</p>
{{ _view.end() }}
{{ _view.assign('title', "it's my title") }}
<p>It's my content</p>
```
@@ -0,0 +1,48 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\Event;
use WyriHaximus\TwigView\View\TwigView;
class ConstructEvent extends Event
{
const EVENT = 'TwigView.TwigView.construct';
/**
* @param TwigView $twigView
* @param \Twig_Environment $twig
* @return static
*/
public static function create(TwigView $twigView, \Twig_Environment $twig)
{
return new static(static::EVENT, $twigView, [
'twigView' => $twigView,
'twig' => $twig,
]);
}
/**
* @return TwigView
*/
public function getTwigView()
{
return $this->getData()['twigView'];
}
/**
* @return \Twig_Environment
*/
public function getTwig()
{
return $this->getData()['twig'];
}
}
@@ -0,0 +1,51 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\Event;
class EnvironmentConfigEvent extends Event
{
const EVENT = 'TwigView.TwigView.environment';
/**
* @var array
*/
private $config = [];
/**
* @param array $config
*
* @return static
*/
public static function create(array $config)
{
$event = new static(static::EVENT);
$event->setConfig($config);
return $event;
}
/**
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* @param array $config
*/
public function setConfig(array $config)
{
$this->config = array_replace_recursive($this->config, $config);
}
}
@@ -0,0 +1,116 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
use Aptoma\Twig\Extension\MarkdownExtension;
use Aptoma\Twig\TokenParser\MarkdownTokenParser;
use Ajgl\Twig\Extension\BreakpointExtension;
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
use Cake\Core\Configure;
use Cake\Event\EventListenerInterface;
use Jasny\Twig\ArrayExtension;
use Jasny\Twig\DateExtension;
use Jasny\Twig\PcreExtension;
use Jasny\Twig\TextExtension;
use Umpirsky\Twig\Extension\PhpFunctionExtension;
use WyriHaximus\TwigView\Lib\Cache;
use WyriHaximus\TwigView\Lib\Twig\Extension;
use WyriHaximus\TwigView\Lib\Twig\TokenParser;
/**
* Class ExtensionsListener
* @package WyriHaximus\TwigView\Event
*/
class ExtensionsListener implements EventListenerInterface
{
/**
* Return implemented events.
*
* @return array
*/
public function implementedEvents()
{
return [
ConstructEvent::EVENT => 'construct',
];
}
/**
* Event handler.
*
* @param ConstructEvent $event Event.
*
* @return void
*/
// @codingStandardsIgnoreStart
public function construct(ConstructEvent $event)
{
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreStart
// Twig core extensions
$event->getTwig()->addExtension(new \Twig_Extension_StringLoader);
$event->getTwig()->addExtension(new \Twig_Extension_Debug);
// CakePHP bridging extensions
$event->getTwig()->addExtension(new Extension\I18n);
$event->getTwig()->addExtension(new Extension\Time);
$event->getTwig()->addExtension(new Extension\Basic);
$event->getTwig()->addExtension(new Extension\Number);
$event->getTwig()->addExtension(new Extension\Utils);
$event->getTwig()->addExtension(new Extension\Arrays);
$event->getTwig()->addExtension(new Extension\Strings);
$event->getTwig()->addExtension(new Extension\Inflector);
if (
!Configure::check('WyriHaximus.TwigView.flags.potentialDangerous') ||
(
Configure::check('WyriHaximus.TwigView.flags.potentialDangerous') &&
Configure::read('WyriHaximus.TwigView.flags.potentialDangerous') === true
)
) {
$event->getTwig()->addExtension(new Extension\PotentialDangerous);
}
// Markdown extension
if (
Configure::check('WyriHaximus.TwigView.markdown.engine') &&
Configure::read('WyriHaximus.TwigView.markdown.engine') instanceof MarkdownEngineInterface
) {
$engine = Configure::read('WyriHaximus.TwigView.markdown.engine');
$event->getTwig()->addExtension(new MarkdownExtension($engine));
$event->getTwig()->addTokenParser(new MarkdownTokenParser($engine));
}
// Third party cache extension
$cacheProvider = new Cache();
$cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
$cacheExtension = new CacheExtension($cacheStrategy);
$event->getTwig()->addExtension($cacheExtension);
// jasny/twig-extensions
$event->getTwig()->addExtension(new DateExtension());
$event->getTwig()->addExtension(new PcreExtension());
$event->getTwig()->addExtension(new TextExtension());
$event->getTwig()->addExtension(new ArrayExtension());
// umpirsky/twig-php-function
$event->getTwig()->addExtension(new PhpFunctionExtension());
// Breakpoint extension
if (Configure::read('debug') === true) {
$event->getTwig()->addExtension(new BreakpointExtension());
}
// @codingStandardsIgnoreEnd
}
}
+53
View File
@@ -0,0 +1,53 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\Event;
class LoaderEvent extends Event
{
const EVENT = 'TwigView.TwigView.loader';
/**
* @param \Twig_LoaderInterface $loader
* @return LoaderEvent
*/
public static function create(\Twig_LoaderInterface $loader)
{
return new static(static::EVENT, $loader, [
'loader' => $loader,
]);
}
/**
* @return \Twig_LoaderInterface
*/
public function getLoader()
{
return $this->getSubject();
}
/**
* @return string|Twig_LoaderInterface
*/
public function getResultLoader()
{
if ($this->result instanceof \Twig_LoaderInterface) {
return $this->result;
}
if (is_array($this->result) && $this->result['loader'] instanceof \Twig_LoaderInterface) {
return $this->result['loader'];
}
return $this->getLoader();
}
}
+35
View File
@@ -0,0 +1,35 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\Event;
class ProfileEvent extends Event
{
const EVENT = 'TwigView.TwigView.profile';
/**
* @param \Twig_Profiler_Profile $profile
* @return static
*/
public static function create(\Twig_Profiler_Profile $profile)
{
return new static(static::EVENT, $profile);
}
/**
* @return \Twig_Profiler_Profile
*/
public function getLoader()
{
return $this->getSubject();
}
}
@@ -0,0 +1,53 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\EventListenerInterface;
use Cake\Event\EventManager;
use WyriHaximus\TwigView\Lib\Cache;
use WyriHaximus\TwigView\Lib\Twig\Extension;
use WyriHaximus\TwigView\Lib\Twig\TokenParser;
/**
* Class ExtensionsListener
* @package WyriHaximus\TwigView\Event
*/
class ProfilerListener implements EventListenerInterface
{
/**
* Return implemented events.
*
* @return array
*/
public function implementedEvents()
{
return [
ConstructEvent::EVENT => 'construct',
];
}
/**
* Event handler.
*
* @param ConstructEvent $event Event.
*
* @return void
*/
public function construct(ConstructEvent $event)
{
$profile = new \Twig_Profiler_Profile();
$event->
getTwig()->
addExtension(new Extension\Profiler($profile));
EventManager::instance()->dispatch(ProfileEvent::create($profile));
}
}
@@ -0,0 +1,53 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Event;
use Cake\Event\EventListenerInterface;
use WyriHaximus\TwigView\Lib\Cache;
use WyriHaximus\TwigView\Lib\Twig\Extension;
use WyriHaximus\TwigView\Lib\Twig\TokenParser;
/**
* Class TokenParsersListener
* @package WyriHaximus\TwigView\Event
*/
class TokenParsersListener implements EventListenerInterface
{
/**
* Return implemented events.
*
* @return array
*/
public function implementedEvents()
{
return [
ConstructEvent::EVENT => 'construct',
];
}
/**
* Event handler.
*
* @param ConstructEvent $event Event.
*
* @return void
*/
// @codingStandardsIgnoreStart
public function construct(ConstructEvent $event)
{
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreStart
// CakePHP specific tags
$event->getTwig()->addTokenParser(new TokenParser\Cell);
$event->getTwig()->addTokenParser(new TokenParser\Element);
// @codingStandardsIgnoreEnd
}
}
+59
View File
@@ -0,0 +1,59 @@
<?php
namespace WyriHaximus\TwigView\Lib;
use Asm89\Twig\CacheExtension\CacheProviderInterface;
use Cake\Cache\Cache as CakeCache;
class Cache implements CacheProviderInterface
{
const CACHE_PREFIX = 'twig-view-in-template-item-';
/**
* Retrieve data from the cache.
*
* @param string $identifier Identifier for this bit of data to read.
*
* @return mixed
*/
public function fetch($identifier)
{
list($config, $key) = $this->configSplit($identifier);
return CakeCache::read(static::CACHE_PREFIX . $key, $config);
}
/**
* Extract $configName and $key from $name and $config.
*
* @param string $name Name.
* @param string $config Cache configuration name to used.
*
* @return array
*/
protected function configSplit($name, $config = 'default')
{
if (strpos($name, ':') !== false) {
$parts = explode(':', $name, 2);
return $parts;
}
return [$config, $name];
}
/**
* Save data to the cache.
*
* @param string $identifier Identifier for this bit of data to write.
* @param string $data Data to cache.
* @param integer $lifeTime Time to life inside the cache.
*
* @return boolean
*/
public function save($identifier, $data, $lifeTime = 0)
{
list($config, $key) = $this->configSplit($identifier);
return CakeCache::write(static::CACHE_PREFIX . $key, $data, $config);
}
}
@@ -0,0 +1,72 @@
<?php
namespace WyriHaximus\TwigView\Lib;
use Cake\Core\App;
/**
* Class RelativeScanner
* @package WyriHaximus\TwigView\Lib
*/
class RelativeScanner
{
/**
* Return all sections (app & plugins) with an Template directory.
*
* @return array
*/
public static function all()
{
return static::strip(Scanner::all());
}
/**
* Return all templates for a given plugin.
*
* @param string $plugin The plugin to find all templates for.
*
* @return mixed
*/
public static function plugin($plugin)
{
return static::strip([
$plugin => Scanner::plugin($plugin),
])[$plugin];
}
/**
* Strip the absolute path of template's paths for all given sections.
*
* @param string $sections Sections to iterate over.
*
* @return array
*/
protected static function strip($sections)
{
foreach ($sections as $section => $paths) {
$sections[$section] = static::stripAbsolutePath($paths, $section == 'APP' ? null : $section);
}
return $sections;
}
/**
* Strip the absolute path of template's paths.
*
* @param array $paths Paths to strip.
* @param string|null $plugin Hold plugin name or null for App.
*
* @return array
*/
protected static function stripAbsolutePath(array $paths, $plugin = null)
{
foreach (App::path('Template', $plugin) as $templatesPath) {
array_walk($paths, function (&$path) use ($templatesPath) {
if (substr($path, 0, strlen($templatesPath)) == $templatesPath) {
$path = substr($path, strlen($templatesPath));
}
});
}
return $paths;
}
}
+166
View File
@@ -0,0 +1,166 @@
<?php
namespace WyriHaximus\TwigView\Lib;
use Cake\Core\App;
use Cake\Core\Plugin;
use WyriHaximus\TwigView\View\TwigView;
/**
* Class Scanner
* @package WyriHaximus\TwigView\Lib
*/
class Scanner
{
/**
* Return all sections (app & plugins) with an Template directory.
*
* @return array
*/
// @codingStandardsIgnoreStart
public static function all()
{
// @codingStandardsIgnoreEnd
$sections = [];
foreach (App::path('Template') as $path) {
if (is_dir($path)) {
$sections['APP'] = isset($sections['APP']) ? $sections['APP'] : [];
$sections['APP'] = array_merge($sections['APP'], static::iteratePath($path));
}
}
foreach (static::pluginsWithTemplates() as $plugin) {
foreach (App::path('Template', $plugin) as $path) {
if (is_dir($path)) {
$sections[$plugin] = isset($sections[$plugin]) ? $sections[$plugin] : [];
$sections[$plugin] = array_merge($sections[$plugin], static::iteratePath($path));
}
}
}
return static::clearEmptySections($sections);
}
/**
* Check sections a remove the ones without anything in them.
*
* @param array $sections Sections to check.
*
* @return array
*/
protected static function clearEmptySections(array $sections)
{
array_walk($sections, function ($templates, $index) use (&$sections) {
if (count($templates) == 0) {
unset($sections[$index]);
}
});
return $sections;
}
/**
* Finds all plugins with a Template directory.
*
* @return array
*/
protected static function pluginsWithTemplates()
{
$plugins = Plugin::loaded();
array_walk($plugins, function ($plugin, $index) use (&$plugins) {
$paths = App::path('Template', $plugin);
array_walk($paths, function ($path, $index) use (&$paths) {
if (!is_dir($path)) {
unset($paths[$index]);
}
});
});
return $plugins;
}
/**
* Return all templates for a given plugin.
*
* @param string $plugin The plugin to find all templates for.
*
* @return mixed
*/
public static function plugin($plugin)
{
$templates = [];
foreach (App::path('Template', $plugin) as $path) {
$templates = array_merge($templates, static::iteratePath($path));
}
return $templates;
}
/**
* Iterage over the given path and return all matching .tpl files in it.
*
* @param string $path Path to iterate over.
*
* @return array
*/
protected static function iteratePath($path)
{
return static::walkIterator(static::setupIterator($path));
}
/**
* Setup iterator for given path.
*
* @param string $path Path to setup iterator for.
*
* @return \Iterator
*/
protected static function setupIterator($path)
{
return new \RegexIterator(new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator(
$path,
\FilesystemIterator::KEY_AS_PATHNAME |
\FilesystemIterator::CURRENT_AS_FILEINFO |
\FilesystemIterator::SKIP_DOTS
),
\RecursiveIteratorIterator::CHILD_FIRST,
\RecursiveIteratorIterator::CATCH_GET_CHILD
), '/.*?' . TwigView::EXT . '$/', \RegexIterator::GET_MATCH);
}
/**
* Walk over the iterator and compile all templates.
*
* @param \Iterator $iterator Iterator to walk.
*
* @return array
*/
// @codingStandardsIgnoreStart
protected static function walkIterator(\Iterator $iterator)
{
$items = [];
$array = iterator_to_array($iterator);
uasort($array, function ($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
});
foreach ($array as $paths) {
foreach ($paths as $path) {
$items[] = $path;
}
}
return $items;
}
// @codingStandardsIgnoreEnd
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace WyriHaximus\TwigView\Lib;
/**
* Class TreeScanner
* @package WyriHaximus\TwigView\Lib
*/
class TreeScanner
{
/**
* Return all sections (app & plugins) with an Template directory.
*
* @return array
*/
public static function all()
{
return static::deepen(RelativeScanner::all());
}
/**
* Return all templates for a given plugin.
*
* @param string $plugin The plugin to find all templates for.
*
* @return mixed
*/
public static function plugin($plugin)
{
return static::deepen([
$plugin => RelativeScanner::plugin($plugin),
])[$plugin];
}
/**
* Strip the absolute path of template's paths for all given sections.
*
* @param string $sections Sections to iterate over.
*
* @return array
*/
protected static function deepen($sections)
{
foreach ($sections as $section => $paths) {
$sections[$section] = static::convertToTree($paths);
}
return $sections;
}
/**
* Turn a set of paths into a tree.
*
* @param array $paths Paths to turn into a tree.
*
* @return array
*/
protected static function convertToTree(array $paths)
{
foreach ($paths as $index => $path) {
static::convertPathToTree($paths, $index, $path);
}
return $paths;
}
/**
* Convert a path into a tree when it contains a directory separator.
*
* @param array $paths The paths to work on.
* @param mixed $index Index of $path.
* @param string $path Path to breakup and turn into a tree.
*
* @return void
*/
protected static function convertPathToTree(array &$paths, $index, $path)
{
if (strpos($path, DIRECTORY_SEPARATOR) !== false) {
$chunks = explode(DIRECTORY_SEPARATOR, $path);
$paths = static::branch($paths, $chunks);
unset($paths[$index]);
}
}
/**
* Create a branch for the current level and push a twig on it.
*
* @param array $paths Paths to append.
* @param array $branches Branches to use untill only one left.
*
* @return array
*/
protected static function branch(array $paths, array $branches)
{
$twig = array_shift($branches);
if (count($branches) == 0) {
$paths[] = $twig;
return $paths;
}
if (!isset($paths[$twig])) {
$paths[$twig] = [];
}
$paths[$twig] = static::branch($paths[$twig], $branches);
return $paths;
}
}
@@ -0,0 +1,51 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Arrays
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Arrays extends \Twig_Extension
{
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('in_array', 'in_array'),
new \Twig_SimpleFunction('explode', 'explode'),
new \Twig_SimpleFunction('array', function ($array) {
return (array)$array;
}),
new \Twig_SimpleFunction('array_push', 'push'),
new \Twig_SimpleFunction('array_add', 'add'),
new \Twig_SimpleFunction('array_prev', 'prev'),
new \Twig_SimpleFunction('array_next', 'next'),
new \Twig_SimpleFunction('array_current', 'current'),
new \Twig_SimpleFunction('array_each', 'each'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'array';
}
}
@@ -0,0 +1,49 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Basic
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Basic extends \Twig_Extension
{
/**
* Get declared filters.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('debug', 'debug'),
new \Twig_SimpleFilter('pr', 'pr'),
new \Twig_SimpleFilter('low', 'low'),
new \Twig_SimpleFilter('up', 'up'),
new \Twig_SimpleFilter('count', 'count'),
new \Twig_SimpleFilter('h', 'h'),
new \Twig_SimpleFilter('null', function () {
return '';
}),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'basic';
}
}
@@ -0,0 +1,45 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class I18n
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class I18n extends \Twig_Extension
{
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('__', '__'),
new \Twig_SimpleFunction('__d', '__d'),
new \Twig_SimpleFunction('__n', '__n'),
new \Twig_SimpleFunction('__x', '__x'),
new \Twig_SimpleFunction('__dn', '__dn'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'i18n';
}
}
@@ -0,0 +1,50 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Inflector
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Inflector extends \Twig_Extension
{
/**
* Get filters for this extension.
*
* @return \Twig_SimpleFunction[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('pluralize', 'Cake\Utility\Inflector::pluralize'),
new \Twig_SimpleFilter('singularize', 'Cake\Utility\Inflector::singularize'),
new \Twig_SimpleFilter('camelize', 'Cake\Utility\Inflector::camelize'),
new \Twig_SimpleFilter('underscore', 'Cake\Utility\Inflector::underscore'),
new \Twig_SimpleFilter('humanize', 'Cake\Utility\Inflector::humanize'),
new \Twig_SimpleFilter('tableize', 'Cake\Utility\Inflector::tableize'),
new \Twig_SimpleFilter('classify', 'Cake\Utility\Inflector::classify'),
new \Twig_SimpleFilter('variable', 'Cake\Utility\Inflector::variable'),
new \Twig_SimpleFilter('dasherize', 'Cake\Utility\Inflector::dasherize'),
new \Twig_SimpleFilter('slug', 'Cake\Utility\Text::slug'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'inflector';
}
}
@@ -0,0 +1,59 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Number
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Number extends \Twig_Extension
{
/**
* Get declared functions.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('toReadableSize', 'Cake\I18n\Number::toReadableSize'),
new \Twig_SimpleFilter('fromReadableSize', 'Cake\I18n\Number::fromReadableSize'),
new \Twig_SimpleFilter('toPercentage', 'Cake\I18n\Number::toPercentage'),
new \Twig_SimpleFilter('number_format', 'Cake\I18n\Number::format'),
new \Twig_SimpleFilter('formatDelta', 'Cake\I18n\Number::formatDelta'),
new \Twig_SimpleFilter('currency', 'Cake\I18n\Number::currency'),
];
}
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('defaultCurrency', 'Cake\I18n\Number::defaultCurrency'),
new \Twig_SimpleFunction('number_formatter', 'Cake\I18n\Number::formatter'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'number';
}
}
@@ -0,0 +1,53 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Basic
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class PotentialDangerous extends \Twig_Extension
{
/**
* Get declared filters.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('env', 'env'),
];
}
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('config', 'Cake\Core\Configure::read'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'potential_dangerous';
}
}
@@ -0,0 +1,50 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
use DebugKit\DebugTimer;
/**
* Class Basic
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Profiler extends \Twig_Extension_Profiler
{
/**
* Enter $profile.
*
* @param \Twig_Profiler_Profile $profile Profile.
*
* @return void
*/
public function enter(\Twig_Profiler_Profile $profile)
{
$name = 'Twig Template: ' . substr($profile->getName(), strlen(ROOT) + 1);
DebugTimer::start($name, __d('twig_view', $name));
parent::enter($profile);
}
/**
* Leave $profile.
*
* @param \Twig_Profiler_Profile $profile Profile.
*
* @return void
*/
public function leave(\Twig_Profiler_Profile $profile)
{
parent::leave($profile);
$name = 'Twig Template: ' . substr($profile->getName(), strlen(ROOT) + 1);
DebugTimer::stop($name);
}
}
@@ -0,0 +1,80 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
use Cake\Utility\Text;
/**
* Class Strings
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Strings extends \Twig_Extension
{
/**
* Get declared filters.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('substr', 'substr'),
new \Twig_SimpleFilter('tokenize', 'Cake\Utility\Text::tokenize'),
new \Twig_SimpleFilter('insert', 'Cake\Utility\Text::insert'),
new \Twig_SimpleFilter('cleanInsert', 'Cake\Utility\Text::cleanInsert'),
new \Twig_SimpleFilter('wrap', 'Cake\Utility\Text::wrap'),
new \Twig_SimpleFilter('wrapBlock', 'Cake\Utility\Text::wrapBlock'),
new \Twig_SimpleFilter('wordWrap', 'Cake\Utility\Text::wordWrap'),
new \Twig_SimpleFilter('highlight', 'Cake\Utility\Text::highlight'),
new \Twig_SimpleFilter('tail', 'Cake\Utility\Text::tail'),
new \Twig_SimpleFilter('truncate', 'Cake\Utility\Text::truncate'),
new \Twig_SimpleFilter('excerpt', 'Cake\Utility\Text::excerpt'),
new \Twig_SimpleFilter('toList', 'Cake\Utility\Text::toList'),
new \Twig_SimpleFilter('stripLinks', function ($string) {
$previousrErrorHandler = set_error_handler(function () {
});
$strippedString = Text::stripLinks($string);
set_error_handler($previousrErrorHandler);
return $strippedString;
}),
new \Twig_SimpleFilter('isMultibyte', 'Cake\Utility\Text::isMultibyte'),
new \Twig_SimpleFilter('utf8', 'Cake\Utility\Text::utf8'),
new \Twig_SimpleFilter('ascii', 'Cake\Utility\Text::ascii'),
new \Twig_SimpleFilter('parseFileSize', 'Cake\Utility\Text::parseFileSize'),
new \Twig_SimpleFilter('none', function ($string) {
return;
}),
];
}
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('uuid', 'Cake\Utility\Text::uuid'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'string';
}
}
@@ -0,0 +1,43 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Time
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Time extends \Twig_Extension
{
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('time', function ($time = null, $timezone = null) {
return new \Cake\I18n\Time($time, $timezone);
}),
new \Twig_SimpleFunction('timezones', 'Cake\I18n\Time::listTimezones'),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'time';
}
}
@@ -0,0 +1,49 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
/**
* Class Utils
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
class Utils extends \Twig_Extension
{
/**
* Get declared filters.
*
* @return \Twig_SimpleFilter[]
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('serialize', 'serialize'),
new \Twig_SimpleFilter('unserialize', 'unserialize'),
new \Twig_SimpleFilter('md5', 'md5'),
new \Twig_SimpleFilter('base64_encode', 'base64_encode'),
new \Twig_SimpleFilter('base64_decode', 'base64_decode'),
new \Twig_SimpleFilter('nl2br', 'nl2br'),
new \Twig_SimpleFilter('string', function ($str) {
return (string)$str;
}),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'utils';
}
}
@@ -0,0 +1,69 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Extension;
use Cake\View\View as CakeView;
/**
* Class View
* @package WyriHaximus\TwigView\Lib\Twig\Extension
*/
// @codingStandardsIgnoreStart
class View extends \Twig_Extension
// @codingStandardsIgnoreEnd
{
/**
* View to call methods upon.
*
* @var CakeView
*/
protected $view;
/**
* Constructor.
*
* @param CakeView $view View instance.
*/
public function __construct(CakeView $view)
{
$this->view = $view;
}
/**
* Get declared functions.
*
* @return \Twig_SimpleFunction[]
*/
public function getFunctions()
{
return [
new \Twig_SimpleFunction('elementExists', function ($name) {
return $this->view->elementExists($name);
}),
new \Twig_SimpleFunction('getVars', function () {
return $this->view->getVars();
}),
new \Twig_SimpleFunction('get', function ($var, $default = null) {
return $this->view->get($var, $default);
}),
];
}
/**
* Get extension name.
*
* @return string
*/
public function getName()
{
return 'view';
}
}
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig;
use Cake\Core\App;
use Cake\Core\Plugin;
use Twig_Error_Loader;
use Twig_Source;
use WyriHaximus\TwigView\View\TwigView;
/**
* Class Loader
* @package WyriHaximus\TwigView\Lib\Twig
*/
class Loader implements \Twig_LoaderInterface, \Twig_ExistsLoaderInterface, \Twig_SourceContextLoaderInterface
{
/**
* Get the file contents of a template.
*
* @param string $name Template.
*
* @return string
*/
public function getSource($name)
{
$name = $this->resolveFileName($name);
return file_get_contents($name);
}
/**
* Get cache key for template.
*
* @param string $name Template.
*
* @return string
*/
public function getCacheKey($name)
{
return $this->resolveFileName($name);
}
/**
* Check if template is still fresh.
*
* @param string $name Template.
* @param integer $time Timestamp.
*
* @return boolean
*/
public function isFresh($name, $time)
{
$name = $this->resolveFileName($name);
return filemtime($name) < $time;
}
/**
* Resolve template name to filename.
*
* @param string $name Template.
*
* @return string
*
* @throws \Twig_Error_Loader Thrown when template file isn't found.
*/
// @codingStandardsIgnoreStart
protected function resolveFileName($name)
{
// @codingStandardsIgnoreEnd
if (file_exists($name)) {
return $name;
}
list($plugin, $file) = pluginSplit($name);
foreach ([
null,
$plugin,
] as $scope) {
$paths = $this->getPaths($scope);
foreach ($paths as $path) {
$filePath = $path . $file;
if (file_exists($filePath)) {
return $filePath;
}
$filePath = $path . $file . TwigView::EXT;
if (file_exists($filePath)) {
return $filePath;
}
}
}
throw new \Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
/**
* Check if $plugin is active and return it's template paths or return the aps template paths.
*
* @param string|null $plugin The plugin in question.
*
* @return array
*/
protected function getPaths($plugin)
{
if ($plugin === null || !Plugin::loaded($plugin)) {
return App::path('Template');
}
return App::path('Template', $plugin);
}
public function exists($name)
{
$name = $this->resolveFileName($name);
return file_exists($name);
}
public function getSourceContext($name)
{
$path = $this->resolveFileName($name);
return new Twig_Source(file_get_contents($path), $name, $path);
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Node;
/**
* Class Cell
* @package WyriHaximus\TwigView\Lib\Twig\Node
*/
// @codingStandardsIgnoreStart
class Cell extends \Twig_Node implements \Twig_NodeOutputInterface
// @codingStandardsIgnoreEnd
{
/**
* Whether to assign the data or not.
*
* @var boolean
*/
protected $assign = false;
/**
* Constructor.
*
* @param boolean $assign Assign or echo.
* @param mixed $variable Variable to assign to.
* @param \Twig_Node_Expression $name Name.
* @param \Twig_Node_Expression $data Data array.
* @param \Twig_Node_Expression $options Options array.
* @param string $lineno Line number.
* @param string $tag Tag name.
*/
public function __construct(
$assign,
$variable,
\Twig_Node_Expression $name,
\Twig_Node_Expression $data = null,
\Twig_Node_Expression $options = null,
$lineno = '',
$tag = null
) {
if ($data === null) {
$data = new \Twig_Node_Expression_Array([], $lineno);
}
if ($options === null) {
$options = new \Twig_Node_Expression_Array([], $lineno);
}
parent::__construct(
[
'name' => $name,
'data' => $data,
'options' => $options,
],
[
'variable' => $variable,
],
$lineno,
$tag
);
$this->assign = $assign;
}
/**
* Compile tag.
*
* @param \Twig_Compiler $compiler Compiler.
*
* @return void
*/
// @codingStandardsIgnoreStart
public function compile(\Twig_Compiler $compiler)
{
// @codingStandardsIgnoreEnd
$compiler->addDebugInfo($this);
if ($this->assign) {
$compiler->raw('$context[\'' . $this->getAttribute('variable') . '\'] = ');
// @codingStandardsIgnoreStart
} else {
// @codingStandardsIgnoreEnd
$compiler->raw('echo ');
}
$compiler->raw('$context[\'_view\']->cell(');
$compiler->subcompile($this->getNode('name'));
$data = $this->getNode('data');
if ($data !== null) {
$compiler->raw(',');
$compiler->subcompile($data);
}
$options = $this->getNode('options');
if ($options !== null) {
$compiler->raw(',');
$compiler->subcompile($options);
}
$compiler->raw(");\n");
}
}
@@ -0,0 +1,80 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\Node;
/**
* Class Element
* @package WyriHaximus\TwigView\Lib\Twig\Node
*/
class Element extends \Twig_Node
{
/**
* Constructor.
*
* @param \Twig_Node_Expression $name Name.
* @param \Twig_Node_Expression $data Data.
* @param \Twig_Node_Expression $options Options.
* @param string $lineno Linenumber.
* @param string $tag Tag.
*/
public function __construct(
\Twig_Node_Expression $name,
\Twig_Node_Expression $data = null,
\Twig_Node_Expression $options = null,
$lineno = '',
$tag = null
) {
if ($data === null) {
$data = new \Twig_Node_Expression_Array([], $lineno);
}
if ($options === null) {
$options = new \Twig_Node_Expression_Array([], $lineno);
}
parent::__construct(
[
'name' => $name,
'data' => $data,
'options' => $options,
],
[],
$lineno,
$tag
);
}
/**
* Compile node.
*
* @param \Twig_Compiler $compiler Compiler.
*
* @return void
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
$compiler->raw('echo $context[\'_view\']->element(');
$compiler->subcompile($this->getNode('name'));
$data = $this->getNode('data');
if ($data !== null) {
$compiler->raw(',');
$compiler->subcompile($data);
}
$options = $this->getNode('options');
if ($options !== null) {
$compiler->raw(',');
$compiler->subcompile($options);
}
$compiler->raw(");\n");
}
}
@@ -0,0 +1,71 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\TokenParser;
use Twig_Token;
use WyriHaximus\TwigView\Lib\Twig\Node\Cell as CellNode;
/**
* Class Element
* @package WyriHaximus\TwigView\Lib\Twig\TokenParser
*/
class Cell extends \Twig_TokenParser_Include
{
/**
* Parse token.
*
* @param Twig_Token $token Token.
*
* @return CellNode
*/
// @codingStandardsIgnoreStart
public function parse(Twig_Token $token)
{
// @codingStandardsIgnoreEnd
$stream = $this->parser->getStream();
// @codingStandardsIgnoreStart
$variable = null;
if ($stream->test(Twig_Token::NAME_TYPE)) {
$variable = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
}
$assign = false;
if ($stream->test(Twig_Token::OPERATOR_TYPE)) {
$stream->expect(Twig_Token::OPERATOR_TYPE, '=');
$assign = true;
}
$name = $this->parser->getExpressionParser()->parseExpression();
$data = null;
if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
$data = $this->parser->getExpressionParser()->parseExpression();
}
$options = null;
if (!$stream->test(Twig_Token::BLOCK_END_TYPE)) {
$options = $this->parser->getExpressionParser()->parseExpression();
}
// @codingStandardsIgnoreEnd
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new CellNode($assign, $variable, $name, $data, $options, $token->getLine(), $this->getTag());
}
/**
* Tag name.
*
* @return string
*/
public function getTag()
{
return 'cell';
}
}
@@ -0,0 +1,62 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Lib\Twig\TokenParser;
use WyriHaximus\TwigView\Lib\Twig\Node\Element as ElementNode;
/**
* Class Element
* @package WyriHaximus\TwigView\Lib\Twig\TokenParser
*/
class Element extends \Twig_TokenParser_Include
{
/**
* Parse token.
*
* @param \Twig_Token $token Token.
*
* @return \Twig_NodeInterface|ElementNode
*/
// @codingStandardsIgnoreStart
public function parse(\Twig_Token $token)
{
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreStart
$stream = $this->parser->getStream();
$name = $this->parser->getExpressionParser()->parseExpression();
$data = null;
if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
$data = $this->parser->getExpressionParser()->parseExpression();
}
$options = null;
if (!$stream->test(\Twig_Token::BLOCK_END_TYPE)) {
$options = $this->parser->getExpressionParser()->parseExpression();
}
// @codingStandardsIgnoreEnd
$stream->expect(\Twig_Token::BLOCK_END_TYPE);
return new ElementNode($name, $data, $options, $token->getLine(), $this->getTag());
}
/**
* Get tag name.
*
* @return string
*/
public function getTag()
{
return 'element';
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace WyriHaximus\TwigView\Panel;
use DebugKit\DebugPanel;
use WyriHaximus\TwigView\Lib\TreeScanner;
// @codingStandardsIgnoreStart
class TwigPanel extends DebugPanel
// @codingStandardsIgnoreEnd
{
/**
* Plugin name.
*
* @var string
*/
// @codingStandardsIgnoreStart
public $plugin = 'WyriHaximus/TwigView';
// @codingStandardsIgnoreEnd
/**
* Get the data for the twig panel.
*
* @return array
*/
public function data()
{
return [
'templates' => TreeScanner::all(),
];
}
}
+174
View File
@@ -0,0 +1,174 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Shell;
use Cake\Console\ConsoleIo;
use Cake\Console\Shell;
use Cake\Core\Plugin;
use WyriHaximus\TwigView\Lib\Scanner;
use WyriHaximus\TwigView\View\TwigView;
/**
* Class CompileTemplatesShell
* @package WyriHaximus\TwigView\Console\Command
*/
// @codingStandardsIgnoreStart
class CompileShell extends Shell
// @codingStandardsIgnoreEnd
{
/**
* Instance of TwigView to be used to compile templates.
*
* @var TwigView
*/
protected $twigView;
/**
* Constructor.
*
* @param ConsoleIo $consoleIo An IO instance.
*/
public function __construct(ConsoleIo $consoleIo = null)
{
parent::__construct($consoleIo);
$this->twigView = new TwigView();
}
/**
* Set TwigView.
*
* @param TwigView $twigView TwigView instance.
*
* @return void
*/
public function setTwigview(TwigView $twigView)
{
$this->twigView = $twigView;
}
/**
* Compile all templates.
*
* @return void
*/
// @codingStandardsIgnoreStart
public function all()
{
$this->out('<info>Compiling all templates</info>');
foreach(Scanner::all() as $section => $templates) {
$this->out('<info>Compiling ' . $section . '\'s templates</info>');
$this->walkIterator($templates);
}
}
// @codingStandardsIgnoreEnd
/**
* Compile only this plugin.
*
* @param string $plugin Plugin name.
*
* @return void
*/
public function plugin($plugin)
{
$this->out('<info>Compiling one ' . $plugin . '\'s templates</info>');
$this->walkIterator(Scanner::plugin($plugin));
}
/**
* Only compile one file.
*
* @param string $fileName File to compile.
*
* @return void
*/
public function file($fileName)
{
$this->out('<info>Compiling one template</info>');
$this->compileTemplate($fileName);
}
/**
* Walk over $iterator and compile all templates in it.
*
* @param mixed $iterator Iterator to walk over.
*
* @return void
*/
protected function walkIterator($iterator)
{
foreach ($iterator as $template) {
$this->compileTemplate($template);
}
}
/**
* Compile a template.
*
* @param string $fileName Template to compile.
*
* @return void
*/
protected function compileTemplate($fileName)
{
try {
$this->
twigView->
getTwig()->
loadTemplate($fileName);
$this->out('<success>' . $fileName . '</success>');
} catch (\Exception $exception) {
$this->out('<error>' . $fileName . '</error>');
$this->out('<error>' . $exception->getMessage() . '</error>');
}
}
/**
* Set options for this console.
*
* @return \Cake\Console\ConsoleOptionParser
*/
// @codingStandardsIgnoreStart
public function getOptionParser()
{
// @codingStandardsIgnoreEnd
return parent::getOptionParser()->addSubcommand(
'all',
[
'short' => 'a',
// @codingStandardsIgnoreStart
'help' => __('Searches and precompiles all twig templates it finds.')
// @codingStandardsIgnoreEnd
]
)->addSubcommand(
'plugin',
[
'short' => 'p',
// @codingStandardsIgnoreStart
'help' => __('Searches and precompiles all twig templates for a specific plugin.')
// @codingStandardsIgnoreEnd
]
)->addSubcommand(
'file',
[
'short' => 'f',
// @codingStandardsIgnoreStart
'help' => __('Precompile a specific file.')
// @codingStandardsIgnoreEnd
]
// @codingStandardsIgnoreStart
)->setDescription(__('TwigView templates precompiler'));
// @codingStandardsIgnoreEnd
}
}
@@ -0,0 +1,58 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\Shell\Task;
use Bake\Shell\Task\TemplateTask;
use Cake\Console\Shell;
use Cake\Core\Configure;
use Cake\Utility\Inflector;
/**
* Task class for creating and updating twig view template files.
*
*/
class TwigTemplateTask extends TemplateTask
{
public function name()
{
return 'twig_template';
}
/**
* Assembles and writes bakes the twig view file.
*
* @param string $template Template to generate content with.
* @param string $content Content to write.
* @param string $outputFile The destination action name. If null, will fallback to $template.
* @return string Generated file content.
*/
public function bake($template, $content = '', $outputFile = null)
{
if ($outputFile === null) {
$outputFile = $template;
}
if ($content === true) {
$content = $this->getContent($template);
}
if (empty($content)) {
$this->err("<warning>No generated content for '{$template}.ctp', not generating template.</warning>");
return false;
}
$this->out("\n" . sprintf('Baking `%s` view twig template file...', $outputFile), 1, Shell::QUIET);
$path = $this->getPath();
$filename = $path . Inflector::underscore($outputFile) . '.twig';
$this->createFile($filename, $content);
return $content;
}
}
@@ -0,0 +1,98 @@
<%
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Cake\Utility\Inflector;
$fields = collection($fields)
->filter(function($field) use ($schema) {
return $schema->getColumnType($field) !== 'binary';
});
if (isset($modelObject) && $modelObject->behaviors()->has('Tree')) {
$fields = $fields->reject(function ($field) {
return $field === 'lft' || $field === 'rght';
});
}
%>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">{{ __('Actions') }}</li>
<% if (strpos($action, 'add') === false): %>
<li>{{ Form.postLink(
__('Delete'),
{'action' : 'delete', 0 : <%= $singularVar %>.<%= $primaryKey[0] %>},
{'confirm' : __('Are you sure you want to delete # {0}?', <%= $singularVar %>.<%= $primaryKey[0] %>)}
)|raw
}}
</li>
<% endif; %>
<li>{{ Html.link(__('List <%= $pluralHumanName %>'), {'action' : 'index'})|raw }}</li>
<%
$done = [];
foreach ($associations as $type => $data) {
foreach ($data as $alias => $details) {
if ($details['controller'] !== $this->name && !in_array($details['controller'], $done)) {
%>
<li>{{ Html.link(__('List <%= $this->_pluralHumanName($alias) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'index'})|raw }}</li>
<li>{{ Html.link(__('New <%= $this->_singularHumanName($alias) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'add'})|raw }}</li>
<%
$done[] = $details['controller'];
}
}
}
%>
</ul>
</nav>
<div class="<%= $pluralVar %> form large-9 medium-8 columns content">
{{ Form.create(<%= $singularVar %>)|raw }}
<fieldset>
<legend>{{ __('<%= Inflector::humanize($action) %> <%= $singularHumanName %>') }}</legend>
<%
foreach ($fields as $field) {
if (in_array($field, $primaryKey)) {
continue;
}
if (isset($keyFields[$field])) {
$fieldData = $schema->getColumn($field);
if (!empty($fieldData['null'])) {
%>
{{ Form.input('<%= $field %>', {'options' : <%= $keyFields[$field] %>, 'empty' : true})|raw }}
<%
} else {
%>
{{ Form.input('<%= $field %>', {'options' : <%= $keyFields[$field] %>})|raw }}
<%
}
continue;
}
if (!in_array($field, ['created', 'modified', 'updated'])) {
$fieldData = $schema->getColumn($field);
if (in_array($fieldData['type'], ['date', 'datetime', 'time']) && (!empty($fieldData['null']))) {
%>
{{ Form.input('<%= $field %>', {'empty' : true})|raw }}
<%
} else {
%>
{{ Form.input('<%= $field %>')|raw }}
<%
}
}
}
if (!empty($associations['BelongsToMany'])) {
foreach ($associations['BelongsToMany'] as $assocName => $assocData) {
%>
{{ Form.input('<%= $assocData['property'] %>._ids', {'options' : <%= $assocData['variable'] %>})|raw }}
<%
}
}
%>
</fieldset>
{{ Form.button(__('Submit'))|raw }}
{{ Form.end()|raw }}
</div>
@@ -0,0 +1,11 @@
<%
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
echo $this->element('form');
@@ -0,0 +1,11 @@
<%
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
echo $this->element('form');
@@ -0,0 +1,107 @@
<%
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Cake\Utility\Inflector;
$fields = collection($fields)
->filter(function($field) use ($schema) {
return !in_array($schema->getColumnType($field), ['binary', 'text']);
});
if (isset($modelObject) && $modelObject->behaviors()->has('Tree')) {
$fields = $fields->reject(function ($field) {
return $field === 'lft' || $field === 'rght';
});
}
if (!empty($indexColumns)) {
$fields = $fields->take($indexColumns);
}
%>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">{{ __('Actions') }}</li>
<li>{{ Html.link(__('New <%= $singularHumanName %>'), {'action' : 'add'})|raw }}</li>
<%
$done = [];
foreach ($associations as $type => $data):
foreach ($data as $alias => $details):
if (!empty($details['navLink']) && $details['controller'] !== $this->name && !in_array($details['controller'], $done)):
%>
<li>{{ Html.link(__('List <%= $this->_pluralHumanName($alias) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'index'})|raw }}</li>
<li>{{ Html.link(__('New <%= $this->_singularHumanName($alias) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'add'})|raw }}</li>
<%
$done[] = $details['controller'];
endif;
endforeach;
endforeach;
%>
</ul>
</nav>
<div class="<%= $pluralVar %> index large-9 medium-8 columns content">
<h3>{{ __('<%= $pluralHumanName %>') }}</h3>
<table cellpadding="0" cellspacing="0">
<thead>
<tr>
<% foreach ($fields as $field): %>
<th>{{ Paginator.sort('<%= $field %>')|raw }}</th>
<% endforeach; %>
<th class="actions">{{ __('Actions') }}</th>
</tr>
</thead>
<tbody>
{% for <%= $singularVar %> in <%= $pluralVar %> %}
<tr>
<% foreach ($fields as $field) {
$isKey = false;
if (!empty($associations['BelongsTo'])) {
foreach ($associations['BelongsTo'] as $alias => $details) {
if ($field === $details['foreignKey']) {
$isKey = true;
%>
<td>{{ <%= $singularVar %>.has('<%= $details['property'] %>') ? Html.link(<%= $singularVar %>.<%= $details['property'] %>.<%= $details['displayField'] %>, {'controller' : '<%= $details['controller'] %>', 'action' : 'view', 0 : <%= $singularVar %>.<%= $details['property'] %>.<%= $details['primaryKey'][0] %>})|raw : '' }}</td>
<%
break;
}
}
}
if ($isKey !== true) {
if (!in_array($schema->getColumnType($field), ['integer', 'biginteger', 'decimal', 'float'])) {
%>
<td>{{ <%= $singularVar %>.<%= $field %>|h }}</td>
<%
} else {
%>
<td>{{ Number.format(<%= $singularVar %>.<%= $field %>) }}</td>
<%
}
}
}
$pk = $singularVar . '.' . $primaryKey[0];
%>
<td class="actions">
{{ Html.link(__('View'), {'action' : 'view', 0 : <%= $pk %>})|raw }}
{{ Html.link(__('Edit'), {'action' : 'edit', 0 : <%= $pk %>})|raw }}
{{ Form.postLink(__('Delete'), {'action' : 'delete', 0 : <%= $pk %>}, {'confirm' : __('Are you sure you want to delete # {0}?', <%= $pk %>)})|raw }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="paginator">
<ul class="pagination">
{{ Paginator.prev('< ' ~ __('previous'))|raw }}
{{ Paginator.numbers()|raw }}
{{ Paginator.next(__('next') ~ ' >')|raw }}
</ul>
<p>{{ Paginator.counter()|raw }}</p>
</div>
</div>
@@ -0,0 +1,166 @@
<%
/**
* This file is part of TwigView.
*
** (c) 2015 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Cake\Utility\Inflector;
$associations += ['BelongsTo' => [], 'HasOne' => [], 'HasMany' => [], 'BelongsToMany' => []];
$immediateAssociations = $associations['BelongsTo'];
$associationFields = collection($fields)
->map(function($field) use ($immediateAssociations) {
foreach ($immediateAssociations as $alias => $details) {
if ($field === $details['foreignKey']) {
return [$field => $details];
}
}
})
->filter()
->reduce(function($fields, $value) {
return $fields + $value;
}, []);
$groupedFields = collection($fields)
->filter(function($field) use ($schema) {
return $schema->getColumnType($field) !== 'binary';
})
->groupBy(function($field) use ($schema, $associationFields) {
$type = $schema->getColumnType($field);
if (isset($associationFields[$field])) {
return 'string';
}
if (in_array($type, ['integer', 'float', 'decimal', 'biginteger'])) {
return 'number';
}
if (in_array($type, ['date', 'time', 'datetime', 'timestamp'])) {
return 'date';
}
return in_array($type, ['text', 'boolean']) ? $type : 'string';
})
->toArray();
$groupedFields += ['number' => [], 'string' => [], 'boolean' => [], 'date' => [], 'text' => []];
$pk = "$singularVar.{$primaryKey[0]}";
%>
<nav class="large-3 medium-4 columns" id="actions-sidebar">
<ul class="side-nav">
<li class="heading">{{ __('Actions') }}</li>
<li>{{ Html.link(__('Edit <%= $singularHumanName %>'), {'action' : 'edit', 0 : <%= $pk %>})|raw }}</li>
<li>{{ Form.postLink(__('Delete <%= $singularHumanName %>'), {'action' : 'delete', 0 : <%= $pk %>}, {'confirm' : __('Are you sure you want to delete # {0}?', <%= $pk %>)})|raw }}</li>
<li>{{ Html.link(__('List <%= $pluralHumanName %>'), {'action' : 'index'})|raw }}</li>
<li>{{ Html.link(__('New <%= $singularHumanName %>'), {'action' : 'add'})|raw }}</li>
<%
$done = [];
foreach ($associations as $type => $data) {
foreach ($data as $alias => $details) {
if ($details['controller'] !== $this->name && !in_array($details['controller'], $done)) {
%>
<li>{{ Html.link(__('List <%= $this->_pluralHumanName($alias) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'index'})|raw }}</li>
<li>{{ Html.link(__('New <%= Inflector::humanize(Inflector::singularize(Inflector::underscore($alias))) %>'), {'controller' : '<%= $details['controller'] %>', 'action' : 'add'})|raw }}</li>
<%
$done[] = $details['controller'];
}
}
}
%>
</ul>
</nav>
<div class="<%= $pluralVar %> view large-9 medium-8 columns content">
<h3>{{ <%= $singularVar %>.<%= $displayField %>|h }}</h3>
<table class="vertical-table">
<% if ($groupedFields['string']) : %>
<% foreach ($groupedFields['string'] as $field) : %>
<% if (isset($associationFields[$field])) :
$details = $associationFields[$field];
%>
<tr>
<th>{{ __('<%= Inflector::humanize($details['property']) %>') }}</th>
<td>{{ <%= $singularVar %>.has('<%= $details['property'] %>') ? Html.link(<%= $singularVar %>.<%= $details['property'] %>.<%= $details['displayField'] %>, {'controller' : '<%= $details['controller'] %>', 'action' : 'view', 0 : <%= $singularVar %>.<%= $details['property'] %>.<%= $details['primaryKey'][0] %>})|raw : '' }}</td>
</tr>
<% else : %>
<tr>
<th>{{ __('<%= Inflector::humanize($field) %>') }}</th>
<td>{{ <%= $singularVar %>.<%= $field %>|h }}</td>
</tr>
<% endif; %>
<% endforeach; %>
<% endif; %>
<% if ($associations['HasOne']) : %>
<%- foreach ($associations['HasOne'] as $alias => $details) : %>
<tr>
<th>{{ __('<%= Inflector::humanize(Inflector::singularize(Inflector::underscore($alias))) %>') }}</th>
<td>{{ <%= $singularVar %>.has('<%= $details['property'] %>') ? Html.link(<%= $singularVar %>.<%= $details['property'] %>.<%= $details['displayField'] %>, {'controller' : '<%= $details['controller'] %>', 'action' : 'view', 0 : <%= $singularVar %>.<%= $details['property'] %>.<%= $details['primaryKey'][0] %>})|raw : '' }}</td>
</tr>
<%- endforeach; %>
<% endif; %>
<% if ($groupedFields['number']) : %>
<% foreach ($groupedFields['number'] as $field) : %>
<tr>
<th>{{ __('<%= Inflector::humanize($field) %>') }}</th>
<td>{{ Number.format(<%= $singularVar %>.<%= $field %>) }}</td>
</tr>
<% endforeach; %>
<% endif; %>
<% if ($groupedFields['date']) : %>
<% foreach ($groupedFields['date'] as $field) : %>
<tr>
<th>{{ __('<%= Inflector::humanize($field) %>') }}</th>
<td>{{ <%= $singularVar %>.<%= $field %>|h }}</td>
</tr>
<% endforeach; %>
<% endif; %>
<% if ($groupedFields['boolean']) : %>
<% foreach ($groupedFields['boolean'] as $field) : %>
<tr>
<th>{{ __('<%= Inflector::humanize($field) %>') }}</th>
<td>{{ <%= $singularVar %>.<%= $field %> ? __('Yes') : __('No'); }}</td>
</tr>
<% endforeach; %>
<% endif; %>
</table>
<% if ($groupedFields['text']) : %>
<% foreach ($groupedFields['text'] as $field) : %>
<div class="row">
<h4>{{ __('<%= Inflector::humanize($field) %>') }}</h4>
{{ Text.autoParagraph(<%= $singularVar %>.<%= $field %>|h)|raw }}
</div>
<% endforeach; %>
<% endif; %>
<%
$relations = $associations['HasMany'] + $associations['BelongsToMany'];
foreach ($relations as $alias => $details):
$otherSingularVar = Inflector::variable($alias);
$otherPluralHumanName = Inflector::humanize(Inflector::underscore($details['controller']));
%>
<div class="related">
<h4>{{ __('Related <%= $otherPluralHumanName %>') }}</h4>
{% if (<%= $singularVar %>.<%= $details['property'] %> is not empty): %}
<table cellpadding="0" cellspacing="0">
<tr>
<% foreach ($details['fields'] as $field): %>
<th>{{ __('<%= Inflector::humanize($field) %>') }}</th>
<% endforeach; %>
<th class="actions">{{ __('Actions') }}</th>
</tr>
{% for (<%= $otherSingularVar %> in <%= $singularVar %>.<%= $details['property'] %>): %}
<tr>
<%- foreach ($details['fields'] as $field): %>
<td>{{ <%= $otherSingularVar %>.<%= $field %>|h }}</td>
<%- endforeach; %>
<%- $otherPk = "{$otherSingularVar}.{$details['primaryKey'][0]}"; %>
<td class="actions">
{{ Html.link(__('View'), {'controller' : '<%= $details['controller'] %>', 'action' : 'view', 0 : <%= $otherPk %>})|raw }}
{{ Html.link(__('Edit'), {'controller' : '<%= $details['controller'] %>', 'action' : 'edit', 0 : <%= $otherPk %>})|raw }}
{{ Form.postLink(__('Delete'), {'controller' : '<%= $details['controller'] %>', 'action' : 'delete', 0 : <%= $otherPk %>], {'confirm' : __('Are you sure you want to delete # {0}?', <%= $otherPk %>)})|raw }}
</td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
<% endforeach; %>
</div>
@@ -0,0 +1,3 @@
<h4>Templates</h4>
<?= $this->Toolbar->makeNeatArray($templates) ?>
+327
View File
@@ -0,0 +1,327 @@
<?php
/**
* This file is part of TwigView.
*
** (c) 2014 Cees-Jan Kiewiet
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace WyriHaximus\TwigView\View;
use Cake\Core\Configure;
use Cake\View\Exception\MissingLayoutException;
use Cake\View\Exception\MissingTemplateException;
use Cake\View\View;
use Exception;
use WyriHaximus\TwigView\Event\ConstructEvent;
use WyriHaximus\TwigView\Event\EnvironmentConfigEvent;
use WyriHaximus\TwigView\Event\LoaderEvent;
use WyriHaximus\TwigView\Lib\Twig\Loader;
/**
* Class TwigView
* @package WyriHaximus\TwigView\View
*/
// @codingStandardsIgnoreStart
class TwigView extends View
// @codingStandardsIgnoreEnd
{
const EXT = '.twig';
const ENV_CONFIG = 'WyriHaximus.TwigView.environment';
/**
* Extension to use.
*
* @var string
*/
// @codingStandardsIgnoreStart
protected $_ext = self::EXT;
// @codingStandardsIgnoreEnd
/**
* @var array
*/
protected $extensions = [
self::EXT,
'.tpl',
'.ctp',
];
/**
* Twig instance.
*
* @var \Twig_Environment
*/
protected $twig;
/**
* Initialize view.
*
* @return void
*/
public function initialize()
{
$this->twig = new \Twig_Environment($this->getLoader(), $this->resolveConfig());
$this->getEventManager()->dispatch(ConstructEvent::create($this, $this->twig));
$this->_ext = self::EXT;
parent::initialize();
}
/**
* @return array
*/
protected function resolveConfig()
{
$config = [
'cache' => CACHE . 'twigView' . DS,
'charset' => strtolower(Configure::readOrFail('App.encoding')),
'auto_reload' => Configure::read('debug'),
'debug' => Configure::read('debug'),
];
$config = array_replace($config, $this->readConfig());
$configEvent = EnvironmentConfigEvent::create($config);
$this->getEventManager()->dispatch($configEvent);
return $configEvent->getConfig();
}
/**
* @return array
*/
protected function readConfig()
{
if (!Configure::check(static::ENV_CONFIG)) {
return [];
}
$config = Configure::read(static::ENV_CONFIG);
if (!is_array($config)) {
return [];
}
return $config;
}
/**
* @param string $extension
*/
public function unshiftExtension($extension)
{
array_unshift($this->extensions, $extension);
}
/**
* Create the template loader.
*
* @return \Twig_LoaderInterface
*/
protected function getLoader()
{
$event = LoaderEvent::create(new Loader());
$this->getEventManager()->dispatch($event);
return $event->getResultLoader();
}
/**
* Get helper list.
*
* @return \Cake\View\Helper[]
*/
protected function generateHelperList()
{
$registry = $this->helpers();
$helperList = [];
foreach ($registry->loaded() as $alias) {
$helperList[$alias] = $registry->get($alias);
}
return $helperList;
}
/**
* Render the template.
*
* @param string $viewFile Template file.
* @param array $data Data that can be used by the template.
*
* @throws \Exception
* @return string
*/
// @codingStandardsIgnoreStart
protected function _render($viewFile, $data = array())
{
// @codingStandardsIgnoreEnd
if (empty($data)) {
$data = $this->viewVars;
}
if (substr($viewFile, -3) === 'ctp') {
$out = parent::_render($viewFile, $data);
// @codingStandardsIgnoreStart
} else {
// @codingStandardsIgnoreEnd
$data = array_merge(
$data,
$this->generateHelperList(),
[
'_view' => $this,
]
);
// @codingStandardsIgnoreStart
try {
$out = $this->getTwig()->loadTemplate($viewFile)->render($data);
} catch (Exception $e) {
$previous = $e->getPrevious();
if ($previous !== null && $previous instanceof Exception) {
throw $previous;
} else {
throw $e;
}
}
// @codingStandardsIgnoreEnd
}
return $out;
}
/**
* @param string|null $name
* @return string
* @throws \Exception
*/
// @codingStandardsIgnoreStart
protected function _getViewFileName($name = null)
{
// @codingStandardsIgnoreEnd
$templatePath = $subDir = '';
if ($this->subDir !== null) {
$subDir = $this->subDir . DIRECTORY_SEPARATOR;
}
if ($this->templatePath) {
$templatePath = $this->templatePath . DIRECTORY_SEPARATOR;
}
if ($name === null) {
$name = $this->template;
}
list($plugin, $name) = $this->pluginSplit($name);
$name = str_replace('/', DIRECTORY_SEPARATOR, $name);
if (strpos($name, DIRECTORY_SEPARATOR) === false && $name[0] !== '.') {
$name = $templatePath . $subDir . $this->_inflectViewFileName($name);
} elseif (strpos($name, DIRECTORY_SEPARATOR) !== false) {
if ($name[0] === DIRECTORY_SEPARATOR || $name[1] === ':') {
$name = trim($name, DIRECTORY_SEPARATOR);
} elseif (!$plugin || $this->templatePath !== $this->name) {
$name = $templatePath . $subDir . $name;
} else {
$name = DIRECTORY_SEPARATOR . $subDir . $name;
}
}
foreach ($this->_paths($plugin) as $path) {
foreach ($this->extensions as $extension) {
if (file_exists($path . $name . $extension)) {
return $this->_checkFilePath($path . $name . $extension, $path);
}
}
}
throw new MissingTemplateException(['file' => $name . $this->_ext]);
}
/**
* @param string|null $name
* @return string
* @throws \Exception
*/
// @codingStandardsIgnoreStart
protected function _getLayoutFileName($name = null)
{
// @codingStandardsIgnoreEnd
if ($name === null) {
$name = $this->layout;
}
$subDir = null;
if ($this->layoutPath) {
$subDir = $this->layoutPath . DIRECTORY_SEPARATOR;
}
list($plugin, $name) = $this->pluginSplit($name);
$layoutPaths = $this->_getSubPaths('Layout' . DIRECTORY_SEPARATOR . $subDir);
foreach ($this->_paths($plugin) as $path) {
foreach ($layoutPaths as $layoutPath) {
$currentPath = $path . $layoutPath;
foreach ($this->extensions as $extension) {
if (file_exists($currentPath . $name . $extension)) {
return $this->_checkFilePath($currentPath . $name . $extension, $currentPath);
}
}
}
}
throw new MissingLayoutException([
'file' => $layoutPaths[0] . $name . $this->_ext
]);
}
/**
* @param string $name
* @param bool $pluginCheck
* @return string
* @throws \Exception
*/
// @codingStandardsIgnoreStart
protected function _getElementFileName($name, $pluginCheck = true)
{
// @codingStandardsIgnoreEnd
list($plugin, $name) = $this->pluginSplit($name, $pluginCheck);
$paths = $this->_paths($plugin);
$elementPaths = $this->_getSubPaths('Element');
foreach ($paths as $path) {
foreach ($elementPaths as $elementPath) {
foreach ($this->extensions as $extension) {
if (file_exists($path . $elementPath . DIRECTORY_SEPARATOR . $name . $extension)) {
return $path . $elementPath . DIRECTORY_SEPARATOR . $name . $extension;
}
}
}
}
return false;
}
/**
* Get twig environment instance.
*
* @return \Twig_Environment
*/
public function getTwig()
{
return $this->twig;
}
/**
* Return empty string when View instance is cast to string.
*
* @return string
*/
public function __toString()
{
return '';
}
}