Initial commit: apps.supersam24.ru

This commit is contained in:
root committed 2026-10-22 00:04:27 +03:00
commit e955e1da36
6566 files changed
+1271095

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
{"presets": [
"@babel/env",
["minify", {
"keepFnName": true,
"keepClassName": true,
"mangle": false
}]
]}
+24
View File
@@ -0,0 +1,24 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.bat]
end_of_line = crlf
[*.yml]
indent_style = space
indent_size = 2
[*.twig]
insert_final_newline = false
[Makefile]
indent_style = tab
+5
View File
@@ -0,0 +1,5 @@
tmp
logs
.idea
files
archive/*
+13
View File
@@ -0,0 +1,13 @@
# Uncomment the following to prevent the httpoxy vulnerability
# See: https://httpoxy.org/
#<IfModule mod_headers.c>
# RequestHeader unset Proxy
#</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteRule ^(\.well-known/.*)$ $1 [L]
RewriteRule ^$ webroot/ [L]
RewriteRule (.*) webroot/$1 [L]
</IfModule>
+30
View File
@@ -0,0 +1,30 @@
language: php
dist: trusty
sudo: false
php:
- 5.6
- 7.0
- 7.1
matrix:
fast_finish: true
include:
- php: 7.0
env: PHPCS=1
before_script:
- if [[ $PHPCS = 1 ]]; then composer require cakephp/cakephp-codesniffer:~2.1; fi
- if [[ $PHPCS != 1 ]]; then composer install; fi
- if [[ $PHPCS != 1 ]]; then composer require phpunit/phpunit:"^5.7|^6.0"; fi
- if [[ $PHPCS != 1 ]]; then composer run-script post-install-cmd --no-interaction; fi
script:
- if [[ $PHPCS != 1 ]]; then vendor/bin/phpunit; fi
- if [[ $PHPCS = 1 ]]; then vendor/bin/phpcs -p --extensions=php --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests ./config ./webroot; fi
notifications:
email: false
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env sh
################################################################################
#
# Cake is a shell script for invoking CakePHP shell commands
#
# CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
#
# @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
# @link https://cakephp.org CakePHP(tm) Project
# @since 1.2.0
# @license https://opensource.org/licenses/mit-license.php MIT License
#
################################################################################
# Canonicalize by following every symlink of the given name recursively
canonicalize() {
NAME="$1"
if [ -f "$NAME" ]
then
DIR=$(dirname -- "$NAME")
NAME=$(cd -P "$DIR" > /dev/null && pwd -P)/$(basename -- "$NAME")
fi
while [ -h "$NAME" ]; do
DIR=$(dirname -- "$NAME")
SYM=$(readlink "$NAME")
NAME=$(cd "$DIR" > /dev/null && cd $(dirname -- "$SYM") > /dev/null && pwd)/$(basename -- "$SYM")
done
echo "$NAME"
}
CONSOLE=$(dirname -- "$(canonicalize "$0")")
APP=$(dirname "$CONSOLE")
if [ $(basename $0) != 'cake' ]
then
exec php "$CONSOLE"/cake.php $(basename $0) "$@"
else
exec php "$CONSOLE"/cake.php "$@"
fi
exit
+27
View File
@@ -0,0 +1,27 @@
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::
:: Cake is a Windows batch script for invoking CakePHP shell commands
::
:: CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
:: Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
::
:: Licensed under The MIT License
:: Redistributions of files must retain the above copyright notice.
::
:: @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
:: @link https://cakephp.org CakePHP(tm) Project
:: @since 2.0.0
:: @license https://opensource.org/licenses/mit-license.php MIT License
::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
@echo off
SET app=%0
SET lib=%~dp0
c:\xampp\php7\php "%lib%cake.php" %*
echo.
exit /B %ERRORLEVEL%
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/php -q
<?php
// Check platform requirements
require dirname(__DIR__) . '/vendor/autoload.php';
use App\Application;
use Cake\Console\CommandRunner;
// Build the runner with an application and root executable name.
$runner = new CommandRunner(new Application(dirname(__DIR__) . '/config'), 'cake');
exit($runner->run($argv));
+62
View File
@@ -0,0 +1,62 @@
{
"name": "cakephp/app",
"description": "CakePHP skeleton app",
"homepage": "https://cakephp.org",
"type": "project",
"license": "MIT",
"require": {
"php": ">=7.0",
"ext-gd": "*",
"ext-mbstring": "*",
"cakephp/cakephp": "3.8.*",
"cakephp/migrations": "^2.0.0",
"cakephp/plugin-installer": "^1.0",
"dompdf/dompdf": "^0.8.3",
"josegonzalez/dotenv": "3.*",
"mobiledetect/mobiledetectlib": "2.*",
"mpdf/mpdf": "^8.0",
"paragonie/random_compat": "^1.4|^2.0",
"parsecsv/php-parsecsv": "^1.1",
"phpmailer/phpmailer": "^6.0",
"phpoffice/phpspreadsheet": "^1.8"
},
"require-dev": {
"cakephp/bake": "^1.9.0",
"cakephp/cakephp-codesniffer": "^3.0",
"cakephp/debug_kit": "^3.2",
"psy/psysh": "@stable"
},
"suggest": {
"markstory/asset_compress": "An asset compression plugin which provides file concatenation and a flexible filter system for preprocessing and minification.",
"dereuromark/cakephp-ide-helper": "After baking your code, this keeps your annotations in sync with the code evolving from there on for maximum IDE and PHPStan compatibility.",
"phpunit/phpunit": "Allows automated tests to be run without system-wide install.",
"cakephp/cakephp-codesniffer": "Allows to check the code against the coding standards used in CakePHP."
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"App\\Test\\": "tests/",
"Cake\\Test\\": "vendor/cakephp/cakephp/tests/"
}
},
"scripts": {
"post-install-cmd": "App\\Console\\Installer::postInstall",
"post-create-project-cmd": "App\\Console\\Installer::postInstall",
"post-autoload-dump": "Cake\\Composer\\Installer\\PluginInstaller::postAutoloadDump",
"check": [
"@test",
"@cs-check"
],
"cs-check": "phpcs --colors -p --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
"cs-fix": "phpcbf --colors --standard=vendor/cakephp/cakephp-codesniffer/CakePHP ./src ./tests",
"test": "phpunit --colors=always"
},
"prefer-stable": true,
"config": {
"sort-packages": true
}
}
Generated
+3612
View File
File diff suppressed because it is too large. Load diff
+155
View File
@@ -0,0 +1,155 @@
<?php
return [
'debug' => false,
'Release' => "2.4.165",
'App' => [
'namespace' => 'App',
'encoding' => env('APP_ENCODING', 'UTF-8'),
'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_US'),
'defaultTimezone' => env('APP_DEFAULT_TIMEZONE', 'UTC'),
'base' => false,
'dir' => 'src',
'webroot' => 'webroot',
'wwwRoot' => WWW_ROOT,
'imageBaseUrl' => 'img/',
'cssBaseUrl' => 'css/',
'jsBaseUrl' => 'js/',
'paths' => [
'plugins' => [ROOT . DS . 'plugins' . DS],
'templates' => [APP . 'Template' . DS],
'locales' => [APP . 'Locale' . DS],
],
],
'Security' => [
'salt' => env('SECURITY_SALT', '2ae8d3f453c9f6a290a2fcdb173be1cf4acc871c6e656411c82aa331bbfefc18'),
],
'Asset' => [
// 'timestamp' => true,
],
'Cache' => [
'default' => [
'className' => 'File',
'path' => CACHE,
'url' => env('CACHE_DEFAULT_URL', null),
],
'_cake_core_' => [
'className' => 'File',
'prefix' => 'myapp_cake_core_',
'path' => CACHE . 'persistent/',
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKECORE_URL', null),
],
'_cake_model_' => [
'className' => 'File',
'prefix' => 'myapp_cake_model_',
'path' => CACHE . 'models/',
'serialize' => true,
'duration' => '+1 years',
'url' => env('CACHE_CAKEMODEL_URL', null),
],
],
'Error' => [
'errorLevel' => E_ALL,
'exceptionRenderer' => 'Cake\Error\ExceptionRenderer',
'skipLog' => [],
'log' => true,
'trace' => true,
],
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',
'username' => 'supersam_apps',
'password' => 'Ft8wV%e3Fre#ZaH-nJ',
'database' => 'supersam_apps',
'encoding' => 'utf8',
'timezone' => 'UTC',
'flags' => [],
'cacheMetadata' => true,
'log' => false,
'quoteIdentifiers' => true,
'url' => env('DATABASE_URL', null),
],
],
'Log' => [
'debug' => [
'className' => 'Cake\Log\Engine\FileLog',
'path' => LOGS,
'file' => 'debug',
'url' => env('LOG_DEBUG_URL', null),
'scopes' => false,
'levels' => ['notice', 'info', 'debug'],
],
'error' => [
'className' => 'Cake\Log\Engine\FileLog',
'path' => LOGS,
'file' => 'error',
'url' => env('LOG_ERROR_URL', null),
'scopes' => false,
'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
],
'queries' => [
'className' => 'Cake\Log\Engine\FileLog',
'path' => LOGS,
'file' => 'queries',
'url' => env('LOG_QUERIES_URL', null),
'scopes' => ['queriesLog'],
],
],
'Session' => [
'defaults' => 'php',
'ini' => [
'session.cookie_lifetime' => 18*60*60, //18h
'session.cookie_httponly' => true
],
'timeout' => 12 * 60 //12h
],
'mpdfPageParams' => [
'mode' => 'utf-8',
'format' => 'A4',
'default_font_size' => 0,
'default_font' => 'arial',
'margin_left' => 15,
'margin_right' => 15,
'margin_top' => 17,
'margin_bottom' => 30
],
'smsMinimalBalance' => 50,
"encrypt" => [
"key" => 'fsjlksf$#%WEtfvV34v59fHgi3rs0gBB<T58903--.we8934W$w3',
"cipher" => "AES-128-CBC",
"options" => OPENSSL_RAW_DATA,
"as_binary" => true
],
'ftpParams' => [
'ip' => '188.191.27.165',
'port' => 53530,
'username' => 'soronorus',
'password' => '5#NqtYB;XOC;!wAtp',
'acquiring24Out' => '/supersam24.ru/acquiring-in',
'acquiring24In' => '/supersam24.ru/acquiring-out',
'acquiring24Refund' => '/supersam24.ru/refund',
]
];
+243
View File
@@ -0,0 +1,243 @@
<?php
require __DIR__ . '/paths.php';
require CORE_PATH . 'config' . DS . 'bootstrap.php';
use Cake\Cache\Cache;
use Cake\Console\ConsoleErrorHandler;
use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Core\Configure\Engine\PhpConfig;
use Cake\Core\Plugin;
use Cake\Database\Type;
use Cake\Datasource\ConnectionManager;
use Cake\Error\ErrorHandler;
use Cake\Http\ServerRequest;
use Cake\Log\Log;
use Cake\Mailer\Email;
use Cake\ORM\TableRegistry;
use Cake\Utility\Inflector;
use Cake\Utility\Security;
try {
Configure::config('default', new PhpConfig());
Configure::load('app', 'default', false);
} catch (\Exception $e) {
exit($e->getMessage() . "\n");
}
if (Configure::read('debug')) {
Configure::write('Cache._cake_model_.duration', '+2 minutes');
Configure::write('Cache._cake_core_.duration', '+2 minutes');
// disable router cache during development
Configure::write('Cache._cake_routes_.duration', '+2 seconds');
}
date_default_timezone_set('Europe/Moscow');
mb_internal_encoding(Configure::read('App.encoding'));
ini_set('intl.default_locale', Configure::read('App.defaultLocale'));
$isCli = PHP_SAPI === 'cli';
if ($isCli) {
(new ConsoleErrorHandler(Configure::read('Error')))->register();
} else {
(new ErrorHandler(Configure::read('Error')))->register();
}
if ($isCli) {
require __DIR__ . '/bootstrap_cli.php';
}
if (!Configure::read('App.fullBaseUrl')) {
$s = null;
if (env('HTTPS')) {
$s = 's';
}
$httpHost = env('HTTP_HOST');
if (isset($httpHost)) {
Configure::write('App.fullBaseUrl', 'http' . $s . '://' . $httpHost);
}
unset($httpHost, $s);
}
Cache::setConfig(Configure::consume('Cache'));
ConnectionManager::setConfig(Configure::consume('Datasources'));
//Email::setConfigTransport(Configure::consume('EmailTransport'));
//Email::setConfig(Configure::consume('Email'));
Log::setConfig(Configure::consume('Log'));
Security::setSalt(Configure::consume('Security.salt'));
ServerRequest::addDetector('mobile', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isMobile();
});
ServerRequest::addDetector('tablet', function ($request) {
$detector = new \Detection\MobileDetect();
return $detector->isTablet();
});
Type::build('time')
->useImmutable();
Type::build('date')
->useImmutable();
Type::build('datetime')
->useImmutable();
Type::build('timestamp')
->useImmutable();
require_once(ROOT . DS . 'vendor' . DS . 'dbf' . DS . 'DBF.class.php');
require_once(ROOT . DS . 'vendor' . DS . 'sms' . DS . 'sms.ru.php');
require_once(ROOT . DS . 'vendor' . DS . 'browser_detection' . DS . 'BrowserDetection.php');
//require_once(ROOT . DS . 'vendor' . DS . 'mpdf' . DS . 'mpdf.php');
function cmpDetailsByBothSides($a, $b) {
if (max($a->width, $a->height) != max($b->width, $b->height)) {
return (max($a->width, $a->height) > max($b->width, $b->height)) ? -1 : 1;
} else {
if (min($a->width, $a->height) != min($b->width, $b->height)) {
return (min($a->width, $a->height) > min($b->width, $b->height)) ? -1 : 1;
} else {
if ($a->height != $b->height) {
return ($a->height > $b->height ) ? -1 : 1;
} else {
if ($a->width == $b->width ) {
return 0;
}
return ($a->width > $b->width ) ? -1 : 1;
}
}
}
}
function cmpDetailsByBothSidesReversed($a, $b) {
if (max($a->width, $a->height) != max($b->width, $b->height)) {
return (max($a->width, $a->height) > max($b->width, $b->height)) ? 1 : -1;
} else {
if (min($a->width, $a->height) != min($b->width, $b->height)) {
return (min($a->width, $a->height) > min($b->width, $b->height)) ? 1 : -1;
} else {
if ($a->height != $b->height) {
return ($a->height > $b->height ) ? 1 : -1;
} else {
if ($a->width == $b->width ) {
return 0;
}
return ($a->width > $b->width ) ? 1 : -1;
}
}
}
}
function cmpDetailsByWidth($a, $b) {
if ($a->width != $b->width) {
return ($a->width > $b->width ) ? -1 : 1;
} else {
return 0;
}
}
function cmpDetailsByWidthReversed($a, $b) {
if ($a->width != $b->width) {
return ($a->width > $b->width ) ? 1 : -1;
} else {
return 0;
}
}
function cmpDetailsByHeight($a, $b) {
if ($a->height != $b->height) {
return ($a->height > $b->height ) ? -1 : 1;
} else {
return 0;
}
}
function cmpDetailsByHeightReversed($a, $b) {
if ($a->height != $b->height) {
return ($a->height > $b->height ) ? 1 : -1;
} else {
return 0;
}
}
function cmpDetailsByBothSidesForToTop($a, $b) {
if ($a->height != $b->height) {
return ($a->height > $b->height ) ? -1 : 1;
} else {
if ($a->width != $b->width) {
return ($a->width > $b->width ) ? -1 : 1;
} else {
return 0;
}
}
}
if (!function_exists('mb_ucfirst') && function_exists('mb_substr')) {
function mb_ucfirst($string) {
$string = mb_strtoupper(mb_substr($string, 0, 1)) . mb_substr($string, 1);
return $string;
}
}
function toFixed2($num) {
return str_replace(".00", "", number_format(ceil($num * 10000) / 10000, 2, ".", ""));
}
function toFixed2Price($num) {
return number_format(str_replace(" ", "", $num), 2, ".", " ");
}
function cleanupPhone($phone) {
return str_replace(["+", "-", " ", "(", ")"], "", $phone);
}
function formatPhone($phone) {
return "+7 (" . substr($phone, 1, 3) . ") " . substr($phone, 4, 3) . "-" . substr($phone, 7, 2) . "-" . substr($phone, 9, 2);
}
function cleanupEmail($email) {
return str_replace([" "], "", $email);
}
function br2nl($str) {
return preg_replace('/\<br(\s*)?\/?\>/ui', "\r\n", $str);
}
function cleanupDetailLabel($label) {
return preg_replace("/_{2,}/", "_", preg_replace("/[^\w\d\\*]/ui", "_" , $label));
}
function cleanupDetailLabel2($label) {
return preg_replace("/_{2,}/", "_", preg_replace("/[^\w\d\\*\-]/ui", "_" , $label));
}
function counterPluralForm($number, $before, $after, $echoResults = false ) {
$cases = array(2,0,1,1,1,2);
$result = $before[($number % 100 > 4 && $number % 100 < 20) ? 2 : $cases[min($number % 10, 5)]] . ' ' . $number . ' ' . $after[($number % 100 > 4 && $number % 100 < 20) ? 2 : $cases[min($number % 10, 5)]];
if ($echoResults) {
echo $result;
} else {
return $result;
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
use App\Application;
use Cake\Core\Configure;
use Cake\Core\Exception\MissingPluginException;
use Cake\Core\Plugin;
Configure::write('Log.error.file', 'cli-error');
try {
Application::addPlugin('Bake');
} catch (MissingPluginException $e) {
}
Application::addPlugin('Migrations');
function isLocked($lock_file) {
# If lock file exists, check if stale. If exists and is not stale, return TRUE
# Else, create lock file and return FALSE.
if( file_exists( $lock_file ) ) {
# check if it's stale
$lockingPID = trim( file_get_contents( $lock_file ) );
# Get all active PIDs.
$pids = explode( "\n", trim( `ps -e | awk '{print $1}'` ) );
# If PID is still active, return true
if( in_array( $lockingPID, $pids ) ) return true;
# Lock-file is stale, so kill it. Then move on to re-creating it.
//echo "Removing stale lock file.\n";
unlink( $lock_file );
}
file_put_contents( $lock_file, getmypid() . "\n" );
return false;
}
+85
View File
@@ -0,0 +1,85 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license MIT License (https://opensource.org/licenses/mit-license.php)
*/
/**
* Use the DS to separate the directories in other defines
*/
if (!defined('DS')) {
define('DS', DIRECTORY_SEPARATOR);
}
/**
* These defines should only be edited if you have cake installed in
* a directory layout other than the way it is distributed.
* When using custom settings be sure to use the DS and do not add a trailing DS.
*/
/**
* The full path to the directory which holds "src", WITHOUT a trailing DS.
*/
define('ROOT', dirname(__DIR__));
/**
* The actual directory name for the application directory. Normally
* named 'src'.
*/
define('APP_DIR', 'src');
/**
* Path to the application's directory.
*/
define('APP', ROOT . DS . APP_DIR . DS);
/**
* Path to the config directory.
*/
define('CONFIG', ROOT . DS . 'config' . DS);
/**
* File path to the webroot directory.
*/
define('WWW_ROOT', ROOT . DS . 'webroot' . DS);
/**
* Path to the tests directory.
*/
define('TESTS', ROOT . DS . 'tests' . DS);
/**
* Path to the temporary files directory.
*/
define('TMP', ROOT . DS . 'tmp' . DS);
/**
* Path to the logs directory.
*/
define('LOGS', ROOT . DS . 'logs' . DS);
/**
* Path to the cache files directory. It can be shared between hosts in a multi-server setup.
*/
define('CACHE', TMP . 'cache' . DS);
/**
* The absolute path to the "cake" directory, WITHOUT a trailing DS.
*
* CakePHP should always be installed with composer, so look there.
*/
define('CAKE_CORE_INCLUDE_PATH', ROOT . DS . 'vendor' . DS . 'cakephp' . DS . 'cakephp');
/**
* Path to the cake directory.
*/
define('CORE_PATH', CAKE_CORE_INCLUDE_PATH . DS);
define('CAKE', CORE_PATH . 'src' . DS);
+19
View File
@@ -0,0 +1,19 @@
<?php
use Cake\Core\Plugin;
use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
use Cake\Routing\Route\DashedRoute;
Router::defaultRouteClass(DashedRoute::class);
Router::scope('/', function (RouteBuilder $routes) {
$routes->connect('/', ['controller' => 'OrderPayment', 'action' => 'display']);
$routes->connect('/delivery', ['controller' => 'Delivery', 'action' => 'display']);
$routes->connect('/delivery/:city', ['controller' => 'Delivery', 'action' => 'display'], ['city' => '\w+', 'pass' => ['city']]);
$routes->fallbacks(DashedRoute::class);
});
+18
View File
@@ -0,0 +1,18 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE i18n (
id int NOT NULL auto_increment,
locale varchar(6) NOT NULL,
model varchar(255) NOT NULL,
foreign_key int(10) NOT NULL,
field varchar(255) NOT NULL,
content text,
PRIMARY KEY (id),
UNIQUE INDEX I18N_LOCALE_FIELD(locale, model, foreign_key, field),
INDEX I18N_FIELD(model, foreign_key, field)
);
+15
View File
@@ -0,0 +1,15 @@
# Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
#
# Licensed under The MIT License
# For full copyright and license information, please see the LICENSE.txt
# Redistributions of files must retain the above copyright notice.
# MIT License (https://opensource.org/licenses/mit-license.php)
CREATE TABLE `sessions` (
`id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
`created` datetime DEFAULT CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- optional, requires MySQL 5.6.5+
`data` blob DEFAULT NULL, -- for PostgreSQL use bytea instead of blob
`expires` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+427
View File
@@ -0,0 +1,427 @@
DROP TABLE IF EXISTS `cut_borders`;
CREATE TABLE `cut_borders` (
`id` int(11) NOT NULL,
`label` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`short_label` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cut_borders` (`id`, `label`, `short_label`) VALUES
(1, 'ORIGIN Рехау 087Т дуб сонома', 'Р 087Т'),
(2, 'ORIGIN Рехау 14650 крем', 'Р 14650'),
(3, 'ORIGIN Рехау 200V венге темный', 'Р 200V'),
(4, 'ORIGIN Рехау 226W акация лэйклэнд', 'Р 226W'),
(5, 'ORIGIN Рехау 4163 Орех', 'Р 4163'),
(6, 'ORIGIN Рехау 4214 Вишня', 'Р 4214'),
(7, 'ORIGIN Рехау 4526 бук', 'Р 4526'),
(8, 'ORIGIN Рехау 463W ноче экко', 'Р 463W'),
(9, 'ORIGIN Рехау 4919 береза', 'Р 4919'),
(10, 'ORIGIN Рехау 553W вудлайн кремовый', 'Р 553W'),
(11, 'ORIGIN Рехау 6240', 'Р 6240'),
(12, 'ORIGIN Рехау 77002 капучино', 'Р 77002'),
(13, 'ORIGIN Рехау 848 W капуччино т-36', 'Р 848W'),
(14, 'ORIGIN Рехау 849W дуб дымчатый', 'Р 849W'),
(15, 'ORIGIN Рехау 91470 Ш. Б./ORIGIN 77039', 'Р 91470-O 77039'),
(16, 'ORIGIN Рехау 9991 Клен', 'Р 9991'),
(17, 'ORIGINAL Рехау 710 W Маслина Севилья', 'Р 710W'),
(18, 'TREND RAL9010 / ORIGIN Белый структура', 'T RAL9010'),
(19, 'TREND/ORIGINAL Рехау 599V Орех', 'T-Р 599V'),
(20, 'TREND Рехау 1147W Дуб серый', 'T-Р 1147W'),
(21, 'TREND Рехау 1416W Твист светлый', 'T-Р1416W'),
(22, 'TREND Рехау 15504 розовый', 'T-Р 15504'),
(23, 'TREND Рехау 2127W Ясень Шимо темный', 'T-Р 2127W'),
(24, 'TREND Рехау 2128W Ясень Шимо светлый 454', 'T-Р 2128W'),
(25, 'TREND Рехау 2459W Орех темный Селект', 'T-Р 2459W'),
(26, 'TREND Рехау 322W дуб феррара', 'T-Р 322W'),
(27, 'TREND Рехау 589V орех аида натуральный', 'T-Р 589V'),
(28, 'TREND Рехау 69164 желто-зеленый', 'T-Р 69164'),
(29, 'TREND Рехау 69165 Голубой', 'T-Р 69165'),
(30, 'TREND Рехау 76399 Ваниль', 'T-Р 76399'),
(31, 'TREND Рехау 76401 Черный/ORIGIN 601', 'T-Р 76401/O-601'),
(32, 'TREND Рехау 76962 желтый', 'T-Р 76962'),
(33, 'TREND Рехау 76973 черный графит', 'T-Р 76973'),
(34, 'TREND Рехау 76989 светло-серый', 'T-Р 76989'),
(35, 'TREND Рехау 76996 оранжевый', 'T-Р 76996'),
(36, 'TREND Рехау 77039 белый гладкий', 'T-Р 77039'),
(37, 'TREND Рехау 78243 Ирис', 'T-Р 78243'),
(38, 'TREND Рехау 812V Дуб Шато серый', 'T-Р 812V'),
(39, 'TREND/ORIGINAL Рехау 6071 дуб выбеленный', 'T/O-Р 6071'),
(40, 'TREND/ORIGINAL Рехау 1309W сосна лоредо', 'T/O-Р 1309W'),
(41, 'TREND/ORIGINAL Рехау 7064 дуб молочный', 'T/O-Р 7064');
DROP TABLE IF EXISTS `cut_border_available_dimensions`;
CREATE TABLE `cut_border_available_dimensions` (
`id` int(11) NOT NULL,
`border_id` int(11) DEFAULT NULL,
`border_dimension_id` int(11) NOT NULL,
`price` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cut_border_available_dimensions` (`id`, `border_id`, `border_dimension_id`, `price`) VALUES
(1, 1, 1, '8.10'),
(2, 1, 4, '41.80'),
(3, 13, 3, '22.10'),
(4, 14, 1, '8.10'),
(5, 14, 3, '22.10'),
(6, 14, 4, '31.70'),
(7, 3, 1, '8.10'),
(8, 3, 3, '22.10'),
(9, 3, 4, '31.70'),
(10, 7, 1, '8.10'),
(11, 7, 3, '22.10'),
(12, 7, 4, '31.70'),
(13, 10, 1, '8.10'),
(14, 10, 3, '22.10'),
(15, 10, 4, '31.70'),
(16, 41, 1, '8.10'),
(17, 41, 3, '22.10'),
(18, 41, 4, '31.70'),
(22, 4, 1, '8.10'),
(23, 4, 2, '28.09'),
(24, 4, 4, '31.70'),
(25, 9, 1, '8.10'),
(26, 9, 2, '28.09'),
(27, 9, 4, '31.70'),
(28, 6, 1, '8.10'),
(29, 6, 3, '28.09'),
(30, 6, 4, '31.70'),
(31, 16, 1, '8.10'),
(32, 16, 3, '28.09'),
(33, 16, 4, '31.70'),
(34, 2, 1, '6.20'),
(35, 2, 3, '20.50'),
(36, 2, 4, '24.90'),
(37, 12, 1, '6.20'),
(38, 12, 3, '20.50'),
(39, 12, 4, '24.90'),
(40, 15, 1, '6.20'),
(41, 15, 3, '20.50'),
(42, 15, 4, '24.90'),
(43, 30, 1, '7.30'),
(44, 30, 3, '22.00'),
(45, 30, 4, '30.00'),
(46, 34, 1, '7.30'),
(47, 34, 3, '22.00'),
(48, 34, 4, '30.00'),
(49, 35, 1, '7.30'),
(50, 35, 3, '22.00'),
(51, 35, 4, '30.00'),
(52, 36, 1, '7.30'),
(53, 36, 3, '22.00'),
(54, 36, 4, '30.00'),
(55, 26, 1, '10.30'),
(56, 26, 3, '25.90'),
(57, 26, 4, '41.80'),
(58, 38, 1, '10.30'),
(59, 38, 3, '25.90'),
(60, 38, 4, '41.80'),
(61, 40, 1, '10.30'),
(62, 40, 3, '25.90'),
(63, 40, 4, '31.70'),
(64, 5, 1, '7.00'),
(65, 5, 3, '22.10'),
(66, 5, 4, '31.70'),
(67, 27, 1, '10.30'),
(68, 27, 4, '31.70'),
(69, 23, 1, '10.30'),
(70, 23, 4, '41.80'),
(71, 24, 1, '10.30'),
(72, 24, 4, '41.80'),
(73, 20, 1, '10.30'),
(74, 20, 4, '41.80'),
(75, 21, 1, '10.30'),
(76, 21, 4, '41.80'),
(77, 25, 1, '10.30'),
(78, 25, 4, '41.80'),
(79, 39, 1, '10.30'),
(80, 39, 4, '31.70'),
(81, 33, 1, '7.30'),
(82, 33, 4, '30.00'),
(83, 22, 1, '7.30'),
(84, 22, 4, '30.00'),
(85, 28, 1, '7.30'),
(86, 28, 4, '30.00'),
(87, 29, 1, '7.30'),
(88, 29, 4, '30.00'),
(89, 32, 1, '7.30'),
(90, 32, 4, '30.00'),
(91, 37, 1, '7.30'),
(92, 37, 4, '30.00'),
(93, 19, 1, '8.10'),
(94, 19, 4, '41.80'),
(95, 31, 3, '22.00'),
(96, 31, 4, '24.90'),
(97, 11, 2, '20.00'),
(98, 11, 4, '31.70'),
(99, 17, 2, '28.09'),
(100, 17, 4, '31.70'),
(101, 8, 3, '28.09'),
(102, 18, 3, '25.90'),
(103, 18, 1, '8.10'),
(104, 18, 4, '31.70');
DROP TABLE IF EXISTS `cut_border_dimensions`;
CREATE TABLE `cut_border_dimensions` (
`id` int(11) NOT NULL,
`label` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`short_label` varchar(16) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`gluing_price` decimal(10,2) DEFAULT NULL,
`2dp_id` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cut_border_dimensions` (`id`, `label`, `short_label`, `gluing_price`, `2dp_id`) VALUES
(1, '19*0,4 ПВХ', '19*0,4', '18.00', '8'),
(2, '23*0,8 ПВХ', '23*0,8', '19.00', '12'),
(3, '19*1 ПВХ', '19*1', '19.00', '1'),
(4, '19*2 ПВХ', '19*2', '22.00', '2');
DROP TABLE IF EXISTS `cut_sheets`;
CREATE TABLE `cut_sheets` (
`id` int(11) NOT NULL,
`label` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`sheet_dimension_id` int(11) NOT NULL,
`default_border_id` int(11) DEFAULT NULL,
`price` decimal(10,2) NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cut_sheets` (`id`, `label`, `sheet_dimension_id`, `default_border_id`, `price`, `is_active`) VALUES
(1, 'Белая структура 605 SWA', 2, 18, '1990.00', 1),
(2, 'Белая шагрень 605 PE', 2, 15, '1990.00', 1),
(3, 'Белый 605 SG', 3, 36, '2255.00', 1),
(4, 'Белый 605 SWN', 3, 18, '2255.00', 1),
(5, 'Белый гладкий 605 SМ', 2, 15, '1990.00', 1),
(6, 'Береза 261 SWN', 3, 13, '2310.00', 1),
(7, 'Бодега 100 SWA', 2, 10, '1990.00', 1),
(8, 'Бодега 101 SWA', 2, 40, '2040.00', 1),
(9, 'Бук Бавария 124 SWA', 2, 7, '1990.00', 1),
(10, 'Бук Бавария 124 SWN', 3, 7, '2200.00', 1),
(11, 'Ваниль 630 РЕ', 3, 30, '2140.00', 1),
(12, 'Венге 175 SWA', 2, 3, '1990.00', 1),
(13, 'Венге 175 SWN', 3, 3, '2200.00', 1),
(14, 'Графит 665 РЕ', 3, 33, '2410.00', 1),
(15, 'Дуб Атланта 302 SWA', 2, 39, '2040.00', 1),
(16, 'Дуб Бонифаций 348 SWN', 3, 4, '2310.00', 1),
(17, 'Дуб Веллингтон 370 SWN', 3, 23, '2310.00', 1),
(18, 'Дуб Кантри 1007 Тиснение WL', 1, 25, '1560.00', 0),
(19, 'Дуб Килкини 7225 Тиснение WL', 1, 21, '1850.00', 0),
(20, 'Дуб молочный 315 SWA', 2, 41, '1990.00', 1),
(21, 'Дуб Сантана 322 SWN', 3, 26, '2260.00', 1),
(22, 'Дуб Саттер 369 SWN', 3, 5, '2260.00', 1),
(23, 'Дуб Сонома 325 SWA', 2, 1, '1990.00', 1),
(24, 'Дуб Сонома 325 SWN', 3, 1, '2210.00', 1),
(25, 'Дуб Сонома 351 SWN', 3, 38, '2210.00', 1),
(26, 'Дуб Шамони св.331 SWA', 2, 24, '2040.00', 1),
(27, 'Дуб Юккон 358 SWN', 3, 14, '2360.00', 1),
(28, 'Желтый 650 РЕ', 3, 32, '2720.00', 1),
(29, 'Зелёный 651 РЕ', 3, 28, '2720.00', 1),
(30, 'Капучино 806 PE', 3, 12, '2280.00', 1),
(31, 'Клен Танзау 482 SWA', 2, 16, '1990.00', 1),
(32, 'Крем 625 PE', 2, 2, '2050.00', 1),
(33, 'Лиственница Сибирская 525 SWN', 3, 10, '2310.00', 1),
(34, 'Ноче Гуарнери 408 SWA', 2, 5, '1990.00', 1),
(35, 'Ноче Экко 412 SWA', 2, 8, '1990.00', 1),
(36, 'Ольха 250 SWA', 2, 6, '2040.00', 1),
(37, 'Ольха Горная светлая 254 SWN', 3, 9, '2260.00', 1),
(38, 'Оранжевый 655 PE', 3, 35, '2720.00', 1),
(39, 'Орех Донской 202 SWA', 2, 19, '1990.00', 1),
(40, 'Орех Лион 204 SWN', 3, 27, '2210.00', 1),
(41, 'Пепел 621 PE', 2, 34, '1940.00', 1),
(42, 'Платина 801 PE', 3, 11, '2280.00', 1),
(43, 'Розовый 657 PE', 2, 22, '2165.00', 1),
(44, 'Серебро 802 PE', 3, 11, '2280.00', 1),
(45, 'Синий 652 PE', 3, 29, '2280.00', 1),
(46, 'Сиреневый 658 РЕ', 3, 37, '2410.00', 1),
(47, 'Сосна Каньон 529 SWN', 3, 10, '2360.00', 1),
(48, 'Таксус 570 SWN', 3, 27, '2310.00', 1),
(49, 'Черный 660 PE', 3, 31, '2280.00', 1),
(50, 'Эбони индийский светлый 3132 Тиснение WL', 1, 17, '1550.00', 0),
(51, 'Ясень Шимо 454 SWA светлый', 2, 24, '1990.00', 1),
(52, 'Ясень Шимо 455 SWA темный', 2, 23, '1990.00', 1),
(53, 'Ясень Рибейра 3130 Тиснение WL', 1, 20, '1600.00', 0);
DROP TABLE IF EXISTS `cut_sheet_dimensions`;
CREATE TABLE `cut_sheet_dimensions` (
`id` int(11) NOT NULL,
`width` int(11) DEFAULT NULL,
`height` int(11) NOT NULL,
`thick` int(11) NOT NULL,
`2dp_id` int(11) NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO `cut_sheet_dimensions` (`id`, `width`, `height`, `thick`, `2dp_id`, `is_active`) VALUES
(1, 2440, 1830, 16, 1, 0),
(2, 2750, 1830, 16, 1, 1),
(3, 2800, 2070, 16, 1, 1);
ALTER TABLE `cut_borders` ADD PRIMARY KEY (`id`), ADD KEY `label` (`label`);
ALTER TABLE `cut_border_available_dimensions` ADD PRIMARY KEY (`id`);
ALTER TABLE `cut_border_dimensions` ADD PRIMARY KEY (`id`);
ALTER TABLE `cut_sheets` ADD PRIMARY KEY (`id`), ADD KEY `label` (`label`);
ALTER TABLE `cut_sheet_dimensions` ADD PRIMARY KEY (`id`);
ALTER TABLE `cut_borders` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=42;
ALTER TABLE `cut_border_available_dimensions` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=105;
ALTER TABLE `cut_border_dimensions` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
ALTER TABLE `cut_sheets` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=54;
ALTER TABLE `cut_sheet_dimensions` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
DROP TABLE IF EXISTS `cut_orders`;
CREATE TABLE `cut_orders` (
`id` int(11) NOT NULL,
`order_num` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`email` varchar(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`comment` text COLLATE utf32_unicode_ci,
`is_rotate_allowed` tinyint(1) NOT NULL DEFAULT '1',
`sheet_data` text CHARACTER SET utf8,
`details` text CHARACTER SET utf8 NOT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf32 COLLATE=utf32_unicode_ci;
ALTER TABLE `cut_orders` ADD PRIMARY KEY (`id`);
ALTER TABLE `cut_orders` MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `cut_orders` ADD `order_type` ENUM('order','print') NOT NULL DEFAULT 'order' AFTER `id`, ADD `branch_id` INT NULL DEFAULT NULL AFTER `order_type`;
CREATE USER 'supersam_cut'@'%' IDENTIFIED WITH mysql_native_password AS 'Ft8wV%e3Fre#ZaH-nJS';GRANT USAGE ON *.* TO 'supersam_cut'@'%' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;GRANT ALL PRIVILEGES ON `supersam_cut`.* TO 'supersam_cut'@'%';
ALTER USER 'supersam_cut'@'%' IDENTIFIED BY 'Ft8wV%e3Fre#ZaH-nJ';
ALTER TABLE `orders` ADD `print_url` VARCHAR(255) NULL DEFAULT NULL AFTER `taken_date`;
ALTER TABLE `sheets` ADD `image` VARCHAR(100) NULL DEFAULT NULL AFTER `is_active`, ADD `thumb` VARCHAR(100) NULL DEFAULT NULL AFTER `image`;
update borders set image = concat(id, '.jpg');
update sheets set image = concat(id, '.jpg');
update sheets set thumb = concat(id, '.jpg');
ALTER TABLE `users` ADD `email` VARCHAR(128) NOT NULL AFTER `name`;
ALTER TABLE `orders` ADD `html_letter` TEXT NULL AFTER `print_url`;
ALTER TABLE `orders` ADD `text_letter` TEXT NULL AFTER `html_letter`;
ALTER TABLE `orders` ADD `statistics` TEXT NULL DEFAULT NULL AFTER `text_letter`;
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, NULL, 'notifySaveOrderDataMessage', 'Заказ был успешно сохранен.', 'labels');
ALTER TABLE `orders` ADD `price` DECIMAL(10,2) NOT NULL DEFAULT '0' AFTER `details`;
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'Сообщение об ошибке по умолчанию', 'defaultErrorMessage', 'Произошла ошибка. Пожалуйста, проверьте данные, убедитесь, что вы авторизованы и повторите действие.', 'labels');
ALTER TABLE `orders` ADD `merge_similar_details` TINYINT(1) NOT NULL DEFAULT '1' AFTER `is_rotate_allowed`;
ALTER TABLE `branches` ADD `is_deleted` TINYINT(1) NOT NULL DEFAULT '0' AFTER `order_notify_manager_sms`;
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'Сообщение об ошибке загрузки данных при построении раскроя', 'dataLoadingError', 'Произошла ошибка при загрузке данных. Пожалуйста, повторите позже.', 'labels');
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'Сообщение об ошибке при отправке заказа менеджеру', 'orderSendManagerError', 'Произошла ошибка при отправке вашего заказа менеджеру. Пожалуйста, повторите позже.', 'labels');
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'Сообщение об ошибке при сохранении заказа менеджером', 'orderSaveManagerError', 'Произошла ошибка при сохранении вашего заказа. Пожалуйста, повторите позже.', 'labels');
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'Сообщение о необходимости дождаться окончания расчета раскроя при попытке отправить заказ', 'orderSendManagerCutIsActive', 'Пожалуйста, дождитесь окончания расчета перед печатью заказа или отправкой менеджеру.', 'labels');
ALTER TABLE `orders` ADD `order_cut_results` TEXT NULL DEFAULT NULL AFTER `updated_by_manager`;
ALTER TABLE `orders` DROP `is_rotate_allowed`;
ALTER TABLE `orders` ADD INDEX( `order_type`, `created`, `manager_id`);
ALTER TABLE `orders` ADD INDEX( `order_type`, `branch_id`, `created`, `manager_id`);
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'НА РАСПИЛ УКАЗЫВАЮТСЯ ГОТОВЫЕ РАЗМЕРЫ ДЕТАЛЕЙ С УЧЕТОМ КРОМКИ', 'detailWithBordersWarning', 'НА РАСПИЛ УКАЗЫВАЮТСЯ ГОТОВЫЕ РАЗМЕРЫ ДЕТАЛЕЙ С УЧЕТОМ КРОМКИ', 'labels');
-------------------------
delete from cut_settings where `group` = 'updates' or `group` = 'users';
delete from settings where `group` != 'updates' and `group` != 'users';
ALTER TABLE `cut_settings` CHANGE `group` `group` ENUM('prices','borders','common','labels','sheets','sheetDimensions','borderDimensions','branches','groovePrices','cornerPrices','grooveDefaults','cornerDefaults') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `settings` CHANGE `group` `group` ENUM('users','updates','cut_updates') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `users` ADD `available_apps` VARCHAR(256) NOT NULL AFTER `is_active`;
UPDATE `users` SET `available_apps` = '[\"cut\",\"calcDoors\"]' WHERE 1;
ALTER TABLE `settings` CHANGE `group` `group` ENUM('users','updates','cut_updates','common') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `cut_orders` ADD `address` VARCHAR(256) NULL DEFAULT NULL AFTER `comment`;
ALTER TABLE `calc_doors_orders` ADD `ready_date` DATETIME NULL AFTER `order_calc_results`;
ALTER TABLE `calc_doors_orders` ADD `actual_price` DECIMAL(15,2) NULL DEFAULT NULL AFTER `ready_date`;
ALTER TABLE `cut_orders` CHANGE `print_url` `view_order_url` VARCHAR(255) CHARACTER SET utf32 COLLATE utf32_unicode_ci NULL DEFAULT NULL;
ALTER TABLE `calc_doors_orders` CHANGE `print_url` `view_order_url` VARCHAR(255) CHARACTER SET utf32 COLLATE utf32_unicode_ci NULL DEFAULT NULL;
ALTER TABLE `users` ADD `last_active` DATETIME NULL DEFAULT NULL AFTER `available_apps`;
ALTER TABLE `users` ADD `logging_attempts` INT NOT NULL DEFAULT '0' AFTER `last_active`;
ALTER TABLE `sms_subscribes` CHANGE `created_by` `createdBy` INT(11) NULL DEFAULT NULL;
ALTER TABLE `sms_subscribes` CHANGE `queued_by` `queuedBy` INT(11) NULL DEFAULT NULL;
ALTER TABLE `sms_subscribes` CHANGE `stopped_by` `stoppedBy` INT(11) NULL DEFAULT NULL;
ALTER TABLE `cut_orders` ADD INDEX( `order_type`, `name`, `created`);
-- 2019-09-18
ALTER TABLE `calc_doors_orders` ADD `price` DECIMAL(15,2) NULL DEFAULT NULL AFTER `ready_date`;
ALTER TABLE `cut_orders` ADD `price` DECIMAL(15,2) NULL DEFAULT NULL AFTER `order_cut_results`;
ALTER TABLE `calc_doors_orders` ADD INDEX( `order_type`, `name`, `created`);
-- 2091-09-27
ALTER TABLE `sms_subscribe_recipients` CHANGE `smsSubscribeId` `subscribeId` INT(11) NOT NULL;
ALTER TABLE `sms_subscribes` CHANGE `sentSmsCount` `sentRecipientsCount` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `sms_subscribes` CHANGE `sentRecipientsCount` `sentCount` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `email_subscribe_attachments` ADD `mime` VARCHAR(64) NULL AFTER `filesize`;
-- 2010-01-21
ALTER TABLE `users` CHANGE `role` `role` ENUM('admin','manager','head_of_transport','head_of_warehouse') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'manager';
-- 2010-01-30
ALTER TABLE `cut_sheets` ADD `sheetIdFor1C` VARCHAR(36) NULL DEFAULT NULL AFTER `thumb`;
ALTER TABLE `cut_sheets` ADD `quantityIn1C` INT NOT NULL DEFAULT '0' AFTER `sheetIdFor1C`
ALTER TABLE `cut_sheets` CHANGE `quantityIn1C` `quantityIn1C` INT(11) NULL DEFAULT NULL;
UPDATE `cut_sheets` SET `quantityIn1C` = NULL WHERE 1;
ALTER TABLE `sms_orders_ready` CHANGE `sentDate` `sentDate` DATETIME NULL DEFAULT NULL;
-- 2010-04-06
ALTER TABLE `calc_doors` ADD `colorNapravlyaushayaVerh` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL AFTER `color`,
ADD `colorNapravlyaushayaNiz` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL AFTER `colorNapravlyaushayaVerh`;
update calc_doors set colorNapravlyaushayaVerh = color, colorNapravlyaushayaNiz = color where 1;
ALTER TABLE `calc_doors_settings` CHANGE `group` `group` ENUM('prices','common','labels','prices_aristo', 'limits_aristo') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
INSERT INTO `calc_doors_settings` (`label`, `key`, `value`, `group`) VALUES
('Минимальная высота проёма, мм', 'minDoorHeight', '740', 'limits_aristo'),
('Максимальная высота проёма, мм', 'maxDoorHeight', '2700', 'limits_aristo'),
('Минимальная ширина двери, мм', 'minDoorWidth', '450', 'limits_aristo'),
('Максимальная ширина двери, мм', 'maxDoorWidth', '900', 'limits_aristo');
ALTER TABLE `settings` CHANGE `group` `group` ENUM('users','updates','cut_updates','global','common','global_labels','calcDoors_updates','ordersInfo_updates') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `settings` CHANGE `group` `group` ENUM('users','updates','cut_updates','global','common','global_labels','calcDoors_updates','ordersInfo_updates','order_payment') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
INSERT INTO `settings` (`id`, `label`, `key`, `value`, `group`) VALUES (NULL, 'API ключ яндекс карт', 'yandexMapApiKey', '96000e94-bf7a-4e74-9180-068e83944d02', 'global');
ALTER TABLE `delivery` ADD `shopShippingId` VARCHAR(32) NULL DEFAULT NULL AFTER `minimalCost`;
UPDATE `delivery` SET `shopShippingId` = 'carrier_yalta' WHERE `delivery`.`id` = 7;
UPDATE `delivery` SET `shopShippingId` = 'carrier_sev' WHERE `delivery`.`id` = 6;
ALTER TABLE `delivery` ADD `shopOrderMinTotal` DECIMAL(10,2) NULL DEFAULT NULL AFTER `shopShippingId`;
UPDATE `delivery` SET `shopOrderMinTotal` = 500 WHERE `delivery`.`id` = 7;
UPDATE `delivery` SET `shopOrderMinTotal` = 500 WHERE `delivery`.`id` = 6;
-- SELECT * FROM `oc_setting` WHERE `code` = 'carrier_sev' or `code` = 'carrier_yalta'
ALTER TABLE `sms_orders_ready` ADD `smsType` ENUM('order','other') NOT NULL AFTER `statusTime`;
UPDATE `sms_orders_ready` SET `smsType` = 'order' WHERE 1;
ALTER TABLE `sms_orders_ready` CHANGE `smsType` `smsType` ENUM('order','other','apps_order','supersam24_order') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
ALTER TABLE `order_payment` ADD `name` VARCHAR(256) NULL DEFAULT NULL AFTER `order_num`, ADD `summa_order_decimal` DECIMAL(10,2) NULL DEFAULT NULL AFTER `name`;
ALTER TABLE `order_payment` ADD `summa_order_decimal_paid` DECIMAL(10,2) NOT NULL DEFAULT '0' AFTER `payment`;
ALTER TABLE `order_payment` ADD `isDeleted` TINYINT(1) NOT NULL DEFAULT '0' AFTER `expiry_date`;
ALTER TABLE `orders_info` ADD `created` DATETIME NULL DEFAULT NULL AFTER `isDeleted`;
ALTER TABLE `order_payment` ADD `telephone` VARCHAR(20) NULL DEFAULT NULL AFTER `name`;
ALTER TABLE `order_payment` ADD `paid_date` DATETIME NULL DEFAULT NULL AFTER `expiry_date`;
ALTER TABLE `order_payment` ADD `domain` ENUM('apps.supersamsev.ru','supersam24.ru') NOT NULL DEFAULT 'apps.supersamsev.ru' AFTER `isDeleted`;
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
require 'webroot' . DIRECTORY_SEPARATOR . 'index.php';
+3031
View File
File diff suppressed because it is too large. Load diff
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="./tests/bootstrap.php"
>
<php>
<ini name="memory_limit" value="-1"/>
<ini name="apc.enable_cli" value="1"/>
</php>
<!-- Add any additional test suites you want to run here -->
<testsuites>
<testsuite name="app">
<directory>./tests/TestCase</directory>
</testsuite>
<!-- Add plugin test suites here. -->
</testsuites>
<!-- Setup a listener for fixtures -->
<listeners>
<listener
class="\Cake\TestSuite\Fixture\FixtureInjector"
file="./vendor/cakephp/cakephp/src/TestSuite/Fixture/FixtureInjector.php">
<arguments>
<object class="\Cake\TestSuite\Fixture\FixtureManager" />
</arguments>
</listener>
</listeners>
<!-- Ignore vendor tests in code coverage reports -->
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
<directory suffix=".php">./plugins/*/src/</directory>
</whitelist>
</filter>
</phpunit>
+52
View File
@@ -0,0 +1,52 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.3.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App;
use Cake\Core\Configure;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
/**
* Application setup class.
*
* This defines the bootstrapping logic and middleware layers you
* want to use in your application.
*/
class Application extends BaseApplication
{
/**
* Setup the middleware queue your application will use.
*
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
* @return \Cake\Http\MiddlewareQueue The updated middleware queue.
*/
public function middleware($middlewareQueue)
{
$middlewareQueue
// Catch any exceptions in the lower layers,
// and make an error page/response
->add(ErrorHandlerMiddleware::class)
// Handle plugin/theme assets like CakePHP normally does.
->add(AssetMiddleware::class)
// Add routing middleware.
->add(new RoutingMiddleware($this));
return $middlewareQueue;
}
}
+246
View File
@@ -0,0 +1,246 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\Console;
if (!defined('STDIN')) {
define('STDIN', fopen('php://stdin', 'r'));
}
use Cake\Utility\Security;
use Composer\Script\Event;
use Exception;
/**
* Provides installation hooks for when this application is installed via
* composer. Customize this class to suit your needs.
*/
class Installer
{
/**
* An array of directories to be made writable
*/
const WRITABLE_DIRS = [
'logs',
'tmp',
'tmp/cache',
'tmp/cache/models',
'tmp/cache/persistent',
'tmp/cache/views',
'tmp/sessions',
'tmp/tests'
];
/**
* Does some routine installation tasks so people don't have to.
*
* @param \Composer\Script\Event $event The composer event object.
* @throws \Exception Exception raised by validator.
* @return void
*/
public static function postInstall(Event $event)
{
$io = $event->getIO();
$rootDir = dirname(dirname(__DIR__));
static::createAppConfig($rootDir, $io);
static::createWritableDirectories($rootDir, $io);
// ask if the permissions should be changed
if ($io->isInteractive()) {
$validator = function ($arg) {
if (in_array($arg, ['Y', 'y', 'N', 'n'])) {
return $arg;
}
throw new Exception('This is not a valid answer. Please choose Y or n.');
};
$setFolderPermissions = $io->askAndValidate(
'<info>Set Folder Permissions ? (Default to Y)</info> [<comment>Y,n</comment>]? ',
$validator,
10,
'Y'
);
if (in_array($setFolderPermissions, ['Y', 'y'])) {
static::setFolderPermissions($rootDir, $io);
}
} else {
static::setFolderPermissions($rootDir, $io);
}
static::setSecuritySalt($rootDir, $io);
if (class_exists('\Cake\Codeception\Console\Installer')) {
\Cake\Codeception\Console\Installer::customizeCodeceptionBinary($event);
}
}
/**
* Create the config/app.php file if it does not exist.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createAppConfig($dir, $io)
{
$appConfig = $dir . '/config/app.php';
$defaultConfig = $dir . '/config/app.default.php';
if (!file_exists($appConfig)) {
copy($defaultConfig, $appConfig);
$io->write('Created `config/app.php` file');
}
}
/**
* Create the `logs` and `tmp` directories.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function createWritableDirectories($dir, $io)
{
foreach (static::WRITABLE_DIRS as $path) {
$path = $dir . '/' . $path;
if (!file_exists($path)) {
mkdir($path);
$io->write('Created `' . $path . '` directory');
}
}
}
/**
* Set globally writable permissions on the "tmp" and "logs" directory.
*
* This is not the most secure default, but it gets people up and running quickly.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setFolderPermissions($dir, $io)
{
// Change the permissions on a path and output the results.
$changePerms = function ($path, $perms, $io) {
// Get permission bits from stat(2) result.
$currentPerms = fileperms($path) & 0777;
if (($currentPerms & $perms) == $perms) {
return;
}
$res = chmod($path, $currentPerms | $perms);
if ($res) {
$io->write('Permissions set on ' . $path);
} else {
$io->write('Failed to set permissions on ' . $path);
}
};
$walker = function ($dir, $perms, $io) use (&$walker, $changePerms) {
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (!is_dir($path)) {
continue;
}
$changePerms($path, $perms, $io);
$walker($path, $perms, $io);
}
};
$worldWritable = bindec('0000000111');
$walker($dir . '/tmp', $worldWritable, $io);
$changePerms($dir . '/tmp', $worldWritable, $io);
$changePerms($dir . '/logs', $worldWritable, $io);
}
/**
* Set the security.salt value in the application's config file.
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @return void
*/
public static function setSecuritySalt($dir, $io)
{
$newKey = hash('sha256', Security::randomBytes(64));
static::setSecuritySaltInFile($dir, $io, $newKey, 'app.php');
}
/**
* Set the security.salt value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $newKey key to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setSecuritySaltInFile($dir, $io, $newKey, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__SALT__', $newKey, $content, $count);
if ($count == 0) {
$io->write('No Security.salt placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated Security.salt value in config/' . $file);
return;
}
$io->write('Unable to update Security.salt value.');
}
/**
* Set the APP_NAME value in a given file
*
* @param string $dir The application's root directory.
* @param \Composer\IO\IOInterface $io IO interface to write to console.
* @param string $appName app name to set in the file
* @param string $file A path to a file relative to the application's root
* @return void
*/
public static function setAppNameInFile($dir, $io, $appName, $file)
{
$config = $dir . '/config/' . $file;
$content = file_get_contents($config);
$content = str_replace('__APP_NAME__', $appName, $content, $count);
if ($count == 0) {
$io->write('No __APP_NAME__ placeholder to replace.');
return;
}
$result = file_put_contents($config, $content);
if ($result) {
$io->write('Updated __APP_NAME__ value in config/' . $file);
return;
}
$io->write('Unable to update __APP_NAME__ value.');
}
}
+195
View File
@@ -0,0 +1,195 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\Controller\Controller;
use Cake\ORM\TableRegistry;
use Cake\Http\Exception\NotFoundException;
use Cake\Core\Configure;
use Cake\Routing\Router;
use BrowserDetection;
use Cake\Utility\Hash;
class AppController extends Controller
{
public $config = null;
public function initialize() {
parent::initialize();
$this->loadComponent('RequestHandler', [
'enableBeforeRedirect' => false
]);
$this->loadComponent('Flash');
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
$browser = new BrowserDetection();
if (($browser->getName() == BrowserDetection::BROWSER_IE &&
$browser->compareVersions($browser->getVersion(), '11.0') < 0) ||
($browser->getName() == BrowserDetection::BROWSER_OPERA &&
$browser->compareVersions($browser->getVersion(), '20.0') < 0) ||
($browser->getName() == BrowserDetection::BROWSER_CHROME &&
$browser->compareVersions($browser->getVersion(), '40.0') < 0) ||
($browser->getName() == BrowserDetection::BROWSER_FIREFOX &&
$browser->compareVersions($browser->getVersion(), '50.0') < 0)
){
$this->set("isRejectedBrowser", true);
}
$request = Router::getRequest();
$controller = $request->getParam('controller');
if (in_array($controller, ["OrderPayment"])) {
$currentApp = "orderPayment";
}
$this->config = $this->getAppConfig($currentApp);
$this->set('siteTitle', $this->config['common']['siteTitle']['value']);
}
public function jsonSimpleResponse($result, $withCORS = false) {
if ($withCORS) {
$this->response->setHeader('Access-Control-Allow-Origin: *');
}
$this->set(compact('result'));
$this->RequestHandler->renderAs($this, 'json');
$this->render('/Element/json_result');
}
public function getAppConfig($app) {
$config = [];
$settings = TableRegistry::getTableLocator()->get('Settings')->find('all')->toArray();
foreach ($settings as $s) {
if (!isset($config[$s['group']])) {
$config[$s['group']] = [];
}
if (in_array($s['key'], ["smtpParams"])) {
$s['value'] = @json_decode($s['value'], true);
}
$config[$s['group']][$s['key']] = [
"id" => $s['key'],
"label" => $s['label'],
"value" => $s['value']
];
};
$config["encrypt"] = Configure::read("encrypt");
$tmpDelivery = $this->getDeliveryListForJS();
$config['delivery'] = [];
foreach ($tmpDelivery as $delivery) {
$config['delivery'][$delivery['id']] = $delivery->toArray();
}
$config["appsList"] = [
"cut" => [
"id" => "cut",
"label" => "Раскрой ДСП",
"label_long" => "Раскрой ДСП",
"site_label" => "Online распил ДСП",
"url" => "cut",
],
"fasad" => [
"id" => "fasad",
"label" => "Распил фасадов",
"label_long" => "Распил фасадов",
"site_label" => "Online распил фасадов",
"url" => "fasad",
],
/*"calcDoors" => [
"id" => "calcDoors",
"label" => "Двери",
"label_long" => "Расчет стоимости изготовления дверей",
"url" => "calc-doors",
],*/
"ordersInfo" => [
"id" => "ordersInfo",
"label" => "Информация по счетам",
"label_long" => "Информация по счетам",
"url" => "orders-info",
],
];
return $config;
}
public function getAppJsConfig($app = "", $isManager = false, $savedOrderData = null, $orderClientId = null) {
//$conditions = [];
$jsConfig = [
"urls" => [],
"primarySiteUrl" => "https://supersam24.ru/",
"siteDomain" => (!empty(env('HTTP_CUSTOMHOST')) ? str_replace('apps.', '', env('HTTP_CUSTOMHOST')) : "supersam24.ru"),
"primarySiteTitle" => "СуперСам – Центр мебельных технологий",
"branches" => [],
"delivery" => [],
"global_labels" => $this->config["global_labels"],
];
//$conditions = ['is_active' => true];
$jsConfig = array_merge($jsConfig, [
"version" => Configure::read('Release')
]);
$jsConfig = array_merge($jsConfig, [
"common" => $this->config["common"],
"global" => $this->config["global"],
]);
if ($app == "orderPayment") {
$jsConfig = array_merge($jsConfig, [
"getPaymentStatusUrl" => Router::url(['controller' => 'OrderPayment', 'action' => 'get_payment_status', 'prefix' => false]),
"orderPayment" => (isset($this->config["order_payment"]) ? $this->config["order_payment"] : []),
]);
}
$request = Router::getRequest();
$jsConfig["appsList"] = $this->config["appsList"];
foreach ($this->config["delivery"] as $cfgKey => $cfgDelivery) {
if (empty($cfgDelivery["is_deleted"])) {
$jsConfig["delivery"][] = array_merge(['id' => $cfgKey], $cfgDelivery);
}
}
if (!empty($savedOrderData)) {
$jsConfig['savedOrderData'] = $savedOrderData;
}
return $jsConfig;
}
public function patchTimestampForJS($date, $delta = 1, $returnDateString = true, $format = 'd-m-Y H:i') {
$date = new \DateTime($date);
date_sub($date, date_interval_create_from_date_string($delta . ' hour'));
if ($returnDateString) {
return $date->format($format);
} else {
return $date;
}
}
public function getDeliveryListForJS() {
return TableRegistry::getTableLocator()->get('Delivery')->find('all')->order(['Delivery.label' => 'ASC'])->all()->toArray();
}
}
View File
Whitespace-only changes.
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class DeliveryController extends AppController {
public function beforeFilter(Event $event)
{
parent::beforeFilter($event);
// $this->Auth->allow(['display']);
$this->viewBuilder()->setLayout('delivery');
}
public function display($city = null) {
$isCityFound = false;
if (!empty($city)) {
$deliveryTable = TableRegistry::getTableLocator()->get('Delivery');
$delivery = $deliveryTable->find('all')->where(['slug' => $city, 'is_deleted' => false])->first();
if (!empty($delivery)) {
$delivery = $delivery->toArray();
$this->set('apiKey', $this->config['global']['yandexMapApiKey']['value']);
$this->set('siteTitle', $delivery['label']);
$this->set('delivery', $delivery);
$isCityFound = true;
}
}
if (empty($isCityFound)) {
$this->redirect('/404');
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace App\Controller;
use Cake\Event\Event;
class ErrorController extends AppController {
public $config = null;
public function initialize() {
$this->loadComponent('RequestHandler');
}
public function beforeFilter(Event $event) {
}
public function beforeRender(Event $event) {
parent::beforeRender($event);
$this->viewBuilder()->setTemplatePath('Error');
$this->config = $this->getAppConfig("");
$this->set('jsConfig', $this->getAppJsConfig("", false));
}
public function afterFilter(Event $event) {
}
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace App\Controller;
class OrderPaymentController extends AppController {
public function display() {
$this->redirect('https://apps.supersamsev.ru/services');
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
use Cake\Auth\DefaultPasswordHasher;
class User extends Entity {
protected $_accessible = [
'*' => true,
'id' => false
];
protected function _setPassword($password) {
return (new DefaultPasswordHasher)->hash($password);
}
}
+208
View File
@@ -0,0 +1,208 @@
<div id="result"></div>
<div id="map"></div>
<a tabindex="-1" href="/services" title="{{::config.primarySiteTitle}}" target="_blank" class="delivery-logo-row">
<img class="apps-logo" src="/img/logo-<?=$jsConfig['siteDomain'];?>.png" alt="{{::config.primarySiteTitle}}">
</a>
<script type="text/javascript">
function resultsClose() {
$('#result').removeClass('active');
}
function init() {
var myMap = new ymaps.Map('map', {
center: [<?=$delivery['location'];?>],
zoom: 17,
type: 'yandex#map',
controls: []
}),
searchFinishPoint = new ymaps.control.SearchControl({
options: {
useMapBounds: true,
noCentering: true,
noPopup: true,
noPlacemark: true,
placeholderContent: 'Адрес доставки',
size: 'large',
float: 'none',
position: { left: 10, top: 44 }
}
}),
calculator = new DeliveryCalculator(myMap, myMap.getCenter()) ;
searchFinishPoint.events.add('load', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('clear', function (event) { $('#result').removeClass('active'); });
searchFinishPoint.events.add('submit', function (event) { $('#result').removeClass('active'); });
myMap.controls.add(searchFinishPoint);
calculator.setStartPoint([<?=$delivery['location'];?>]);
searchFinishPoint.events.add('resultselect', function (e) {
var results = searchFinishPoint.getResultsArray(),
selected = e.get('index'),
point = results[selected].geometry.getCoordinates();
calculator.setFinishPoint(point);
})
.add('load', function (event) {
if (!event.get('skip') && searchFinishPoint.getResultsCount()) {
searchFinishPoint.showResult(0);
}
});
}
function DeliveryCalculator(map, finish) {
this._map = map;
this._start = null;
this._route = null;
this._startBalloon;
this._finishBalloon;
map.events.add('click', this._onClick, this);
}
var ptp = DeliveryCalculator.prototype;
ptp._onClick= function (e) {
if (this._start) {
this.setFinishPoint(e.get('coords'));
} else {
this.setStartPoint(e.get('coords'));
}
};
ptp._onStartDragEnd = function (e) {
var coords = this._start.geometry.getCoordinates();
this.geocode("start", coords);
};
ptp._onFinishDragEnd = function (e) {
var coords = this._finish.geometry.getCoordinates();
this.geocode("finish", coords);
};
ptp.getDirection = function () {
if(this._route) {
this._map.geoObjects.remove(this._route);
}
if (this._start && this._finish) {
var self = this,
start = this._start.geometry.getCoordinates(),
finish = this._finish.geometry.getCoordinates(),
startBalloon = this._startBalloon,
finishBalloon = this._finishBalloon;
ymaps.route([
finish,
start,
], {multiRoute: true})
.then(function (router) {
//self._map.geoObjects.add(router);
var routes = router.getRoutes(), distanceWithTolls = 0, distanceWithoutTolls = 0, distance = 0,
selectedRoute, selectedRouteWithTolls, selectedRouteWithoutTolls;
for (var i = 0, l = routes.getLength(); i < l; i++) {
var route = routes.get(i);
if (!route.properties.get('blocked')) {
distance = Math.ceil(route.properties.get('distance').value / 1000)
if (route.properties.get('hasTolls')) {
if ((distanceWithTolls == 0) || (distance < distanceWithTolls)) {
selectedRouteWithTolls = route;
distanceWithTolls = distance;
}
} else {
if ((distanceWithoutTolls == 0) || (distance < distanceWithoutTolls)) {
selectedRouteWithoutTolls = route;
distanceWithoutTolls = distance;
}
}
}
}
if (distanceWithoutTolls > 0) { //route without tolls exists
selectedRoute = selectedRouteWithoutTolls;
distance = distanceWithoutTolls;
} else {
selectedRoute = selectedRouteWithTolls;
distance = distanceWithTolls;
}
router.setActiveRoute(selectedRoute);
var /*distance = Math.round(router.getLength() / 1000),*/
distanceLabel = "",
message = '<div class="resultInner"><span>Расстояние: ' + (distance < 1 ? 1 : distance) + 'км.</span><br/>' +
'<span style="font-weight: bold; font-style: italic; ">Стоимость доставки: %sр.</span><span id="resultsClose" onClick="javascript:resultsClose();">&times;</span><div>'
distanceLabel = self.calculate(distance);
self._route = selectedRoute.getPaths();
self._route.options.set({ strokeWidth: 5, strokeColor: '0000ffff', opacity: 0.5 });
self._map.geoObjects.add(self._route);
self._start.properties.set('balloonContentBody', startBalloon + message.replace('%s', distanceLabel));
self._finish.properties.set('balloonContentBody', finishBalloon + message.replace('%s', distanceLabel));
$('#result').html(finishBalloon + message.replace('%s', distanceLabel)).addClass('active');
});
self._map.setBounds(self._map.geoObjects.getBounds())
}
};
ptp.setStartPoint = function (position) {
if(this._start) {
this._start.geometry.setCoordinates(position);
}
else {
this._start = new ymaps.Placemark(position, { iconContent: 'А', }, { draggable: false });
this._start.events.add('dragend', this._onStartDragEnd, this);
this._map.geoObjects.add(this._start);
}
this.geocode("start", position);
};
ptp.setFinishPoint = function (position) {
if(this._finish) {
this._finish.geometry.setCoordinates(position);
}
else {
this._finish = new ymaps.Placemark(position, { iconContent: 'Б' }, { draggable: true });
this._finish.events.add('dragend', this._onFinishDragEnd, this);
this._map.geoObjects.add(this._finish);
}
if (this._start) {
this.geocode("finish", position);
}
};
ptp.geocode = function (str, point) {
ymaps.geocode(point).then(function(geocode) {
if (str == "start") {
this._startBalloon = "<h3><?=$delivery['address'];?></h3><p>" + geocode.geoObjects.get(0).properties.get('description') + "</p>";
this._start.properties.set('balloonContentBody', this._startBalloon);
} else {
this._finishBalloon = geocode.geoObjects.get(0) &&
geocode.geoObjects.get(0).properties.get('balloonContentBody') || '';
}
this.getDirection();
}, this);
}
ptp.calculate = function (len) {
// Константы.
var DELIVERY_TARIF = <?=$delivery['costPerKm'];?>,
MINIMUM_COST = <?=$delivery['minimalCost'];?>;
if (len == 1) return (MINIMUM_COST);
return Math.max((len - 1) * DELIVERY_TARIF + MINIMUM_COST, MINIMUM_COST);
};
ymaps.ready(init);
</script>
<?php $this->Assign('title', $siteTitle);?>
+10
View File
@@ -0,0 +1,10 @@
<?php
$class = 'message';
if (!empty($params['class'])) {
$class .= ' ' . $params['class'];
}
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="<?= h($class) ?>" onclick="this.classList.add('hidden');"><?= $message ?></div>
+4
View File
@@ -0,0 +1,4 @@
<div class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
<p><?= $message ?></p>
</div>
+6
View File
@@ -0,0 +1,6 @@
<?php
if (!isset($params['escape']) || $params['escape'] !== false) {
$message = h($message);
}
?>
<div class="message success" onclick="this.classList.add('hidden')"><?= $message ?></div>
+7
View File
@@ -0,0 +1,7 @@
<?php use Cake\Core\Configure;?>
<script type="text/javascript">
supersamApp.factory('config', function() {
return <?php echo json_encode(isset($jsConfig) ? $jsConfig : []); ?>;
});
</script>
+38
View File
@@ -0,0 +1,38 @@
<script type="text/javascript">
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
else return null;
}
function setCookie(name,value,days) {
var expires = "";
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days*24*60*60*1000));
expires = "; expires=" + date.toUTCString();
}
document.cookie = name + "=" + (value || "") + expires + "; path=/";
}
(function($, document) {
$(document).ready(function () {
var cookieConsent = getCookie("cookie_consent");
if (cookieConsent == null) {
$("<div class=\"cookie-consent-container-wrap\"><div class=\"cookie-consent-container\"></div>"+
"<div class=\"cookie-consent-content\">"+
"<div><span>Мы используем файлы cookie, чтобы обеспечить максимальное удобство пользователям. Продолжая использовать этот сайт, вы даете согласие на использование файлов cookies.</span> <a href=\"https://supersamsev.ru/cookie-policy\" class=\"cookie-consent-more\" target=\"_blank\">Подробнее</a></div>"+
"<a class=\"cookie-consent-ok\">Согласен</a>"+
"</div>"+
"</div>").appendTo('body');
$(".cookie-consent-ok").on('click', function () {
setCookie('cookie_consent', true, 1095);
$(".cookie-consent-container-wrap").css({'opacity': '0', 'visibility': 'hidden'});
});
}
})
})(jQuery, document);
</script>
+8
View File
@@ -0,0 +1,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="/img/favicon/favicon_32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/favicon/favicon_16.png">
<link rel="shortcut icon" type="image/png" href="/img/favicon/favicon_16.png" />
<?php foreach(['57', '60', '76', '114', '120', '128', '144', '152', '167', '180', '192'] as $s) {
echo '<link rel="apple-touch-icon-precomposed" sizes="' . $s . 'x' . $s . '" href="/img/favicon/apple-touch-icon-' . $s . '-precomposed.png" />' . "\n";
} ?>
<link rel="apple-touch-icon-precomposed" href="/img/favicon/apple-touch-icon-57-precomposed.png" />
+2
View File
@@ -0,0 +1,2 @@
<?php
echo json_encode($result);
+15
View File
@@ -0,0 +1,15 @@
<div class="page-404">
<div class="container">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-12 text-center">
<a href="<?=$jsConfig['primarySiteUrl'];?>" title="<?=$jsConfig['primarySiteTitle'];?>"><img class="apps-logo" src="/img/logo-<?=$jsConfig['siteDomain'];?>.png"></a><br/><br/>
<?= $this->element("services");?>
</div>
</div>
</div>
</div>
</div>
</div>
+34
View File
@@ -0,0 +1,34 @@
<div>
<a href="https://supersam24.ru" class="services-link btn btn-primary">Магазин фурнитуры</a>
<a href="https://supersamsev.ru" class="services-link btn btn-primary">Магазин ДСП</a>
<a href="https://prometeysev.ru" class="services-link btn btn-primary">Все для кухни, ремонта и дома</a>
<div class="services-social-links">
<a class="vk" href="https://vk.com/supersam_aristo" target="_blank">
<img src="/img/social/vk-h.png">
</a>
</div>
<br/><br/></div>
<b>Также вы можете воспользоваться нашими сервисами:</b><br/>
<div>
<?php
foreach ($jsConfig["appsList"] as $app) {
if (!empty($app['url'])) {
echo '<a href="' . $app['url'] . '" class="services-link btn btn-info">' . (!empty($app['site_label']) ? $app['site_label'] : $app['label_long']) . '</a>';
}
}
?>
</div>
<br/>
<div>
<?php
foreach ($jsConfig["delivery"] as $delivery) {
if (empty($delivery['is_deleted'])) {
echo '<a href="/delivery/' . $delivery['slug'] . '" class="services-link btn btn-info">' . $delivery['label404'] . '</a>';
}
}
?>
</div>
+20
View File
@@ -0,0 +1,20 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
$content = explode("\n", $content);
foreach ($content as $line) :
echo '<p> ' . $line . "</p>\n";
endforeach;
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $content;
+34
View File
@@ -0,0 +1,34 @@
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error400.ctp');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?= $this->element('auto_table_warning') ?>
<?php
if (extension_loaded('xdebug')) :
xdebug_print_function_stack();
endif;
$this->end();
endif;
?>
<?= $this->element("page404");?>
+39
View File
@@ -0,0 +1,39 @@
<?php
use Cake\Core\Configure;
use Cake\Error\Debugger;
$this->layout = 'error';
if (Configure::read('debug')) :
$this->layout = 'dev_error';
$this->assign('title', $message);
$this->assign('templateName', 'error500.ctp');
$this->start('file');
?>
<?php if (!empty($error->queryString)) : ?>
<p class="notice">
<strong>SQL Query: </strong>
<?= h($error->queryString) ?>
</p>
<?php endif; ?>
<?php if (!empty($error->params)) : ?>
<strong>SQL Query Params: </strong>
<?php Debugger::dump($error->params) ?>
<?php endif; ?>
<?php if ($error instanceof Error) : ?>
<strong>Error in: </strong>
<?= sprintf('%s, line %s', str_replace(ROOT, 'ROOT', $error->getFile()), $error->getLine()) ?>
<?php endif; ?>
<?php
echo $this->element('auto_table_warning');
if (extension_loaded('xdebug')) :
xdebug_print_function_stack();
endif;
$this->end();
endif;
?>
<?= $this->element("page404");?>
@@ -0,0 +1,24 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title><?= $this->fetch('title') ?></title>
</head>
<body>
<?= $this->fetch('content') ?>
</body>
</html>
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $this->fetch('content');
+16
View File
@@ -0,0 +1,16 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 0.10.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
echo $this->fetch('content');
+55
View File
@@ -0,0 +1,55 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title><?= $this->fetch('title');?></title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"delivery.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body>
<?php if (empty($isRejectedBrowser)) {
$scripts = [
"jquery.min.js",
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
$scripts[] = "https://api-maps.yandex.ru/2.1/?apikey=".$apiKey."&lang=ru_RU";
echo $this->Html->script($scripts);
echo $this->fetch('content');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
} ?>
</body>
</html>
+54
View File
@@ -0,0 +1,54 @@
<?php use Cake\Core\Configure; ?>
<!DOCTYPE html>
<html>
<head>
<?= $this->Html->charset() ?>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>СуперСам :: 404</title>
<?php echo $this->element("favicon"); ?>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<?php if (empty($isRejectedBrowser)) {
$release = "?v".Configure::read('Release');
$cssList = [
"bootstrap.min.css",
"styles.css",
];
foreach ($cssList as $key => $css) {
$cssList[$key] = $css.$release;
}
echo $this->Html->css($cssList);
} else {
echo $this->Html->css('jquery.reject');
} ?>
<?= $this->fetch('meta'); ?>
<?= $this->fetch('css'); ?>
<?= $this->fetch('script'); ?>
<base href="/">
</head>
<body>
<?php if (empty($isRejectedBrowser)) {
$scripts = [
"jquery.min.js",
];
foreach ($scripts as $key => $script) {
$scripts[$key] = $script.$release;
}
echo $this->Html->script($scripts);
echo $this->fetch('content');
echo $this->element('cookie_consent');
} else {
echo $this->Html->script("jquery-1.min.js");
echo $this->Html->script("jquery.reject.js");
echo $this->Html->script("jquery.reject-config.js");
} ?>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<?php
if (!isset($channel)) :
$channel = [];
endif;
if (!isset($channel['title'])) :
$channel['title'] = $this->fetch('title');
endif;
echo $this->Rss->document(
$this->Rss->channel([], $channel, $this->fetch('content'))
);
+18
View File
@@ -0,0 +1,18 @@
<div class="services">
<div class="container">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-md-12 text-center">
<a href="<?=$jsConfig['primarySiteUrl'];?>" title="<?=$jsConfig['primarySiteTitle'];?>"><img src="/img/logo.png"></a><br/><br/>
<?= $this->element("services");?>
</div>
</div>
</div>
</div>
</div>
</div>
<?php $this->Assign('title', $siteTitle);?>
+49
View File
@@ -0,0 +1,49 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.4
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\View;
use Cake\Event\EventManager;
use Cake\Http\Response;
use Cake\Http\ServerRequest;
/**
* A view class that is used for AJAX responses.
* Currently only switches the default layout and sets the response type -
* which just maps to text/html by default.
*/
class AjaxView extends AppView
{
/**
* The name of the layout file to render the view inside of. The name
* specified is the filename of the layout in /src/Template/Layout without
* the .ctp extension.
*
* @var string
*/
public $layout = 'ajax';
/**
* Initialization hook method.
*
* @return void
*/
public function initialize()
{
parent::initialize();
$this->response = $this->response->withType('ajax');
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
/**
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @since 3.0.0
* @license https://opensource.org/licenses/mit-license.php MIT License
*/
namespace App\View;
use Cake\View\View;
/**
* Application View
*
* Your application’s default view class
*
* @link https://book.cakephp.org/3.0/en/views.html#the-app-view
*/
class AppView extends View
{
/**
* Initialization hook method.
*
* Use this method to add common initialization code like loading helpers.
*
* e.g. `$this->loadHelper('Html');`
*
* @return void
*/
public function initialize()
{
}
}
View File
Whitespace-only changes.
View File
Whitespace-only changes.
@@ -0,0 +1,4 @@
/vendor/
*.cache
composer.lock
phpunit.xml
+36
View File
@@ -0,0 +1,36 @@
<?php
$header = <<<EOF
AJGL Breakpoint Twig Extension Component
Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
EOF;
return \PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules(
[
'@Symfony' => true,
'@Symfony:risky' => true,
'array_syntax' => array('syntax' => 'short'),
'fully_qualified_strict_types' => true,
'header_comment' => array('header' => $header),
'native_function_invocation' => false,
'ordered_imports' => [
'imports_order' => ['class', 'const', 'function'],
],
'phpdoc_order' => true,
'psr4' => true,
'strict_comparison' => true,
'strict_param' => true,
]
)
->setFinder(
\PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->in(__DIR__.'/tests')
)
;
@@ -0,0 +1,7 @@
# .scrutinizer.yml
checks:
php: true
tools:
external_code_coverage:
timeout: 900
+68
View File
@@ -0,0 +1,68 @@
language: php
sudo: false
cache:
directories:
- $HOME/.composer/cache/files
- $HOME/symfony-bridge/.phpunit
env:
global:
- PHPUNIT_FLAGS="-v"
- SYMFONY_PHPUNIT_DIR="$HOME/symfony-bridge/.phpunit"
matrix:
fast_finish: true
include:
# Minimum supported dependencies with the latest and oldest PHP version
- php: 7.3
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
- php: 5.6
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
# Test the latest stable release
- php: 5.6
- php: 7.0
- php: 7.1
- php: 7.2
- php: 7.3
env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text" LINT=true
# Test LTS versions. This makes sure we do not use Symfony packages with version greater
# than 2 or 3 respectively. Read more at https://github.com/symfony/lts
- php: 7.3
env: DEPENDENCIES="symfony/lts:^2"
- php: 7.3
env: DEPENDENCIES="symfony/lts:^3"
# Latest commit to master
- php: 7.3
env: STABILITY="dev"
- php: 7.4
env: STABILITY="dev"
- php: nightly
env: STABILITY="dev"
allow_failures:
# Dev-master is allowed to fail.
- env: STABILITY="dev"
before_install:
- if [[ $COVERAGE != true ]]; then phpenv config-rm xdebug.ini || true; fi
- if ! [ -z "$STABILITY" ]; then composer config minimum-stability ${STABILITY}; fi;
- if ! [ -v "$DEPENDENCIES" ]; then composer require --no-update ${DEPENDENCIES}; fi;
- if [[ $LINT = true ]]; then wget https://cs.symfony.com/download/php-cs-fixer-v2.phar; fi
install:
- composer update ${COMPOSER_FLAGS} --prefer-dist --no-interaction
- ./vendor/bin/simple-phpunit install
script:
- composer validate --strict --no-check-lock
- if [[ $LINT = true ]]; then php php-cs-fixer-v2.phar fix --dry-run --diff --no-ansi; fi
# simple-phpunit is the PHPUnit wrapper provided by the PHPUnit Bridge component and
# it helps with testing legacy code and deprecations (composer require symfony/phpunit-bridge)
- ./vendor/bin/simple-phpunit $PHPUNIT_FLAGS
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
+60
View File
@@ -0,0 +1,60 @@
# [CHANGELOG](http://keepachangelog.com/)
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased][unreleased]
## [0.3.4] - 2019-04-10
### Fixed
- Population of `$arguments` variable
## [0.3.3] - 2019-03-18
### Fixed
- Population of `$arguments` variable
- Usage of deprecated classes
## [0.3.2] - 2018-12-10
### Added
- Add support for Symfony 4
## [0.3.1] - 2017-11-20
### Changed
- Require PHP 5.6
### Fixed
- Update requirements to allow twig 2.x
## [0.3.0] - 2016-03-31
### Added
- Add `$environment` and `$context` variables
- Allow to inspect function arguments
## [0.2.0] - 2016-03-10
### Added
- Add Symfony Bundle
## 0.1.0 - 2016-03-09
### Added
- Add `breakpoint` function
[unreleased]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.3.4...master
[0.3.4]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.3.3...0.3.4
[0.3.3]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.3.2...0.3.3
[0.3.2]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.3.1...0.3.2
[0.3.1]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.3.0...0.3.1
[0.3.0]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/compare/0.1.0...0.2.0
+23
View File
@@ -0,0 +1,23 @@
Copyright (c) 2013-2016 Antonio J. García Lagar <aj@garcialagar.es>
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.
+124
View File
@@ -0,0 +1,124 @@
AjglBreakpointTwigExtension
===========================
The AjglBreakpointTwigExtension component allows you set breakpoints in twig templates.
[![Build Status](https://travis-ci.org/ajgarlag/AjglBreakpointTwigExtension.png?branch=master)](https://travis-ci.org/ajgarlag/AjglBreakpointTwigExtension)
[![Latest Stable Version](https://poser.pugx.org/ajgl/breakpoint-twig-extension/v/stable.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![Latest Unstable Version](https://poser.pugx.org/ajgl/breakpoint-twig-extension/v/unstable.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![Total Downloads](https://poser.pugx.org/ajgl/breakpoint-twig-extension/downloads.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![Montly Downloads](https://poser.pugx.org/ajgl/breakpoint-twig-extension/d/monthly.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![Daily Downloads](https://poser.pugx.org/ajgl/breakpoint-twig-extension/d/daily.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![License](https://poser.pugx.org/ajgl/breakpoint-twig-extension/license.png)](https://packagist.org/packages/ajgl/breakpoint-twig-extension)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/ajgarlag/AjglBreakpointTwigExtension/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/ajgarlag/AjglBreakpointTwigExtension/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/ajgarlag/AjglBreakpointTwigExtension/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/ajgarlag/AjglBreakpointTwigExtension/?branch=master)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/e0f1276d-6ded-4a20-9b3f-1a7c77a92015/mini.png)](https://insight.sensiolabs.com/projects/e0f1276d-6ded-4a20-9b3f-1a7c77a92015)
This component requires the [Xdebug] PHP extension to be installed.
Installation
------------
To install the latest stable version of this component, open a console and execute the following command:
```
$ composer require ajgl/breakpoint-twig-extension
```
Usage
-----
The first step is to register the extension into the twig environment
```php
/* @var $twig Twig_Environment */
$twig->addExtension(new Ajgl\Twig\Extension\BreakpointExtension());
```
Once registered, you can call the new `breakpoint` function:
```twig
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
{{ breakpoint() }}
</body>
</html>
```
Once stopped, your debugger will allow you to inspect the `$environment` and `$context` variables.
### Function arguments
Any argument passed to the twig function will be added to the `$arguments` array, so you can inspect it easily.
```twig
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
{{ breakpoint(app.user, app.session) }}
</body>
</html>
```
Symfony Bundle
--------------
If you want to use this extension in your Symfony application, you can enable the
Symfony Bundle included in this package.
The bundle will register the twig extension automatically. So, once enabled, you
can insert the `breakpoint` twig function in your templates.
### Symfony 2/3
```php
// app/AppKernel.php
if (in_array($this->getEnvironment(), array('dev', 'test'), true)) {
$bundles[] = new Ajgl\Twig\Extension\SymfonyBundle\AjglBreakpointTwigExtensionBundle();
}
```
### Symfony 4
```php
// config/bundles.php
//...
return [
//...
Ajgl\Twig\Extension\SymfonyBundle\AjglBreakpointTwigExtensionBundle::class => ['dev' => true]
];
```
License
-------
This component is under the MIT license. See the complete license in the [LICENSE] file.
Reporting an issue or a feature request
---------------------------------------
Issues and feature requests are tracked in the [Github issue tracker].
Author Information
------------------
Developed with ♥ by [Antonio J. García Lagar].
If you find this component useful, please add a ★ in the [GitHub repository page] and/or the [Packagist package page].
[Xdebug]: https://xdebug.org/
[LICENSE]: LICENSE
[Github issue tracker]: https://github.com/ajgarlag/AjglBreakpointTwigExtension/issues
[Antonio J. García Lagar]: http://aj.garcialagar.es
[GitHub repository page]: https://github.com/ajgarlag/AjglBreakpointTwigExtension
[Packagist package page]: https://packagist.org/packages/ajgl/breakpoint-twig-extension
+46
View File
@@ -0,0 +1,46 @@
{
"name": "ajgl/breakpoint-twig-extension",
"description": "Twig extension to set breakpoints",
"keywords": ["twig", "xdebug", "breakpoint"],
"homepage": "https://github.com/ajgarlag/AjglBreakpointTwigExtension",
"license": "MIT",
"authors": [
{
"name": "Antonio J. García Lagar",
"email": "aj@garcialagar.es",
"homepage": "http://aj.garcialagar.es",
"role": "developer"
}
],
"autoload": {
"psr-4": { "Ajgl\\Twig\\Extension\\": "src/" }
},
"autoload-dev": {
"psr-4": { "Ajgl\\Twig\\Extension\\Tests\\": "tests/" }
},
"require": {
"php": ">=5.6",
"twig/twig": "^1.14|^2.0"
},
"require-dev": {
"symfony/framework-bundle": "^2.7|^3.2|^4.1",
"symfony/twig-bundle": "^2.7|^3.2|^4.1",
"symfony/phpunit-bridge": "^3.4|^4.1"
},
"suggest": {
"ext-xdebug": "The Xdebug extension is required for the breakpoint to work",
"symfony/framework-bundle": "The framework bundle to integrate the extension into Symfony",
"symfony/twig-bundle": "The twig bundle to integrate the extension into Symfony"
},
"scripts": {
"phpunit": "vendor/bin/simple-phpunit --colors=always",
"test": [
"@phpunit"
]
},
"extra": {
"branch-alias": {
"dev-master": "0.3.x-dev"
}
}
}
+22
View File
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.0/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="AjglBreakpointTwigExtension Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit>
@@ -0,0 +1,49 @@
<?php
/*
* AJGL Breakpoint Twig Extension Component
*
* Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Ajgl\Twig\Extension;
use Twig\Environment;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
/**
* @author Antonio J. García Lagar <aj@garcialagar.es>
*/
class BreakpointExtension extends AbstractExtension
{
public function getName()
{
return 'breakpoint';
}
public function getFunctions()
{
return [
new TwigFunction('breakpoint', [$this, 'setBreakpoint'], ['needs_environment' => true, 'needs_context' => true]),
];
}
/**
* If Xdebug is detected, makes the debugger break.
*
* @param Environment $environment the environment instance
* @param mixed $context variables from the Twig template
*/
public function setBreakpoint(Environment $environment, $context)
{
if (function_exists('xdebug_break')) {
$arguments = func_get_args();
$arguments = array_slice($arguments, 2);
xdebug_break();
}
}
}
@@ -0,0 +1,21 @@
<?php
/*
* AJGL Breakpoint Twig Extension Component
*
* Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Ajgl\Twig\Extension\SymfonyBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
/**
* @author Antonio J. García Lagar <aj@garcialagar.es>
*/
class AjglBreakpointTwigExtensionBundle extends Bundle
{
}
@@ -0,0 +1,29 @@
<?php
/*
* AJGL Breakpoint Twig Extension Component
*
* Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Ajgl\Twig\Extension\SymfonyBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* @author Antonio J. García Lagar <aj@garcialagar.es>
*/
class AjglBreakpointTwigExtensionExtension extends Extension
{
public function load(array $config, ContainerBuilder $container)
{
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('twig.xml');
}
}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="ajgl_twig_extension.breakpoint" class="Ajgl\Twig\Extension\BreakpointExtension" public="false">
<tag name="twig.extension"/>
</service>
</services>
</container>
@@ -0,0 +1,49 @@
<?php
/*
* AJGL Breakpoint Twig Extension Component
*
* Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Ajgl\Twig\Extension\Tests;
use Ajgl\Twig\Extension\BreakpointExtension;
use PHPUnit\Framework\TestCase;
/**
* @author Antonio J. García Lagar <aj@garcialagar.es>
*/
class BreakpointExtensionTest extends TestCase
{
/**
* @var BreakpointExtension
*/
protected $extension;
protected function setUp()
{
$this->extension = new BreakpointExtension();
}
public function testGetName()
{
$this->assertSame('breakpoint', $this->extension->getName());
}
public function testGetFunctions()
{
$functions = $this->extension->getFunctions();
$this->assertCount(1, $functions);
$function = reset($functions);
$this->assertInstanceOf('Twig_SimpleFunction', $function);
$callable = $function->getCallable();
$this->assertTrue(is_array($callable));
$this->assertCount(2, $callable);
$this->assertSame($this->extension, $callable[0]);
$this->assertSame('setBreakpoint', $callable[1]);
}
}
@@ -0,0 +1,50 @@
<?php
/*
* AJGL Breakpoint Twig Extension Component
*
* Copyright (C) Antonio J. García Lagar <aj@garcialagar.es>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Ajgl\Twig\Extension\Tests\SymfonyBundle\DependencyInjection;
use Ajgl\Twig\Extension\SymfonyBundle\DependencyInjection\AjglBreakpointTwigExtensionExtension;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @author Antonio J. García Lagar <aj@garcialagar.es>
*/
class AjglBreakpointTwigExtensionExtensionTest extends TestCase
{
/**
* @var ContainerBuilder
*/
protected $container;
/**
* @var AjglBreakpointTwigExtensionExtension
*/
protected $extension;
protected function setUp()
{
$this->container = new ContainerBuilder();
$this->extension = new AjglBreakpointTwigExtensionExtension();
}
public function testTwigExtensionsDefinition()
{
$this->extension->load([], $this->container);
$this->assertTrue($this->container->hasDefinition('ajgl_twig_extension.breakpoint'));
$definition = $this->container->getDefinition('ajgl_twig_extension.breakpoint');
$this->assertSame(
'Ajgl\Twig\Extension\BreakpointExtension',
$definition->getClass()
);
$this->assertNotNull($definition->getTag('twig.extension'));
}
}
+27
View File
@@ -0,0 +1,27 @@
language: php
sudo: false
php:
- 5.4
- 5.5
- 5.6
- 7
env:
global:
# travis encrypt CODECLIMATE_REPO_TOKEN=<https://codeclimate.com/repos/53e0fcc8e30ba02d78013d4c/coverage_setup>
- secure: "n4I9GwRCxi9UGjhDGe79SV6Nr/fRKpA8N+qqqbkveJv01Z4IRcAsRBRxKrSAX8zJUoFhlJK73VeRnBfdSBddsL3RqgDMbHilZFy6yC7Dj/vVyNe90RPWxc5J7w3sidT2g1OsgWueiUis8y3dGOQd2sewfcb2pZa29/eQusBV5i0="
install:
- composer install
before_script:
- mkdir -p build/logs
script:
- vendor/bin/phpunit --coverage-clover build/logs/clover.xml
after_script:
- php vendor/bin/coveralls -v
- ./vendor/bin/test-reporter --stdout > codeclimate.json
- "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v1.0.1-dev)' https://codeclimate.com/test_reports"
+23
View File
@@ -0,0 +1,23 @@
CHANGELOG
=========
2.0.0
-----
- **BC**: Require Twig v1.12, in order to replace deprecated Twig_Filter_Method with Twig_SimpleFilter
- **Added**: Add support for ParsedownEnging
1.2.0
-----
- **Added**: Add support for GitHub's markdown engine
1.1.0
-----
- **Added**: Add support for PHP League CommonMark engine
1.0.0
-----
- **BC**: Remove deprecated dflydev-markdown parser
+171
View File
@@ -0,0 +1,171 @@
Twig Markdown Extension
=======================
[![Build Status](https://secure.travis-ci.org/aptoma/twig-markdown.png?branch=master)](http://travis-ci.org/aptoma/twig-markdown)
[![Coverage Status](https://img.shields.io/coveralls/aptoma/twig-markdown.svg)](https://coveralls.io/r/aptoma/twig-markdown)
Twig Markdown extension provides a new filter and a tag to allow parsing of
content as Markdown in [Twig][1] templates.
This extension could be integrated with several Markdown parser as it provides an interface, which allows you to customize your Markdown parser.
### Supported parsers
* [michelf/php-markdown](https://github.com/michelf/php-markdown) (+ MarkdownExtra)
* [league/commonmark](http://commonmark.thephpleague.com/)
* [KnpLabs/php-github-api](https://github.com/KnpLabs/php-github-api)
* [erusev/parsedown](https://github.com/erusev/parsedown)
## Features
* Filter support `{{ "# Heading Level 1"|markdown }}`
* Tag support `{% markdown %}{% endmarkdown %}`
When used as a tag, the indentation level of the first line sets the default indentation level for the rest of the tag content.
From this indentation level, all same indentation or outdented levels text will be transformed as regular text.
This feature allows you to write your Markdown content at any indentation level without caring of Markdown internal transformation:
```php
<div>
<h1 class="someClass">{{ title }}</h1>
{% markdown %}
This is a list that is indented to match the context around the markdown tag:
* List item 1
* List item 2
* Sub List Item
* Sub Sub List Item
The following block will be transformed as code, as it is indented more than the
surrounding content:
$code = "good";
{% endmarkdown %}
</div>
```
## Installation
Run the composer command to install the latest stable version:
```bash
composer require aptoma/twig-markdown
```
Or update your `composer.json`:
```json
{
"require": {
"aptoma/twig-markdown": "~1.1"
}
}
```
You can choose to provide your own Markdown engine, although we recommend
using [michelf/php-markdown](https://github.com/michelf/php-markdown):
```bash
composer require michelf/php-markdown ~1.3
```
```json
{
"require": {
"michelf/php-markdown": "~1.3"
}
}
```
You may also use the [PHP League CommonMark engine](http://commonmark.thephpleague.com/):
```bash
composer require league/commonmark ~0.5
```
```json
{
"require": {
"league/commonmark": "~0.5"
}
}
```
## Usage
### Twig Extension
The Twig extension provides the `markdown` tag and filter support.
Assuming that you are using [composer](http://getcomposer.org) autoloading,
add the extension to the Twig environment:
```php
use Aptoma\Twig\Extension\MarkdownExtension;
use Aptoma\Twig\Extension\MarkdownEngine;
$engine = new MarkdownEngine\MichelfMarkdownEngine();
$twig->addExtension(new MarkdownExtension($engine));
```
### Twig Token Parser
The Twig token parser provides the `markdown` tag only!
```php
use Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\TokenParser\MarkdownTokenParser;
$engine = new MarkdownEngine\MichelfMarkdownEngine();
$twig->addTokenParser(new MarkdownTokenParser($engine));
```
### GitHub Markdown Engine
`MarkdownEngine\GitHubMarkdownEngine` provides an interface to GitHub's markdown engine using their public API via [`KnpLabs\php-github-api`][2]. To reduce API calls, rendered documents are hashed and cached in the filesystem. You can pass a GitHub repository and the path to be used for caching to the constructor:
```php
use Aptoma\Twig\Extension\MarkdownEngine;
$engine = new MarkdownEngine\GitHubMarkdownEngine(
'aptoma/twig-markdown', // The GitHub repository to use as a context
true, // Whether to use GitHub's Flavored Markdown (GFM)
'/tmp/markdown-cache', // Path on filesystem to store rendered documents
);
```
In order to authenticate the API client (for instance), it's possible to pass an own instance of `\GitHub\Client` instead of letting the engine create one itself:
```php
$client = new \GitHub\Client;
$client->authenticate('GITHUB_CLIENT_ID', 'GITHUB_CLIENT_SECRET', \Github\Client::AUTH_URL_CLIENT_ID);
$engine = new MarkdownEngine\GitHubMarkdownEngine('aptoma/twig-markdown', true, '/tmp/markdown-cache', $client);
```
### Using a different Markdown parser engine
If you want to use a different Markdown parser, you need to create an adapter
that implements `Aptoma\Twig\Extension\MarkdownEngineInterface.php`. Have
a look at `Aptoma\Twig\Extension\MarkdownEngine\MichelfMarkdownEngine` for an
example.
## Tests
The test suite uses PHPUnit:
$ phpunit
## License
Twig Markdown Extension is licensed under the MIT license.
[1]: http://twig.sensiolabs.org
[2]: https://github.com/knplabs/php-github-api
+35
View File
@@ -0,0 +1,35 @@
{
"name": "aptoma/twig-markdown",
"description": "Twig extension to work with Markdown content",
"keywords": ["twig", "markdown"],
"license": "MIT",
"authors": [
{
"name": "Gunnar Lium",
"email": "gunnar@aptoma.com"
},
{
"name": "Joris Berthelot",
"email": "joris@berthelot.tel"
}
],
"require": {
"twig/twig": "~1.12"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "~0.6",
"codeclimate/php-test-reporter": "dev-master",
"michelf/php-markdown": "~1",
"league/commonmark": "~0.5",
"knplabs/github-api": "~1.2",
"erusev/parsedown": "^1.6"
},
"suggest": {
"michelf/php-markdown": "Original Markdown engine with MarkdownExtra.",
"knplabs/github-api": "Needed for using GitHub's Markdown engine provided through their API."
},
"autoload": {
"psr-0": { "Aptoma": "src/" }
}
}
+1772
View File
File diff suppressed because it is too large. Load diff
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="vendor/autoload.php"
>
<testsuites>
<testsuite name="Aptoma Markdown Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
</phpunit>
@@ -0,0 +1,76 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
/**
* GithubMarkdownEngine.php
*
* Maps GitHub's Markdown engine API to Aptoma\Twig Markdown Extension using
* KnpLabs\php-github-api.
*
* @author Lukas W <lukaswhl@gmail.com>
*/
class GitHubMarkdownEngine implements MarkdownEngineInterface
{
/**
* Constructor
*
* @param string $contextRepo The repository context. Pass a GitHub repo
* such as 'aptoma/twig-markdown' to render e.g. issues #23 in the
* context of the repo.
* @param bool $gfm Whether to use GitHub's Flavored Markdown or the
* standard markdown. Default is true.
* @param string $cacheDir Location on disk where rendered documents should
* be stored.
* @param \Github\Client $client Client object to use. A new Github\Client()
* object is constructed automatically if $client is null.
*/
public function __construct($contextRepo = null, $gfm = true, $cacheDir = '/tmp/github-markdown-cache', \GitHub\Client $client=null)
{
$this->repo = $contextRepo;
$this->mode = $gfm ? 'gfm' : 'markdown';
$this->cacheDir = rtrim($cacheDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (!is_dir($this->cacheDir)) {
@mkdir($this->cacheDir, 0777, true);
}
if ($client === null) {
$client = new \Github\Client();
}
$this->api = $client->api('markdown');
}
/**
* {@inheritdoc}
*/
public function transform($content)
{
$cacheFile = $this->getCachePath($content);
if (file_exists($cacheFile)) {
return file_get_contents($cacheFile);;
}
$response = $this->api->render($content, $this->mode, $this->repo);
file_put_contents($cacheFile, $response);
return $response;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'KnpLabs\php-github-api';
}
private function getCachePath($content)
{
return $this->cacheDir . md5($content) . '_' . $this->mode. '_' . str_replace('/', '.', $this->repo);
}
private $api;
private $cacheDir;
private $repo;
private $mode;
}
@@ -0,0 +1,32 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
use Michelf\MarkdownExtra;
/**
* MichelfMarkdownEngine.php
*
* Maps Michelf\MarkdownExtra to Aptoma\Twig Markdown Extension
*
* @author Joris Berthelot <joris@berthelot.tel>
*/
class MichelfMarkdownEngine implements MarkdownEngineInterface
{
/**
* {@inheritdoc}
*/
public function transform($content)
{
return MarkdownExtra::defaultTransform($content);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'Michelf\Markdown';
}
}
@@ -0,0 +1,49 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
use League\CommonMark\CommonMarkConverter;
/**
* PHPLeagueCommonMarkEngine.php
*
* Maps League\CommonMark\CommonMarkConverter to Aptoma\Twig Markdown Extension
*
* @author Casey McLaughlin <caseyamcl@gmail.com>
*/
class PHPLeagueCommonMarkEngine implements MarkdownEngineInterface
{
/**
* @var \League\CommonMark\CommonMarkConverter
*/
private $converter;
/**
* Constructor
*
* Accepts CommonMarkConverter or creates one automatically
*
* @param \League\CommonMark\CommonMarkConverter $converter
*/
public function __construct(CommonMarkConverter $converter = null)
{
$this->converter = $converter ?: new CommonMarkConverter();
}
/**
* {@inheritdoc}
*/
public function transform($content)
{
return $this->converter->convertToHtml($content);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'League\CommonMark';
}
}
@@ -0,0 +1,45 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
use Parsedown;
/**
* ParsedownEngine.php
*
* Maps erusev/parsedown to Aptoma\Twig Markdown Extension
*
* @author Sébastien Lourseau <https://github.com/SebLours>
*/
class ParsedownEngine implements MarkdownEngineInterface
{
/**
* @var Parsedown
*/
protected $engine;
/**
* @param string|null $instanceName
*/
public function __construct($instanceName = null)
{
$this->engine = Parsedown::instance($instanceName);
}
/**
* {@inheritdoc}
*/
public function transform($content)
{
return $this->engine->parse($content);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'erusev/parsedown';
}
}
@@ -0,0 +1,28 @@
<?php
namespace Aptoma\Twig\Extension;
/**
* MarkdownEngineInterface.php
*
* Provide software interface to maps various Markdown engines
*
* @author Joris Berthelot <joris@berthelot.tel>
*/
interface MarkdownEngineInterface
{
/**
* Transforms the given markdown data in HTML
*
* @param $content Markdown data
* @return string
*/
public function transform($content);
/**
* Return Markdown engine vendor ID
*
* @return string
*/
public function getName();
}
@@ -0,0 +1,69 @@
<?php
namespace Aptoma\Twig\Extension;
use Aptoma\Twig\TokenParser\MarkdownTokenParser;
/**
* MarkdownExtension provides support for Markdown.
*
* @author Gunnar Lium <gunnar@aptoma.com>
* @author Joris Berthelot <joris@berthelot.tel>
*/
class MarkdownExtension extends \Twig_Extension
{
/**
* @var MarkdownEngineInterface $markdownEngine
*/
private $markdownEngine;
/**
* @param MarkdownEngineInterface $markdownEngine The Markdown parser engine
*/
public function __construct(MarkdownEngineInterface $markdownEngine)
{
$this->markdownEngine = $markdownEngine;
}
/**
* {@inheritdoc}
*/
public function getFilters()
{
return array(
new \Twig_SimpleFilter(
'markdown',
array($this, 'parseMarkdown'),
array('is_safe' => array('html'))
)
);
}
/**
* Transform Markdown content to HTML
*
* @param $content The Markdown content to be transformed
* @return string The result of the Markdown engine transformation
*/
public function parseMarkdown($content)
{
return $this->markdownEngine->transform($content);
}
/**
* {@inheritdoc}
*/
public function getTokenParsers()
{
return array(new MarkdownTokenParser($this->markdownEngine));
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'markdown';
}
}
@@ -0,0 +1,38 @@
<?php
namespace Aptoma\Twig\Node;
/**
* Represents a markdown node.
*
* It parses content as Markdown.
*
* @author Gunnar Lium <gunnar@aptoma.com>
* @author Joris Berthelot <joris@berthelot.tel>
*/
class MarkdownNode extends \Twig_Node
{
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = 'markdown')
{
parent::__construct(array('body' => $body), array(), $lineno, $tag);
}
/**
* Compiles the node to PHP.
*
* @param \Twig_Compiler A Twig_Compiler instance
*/
public function compile(\Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('ob_start();' . PHP_EOL)
->subcompile($this->getNode('body'))
->write('$content = ob_get_clean();' . PHP_EOL)
->write('preg_match("/^\s*/", $content, $matches);' . PHP_EOL)
->write('$lines = explode("\n", $content);' . PHP_EOL)
->write('$content = preg_replace(\'/^\' . $matches[0]. \'/\', "", $lines);' . PHP_EOL)
->write('$content = join("\n", $content);' . PHP_EOL)
->write('echo $this->env->getTokenParsers()->getTokenParser(\'markdown\')->getEngine()->transform($content);' . PHP_EOL);
}
}
@@ -0,0 +1,76 @@
<?php
namespace Aptoma\Twig\TokenParser;
use Aptoma\Twig\Node\MarkdownNode;
use Aptoma\Twig\Extension\MarkdownEngineInterface;
/**
* @author Gunnar Lium <gunnar@aptoma.com>
* @author Joris Berthelot <joris@berthelot.tel>
*/
class MarkdownTokenParser extends \Twig_TokenParser
{
/**
* @var The Markdown engine
*/
protected $markdownEngine;
/**
* @param MarkdownEngineInterface $markdownEngine The Markdown parser engine
*/
public function __construct(MarkdownEngineInterface $markdownEngine)
{
$this->markdownEngine = $markdownEngine;
}
/**
* Markdown parser engine getter
*
* @return MarkdownEngineInterface
*/
public function getEngine()
{
return $this->markdownEngine;
}
/**
* Parses a token and returns a node.
*
* @param \Twig_Token $token A \Twig_Token instance
*
* @throws \Twig_Error_Syntax
* @return \Twig_NodeInterface A \Twig_NodeInterface instance
*/
public function parse(\Twig_Token $token)
{
$lineno = $token->getLine();
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideMarkdownEnd'), true);
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new MarkdownNode($body, $lineno, $this->getTag());
}
/**
* Decide if current token marks end of Markdown block.
*
* @param \Twig_Token $token
* @return bool
*/
public function decideMarkdownEnd(\Twig_Token $token)
{
return $token->test('endmarkdown');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'markdown';
}
}
@@ -0,0 +1,31 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownExtensionTest;
require_once(__DIR__ . '/../MarkdownExtensionTest.php');
/**
* Class GitHubMarkdownEngineTest
*
* @author Lukas W <lukaswhl@gmail.com>
*/
class GitHubMarkdownEngineTest extends MarkdownExtensionTest
{
public function getParseMarkdownTests()
{
return array(
array('{{ "# Main Title"|markdown }}', '<h1>Main Title</h1>'),
array('{{ content|markdown }}', '<h1>Main Title</h1>', array('content' => '# Main Title')),
// Check if GFM is working
array('{{ "@aptoma"|markdown }}',
'<p><a href="https://github.com/aptoma" class="user-mention">@aptoma</a></p>'),
);
}
protected function getEngine()
{
return new GitHubMarkdownEngine();
}
}
@@ -0,0 +1,23 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownExtensionTest;
// Require parent class if not autoloaded
if (!class_exists('\Aptoma\Twig\Extension\MarkdownExtensionTest')) {
require_once(__DIR__ . '/../MarkdownExtensionTest.php');
}
/**
* Class PHPLeagueCommonMarkEngineTest
*
* @author Casey McLaughlin <caseyamcl@gmail.com>
*/
class PHPLeagueCommonMarkEngineTest extends MarkdownExtensionTest
{
protected function getEngine()
{
return new PHPLeagueCommonMarkEngine();
}
}
@@ -0,0 +1,32 @@
<?php
namespace Aptoma\Twig\Extension\MarkdownEngine;
use Aptoma\Twig\Extension\MarkdownExtensionTest;
// Require parent class if not autoloaded
if (!class_exists('\Aptoma\Twig\Extension\MarkdownExtensionTest')) {
require_once(__DIR__ . '/../MarkdownExtensionTest.php');
}
/**
* Class ParsedownEngineTest
*
* @author Sébastien Lourseau <https://github.com/SebLours>
*/
class ParsedownEngineTest extends MarkdownExtensionTest
{
public function getParseMarkdownTests()
{
return array(
array('{{ "# Main Title"|markdown }}', '<h1>Main Title</h1>'),
array('{{ content|markdown }}', '<h1>Main Title</h1>', array('content' => '# Main Title')),
array('{% markdown %}{{ content }}{% endmarkdown %}', '<h1>Main Title</h1>', array('content' => '# Main Title'))
);
}
protected function getEngine()
{
return new ParsedownEngine();
}
}
@@ -0,0 +1,41 @@
<?php
namespace Aptoma\Twig\Extension;
use Aptoma\Twig\Extension\MarkdownEngine\MichelfMarkdownEngine;
/**
* @author Gunnar Liun <gunnar@aptoma.com>
*/
class MarkdownExtensionTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getParseMarkdownTests
*/
public function testParseMarkdown($template, $expected, $context = array())
{
$this->assertEquals($expected, $this->getTemplate($template)->render($context));
}
public function getParseMarkdownTests()
{
return array(
array('{{ "# Main Title"|markdown }}', '<h1>Main Title</h1>' . PHP_EOL),
array('{{ content|markdown }}', '<h1>Main Title</h1>' . PHP_EOL, array('content' => '# Main Title'))
);
}
protected function getEngine()
{
return new MichelfMarkdownEngine();
}
protected function getTemplate($template)
{
$loader = new \Twig_Loader_Array(array('index' => $template));
$twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false));
$twig->addExtension(new MarkdownExtension($this->getEngine()));
return $twig->loadTemplate('index');
}
}
@@ -0,0 +1,109 @@
<?php
namespace Aptoma\Twig\TokenParser;
use Aptoma\Twig\Extension\MarkdownEngine\MichelfMarkdownEngine;
use Aptoma\Twig\Node\MarkdownNode;
/**
* @author Gunnar Lium <gunnar@aptoma.com>
*/
class MarkdownTokenParserTest extends \Twig_Test_NodeTestCase
{
public function testConstructor()
{
$body = new \Twig_Node(array(new \Twig_Node_Text("#Title\n\nparagraph\n", 1)));
$node = new MarkdownNode($body, 1);
$this->assertEquals($body, $node->getNode('body'));
}
/**
* Test that the generated code actually do what we expect
*
* The contents of this test is the same that we write in the compile method.
* This requires manual synchronization, which we should probably not rely on.
*/
public function testMarkdownPrepareBehavior()
{
$body = " #Title\n\n paragraph\n\n code";
$bodyPrepared = "#Title\n\nparagraph\n\n code";
ob_start();
echo $body;
$content = ob_get_clean();
preg_match("/^\s*/", $content, $matches);
$lines = explode("\n", $content);
$content = preg_replace('/^' . $matches[0]. '/', "", $lines);
$content = join("\n", $content);
// Assert prepared content looks right
$this->assertEquals($bodyPrepared, $content);
// Assert Markdown output
$expectedOutput = "<h1>Title</h1>\n\n<p>paragraph</p>\n\n<pre><code>code\n</code></pre>\n";
$this->assertEquals($expectedOutput, $this->getEngine()->transform($content));
}
/**
* Test that the generated code looks as expected
*
* @dataProvider getTests
*/
public function testCompile($node, $source, $environment = null)
{
parent::testCompile($node, $source, $environment);
}
protected function getEngine()
{
return new MichelfMarkdownEngine();
}
public function getTests()
{
$tests = array();
$body = new \Twig_Node(array(new \Twig_Node_Text("#Title\n\nparagraph\n", 1)));
$node = new MarkdownNode($body, 1);
$tests['simple text'] = array($node, <<<EOF
// line 1
ob_start();
echo "#Title
paragraph
";
\$content = ob_get_clean();
preg_match("/^\s*/", \$content, \$matches);
\$lines = explode("\\n", \$content);
\$content = preg_replace('/^' . \$matches[0]. '/', "", \$lines);
\$content = join("\\n", \$content);
echo \$this->env->getTokenParsers()->getTokenParser('markdown')->getEngine()->transform(\$content);
EOF
);
$body = new \Twig_Node(array(new \Twig_Node_Text(" #Title\n\n paragraph\n\n code\n", 1)));
$node = new MarkdownNode($body, 1);
$tests['text with leading indent'] = array($node, <<<EOF
// line 1
ob_start();
echo " #Title
paragraph
code
";
\$content = ob_get_clean();
preg_match("/^\s*/", \$content, \$matches);
\$lines = explode("\\n", \$content);
\$content = preg_replace('/^' . \$matches[0]. '/', "", \$lines);
\$content = join("\\n", \$content);
echo \$this->env->getTokenParsers()->getTokenParser('markdown')->getEngine()->transform(\$content);
EOF
);
return $tests;
}
}
@@ -0,0 +1,4 @@
phpunit.xml
composer.lock
composer.phar
/vendor/
+44
View File
@@ -0,0 +1,44 @@
language: php
matrix:
include:
- php: 5.6
dist: xenial
env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest" TWIG_VERSION="^1.0"'
- php: 5.6
dist: xenial
env: TWIG_VERSION="^1.0"
- php: 7.0
dist: xenial
env: TWIG_VERSION="^1.0"
- php: 7.1
dist: bionic
env: TWIG_VERSION="^1.0"
- php: 7.2
dist: bionic
env: TWIG_VERSION="^1.0"
- php: 7.3
dist: bionic
env: TWIG_VERSION="^1.0"
- php: 7.4
dist: bionic
env: TWIG_VERSION="^1.0"
- php: 7.0
dist: xenial
env: TWIG_VERSION="^2.0"
- php: 7.1
dist: bionic
env: TWIG_VERSION="^2.0"
- php: 7.2
dist: bionic
env: TWIG_VERSION="^2.0"
- php: 7.3
dist: bionic
env: TWIG_VERSION="^2.0"
- php: 7.4
dist: bionic
env: TWIG_VERSION="^2.0"
install: composer require twig/twig:${TWIG_VERSION}
script: composer test
+19
View File
@@ -0,0 +1,19 @@
Copyright (c) 2013 Alexander <iam.asm89@gmail.com>
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.
+238
View File
@@ -0,0 +1,238 @@
Twig cache extension
====================
The missing cache extension for Twig. The extension allows for caching rendered parts of
templates using several cache strategies.
[![Build Status](https://secure.travis-ci.org/asm89/twig-cache-extension.png?branch=master)](http://travis-ci.org/asm89/twig-cache-extension)
## Installation
The extension is installable via composer:
```json
{
"require": {
"asm89/twig-cache-extension": "~1.0"
}
}
```
## Quick start
### Setup
A minimal setup for adding the extension with the `LifeTimeCacheStrategy` and
doctrine array cache is as following:
```php
<?php
use Doctrine\Common\Cache\ArrayCache;
use Asm89\Twig\CacheExtension\CacheProvider\DoctrineCacheAdapter;
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
$cacheProvider = new DoctrineCacheAdapter(new ArrayCache());
$cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
$cacheExtension = new CacheExtension($cacheStrategy);
$twig->addExtension($cacheExtension);
```
### Want to use a PSR-6 cache pool?
Instead of using the default `DoctrineCacheAdapter` the extension also has
a `PSR-6` compatible adapter. You need to instantiate one of the cache pool
implementations as can be found on: http://php-cache.readthedocs.io/en/latest/
Example: Making use of the `ApcuCachePool` via the `PsrCacheAdapter`:
```bash
composer require cache/apcu-adapter
```
```php
<?php
use Asm89\Twig\CacheExtension\CacheProvider\PsrCacheAdapter;
use Asm89\Twig\CacheExtension\CacheStrategy\LifetimeCacheStrategy;
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
use Cache\Adapter\Apcu\ApcuCachePool;
$cacheProvider = new PsrCacheAdapter(new ApcuCachePool());
$cacheStrategy = new LifetimeCacheStrategy($cacheProvider);
$cacheExtension = new CacheExtension($cacheStrategy);
$twig->addExtension($cacheExtension);
```
### Usage
To cache a part of a template in Twig surround the code with a `cache` block.
The cache block takes two parameters, first an "annotation" part, second the
"value" the cache strategy can work with. Example:
```jinja
{% cache 'v1/summary' 900 %}
{# heavy lifting template stuff here, include/render other partials etc #}
{% endcache %}
```
Cache blocks can be nested:
```jinja
{% cache 'v1' 900 %}
{% for item in items %}
{% cache 'v1' item %}
{# ... #}
{% endcache %}
{% endfor %}
{% endcache %}
```
The annotation can also be an expression:
```jinja
{% set version = 42 %}
{% cache 'hello_v' ~ version 900 %}
Hello {{ name }}!
{% endcache %}
```
## Cache strategies
The extension ships with a few cache strategies out of the box. Setup and usage
of all of them is described below.
### Lifetime
See the ["Quick start"](#quick-start) for usage information of the `LifetimeCacheStrategy`.
### Generational
Strategy for generational caching.
In theory the strategy only saves fragments to the cache with infinite
lifetime. The key of the strategy lies in the fact that the keys for blocks
will change as the value for which the key is generated changes.
For example: entities containing a last update time, would include a timestamp
in the key. For an interesting blog post about this type of caching see:
http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
### Blackhole
Strategy for development mode.
In development mode it often not very useful to cache fragments. The blackhole
strategy provides an easy way to not cache anything it all. It always generates
a new key and does not fetch or save any fragments.
#### Setup
In order to use the strategy you need to setup a `KeyGenerator` class that is
able to generate a cache key for a given value.
The following naive example always assumes the value is an object with the methods
`getId()` and `getUpdatedAt()` method. The key then composed from the class
name, the id and the updated time of the object:
```php
<?php
use Asm89\Twig\CacheExtension\CacheStrategy\KeyGeneratorInterface;
class MyKeyGenerator implements KeyGeneratorInterface
{
public function generateKey($value)
{
return get_class($value) . '_' . $value->getId() . '_' . $value->getUpdatedAt();
}
}
```
Next the `GenerationalCacheStrategy` needs to be setup with the keygenerator.
```php
<?php
use Asm89\Twig\CacheExtension\CacheStrategy\GenerationalCacheStrategy;
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
$keyGenerator = new MyKeyGenerator();
$cacheProvider = /* see Quick start */;
$cacheStrategy = new GenerationalCacheStrategy($cacheProvider, $keyGenerator, 0 /* = infinite lifetime */);
$cacheExtension = new CacheExtension($cacheStrategy);
$twig->addExtension($cacheExtension);
```
#### Usage
The strategy expects an object as value for determining the cache key of the
block:
```jinja
{% cache 'v1/summary' item %}
{# heavy lifting template stuff here, include/render other partials etc #}
{% endcache %}
```
### Using multiple strategies
Different cache strategies are useful for different usecases. It is possible to
mix multiple strategies in an application with the
`IndexedChainingCacheStrategy`. The strategy takes an array of `'name' =>
$strategy` and delegates the caching to the appropriate strategy.
#### Setup
```php
<?php
use Asm89\Twig\CacheExtension\CacheStrategy\IndexedChainingCacheStrategy;
use Asm89\Twig\CacheExtension\Extension as CacheExtension;
$cacheStrategy = new IndexedChainingCacheStrategy(array(
'time' => $lifetimeCacheStrategy,
'gen' => $generationalCacheStrategy,
));
$cacheExtension = new CacheExtension($cacheStrategy);
$twig->addExtension($cacheExtension);
```
#### Usage
The strategy expects an array with as key the name of the strategy which it
needs to delegate to and as value the appropriate value for the delegated
strategy.
```jinja
{# delegate to lifetime strategy #}
{% cache 'v1/summary' {time: 300} %}
{# heavy lifting template stuff here, include/render other partials etc #}
{% endcache %}
{# delegate to generational strategy #}
{% cache 'v1/summary' {gen: item} %}
{# heavy lifting template stuff here, include/render other partials etc #}
{% endcache %}
```
## Implementing a cache strategy
Creating separate caches for different access levels, languages or other
usecases can be done by implementing a custom cache strategy. In order to do so
implement the `CacheProviderInterface`. It is recommended to use composition
and wrap a custom strategy around an existing one.
## Authors
Alexander <iam.asm89@gmail.com>
## License
twig-cache-extension is licensed under the MIT License - see the LICENSE file for details
+43
View File
@@ -0,0 +1,43 @@
{
"name": "asm89/twig-cache-extension",
"description": "Cache fragments of templates directly within Twig.",
"keywords": ["twig", "cache", "extension"],
"homepage": "https://github.com/asm89/twig-cache-extension",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Alexander",
"email": "iam.asm89@gmail.com"
}
],
"require": {
"php": ">=5.3.2",
"twig/twig": "^1.0|^2.0"
},
"require-dev": {
"phpunit/phpunit": "^5.0 || ^4.8.10",
"doctrine/cache": "~1.0"
},
"scripts": {
"test": "phpunit"
},
"suggest": {
"psr/cache-implementation": "To make use of PSR-6 cache implementation via PsrCacheAdapter."
},
"autoload": {
"psr-4": {
"": "lib/"
}
},
"autoload-dev": {
"psr-4": {
"": "test/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
}
}
@@ -0,0 +1,49 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheProvider;
use Asm89\Twig\CacheExtension\CacheProviderInterface;
use Doctrine\Common\Cache\Cache;
/**
* Adapter class to use the cache classes provider by Doctrine.
*
* @author Alexander <iam.asm89@gmail.com>
*/
class DoctrineCacheAdapter implements CacheProviderInterface
{
private $cache;
/**
* @param Cache $cache
*/
public function __construct(Cache $cache)
{
$this->cache = $cache;
}
/**
* {@inheritDoc}
*/
public function fetch($key)
{
return $this->cache->fetch($key);
}
/**
* {@inheritDoc}
*/
public function save($key, $value, $lifetime = 0)
{
return $this->cache->save($key, $value, $lifetime);
}
}
@@ -0,0 +1,68 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheProvider;
use Asm89\Twig\CacheExtension\CacheProviderInterface;
use Psr\Cache\CacheItemPoolInterface;
/**
* Adapter class to make extension interoperable with every PSR-6 adapter.
*
* @see http://php-cache.readthedocs.io/
*
* @author Rvanlaak <rvanlaak@gmail.com>
*/
class PsrCacheAdapter implements CacheProviderInterface
{
/**
* @var CacheItemPoolInterface
*/
private $cache;
/**
* @param CacheItemPoolInterface $cache
*/
public function __construct(CacheItemPoolInterface $cache)
{
$this->cache = $cache;
}
/**
* @param string $key
* @return mixed|false
*/
public function fetch($key)
{
// PSR-6 implementation returns null, CacheProviderInterface expects false
$item = $this->cache->getItem($key);
if ($item->isHit()) {
return $item->get();
}
return false;
}
/**
* @param string $key
* @param string $value
* @param int|\DateInterval $lifetime
* @return bool
*/
public function save($key, $value, $lifetime = 0)
{
$item = $this->cache->getItem($key);
$item->set($value);
$item->expiresAfter($lifetime);
return $this->cache->save($item);
}
}
@@ -0,0 +1,36 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension;
/**
* Cache provider interface.
*
* @author Alexander <iam.asm89@gmail.com>
*/
interface CacheProviderInterface
{
/**
* @param string $key
*
* @return mixed False, if there was no value to be fetched. Null or a string otherwise.
*/
public function fetch($key);
/**
* @param string $key
* @param string $value
* @param integer $lifetime
*
* @return boolean
*/
public function save($key, $value, $lifetime = 0);
}
@@ -0,0 +1,53 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheStrategy;
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
/**
* CacheStrategy which doesn't cache at all
*
* This strategy can be used in development mode, e.g. editing twig templates,
* to prevent previously cached versions from being rendered.
*
* @see https://github.com/asm89/twig-cache-extension/pull/29
*
* @author Hagen Hübel <hhuebel@itinance.com>
*
* @package Asm89\Twig\CacheExtension\CacheStrategy
*/
class BlackholeCacheStrategy implements CacheStrategyInterface
{
/**
* {@inheritDoc}
*/
public function fetchBlock($key)
{
return false;
}
/**
* {@inheritDoc}
*/
public function generateKey($annotation, $value)
{
return microtime(true) . mt_rand();
}
/**
* {@inheritDoc}
*/
public function saveBlock($key, $block)
{
// fire and forget
}
}
@@ -0,0 +1,79 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheStrategy;
use Asm89\Twig\CacheExtension\CacheProviderInterface;
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
use Asm89\Twig\CacheExtension\Exception\InvalidCacheKeyException;
/**
* Strategy for generational caching.
*
* In theory the strategy only saves fragments to the cache with infinite
* lifetime. The key of the strategy lies in the fact that the keys for blocks
* will change as the value for which the key is generated changes.
*
* For example: entities containing a last update time, would include a
* timestamp in the key.
*
* @see http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
*
* @author Alexander <iam.asm89@gmail.com>
*/
class GenerationalCacheStrategy implements CacheStrategyInterface
{
private $keyGenerator;
private $cache;
private $lifetime;
/**
* @param CacheProviderInterface $cache
* @param KeyGeneratorInterface $keyGenerator
* @param integer $lifetime
*/
public function __construct(CacheProviderInterface $cache, KeyGeneratorInterface $keyGenerator, $lifetime = 0)
{
$this->keyGenerator = $keyGenerator;
$this->cache = $cache;
$this->lifetime = $lifetime;
}
/**
* {@inheritDoc}
*/
public function fetchBlock($key)
{
return $this->cache->fetch($key);
}
/**
* {@inheritDoc}
*/
public function generateKey($annotation, $value)
{
$key = $this->keyGenerator->generateKey($value);
if (null === $key) {
throw new InvalidCacheKeyException();
}
return $annotation . '__GCS__' . $key;
}
/**
* {@inheritDoc}
*/
public function saveBlock($key, $block)
{
return $this->cache->save($key, $block, $this->lifetime);
}
}
@@ -0,0 +1,76 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheStrategy;
use Asm89\Twig\CacheExtension\CacheStrategyInterface;
use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyException;
use Asm89\Twig\CacheExtension\Exception\NonExistingStrategyKeyException;
/**
* Combines several configured cache strategies.
*
* Useful for combining for example generational cache strategy with a lifetime
* cache strategy, but also useful when combining several generational cache
* strategies which differ on cache lifetime (infinite, 1hr, 5m).
*
* @author Alexander <iam.asm89@gmail.com>
*/
class IndexedChainingCacheStrategy implements CacheStrategyInterface
{
/**
* @var CacheStrategyInterface[]
*/
private $strategies;
/**
* @param array $strategies
*/
public function __construct(array $strategies)
{
$this->strategies = $strategies;
}
/**
* {@inheritDoc}
*/
public function fetchBlock($key)
{
return $this->strategies[$key['strategyKey']]->fetchBlock($key['key']);
}
/**
* {@inheritDoc}
*/
public function generateKey($annotation, $value)
{
if (!is_array($value) || null === $strategyKey = key($value)) {
throw new NonExistingStrategyKeyException();
}
if (!isset($this->strategies[$strategyKey])) {
throw new NonExistingStrategyException($strategyKey);
}
return array(
'strategyKey' => $strategyKey,
'key' => $this->strategies[$strategyKey]->generateKey($annotation, current($value)),
);
}
/**
* {@inheritDoc}
*/
public function saveBlock($key, $block)
{
return $this->strategies[$key['strategyKey']]->saveBlock($key['key'], $block);
}
}
@@ -0,0 +1,29 @@
<?php
/*
* This file is part of twig-cache-extension.
*
* (c) Alexander <iam.asm89@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Asm89\Twig\CacheExtension\CacheStrategy;
/**
* Generates a key for a given value.
*
* @author Alexander <iam.asm89@gmail.com>
*/
interface KeyGeneratorInterface
{
/**
* Generate a cache key for a given value.
*
* @param mixed $value
*
* @return string
*/
public function generateKey($value);
}
Loaded 100 of 6566 files, more files were not shown because too many files have changed in this diff. Show more